alexedwards / flow Goto Github PK
View Code? Open in Web Editor NEWA delightfully tiny but powerful HTTP router for Go web applications
License: MIT License
A delightfully tiny but powerful HTTP router for Go web applications
License: MIT License
Like for example rata.NewRequestGenerator()
, see https://github.com/tedsuo/rata
If not, how would you suggest implementing the client side of an API?
thanks!
Hi, sorry if this is a bad way to contact you, but I wonder if you have any feedback for the recent ServeMux proposal: golang/go#61410
The current implementation of the HTTP router suffers from redundancy and inefficiencies, particularly in route handling. Routes with the same path but different HTTP methods or handlers lead to duplicated route definitions, increased memory consumption, and slower lookup times.
Example:
package main
import (
"fmt"
"net/http"
"test/pkg/flow"
)
func main() {
mux := flow.New()
mux.HandleFunc("/index", indexHandler, "GET", "POST")
mux.HandleFunc("/index", indexPutHandler, "PUT")
mux.HandleFunc("/index", indexDeleteHandler, "DELETE")
fmt.Println(*mux.GetAllRoutes())
}
func indexHandler(w http.ResponseWriter, r *http.Request) {}
func indexPutHandler(w http.ResponseWriter, r *http.Request) {}
func indexDeleteHandler(w http.ResponseWriter, r *http.Request) {}
type Mux struct {
NotFound http.Handler
MethodNotAllowed http.Handler
Options http.Handler
routes *[]route
middlewares []func(http.Handler) http.Handler
}
// for testing purposes
func (m *Mux) GetAllRoutes() *[]route {
return m.routes
}
Output:
[{GET [ index] false 0x100a99160} {POST [ index] false 0x100a99160} {HEAD [ index] false 0x100a99160} {PUT [ index] false 0x100a99170} {DELETE [ index] false 0x100a99180}]
To address these issues, I propose optimizing the HTTP router by consolidating routes with the same path into single route objects and internally mapping HTTP methods to their corresponding handlers. This approach reduces memory usage, streamlines route matching, and simplifies the codebase. By leveraging maps for method-handler associations, we achieve faster lookup times and improved runtime performance.
Implementation Details:
P.S This is my first-ever issue/proposal so feel free to correct and educate me in case I am wrong or missing something!
P.S.S @alexedwards In case this proposal seems reasonable I will be more than happy to create a PR :)
Hello Mr. Edwards,
hope you are doing well!
func (m *Mux) Group(fn func(*Mux)) {
mm := *m
fn(&mm)
}
I wonder why making a copy of m
and then passing the address of the newly created copy to the function, isn't better to update the function declaration to accept Mux
instead of *Mux
and passing the dereferenced value of m
?
func (m *Mux) Group(fn func(Mux)) {
fn(*m)
}
The functionality will remain the same.
The differences between them are:
m
is created, then a copy of the address will be created, and the (*Mux).Group
function contains 2 lines of code.m
will be created, and the (*Mux).Group
contains only 1 line of code.I have public folder and inside public
folder I have test.html
mux := flow.New()
fs := http.FileServer(http.Dir("public"))
mux.Handle("/public/", http.StripPrefix("/public/", fs))
err := http.ListenAndServe(fmt.Sprintf(":%v", config.Get().ServerPort), mux)
http://localhost:5555/public/test.html does not serve the page
however if I change that with default mux I get my html page. I will try to debug it but I am openning issue just in case you spot the problem quickly
I'm working through Let's Go Further for the second time. I'm using the Flow router this time. I'm on chapter 2 of the book and I've built out 3 basic routes.
package main
import (
"net/http"
"github.com/alexedwards/flow"
)
func (app *application) routes() http.Handler {
// initialize a new router (flow router from Alex Edwards)
router := flow.New()
// Routes --------------------------------------------- //
router.HandleFunc("/v1/healthcheck", app.healthcheckHandler, "GET")
router.HandleFunc("/v1/widgets", app.createWidgetHandler, "POST")
router.HandleFunc("/v1/widgets/:id", app.getWidgetHandler, "GET")
return router
}
Here's my getWidgetHandler function:
func (app *application) getWidgetHandler(w http.ResponseWriter, r *http.Request) {
// get the id value as a string.
stringId := flow.Param(r.Context(), "id")
fmt.Fprintf(w, "id: %s", stringId)
}
I sent the following curl request:
curl localhost:4000/v1/widgets/123
I would expect to get the following output:
id: 123
However, I'm getting:
id: 123%
Oddly enough, if I use fmt.Println(stringId)
the output is 123
.
Am I doing something wrong? Or is this a bug?
Thanks,
Chris
Very cool! I like this a lot. I wrote my own router which is also a sort of stripped down take on Chi/Way/httprouter. It's interesting to see more versions of this idea.
Some thoughts:
func ParamInt[Int int|int32uint32|int64|uint64](ctx context.Context, param string, n *Int) (ok bool)
would be nice to have.type route struct {
method string
segments []string
matchers []*regexp.Regexp
wildcard bool
handler http.Handler
}
and Handle to
segments := strings.Split(pattern, "/")
var matchers []*regexp.Regexp
for _, seg := range segments {
key, rxpattern, ok := strings.Cut(seg, "|")
if !ok {
continue
}
rx := regexp.MustCompile(rxpattern)
matchers = append(matchers, rx)
}
for _, method := range methods {
route := route{
method: strings.ToUpper(method),
segments: segments,
matchers: matchers,
wildcard: strings.HasSuffix(pattern, "/..."),
handler: m.wrap(handler),
}
*m.routes = append(*m.routes, route)
}
And in match you can just use the next matcher in the slice whenever you see a pipe.
Very nice router! I like it very much.
Have a question. How can I mount multiple sets of routes like chi.Mount()
does. For example I have some subdomens/components, each with their own routes set. How can I wire them together for passing to main http.Server
?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.