Skip to content

Commit

Permalink
httpserver: WithPort becomes WithAddr. Reimplement options as functio…
Browse files Browse the repository at this point in the history
…ns, rather than structs.
  • Loading branch information
clambin committed Apr 8, 2023
1 parent 784a99c commit 4e9f82e
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 80 deletions.
9 changes: 5 additions & 4 deletions cache/scrubber.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ type scrubber[K comparable, V any] struct {

func (s *scrubber[K, V]) run() {
ticker := time.NewTicker(s.period)
for running := true; running; {
defer ticker.Stop()

for {
select {
case <-s.halt:
running = false
case <-ticker.C:
s.cache.scrub()
case <-s.halt:
return
}
}
ticker.Stop()
}

func stopScrubber[K comparable, V any](c *Cache[K, V]) {
Expand Down
11 changes: 5 additions & 6 deletions httpserver/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ import (

func Example() {
s, err := httpserver.New(
httpserver.WithPort{Port: 8080},
httpserver.WithPrometheus{},
httpserver.WithMetrics{Application: "example", MetricsType: httpserver.Summary},
httpserver.WithHandlers{Handlers: []httpserver.Handler{{
httpserver.WithAddr(":8080"),
httpserver.WithPrometheus(""),
httpserver.WithMetrics("", "", "example", httpserver.Summary, nil),
httpserver.WithHandlers([]httpserver.Handler{{
Path: "/",
Methods: []string{http.MethodGet},
Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("Hello world"))
}),
}},
},
}}),
)

if err != nil {
Expand Down
88 changes: 37 additions & 51 deletions httpserver/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,55 @@ import (
)

// Option specified configuration options for Server
type Option interface {
apply(server *Server)
}
type Option func(*Server)

// WithPort specifies the Server's listening port. If no port is specified, Server will listen on a random port.
// WithAddr specifies the Server's listening address. If the port is zero, Server will listen on a random port.
// Use GetPort() to determine the actual listening port
type WithPort struct {
Port int
}

func (o WithPort) apply(s *Server) {
s.port = o.Port
func WithAddr(addr string) Option {
return func(s *Server) {
s.addr = addr
}
}

// WithPrometheus adds a Prometheus metrics endpoint to the server at the specified Path. Default path is "/metrics"
type WithPrometheus struct {
Path string
}

func (o WithPrometheus) apply(s *Server) {
if o.Path == "" {
o.Path = "/metrics"
func WithPrometheus(path string) Option {
return func(s *Server) {
if path == "" {
path = "/metrics"
}
s.handlers = append(s.handlers, Handler{
Path: path,
Handler: promhttp.Handler(),
Methods: []string{http.MethodGet},
})
}
s.handlers = append(s.handlers, Handler{
Path: o.Path,
Handler: promhttp.Handler(),
Methods: []string{http.MethodGet},
})
}

// WithHandlers adds the specified handlers to the server
type WithHandlers struct {
Handlers []Handler
}

func (o WithHandlers) apply(s *Server) {
s.handlers = append(s.handlers, o.Handlers...)
func WithHandlers(handlers []Handler) Option {
return func(s *Server) {
s.handlers = append(s.handlers, handlers...)
}
}

// WithMetrics will collect the specified metrics to instrument the Server's Handlers.
type WithMetrics struct {
Namespace string
Subsystem string
Application string
MetricsType MetricsType
Buckets []float64
func WithMetrics(namespace, subsystem, application string, metricsType MetricsType, buckets []float64) Option {
return func(s *Server) {
// TODO: this is ugly is f*ck
mwOptions := middleware.PrometheusMetricsOptions{
Namespace: namespace,
Subsystem: subsystem,
Application: application,
Buckets: buckets,
}
switch metricsType {
case Summary:
mwOptions.MetricsType = middleware.Summary
case Histogram:
mwOptions.MetricsType = middleware.Histogram
}
s.instrumentedHandler = middleware.NewPrometheusMetrics(mwOptions)
}
}

// MetricsType specifies the type of metrics to record for request duration. Use Summary if you are only interested in the average latency.
Expand All @@ -66,20 +69,3 @@ const (
// specify the buckets to be used. If none are provided, prometheus.DefBuckets will be used.
Histogram
)

func (o WithMetrics) apply(s *Server) {
// TODO: this is ugly is f*ck
mwOptions := middleware.PrometheusMetricsOptions{
Namespace: o.Namespace,
Subsystem: o.Subsystem,
Application: o.Application,
Buckets: o.Buckets,
}
switch o.MetricsType {
case Summary:
mwOptions.MetricsType = middleware.Summary
case Histogram:
mwOptions.MetricsType = middleware.Histogram
}
s.instrumentedHandler = middleware.NewPrometheusMetrics(mwOptions)
}
9 changes: 6 additions & 3 deletions httpserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// Server implements a configurable HTTP Server. See the different WithXXX structs for available options.
type Server struct {
port int
addr string
handlers []Handler
instrumentedHandler *middleware.PrometheusMetrics
listener net.Listener
Expand All @@ -35,11 +35,14 @@ type Handler struct {
func New(options ...Option) (*Server, error) {
var s Server
for _, o := range options {
o.apply(&s)
o(&s)
}
if s.addr == "" {
s.addr = ":0"
}

var err error
s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", s.port))
s.listener, err = net.Listen("tcp", s.addr)
if err != nil {
return nil, fmt.Errorf("http server: %w", err)
}
Expand Down
32 changes: 16 additions & 16 deletions httpserver/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestServer_ServeHTTP(t *testing.T) {
{
name: "prometheus only",
options: []httpserver.Option{
httpserver.WithPrometheus{},
httpserver.WithPrometheus(""),
},
endpoints: []endpoint{
{path: "/metrics", method: http.MethodGet, result: http.StatusOK},
Expand All @@ -42,15 +42,15 @@ func TestServer_ServeHTTP(t *testing.T) {
{
name: "handlers only",
options: []httpserver.Option{
httpserver.WithHandlers{Handlers: []httpserver.Handler{
httpserver.WithHandlers([]httpserver.Handler{
{
Path: "/foo",
Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("OK"))
}),
Methods: []string{http.MethodPost},
},
}},
}),
},
endpoints: []endpoint{
{path: "/foo", method: http.MethodPost, result: http.StatusOK},
Expand All @@ -61,15 +61,15 @@ func TestServer_ServeHTTP(t *testing.T) {
{
name: "combined",
options: []httpserver.Option{
httpserver.WithPrometheus{},
httpserver.WithHandlers{Handlers: []httpserver.Handler{
httpserver.WithPrometheus(""),
httpserver.WithHandlers([]httpserver.Handler{
{
Path: "/foo",
Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("OK"))
}),
},
}},
}),
},
endpoints: []endpoint{
{path: "/foo", method: http.MethodGet, result: http.StatusOK},
Expand All @@ -81,8 +81,8 @@ func TestServer_ServeHTTP(t *testing.T) {
{
name: "fixed port",
options: []httpserver.Option{
httpserver.WithPort{Port: 8080},
httpserver.WithPrometheus{},
httpserver.WithAddr(":8080"),
httpserver.WithPrometheus(""),
},
endpoints: []endpoint{
{path: "/metrics", method: http.MethodGet, result: http.StatusOK},
Expand Down Expand Up @@ -114,19 +114,19 @@ func TestServer_ServeHTTP(t *testing.T) {
func TestServer_ServerHTTP_WithMetrics(t *testing.T) {
testCases := []struct {
name string
metrics httpserver.WithMetrics
metrics httpserver.Option
evalCount func(t *testing.T, r prometheus.Gatherer)
evalDuration func(t *testing.T, r prometheus.Gatherer)
}{
{
name: "histogram",
metrics: httpserver.WithMetrics{MetricsType: httpserver.Histogram, Application: "foobar"},
metrics: httpserver.WithMetrics("", "", "foobar", httpserver.Histogram, nil),
evalCount: evalRequestsCounter,
evalDuration: evalDurationHistogram,
},
{
name: "summary",
metrics: httpserver.WithMetrics{MetricsType: httpserver.Summary, Application: "foobar"},
metrics: httpserver.WithMetrics("", "", "foobar", httpserver.Summary, nil),
evalCount: evalRequestsCounter,
evalDuration: evalDurationSummary,
},
Expand All @@ -136,12 +136,12 @@ func TestServer_ServerHTTP_WithMetrics(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
s, err := httpserver.New(
httpserver.WithHandlers{Handlers: []httpserver.Handler{{
httpserver.WithHandlers([]httpserver.Handler{{
Path: "/foo",
Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("OK"))
}),
}}},
}}),
tt.metrics,
)
require.NoError(t, err)
Expand Down Expand Up @@ -169,12 +169,12 @@ func TestServer_ServerHTTP_WithMetrics(t *testing.T) {
}

func TestServer_Serve(t *testing.T) {
s, err := httpserver.New(httpserver.WithHandlers{Handlers: []httpserver.Handler{{
s, err := httpserver.New(httpserver.WithHandlers([]httpserver.Handler{{
Path: "/",
Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("OK"))
}),
}}})
}}))
require.NoError(t, err)

var wg sync.WaitGroup
Expand All @@ -198,7 +198,7 @@ func TestServer_Serve(t *testing.T) {
}

func TestServer_Run_BadPort(t *testing.T) {
_, err := httpserver.New(httpserver.WithPort{Port: -1})
_, err := httpserver.New(httpserver.WithAddr(":-1"))
assert.Error(t, err)
}

Expand Down

0 comments on commit 4e9f82e

Please sign in to comment.