initial commit

This commit is contained in:
2017-03-30 01:09:55 +01:00
commit ce38f0ec96
134 changed files with 34785 additions and 0 deletions

22
vendor/github.com/qiangxue/fasthttp-routing/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The BSD 3-Clause License
Copyright (c) 2016, Qiang Xue
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

244
vendor/github.com/qiangxue/fasthttp-routing/README.md generated vendored Normal file
View File

@@ -0,0 +1,244 @@
# fasthttp-routing
[![GoDoc](https://godoc.org/github.com/qiangxue/fasthttp-routing?status.png)](http://godoc.org/github.com/qiangxue/fasthttp-routing)
[![Go Report](http://goreportcard.com/badge/qiangxue/fasthttp-routing)](http://goreportcard.com/report/qiangxue/fasthttp-routing)
## Description
fasthttp-routing is a Go package that is adapted from [ozzo-routing](https://github.com/go-ozzo/ozzo-routing) to provide
fast and powerful routing features for the high-performance [fasthttp](https://github.com/valyala/fasthttp) server.
The package has the following features:
* middleware pipeline architecture, similar to that of the [Express framework](http://expressjs.com).
* extremely fast request routing with zero dynamic memory allocation
* modular code organization through route grouping
* flexible URL path matching, supporting URL parameters and regular expressions
* URL creation according to the predefined routes
## Requirements
Go 1.5 or above.
## Installation
Run the following command to install the package:
```
go get github.com/qiangxue/fasthttp-routing
```
## Getting Started
Create a `server.go` file with the following content:
```go
package main
import (
"fmt"
"github.com/qiangxue/fasthttp-routing"
"github.com/valyala/fasthttp"
)
func main() {
router := routing.New()
router.Get("/", func(c *routing.Context) error {
fmt.Fprintf(c, "Hello, world!")
return nil
})
panic(fasthttp.ListenAndServe(":8080", router.HandleRequest))
}
```
Now run the following command to start the Web server:
```
go run server.go
```
You should be able to access URLs such as `http://localhost:8080`.
### Routes
ozzo-routing works by building a routing table in a router and then dispatching HTTP requests to the matching handlers
found in the routing table. An intuitive illustration of a routing table is as follows:
Routes | Handlers
--------------------|-----------------
`GET /users` | m1, m2, h1, ...
`POST /users` | m1, m2, h2, ...
`PUT /users/<id>` | m1, m2, h3, ...
`DELETE /users/<id>`| m1, m2, h4, ...
For an incoming request `GET /users`, the first route would match and the handlers m1, m2, and h1 would be executed.
If the request is `PUT /users/123`, the third route would match and the corresponding handlers would be executed.
Note that the token `<id>` can match any number of non-slash characters and the matching part can be accessed as
a path parameter value in the handlers.
**If an incoming request matches multiple routes in the table, the route added first to the table will take precedence.
All other matching routes will be ignored.**
The actual implementation of the routing table uses a variant of the radix tree data structure, which makes the routing
process as fast as working with a hash table, thanks to the inspiration from [httprouter](https://github.com/julienschmidt/httprouter).
To add a new route and its handlers to the routing table, call the `To` method like the following:
```go
router := routing.New()
router.To("GET", "/users", m1, m2, h1)
router.To("POST", "/users", m1, m2, h2)
```
You can also use shortcut methods, such as `Get`, `Post`, `Put`, etc., which are named after the HTTP method names:
```go
router.Get("/users", m1, m2, h1)
router.Post("/users", m1, m2, h2)
```
If you have multiple routes with the same URL path but different HTTP methods, like the above example, you can
chain them together as follows,
```go
router.Get("/users", m1, m2, h1).Post(m1, m2, h2)
```
If you want to use the same set of handlers to handle the same URL path but different HTTP methods, you can take
the following shortcut:
```go
router.To("GET,POST", "/users", m1, m2, h)
```
A route may contain parameter tokens which are in the format of `<name:pattern>`, where `name` stands for the parameter
name, and `pattern` is a regular expression which the parameter value should match. A token `<name>` is equivalent
to `<name:[^/]*>`, i.e., it matches any number of non-slash characters. At the end of a route, an asterisk character
can be used to match any number of arbitrary characters. Below are some examples:
* `/users/<username>`: matches `/users/admin`
* `/users/accnt-<id:\d+>`: matches `/users/accnt-123`, but not `/users/accnt-admin`
* `/users/<username>/*`: matches `/users/admin/profile/address`
When a URL path matches a route, the matching parameters on the URL path can be accessed via `Context.Param()`:
```go
router := routing.New()
router.Get("/users/<username>", func (c *routing.Context) error {
fmt.Fprintf(c, "Name: %v", c.Param("username"))
return nil
})
```
### Route Groups
Route group is a way of grouping together the routes which have the same route prefix. The routes in a group also
share the same handlers that are registered with the group via its `Use` method. For example,
```go
router := routing.New()
api := router.Group("/api")
api.Use(m1, m2)
api.Get("/users", h1).Post(h2)
api.Put("/users/<id>", h3).Delete(h4)
```
The above `/api` route group establishes the following routing table:
Routes | Handlers
------------------------|-------------
`GET /api/users` | m1, m2, h1, ...
`POST /api/users` | m1, m2, h2, ...
`PUT /api/users/<id>` | m1, m2, h3, ...
`DELETE /api/users/<id>`| m1, m2, h4, ...
As you can see, all these routes have the same route prefix `/api` and the handlers `m1` and `m2`. In other similar
routing frameworks, the handlers registered with a route group are also called *middlewares*.
Route groups can be nested. That is, a route group can create a child group by calling the `Group()` method. The router
serves as the top level route group. A child group inherits the handlers registered with its parent group. For example,
```go
router := routing.New()
router.Use(m1)
api := router.Group("/api")
api.Use(m2)
users := group.Group("/users")
users.Use(m3)
users.Put("/<id>", h1)
```
Because the router serves as the parent of the `api` group which is the parent of the `users` group,
the `PUT /api/users/<id>` route is associated with the handlers `m1`, `m2`, `m3`, and `h1`.
### Router
Router manages the routing table and dispatches incoming requests to appropriate handlers. A router instance is created
by calling the `routing.New()` method.
To hook up router with fasthttp, use the following code:
```go
router := routing.New()
fasthttp.ListenAndServe(":8080", router.HandleRequest)
```
### Handlers
A handler is a function with the signature `func(*routing.Context) error`. A handler is executed by the router if
the incoming request URL path matches the route that the handler is associated with. Through the `routing.Context`
parameter, you can access the request information in handlers.
A route may be associated with multiple handlers. These handlers will be executed in the order that they are registered
to the route. The execution sequence can be terminated in the middle using one of the following two methods:
* A handler returns an error: the router will skip the rest of the handlers and handle the returned error.
* A handler calls `Context.Abort()`: the router will simply skip the rest of the handlers. There is no error to be handled.
A handler can call `Context.Next()` to explicitly execute the rest of the unexecuted handlers and take actions after
they finish execution. For example, a response compression handler may start the output buffer, call `Context.Next()`,
and then compress and send the output to response.
### Context
For each incoming request, a `routing.Context` object is passed through the relevant handlers. Because `routing.Context`
embeds `fasthttp.RequestCtx`, you can access all properties and methods provided by the latter.
Additionally, the `Context.Param()` method allows handlers to access the URL path parameters that match the current route.
Using `Context.Get()` and `Context.Set()`, handlers can share data between each other. For example, an authentication
handler can store the authenticated user identity by calling `Context.Set()`, and other handlers can retrieve back
the identity information by calling `Context.Get()`.
Context also provides a handy `WriteData()` method that can be used to write data of arbitrary type to the response.
The `WriteData()` method can also be overridden (by replacement) to achieve more versatile response data writing.
### Error Handling
A handler may return an error indicating some erroneous condition. Sometimes, a handler or the code it calls may cause
a panic. Both should be handled properly to ensure best user experience. It is recommended that you use
the `fault.Recover` handler or a similar error handler to handle these errors.
If an error is not handled by any handler, the router will handle it by calling its `handleError()` method which
simply sets an appropriate HTTP status code and writes the error message to the response.
When an incoming request has no matching route, the router will call the handlers registered via the `Router.NotFound()`
method. All the handlers registered via `Router.Use()` will also be called in advance. By default, the following two
handlers are registered with `Router.NotFound()`:
* `routing.MethodNotAllowedHandler`: a handler that sends an `Allow` HTTP header indicating the allowed HTTP methods for a requested URL
* `routing.NotFoundHandler`: a handler triggering 404 HTTP error

126
vendor/github.com/qiangxue/fasthttp-routing/context.go generated vendored Normal file
View File

@@ -0,0 +1,126 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package routing
import (
"fmt"
"github.com/valyala/fasthttp"
)
// SerializeFunc serializes the given data of arbitrary type into a byte array.
type SerializeFunc func(data interface{}) ([]byte, error)
// Context represents the contextual data and environment while processing an incoming HTTP request.
type Context struct {
*fasthttp.RequestCtx
Serialize SerializeFunc // the function serializing the given data of arbitrary type into a byte array.
router *Router
pnames []string // list of route parameter names
pvalues []string // list of parameter values corresponding to pnames
data map[string]interface{} // data items managed by Get and Set
index int // the index of the currently executing handler in handlers
handlers []Handler // the handlers associated with the current route
}
// Router returns the Router that is handling the incoming HTTP request.
func (c *Context) Router() *Router {
return c.router
}
// Param returns the named parameter value that is found in the URL path matching the current route.
// If the named parameter cannot be found, an empty string will be returned.
func (c *Context) Param(name string) string {
for i, n := range c.pnames {
if n == name {
return c.pvalues[i]
}
}
return ""
}
// Get returns the named data item previously registered with the context by calling Set.
// If the named data item cannot be found, nil will be returned.
func (c *Context) Get(name string) interface{} {
return c.data[name]
}
// Set stores the named data item in the context so that it can be retrieved later.
func (c *Context) Set(name string, value interface{}) {
if c.data == nil {
c.data = make(map[string]interface{})
}
c.data[name] = value
}
// Next calls the rest of the handlers associated with the current route.
// If any of these handlers returns an error, Next will return the error and skip the following handlers.
// Next is normally used when a handler needs to do some postprocessing after the rest of the handlers
// are executed.
func (c *Context) Next() error {
c.index++
for n := len(c.handlers); c.index < n; c.index++ {
if err := c.handlers[c.index](c); err != nil {
return err
}
}
return nil
}
// Abort skips the rest of the handlers associated with the current route.
// Abort is normally used when a handler handles the request normally and wants to skip the rest of the handlers.
// If a handler wants to indicate an error condition, it should simply return the error without calling Abort.
func (c *Context) Abort() {
c.index = len(c.handlers)
}
// URL creates a URL using the named route and the parameter values.
// The parameters should be given in the sequence of name1, value1, name2, value2, and so on.
// If a parameter in the route is not provided a value, the parameter token will remain in the resulting URL.
// Parameter values will be properly URL encoded.
// The method returns an empty string if the URL creation fails.
func (c *Context) URL(route string, pairs ...interface{}) string {
if r := c.router.routes[route]; r != nil {
return r.URL(pairs...)
}
return ""
}
// WriteData writes the given data of arbitrary type to the response.
// The method calls the Serialize() method to convert the data into a byte array and then writes
// the byte array to the response.
func (c *Context) WriteData(data interface{}) (err error) {
var bytes []byte
if bytes, err = c.Serialize(data); err == nil {
_, err = c.Write(bytes)
}
return
}
// init sets the request and response of the context and resets all other properties.
func (c *Context) init(ctx *fasthttp.RequestCtx) {
c.RequestCtx = ctx
c.data = nil
c.index = -1
c.Serialize = Serialize
}
// Serialize converts the given data into a byte array.
// If the data is neither a byte array nor a string, it will call fmt.Sprint to convert it into a string.
func Serialize(data interface{}) (bytes []byte, err error) {
switch data.(type) {
case []byte:
return data.([]byte), nil
case string:
return []byte(data.(string)), nil
default:
if data != nil {
return []byte(fmt.Sprint(data)), nil
}
}
return nil, nil
}

40
vendor/github.com/qiangxue/fasthttp-routing/error.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package routing
import "net/http"
// HTTPError represents an HTTP error with HTTP status code and error message
type HTTPError interface {
error
// StatusCode returns the HTTP status code of the error
StatusCode() int
}
// Error contains the error information reported by calling Context.Error().
type httpError struct {
Status int `json:"status" xml:"status"`
Message string `json:"message" xml:"message"`
}
// NewHTTPError creates a new HttpError instance.
// If the error message is not given, http.StatusText() will be called
// to generate the message based on the status code.
func NewHTTPError(status int, message ...string) HTTPError {
if len(message) > 0 {
return &httpError{status, message[0]}
}
return &httpError{status, http.StatusText(status)}
}
// Error returns the error message.
func (e *httpError) Error() string {
return e.Message
}
// StatusCode returns the HTTP status code.
func (e *httpError) StatusCode() int {
return e.Status
}

107
vendor/github.com/qiangxue/fasthttp-routing/group.go generated vendored Normal file
View File

@@ -0,0 +1,107 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package routing
import (
"strings"
)
// RouteGroup represents a group of routes that share the same path prefix.
type RouteGroup struct {
prefix string
router *Router
handlers []Handler
}
// newRouteGroup creates a new RouteGroup with the given path prefix, router, and handlers.
func newRouteGroup(prefix string, router *Router, handlers []Handler) *RouteGroup {
return &RouteGroup{
prefix: prefix,
router: router,
handlers: handlers,
}
}
// Get adds a GET route to the router with the given route path and handlers.
func (r *RouteGroup) Get(path string, handlers ...Handler) *Route {
return newRoute(path, r).Get(handlers...)
}
// Post adds a POST route to the router with the given route path and handlers.
func (r *RouteGroup) Post(path string, handlers ...Handler) *Route {
return newRoute(path, r).Post(handlers...)
}
// Put adds a PUT route to the router with the given route path and handlers.
func (r *RouteGroup) Put(path string, handlers ...Handler) *Route {
return newRoute(path, r).Put(handlers...)
}
// Patch adds a PATCH route to the router with the given route path and handlers.
func (r *RouteGroup) Patch(path string, handlers ...Handler) *Route {
return newRoute(path, r).Patch(handlers...)
}
// Delete adds a DELETE route to the router with the given route path and handlers.
func (r *RouteGroup) Delete(path string, handlers ...Handler) *Route {
return newRoute(path, r).Delete(handlers...)
}
// Connect adds a CONNECT route to the router with the given route path and handlers.
func (r *RouteGroup) Connect(path string, handlers ...Handler) *Route {
return newRoute(path, r).Connect(handlers...)
}
// Head adds a HEAD route to the router with the given route path and handlers.
func (r *RouteGroup) Head(path string, handlers ...Handler) *Route {
return newRoute(path, r).Head(handlers...)
}
// Options adds an OPTIONS route to the router with the given route path and handlers.
func (r *RouteGroup) Options(path string, handlers ...Handler) *Route {
return newRoute(path, r).Options(handlers...)
}
// Trace adds a TRACE route to the router with the given route path and handlers.
func (r *RouteGroup) Trace(path string, handlers ...Handler) *Route {
return newRoute(path, r).Trace(handlers...)
}
// Any adds a route with the given route, handlers, and the HTTP methods as listed in routing.Methods.
func (r *RouteGroup) Any(path string, handlers ...Handler) *Route {
route := newRoute(path, r)
for _, method := range Methods {
route.add(method, handlers)
}
return route
}
// To adds a route to the router with the given HTTP methods, route path, and handlers.
// Multiple HTTP methods should be separated by commas (without any surrounding spaces).
func (r *RouteGroup) To(methods, path string, handlers ...Handler) *Route {
route := newRoute(path, r)
for _, method := range strings.Split(methods, ",") {
route.add(method, handlers)
}
return route
}
// Group creates a RouteGroup with the given route path prefix and handlers.
// The new group will combine the existing path prefix with the new one.
// If no handler is provided, the new group will inherit the handlers registered
// with the current group.
func (r *RouteGroup) Group(prefix string, handlers ...Handler) *RouteGroup {
if len(handlers) == 0 {
handlers = make([]Handler, len(r.handlers))
copy(handlers, r.handlers)
}
return newRouteGroup(r.prefix+prefix, r.router, handlers)
}
// Use registers one or multiple handlers to the current route group.
// These handlers will be shared by all routes belong to this group and its subgroups.
func (r *RouteGroup) Use(handlers ...Handler) {
r.handlers = append(r.handlers, handlers...)
}

161
vendor/github.com/qiangxue/fasthttp-routing/route.go generated vendored Normal file
View File

@@ -0,0 +1,161 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package routing
import (
"fmt"
"net/url"
"strings"
)
// Route represents a URL path pattern that can be used to match requested URLs.
type Route struct {
group *RouteGroup
name, path string
template string
}
// newRoute creates a new Route with the given route path and route group.
func newRoute(path string, group *RouteGroup) *Route {
path = group.prefix + path
name := path
// an asterisk at the end matches any number of characters
if strings.HasSuffix(path, "*") {
path = path[:len(path)-1] + "<:.*>"
}
route := &Route{
group: group,
name: name,
path: path,
template: buildURLTemplate(path),
}
group.router.routes[name] = route
return route
}
// Name sets the name of the route.
// This method will update the registration of the route in the router as well.
func (r *Route) Name(name string) *Route {
r.name = name
r.group.router.routes[name] = r
return r
}
// Get adds the route to the router using the GET HTTP method.
func (r *Route) Get(handlers ...Handler) *Route {
return r.add("GET", handlers)
}
// Post adds the route to the router using the POST HTTP method.
func (r *Route) Post(handlers ...Handler) *Route {
return r.add("POST", handlers)
}
// Put adds the route to the router using the PUT HTTP method.
func (r *Route) Put(handlers ...Handler) *Route {
return r.add("PUT", handlers)
}
// Patch adds the route to the router using the PATCH HTTP method.
func (r *Route) Patch(handlers ...Handler) *Route {
return r.add("PATCH", handlers)
}
// Delete adds the route to the router using the DELETE HTTP method.
func (r *Route) Delete(handlers ...Handler) *Route {
return r.add("DELETE", handlers)
}
// Connect adds the route to the router using the CONNECT HTTP method.
func (r *Route) Connect(handlers ...Handler) *Route {
return r.add("CONNECT", handlers)
}
// Head adds the route to the router using the HEAD HTTP method.
func (r *Route) Head(handlers ...Handler) *Route {
return r.add("HEAD", handlers)
}
// Options adds the route to the router using the OPTIONS HTTP method.
func (r *Route) Options(handlers ...Handler) *Route {
return r.add("OPTIONS", handlers)
}
// Trace adds the route to the router using the TRACE HTTP method.
func (r *Route) Trace(handlers ...Handler) *Route {
return r.add("TRACE", handlers)
}
// To adds the route to the router with the given HTTP methods and handlers.
// Multiple HTTP methods should be separated by commas (without any surrounding spaces).
func (r *Route) To(methods string, handlers ...Handler) *Route {
for _, method := range strings.Split(methods, ",") {
r.add(method, handlers)
}
return r
}
// URL creates a URL using the current route and the given parameters.
// The parameters should be given in the sequence of name1, value1, name2, value2, and so on.
// If a parameter in the route is not provided a value, the parameter token will remain in the resulting URL.
// The method will perform URL encoding for all given parameter values.
func (r *Route) URL(pairs ...interface{}) (s string) {
s = r.template
for i := 0; i < len(pairs); i++ {
name := fmt.Sprintf("<%v>", pairs[i])
value := ""
if i < len(pairs)-1 {
value = url.QueryEscape(fmt.Sprint(pairs[i+1]))
}
s = strings.Replace(s, name, value, -1)
}
return
}
// add registers the route, the specified HTTP method and the handlers to the router.
// The handlers will be combined with the handlers of the route group.
func (r *Route) add(method string, handlers []Handler) *Route {
hh := combineHandlers(r.group.handlers, handlers)
r.group.router.add(method, r.path, hh)
return r
}
// buildURLTemplate converts a route pattern into a URL template by removing regular expressions in parameter tokens.
func buildURLTemplate(path string) string {
template, start, end := "", -1, -1
for i := 0; i < len(path); i++ {
if path[i] == '<' && start < 0 {
start = i
} else if path[i] == '>' && start >= 0 {
name := path[start+1 : i]
for j := start + 1; j < i; j++ {
if path[j] == ':' {
name = path[start+1 : j]
break
}
}
template += path[end+1:start] + "<" + name + ">"
end = i
start = -1
}
}
if end < 0 {
template = path
} else if end < len(path)-1 {
template += path[end+1:]
}
return template
}
// combineHandlers merges two lists of handlers into a new list.
func combineHandlers(h1 []Handler, h2 []Handler) []Handler {
hh := make([]Handler, len(h1)+len(h2))
copy(hh, h1)
copy(hh[len(h1):], h2)
return hh
}

169
vendor/github.com/qiangxue/fasthttp-routing/router.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package routing provides high performance and powerful HTTP routing capabilities.
package routing
import (
"net/http"
"sort"
"strings"
"sync"
"github.com/valyala/fasthttp"
)
type (
// Handler is the function for handling HTTP requests.
Handler func(*Context) error
// Router manages routes and dispatches HTTP requests to the handlers of the matching routes.
Router struct {
RouteGroup
pool sync.Pool
routes map[string]*Route
stores map[string]routeStore
maxParams int
notFound []Handler
notFoundHandlers []Handler
}
// routeStore stores route paths and the corresponding handlers.
routeStore interface {
Add(key string, data interface{}) int
Get(key string, pvalues []string) (data interface{}, pnames []string)
String() string
}
)
// Methods lists all supported HTTP methods by Router.
var Methods = []string{
"CONNECT",
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
"TRACE",
}
// New creates a new Router object.
func New() *Router {
r := &Router{
routes: make(map[string]*Route),
stores: make(map[string]routeStore),
}
r.RouteGroup = *newRouteGroup("", r, make([]Handler, 0))
r.NotFound(MethodNotAllowedHandler, NotFoundHandler)
r.pool.New = func() interface{} {
return &Context{
pvalues: make([]string, r.maxParams),
router: r,
}
}
return r
}
// HandleRequest handles the HTTP request.
func (r *Router) HandleRequest(ctx *fasthttp.RequestCtx) {
c := r.pool.Get().(*Context)
c.init(ctx)
c.handlers, c.pnames = r.find(string(ctx.Method()), string(ctx.Path()), c.pvalues)
if err := c.Next(); err != nil {
r.handleError(c, err)
}
r.pool.Put(c)
}
// Route returns the named route.
// Nil is returned if the named route cannot be found.
func (r *Router) Route(name string) *Route {
return r.routes[name]
}
// Use appends the specified handlers to the router and shares them with all routes.
func (r *Router) Use(handlers ...Handler) {
r.RouteGroup.Use(handlers...)
r.notFoundHandlers = combineHandlers(r.handlers, r.notFound)
}
// NotFound specifies the handlers that should be invoked when the router cannot find any route matching a request.
// Note that the handlers registered via Use will be invoked first in this case.
func (r *Router) NotFound(handlers ...Handler) {
r.notFound = handlers
r.notFoundHandlers = combineHandlers(r.handlers, r.notFound)
}
// handleError is the error handler for handling any unhandled errors.
func (r *Router) handleError(c *Context, err error) {
if httpError, ok := err.(HTTPError); ok {
c.Error(httpError.Error(), httpError.StatusCode())
} else {
c.Error(err.Error(), http.StatusInternalServerError)
}
}
func (r *Router) add(method, path string, handlers []Handler) {
store := r.stores[method]
if store == nil {
store = newStore()
r.stores[method] = store
}
if n := store.Add(path, handlers); n > r.maxParams {
r.maxParams = n
}
}
func (r *Router) find(method, path string, pvalues []string) (handlers []Handler, pnames []string) {
var hh interface{}
if store := r.stores[method]; store != nil {
hh, pnames = store.Get(path, pvalues)
}
if hh != nil {
return hh.([]Handler), pnames
}
return r.notFoundHandlers, pnames
}
func (r *Router) findAllowedMethods(path string) map[string]bool {
methods := make(map[string]bool)
pvalues := make([]string, r.maxParams)
for m, store := range r.stores {
if handlers, _ := store.Get(path, pvalues); handlers != nil {
methods[m] = true
}
}
return methods
}
// NotFoundHandler returns a 404 HTTP error indicating a request has no matching route.
func NotFoundHandler(*Context) error {
return NewHTTPError(http.StatusNotFound)
}
// MethodNotAllowedHandler handles the situation when a request has matching route without matching HTTP method.
// In this case, the handler will respond with an Allow HTTP header listing the allowed HTTP methods.
// Otherwise, the handler will do nothing and let the next handler (usually a NotFoundHandler) to handle the problem.
func MethodNotAllowedHandler(c *Context) error {
methods := c.Router().findAllowedMethods(string(c.Path()))
if len(methods) == 0 {
return nil
}
methods["OPTIONS"] = true
ms := make([]string, len(methods))
i := 0
for method := range methods {
ms[i] = method
i++
}
sort.Strings(ms)
c.Response.Header.Set("Allow", strings.Join(ms, ", "))
if string(c.Method()) != "OPTIONS" {
c.Response.SetStatusCode(http.StatusMethodNotAllowed)
}
c.Abort()
return nil
}

317
vendor/github.com/qiangxue/fasthttp-routing/store.go generated vendored Normal file
View File

@@ -0,0 +1,317 @@
// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package routing
import (
"fmt"
"math"
"regexp"
"strings"
)
// store is a radix tree that supports storing data with parametric keys and retrieving them back with concrete keys.
// When retrieving a data item with a concrete key, the matching parameter names and values will be returned as well.
// A parametric key is a string containing tokens in the format of "<name>", "<name:pattern>", or "<:pattern>".
// Each token represents a single parameter.
type store struct {
root *node // the root node of the radix tree
count int // the number of data nodes in the tree
}
// newStore creates a new store.
func newStore() *store {
return &store{
root: &node{
static: true,
children: make([]*node, 256),
pchildren: make([]*node, 0),
pindex: -1,
pnames: []string{},
},
}
}
// Add adds a new data item with the given parametric key.
// The number of parameters in the key is returned.
func (s *store) Add(key string, data interface{}) int {
s.count++
return s.root.add(key, data, s.count)
}
// Get returns the data item matching the given concrete key.
// If the data item was added to the store with a parametric key before, the matching
// parameter names and values will be returned as well.
func (s *store) Get(path string, pvalues []string) (data interface{}, pnames []string) {
data, pnames, _ = s.root.get(path, pvalues)
return
}
// String dumps the radix tree kept in the store as a string.
func (s *store) String() string {
return s.root.print(0)
}
// node represents a radix trie node
type node struct {
static bool // whether the node is a static node or param node
key string // the key identifying this node
data interface{} // the data associated with this node. nil if not a data node.
order int // the order at which the data was added. used to be pick the first one when matching multiple
minOrder int // minimum order among all the child nodes and this node
children []*node // child static nodes, indexed by the first byte of each child key
pchildren []*node // child param nodes
regex *regexp.Regexp // regular expression for a param node containing regular expression key
pindex int // the parameter index, meaningful only for param node
pnames []string // the parameter names collected from the root till this node
}
// add adds a new data item to the tree rooted at the current node.
// The number of parameters in the key is returned.
func (n *node) add(key string, data interface{}, order int) int {
matched := 0
// find the common prefix
for ; matched < len(key) && matched < len(n.key); matched++ {
if key[matched] != n.key[matched] {
break
}
}
if matched == len(n.key) {
if matched == len(key) {
// the node key is the same as the key: make the current node as data node
// if the node is already a data node, ignore the new data since we only care the first matched node
if n.data == nil {
n.data = data
n.order = order
}
return n.pindex + 1
}
// the node key is a prefix of the key: create a child node
newKey := key[matched:]
// try adding to a static child
if child := n.children[newKey[0]]; child != nil {
if pn := child.add(newKey, data, order); pn >= 0 {
return pn
}
}
// try adding to a param child
for _, child := range n.pchildren {
if pn := child.add(newKey, data, order); pn >= 0 {
return pn
}
}
return n.addChild(newKey, data, order)
}
if matched == 0 || !n.static {
// no common prefix, or partial common prefix with a non-static node: should skip this node
return -1
}
// the node key shares a partial prefix with the key: split the node key
n1 := &node{
static: true,
key: n.key[matched:],
data: n.data,
order: n.order,
minOrder: n.minOrder,
pchildren: n.pchildren,
children: n.children,
pindex: n.pindex,
pnames: n.pnames,
}
n.key = key[0:matched]
n.data = nil
n.pchildren = make([]*node, 0)
n.children = make([]*node, 256)
n.children[n1.key[0]] = n1
return n.add(key, data, order)
}
// addChild creates static and param nodes to store the given data
func (n *node) addChild(key string, data interface{}, order int) int {
// find the first occurrence of a param token
p0, p1 := -1, -1
for i := 0; i < len(key); i++ {
if p0 < 0 && key[i] == '<' {
p0 = i
}
if p0 >= 0 && key[i] == '>' {
p1 = i
break
}
}
if p0 > 0 && p1 > 0 || p1 < 0 {
// param token occurs after a static string, or no param token: create a static node
child := &node{
static: true,
key: key,
minOrder: order,
children: make([]*node, 256),
pchildren: make([]*node, 0),
pindex: n.pindex,
pnames: n.pnames,
}
n.children[key[0]] = child
if p1 > 0 {
// param token occurs after a static string
child.key = key[:p0]
n = child
} else {
// no param token: done adding the child
child.data = data
child.order = order
return child.pindex + 1
}
}
// add param node
child := &node{
static: false,
key: key[p0 : p1+1],
minOrder: order,
children: make([]*node, 256),
pchildren: make([]*node, 0),
pindex: n.pindex,
pnames: n.pnames,
}
pattern := ""
pname := key[p0+1 : p1]
for i := p0 + 1; i < p1; i++ {
if key[i] == ':' {
pname = key[p0+1 : i]
pattern = key[i+1 : p1]
break
}
}
if pattern != "" {
// the param token contains a regular expression
child.regex = regexp.MustCompile("^" + pattern)
}
pnames := make([]string, len(n.pnames)+1)
copy(pnames, n.pnames)
pnames[len(n.pnames)] = pname
child.pnames = pnames
child.pindex = len(pnames) - 1
n.pchildren = append(n.pchildren, child)
if p1 == len(key)-1 {
// the param token is at the end of the key
child.data = data
child.order = order
return child.pindex + 1
}
// process the rest of the key
return child.addChild(key[p1+1:], data, order)
}
// get returns the data item with the key matching the tree rooted at the current node
func (n *node) get(key string, pvalues []string) (data interface{}, pnames []string, order int) {
order = math.MaxInt32
repeat:
if n.static {
// check if the node key is a prefix of the given key
// a slightly optimized version of strings.HasPrefix
nkl := len(n.key)
if nkl > len(key) {
return
}
for i := nkl - 1; i >= 0; i-- {
if n.key[i] != key[i] {
return
}
}
key = key[nkl:]
} else if n.regex != nil {
// param node with regular expression
if n.regex.String() == "^.*" {
pvalues[n.pindex] = key
key = ""
} else if match := n.regex.FindStringIndex(key); match != nil {
pvalues[n.pindex] = key[0:match[1]]
key = key[match[1]:]
} else {
return
}
} else {
// param node matching non-"/" characters
i, kl := 0, len(key)
for ; i < kl; i++ {
if key[i] == '/' {
pvalues[n.pindex] = key[0:i]
key = key[i:]
break
}
}
if i == kl {
pvalues[n.pindex] = key
key = ""
}
}
if len(key) > 0 {
// find a static child that can match the rest of the key
if child := n.children[key[0]]; child != nil {
if len(n.pchildren) == 0 {
// use goto to avoid recursion when no param children
n = child
goto repeat
}
data, pnames, order = child.get(key, pvalues)
}
} else if n.data != nil {
// do not return yet: a param node may match an empty string with smaller order
data, pnames, order = n.data, n.pnames, n.order
}
// try matching param children
tvalues := pvalues
allocated := false
for _, child := range n.pchildren {
if child.minOrder >= order {
continue
}
if data != nil && !allocated {
tvalues = make([]string, len(pvalues))
allocated = true
}
if d, p, s := child.get(key, tvalues); d != nil && s < order {
if allocated {
for i := child.pindex; i < len(p); i++ {
pvalues[i] = tvalues[i]
}
}
data, pnames, order = d, p, s
}
}
return
}
func (n *node) print(level int) string {
r := fmt.Sprintf("%v{key: %v, regex: %v, data: %v, order: %v, minOrder: %v, pindex: %v, pnames: %v}\n", strings.Repeat(" ", level<<2), n.key, n.regex, n.data, n.order, n.minOrder, n.pindex, n.pnames)
for _, child := range n.children {
if child != nil {
r += child.print(level + 1)
}
}
for _, child := range n.pchildren {
r += child.print(level + 1)
}
return r
}