mux

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2021 License: MIT Imports: 10 Imported by: 1

README

mux

Go

A minimal HTTP router that wraps the https://github.com/julienschmidt/httprouter.

Quick Start

go get github.com/josestg/mux
Example
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/google/uuid"
	"github.com/josestg/mux"
)

func main() {

	requestIDMaker := new(IDMaker)
	shutdownChannel := make(chan os.Signal, 1)

	router := mux.NewRouter(
		requestIDMaker,
		shutdownChannel,
		logger,
	)

	router.Get("/check", func(w http.ResponseWriter, r *http.Request) error {
		status := map[string]string{
			"status": "running",
		}
		return mux.ToJSON(r.Context(), w, &status, http.StatusOK)
	})

	router.With(auth).Post("/auth", func(w http.ResponseWriter, r *http.Request) error {
		data := struct {
			Status string
		}{
			Status: "Authenticated",
		}

		return mux.ToJSON(r.Context(), w, &data, http.StatusOK)
	})

	server := http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: router,
	}

	routerErr := make(chan error, 1)
	go func() {
		log.Printf("server is listening on: %s", server.Addr)
		routerErr <- server.ListenAndServe()
	}()

	select {
	case <-shutdownChannel:
		if err := server.Shutdown(context.Background()); err != nil {
			_ = server.Close()
		}
	case err := <-routerErr:
		log.Printf("server not listening: %v", err)
	}
}

type IDMaker struct {
}

func (I IDMaker) NextRequestID() string {
	return uuid.NewString()
}

// logger is a logger middleware.
func logger(handleFunc mux.HandleFunc) mux.HandleFunc {
	return func(w http.ResponseWriter, r *http.Request) error {
		state, err := mux.GetState(r.Context())
		if err != nil {
			return mux.NewShutdownError(err.Error())
		}

		log.Println("request started")
		defer func() {
			log.Printf("request completed: %v", time.Since(state.RequestCreated))
		}()

		return handleFunc(w, r)
	}
}

// auth is a fake auth middleware.
func auth(handleFunc mux.HandleFunc) mux.HandleFunc {
	return func(w http.ResponseWriter, r *http.Request) error {
		var body struct {
			Password string `json:"password"`
		}

		if err := mux.FromJSON(r.Body, &body); err != nil {
			return mux.NewShutdownError(err.Error())
		}

		if body.Password != "secret" {
			resp := struct {
				Message string `json:"message"`
			}{
				Message: "Invalid password",
			}

			return mux.ToJSON(r.Context(), w, &resp, http.StatusUnauthorized)
		}

		return handleFunc(w, r)
	}
}

Documentation

Overview

Package mux is a minimal http router that wraps the https://github.com/julienschmidt/httprouter. Example:

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/google/uuid"
	"github.com/josestg/mux"
)

func main() {

	requestIDMaker := new(IDMaker)
	shutdownChannel := make(chan os.Signal, 1)

	router := mux.NewRouter(
		requestIDMaker,
		shutdownChannel,
		logger,
	)

	router.Get("/check", func(w http.ResponseWriter, r *http.Request) error {
		status := map[string]string{
			"status": "running",
		}
		return mux.ToJSON(r.Context(), w, &status, http.StatusOK)
	})

	router.With(auth).Post("/auth", func(w http.ResponseWriter, r *http.Request) error {
		data := struct {
			Status string
		}{
			Status: "Authenticated",
		}

		return mux.ToJSON(r.Context(), w, &data, http.StatusOK)
	})

	server := http.Server{
		Addr:    "0.0.0.0:8080",
		Handler: router,
	}

	routerErr := make(chan error, 1)
	go func() {
		log.Printf("server is listening on: %s", server.Addr)
		routerErr <- server.ListenAndServe()
	}()

	select {
	case <-shutdownChannel:
		if err := server.Shutdown(context.Background()); err != nil {
			_ = server.Close()
		}
	case err := <-routerErr:
		log.Printf("server not listening: %v", err)
	}
}

type IDMaker struct {
}

func (I IDMaker) NextRequestID() string {
	return uuid.NewString()
}

// logger is a logger middleware.
func logger(handleFunc mux.HandleFunc) mux.HandleFunc {
	return func(w http.ResponseWriter, r *http.Request) error {
		state, err := mux.GetState(r.Context())
		if err != nil {
			return mux.NewShutdownError(err.Error())
		}

		log.Println("request started")
		defer func() {
			log.Printf("request completed: %v", time.Since(state.RequestCreated))
		}()

		return handleFunc(w, r)
	}
}

// auth is a fake auth middleware.
func auth(handleFunc mux.HandleFunc) mux.HandleFunc {
	return func(w http.ResponseWriter, r *http.Request) error {
		var body struct {
			Password string `json:"password"`
		}

		if err := mux.FromJSON(r.Body, &body); err != nil {
			return mux.NewShutdownError(err.Error())
		}

		if body.Password != "secret" {
			resp := struct {
				Message string `json:"message"`
			}{
				Message: "Invalid password",
			}

			return mux.ToJSON(r.Context(), w, &resp, http.StatusUnauthorized)
		}

		return handleFunc(w, r)
	}
}

Index

Constants

View Source
const StateKey = keyType(0)

StateKey is a key to stores and retrieves the State from the request context.

Variables

View Source
var ErrJSONDecoding = errors.New("mux: error decodes JSON")

ErrJSONDecoding is an error when decoding JSON

Functions

func FromJSON

func FromJSON(r io.Reader, v interface{}) error

FromJSON reads JSON-encoded data from the request body and stores it in the value pointed to by v

func IsShutdownError

func IsShutdownError(err error) bool

IsShutdownError returns true if the error is a shutdown error.

func NewShutdownError

func NewShutdownError(msg string) error

NewShutdownError creates a new shutdown error with message.

func ToJSON

func ToJSON(ctx context.Context, w http.ResponseWriter, data interface{}, status int) error

ToJSON encodes the given data to JSON and write it the given w.

Types

type HandleFunc

type HandleFunc func(w http.ResponseWriter, r *http.Request) error

HandleFunc is just a http.HandleFunc that can returns an error. By returning an error, now we can make a centralized error handling.

type Middleware

type Middleware func(handleFunc HandleFunc) HandleFunc

Middleware is a function that will be executed before or/and after the given handleFunc has been executed.

There are two types of middleware in this Router. First is the Global middlewares and the second is the Route middlewares. The Global middlewares will be applied to all handleFunc, meanwhile the Route middlewares only applied specific to the handleFunc in the given route.

type Params

type Params struct {
	// contains filtered or unexported fields
}

Params is a Param-slice, as returned by the router. The slice is ordered, the first URL parameter is also the first slice value. It is therefore safe to read values by the index.

func GetParam

func GetParam(ctx context.Context) *Params

GetParam pulls the URL parameters from a request context, or returns nil if none are present

func (*Params) ByName

func (pr *Params) ByName(name string) string

ByName returns the value of the first Param which key matches the given name. If no matching Param is found, an empty string is returned.

type RequestIDMaker

type RequestIDMaker interface {
	// NextRequestID returns a unique ID for the request.
	NextRequestID() string
}

RequestIDMaker knows how to generate a unique ID for each request. The generated request ID will be stored in the request state.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router is an 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.

This router wraps the https://github.com/julienschmidt/httprouter.

func NewRouter

func NewRouter(requestIDMaker RequestIDMaker, sc ShutdownChannel, middlewares ...Middleware) *Router

NewRouter creates a new mux router.

func (*Router) Delete

func (mr *Router) Delete(path string, handleFunc HandleFunc)

Delete is the syntactic sugar for Handle("DELETE", path, handleFunc)

func (*Router) Get

func (mr *Router) Get(path string, handleFunc HandleFunc)

Get is the syntactic sugar for Handle("GET", path, handleFunc)

func (*Router) Handle

func (mr *Router) Handle(method, path string, handleFunc HandleFunc)

Handle registers the handleFunc for the given HTTP method and URL path.

func (*Router) MiddlewareHandle

func (mr *Router) MiddlewareHandle(method, path string, handleFunc HandleFunc, middlewares []Middleware)

MiddlewareHandle registers the handleFunc with the Route middlewares for the given HTTP method and URL path.

func (*Router) Patch

func (mr *Router) Patch(path string, handleFunc HandleFunc)

Patch is the syntactic sugar for Handle("PATCH", path, handleFunc)

func (*Router) Post

func (mr *Router) Post(path string, handleFunc HandleFunc)

Post is the syntactic sugar for Handle("POST", path, handleFunc)

func (*Router) Put

func (mr *Router) Put(path string, handleFunc HandleFunc)

Put is the syntactic sugar for Handle("PUT", path, handleFunc)

func (*Router) ServeHTTP

func (mr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface.

func (*Router) SignalShutdown

func (mr *Router) SignalShutdown()

SignalShutdown sends a shutdown signal through the shutdown channel.

func (*Router) With

func (mr *Router) With(middleware Middleware, middlewares ...Middleware) *withMiddleware

With is a helper method to make Handle with middleware at the route level more easier to read. For example:

Without using With the Handle method looks like this:

router.MiddlewareHandle("GET", "/some-path", handleFunc, m1, m2, m3,..., mk)

By using With it will look like this:

router.With(m1, m2, m3, ..., mk).Handle("GET", "/some-path", handleFunc)

type ShutdownChannel

type ShutdownChannel chan os.Signal

ShutdownChannel is a channel that Mux used to tell the application to shutdown gracefully by sending a termination signal (syscall.SIGTERM).

type State

type State struct {
	RequestID      string
	RequestCreated time.Time
	StatusCode     int
}

State is the initial state for each request.

func GetState

func GetState(ctx context.Context) (*State, error)

GetState gets the initial state form the given context.

Jump to

Keyboard shortcuts

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