Pure is a fast radix-tree based HTTP router

Overview

package pure

Project status Build Status Coverage Status Go Report Card GoDoc License Gitter

Pure is a fast radix-tree based HTTP router that sticks to the native implementations of Go's "net/http" package; in essence, keeping the handler implementations 'pure' by using Go 1.7's "context" package.

This makes heavy usage of github.com/go-playground/pkg/v5 for HTTP abstractions.

Why Another HTTP Router?

I initially created lars, which I still maintain, that wraps the native implementation, think of this package as a Go pure implementation of lars

Key & Unique Features

  • It sticks to Go's native implementations while providing helper functions for convenience
  • Fast & Efficient - pure uses a custom version of httprouter's radix tree, so incredibly fast and efficient.

Installation

Use go get

go get -u github.com/go-playground/pure/v5

Usage

package main

import (
	"net/http"

	"github.com/go-playground/pure/v5"
	mw "github.com/go-playground/pure/v5/_examples/middleware/logging-recovery"
)

func main() {

	p := pure.New()
	p.Use(mw.LoggingAndRecovery(true))

	p.Get("/", helloWorld)

	http.ListenAndServe(":3007", p.Serve())
}

func helloWorld(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World"))
}

RequestVars

This is an interface that is used to pass request scoped variables and functions using context.Context. It is implemented in this way because retrieving values from context isn't the fastest, and so using this the router can store multiple pieces of information while reducing lookup time to a single stored RequestVars.

Currently only the URL/SEO params are stored on the RequestVars but if/when more is added they can merely be added to the RequestVars and there will be no additional lookup time.

URL Params

p := p.New()

// the matching param will be stored in the context's params with name "id"
p.Get("/user/:id", UserHandler)

// extract params like so
rv := pure.RequestVars(r) // done this way so only have to extract from context once, read above
rv.URLParam(paramname)

// serve css, js etc.. pure.RequestVars(r).URLParam(pure.WildcardParam) will return the remaining path if 
// you need to use it in a custom handler...
p.Get("/static/*", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))).ServeHTTP)

...

Note: Since this router has only explicit matches, you can not register static routes and parameters for the same path segment. For example you can not register the patterns /user/new and /user/:user for the same request method at the same time. The routing of different request methods is independent from each other. I was initially against this, however it nearly cost me in a large web application where the dynamic param value say :type actually could have matched another static route and that's just too dangerous and so it is not allowed.

Groups

p.Use(LoggingAndRecovery, Gzip...)
...
p.Post("/users/add", ...)

// creates a group for /user/:userid + inherits all middleware registered previously by p
user := p.Group("/user/:userid")
user.Get("", ...)
user.Post("", ...)
user.Delete("/delete", ...)

contactInfo := user.Group("/contact-info/:cid")
contactinfo.Delete("/delete", ...)

// creates a group for /others, inherits all middleware registered previously by p + adds 
// OtherHandler to middleware
others := p.GroupWithMore("/others", OtherHandler)

// creates a group for /admin WITH NO MIDDLEWARE... more can be added using admin.Use()
admin := p.GroupWithNone("/admin")
admin.Use(SomeAdminSecurityMiddleware)
...

Decoding Body

currently JSON, XML, FORM, Multipart Form and url.Values are support out of the box; there are also individual functions for each as well when you know the Content-Type.

	// second argument denotes yes or no I would like URL query parameter fields
	// to be included. i.e. 'id' and 'id2' in route '/user/:id?id2=val' should it be included.
	if err := pure.Decode(r, true, maxBytes, &user); err != nil {
		log.Println(err)
	}

Misc

// set custom 404 ( not Found ) handler
p.Register404(404Handler, middleware_like_logging)

// Redirect to or from ending slash if route not found, default is true
p.SetRedirectTrailingSlash(true)

// Handle 405 ( Method Not allowed ), default is false
p.RegisterMethodNotAllowed(middleware)

// automatically handle OPTION requests; manually configured
// OPTION handlers take precedence. default false
p.RegisterAutomaticOPTIONS(middleware)

Middleware

There are some pre-defined middlewares within the middleware folder; NOTE: that the middleware inside will comply with the following rule(s):

  • Are completely reusable by the community without modification

Other middleware will be listed under the _examples/middleware/... folder for a quick copy/paste modify. As an example a LoddingAndRecovery middleware is very application dependent and therefore will be listed under the _examples/middleware/...

Benchmarks

Run on i5-7600 16 GB DDR4-2400 using Go version go1.12.5 darwin/amd64

NOTICE: pure uses a custom version of httprouter's radix tree, benchmarks can be found here the slowdown is with the use of the context package, as you can see when no SEO params are defined, and therefore no need to store anything in the context, it is faster than even lars.

go test -bench=. -benchmem=true
#GithubAPI Routes: 203
   Pure: 37096 Bytes

#GPlusAPI Routes: 13
   Pure: 2792 Bytes

#ParseAPI Routes: 26
   Pure: 5040 Bytes

#Static Routes: 157
   HttpServeMux: 14992 Bytes
   Pure: 21096 Bytes


BenchmarkPure_Param             10000000               184 ns/op             384 B/op          2 allocs/op
BenchmarkPure_Param5            10000000               236 ns/op             384 B/op          2 allocs/op
BenchmarkPure_Param20            5000000               393 ns/op             384 B/op          2 allocs/op
BenchmarkPure_ParamWrite         5000000               240 ns/op             384 B/op          2 allocs/op
BenchmarkPure_GithubStatic      50000000                36.2 ns/op             0 B/op          0 allocs/op
BenchmarkPureGithubParam        10000000               230 ns/op             384 B/op          2 allocs/op
BenchmarkPure_GithubAll            30000             43887 ns/op           64130 B/op        334 allocs/op
BenchmarkPure_GPlusStatic       50000000                22.8 ns/op             0 B/op          0 allocs/op
BenchmarkPure_GPlusParam        10000000               192 ns/op             384 B/op          2 allocs/op
BenchmarkPure_GPlus2Params      10000000               211 ns/op             384 B/op          2 allocs/op
BenchmarkPure_GPlusAll            500000              2457 ns/op            4224 B/op         22 allocs/op
BenchmarkPure_ParseStatic       100000000               23.7 ns/op             0 B/op          0 allocs/op
BenchmarkPure_ParseParam        10000000               177 ns/op             384 B/op          2 allocs/op
BenchmarkPure_Parse2Params      10000000               193 ns/op             384 B/op          2 allocs/op
BenchmarkPure_ParseAll            500000              3751 ns/op            6144 B/op         32 allocs/op
BenchmarkPure_StaticAll           200000              8574 ns/op               0 B/op          0 allocs/op

Package Versioning

I'm jumping on the vendoring bandwagon, you should vendor this package as I will not be creating different version with gopkg.in like allot of my other libraries.

Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE, it is so freeing not to worry about it and will help me keep pouring out bigger and better things for you the community.

I am open versioning with gopkg.in should anyone request it, but this should be stable going forward.

Licenses

  • MIT License (MIT), Copyright (c) 2016 Dean Karn
  • BSD License, Copyright (c) 2013 Julien Schmidt. All rights reserved.
Comments
  • _examples/ vs examples/

    _examples/ vs examples/

    Some your other packages (in other repos) try to import using examples/ path instead of _examples/ - this breaks things like go install all which process all packages, including ones in examples directories.

    opened by powerman 3
  • Can middleware share info?

    Can middleware share info?

    Hi Dean,

    How would a middleware function (e.g. JWT auth) share data (a JWT struct) to all the remaining middleware functions in the chain? RequestVars seems to be read-only.

    opened by nkev 3
  •  /user/100/  match  p.Get(

    /user/100/ match p.Get("/user/:id"

    package main
    
    import (
    	"net/http"
    
    	"github.com/go-playground/pure/v5"
    	mw "github.com/go-playground/pure/v5/_examples/middleware/logging-recovery"
    )
    
    func main() {
    
    	p := pure.New()
    	p.Use(mw.LoggingAndRecovery(true))
    
    	p.Get("/user/:id", helloWorld)
    
    	http.ListenAndServe(":3007", p.Serve())
    }
    
    func helloWorld(w http.ResponseWriter, r *http.Request) {
    	w.Write([]byte("Hello World"))
    }
    

    http://localhost:3007/user/ 404 http://localhost:3007/user/100 right http://localhost:3007/user/100/ expect 404 but result same as /user/100

    opened by tablecell 1
  • Updated

    Updated

    • Updated to use Go Modules
    • Updated to use github.com/go-playground/pkg for HTTP abstractions. pure now also relies on the github.com/go-playground/pkg/net/http.DefaultFormDecoder; it is configurable.
    opened by deankarn 1
  • static file syntax

    static file syntax

    I have a root folder /static with subfolders for js, css, etc. The code:

    p.Get("/static/*", http.FileServer(http.Dir("static/")).ServeHTTP)
    

    ...gives me a 404 in the browser even though the paths in the browser are correct (e.g. http://localhost:5000/js/plugins/prism.js)

    I would like the URL in the browser to remain http://localhost:5000/js/plugins/prism.js What should the code look like?

    opened by nkev 1
  • Group function update

    Group function update

    What Changed?

    • It used to be that the Group function handled 3 different scenarios:
    1. .Group("/users") - group with existing middleware
    2. .Group("/users", nil) - group but retain no middleware
    3. .Group("/users", MoreMiddleware) - group, retain existing and add MoreMiddleware

    This was confusing and even I was always referring to the function to remember what it all did so here is how it works now.

    1. .Group("/users") - group with existing middleware
    2. .GroupWithNone("/users") - group but retain no middleware.
    3. .GroupWithMore("/users", MoreMiddleware) - group, retain existing and add MoreMiddleware
    opened by deankarn 0
Releases(v5.2.0)
  • v5.2.0(Nov 19, 2020)

  • v5.1.0(Jul 2, 2020)

    What's new?

    • Updated to latest github.com/go-playground/pkg/v5
    • Added EncodeToURLValues helper
    • Updated to use GitHub Actions for CI
    • update godoc link to new site.
    Source code(tar.gz)
    Source code(zip)
  • v5.0.5(Nov 11, 2019)

  • v5.0.4(Nov 11, 2019)

  • v5.0.3(May 22, 2019)

  • v5.0.2(May 13, 2019)

  • v5.0.1(May 13, 2019)

  • v5.0.0(May 11, 2019)

  • v4.2.0(May 10, 2019)

  • v4.1.1(Jun 25, 2017)

    What was fixed?

    • Corrected bug in Group logic whereby the top level middleware was being copied, rather than the RouteGroup's middleware (which may be the top level also and why it wasn't found until now); it only became apparent will multiple levels of Grouping.
    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Apr 20, 2017)

    What was added?

    • Added DecodeSEOQueryParams(...) function to allow only the SEO params to be decoded.
    • Cleaned up some spelling
    • Cleaned up a few inefassign
    • Change to Travis CI from Semaphore
    Source code(tar.gz)
    Source code(zip)
  • v4.0.1(Feb 13, 2017)

  • v4.0.0(Feb 8, 2017)

    What Changed?

    • It used to be that the Group function handled 3 different scenarios:
    • .Group("/users") - group with existing middleware
    • .Group("/users", nil) - group but retain no middleware
    • .Group("/users", MoreMiddleware) - group, retain existing and add MoreMiddleware

    This was confusing and even I was always referring to the function to remember what it all did so here is how it works now.

    1. .Group("/users") - group with existing middleware
    2. .GroupWithNone("/users") - group but retain no middleware.
    3. .GroupWithMore("/users", MoreMiddleware) - group, retain existing and add MoreMiddleware
    Source code(tar.gz)
    Source code(zip)
  • v3.1.1(Jan 17, 2017)

  • v3.1.0(Jan 17, 2017)

    What was added?

    • Add/separate DecodeJSON, DecodeXML, DecodeForm and DecodeMultipartForm functions into the helper functions for when you already know the Content-Type
    Source code(tar.gz)
    Source code(zip)
  • v3.0.1(Oct 24, 2016)

  • v3.0.0(Oct 23, 2016)

    What changed?

    • removed QueryParams function from RequestVars, storing a single copy if requested multiple times really wasn't much faster because of the context.Context lookup time so removed.

    What was added?

    • added DecodeQueryParams(...) function to explicitly decode url.Values only if desired
    • added QueryParams(...) function to allow retrieving of r.URL.Query() values and optionally SEO params.
    • updated Decode(...) so the the includeQueryParams flags also applies to JSON and XML
    Source code(tar.gz)
    Source code(zip)
  • v2.6.0(Oct 17, 2016)

    What's new

    • reconfigured pure's serveHTTP function to only incur the overhead of request vars, when one is found in the route itself, which increases performance for non parameter/static routes like those used in an API vs SEO website.
    
    benchmark                      old ns/op     new ns/op     delta
    BenchmarkPure_Param            157           161           +2.55%
    BenchmarkPure_Param5           208           205           -1.44%
    BenchmarkPure_Param20          350           356           +1.71%
    BenchmarkPure_ParamWrite       221           215           -2.71%
    BenchmarkPure_GithubStatic     72.6          47.1          -35.12%
    BenchmarkPure_GithubParam      230           227           -1.30%
    BenchmarkPure_GithubAll        43054         42284         -1.79%
    BenchmarkPure_GPlusStatic      54.0          31.7          -41.30%
    BenchmarkPure_GPlusParam       182           178           -2.20%
    BenchmarkPure_GPlus2Params     207           193           -6.76%
    BenchmarkPure_GPlusAll         2297          2159          -6.01%
    BenchmarkPure_ParseStatic      56.2          30.9          -45.02%
    BenchmarkPure_ParseParam       166           159           -4.22%
    BenchmarkPure_Parse2Params     180           174           -3.33%
    BenchmarkPure_ParseAll         3671          3290          -10.38%
    BenchmarkPure_StaticAll        14646         9964          -31.97%
    
    benchmark                      old allocs     new allocs     delta
    BenchmarkPure_Param            1              1              +0.00%
    BenchmarkPure_Param5           1              1              +0.00%
    BenchmarkPure_Param20          1              1              +0.00%
    BenchmarkPure_ParamWrite       1              1              +0.00%
    BenchmarkPure_GithubStatic     0              0              +0.00%
    BenchmarkPure_GithubParam      1              1              +0.00%
    BenchmarkPure_GithubAll        167            167            +0.00%
    BenchmarkPure_GPlusStatic      0              0              +0.00%
    BenchmarkPure_GPlusParam       1              1              +0.00%
    BenchmarkPure_GPlus2Params     1              1              +0.00%
    BenchmarkPure_GPlusAll         11             11             +0.00%
    BenchmarkPure_ParseStatic      0              0              +0.00%
    BenchmarkPure_ParseParam       1              1              +0.00%
    BenchmarkPure_Parse2Params     1              1              +0.00%
    BenchmarkPure_ParseAll         16             16             +0.00%
    BenchmarkPure_StaticAll        0              0              +0.00%
    
    benchmark                      old bytes     new bytes     delta
    BenchmarkPure_Param            240           240           +0.00%
    BenchmarkPure_Param5           240           240           +0.00%
    BenchmarkPure_Param20          240           240           +0.00%
    BenchmarkPure_ParamWrite       240           240           +0.00%
    BenchmarkPure_GithubStatic     0             0             +0.00%
    BenchmarkPure_GithubParam      240           240           +0.00%
    BenchmarkPure_GithubAll        40082         40082         +0.00%
    BenchmarkPure_GPlusStatic      0             0             +0.00%
    BenchmarkPure_GPlusParam       240           240           +0.00%
    BenchmarkPure_GPlus2Params     240           240           +0.00%
    BenchmarkPure_GPlusAll         2640          2640          +0.00%
    BenchmarkPure_ParseStatic      0             0             +0.00%
    BenchmarkPure_ParseParam       240           240           +0.00%
    BenchmarkPure_Parse2Params     240           240           +0.00%
    BenchmarkPure_ParseAll         3840          3840          +0.00%
    BenchmarkPure_StaticAll        0             0             +0.00%
    
    
    Source code(tar.gz)
    Source code(zip)
  • v2.5.0(Oct 14, 2016)

  • v2.4.0(Oct 2, 2016)

    What's new?

    • Changed to reuse context.Context objects for efficiency, effectively the same except not created each request.
    • change some core logic to use maps instead of switch statements, not so much for performance, but much better code readability and maintainability.
    Source code(tar.gz)
    Source code(zip)
  • v2.3.0(Sep 29, 2016)

  • v2.2.1(Sep 27, 2016)

  • v2.2.0(Sep 27, 2016)

  • v2.1.0(Sep 26, 2016)

    What's new?

    Added another case for Decode(..) function for if you want to parse query params, including SEO params, for a GET request it will decode the r.URL.Query() params and any SEO query params.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Sep 26, 2016)

    What's changed?

    • Change the Pure struct name to Mux to avoid stutter; pure.Pure stutters pure.Mux doesn't

    although no other changes were made, had to increment Major to 2 to follow semantic versioning rules.

    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Sep 26, 2016)

    What's new?

    Added new helper functions

    1. Add ParseForm(...) & ParseMultiPartForm(...) functions that also add the SEO params to the parsed Form eg. /users/:id, id will be added to the request.Form just like normal query params would be.
    2. Modify Decode(...) function to use the new #1 functions when includeQueryParams=true
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Sep 23, 2016)

    Initial Release

    Hello All,

    Pure is a fast radix-tree based HTTP router that sticks to the native implementations of Go's "net/http" package.

    It remains pure by sticking to Go's http handler definitions and passing only scoped data through the new context package in Go 1.7

    this of this as a pure version of lars

    Source code(tar.gz)
    Source code(zip)
Owner
Go Playgound
multiple packages, libraries and programs to further the advancement of Go!
Go Playgound
Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer).

Bxog is a simple and fast HTTP router for Go (HTTP request multiplexer). Usage An example of using the multiplexer: package main import ( "io" "net

Eduard 106 Dec 26, 2022
FastRouter is a fast, flexible HTTP router written in Go.

FastRouter FastRouter is a fast, flexible HTTP router written in Go. FastRouter contains some customizable options, such as TrailingSlashesPolicy, Pan

Razon Yang 22 Sep 27, 2022
:rotating_light: Is a lightweight, fast and extensible zero allocation HTTP router for Go used to create customizable frameworks.

LARS LARS is a fast radix-tree based, zero allocation, HTTP router for Go. view examples. If looking for a more pure Go solution, be sure to check out

Go Playgound 388 Dec 27, 2022
An extremely fast Go (golang) HTTP router that supports regular expression route matching. Comes with full support for building RESTful APIs.

ozzo-routing You may consider using go-rest-api to jumpstart your new RESTful applications with ozzo-routing. Description ozzo-routing is a Go package

Ozzo Framework 447 Dec 31, 2022
xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework.

gorouter xujiajun/gorouter is a simple and fast HTTP router for Go. It is easy to build RESTful APIs and your web framework. Motivation I wanted a sim

徐佳军 533 Dec 8, 2022
Fast and flexible HTTP router

treemux - fast and flexible HTTP router Basic example Debug logging CORS example Error handling Rate limiting using Redis Gzip compression OpenTelemet

Vladimir Mihailenco 534 Dec 27, 2022
Fast, simple, and lightweight HTTP router for Golang

Sariaf Fast, simple and lightweight HTTP router for golang Install go get -u github.com/majidsajadi/sariaf Features Lightweight compatible with net/ht

defectivepixel 33 Aug 19, 2022
Simple Golang HTTP router

Bellt Simple Golang HTTP router Bellt Package implements a request router with the aim of managing controller actions based on fixed and parameterized

Guilherme Caruso 55 Sep 27, 2022
Go Server/API micro framework, HTTP request router, multiplexer, mux

?? gorouter Go Server/API micro framework, HTTP request router, multiplexer, mux. ?? ABOUT Contributors: Rafał Lorenz Want to contribute ? Feel free t

Rafał Lorenz 143 Dec 16, 2022
A high performance HTTP request router that scales well

HttpRouter HttpRouter is a lightweight high performance HTTP request router (also called multiplexer or just mux for short) for Go. In contrast to the

Julien Schmidt 14.8k Dec 28, 2022
A powerful HTTP router and URL matcher for building Go web servers with 🦍

gorilla/mux https://www.gorillatoolkit.org/pkg/mux Package gorilla/mux implements a request router and dispatcher for matching incoming requests to th

Gorilla Web Toolkit 18k Jan 9, 2023
Go HTTP router

violetear Go HTTP router http://violetear.org Design Goals Keep it simple and small, avoiding extra complexity at all cost. KISS Support for static an

Nicolas Embriz 106 Dec 10, 2022
lightweight, idiomatic and composable router for building Go HTTP services

chi is a lightweight, idiomatic and composable router for building Go HTTP services. It's especially good at helping you write large REST API services

go-chi 13k Jan 8, 2023
:tongue: CleverGo is a lightweight, feature rich and high performance HTTP router for Go.

CleverGo CleverGo is a lightweight, feature rich and trie based high performance HTTP request router. go get -u clevergo.tech/clevergo English 简体中文 Fe

CleverGo Web Framework 248 Nov 17, 2022
Simple router build on `net/http` supports custom middleWare.

XMUS-ROUTER Fast lightweight router build on net/http supports delegate and in url params. usage : Create new router using NewRouter() which need rout

amupxm [amir hossein mokarrami far] 5 Dec 27, 2021
Simple HTTP router for Go

ngamux Simple HTTP router for Go Installation Examples Todo Installation Run this command with correctly configured Go toolchain. go get github.com/ng

null 55 Dec 13, 2022
Lightweight Router for Golang using net/http standard library with custom route parsing, handler and context.

Go-Lightweight-Router Lightweight Router for Golang using net/http standard library with custom route parsing, handler and context. Further developmen

null 0 Nov 3, 2021
A high performance HTTP request router that scales well

HttpRouter HttpRouter is a lightweight high performance HTTP request router (also called multiplexer or just mux for short) for Go. In contrast to the

Julien Schmidt 13.5k Dec 9, 2021
a small wrapper around go-chi HTTP router.

sigma a small wrapper around go-chi HTTP router. NOTE: This package is provided "as is" with no guarantee. Use it at your own risk and always test it

Iss Meftah 3 May 14, 2022