vestigo

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2019 License: MIT Imports: 8 Imported by: 111

README

Vestigo - A Standalone Golang URL Router

Build Status Coverage Status GoDoc

Abstract

Many fast Golang URL routers are often embedded inside frameworks. Vestigo is a stand alone url router which has respectable performance that passes URL parameters to handlers by embedding them into the request's Form.

There is such an abundance of parts and pieces that can be fit together for go web services, it seems like a shame to have a very fast URL router require the use of one framework, and one context model. This library aims to give the world a fast, and featureful URL router that can stand on it's own, without being forced into a particular web framework.

Design

  1. Radix Tree Based
  2. Attach URL Parameters into Request (PAT style) instead of context
  3. HTTP Compliance (TRACE, OPTIONS, HEAD)
  4. CORS Enabled (per resource access-controls)
TODOs for V1
  • Router functioning with a resource concept attached to leaf nodes
  • Use resources to drive responses to particular Methods (not found v not allowed)
  • Implement Resource and Globally scoped CORS preflights
  • Fix bug in router where handler.allowedMethods is getting populated where it shouldn't be
  • Validate with Tests RFC 2616 Compliance (OPTIONS, etc)
TODOs for V2
  • Validators for URL params
  • Implement RFC 6570 URI Parameters

Performance

Initial implementation on a fork of standard http performance testing library shows the following:

BenchmarkVestigo_GithubAll         20000             75763 ns/op            9280 B/op        339 allocs/op

I should mention that the above performance is about 2x slower than the fastest URL router I have tested (Echo/Gin), and is slightly worse than HTTPRouter, but I am happy with this performance considering this implementation is the fastest implementation that can handle standard http.HandlerFunc handlers, without forcing end users to use a particular context, or use a non-standard handler function, locking them into an implementation.

Examples

package main

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

	"github.com/husobee/vestigo"
)

func main() {
	router := vestigo.NewRouter()
	// you can enable trace by setting this to true
	vestigo.AllowTrace = true

	// Setting up router global  CORS policy
	// These policy guidelines are overriddable at a per resource level shown below
	router.SetGlobalCors(&vestigo.CorsAccessControl{
		AllowOrigin:      []string{"*", "test.com"},
		AllowCredentials: true,
		ExposeHeaders:    []string{"X-Header", "X-Y-Header"},
		MaxAge:           3600 * time.Second,
		AllowHeaders:     []string{"X-Header", "X-Y-Header"},
	})

	// setting two methods on the same resource
	router.Get("/welcome", GetWelcomeHandler)
	router.Post("/welcome", PostWelcomeHandler)

	// URL parameter "name"
	router.Post("/welcome/:name", PostWelcomeHandler)

	// Catch-All methods to allow easy migration from http.ServeMux
	router.HandleFunc("/general", GeneralHandler)

	// Below Applies Local CORS capabilities per Resource (both methods covered)
	// by default this will merge the "GlobalCors" settings with the resource
	// cors settings.  Without specifying the AllowMethods, the router will
	// accept any Request-Methods that have valid handlers associated
	router.SetCors("/welcome", &vestigo.CorsAccessControl{
		AllowMethods: []string{"GET"},                    // only allow cors for this resource on GET calls
		AllowHeaders: []string{"X-Header", "X-Z-Header"}, // Allow this one header for this resource
	})

	log.Fatal(http.ListenAndServe(":1234", router))
}

func PostWelcomeHandler(w http.ResponseWriter, r *http.Request) {
	name := vestigo.Param(r, "name") // url params live in the request
	w.WriteHeader(200)
	w.Write([]byte("welcome " + name + "!"))
}

func GetWelcomeHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("welcome!"))
}

func GeneralHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("Gotta catch em all!"))
}

Middleware

Router helper methods (Get, Post, ...) support optional middleware (vestigo provides only middleware type, it is up to the user to create one).

router.Get("/welcome", GetWelcomeHandler, someMiddleware)

someMiddleware := func(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// before
		f(w, r)
		// after
	}
}

To break the chain (for example in case of authentication middleware, we don't want to continue execution), just do not call passed handler function. Example:

auth := func(f http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if authSuccessful() {
			f(w, r)
		}
	}
}

App Performance with net/http/pprof

It is often very helpful to view profiling information from your web application. Below is an example of hooking up net/http/pprof with vestigo serving the routes:

// Load the routes.
func Load(router *vestigo.Router) {
	router.Get("/debug/pprof/", Index)
	router.Get("/debug/pprof/:pprof", Profile)
}

// Index shows the profile index.
func Index(w http.ResponseWriter, r *http.Request) {
	pprof.Index(w, r)
}

// Profile shows the individual profiles.
func Profile(w http.ResponseWriter, r *http.Request) {
	switch vestigo.Param(r, "pprof") {
	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:
		Index(w, r)
	}
}

Note on wildcards: if you want to get the actual path matched by the wildcard you can perform vestigo.Param(*http.Request, "_name") to get the matched path, example below:

router.Get("/*", func(w http.ResponseWriter, r *http.Request) {
	fmt.Println(vestigo.Param(r, "_name"))
})

Licensing

Contributing

If you wish to contribute, please fork this repository, submit an issue, or pull request with your suggestions. Please use gofmt and golint before trying to contribute.

Documentation

Overview

Package vestigo implements a performant, stand-alone, HTTP compliant URL Router for go web applications. Vestigo utilizes a simple radix trie for url route indexing and search, and puts any URL parameters found in a request in the request's Form, much like PAT. Vestigo boasts standards compliance regarding the proper behavior when methods are not allowed on a given resource as well as when a resource isn't found. vestigo also includes built in CORS support on a global and per resource capability.

Index

Constants

This section is empty.

Variables

View Source
var AllowTrace = false

AllowTrace - Globally allow the TRACE method handling within vestigo url router. This generally not a good idea to have true in production settings, but excellent for testing.

Functions

func AddParam added in v1.0.2

func AddParam(r *http.Request, name, value string)

AddParam - Add a vestigo-style parameter to the request -- useful for middleware Appends :name=value onto a blank request query string or appends &:name=value onto a non-blank request query string

func CustomMethodNotAllowedHandlerFunc added in v1.0.2

func CustomMethodNotAllowedHandlerFunc(f MethodNotAllowedHandlerFunc)

CustomMethodNotAllowedHandlerFunc - This function will allow the caller to set vestigo's methodNotAllowedHandler. This function needs to return an http.Handlerfunc and take in a formatted string of methods that ARE allowed. Follow the convention for methodNotAllowedHandler. Note that if you overwrite you will be responsible for making sure the allowed methods are put into headers

func CustomNotFoundHandlerFunc added in v1.0.2

func CustomNotFoundHandlerFunc(f http.HandlerFunc)

CustomNotFoundHandlerFunc - Specify a Handlerfunc to use for a custom NotFound Handler. Can only be performed once.

func Param

func Param(r *http.Request, name string) string

Param - Get a url parameter by name

func ParamNames

func ParamNames(r *http.Request) []string

ParamNames - Get a url parameter name list with the leading :

func TrimmedParamNames added in v1.0.2

func TrimmedParamNames(r *http.Request) []string

TrimmedParamNames - Get a url parameter name list without the leading :

Types

type CorsAccessControl

type CorsAccessControl struct {
	AllowOrigin      []string
	AllowCredentials bool
	ExposeHeaders    []string
	MaxAge           time.Duration
	AllowMethods     []string
	AllowHeaders     []string
}

CorsAccessControl - Default implementation of Cors

func (*CorsAccessControl) GetAllowCredentials

func (c *CorsAccessControl) GetAllowCredentials() bool

GetAllowCredentials - returns the allow-credentials string representation

func (*CorsAccessControl) GetAllowHeaders

func (c *CorsAccessControl) GetAllowHeaders() []string

GetAllowHeaders - returns the allow-headers string representation

func (*CorsAccessControl) GetAllowMethods

func (c *CorsAccessControl) GetAllowMethods() []string

GetAllowMethods - returns the allow-methods string representation

func (*CorsAccessControl) GetAllowOrigin

func (c *CorsAccessControl) GetAllowOrigin() []string

GetAllowOrigin - returns the allow-origin string representation

func (*CorsAccessControl) GetExposeHeaders

func (c *CorsAccessControl) GetExposeHeaders() []string

GetExposeHeaders - returns the expose-headers string representation

func (*CorsAccessControl) GetMaxAge

func (c *CorsAccessControl) GetMaxAge() time.Duration

GetMaxAge - returns the max-age string representation

func (*CorsAccessControl) Merge

Merge - Merge the values of one CORS policy into 'this' one

type MethodNotAllowedHandlerFunc added in v1.0.2

type MethodNotAllowedHandlerFunc func(string) func(w http.ResponseWriter, r *http.Request)

type Middleware added in v1.1.0

type Middleware func(http.HandlerFunc) http.HandlerFunc

middleware takes in HandlerFunc (which can be another middleware or handler) and wraps it within another one

type Router

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

Router - The main vestigo router data structure

func NewRouter

func NewRouter() *Router

NewRouter - Create a new vestigo router

func (*Router) Add

func (r *Router) Add(method, path string, h http.HandlerFunc, middleware ...Middleware)

Add - Add a method/handler combination to the router

func (*Router) Connect

func (r *Router) Connect(path string, handler http.HandlerFunc, middleware ...Middleware)

Connect - Helper method to add HTTP CONNECT Method to router

func (*Router) Delete

func (r *Router) Delete(path string, handler http.HandlerFunc, middleware ...Middleware)

Delete - Helper method to add HTTP DELETE Method to router

func (*Router) Find

func (r *Router) Find(req *http.Request) (h http.HandlerFunc)

Find - Find A route within the router tree

func (*Router) Get

func (r *Router) Get(path string, handler http.HandlerFunc, middleware ...Middleware)

Get - Helper method to add HTTP GET Method to router

func (*Router) GetMatchedPathTemplate added in v1.0.2

func (r *Router) GetMatchedPathTemplate(req *http.Request) string

GetMatchedPathTemplate - get the path template from the url in the request

func (*Router) Handle added in v1.0.2

func (r *Router) Handle(path string, handler http.Handler, middleware ...Middleware)

Handle - Helper method to add all HTTP Methods to router

func (*Router) HandleFunc added in v1.0.2

func (r *Router) HandleFunc(path string, handler http.HandlerFunc, middleware ...Middleware)

HandleFunc - Helper method to add all HTTP Methods to router

func (*Router) Patch

func (r *Router) Patch(path string, handler http.HandlerFunc, middleware ...Middleware)

Patch - Helper method to add HTTP PATCH Method to router

func (*Router) Post

func (r *Router) Post(path string, handler http.HandlerFunc, middleware ...Middleware)

Post - Helper method to add HTTP POST Method to router

func (*Router) Put

func (r *Router) Put(path string, handler http.HandlerFunc, middleware ...Middleware)

Put - Helper method to add HTTP PUT Method to router

func (*Router) ServeHTTP

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

ServeHTTP - implementation of a http.Handler, making Router a http.Handler

func (*Router) SetCors

func (r *Router) SetCors(path string, c *CorsAccessControl)

SetCors - Set per resource Cors Policy. The CorsAccessControl policy passed in will map to the policy that is validated against the "path" resource. This policy will be merged with the global policy, and values will be deduplicated if there are overlaps.

func (*Router) SetGlobalCors

func (r *Router) SetGlobalCors(c *CorsAccessControl)

SetGlobalCors - Settings for Global Cors Options. This takes a *CorsAccessControl policy, and will apply said policy to every resource. If this is not set on the router, CORS functionality is turned off.

func (*Router) Trace

func (r *Router) Trace(path string, handler http.HandlerFunc, middleware ...Middleware)

Trace - Helper method to add HTTP TRACE Method to router

Jump to

Keyboard shortcuts

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