Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issues/134: Add a router/muxer with a bit more functionality #140

Merged
merged 43 commits into from
Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1f7aec7
take code from github.com/matryer/way
komuw Sep 27, 2022
cf65e5f
add t.parallel and name tests
komuw Sep 27, 2022
fb6dcc2
f
komuw Sep 27, 2022
6407688
remove pointer
komuw Sep 27, 2022
dbf0621
remove the feature where uri paths/routes can have ... suffix. Like `…
komuw Sep 27, 2022
e42aec2
remove the need to handle prefixes
komuw Sep 27, 2022
39f8df5
add detection for route conflicts;
komuw Sep 27, 2022
8231c4e
add test for conflicts
komuw Sep 27, 2022
0e84e91
f
komuw Sep 27, 2022
9673830
add test for conflicts
komuw Sep 27, 2022
1e06c10
d
komuw Sep 27, 2022
02cf4d1
f
komuw Sep 27, 2022
7c8fcca
f
komuw Sep 27, 2022
b0da261
f
komuw Sep 27, 2022
2a0e832
f
komuw Sep 27, 2022
eb15a8a
d
komuw Sep 27, 2022
4b72b4e
stop using * for methods
komuw Sep 27, 2022
21d19ff
s
komuw Sep 27, 2022
b257433
ServeHTTP() method has no business to decide whether to admit a reque…
komuw Sep 27, 2022
2071abf
f
komuw Sep 27, 2022
9af8ff1
f
komuw Sep 27, 2022
2e3283d
f
komuw Sep 27, 2022
d780d7f
f
komuw Sep 27, 2022
cb791ec
f
komuw Sep 27, 2022
ea3f7ee
f
komuw Sep 27, 2022
86bb90d
f
komuw Sep 27, 2022
b00b84f
f
komuw Sep 27, 2022
aae6371
f
komuw Sep 27, 2022
587139a
f
komuw Sep 27, 2022
930755a
f
komuw Sep 27, 2022
c2b54b5
f
komuw Sep 27, 2022
c4bd221
f
komuw Sep 27, 2022
a2feb05
f
komuw Sep 27, 2022
5d49f79
f
komuw Sep 27, 2022
21f997d
f
komuw Sep 27, 2022
8fd25cf
f
komuw Sep 27, 2022
bf050eb
f
komuw Sep 27, 2022
9071414
f
komuw Sep 27, 2022
6d28c85
f
komuw Sep 27, 2022
66ce59d
f
komuw Sep 27, 2022
1a2f361
f
komuw Sep 27, 2022
050c349
f
komuw Sep 27, 2022
0de5610
f
komuw Sep 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Most recent version is listed first.
- Add password hashing capabilities: https://github.com/komuw/ong/pull/137
- Simplify loadshedding implementation: https://github.com/komuw/ong/pull/138
- Make automax to be a stand-alone package: https://github.com/komuw/ong/pull/139
- Add a router/muxer with a bit more functionality: https://github.com/komuw/ong/pull/140

## v0.0.8
- Improve documentation.
Expand Down
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,33 @@ import (

"github.com/komuw/ong/log"
"github.com/komuw/ong/middleware"
"github.com/komuw/ong/mux"
"github.com/komuw/ong/server"
)

func main() {
l := log.New(context.Background(), os.Stdout, 1000)
secretKey := "hard-password"
mux := server.NewMux(
mux := mux.New(
l,
middleware.WithOpts("localhost", 65081, secretKey, l),
server.Routes{
server.NewRoute(
mux.Routes{
mux.NewRoute(
"hello/",
server.MethodGet,
mux.MethodGet,
hello("hello world"),
),
mux.NewRoute(
"check/:age/",
mux.MethodAll,
check(),
),
})

_, _ = server.CreateDevCertKey()
err := server.Run(mux, server.DevOpts(), l)
opts := server.DevOpts() // dev options.
// alternatively for production:
// opts := server.LetsEncryptOpts("[email protected]", "*.some-domain.com")
err := server.Run(mux, opts, l)
if err != nil {
fmt.Println(err)
os.Exit(1)
Expand All @@ -65,6 +73,13 @@ func hello(msg string) http.HandlerFunc {
fmt.Fprint(w, msg)
}
}

func check() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
age := mux.Param(r.Context(), "age")
_, _ = fmt.Fprintf(w, "Age is %s", age)
}
}
```

`go run -race ./...`
Expand Down
34 changes: 17 additions & 17 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/komuw/ong/log"
"github.com/komuw/ong/middleware"
"github.com/komuw/ong/mux"
"github.com/komuw/ong/server"
)

Expand All @@ -23,33 +24,32 @@ func main() {
api := NewMyApi("someDb")
l := log.New(context.Background(), os.Stdout, 1000)
secretKey := "hard-password"
mux := server.NewMux(
mux := mux.New(
l,
middleware.WithOpts("localhost", 65081, secretKey, l),
server.Routes{
server.NewRoute(
mux.Routes{
mux.NewRoute(
"/api",
server.MethodPost,
mux.MethodPost,
api.handleAPI(),
),
server.NewRoute(
mux.NewRoute(
"serveDirectory",
server.MethodAll,
mux.MethodAll,
middleware.BasicAuth(api.handleFileServer(), "user", "some-long-passwd"),
),
server.NewRoute(
"check/",
server.MethodAll,
api.check(200),
mux.NewRoute(
"check/:age/",
mux.MethodAll,
api.check("world"),
),
server.NewRoute(
mux.NewRoute(
"login",
server.MethodAll,
mux.MethodAll,
api.login(),
),
})

_, _ = server.CreateDevCertKey()
err := server.Run(mux, server.DevOpts(), l)
if err != nil {
l.Error(err, log.F{"msg": "server.Run error"})
Expand Down Expand Up @@ -115,15 +115,15 @@ func (m myAPI) handleAPI() http.HandlerFunc {
}

// you can take arguments for handler specific dependencies
func (m myAPI) check(code int) http.HandlerFunc {
func (m myAPI) check(msg string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cspNonce := middleware.GetCspNonce(r.Context())
csrfToken := middleware.GetCsrfToken(r.Context())
m.l.Info(log.F{"msg": "check called", "cspNonce": cspNonce, "csrfToken": csrfToken})

_, _ = fmt.Fprint(w, "hello from check/ endpoint")
// use code, which is a dependency specific to this handler
w.WriteHeader(code)
age := mux.Param(r.Context(), "age")
// use msg, which is a dependency specific to this handler
_, _ = fmt.Fprintf(w, "hello %s. Age is %s", msg, age)
}
}

Expand Down
54 changes: 54 additions & 0 deletions mux/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package mux_test

import (
"context"
"fmt"
"net/http"
"os"

"github.com/komuw/ong/log"
"github.com/komuw/ong/middleware"
"github.com/komuw/ong/mux"
)

func LoginHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprint(w, "welcome to your favorite website.")
}
}

func BooksByAuthorHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
author := mux.Param(r.Context(), "author")
_, _ = fmt.Fprintf(w, "fetching books by author: %s", author)
}
}

func ExampleMux() {
l := log.New(context.Background(), os.Stdout, 1000)
mux := mux.New(
l,
middleware.WithOpts("localhost", 8080, "secretKey", l),
mux.Routes{
mux.NewRoute(
"login/",
mux.MethodGet,
LoginHandler(),
),
mux.NewRoute(
"/books/:author",
mux.MethodAll,
BooksByAuthorHandler(),
),
},
)

server := &http.Server{
Handler: mux,
Addr: ":8080",
}
err := server.ListenAndServe()
if err != nil {
panic(err)
}
}
67 changes: 35 additions & 32 deletions server/mux.go → mux/mux.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package server
// Package mux implements a HTTP request multiplexer.
package mux

import (
"context"
"net/http"
"strings"

"github.com/komuw/ong/log"
"github.com/komuw/ong/middleware"
Expand All @@ -21,12 +22,8 @@ const (
MethodTrace = http.MethodTrace
)

// route relates a uri to its http method and http Handler.
type route struct {
pattern string
method string
handler http.HandlerFunc
}
// Routes is a list of all the route for an application.
type Routes []route

// NewRoute creates a new route.
func NewRoute(
Expand All @@ -35,26 +32,30 @@ func NewRoute(
handler http.HandlerFunc,
) route {
return route{
pattern: pattern,
method: method,
pattern: pattern,
segs: pathSegments(pattern),
handler: handler,
}
}

// Routes is a list of all the route for an application.
type Routes []route

// mux implements http.Handler
type mux struct {
// Mux is a HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that most closely matches the URL.
// It implements http.Handler
//
// Use [New] to get a valid Mux.
type Mux struct {
l log.Logger
router *http.ServeMux // some router
router *router // some router
}

// NewMux creates a new mux.
func NewMux(l log.Logger, opt middleware.Opts, rts Routes) *mux {
m := &mux{
// New return a HTTP request multiplexer that has the routes/paths in rts.
// It panics with a helpful message if it detects conflicting routes.
func New(l log.Logger, opt middleware.Opts, rts Routes) Mux {
m := Mux{
l: l,
router: http.NewServeMux(),
router: newRouter(),
}

mid := middleware.All //nolint:ineffassign
Expand All @@ -77,6 +78,7 @@ func NewMux(l log.Logger, opt middleware.Opts, rts Routes) *mux {
}

m.addPattern(
rt.method,
rt.pattern,
mid(rt.handler, opt),
)
Expand All @@ -86,20 +88,21 @@ func NewMux(l log.Logger, opt middleware.Opts, rts Routes) *mux {
}

// ServeHTTP implements a http.Handler
func (m *mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.router.ServeHTTP(w, r)
// It routes incoming http requests based on method and path extracting path parameters as it goes.
func (m Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.router.serveHTTP(w, r)
}

func (m *mux) addPattern(pattern string, handler func(http.ResponseWriter, *http.Request)) {
if !strings.HasSuffix(pattern, "/") {
// this will make the mux send requests for;
// - localhost:80/check
// - localhost:80/check/
// to the same handler.
pattern = pattern + "/"
}
if !strings.HasPrefix(pattern, "/") {
pattern = "/" + pattern
func (m Mux) addPattern(method, pattern string, handler http.HandlerFunc) {
m.router.handle(method, pattern, handler)
}

// Param gets the path parameter from the specified Context.
// Returns an empty string if the parameter was not found.
func Param(ctx context.Context, param string) string {
vStr, ok := ctx.Value(muxContextKey(param)).(string)
if !ok {
return ""
}
m.router.HandleFunc(pattern, handler)
return vStr
}
13 changes: 9 additions & 4 deletions server/mux_test.go → mux/mux_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package server
package mux

import (
"bytes"
Expand All @@ -15,6 +15,11 @@ import (
"github.com/komuw/ong/middleware"
)

func getSecretKey() string {
key := "hard-password"
return key
}

func someMuxHandler(msg string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, msg)
Expand All @@ -35,7 +40,7 @@ func TestMux(t *testing.T) {
t.Parallel()

msg := "hello world"
mux := NewMux(
mux := New(
l,
middleware.WithOpts("localhost", 443, getSecretKey(), l),
Routes{
Expand All @@ -62,7 +67,7 @@ func TestMux(t *testing.T) {

uri := "/api/" // forward slash at suffix is important.
msg := "hello world"
mux := NewMux(
mux := New(
l,
middleware.WithOpts("localhost", 443, getSecretKey(), l),
Routes{
Expand Down Expand Up @@ -108,7 +113,7 @@ func TestMux(t *testing.T) {

msg := "hello world"
uri := "/api"
mux := NewMux(
mux := New(
l,
middleware.WithOpts("localhost", 443, getSecretKey(), l),
Routes{
Expand Down
Loading