1
0
Fork 0
forked from forgejo/forgejo

Use vendored go-swagger (#8087)

* Use vendored go-swagger

* vendor go-swagger

* revert un wanteed change

* remove un-needed GO111MODULE

* Update Makefile

Co-Authored-By: techknowlogick <matti@mdranta.net>
This commit is contained in:
Antoine GIRARD 2019-09-04 21:53:54 +02:00 committed by Lauris BH
parent 4cb1bdddc8
commit 9fe4437bda
686 changed files with 143379 additions and 17 deletions

View file

@ -0,0 +1,19 @@
Copyright (c) 2014 Naoya Inada <naoina@kuune.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,180 @@
# Denco [![Build Status](https://travis-ci.org/naoina/denco.png?branch=master)](https://travis-ci.org/naoina/denco)
The fast and flexible HTTP request router for [Go](http://golang.org).
Denco is based on Double-Array implementation of [Kocha-urlrouter](https://github.com/naoina/kocha-urlrouter).
However, Denco is optimized and some features added.
## Features
* Fast (See [go-http-routing-benchmark](https://github.com/naoina/go-http-routing-benchmark))
* [URL patterns](#url-patterns) (`/foo/:bar` and `/foo/*wildcard`)
* Small (but enough) URL router API
* HTTP request multiplexer like `http.ServeMux`
## Installation
go get -u github.com/go-openapi/runtime/middleware/denco
## Using as HTTP request multiplexer
```go
package main
import (
"fmt"
"log"
"net/http"
"github.com/go-openapi/runtime/middleware/denco"
)
func Index(w http.ResponseWriter, r *http.Request, params denco.Params) {
fmt.Fprintf(w, "Welcome to Denco!\n")
}
func User(w http.ResponseWriter, r *http.Request, params denco.Params) {
fmt.Fprintf(w, "Hello %s!\n", params.Get("name"))
}
func main() {
mux := denco.NewMux()
handler, err := mux.Build([]denco.Handler{
mux.GET("/", Index),
mux.GET("/user/:name", User),
mux.POST("/user/:name", User),
})
if err != nil {
panic(err)
}
log.Fatal(http.ListenAndServe(":8080", handler))
}
```
## Using as URL router
```go
package main
import (
"fmt"
"github.com/go-openapi/runtime/middleware/denco"
)
type route struct {
name string
}
func main() {
router := denco.New()
router.Build([]denco.Record{
{"/", &route{"root"}},
{"/user/:id", &route{"user"}},
{"/user/:name/:id", &route{"username"}},
{"/static/*filepath", &route{"static"}},
})
data, params, found := router.Lookup("/")
// print `&main.route{name:"root"}, denco.Params(nil), true`.
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
data, params, found = router.Lookup("/user/hoge")
// print `&main.route{name:"user"}, denco.Params{denco.Param{Name:"id", Value:"hoge"}}, true`.
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
data, params, found = router.Lookup("/user/hoge/7")
// print `&main.route{name:"username"}, denco.Params{denco.Param{Name:"name", Value:"hoge"}, denco.Param{Name:"id", Value:"7"}}, true`.
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
data, params, found = router.Lookup("/static/path/to/file")
// print `&main.route{name:"static"}, denco.Params{denco.Param{Name:"filepath", Value:"path/to/file"}}, true`.
fmt.Printf("%#v, %#v, %#v\n", data, params, found)
}
```
See [Godoc](http://godoc.org/github.com/go-openapi/runtime/middleware/denco) for more details.
## Getting the value of path parameter
You can get the value of path parameter by 2 ways.
1. Using [`denco.Params.Get`](http://godoc.org/github.com/go-openapi/runtime/middleware/denco#Params.Get) method
2. Find by loop
```go
package main
import (
"fmt"
"github.com/go-openapi/runtime/middleware/denco"
)
func main() {
router := denco.New()
if err := router.Build([]denco.Record{
{"/user/:name/:id", "route1"},
}); err != nil {
panic(err)
}
// 1. Using denco.Params.Get method.
_, params, _ := router.Lookup("/user/alice/1")
name := params.Get("name")
if name != "" {
fmt.Printf("Hello %s.\n", name) // prints "Hello alice.".
}
// 2. Find by loop.
for _, param := range params {
if param.Name == "name" {
fmt.Printf("Hello %s.\n", name) // prints "Hello alice.".
}
}
}
```
## URL patterns
Denco's route matching strategy is "most nearly matching".
When routes `/:name` and `/alice` have been built, URI `/alice` matches the route `/alice`, not `/:name`.
Because URI `/alice` is more match with the route `/alice` than `/:name`.
For more example, when routes below have been built:
```
/user/alice
/user/:name
/user/:name/:id
/user/alice/:id
/user/:id/bob
```
Routes matching are:
```
/user/alice => "/user/alice" (no match with "/user/:name")
/user/bob => "/user/:name"
/user/naoina/1 => "/user/:name/1"
/user/alice/1 => "/user/alice/:id" (no match with "/user/:name/:id")
/user/1/bob => "/user/:id/bob" (no match with "/user/:name/:id")
/user/alice/bob => "/user/alice/:id" (no match with "/user/:name/:id" and "/user/:id/bob")
```
## Limitation
Denco has some limitations below.
* Number of param records (such as `/:name`) must be less than 2^22
* Number of elements of internal slice must be less than 2^22
## Benchmarks
cd $GOPATH/github.com/go-openapi/runtime/middleware/denco
go test -bench . -benchmem
## License
Denco is licensed under the MIT License.

View file

@ -0,0 +1,452 @@
// Package denco provides fast URL router.
package denco
import (
"fmt"
"sort"
"strings"
)
const (
// ParamCharacter is a special character for path parameter.
ParamCharacter = ':'
// WildcardCharacter is a special character for wildcard path parameter.
WildcardCharacter = '*'
// TerminationCharacter is a special character for end of path.
TerminationCharacter = '#'
// MaxSize is max size of records and internal slice.
MaxSize = (1 << 22) - 1
)
// Router represents a URL router.
type Router struct {
// SizeHint expects the maximum number of path parameters in records to Build.
// SizeHint will be used to determine the capacity of the memory to allocate.
// By default, SizeHint will be determined from given records to Build.
SizeHint int
static map[string]interface{}
param *doubleArray
}
// New returns a new Router.
func New() *Router {
return &Router{
SizeHint: -1,
static: make(map[string]interface{}),
param: newDoubleArray(),
}
}
// Lookup returns data and path parameters that associated with path.
// params is a slice of the Param that arranged in the order in which parameters appeared.
// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}].
func (rt *Router) Lookup(path string) (data interface{}, params Params, found bool) {
if data, found := rt.static[path]; found {
return data, nil, true
}
if len(rt.param.node) == 1 {
return nil, nil, false
}
nd, params, found := rt.param.lookup(path, make([]Param, 0, rt.SizeHint), 1)
if !found {
return nil, nil, false
}
for i := 0; i < len(params); i++ {
params[i].Name = nd.paramNames[i]
}
return nd.data, params, true
}
// Build builds URL router from records.
func (rt *Router) Build(records []Record) error {
statics, params := makeRecords(records)
if len(params) > MaxSize {
return fmt.Errorf("denco: too many records")
}
if rt.SizeHint < 0 {
rt.SizeHint = 0
for _, p := range params {
size := 0
for _, k := range p.Key {
if k == ParamCharacter || k == WildcardCharacter {
size++
}
}
if size > rt.SizeHint {
rt.SizeHint = size
}
}
}
for _, r := range statics {
rt.static[r.Key] = r.Value
}
if err := rt.param.build(params, 1, 0, make(map[int]struct{})); err != nil {
return err
}
return nil
}
// Param represents name and value of path parameter.
type Param struct {
Name string
Value string
}
// Params represents the name and value of path parameters.
type Params []Param
// Get gets the first value associated with the given name.
// If there are no values associated with the key, Get returns "".
func (ps Params) Get(name string) string {
for _, p := range ps {
if p.Name == name {
return p.Value
}
}
return ""
}
type doubleArray struct {
bc []baseCheck
node []*node
}
func newDoubleArray() *doubleArray {
return &doubleArray{
bc: []baseCheck{0},
node: []*node{nil}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
}
}
// baseCheck contains BASE, CHECK and Extra flags.
// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
//
// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
// |----------------------|--|--------|
// 32 10 8 0
type baseCheck uint32
func (bc baseCheck) Base() int {
return int(bc >> 10)
}
func (bc *baseCheck) SetBase(base int) {
*bc |= baseCheck(base) << 10
}
func (bc baseCheck) Check() byte {
return byte(bc)
}
func (bc *baseCheck) SetCheck(check byte) {
*bc |= baseCheck(check)
}
func (bc baseCheck) IsEmpty() bool {
return bc&0xfffffcff == 0
}
func (bc baseCheck) IsSingleParam() bool {
return bc&paramTypeSingle == paramTypeSingle
}
func (bc baseCheck) IsWildcardParam() bool {
return bc&paramTypeWildcard == paramTypeWildcard
}
func (bc baseCheck) IsAnyParam() bool {
return bc&paramTypeAny != 0
}
func (bc *baseCheck) SetSingleParam() {
*bc |= (1 << 8)
}
func (bc *baseCheck) SetWildcardParam() {
*bc |= (1 << 9)
}
const (
paramTypeSingle = 0x0100
paramTypeWildcard = 0x0200
paramTypeAny = 0x0300
)
func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) {
indices := make([]uint64, 0, 1)
for i := 0; i < len(path); i++ {
if da.bc[idx].IsAnyParam() {
indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff))
}
c := path[i]
if idx = nextIndex(da.bc[idx].Base(), c); idx >= len(da.bc) || da.bc[idx].Check() != c {
goto BACKTRACKING
}
}
if next := nextIndex(da.bc[idx].Base(), TerminationCharacter); next < len(da.bc) && da.bc[next].Check() == TerminationCharacter {
return da.node[da.bc[next].Base()], params, true
}
BACKTRACKING:
for j := len(indices) - 1; j >= 0; j-- {
i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff)
if da.bc[idx].IsSingleParam() {
idx := nextIndex(da.bc[idx].Base(), ParamCharacter)
if idx >= len(da.bc) {
break
}
next := NextSeparator(path, i)
params := append(params, Param{Value: path[i:next]})
if nd, params, found := da.lookup(path[next:], params, idx); found {
return nd, params, true
}
}
if da.bc[idx].IsWildcardParam() {
idx := nextIndex(da.bc[idx].Base(), WildcardCharacter)
params := append(params, Param{Value: path[i:]})
return da.node[da.bc[idx].Base()], params, true
}
}
return nil, nil, false
}
// build builds double-array from records.
func (da *doubleArray) build(srcs []*record, idx, depth int, usedBase map[int]struct{}) error {
sort.Stable(recordSlice(srcs))
base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
if err != nil {
return err
}
if leaf != nil {
nd, err := makeNode(leaf)
if err != nil {
return err
}
da.bc[idx].SetBase(len(da.node))
da.node = append(da.node, nd)
}
for _, sib := range siblings {
da.setCheck(nextIndex(base, sib.c), sib.c)
}
for _, sib := range siblings {
records := srcs[sib.start:sib.end]
switch sib.c {
case ParamCharacter:
for _, r := range records {
next := NextSeparator(r.Key, depth+1)
name := r.Key[depth+1 : next]
r.paramNames = append(r.paramNames, name)
r.Key = r.Key[next:]
}
da.bc[idx].SetSingleParam()
if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
return err
}
case WildcardCharacter:
r := records[0]
name := r.Key[depth+1 : len(r.Key)-1]
r.paramNames = append(r.paramNames, name)
r.Key = ""
da.bc[idx].SetWildcardParam()
if err := da.build(records, nextIndex(base, sib.c), 0, usedBase); err != nil {
return err
}
default:
if err := da.build(records, nextIndex(base, sib.c), depth+1, usedBase); err != nil {
return err
}
}
}
return nil
}
// setBase sets BASE.
func (da *doubleArray) setBase(i, base int) {
da.bc[i].SetBase(base)
}
// setCheck sets CHECK.
func (da *doubleArray) setCheck(i int, check byte) {
da.bc[i].SetCheck(check)
}
// findEmptyIndex returns an index of unused BASE/CHECK node.
func (da *doubleArray) findEmptyIndex(start int) int {
i := start
for ; i < len(da.bc); i++ {
if da.bc[i].IsEmpty() {
break
}
}
return i
}
// findBase returns good BASE.
func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
base = nextIndex(idx, firstChar)
if _, used := usedBase[base]; used {
continue
}
i := 0
for ; i < len(siblings); i++ {
next := nextIndex(base, siblings[i].c)
if len(da.bc) <= next {
da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
}
if !da.bc[next].IsEmpty() {
break
}
}
if i == len(siblings) {
break
}
}
usedBase[base] = struct{}{}
return base
}
func (da *doubleArray) arrange(records []*record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
siblings, leaf, err = makeSiblings(records, depth)
if err != nil {
return -1, nil, nil, err
}
if len(siblings) < 1 {
return -1, nil, leaf, nil
}
base = da.findBase(siblings, idx, usedBase)
if base > MaxSize {
return -1, nil, nil, fmt.Errorf("denco: too many elements of internal slice")
}
da.setBase(idx, base)
return base, siblings, leaf, err
}
// node represents a node of Double-Array.
type node struct {
data interface{}
// Names of path parameters.
paramNames []string
}
// makeNode returns a new node from record.
func makeNode(r *record) (*node, error) {
dups := make(map[string]bool)
for _, name := range r.paramNames {
if dups[name] {
return nil, fmt.Errorf("denco: path parameter `%v' is duplicated in the key `%v'", name, r.Key)
}
dups[name] = true
}
return &node{data: r.Value, paramNames: r.paramNames}, nil
}
// sibling represents an intermediate data of build for Double-Array.
type sibling struct {
// An index of start of duplicated characters.
start int
// An index of end of duplicated characters.
end int
// A character of sibling.
c byte
}
// nextIndex returns a next index of array of BASE/CHECK.
func nextIndex(base int, c byte) int {
return base ^ int(c)
}
// makeSiblings returns slice of sibling.
func makeSiblings(records []*record, depth int) (sib []sibling, leaf *record, err error) {
var (
pc byte
n int
)
for i, r := range records {
if len(r.Key) <= depth {
leaf = r
continue
}
c := r.Key[depth]
switch {
case pc < c:
sib = append(sib, sibling{start: i, c: c})
case pc == c:
continue
default:
return nil, nil, fmt.Errorf("denco: BUG: routing table hasn't been sorted")
}
if n > 0 {
sib[n-1].end = i
}
pc = c
n++
}
if n == 0 {
return nil, leaf, nil
}
sib[n-1].end = len(records)
return sib, leaf, nil
}
// Record represents a record data for router construction.
type Record struct {
// Key for router construction.
Key string
// Result value for Key.
Value interface{}
}
// NewRecord returns a new Record.
func NewRecord(key string, value interface{}) Record {
return Record{
Key: key,
Value: value,
}
}
// record represents a record that use to build the Double-Array.
type record struct {
Record
paramNames []string
}
// makeRecords returns the records that use to build Double-Arrays.
func makeRecords(srcs []Record) (statics, params []*record) {
spChars := string([]byte{ParamCharacter, WildcardCharacter})
termChar := string(TerminationCharacter)
for _, r := range srcs {
if strings.ContainsAny(r.Key, spChars) {
r.Key += termChar
params = append(params, &record{Record: r})
} else {
statics = append(statics, &record{Record: r})
}
}
return statics, params
}
// recordSlice represents a slice of Record for sort and implements the sort.Interface.
type recordSlice []*record
// Len implements the sort.Interface.Len.
func (rs recordSlice) Len() int {
return len(rs)
}
// Less implements the sort.Interface.Less.
func (rs recordSlice) Less(i, j int) bool {
return rs[i].Key < rs[j].Key
}
// Swap implements the sort.Interface.Swap.
func (rs recordSlice) Swap(i, j int) {
rs[i], rs[j] = rs[j], rs[i]
}

View file

@ -0,0 +1,106 @@
package denco
import (
"net/http"
)
// Mux represents a multiplexer for HTTP request.
type Mux struct{}
// NewMux returns a new Mux.
func NewMux() *Mux {
return &Mux{}
}
// GET is shorthand of Mux.Handler("GET", path, handler).
func (m *Mux) GET(path string, handler HandlerFunc) Handler {
return m.Handler("GET", path, handler)
}
// POST is shorthand of Mux.Handler("POST", path, handler).
func (m *Mux) POST(path string, handler HandlerFunc) Handler {
return m.Handler("POST", path, handler)
}
// PUT is shorthand of Mux.Handler("PUT", path, handler).
func (m *Mux) PUT(path string, handler HandlerFunc) Handler {
return m.Handler("PUT", path, handler)
}
// HEAD is shorthand of Mux.Handler("HEAD", path, handler).
func (m *Mux) HEAD(path string, handler HandlerFunc) Handler {
return m.Handler("HEAD", path, handler)
}
// Handler returns a handler for HTTP method.
func (m *Mux) Handler(method, path string, handler HandlerFunc) Handler {
return Handler{
Method: method,
Path: path,
Func: handler,
}
}
// Build builds a http.Handler.
func (m *Mux) Build(handlers []Handler) (http.Handler, error) {
recordMap := make(map[string][]Record)
for _, h := range handlers {
recordMap[h.Method] = append(recordMap[h.Method], NewRecord(h.Path, h.Func))
}
mux := newServeMux()
for m, records := range recordMap {
router := New()
if err := router.Build(records); err != nil {
return nil, err
}
mux.routers[m] = router
}
return mux, nil
}
// Handler represents a handler of HTTP request.
type Handler struct {
// Method is an HTTP method.
Method string
// Path is a routing path for handler.
Path string
// Func is a function of handler of HTTP request.
Func HandlerFunc
}
// The HandlerFunc type is aliased to type of handler function.
type HandlerFunc func(w http.ResponseWriter, r *http.Request, params Params)
type serveMux struct {
routers map[string]*Router
}
func newServeMux() *serveMux {
return &serveMux{
routers: make(map[string]*Router),
}
}
// ServeHTTP implements http.Handler interface.
func (mux *serveMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
handler, params := mux.handler(r.Method, r.URL.Path)
handler(w, r, params)
}
func (mux *serveMux) handler(method, path string) (HandlerFunc, []Param) {
if router, found := mux.routers[method]; found {
if handler, params, found := router.Lookup(path); found {
return handler.(HandlerFunc), params
}
}
return NotFound, nil
}
// NotFound replies to the request with an HTTP 404 not found error.
// NotFound is called when unknown HTTP method or a handler not found.
// If you want to use the your own NotFound handler, please overwrite this variable.
var NotFound = func(w http.ResponseWriter, r *http.Request, _ Params) {
http.NotFound(w, r)
}

View file

@ -0,0 +1,12 @@
package denco
// NextSeparator returns an index of next separator in path.
func NextSeparator(path string, start int) int {
for start < len(path) {
if c := path[start]; c == '/' || c == TerminationCharacter {
break
}
start++
}
return start
}