middleware

package module
v0.0.0-...-403239c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 13, 2018 License: Apache-2.0 Imports: 13 Imported by: 1

README

middleware

import "github.com/beamly/go-http-middleware"

Overview

Package middleware provides a net/http middleware for APIs and web servers.

It largely works in much the same way as node-autoscaling does: it wraps a struct that implements http.Handler by:

  1. Minting a requestID
  2. Running, and timing, the wrapped handler
  3. Adding the request ID to the response
  4. responding with this data
  5. Logging out, to STDOUT, request metadata

A simple implementation would look like:

package main

import (
        "fmt"
        "net/http"

        "github.com/zeebox/go-http-middleware"
)

func main() {
        m := middleware.NewMiddleware(API{})
        http.Handle("/", m)
        panic(http.ListenAndServe(":8008", nil))
}

type API struct{}

func (a API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %q", r.URL.Path)
}

A request to localhost:8008 would then log out:

{"duration":"34.679µs","ip_address":"[::1]:62865","request_id":"1a3d633b-69c3-4131-9f5b-93274e8c39ae","status":200,"time":"2017-05-27T15:23:33.437735653+01:00","url":"/"}

With the response:

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8008 (#0)
> HEAD / HTTP/1.1
> Host: localhost:8008
> User-Agent: curl/7.51.0
> Accept: text/plain
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
Content-Type: text/plain; charset=utf-8
< X-Request-Id: 72c2b7aa-3bcb-478f-8724-66f38cd3abc0
X-Request-Id: 72c2b7aa-3bcb-478f-8724-66f38cd3abc0
< Content-Length: 10
Content-Length: 10

<
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact

Index

Package files

default_logger.go doc.go middleware.go

Constants

const (
    // This is a v5 UUID generated against the DNS namespace
    // The prescence of this UUID in logs means that UUID generation is broken-
    // specifically that https://golang.org/pkg/crypto/rand/#Read is returning
    // an error.
    //
    // The URL used to seed this UUID is james-is-great.beamly.com. This domain
    // does not exist and is, thus, safe to use.
    DefaultBrokenUUID = "cd9bbcae-e076-549f-82bf-a08e8c838dd3"
)

type FasthttpHandler

type FasthttpHandler interface {
    Handle(*fasthttp.RequestCtx)
}

FasthttpHandler represents an opinionated fasthttp based API handler. Because fasthttp doesn't have the concept of a handler interface, like net/http does, we need to build our own.

type LogEntry

type LogEntry struct {
    Duration   string    `json:"duration"`
    DurationMS float64   `json:"duration_ms"`
    IPAddress  string    `json:"ip_address"`
    RequestID  string    `json:"request_id"`
    Status     int       `json:"status"`
    Time       time.Time `json:"time"`
    URL        string    `json:"url"`
}

LogEntry holds a particular requests data, metadata

type Loggable

type Loggable interface {
    Log(LogEntry)
}

Loggable is an interface designed to.... log out

type Middleware

type Middleware struct {

    // Requests contains a hit counter for each route, minus sensitive data like passwords
    // it is exported for use in telemetry and monitoring endpoints.
    Requests map[string]*expvar.Int
    // contains filtered or unexported fields
}

Middleware handles and stores state for the middleware it's self. It, by and large, wraps our handlers and loggers

func New
func New(h interface{}) *Middleware

New wraps NewMiddleware- it exists to remove the stutter from middleware.NewMiddleware and provide a nicer developer experience

func NewMiddleware
func NewMiddleware(h interface{}) (m *Middleware)

NewMiddleware takes either:

* a net/http http.Handler; or
* a middleware.FasthttpHandler

to wrap and returns mutable Middleware object

func (*Middleware) AddLogger
func (m *Middleware) AddLogger(l Loggable)

AddLogger takes anything which implements the Loggable interface and appends it to the Middleware log list which is then used to log stuff out

func (*Middleware) ServeFastHTTP
func (m *Middleware) ServeFastHTTP(ctx *fasthttp.RequestCtx)

ServeFastHTTP wraps our fasthttp requests and produces useful log lines. It does this by wrapping fasthttp compliant endopoints and calling them while timing and catching errors.

Log lines are produced as per:

{"duration":"394.823µs","ip_address":"[::1]:62405","request_id":"80d1b249-0b43-4adc-9456-e42e0b942ec0","status":200,"time":"2017-05-27T14:57:48.750350842+01:00","url":"/"}

where sample-app is the 'app' string passed into NewMiddleware()

These logs are written to STDOUT

func (*Middleware) ServeHTTP
func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP wraps our net/http requests and produces useful log lines. This happens by intercepting the response which the default handler responds with and then sending that on outselves. This approach adds latency to a response, but it gives us access to things like status codes - information which we absolutely need.

Log lines are produced as per:

{"duration":"394.823µs","ip_address":"[::1]:62405","request_id":"80d1b249-0b43-4adc-9456-e42e0b942ec0","status":200,"time":"2017-05-27T14:57:48.750350842+01:00","url":"/"}

where sample-app is the 'app' string passed into NewMiddleware()

These logs are written to STDOUT


Generated by godoc2md

Documentation

Overview

* Package middleware provides a net/http middleware for APIs and web servers.

It largely works in much the same way as `node-autoscaling` does: it wraps a struct that implements `http.Handler` by:

1. Minting a requestID 2. Running, and timing, the wrapped handler 3. Adding the request ID to the response 4. _responding_ with this data 5. Logging out, to STDOUT, request metadata

A simple implementation would look like:

package main

import (
        "fmt"
        "net/http"

        "github.com/zeebox/go-http-middleware"
)

func main() {
        m := middleware.NewMiddleware(API{})
        http.Handle("/", m)
        panic(http.ListenAndServe(":8008", nil))
}

type API struct{}

func (a API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %q", r.URL.Path)
}

A request to `localhost:8008` would then log out:

{"duration":"34.679µs","ip_address":"[::1]:62865","request_id":"1a3d633b-69c3-4131-9f5b-93274e8c39ae","status":200,"time":"2017-05-27T15:23:33.437735653+01:00","url":"/"}

With the response:

  • Trying ::1...

  • TCP_NODELAY set

  • Connected to localhost (::1) port 8008 (#0) > HEAD / HTTP/1.1 > Host: localhost:8008 > User-Agent: curl/7.51.0 > Accept: text/plain > < HTTP/1.1 200 OK HTTP/1.1 200 OK < Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8 < X-Request-Id: 72c2b7aa-3bcb-478f-8724-66f38cd3abc0 X-Request-Id: 72c2b7aa-3bcb-478f-8724-66f38cd3abc0 < Content-Length: 10 Content-Length: 10

    <

  • Curl_http_done: called premature == 0

  • Connection #0 to host localhost left intact

*

Index

Constants

View Source
const (
	// This is a v5 UUID generated against the DNS namespace
	// The prescence of this UUID in logs means that UUID generation is broken-
	// specifically that https://golang.org/pkg/crypto/rand/#Read is returning
	// an error.
	//
	// The URL used to seed this UUID is james-is-great.beamly.com. This domain
	// does not exist and is, thus, safe to use.
	DefaultBrokenUUID = "cd9bbcae-e076-549f-82bf-a08e8c838dd3"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type FasthttpHandler

type FasthttpHandler interface {
	Handle(*fasthttp.RequestCtx)
}

FasthttpHandler represents an opinionated fasthttp based API handler. Because fasthttp doesn't have the concept of a handler interface, like net/http does, we need to build our own.

type LogEntry

type LogEntry struct {
	Duration   string    `json:"duration"`
	DurationMS float64   `json:"duration_ms"`
	IPAddress  string    `json:"ip_address"`
	RequestID  string    `json:"request_id"`
	Status     int       `json:"status"`
	Time       time.Time `json:"time"`
	URL        string    `json:"url"`
	UserAgent  string    `json:"useragent"`
}

LogEntry holds a particular requests data, metadata

type Loggable

type Loggable interface {
	Log(LogEntry)
}

Loggable is an interface designed to.... log out

type Middleware

type Middleware struct {

	// Requests contains a hit counter for each route, minus sensitive data like passwords
	// it is exported for use in telemetry and monitoring endpoints.
	Requests map[string]*expvar.Int
	// contains filtered or unexported fields
}

Middleware handles and stores state for the middleware it's self. It, by and large, wraps our handlers and loggers

func New

func New(h interface{}) *Middleware

New wraps NewMiddleware- it exists to remove the stutter from middleware.NewMiddleware and provide a nicer developer experience

func NewMiddleware

func NewMiddleware(h interface{}) (m *Middleware)

NewMiddleware takes either:

  • a net/http http.Handler; or
  • a middleware.FasthttpHandler

to wrap and returns mutable Middleware object

func (*Middleware) AddLogger

func (m *Middleware) AddLogger(l Loggable)

AddLogger takes anything which implements the Loggable interface and appends it to the Middleware log list which is then used to log stuff out

func (*Middleware) ServeFastHTTP

func (m *Middleware) ServeFastHTTP(ctx *fasthttp.RequestCtx)

ServeFastHTTP wraps our fasthttp requests and produces useful log lines. It does this by wrapping fasthttp compliant endopoints and calling them while timing and catching errors.

Log lines are produced as per:

{"duration":"394.823µs","ip_address":"[::1]:62405","request_id":"80d1b249-0b43-4adc-9456-e42e0b942ec0","status":200,"time":"2017-05-27T14:57:48.750350842+01:00","url":"/"}

where `sample-app` is the 'app' string passed into NewMiddleware()

These logs are written to `STDOUT`

func (*Middleware) ServeHTTP

func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP wraps our net/http requests and produces useful log lines. This happens by intercepting the response which the default handler responds with and then sending that on outselves. This approach adds latency to a response, but it gives us access to things like status codes - information which we absolutely need.

Log lines are produced as per:

{"duration":"394.823µs","ip_address":"[::1]:62405","request_id":"80d1b249-0b43-4adc-9456-e42e0b942ec0","status":200,"time":"2017-05-27T14:57:48.750350842+01:00","url":"/"}

where `sample-app` is the 'app' string passed into NewMiddleware()

These logs are written to `STDOUT`

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL