Vestigo - A Standalone Golang URL Router
![GoDoc](https://godoc.org/github.com/husobee/vestigo?status.svg)
Abstract
Many fast Golang URL routers are often embedded inside frameworks. Vestigo is a stand alone url router
which has respectable performance that passes URL parameters to handlers by embedding them into the request's
Form.
There is such an abundance of parts and pieces that can be fit together for go web services, it seems like a
shame to have a very fast URL router require the use of one framework, and one context model. This library
aims to give the world a fast, and featureful URL router that can stand on it's own, without being forced into
a particular web framework.
Design
- Radix Tree Based
- Attach URL Parameters into Request (PAT style) instead of context
- HTTP Compliance (TRACE, OPTIONS, HEAD)
- CORS Enabled (per resource access-controls)
TODOs for V1
TODOs for V2
Performance
Initial implementation on a fork of standard http performance testing library shows the following:
BenchmarkVestigo_GithubAll 20000 75763 ns/op 9280 B/op 339 allocs/op
I should mention that the above performance is about 2x slower than the fastest URL router I have tested (Echo/Gin), and
is slightly worse than HTTPRouter, but I am happy with this performance considering this implementation is the fastest
implementation that can handle standard http.HandlerFunc handlers, without forcing end users to use a particular context,
or use a non-standard handler function, locking them into an implementation.
Examples
package main
import (
"log"
"net/http"
"time"
"github.com/husobee/vestigo"
)
func main() {
router := vestigo.NewRouter()
vestigo.AllowTrace = true
router.SetGlobalCors(&vestigo.CorsAccessControl{
AllowOrigin: []string{"*", "test.com"},
AllowCredentials: true,
ExposeHeaders: []string{"X-Header", "X-Y-Header"},
MaxAge: 3600 * time.Second,
AllowHeaders: []string{"X-Header", "X-Y-Header"},
})
router.Get("/welcome", GetWelcomeHandler)
router.Post("/welcome", PostWelcomeHandler)
router.Post("/welcome/:name", PostWelcomeHandler)
router.HandleFunc("/general", GeneralHandler)
router.SetCors("/welcome", &vestigo.CorsAccessControl{
AllowMethods: []string{"GET"},
AllowHeaders: []string{"X-Header", "X-Z-Header"},
})
log.Fatal(http.ListenAndServe(":1234", router))
}
func PostWelcomeHandler(w http.ResponseWriter, r *http.Request) {
name := vestigo.Param(r, "name")
w.WriteHeader(200)
w.Write([]byte("welcome " + name + "!"))
}
func GetWelcomeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("welcome!"))
}
func GeneralHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Gotta catch em all!"))
}
Middleware
Router helper methods (Get, Post, ...) support optional middleware (vestigo provides only middleware type, it is up to
the user to create one).
router.Get("/welcome", GetWelcomeHandler, someMiddleware)
someMiddleware := func(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
f(w, r)
}
}
To break the chain (for example in case of authentication middleware, we don't want to continue execution), just do not
call passed handler function. Example:
auth := func(f http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if authSuccessful() {
f(w, r)
}
}
}
App Performance with net/http/pprof
It is often very helpful to view profiling information from your web application.
Below is an example of hooking up net/http/pprof with vestigo serving the routes:
func Load(router *vestigo.Router) {
router.Get("/debug/pprof/", Index)
router.Get("/debug/pprof/:pprof", Profile)
}
func Index(w http.ResponseWriter, r *http.Request) {
pprof.Index(w, r)
}
func Profile(w http.ResponseWriter, r *http.Request) {
switch vestigo.Param(r, "pprof") {
case "cmdline":
pprof.Cmdline(w, r)
case "profile":
pprof.Profile(w, r)
case "symbol":
pprof.Symbol(w, r)
case "trace":
pprof.Trace(w, r)
default:
Index(w, r)
}
}
Note on wildcards: if you want to get the actual path matched by the wildcard
you can perform vestigo.Param(*http.Request, "_name")
to get the matched path, example below:
router.Get("/*", func(w http.ResponseWriter, r *http.Request) {
fmt.Println(vestigo.Param(r, "_name"))
})
Licensing
Contributing
If you wish to contribute, please fork this repository, submit an issue, or pull request with your suggestions.
Please use gofmt and golint before trying to contribute.