Skip to content

Commit

Permalink
Add internal/mux (#372)
Browse files Browse the repository at this point in the history
- This internal muxer contains the bulk of implementation and the exported muxer forwards to it.
- This is done so that we can be able to add methods to the muxer that are needed by ong but aren't necessary for external users.
- We also add one internal muxer method `AddRoute`. This can be used to add a route to an already existing muxer.
   It is used by ong/server to add a route to handle ACME requests. 
   It will also be used in future to add a route for handling pprof requests(#366)

- Updates: #366
  • Loading branch information
komuw authored Aug 31, 2023
1 parent c1f3b86 commit 40b32f7
Show file tree
Hide file tree
Showing 11 changed files with 677 additions and 433 deletions.
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.
- ong/middleware: Add support for http.NewResponseController: https://github.com/komuw/ong/pull/368
- ong/middleware: Improve formatting of some types: https://github.com/komuw/ong/pull/370
- ong/mux: Remove logger from mux: https://github.com/komuw/ong/pull/371
- ong/mux: Add internal/mux: https://github.com/komuw/ong/pull/372

# v0.0.76
- ong/middleware: Configure what percentage of ratelimited or loadshed responses should be logged: https://github.com/komuw/ong/pull/364
Expand Down
20 changes: 10 additions & 10 deletions internal/acme/acme.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,6 @@ func Handler(wrappedHandler http.Handler) http.HandlerFunc {
return
}

if _, errC := netip.ParseAddr(domain); errC == nil {
e := errors.New("ong/acme: request.host for well-known/acme-challenge request should not be IP address")
http.Error(
w,
e.Error(),
http.StatusTeapot,
)
return
}

if strings.Contains(domain, ":") {
d, _, errD := net.SplitHostPort(domain)
if errD != nil {
Expand All @@ -203,6 +193,16 @@ func Handler(wrappedHandler http.Handler) http.HandlerFunc {
domain = d
}

if _, errC := netip.ParseAddr(domain); errC == nil {
e := errors.New("ong/acme: request.host for well-known/acme-challenge request should not be IP address")
http.Error(
w,
e.Error(),
http.StatusTeapot,
)
return
}

// todo: check if the token supplied in the url is known to us and error otherwise.
}

Expand Down
141 changes: 141 additions & 0 deletions internal/mx/mx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Package mx implements a HTTP request multiplexer.
// It is an internal package so that we can be able to add functionality to it that is used by ong, but cannot be called by third parties.
// Proper documentation for users should be added to github.com/komuw/ong/mux instead.
package mx

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

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

// Common HTTP methods.
const (
MethodAll = "ALL"
MethodGet = http.MethodGet
MethodHead = http.MethodHead
MethodPost = http.MethodPost
MethodPut = http.MethodPut
MethodPatch = http.MethodPatch
MethodDelete = http.MethodDelete
MethodConnect = http.MethodConnect
MethodOptions = http.MethodOptions
MethodTrace = http.MethodTrace
)

// Muxer is a HTTP request multiplexer.
type Muxer struct {
router *router
opt middleware.Opts // needed by AddRoute
}

// String implements [fmt.Stringer]
func (m Muxer) String() string {
return fmt.Sprintf(`Muxer{
router: %v
opt: %s
}`,
m.router,
m.opt,
)
}

// GoString implements [fmt.GoStringer]
func (m Muxer) GoString() string {
return m.String()
}

// New returns a HTTP request multiplexer that has the paths in routes.
func New(opt middleware.Opts, notFoundHandler http.Handler, routes ...Route) (Muxer, error) {
m := Muxer{
router: newRouter(notFoundHandler),
opt: opt,
}

mid := middleware.All //nolint:ineffassign
for _, rt := range routes {
switch rt.method {
case MethodAll:
mid = middleware.All
case MethodGet:
mid = middleware.Get
case MethodPost:
mid = middleware.Post
case MethodHead:
mid = middleware.Head
case MethodPut:
mid = middleware.Put
case MethodDelete:
mid = middleware.Delete
default:
mid = middleware.All
}

if err := m.addPattern(
rt.method,
rt.pattern,
rt.originalHandler,
mid(rt.originalHandler, opt),
); err != nil {
return Muxer{}, err
}
}

return m, nil
}

func (m Muxer) addPattern(method, pattern string, originalHandler, wrappingHandler http.Handler) error {
return m.router.handle(method, pattern, originalHandler, wrappingHandler)
}

// ServeHTTP implements a http.Handler
func (m Muxer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
m.router.serveHTTP(w, r)
}

// Resolve resolves a URL path to its corresponding [Route] and hence http handler.
func (m Muxer) Resolve(path string) Route {
zero := Route{}

u, err := url.Parse(path)
if err != nil {
return zero
}

{
// todo: unify this logic with that found in `router.serveHTTP`
segs := pathSegments(u.Path)
for _, rt := range m.router.routes {
if _, ok := rt.match(context.Background(), segs); ok {
return rt
}
}
}

return zero
}

// AddRoute adds a new [Route] to an existing Mux.
// This is only expected to be used internally by ong.
// Users of ong should not use this method. Instead, pass all your routes when calling [New]
func (m Muxer) AddRoute(rt Route) error {
// AddRoute should only be used internally by ong.
return m.addPattern(
rt.method,
rt.pattern,
rt.originalHandler,
middleware.All(rt.originalHandler, m.opt),
)
}

// Param gets the path/url parameter from the specified Context.
func Param(ctx context.Context, param string) string {
vStr, ok := ctx.Value(muxContextKey(param)).(string)
if !ok {
return ""
}
return vStr
}
Loading

0 comments on commit 40b32f7

Please sign in to comment.