Skip to content

Instantly share code, notes, and snippets.

@cstockton
Created June 3, 2017 15:43
Show Gist options
  • Save cstockton/cdbbb1c5559ad7c002f8be09925bcb55 to your computer and use it in GitHub Desktop.
Save cstockton/cdbbb1c5559ad7c002f8be09925bcb55 to your computer and use it in GitHub Desktop.
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/julienschmidt/httprouter"
)
const (
testURL = `/orgs/acmeorg/teams/acmeteam/users/cstockton`
)
func BenchmarkHTTPRouter(b *testing.B) {
req := httptest.NewRequest(
`GET`, testURL, nil)
router := newHTTPRouterHandler(routesGET)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
router.ServeHTTP(discardWriter, req)
}
}
func BenchmarkHandyRouter(b *testing.B) {
req := httptest.NewRequest(
`GET`, testURL, nil)
router := newHandyHandler(routesGET)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
router.ServeHTTP(discardWriter, req)
}
}
func newHTTPRouterHandler(routes []Route) http.Handler {
router := httprouter.New()
for _, route := range routes {
fn := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if ps.ByName(`user`) != `cstockton` {
panic(`fail`)
}
if ps.ByName(`team`) != `acmeteam` {
panic(`fail`)
}
if ps.ByName(`org`) != `acmeorg` {
panic(`fail`)
}
}
router.Handle(route.Method, route.Path, fn)
}
return router
}
func newHandyHandler(routes []Route) http.Handler {
return new(handyRouter)
}
type handyRouter struct{}
type Params struct {
Org string
Team string
User string
}
func (p *Params) From(path string) {
const (
astgenConst1 = `badaction`
astgenConst2 = `badroute`
)
i, last := 1, 0
for ; i < len(path); i++ {
if path[i] != '/' {
continue
}
switch path[last:i] {
case `/orgs`:
i++
last = i
goto astgenSeg1Assign1
case `/users`:
goto astgenBadAction
case `/teams`:
goto astgenBadAction
default:
panic(astgenConst1)
}
}
astgenBadAction:
panic(astgenConst1)
astgenSeg1Assign1:
for ; i < len(path); i++ {
if path[i] != '/' {
continue
}
p.Org = path[last:i]
i++
last = i
goto astgenSeg1Scan2
}
astgenSeg1Scan2:
for ; i < len(path); i++ {
if path[i] != '/' {
continue
}
if path[last:i] == `teams` {
i++
last = i
goto astgenSeg1Assign2
}
panic(astgenConst2)
}
astgenSeg1Assign2:
for ; i < len(path); i++ {
if path[i] != '/' {
continue
}
p.Team = path[last:i]
i++
last = i
goto astgenSeg1Scan3
}
astgenSeg1Scan3:
for ; i < len(path); i++ {
if path[i] != '/' {
continue
}
if path[last:i] == `users` {
p.User = path[i+1:]
return
}
panic(astgenConst2)
}
}
func (handyRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var p Params
p.From(r.URL.Path)
if p.User != `cstockton` {
panic(`failUSER`)
}
if p.Team != `acmeteam` {
panic(`fail`)
}
if p.Org != `acmeorg` {
panic(`fail`)
}
}
type Route struct {
Method string
Path string
}
var (
routes, routesGET []Route
discardWriter = new(DiscardWriter)
)
func init() {
for _, route := range []Route{
{Path: "/users"},
{Path: "/users/:user"},
{Path: "/users/:user/teams"},
{Path: "/users/:user/orgs"},
{Path: "/orgs"},
{Path: "/orgs/:org"},
{Path: "/orgs/:org/users"},
{Path: "/orgs/:org/teams"},
{Path: "/orgs/:org/teams/:team"},
{Path: "/orgs/:org/teams/:team/users"},
{Path: "/orgs/:org/teams/:team/users/:user"},
{Path: "/teams"},
{Path: "/teams/:team"},
{Path: "/teams/:team/repos"},
{Path: "/teams/:team/users/:user"},
} {
routesGET = append(routesGET, Route{`GET`, route.Path})
for _, method := range []string{`GET`, `POST`, `DELETE`, `PUT`} {
route.Method = method
routes = append(routes, route)
}
}
}
type DiscardWriter struct{}
func (w *DiscardWriter) Header() http.Header { return http.Header{} }
func (w *DiscardWriter) Write(p []byte) (int, error) { return len(p), nil }
func (w *DiscardWriter) WriteString(s string) (int, error) { return len(s), nil }
func (w *DiscardWriter) WriteHeader(int) {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment