bmf-san / goblin Goto Github PK
View Code? Open in Web Editor NEWA golang http router based on trie tree.
License: MIT License
A golang http router based on trie tree.
License: MIT License
Bring coverage closer to 100%.
Cover at least conditional branches that cannot be covered.
Run benchmark tests in the latest environment
goblin: [1.0.0](https://github.com/bmf-san/goblin/releases/tag/1.1.0) → Run with latest version!
Golang version: 1.14 → Run with latest version!
Model Name: MacBook Air
Model Identifier: MacBookAir8,1
Processor Name: Dual-Core Intel Core i5
Processor Speed: 1.6 GHz
Number of Processors: 1
Total Number of Cores: 2
Memory: 16 GB
Add test case name to test cases.
ex.
t.Run(testname, func(t *testing.T) {
// do something
})
I want to set the name because it is difficult to understand which test case failed.
I noticed a data structure problem while considering implementing middleware for CORS.
In the current structure of the trie tree, HTTP verb matching is indispensable for routing matching.
This is inconvenient for middleware application.
There are cases where you want to apply middleware processing before determining HTTP verb matching.
Modify the data structure so that the Node has an HTTP verb.
The tree that Router has might as well be a map.
tree map[string]*tree // key is http method
cf. https://github.com/bmf-san/goblin/blob/master/router.go#L11
For data structure optimization.
Need to prove efficency by benchmark tests.
r := goblin.NewRouter()
r.GET(`/`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "/")
}))
r.GET(`/foo/`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "/foo/")
}))
r.GET(`/foo/bar/`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "/foo/bar/")
}))
r.GET(`/foo/bar/:id/`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := goblin.GetParam(r.Context(), "id")
fmt.Fprintf(w, "/foo/bar/%v/", id)
}))
...
This definition expects access to the following URI,
/
/foo/
/foo/bar/
/foo/bar/{id} ex. /foo/bar/1
But in fact, I don't know why I can access undefined URIs without any errors.
/hoge → No errors, and return responses for /.
Improve allocation to 0 allocs in the path parameter test cases.
make test-benchmark CPU=1 COUNT=1
go test -bench=. -cpu=1 -benchmem -count=1
goos: darwin
goarch: arm64
pkg: github.com/bmf-san/goblin
BenchmarkStaticRoutesRootGoblin 75517075 15.65 ns/op 0 B/op 0 allocs/op
BenchmarkStaticRoutes1Goblin 39804074 29.65 ns/op 0 B/op 0 allocs/op
BenchmarkStaticRoutes5Goblin 9692804 124.4 ns/op 0 B/op 0 allocs/op
BenchmarkStaticRoutes10Goblin 4268844 280.4 ns/op 0 B/op 0 allocs/op
BenchmarkPathParamRoutes1ColonGoblin 6099525 199.8 ns/op 328 B/op 3 allocs/op // here
BenchmarkPathParamRoutes5ColonGoblin 3435362 350.5 ns/op 328 B/op 3 allocs/op // here
BenchmarkPathParamRoutes10ColonGoblin 2185375 553.1 ns/op 328 B/op 3 allocs/op // here
PASS
ok github.com/bmf-san/goblin 10.852s
I think it's probably due to the processing of the path parameter in the search method.
https://github.com/bmf-san/goblin/blob/master/trie.go#L250-L263
Umm...
Support Request.PathValue for compatibility.
cf. go-chi/chi#901
cf. go-chi/chi#873
cf. go-chi/chi#901
cf. go-chi/chi#873
As a title.
DisableGeneralOptionsHandler will be available in go1.20.
Want to have an option that can set the DisableGeneralOptionsHandler.
About this issue.
golang/go#61410
I can't say anything definitive because I don't know what the final implementation will be, but I think it will be easier to consider the option of using standard ServeMux instead of using third-party libraries.
I think that the reasons for using third-party libraries are good performance and richness of functions.
Regarding the future of goblin, we intend to continue maintenance for a while.
For applications that personally use goblin, I might switch to the standard ServeMux.
It's hard to say whether goblin will have any advantages over the new ServeMux, but one thing that seems likely is support for regular expressions.
io/ioutil
has been deprecated since Go 1.16.
Replace it to io
.
here:
https://github.com/bmf-san/goblin/blob/master/router_test.go#L5
Due to this correspondence, support for Go1.15 will be terminated.
Such like GIN, group can quickly create sub routers for modules with same middlewares. Is there any way to support group or just in feature?
For eg.:
r := goblin.NewRouter()
group :=r.Group("/module1")
group.Use(someMiddlwareOnlyForThisGroup)
group.Methods(http.MethodGet).Handler("/freeAPI", ...)
group.Methods(http.MethodGet).Use(Authenticator).Handler("/secretAPI", ...)
There are few test cases, so reconsider it for reducing bugs and improving quality.
This diagram is out of date and will be updated.
cf. #88
Update diagram.
Write new diagram.
router package has not have a method for OPTION.
Support a OPTION method.
I want to be able to handle preflight requests.
Go regexp lib is slow so we need to improve perfomance.
bmf-go-router uses regexp for routing pattern with regular expression.
Initializing regexp pattern is costly, maybe this will be solved by initializing regexp pattern as global variables.
var re = regexp.MustCompile("[a-z]{3}")
func main() {
fmt.Println(re.FindAllString("foobar", -1))
// => [foo bar]
}
Add benchmark tests for dynamic routes.
Currently support only static routes.
Or examine another idea for benchmark.
cf. julienschmidt/go-http-routing-benchmark
Want to investigate bmf-go-router perfomance.
To refactor pkg structure.
ex.
router
trie
↓
router
trie
param
path
...
As a title.
func CORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Max-Age", "86400")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, Authorization, Access-Control-Allow-Origin")
w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Pagination-Count, Pagination-Pagecount, Pagination-Page, Pagination-Limit")
next.ServeHTTP(w, r)
})
}
r.GET(`/`).Use(first).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "CORS")
}))
r.OPTIONS(`/`).Use(CORS).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
return
}))
func CORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Max-Age", "86400")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PATCH")
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, Authorization, Access-Control-Allow-Origin")
w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Pagination-Count, Pagination-Pagecount, Pagination-Page, Pagination-Limit")
next.ServeHTTP(w, r)
})
}
r.Methods(http.MethodGet, httpp.MethodOptions).Use(first).Handler(`/`, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "CORS")
}))
I think above pattern is more better.
I feel that the error message is messy, so I want to make it feel good.
Improve some error messages.
Error message improvement points
Do you have any other good ideas?
Line 127 in fce2974
Line 148 in 78aa557
Line 165 in fce2974
etc...
To prepare an example for using goblin with middleware.
Adding an examle for using goblin with middleware to README.
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.