Added router benchmarks
This commit is contained in:
parent
070b374c6e
commit
cb6106a26d
38
Benchmarks_test.go
Normal file
38
Benchmarks_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package server_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.akyoto.dev/go/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkHello(b *testing.B) {
|
||||||
|
request := httptest.NewRequest("GET", "/", nil)
|
||||||
|
response := &NullResponse{}
|
||||||
|
s := server.New()
|
||||||
|
|
||||||
|
s.Get("/", func(ctx server.Context) error {
|
||||||
|
return ctx.String("Hello")
|
||||||
|
})
|
||||||
|
|
||||||
|
for range b.N {
|
||||||
|
s.ServeHTTP(response, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkGitHub(b *testing.B) {
|
||||||
|
request := httptest.NewRequest("GET", "/repos/:owner/:repo", nil)
|
||||||
|
response := &NullResponse{}
|
||||||
|
s := server.New()
|
||||||
|
|
||||||
|
for _, route := range loadRoutes("testdata/github.txt") {
|
||||||
|
s.Router().Add(route.Method, route.Path, func(server.Context) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for range b.N {
|
||||||
|
s.ServeHTTP(response, request)
|
||||||
|
}
|
||||||
|
}
|
4
Handler.go
Normal file
4
Handler.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
// Handler is a function that deals with the given request/response context.
|
||||||
|
type Handler func(Context) error
|
11
NullResponse_test.go
Normal file
11
NullResponse_test.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package server_test
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// NullResponse implements the http.ResponseWriter interface with
|
||||||
|
// empty methods to better understand memory usage in benchmarks.
|
||||||
|
type NullResponse struct{}
|
||||||
|
|
||||||
|
func (r *NullResponse) Header() http.Header { return nil }
|
||||||
|
func (r *NullResponse) Write([]byte) (int, error) { return 0, nil }
|
||||||
|
func (r *NullResponse) WriteHeader(int) {}
|
@ -47,7 +47,8 @@ coverage: 100.0% of statements
|
|||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
```
|
```
|
||||||
coming soon
|
BenchmarkHello-12 33502272 33.64 ns/op 0 B/op 0 allocs/op
|
||||||
|
BenchmarkGitHub-12 17698947 65.50 ns/op 0 B/op 0 allocs/op
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
46
Route_test.go
Normal file
46
Route_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package server_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Route represents a single line in the test data.
|
||||||
|
type Route struct {
|
||||||
|
Method string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadRoutes loads all routes from a text file.
|
||||||
|
func loadRoutes(filePath string) []Route {
|
||||||
|
var routes []Route
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.Close()
|
||||||
|
reader := bufio.NewReader(f)
|
||||||
|
|
||||||
|
for {
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
|
||||||
|
if line != "" {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
parts := strings.Split(line, " ")
|
||||||
|
|
||||||
|
routes = append(routes, Route{
|
||||||
|
Method: parts[0],
|
||||||
|
Path: parts[1],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return routes
|
||||||
|
}
|
13
Server.go
13
Server.go
@ -8,9 +8,6 @@ import (
|
|||||||
"git.akyoto.dev/go/router"
|
"git.akyoto.dev/go/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler is a function that deals with the given request/response context.
|
|
||||||
type Handler func(Context) error
|
|
||||||
|
|
||||||
// Server represents a single web service.
|
// Server represents a single web service.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
router *router.Router[Handler]
|
router *router.Router[Handler]
|
||||||
@ -43,15 +40,21 @@ func (server *Server) Put(path string, handler Handler) {
|
|||||||
server.router.Add(http.MethodPut, path, handler)
|
server.router.Add(http.MethodPut, path, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Router returns the router used by the server.
|
||||||
|
func (server *Server) Router() *router.Router[Handler] {
|
||||||
|
return server.router
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP responds to the given request.
|
// ServeHTTP responds to the given request.
|
||||||
func (server *Server) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
func (server *Server) ServeHTTP(response http.ResponseWriter, request *http.Request) {
|
||||||
ctx := newContext(request, response)
|
ctx := newContext(request, response)
|
||||||
|
defer contextPool.Put(ctx)
|
||||||
|
|
||||||
handler := server.router.LookupNoAlloc(request.Method, request.URL.Path, ctx.addParameter)
|
handler := server.router.LookupNoAlloc(request.Method, request.URL.Path, ctx.addParameter)
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
response.WriteHeader(http.StatusNotFound)
|
response.WriteHeader(http.StatusNotFound)
|
||||||
response.(io.StringWriter).WriteString(http.StatusText(http.StatusNotFound))
|
response.(io.StringWriter).WriteString(http.StatusText(http.StatusNotFound))
|
||||||
contextPool.Put(ctx)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +64,4 @@ func (server *Server) ServeHTTP(response http.ResponseWriter, request *http.Requ
|
|||||||
response.(io.StringWriter).WriteString(err.Error())
|
response.(io.StringWriter).WriteString(err.Error())
|
||||||
log.Println(request.URL, err)
|
log.Println(request.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
contextPool.Put(ctx)
|
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ func TestRouter(t *testing.T) {
|
|||||||
func TestPanic(t *testing.T) {
|
func TestPanic(t *testing.T) {
|
||||||
s := server.New()
|
s := server.New()
|
||||||
|
|
||||||
s.Get("/panic", func(ctx server.Context) error {
|
s.Router().Add(http.MethodGet, "/panic", func(ctx server.Context) error {
|
||||||
panic("Something unbelievable happened")
|
panic("Something unbelievable happened")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
17
examples/hello/main.go
Normal file
17
examples/hello/main.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.akyoto.dev/go/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
s := server.New()
|
||||||
|
|
||||||
|
s.Get("/", func(ctx server.Context) error {
|
||||||
|
return ctx.String("Hello")
|
||||||
|
})
|
||||||
|
|
||||||
|
http.ListenAndServe(":8080", s)
|
||||||
|
}
|
2
go.mod
2
go.mod
@ -4,5 +4,5 @@ go 1.22
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
git.akyoto.dev/go/assert v0.1.3
|
git.akyoto.dev/go/assert v0.1.3
|
||||||
git.akyoto.dev/go/router v0.1.1
|
git.akyoto.dev/go/router v0.1.2
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -1,4 +1,4 @@
|
|||||||
git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8=
|
git.akyoto.dev/go/assert v0.1.3 h1:QwCUbmG4aZYsNk/OuRBz1zWVKmGlDUHhOnnDBfn8Qw8=
|
||||||
git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=
|
git.akyoto.dev/go/assert v0.1.3/go.mod h1:0GzMaM0eURuDwtGkJJkCsI7r2aUKr+5GmWNTFPgDocM=
|
||||||
git.akyoto.dev/go/router v0.1.1 h1:6fHjzv59MKMhO2DsM90mkI5hy5PrcjV4WeD1Vk1xXXs=
|
git.akyoto.dev/go/router v0.1.2 h1:UZq92uOqQwxH+fS8geEhQ5RLZS7sb/QLvwrRzXjaTcg=
|
||||||
git.akyoto.dev/go/router v0.1.1/go.mod h1:IwwEUJU2ExmozpZKMbDOKdiVT516oAijnxGDg9kvBt4=
|
git.akyoto.dev/go/router v0.1.2/go.mod h1:VfSsK/Z6fUhT3pWaAAnuAcj++bWRZD+bzNaqJoTAunU=
|
||||||
|
Loading…
Reference in New Issue
Block a user