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

Use net/http router instead of chi #215

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ require (
github.com/charmbracelet/ssh v0.0.0-20240725163421-eb71b85b27aa
github.com/charmbracelet/wish v1.4.3
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi/v5 v5.1.0
github.com/jaevor/go-nanoid v1.4.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/klauspost/compress v1.17.10
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ github.com/galeone/tensorflow/tensorflow/go v0.0.0-20221023090153-6b7fa0680c3e h
github.com/galeone/tensorflow/tensorflow/go v0.0.0-20221023090153-6b7fa0680c3e/go.mod h1:TelZuq26kz2jysARBwOrTv16629hyUsHmIoj54QqyFo=
github.com/galeone/tfgo v0.0.0-20230214145115-56cedbc50978 h1:8xhEVC2zjvI+3xWkt+78Krkd6JYp+0+iEoBVi0UBlJs=
github.com/galeone/tfgo v0.0.0-20230214145115-56cedbc50978/go.mod h1:3YgYBeIX42t83uP27Bd4bSMxTnQhSbxl0pYSkCDB1tc=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
Expand Down
18 changes: 10 additions & 8 deletions internal/http/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ var (
type Assets interface {
Doc(filename string) ([]byte, error)
Template() *template.Template
ServeJS(w http.ResponseWriter, r *http.Request)
ServeCSS(w http.ResponseWriter, r *http.Request)
Serve(w http.ResponseWriter, r *http.Request)
}

type StaticAssets struct {
Expand Down Expand Up @@ -137,12 +136,15 @@ func (a *StaticAssets) Template() *template.Template {
return a.tmpl
}

func (a *StaticAssets) ServeJS(w http.ResponseWriter, r *http.Request) {
serve(w, r, a.js, jsMime)
}

func (a *StaticAssets) ServeCSS(w http.ResponseWriter, r *http.Request) {
serve(w, r, a.css, cssMime)
func (a *StaticAssets) Serve(w http.ResponseWriter, r *http.Request) {
switch r.PathValue("asset") {
case "index.js":
serve(w, r, a.js, jsMime)
case "index.css":
serve(w, r, a.css, cssMime)
default:
http.NotFound(w, r)
}
}

// serve serves the content, gzipped if the client accepts it.
Expand Down
25 changes: 21 additions & 4 deletions internal/http/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import (
"encoding/json"
"html/template"
"net/http"
"net/http/pprof"
"net/url"
"strings"

"github.com/dustin/go-humanize"
"github.com/go-chi/chi/v5"
"github.com/robherley/snips.sh/internal/config"
"github.com/robherley/snips.sh/internal/db"
"github.com/robherley/snips.sh/internal/logger"
Expand All @@ -22,6 +22,24 @@ func HealthHandler(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("💚\n"))
}

func ProfileHandler(w http.ResponseWriter, r *http.Request) {
profiler := r.PathValue("profile")
switch profiler {
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:
// Available profiles can be found in [runtime/pprof.Profile].
// https://pkg.go.dev/runtime/pprof#Profile
pprof.Handler(profiler).ServeHTTP(w, r)
}
}

func MetaHandler(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
Expand Down Expand Up @@ -53,7 +71,7 @@ func DocHandler(assets Assets) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log := logger.From(r.Context())

name := chi.URLParam(r, "name")
name := r.PathValue("name")
if name == "" {
name = "README.md"
}
Expand Down Expand Up @@ -94,8 +112,7 @@ func FileHandler(cfg *config.Config, database db.DB, assets Assets) http.Handler
return func(w http.ResponseWriter, r *http.Request) {
log := logger.From(r.Context())

fileID := chi.URLParam(r, "fileID")

fileID := r.PathValue("fileID")
if fileID == "" {
http.NotFound(w, r)
return
Expand Down
44 changes: 33 additions & 11 deletions internal/http/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,34 @@ package http
import (
"context"
"net/http"
"strings"
"time"

"github.com/armon/go-metrics"
"github.com/go-chi/chi/v5"
"github.com/robherley/snips.sh/internal/id"
"github.com/robherley/snips.sh/internal/logger"
"github.com/rs/zerolog/log"
)

type Middleware func(next http.Handler) http.Handler

var DefaultMiddleware = []Middleware{
WithRecover,
WithMetrics,
WithLogger,
WithRequestID,
}

func WithMiddleware(handler http.Handler, middlewares ...Middleware) http.Handler {
middlewares = append(DefaultMiddleware, middlewares...)

withMiddleware := handler
for i := range middlewares {
withMiddleware = middlewares[i](withMiddleware)
}
return withMiddleware
}

// WithRequestID adds a unique request ID to the request context.
func WithRequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -40,9 +59,10 @@ func WithLogger(next http.Handler) http.Handler {
ctx := context.WithValue(r.Context(), logger.ContextKey, &reqLog)
reqLog.Info().Msg("connected")

next.ServeHTTP(w, r.WithContext(ctx))
r = r.WithContext(ctx)
next.ServeHTTP(w, r)

reqLog.Info().Dur("dur", time.Since(start)).Msg("disconnected")
reqLog.Info().Dur("dur", time.Since(start)).Str("pattern", Pattern(r)).Msg("disconnected")
})
}

Expand All @@ -65,19 +85,21 @@ func WithMetrics(next http.Handler) http.Handler {
start := time.Now()
next.ServeHTTP(w, r)

rctx := chi.RouteContext(r.Context())
pattern := rctx.RoutePattern()
if pattern == "" {
// empty pattern, didn't match router e.g. 404
pattern = "*"
}

labels := []metrics.Label{
{Name: "path", Value: pattern},
{Name: "pattern", Value: Pattern(r)},
{Name: "method", Value: r.Method},
}

metrics.IncrCounterWithLabels([]string{"http", "request"}, 1, labels)
metrics.MeasureSinceWithLabels([]string{"http", "request", "duration"}, start, labels)
})
}

func Pattern(r *http.Request) string {
pattern := strings.TrimPrefix(r.Pattern, r.Method+" ")
if pattern == "" {
// empty pattern, didn't match router e.g. 404
pattern = "*"
}
return pattern
}
37 changes: 14 additions & 23 deletions internal/http/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,32 @@ package http
import (
"net/http"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/robherley/snips.sh/internal/config"
"github.com/robherley/snips.sh/internal/db"
)

type Service struct {
*http.Server
Router *chi.Mux
}

func New(cfg *config.Config, database db.DB, assets Assets) (*Service, error) {
router := chi.NewRouter()
mux := http.NewServeMux()

router.Use(WithRequestID)
router.Use(WithLogger)
router.Use(WithMetrics)
router.Use(WithRecover)

router.Get("/", DocHandler(assets))
router.Get("/docs/{name}", DocHandler(assets))
router.Get("/health", HealthHandler)
router.Get("/f/{fileID}", FileHandler(cfg, database, assets))
router.Get("/assets/index.js", assets.ServeJS)
router.Get("/assets/index.css", assets.ServeCSS)
router.Get("/meta.json", MetaHandler(cfg))
mux.HandleFunc("GET /{$}", DocHandler(assets))
mux.HandleFunc("GET /docs/{name}", DocHandler(assets))
mux.HandleFunc("GET /health", HealthHandler)
mux.HandleFunc("GET /f/{fileID}", FileHandler(cfg, database, assets))
mux.HandleFunc("GET /assets/{asset}", assets.Serve)
mux.HandleFunc("GET /meta.json", MetaHandler(cfg))

if cfg.Debug {
router.Mount("/_debug", middleware.Profiler())
}

httpServer := &http.Server{
Addr: cfg.HTTP.Internal.Host,
Handler: router,
mux.HandleFunc("/_debug/pprof/{profile}", ProfileHandler)
}

return &Service{httpServer, router}, nil
return &Service{
&http.Server{
Addr: cfg.HTTP.Internal.Host,
Handler: WithMiddleware(mux),
},
}, nil
}
2 changes: 1 addition & 1 deletion internal/http/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (suite *HTTPServiceSuite) SetupTest() {
}

func (suite *HTTPServiceSuite) TestHTTPServer() {
ts := httptest.NewServer(suite.service.Router)
ts := httptest.NewServer(suite.service.Handler)
defer ts.Close()

signedFileID := "wdHzc62hsn"
Expand Down
Loading