mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-07-04 08:57:44 +00:00
Update vendor, move to labstack/echo/v4 Fixes #698
This commit is contained in:
25
vendor/github.com/labstack/echo/v4/.editorconfig
generated
vendored
Normal file
25
vendor/github.com/labstack/echo/v4/.editorconfig
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# EditorConfig coding styles definitions. For more information about the
|
||||
# properties used in this file, please see the EditorConfig documentation:
|
||||
# http://editorconfig.org/
|
||||
|
||||
# indicate this is the root of the project
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
|
||||
end_of_line = LF
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
20
vendor/github.com/labstack/echo/v4/.gitattributes
generated
vendored
Normal file
20
vendor/github.com/labstack/echo/v4/.gitattributes
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Automatically normalize line endings for all text-based files
|
||||
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
|
||||
* text=auto
|
||||
|
||||
# For the following file types, normalize line endings to LF on checking and
|
||||
# prevent conversion to CRLF when they are checked out (this is required in
|
||||
# order to prevent newline related issues)
|
||||
.* text eol=lf
|
||||
*.go text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.html text eol=lf
|
||||
*.css text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
LICENSE text eol=lf
|
||||
|
||||
# Exclude `website` and `cookbook` from GitHub's language statistics
|
||||
# https://github.com/github/linguist#using-gitattributes
|
||||
cookbook/* linguist-documentation
|
||||
website/* linguist-documentation
|
7
vendor/github.com/labstack/echo/v4/.gitignore
generated
vendored
Normal file
7
vendor/github.com/labstack/echo/v4/.gitignore
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
coverage.txt
|
||||
_test
|
||||
vendor
|
||||
.idea
|
||||
*.iml
|
||||
*.out
|
16
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- tip
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
install:
|
||||
- go get -v golang.org/x/lint/golint
|
||||
script:
|
||||
- golint -set_exit_status ./...
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
21
vendor/github.com/labstack/echo/v4/LICENSE
generated
vendored
Normal file
21
vendor/github.com/labstack/echo/v4/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 LabStack
|
||||
|
||||
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.
|
3
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
Normal file
3
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
tag:
|
||||
@git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'`
|
||||
@git tag|grep -v ^v
|
114
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
Normal file
114
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<a href="https://echo.labstack.com"><img height="80" src="https://cdn.labstack.com/images/echo-logo.svg"></a>
|
||||
|
||||
[](https://sourcegraph.com/github.com/labstack/echo?badge)
|
||||
[](http://godoc.org/github.com/labstack/echo)
|
||||
[](https://goreportcard.com/report/github.com/labstack/echo)
|
||||
[](https://travis-ci.org/labstack/echo)
|
||||
[](https://codecov.io/gh/labstack/echo)
|
||||
[](https://gitter.im/labstack/echo)
|
||||
[](https://forum.labstack.com)
|
||||
[](https://twitter.com/labstack)
|
||||
[](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
|
||||
|
||||
## Supported Go versions
|
||||
|
||||
As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules).
|
||||
Therefore a Go version capable of understanding /vN suffixed imports is required:
|
||||
|
||||
- 1.9.7+
|
||||
- 1.10.3+
|
||||
- 1.11+
|
||||
|
||||
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
|
||||
way of using Echo going forward.
|
||||
|
||||
For older versions, please use the latest v3 tag.
|
||||
|
||||
## Feature Overview
|
||||
|
||||
- Optimized HTTP router which smartly prioritize routes
|
||||
- Build robust and scalable RESTful APIs
|
||||
- Group APIs
|
||||
- Extensible middleware framework
|
||||
- Define middleware at root, group or route level
|
||||
- Data binding for JSON, XML and form payload
|
||||
- Handy functions to send variety of HTTP responses
|
||||
- Centralized HTTP error handling
|
||||
- Template rendering with any template engine
|
||||
- Define your format for the logger
|
||||
- Highly customizable
|
||||
- Automatic TLS via Let’s Encrypt
|
||||
- HTTP/2 support
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Date: 2018/03/15<br>
|
||||
Source: https://github.com/vishr/web-framework-benchmark<br>
|
||||
Lower is better!
|
||||
|
||||
<img src="https://i.imgur.com/I32VdMJ.png">
|
||||
|
||||
## [Guide](https://echo.labstack.com/guide)
|
||||
|
||||
### Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Middleware
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
|
||||
// Routes
|
||||
e.GET("/", hello)
|
||||
|
||||
// Start server
|
||||
e.Logger.Fatal(e.Start(":1323"))
|
||||
}
|
||||
|
||||
// Handler
|
||||
func hello(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "Hello, World!")
|
||||
}
|
||||
```
|
||||
|
||||
## Help
|
||||
|
||||
- [Forum](https://forum.labstack.com)
|
||||
- [Chat](https://gitter.im/labstack/echo)
|
||||
|
||||
## Contribute
|
||||
|
||||
**Use issues for everything**
|
||||
|
||||
- For a small change, just send a PR.
|
||||
- For bigger changes open an issue for discussion before sending a PR.
|
||||
- PR should have:
|
||||
- Test case
|
||||
- Documentation
|
||||
- Example (If it makes sense)
|
||||
- You can also contribute by:
|
||||
- Reporting issues
|
||||
- Suggesting new features or enhancements
|
||||
- Improve/fix documentation
|
||||
|
||||
## Credits
|
||||
|
||||
- [Vishal Rana](https://github.com/vishr) - Author
|
||||
- [Nitin Rana](https://github.com/nr17) - Consultant
|
||||
- [Contributors](https://github.com/labstack/echo/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/labstack/echo/blob/master/LICENSE)
|
273
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
Normal file
273
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Binder is the interface that wraps the Bind method.
|
||||
Binder interface {
|
||||
Bind(i interface{}, c Context) error
|
||||
}
|
||||
|
||||
// DefaultBinder is the default implementation of the Binder interface.
|
||||
DefaultBinder struct{}
|
||||
|
||||
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
||||
BindUnmarshaler interface {
|
||||
// UnmarshalParam decodes and assigns a value from an form or query param.
|
||||
UnmarshalParam(param string) error
|
||||
}
|
||||
)
|
||||
|
||||
// Bind implements the `Binder#Bind` function.
|
||||
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
||||
req := c.Request()
|
||||
if req.ContentLength == 0 {
|
||||
if req.Method == http.MethodGet || req.Method == http.MethodDelete {
|
||||
if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
|
||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
return NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
|
||||
}
|
||||
ctype := req.Header.Get(HeaderContentType)
|
||||
switch {
|
||||
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
||||
if err = json.NewDecoder(req.Body).Decode(i); err != nil {
|
||||
if ute, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, field=%v, offset=%v", ute.Type, ute.Value, ute.Field, ute.Offset)).SetInternal(err)
|
||||
} else if se, ok := err.(*json.SyntaxError); ok {
|
||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error())).SetInternal(err)
|
||||
}
|
||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
case strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMETextXML):
|
||||
if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
|
||||
if ute, ok := err.(*xml.UnsupportedTypeError); ok {
|
||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error())).SetInternal(err)
|
||||
} else if se, ok := err.(*xml.SyntaxError); ok {
|
||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error())).SetInternal(err)
|
||||
}
|
||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
case strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm):
|
||||
params, err := c.FormParams()
|
||||
if err != nil {
|
||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
if err = b.bindData(i, params, "form"); err != nil {
|
||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||
}
|
||||
default:
|
||||
return ErrUnsupportedMediaType
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
|
||||
typ := reflect.TypeOf(ptr).Elem()
|
||||
val := reflect.ValueOf(ptr).Elem()
|
||||
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return errors.New("binding element must be a struct")
|
||||
}
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
typeField := typ.Field(i)
|
||||
structField := val.Field(i)
|
||||
if !structField.CanSet() {
|
||||
continue
|
||||
}
|
||||
structFieldKind := structField.Kind()
|
||||
inputFieldName := typeField.Tag.Get(tag)
|
||||
|
||||
if inputFieldName == "" {
|
||||
inputFieldName = typeField.Name
|
||||
// If tag is nil, we inspect if the field is a struct.
|
||||
if _, ok := bindUnmarshaler(structField); !ok && structFieldKind == reflect.Struct {
|
||||
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
inputValue, exists := data[inputFieldName]
|
||||
if !exists {
|
||||
// Go json.Unmarshal supports case insensitive binding. However the
|
||||
// url params are bound case sensitive which is inconsistent. To
|
||||
// fix this we must check all of the map values in a
|
||||
// case-insensitive search.
|
||||
inputFieldName = strings.ToLower(inputFieldName)
|
||||
for k, v := range data {
|
||||
if strings.ToLower(k) == inputFieldName {
|
||||
inputValue = v
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
// Call this first, in case we're dealing with an alias to an array type
|
||||
if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
numElems := len(inputValue)
|
||||
if structFieldKind == reflect.Slice && numElems > 0 {
|
||||
sliceOf := structField.Type().Elem().Kind()
|
||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||
for j := 0; j < numElems; j++ {
|
||||
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
val.Field(i).Set(slice)
|
||||
} else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||
// But also call it here, in case we're dealing with an array of BindUnmarshalers
|
||||
if ok, err := unmarshalField(valueKind, val, structField); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
switch valueKind {
|
||||
case reflect.Ptr:
|
||||
return setWithProperType(structField.Elem().Kind(), val, structField.Elem())
|
||||
case reflect.Int:
|
||||
return setIntField(val, 0, structField)
|
||||
case reflect.Int8:
|
||||
return setIntField(val, 8, structField)
|
||||
case reflect.Int16:
|
||||
return setIntField(val, 16, structField)
|
||||
case reflect.Int32:
|
||||
return setIntField(val, 32, structField)
|
||||
case reflect.Int64:
|
||||
return setIntField(val, 64, structField)
|
||||
case reflect.Uint:
|
||||
return setUintField(val, 0, structField)
|
||||
case reflect.Uint8:
|
||||
return setUintField(val, 8, structField)
|
||||
case reflect.Uint16:
|
||||
return setUintField(val, 16, structField)
|
||||
case reflect.Uint32:
|
||||
return setUintField(val, 32, structField)
|
||||
case reflect.Uint64:
|
||||
return setUintField(val, 64, structField)
|
||||
case reflect.Bool:
|
||||
return setBoolField(val, structField)
|
||||
case reflect.Float32:
|
||||
return setFloatField(val, 32, structField)
|
||||
case reflect.Float64:
|
||||
return setFloatField(val, 64, structField)
|
||||
case reflect.String:
|
||||
structField.SetString(val)
|
||||
default:
|
||||
return errors.New("unknown type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
|
||||
switch valueKind {
|
||||
case reflect.Ptr:
|
||||
return unmarshalFieldPtr(val, field)
|
||||
default:
|
||||
return unmarshalFieldNonPtr(val, field)
|
||||
}
|
||||
}
|
||||
|
||||
// bindUnmarshaler attempts to unmarshal a reflect.Value into a BindUnmarshaler
|
||||
func bindUnmarshaler(field reflect.Value) (BindUnmarshaler, bool) {
|
||||
ptr := reflect.New(field.Type())
|
||||
if ptr.CanInterface() {
|
||||
iface := ptr.Interface()
|
||||
if unmarshaler, ok := iface.(BindUnmarshaler); ok {
|
||||
return unmarshaler, ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
|
||||
if unmarshaler, ok := bindUnmarshaler(field); ok {
|
||||
err := unmarshaler.UnmarshalParam(value)
|
||||
field.Set(reflect.ValueOf(unmarshaler).Elem())
|
||||
return true, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
|
||||
if field.IsNil() {
|
||||
// Initialize the pointer to a nil value
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
return unmarshalFieldNonPtr(value, field.Elem())
|
||||
}
|
||||
|
||||
func setIntField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0"
|
||||
}
|
||||
intVal, err := strconv.ParseInt(value, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetInt(intVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setUintField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0"
|
||||
}
|
||||
uintVal, err := strconv.ParseUint(value, 10, bitSize)
|
||||
if err == nil {
|
||||
field.SetUint(uintVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setBoolField(value string, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "false"
|
||||
}
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
if err == nil {
|
||||
field.SetBool(boolVal)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func setFloatField(value string, bitSize int, field reflect.Value) error {
|
||||
if value == "" {
|
||||
value = "0.0"
|
||||
}
|
||||
floatVal, err := strconv.ParseFloat(value, bitSize)
|
||||
if err == nil {
|
||||
field.SetFloat(floatVal)
|
||||
}
|
||||
return err
|
||||
}
|
600
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
Normal file
600
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
Normal file
@ -0,0 +1,600 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Context represents the context of the current HTTP request. It holds request and
|
||||
// response objects, path, path parameters, data and registered handler.
|
||||
Context interface {
|
||||
// Request returns `*http.Request`.
|
||||
Request() *http.Request
|
||||
|
||||
// SetRequest sets `*http.Request`.
|
||||
SetRequest(r *http.Request)
|
||||
|
||||
// Response returns `*Response`.
|
||||
Response() *Response
|
||||
|
||||
// IsTLS returns true if HTTP connection is TLS otherwise false.
|
||||
IsTLS() bool
|
||||
|
||||
// IsWebSocket returns true if HTTP connection is WebSocket otherwise false.
|
||||
IsWebSocket() bool
|
||||
|
||||
// Scheme returns the HTTP protocol scheme, `http` or `https`.
|
||||
Scheme() string
|
||||
|
||||
// RealIP returns the client's network address based on `X-Forwarded-For`
|
||||
// or `X-Real-IP` request header.
|
||||
RealIP() string
|
||||
|
||||
// Path returns the registered path for the handler.
|
||||
Path() string
|
||||
|
||||
// SetPath sets the registered path for the handler.
|
||||
SetPath(p string)
|
||||
|
||||
// Param returns path parameter by name.
|
||||
Param(name string) string
|
||||
|
||||
// ParamNames returns path parameter names.
|
||||
ParamNames() []string
|
||||
|
||||
// SetParamNames sets path parameter names.
|
||||
SetParamNames(names ...string)
|
||||
|
||||
// ParamValues returns path parameter values.
|
||||
ParamValues() []string
|
||||
|
||||
// SetParamValues sets path parameter values.
|
||||
SetParamValues(values ...string)
|
||||
|
||||
// QueryParam returns the query param for the provided name.
|
||||
QueryParam(name string) string
|
||||
|
||||
// QueryParams returns the query parameters as `url.Values`.
|
||||
QueryParams() url.Values
|
||||
|
||||
// QueryString returns the URL query string.
|
||||
QueryString() string
|
||||
|
||||
// FormValue returns the form field value for the provided name.
|
||||
FormValue(name string) string
|
||||
|
||||
// FormParams returns the form parameters as `url.Values`.
|
||||
FormParams() (url.Values, error)
|
||||
|
||||
// FormFile returns the multipart form file for the provided name.
|
||||
FormFile(name string) (*multipart.FileHeader, error)
|
||||
|
||||
// MultipartForm returns the multipart form.
|
||||
MultipartForm() (*multipart.Form, error)
|
||||
|
||||
// Cookie returns the named cookie provided in the request.
|
||||
Cookie(name string) (*http.Cookie, error)
|
||||
|
||||
// SetCookie adds a `Set-Cookie` header in HTTP response.
|
||||
SetCookie(cookie *http.Cookie)
|
||||
|
||||
// Cookies returns the HTTP cookies sent with the request.
|
||||
Cookies() []*http.Cookie
|
||||
|
||||
// Get retrieves data from the context.
|
||||
Get(key string) interface{}
|
||||
|
||||
// Set saves data in the context.
|
||||
Set(key string, val interface{})
|
||||
|
||||
// Bind binds the request body into provided type `i`. The default binder
|
||||
// does it based on Content-Type header.
|
||||
Bind(i interface{}) error
|
||||
|
||||
// Validate validates provided `i`. It is usually called after `Context#Bind()`.
|
||||
// Validator must be registered using `Echo#Validator`.
|
||||
Validate(i interface{}) error
|
||||
|
||||
// Render renders a template with data and sends a text/html response with status
|
||||
// code. Renderer must be registered using `Echo.Renderer`.
|
||||
Render(code int, name string, data interface{}) error
|
||||
|
||||
// HTML sends an HTTP response with status code.
|
||||
HTML(code int, html string) error
|
||||
|
||||
// HTMLBlob sends an HTTP blob response with status code.
|
||||
HTMLBlob(code int, b []byte) error
|
||||
|
||||
// String sends a string response with status code.
|
||||
String(code int, s string) error
|
||||
|
||||
// JSON sends a JSON response with status code.
|
||||
JSON(code int, i interface{}) error
|
||||
|
||||
// JSONPretty sends a pretty-print JSON with status code.
|
||||
JSONPretty(code int, i interface{}, indent string) error
|
||||
|
||||
// JSONBlob sends a JSON blob response with status code.
|
||||
JSONBlob(code int, b []byte) error
|
||||
|
||||
// JSONP sends a JSONP response with status code. It uses `callback` to construct
|
||||
// the JSONP payload.
|
||||
JSONP(code int, callback string, i interface{}) error
|
||||
|
||||
// JSONPBlob sends a JSONP blob response with status code. It uses `callback`
|
||||
// to construct the JSONP payload.
|
||||
JSONPBlob(code int, callback string, b []byte) error
|
||||
|
||||
// XML sends an XML response with status code.
|
||||
XML(code int, i interface{}) error
|
||||
|
||||
// XMLPretty sends a pretty-print XML with status code.
|
||||
XMLPretty(code int, i interface{}, indent string) error
|
||||
|
||||
// XMLBlob sends an XML blob response with status code.
|
||||
XMLBlob(code int, b []byte) error
|
||||
|
||||
// Blob sends a blob response with status code and content type.
|
||||
Blob(code int, contentType string, b []byte) error
|
||||
|
||||
// Stream sends a streaming response with status code and content type.
|
||||
Stream(code int, contentType string, r io.Reader) error
|
||||
|
||||
// File sends a response with the content of the file.
|
||||
File(file string) error
|
||||
|
||||
// Attachment sends a response as attachment, prompting client to save the
|
||||
// file.
|
||||
Attachment(file string, name string) error
|
||||
|
||||
// Inline sends a response as inline, opening the file in the browser.
|
||||
Inline(file string, name string) error
|
||||
|
||||
// NoContent sends a response with no body and a status code.
|
||||
NoContent(code int) error
|
||||
|
||||
// Redirect redirects the request to a provided URL with status code.
|
||||
Redirect(code int, url string) error
|
||||
|
||||
// Error invokes the registered HTTP error handler. Generally used by middleware.
|
||||
Error(err error)
|
||||
|
||||
// Handler returns the matched handler by router.
|
||||
Handler() HandlerFunc
|
||||
|
||||
// SetHandler sets the matched handler by router.
|
||||
SetHandler(h HandlerFunc)
|
||||
|
||||
// Logger returns the `Logger` instance.
|
||||
Logger() Logger
|
||||
|
||||
// Echo returns the `Echo` instance.
|
||||
Echo() *Echo
|
||||
|
||||
// Reset resets the context after request completes. It must be called along
|
||||
// with `Echo#AcquireContext()` and `Echo#ReleaseContext()`.
|
||||
// See `Echo#ServeHTTP()`
|
||||
Reset(r *http.Request, w http.ResponseWriter)
|
||||
}
|
||||
|
||||
context struct {
|
||||
request *http.Request
|
||||
response *Response
|
||||
path string
|
||||
pnames []string
|
||||
pvalues []string
|
||||
query url.Values
|
||||
handler HandlerFunc
|
||||
store Map
|
||||
echo *Echo
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMemory = 32 << 20 // 32 MB
|
||||
indexPage = "index.html"
|
||||
defaultIndent = " "
|
||||
)
|
||||
|
||||
func (c *context) writeContentType(value string) {
|
||||
header := c.Response().Header()
|
||||
if header.Get(HeaderContentType) == "" {
|
||||
header.Set(HeaderContentType, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *context) Request() *http.Request {
|
||||
return c.request
|
||||
}
|
||||
|
||||
func (c *context) SetRequest(r *http.Request) {
|
||||
c.request = r
|
||||
}
|
||||
|
||||
func (c *context) Response() *Response {
|
||||
return c.response
|
||||
}
|
||||
|
||||
func (c *context) IsTLS() bool {
|
||||
return c.request.TLS != nil
|
||||
}
|
||||
|
||||
func (c *context) IsWebSocket() bool {
|
||||
upgrade := c.request.Header.Get(HeaderUpgrade)
|
||||
return upgrade == "websocket" || upgrade == "Websocket"
|
||||
}
|
||||
|
||||
func (c *context) Scheme() string {
|
||||
// Can't use `r.Request.URL.Scheme`
|
||||
// See: https://groups.google.com/forum/#!topic/golang-nuts/pMUkBlQBDF0
|
||||
if c.IsTLS() {
|
||||
return "https"
|
||||
}
|
||||
if scheme := c.request.Header.Get(HeaderXForwardedProto); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if scheme := c.request.Header.Get(HeaderXForwardedProtocol); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
if ssl := c.request.Header.Get(HeaderXForwardedSsl); ssl == "on" {
|
||||
return "https"
|
||||
}
|
||||
if scheme := c.request.Header.Get(HeaderXUrlScheme); scheme != "" {
|
||||
return scheme
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
func (c *context) RealIP() string {
|
||||
if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" {
|
||||
return strings.Split(ip, ", ")[0]
|
||||
}
|
||||
if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
|
||||
return ip
|
||||
}
|
||||
ra, _, _ := net.SplitHostPort(c.request.RemoteAddr)
|
||||
return ra
|
||||
}
|
||||
|
||||
func (c *context) Path() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *context) SetPath(p string) {
|
||||
c.path = p
|
||||
}
|
||||
|
||||
func (c *context) Param(name string) string {
|
||||
for i, n := range c.pnames {
|
||||
if i < len(c.pvalues) {
|
||||
if n == name {
|
||||
return c.pvalues[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *context) ParamNames() []string {
|
||||
return c.pnames
|
||||
}
|
||||
|
||||
func (c *context) SetParamNames(names ...string) {
|
||||
c.pnames = names
|
||||
}
|
||||
|
||||
func (c *context) ParamValues() []string {
|
||||
return c.pvalues[:len(c.pnames)]
|
||||
}
|
||||
|
||||
func (c *context) SetParamValues(values ...string) {
|
||||
c.pvalues = values
|
||||
}
|
||||
|
||||
func (c *context) QueryParam(name string) string {
|
||||
if c.query == nil {
|
||||
c.query = c.request.URL.Query()
|
||||
}
|
||||
return c.query.Get(name)
|
||||
}
|
||||
|
||||
func (c *context) QueryParams() url.Values {
|
||||
if c.query == nil {
|
||||
c.query = c.request.URL.Query()
|
||||
}
|
||||
return c.query
|
||||
}
|
||||
|
||||
func (c *context) QueryString() string {
|
||||
return c.request.URL.RawQuery
|
||||
}
|
||||
|
||||
func (c *context) FormValue(name string) string {
|
||||
return c.request.FormValue(name)
|
||||
}
|
||||
|
||||
func (c *context) FormParams() (url.Values, error) {
|
||||
if strings.HasPrefix(c.request.Header.Get(HeaderContentType), MIMEMultipartForm) {
|
||||
if err := c.request.ParseMultipartForm(defaultMemory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := c.request.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.request.Form, nil
|
||||
}
|
||||
|
||||
func (c *context) FormFile(name string) (*multipart.FileHeader, error) {
|
||||
_, fh, err := c.request.FormFile(name)
|
||||
return fh, err
|
||||
}
|
||||
|
||||
func (c *context) MultipartForm() (*multipart.Form, error) {
|
||||
err := c.request.ParseMultipartForm(defaultMemory)
|
||||
return c.request.MultipartForm, err
|
||||
}
|
||||
|
||||
func (c *context) Cookie(name string) (*http.Cookie, error) {
|
||||
return c.request.Cookie(name)
|
||||
}
|
||||
|
||||
func (c *context) SetCookie(cookie *http.Cookie) {
|
||||
http.SetCookie(c.Response(), cookie)
|
||||
}
|
||||
|
||||
func (c *context) Cookies() []*http.Cookie {
|
||||
return c.request.Cookies()
|
||||
}
|
||||
|
||||
func (c *context) Get(key string) interface{} {
|
||||
return c.store[key]
|
||||
}
|
||||
|
||||
func (c *context) Set(key string, val interface{}) {
|
||||
if c.store == nil {
|
||||
c.store = make(Map)
|
||||
}
|
||||
c.store[key] = val
|
||||
}
|
||||
|
||||
func (c *context) Bind(i interface{}) error {
|
||||
return c.echo.Binder.Bind(i, c)
|
||||
}
|
||||
|
||||
func (c *context) Validate(i interface{}) error {
|
||||
if c.echo.Validator == nil {
|
||||
return ErrValidatorNotRegistered
|
||||
}
|
||||
return c.echo.Validator.Validate(i)
|
||||
}
|
||||
|
||||
func (c *context) Render(code int, name string, data interface{}) (err error) {
|
||||
if c.echo.Renderer == nil {
|
||||
return ErrRendererNotRegistered
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if err = c.echo.Renderer.Render(buf, name, data, c); err != nil {
|
||||
return
|
||||
}
|
||||
return c.HTMLBlob(code, buf.Bytes())
|
||||
}
|
||||
|
||||
func (c *context) HTML(code int, html string) (err error) {
|
||||
return c.HTMLBlob(code, []byte(html))
|
||||
}
|
||||
|
||||
func (c *context) HTMLBlob(code int, b []byte) (err error) {
|
||||
return c.Blob(code, MIMETextHTMLCharsetUTF8, b)
|
||||
}
|
||||
|
||||
func (c *context) String(code int, s string) (err error) {
|
||||
return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s))
|
||||
}
|
||||
|
||||
func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error) {
|
||||
enc := json.NewEncoder(c.response)
|
||||
_, pretty := c.QueryParams()["pretty"]
|
||||
if c.echo.Debug || pretty {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
c.writeContentType(MIMEApplicationJavaScriptCharsetUTF8)
|
||||
c.response.WriteHeader(code)
|
||||
if _, err = c.response.Write([]byte(callback + "(")); err != nil {
|
||||
return
|
||||
}
|
||||
if err = enc.Encode(i); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = c.response.Write([]byte(");")); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) json(code int, i interface{}, indent string) error {
|
||||
enc := json.NewEncoder(c.response)
|
||||
if indent != "" {
|
||||
enc.SetIndent("", indent)
|
||||
}
|
||||
c.writeContentType(MIMEApplicationJSONCharsetUTF8)
|
||||
c.response.WriteHeader(code)
|
||||
return enc.Encode(i)
|
||||
}
|
||||
|
||||
func (c *context) JSON(code int, i interface{}) (err error) {
|
||||
indent := ""
|
||||
if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty {
|
||||
indent = defaultIndent
|
||||
}
|
||||
return c.json(code, i, indent)
|
||||
}
|
||||
|
||||
func (c *context) JSONPretty(code int, i interface{}, indent string) (err error) {
|
||||
return c.json(code, i, indent)
|
||||
}
|
||||
|
||||
func (c *context) JSONBlob(code int, b []byte) (err error) {
|
||||
return c.Blob(code, MIMEApplicationJSONCharsetUTF8, b)
|
||||
}
|
||||
|
||||
func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
|
||||
return c.jsonPBlob(code, callback, i)
|
||||
}
|
||||
|
||||
func (c *context) JSONPBlob(code int, callback string, b []byte) (err error) {
|
||||
c.writeContentType(MIMEApplicationJavaScriptCharsetUTF8)
|
||||
c.response.WriteHeader(code)
|
||||
if _, err = c.response.Write([]byte(callback + "(")); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = c.response.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = c.response.Write([]byte(");"))
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) xml(code int, i interface{}, indent string) (err error) {
|
||||
c.writeContentType(MIMEApplicationXMLCharsetUTF8)
|
||||
c.response.WriteHeader(code)
|
||||
enc := xml.NewEncoder(c.response)
|
||||
if indent != "" {
|
||||
enc.Indent("", indent)
|
||||
}
|
||||
if _, err = c.response.Write([]byte(xml.Header)); err != nil {
|
||||
return
|
||||
}
|
||||
return enc.Encode(i)
|
||||
}
|
||||
|
||||
func (c *context) XML(code int, i interface{}) (err error) {
|
||||
indent := ""
|
||||
if _, pretty := c.QueryParams()["pretty"]; c.echo.Debug || pretty {
|
||||
indent = defaultIndent
|
||||
}
|
||||
return c.xml(code, i, indent)
|
||||
}
|
||||
|
||||
func (c *context) XMLPretty(code int, i interface{}, indent string) (err error) {
|
||||
return c.xml(code, i, indent)
|
||||
}
|
||||
|
||||
func (c *context) XMLBlob(code int, b []byte) (err error) {
|
||||
c.writeContentType(MIMEApplicationXMLCharsetUTF8)
|
||||
c.response.WriteHeader(code)
|
||||
if _, err = c.response.Write([]byte(xml.Header)); err != nil {
|
||||
return
|
||||
}
|
||||
_, err = c.response.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) Blob(code int, contentType string, b []byte) (err error) {
|
||||
c.writeContentType(contentType)
|
||||
c.response.WriteHeader(code)
|
||||
_, err = c.response.Write(b)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) Stream(code int, contentType string, r io.Reader) (err error) {
|
||||
c.writeContentType(contentType)
|
||||
c.response.WriteHeader(code)
|
||||
_, err = io.Copy(c.response, r)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) File(file string) (err error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return NotFoundHandler(c)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fi, _ := f.Stat()
|
||||
if fi.IsDir() {
|
||||
file = filepath.Join(file, indexPage)
|
||||
f, err = os.Open(file)
|
||||
if err != nil {
|
||||
return NotFoundHandler(c)
|
||||
}
|
||||
defer f.Close()
|
||||
if fi, err = f.Stat(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), f)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *context) Attachment(file, name string) error {
|
||||
return c.contentDisposition(file, name, "attachment")
|
||||
}
|
||||
|
||||
func (c *context) Inline(file, name string) error {
|
||||
return c.contentDisposition(file, name, "inline")
|
||||
}
|
||||
|
||||
func (c *context) contentDisposition(file, name, dispositionType string) error {
|
||||
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
|
||||
return c.File(file)
|
||||
}
|
||||
|
||||
func (c *context) NoContent(code int) error {
|
||||
c.response.WriteHeader(code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) Redirect(code int, url string) error {
|
||||
if code < 300 || code > 308 {
|
||||
return ErrInvalidRedirectCode
|
||||
}
|
||||
c.response.Header().Set(HeaderLocation, url)
|
||||
c.response.WriteHeader(code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) Error(err error) {
|
||||
c.echo.HTTPErrorHandler(err, c)
|
||||
}
|
||||
|
||||
func (c *context) Echo() *Echo {
|
||||
return c.echo
|
||||
}
|
||||
|
||||
func (c *context) Handler() HandlerFunc {
|
||||
return c.handler
|
||||
}
|
||||
|
||||
func (c *context) SetHandler(h HandlerFunc) {
|
||||
c.handler = h
|
||||
}
|
||||
|
||||
func (c *context) Logger() Logger {
|
||||
return c.echo.Logger
|
||||
}
|
||||
|
||||
func (c *context) Reset(r *http.Request, w http.ResponseWriter) {
|
||||
c.request = r
|
||||
c.response.reset(w)
|
||||
c.query = nil
|
||||
c.handler = NotFoundHandler
|
||||
c.store = nil
|
||||
c.path = ""
|
||||
c.pnames = nil
|
||||
// NOTE: Don't reset because it has to have length c.echo.maxParam at all times
|
||||
// c.pvalues = nil
|
||||
}
|
||||
|
782
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
Normal file
782
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
Normal file
@ -0,0 +1,782 @@
|
||||
/*
|
||||
Package echo implements high performance, minimalist Go web framework.
|
||||
|
||||
Example:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
)
|
||||
|
||||
// Handler
|
||||
func hello(c echo.Context) error {
|
||||
return c.String(http.StatusOK, "Hello, World!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Middleware
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
|
||||
// Routes
|
||||
e.GET("/", hello)
|
||||
|
||||
// Start server
|
||||
e.Logger.Fatal(e.Start(":1323"))
|
||||
}
|
||||
|
||||
Learn more at https://echo.labstack.com
|
||||
*/
|
||||
package echo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
stdContext "context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
stdLog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/gommon/color"
|
||||
"github.com/labstack/gommon/log"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
type (
|
||||
// Echo is the top-level framework instance.
|
||||
Echo struct {
|
||||
StdLogger *stdLog.Logger
|
||||
colorer *color.Color
|
||||
premiddleware []MiddlewareFunc
|
||||
middleware []MiddlewareFunc
|
||||
maxParam *int
|
||||
router *Router
|
||||
notFoundHandler HandlerFunc
|
||||
pool sync.Pool
|
||||
Server *http.Server
|
||||
TLSServer *http.Server
|
||||
Listener net.Listener
|
||||
TLSListener net.Listener
|
||||
AutoTLSManager autocert.Manager
|
||||
DisableHTTP2 bool
|
||||
Debug bool
|
||||
HideBanner bool
|
||||
HidePort bool
|
||||
HTTPErrorHandler HTTPErrorHandler
|
||||
Binder Binder
|
||||
Validator Validator
|
||||
Renderer Renderer
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
// Route contains a handler and information for matching against requests.
|
||||
Route struct {
|
||||
Method string `json:"method"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// HTTPError represents an error that occurred while handling a request.
|
||||
HTTPError struct {
|
||||
Code int
|
||||
Message interface{}
|
||||
Internal error // Stores the error returned by an external dependency
|
||||
}
|
||||
|
||||
// MiddlewareFunc defines a function to process middleware.
|
||||
MiddlewareFunc func(HandlerFunc) HandlerFunc
|
||||
|
||||
// HandlerFunc defines a function to serve HTTP requests.
|
||||
HandlerFunc func(Context) error
|
||||
|
||||
// HTTPErrorHandler is a centralized HTTP error handler.
|
||||
HTTPErrorHandler func(error, Context)
|
||||
|
||||
// Validator is the interface that wraps the Validate function.
|
||||
Validator interface {
|
||||
Validate(i interface{}) error
|
||||
}
|
||||
|
||||
// Renderer is the interface that wraps the Render function.
|
||||
Renderer interface {
|
||||
Render(io.Writer, string, interface{}, Context) error
|
||||
}
|
||||
|
||||
// Map defines a generic map of type `map[string]interface{}`.
|
||||
Map map[string]interface{}
|
||||
|
||||
// i is the interface for Echo and Group.
|
||||
i interface {
|
||||
GET(string, HandlerFunc, ...MiddlewareFunc) *Route
|
||||
}
|
||||
)
|
||||
|
||||
// HTTP methods
|
||||
// NOTE: Deprecated, please use the stdlib constants directly instead.
|
||||
const (
|
||||
CONNECT = http.MethodConnect
|
||||
DELETE = http.MethodDelete
|
||||
GET = http.MethodGet
|
||||
HEAD = http.MethodHead
|
||||
OPTIONS = http.MethodOptions
|
||||
PATCH = http.MethodPatch
|
||||
POST = http.MethodPost
|
||||
// PROPFIND = "PROPFIND"
|
||||
PUT = http.MethodPut
|
||||
TRACE = http.MethodTrace
|
||||
)
|
||||
|
||||
// MIME types
|
||||
const (
|
||||
MIMEApplicationJSON = "application/json"
|
||||
MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8
|
||||
MIMEApplicationJavaScript = "application/javascript"
|
||||
MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
|
||||
MIMEApplicationXML = "application/xml"
|
||||
MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + charsetUTF8
|
||||
MIMETextXML = "text/xml"
|
||||
MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + charsetUTF8
|
||||
MIMEApplicationForm = "application/x-www-form-urlencoded"
|
||||
MIMEApplicationProtobuf = "application/protobuf"
|
||||
MIMEApplicationMsgpack = "application/msgpack"
|
||||
MIMETextHTML = "text/html"
|
||||
MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + charsetUTF8
|
||||
MIMETextPlain = "text/plain"
|
||||
MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + charsetUTF8
|
||||
MIMEMultipartForm = "multipart/form-data"
|
||||
MIMEOctetStream = "application/octet-stream"
|
||||
)
|
||||
|
||||
const (
|
||||
charsetUTF8 = "charset=UTF-8"
|
||||
// PROPFIND Method can be used on collection and property resources.
|
||||
PROPFIND = "PROPFIND"
|
||||
)
|
||||
|
||||
// Headers
|
||||
const (
|
||||
HeaderAccept = "Accept"
|
||||
HeaderAcceptEncoding = "Accept-Encoding"
|
||||
HeaderAllow = "Allow"
|
||||
HeaderAuthorization = "Authorization"
|
||||
HeaderContentDisposition = "Content-Disposition"
|
||||
HeaderContentEncoding = "Content-Encoding"
|
||||
HeaderContentLength = "Content-Length"
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderCookie = "Cookie"
|
||||
HeaderSetCookie = "Set-Cookie"
|
||||
HeaderIfModifiedSince = "If-Modified-Since"
|
||||
HeaderLastModified = "Last-Modified"
|
||||
HeaderLocation = "Location"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
HeaderVary = "Vary"
|
||||
HeaderWWWAuthenticate = "WWW-Authenticate"
|
||||
HeaderXForwardedFor = "X-Forwarded-For"
|
||||
HeaderXForwardedProto = "X-Forwarded-Proto"
|
||||
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
|
||||
HeaderXForwardedSsl = "X-Forwarded-Ssl"
|
||||
HeaderXUrlScheme = "X-Url-Scheme"
|
||||
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
|
||||
HeaderXRealIP = "X-Real-IP"
|
||||
HeaderXRequestID = "X-Request-ID"
|
||||
HeaderXRequestedWith = "X-Requested-With"
|
||||
HeaderServer = "Server"
|
||||
HeaderOrigin = "Origin"
|
||||
|
||||
// Access control
|
||||
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
|
||||
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
|
||||
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
|
||||
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
|
||||
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
||||
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
|
||||
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
||||
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
|
||||
|
||||
// Security
|
||||
HeaderStrictTransportSecurity = "Strict-Transport-Security"
|
||||
HeaderXContentTypeOptions = "X-Content-Type-Options"
|
||||
HeaderXXSSProtection = "X-XSS-Protection"
|
||||
HeaderXFrameOptions = "X-Frame-Options"
|
||||
HeaderContentSecurityPolicy = "Content-Security-Policy"
|
||||
HeaderXCSRFToken = "X-CSRF-Token"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version of Echo
|
||||
Version = "4.0.0"
|
||||
website = "https://echo.labstack.com"
|
||||
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
|
||||
banner = `
|
||||
____ __
|
||||
/ __/___/ / ___
|
||||
/ _// __/ _ \/ _ \
|
||||
/___/\__/_//_/\___/ %s
|
||||
High performance, minimalist Go web framework
|
||||
%s
|
||||
____________________________________O/_______
|
||||
O\
|
||||
`
|
||||
)
|
||||
|
||||
var (
|
||||
methods = [...]string{
|
||||
http.MethodConnect,
|
||||
http.MethodDelete,
|
||||
http.MethodGet,
|
||||
http.MethodHead,
|
||||
http.MethodOptions,
|
||||
http.MethodPatch,
|
||||
http.MethodPost,
|
||||
PROPFIND,
|
||||
http.MethodPut,
|
||||
http.MethodTrace,
|
||||
}
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
||||
ErrNotFound = NewHTTPError(http.StatusNotFound)
|
||||
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized)
|
||||
ErrForbidden = NewHTTPError(http.StatusForbidden)
|
||||
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
|
||||
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
|
||||
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests)
|
||||
ErrBadRequest = NewHTTPError(http.StatusBadRequest)
|
||||
ErrBadGateway = NewHTTPError(http.StatusBadGateway)
|
||||
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError)
|
||||
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout)
|
||||
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable)
|
||||
ErrValidatorNotRegistered = errors.New("validator not registered")
|
||||
ErrRendererNotRegistered = errors.New("renderer not registered")
|
||||
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
||||
ErrCookieNotFound = errors.New("cookie not found")
|
||||
)
|
||||
|
||||
// Error handlers
|
||||
var (
|
||||
NotFoundHandler = func(c Context) error {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
MethodNotAllowedHandler = func(c Context) error {
|
||||
return ErrMethodNotAllowed
|
||||
}
|
||||
)
|
||||
|
||||
// New creates an instance of Echo.
|
||||
func New() (e *Echo) {
|
||||
e = &Echo{
|
||||
Server: new(http.Server),
|
||||
TLSServer: new(http.Server),
|
||||
AutoTLSManager: autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
},
|
||||
Logger: log.New("echo"),
|
||||
colorer: color.New(),
|
||||
maxParam: new(int),
|
||||
}
|
||||
e.Server.Handler = e
|
||||
e.TLSServer.Handler = e
|
||||
e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
|
||||
e.Binder = &DefaultBinder{}
|
||||
e.Logger.SetLevel(log.ERROR)
|
||||
e.StdLogger = stdLog.New(e.Logger.Output(), e.Logger.Prefix()+": ", 0)
|
||||
e.pool.New = func() interface{} {
|
||||
return e.NewContext(nil, nil)
|
||||
}
|
||||
e.router = NewRouter(e)
|
||||
return
|
||||
}
|
||||
|
||||
// NewContext returns a Context instance.
|
||||
func (e *Echo) NewContext(r *http.Request, w http.ResponseWriter) Context {
|
||||
return &context{
|
||||
request: r,
|
||||
response: NewResponse(w, e),
|
||||
store: make(Map),
|
||||
echo: e,
|
||||
pvalues: make([]string, *e.maxParam),
|
||||
handler: NotFoundHandler,
|
||||
}
|
||||
}
|
||||
|
||||
// Router returns router.
|
||||
func (e *Echo) Router() *Router {
|
||||
return e.router
|
||||
}
|
||||
|
||||
// DefaultHTTPErrorHandler is the default HTTP error handler. It sends a JSON response
|
||||
// with status code.
|
||||
func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
||||
var (
|
||||
code = http.StatusInternalServerError
|
||||
msg interface{}
|
||||
)
|
||||
|
||||
if he, ok := err.(*HTTPError); ok {
|
||||
code = he.Code
|
||||
msg = he.Message
|
||||
if he.Internal != nil {
|
||||
err = fmt.Errorf("%v, %v", err, he.Internal)
|
||||
}
|
||||
} else if e.Debug {
|
||||
msg = err.Error()
|
||||
} else {
|
||||
msg = http.StatusText(code)
|
||||
}
|
||||
if _, ok := msg.(string); ok {
|
||||
msg = Map{"message": msg}
|
||||
}
|
||||
|
||||
// Send response
|
||||
if !c.Response().Committed {
|
||||
if c.Request().Method == http.MethodHead { // Issue #608
|
||||
err = c.NoContent(code)
|
||||
} else {
|
||||
err = c.JSON(code, msg)
|
||||
}
|
||||
if err != nil {
|
||||
e.Logger.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pre adds middleware to the chain which is run before router.
|
||||
func (e *Echo) Pre(middleware ...MiddlewareFunc) {
|
||||
e.premiddleware = append(e.premiddleware, middleware...)
|
||||
}
|
||||
|
||||
// Use adds middleware to the chain which is run after router.
|
||||
func (e *Echo) Use(middleware ...MiddlewareFunc) {
|
||||
e.middleware = append(e.middleware, middleware...)
|
||||
}
|
||||
|
||||
// CONNECT registers a new CONNECT route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodConnect, path, h, m...)
|
||||
}
|
||||
|
||||
// DELETE registers a new DELETE route for a path with matching handler in the router
|
||||
// with optional route-level middleware.
|
||||
func (e *Echo) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodDelete, path, h, m...)
|
||||
}
|
||||
|
||||
// GET registers a new GET route for a path with matching handler in the router
|
||||
// with optional route-level middleware.
|
||||
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodGet, path, h, m...)
|
||||
}
|
||||
|
||||
// HEAD registers a new HEAD route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodHead, path, h, m...)
|
||||
}
|
||||
|
||||
// OPTIONS registers a new OPTIONS route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodOptions, path, h, m...)
|
||||
}
|
||||
|
||||
// PATCH registers a new PATCH route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodPatch, path, h, m...)
|
||||
}
|
||||
|
||||
// POST registers a new POST route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodPost, path, h, m...)
|
||||
}
|
||||
|
||||
// PUT registers a new PUT route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodPut, path, h, m...)
|
||||
}
|
||||
|
||||
// TRACE registers a new TRACE route for a path with matching handler in the
|
||||
// router with optional route-level middleware.
|
||||
func (e *Echo) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return e.Add(http.MethodTrace, path, h, m...)
|
||||
}
|
||||
|
||||
// Any registers a new route for all HTTP methods and path with matching handler
|
||||
// in the router with optional route-level middleware.
|
||||
func (e *Echo) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
||||
routes := make([]*Route, len(methods))
|
||||
for i, m := range methods {
|
||||
routes[i] = e.Add(m, path, handler, middleware...)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Match registers a new route for multiple HTTP methods and path with matching
|
||||
// handler in the router with optional route-level middleware.
|
||||
func (e *Echo) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
||||
routes := make([]*Route, len(methods))
|
||||
for i, m := range methods {
|
||||
routes[i] = e.Add(m, path, handler, middleware...)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Static registers a new route with path prefix to serve static files from the
|
||||
// provided root directory.
|
||||
func (e *Echo) Static(prefix, root string) *Route {
|
||||
if root == "" {
|
||||
root = "." // For security we want to restrict to CWD.
|
||||
}
|
||||
return static(e, prefix, root)
|
||||
}
|
||||
|
||||
func static(i i, prefix, root string) *Route {
|
||||
h := func(c Context) error {
|
||||
p, err := url.PathUnescape(c.Param("*"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
|
||||
return c.File(name)
|
||||
}
|
||||
i.GET(prefix, h)
|
||||
if prefix == "/" {
|
||||
return i.GET(prefix+"*", h)
|
||||
}
|
||||
|
||||
return i.GET(prefix+"/*", h)
|
||||
}
|
||||
|
||||
// File registers a new route with path to serve a static file with optional route-level middleware.
|
||||
func (e *Echo) File(path, file string, m ...MiddlewareFunc) *Route {
|
||||
return e.GET(path, func(c Context) error {
|
||||
return c.File(file)
|
||||
}, m...)
|
||||
}
|
||||
|
||||
// Add registers a new route for an HTTP method and path with matching handler
|
||||
// in the router with optional route-level middleware.
|
||||
func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
|
||||
name := handlerName(handler)
|
||||
e.router.Add(method, path, func(c Context) error {
|
||||
h := handler
|
||||
// Chain middleware
|
||||
for i := len(middleware) - 1; i >= 0; i-- {
|
||||
h = middleware[i](h)
|
||||
}
|
||||
return h(c)
|
||||
})
|
||||
r := &Route{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Name: name,
|
||||
}
|
||||
e.router.routes[method+path] = r
|
||||
return r
|
||||
}
|
||||
|
||||
// Group creates a new router group with prefix and optional group-level middleware.
|
||||
func (e *Echo) Group(prefix string, m ...MiddlewareFunc) (g *Group) {
|
||||
g = &Group{prefix: prefix, echo: e}
|
||||
g.Use(m...)
|
||||
return
|
||||
}
|
||||
|
||||
// URI generates a URI from handler.
|
||||
func (e *Echo) URI(handler HandlerFunc, params ...interface{}) string {
|
||||
name := handlerName(handler)
|
||||
return e.Reverse(name, params...)
|
||||
}
|
||||
|
||||
// URL is an alias for `URI` function.
|
||||
func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
|
||||
return e.URI(h, params...)
|
||||
}
|
||||
|
||||
// Reverse generates an URL from route name and provided parameters.
|
||||
func (e *Echo) Reverse(name string, params ...interface{}) string {
|
||||
uri := new(bytes.Buffer)
|
||||
ln := len(params)
|
||||
n := 0
|
||||
for _, r := range e.router.routes {
|
||||
if r.Name == name {
|
||||
for i, l := 0, len(r.Path); i < l; i++ {
|
||||
if r.Path[i] == ':' && n < ln {
|
||||
for ; i < l && r.Path[i] != '/'; i++ {
|
||||
}
|
||||
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
||||
n++
|
||||
}
|
||||
if i < l {
|
||||
uri.WriteByte(r.Path[i])
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return uri.String()
|
||||
}
|
||||
|
||||
// Routes returns the registered routes.
|
||||
func (e *Echo) Routes() []*Route {
|
||||
routes := make([]*Route, 0, len(e.router.routes))
|
||||
for _, v := range e.router.routes {
|
||||
routes = append(routes, v)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// AcquireContext returns an empty `Context` instance from the pool.
|
||||
// You must return the context by calling `ReleaseContext()`.
|
||||
func (e *Echo) AcquireContext() Context {
|
||||
return e.pool.Get().(Context)
|
||||
}
|
||||
|
||||
// ReleaseContext returns the `Context` instance back to the pool.
|
||||
// You must call it after `AcquireContext()`.
|
||||
func (e *Echo) ReleaseContext(c Context) {
|
||||
e.pool.Put(c)
|
||||
}
|
||||
|
||||
// ServeHTTP implements `http.Handler` interface, which serves HTTP requests.
|
||||
func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Acquire context
|
||||
c := e.pool.Get().(*context)
|
||||
c.Reset(r, w)
|
||||
|
||||
h := NotFoundHandler
|
||||
|
||||
if e.premiddleware == nil {
|
||||
e.router.Find(r.Method, getPath(r), c)
|
||||
h = c.Handler()
|
||||
for i := len(e.middleware) - 1; i >= 0; i-- {
|
||||
h = e.middleware[i](h)
|
||||
}
|
||||
} else {
|
||||
h = func(c Context) error {
|
||||
e.router.Find(r.Method, getPath(r), c)
|
||||
h := c.Handler()
|
||||
for i := len(e.middleware) - 1; i >= 0; i-- {
|
||||
h = e.middleware[i](h)
|
||||
}
|
||||
return h(c)
|
||||
}
|
||||
for i := len(e.premiddleware) - 1; i >= 0; i-- {
|
||||
h = e.premiddleware[i](h)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute chain
|
||||
if err := h(c); err != nil {
|
||||
e.HTTPErrorHandler(err, c)
|
||||
}
|
||||
|
||||
// Release context
|
||||
e.pool.Put(c)
|
||||
}
|
||||
|
||||
// Start starts an HTTP server.
|
||||
func (e *Echo) Start(address string) error {
|
||||
e.Server.Addr = address
|
||||
return e.StartServer(e.Server)
|
||||
}
|
||||
|
||||
// StartTLS starts an HTTPS server.
|
||||
func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) {
|
||||
if certFile == "" || keyFile == "" {
|
||||
return errors.New("invalid tls configuration")
|
||||
}
|
||||
s := e.TLSServer
|
||||
s.TLSConfig = new(tls.Config)
|
||||
s.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||
s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return e.startTLS(address)
|
||||
}
|
||||
|
||||
// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
|
||||
func (e *Echo) StartAutoTLS(address string) error {
|
||||
s := e.TLSServer
|
||||
s.TLSConfig = new(tls.Config)
|
||||
s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
|
||||
return e.startTLS(address)
|
||||
}
|
||||
|
||||
func (e *Echo) startTLS(address string) error {
|
||||
s := e.TLSServer
|
||||
s.Addr = address
|
||||
if !e.DisableHTTP2 {
|
||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")
|
||||
}
|
||||
return e.StartServer(e.TLSServer)
|
||||
}
|
||||
|
||||
// StartServer starts a custom http server.
|
||||
func (e *Echo) StartServer(s *http.Server) (err error) {
|
||||
// Setup
|
||||
e.colorer.SetOutput(e.Logger.Output())
|
||||
s.ErrorLog = e.StdLogger
|
||||
s.Handler = e
|
||||
if e.Debug {
|
||||
e.Logger.SetLevel(log.DEBUG)
|
||||
}
|
||||
|
||||
if !e.HideBanner {
|
||||
e.colorer.Printf(banner, e.colorer.Red("v"+Version), e.colorer.Blue(website))
|
||||
}
|
||||
|
||||
if s.TLSConfig == nil {
|
||||
if e.Listener == nil {
|
||||
e.Listener, err = newListener(s.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !e.HidePort {
|
||||
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
||||
}
|
||||
return s.Serve(e.Listener)
|
||||
}
|
||||
if e.TLSListener == nil {
|
||||
l, err := newListener(s.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.TLSListener = tls.NewListener(l, s.TLSConfig)
|
||||
}
|
||||
if !e.HidePort {
|
||||
e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
|
||||
}
|
||||
return s.Serve(e.TLSListener)
|
||||
}
|
||||
|
||||
// Close immediately stops the server.
|
||||
// It internally calls `http.Server#Close()`.
|
||||
func (e *Echo) Close() error {
|
||||
if err := e.TLSServer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Server.Close()
|
||||
}
|
||||
|
||||
// Shutdown stops the server gracefully.
|
||||
// It internally calls `http.Server#Shutdown()`.
|
||||
func (e *Echo) Shutdown(ctx stdContext.Context) error {
|
||||
if err := e.TLSServer.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// NewHTTPError creates a new HTTPError instance.
|
||||
func NewHTTPError(code int, message ...interface{}) *HTTPError {
|
||||
he := &HTTPError{Code: code, Message: http.StatusText(code)}
|
||||
if len(message) > 0 {
|
||||
he.Message = message[0]
|
||||
}
|
||||
return he
|
||||
}
|
||||
|
||||
// Error makes it compatible with `error` interface.
|
||||
func (he *HTTPError) Error() string {
|
||||
return fmt.Sprintf("code=%d, message=%v", he.Code, he.Message)
|
||||
}
|
||||
|
||||
// SetInternal sets error to HTTPError.Internal
|
||||
func (he *HTTPError) SetInternal(err error) *HTTPError {
|
||||
he.Internal = err
|
||||
return he
|
||||
}
|
||||
|
||||
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
|
||||
func WrapHandler(h http.Handler) HandlerFunc {
|
||||
return func(c Context) error {
|
||||
h.ServeHTTP(c.Response(), c.Request())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WrapMiddleware wraps `func(http.Handler) http.Handler` into `echo.MiddlewareFunc`
|
||||
func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
|
||||
return func(next HandlerFunc) HandlerFunc {
|
||||
return func(c Context) (err error) {
|
||||
m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c.SetRequest(r)
|
||||
err = next(c)
|
||||
})).ServeHTTP(c.Response(), c.Request())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPath(r *http.Request) string {
|
||||
path := r.URL.RawPath
|
||||
if path == "" {
|
||||
path = r.URL.Path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func handlerName(h HandlerFunc) string {
|
||||
t := reflect.ValueOf(h).Type()
|
||||
if t.Kind() == reflect.Func {
|
||||
return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
|
||||
}
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// // PathUnescape is wraps `url.PathUnescape`
|
||||
// func PathUnescape(s string) (string, error) {
|
||||
// return url.PathUnescape(s)
|
||||
// }
|
||||
|
||||
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||
// connections. It's used by ListenAndServe and ListenAndServeTLS so
|
||||
// dead TCP connections (e.g. closing laptop mid-download) eventually
|
||||
// go away.
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func newListener(address string) (*tcpKeepAliveListener, error) {
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpKeepAliveListener{l.(*net.TCPListener)}, nil
|
||||
}
|
13
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
Normal file
13
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
module github.com/labstack/echo/v4
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/labstack/gommon v0.2.8
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc // indirect
|
||||
)
|
23
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
Normal file
23
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/labstack/gommon v0.2.8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0=
|
||||
github.com/labstack/gommon v0.2.8/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664 h1:YbZJ76lQ1BqNhVe7dKTSB67wDrc2VPRR75IyGyyPDX8=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
121
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
Normal file
121
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
type (
|
||||
// Group is a set of sub-routes for a specified route. It can be used for inner
|
||||
// routes that share a common middleware or functionality that should be separate
|
||||
// from the parent echo instance while still inheriting from it.
|
||||
Group struct {
|
||||
prefix string
|
||||
middleware []MiddlewareFunc
|
||||
echo *Echo
|
||||
}
|
||||
)
|
||||
|
||||
// Use implements `Echo#Use()` for sub-routes within the Group.
|
||||
func (g *Group) Use(middleware ...MiddlewareFunc) {
|
||||
g.middleware = append(g.middleware, middleware...)
|
||||
// Allow all requests to reach the group as they might get dropped if router
|
||||
// doesn't find a match, making none of the group middleware process.
|
||||
for _, p := range []string{"", "/*"} {
|
||||
g.echo.Any(path.Clean(g.prefix+p), func(c Context) error {
|
||||
return NotFoundHandler(c)
|
||||
}, g.middleware...)
|
||||
}
|
||||
}
|
||||
|
||||
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
|
||||
func (g *Group) CONNECT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodConnect, path, h, m...)
|
||||
}
|
||||
|
||||
// DELETE implements `Echo#DELETE()` for sub-routes within the Group.
|
||||
func (g *Group) DELETE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodDelete, path, h, m...)
|
||||
}
|
||||
|
||||
// GET implements `Echo#GET()` for sub-routes within the Group.
|
||||
func (g *Group) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodGet, path, h, m...)
|
||||
}
|
||||
|
||||
// HEAD implements `Echo#HEAD()` for sub-routes within the Group.
|
||||
func (g *Group) HEAD(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodHead, path, h, m...)
|
||||
}
|
||||
|
||||
// OPTIONS implements `Echo#OPTIONS()` for sub-routes within the Group.
|
||||
func (g *Group) OPTIONS(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodOptions, path, h, m...)
|
||||
}
|
||||
|
||||
// PATCH implements `Echo#PATCH()` for sub-routes within the Group.
|
||||
func (g *Group) PATCH(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodPatch, path, h, m...)
|
||||
}
|
||||
|
||||
// POST implements `Echo#POST()` for sub-routes within the Group.
|
||||
func (g *Group) POST(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodPost, path, h, m...)
|
||||
}
|
||||
|
||||
// PUT implements `Echo#PUT()` for sub-routes within the Group.
|
||||
func (g *Group) PUT(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodPut, path, h, m...)
|
||||
}
|
||||
|
||||
// TRACE implements `Echo#TRACE()` for sub-routes within the Group.
|
||||
func (g *Group) TRACE(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
|
||||
return g.Add(http.MethodTrace, path, h, m...)
|
||||
}
|
||||
|
||||
// Any implements `Echo#Any()` for sub-routes within the Group.
|
||||
func (g *Group) Any(path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
||||
routes := make([]*Route, len(methods))
|
||||
for i, m := range methods {
|
||||
routes[i] = g.Add(m, path, handler, middleware...)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Match implements `Echo#Match()` for sub-routes within the Group.
|
||||
func (g *Group) Match(methods []string, path string, handler HandlerFunc, middleware ...MiddlewareFunc) []*Route {
|
||||
routes := make([]*Route, len(methods))
|
||||
for i, m := range methods {
|
||||
routes[i] = g.Add(m, path, handler, middleware...)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// Group creates a new sub-group with prefix and optional sub-group-level middleware.
|
||||
func (g *Group) Group(prefix string, middleware ...MiddlewareFunc) *Group {
|
||||
m := make([]MiddlewareFunc, 0, len(g.middleware)+len(middleware))
|
||||
m = append(m, g.middleware...)
|
||||
m = append(m, middleware...)
|
||||
return g.echo.Group(g.prefix+prefix, m...)
|
||||
}
|
||||
|
||||
// Static implements `Echo#Static()` for sub-routes within the Group.
|
||||
func (g *Group) Static(prefix, root string) {
|
||||
static(g, prefix, root)
|
||||
}
|
||||
|
||||
// File implements `Echo#File()` for sub-routes within the Group.
|
||||
func (g *Group) File(path, file string) {
|
||||
g.echo.File(g.prefix+path, file)
|
||||
}
|
||||
|
||||
// Add implements `Echo#Add()` for sub-routes within the Group.
|
||||
func (g *Group) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
|
||||
// Combine into a new slice to avoid accidentally passing the same slice for
|
||||
// multiple routes, which would lead to later add() calls overwriting the
|
||||
// middleware from earlier calls.
|
||||
m := make([]MiddlewareFunc, 0, len(g.middleware)+len(middleware))
|
||||
m = append(m, g.middleware...)
|
||||
m = append(m, middleware...)
|
||||
return g.echo.Add(method, g.prefix+path, handler, m...)
|
||||
}
|
41
vendor/github.com/labstack/echo/v4/log.go
generated
vendored
Normal file
41
vendor/github.com/labstack/echo/v4/log.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/labstack/gommon/log"
|
||||
)
|
||||
|
||||
type (
|
||||
// Logger defines the logging interface.
|
||||
Logger interface {
|
||||
Output() io.Writer
|
||||
SetOutput(w io.Writer)
|
||||
Prefix() string
|
||||
SetPrefix(p string)
|
||||
Level() log.Lvl
|
||||
SetLevel(v log.Lvl)
|
||||
SetHeader(h string)
|
||||
Print(i ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Printj(j log.JSON)
|
||||
Debug(i ...interface{})
|
||||
Debugf(format string, args ...interface{})
|
||||
Debugj(j log.JSON)
|
||||
Info(i ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Infoj(j log.JSON)
|
||||
Warn(i ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Warnj(j log.JSON)
|
||||
Error(i ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Errorj(j log.JSON)
|
||||
Fatal(i ...interface{})
|
||||
Fatalj(j log.JSON)
|
||||
Fatalf(format string, args ...interface{})
|
||||
Panic(i ...interface{})
|
||||
Panicj(j log.JSON)
|
||||
Panicf(format string, args ...interface{})
|
||||
}
|
||||
)
|
106
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
Normal file
106
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// BasicAuthConfig defines the config for BasicAuth middleware.
|
||||
BasicAuthConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Validator is a function to validate BasicAuth credentials.
|
||||
// Required.
|
||||
Validator BasicAuthValidator
|
||||
|
||||
// Realm is a string to define realm attribute of BasicAuth.
|
||||
// Default value "Restricted".
|
||||
Realm string
|
||||
}
|
||||
|
||||
// BasicAuthValidator defines a function to validate BasicAuth credentials.
|
||||
BasicAuthValidator func(string, string, echo.Context) (bool, error)
|
||||
)
|
||||
|
||||
const (
|
||||
basic = "basic"
|
||||
defaultRealm = "Restricted"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultBasicAuthConfig is the default BasicAuth middleware config.
|
||||
DefaultBasicAuthConfig = BasicAuthConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Realm: defaultRealm,
|
||||
}
|
||||
)
|
||||
|
||||
// BasicAuth returns an BasicAuth middleware.
|
||||
//
|
||||
// For valid credentials it calls the next handler.
|
||||
// For missing or invalid credentials, it sends "401 - Unauthorized" response.
|
||||
func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
|
||||
c := DefaultBasicAuthConfig
|
||||
c.Validator = fn
|
||||
return BasicAuthWithConfig(c)
|
||||
}
|
||||
|
||||
// BasicAuthWithConfig returns an BasicAuth middleware with config.
|
||||
// See `BasicAuth()`.
|
||||
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Validator == nil {
|
||||
panic("echo: basic-auth middleware requires a validator function")
|
||||
}
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultBasicAuthConfig.Skipper
|
||||
}
|
||||
if config.Realm == "" {
|
||||
config.Realm = defaultRealm
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
||||
l := len(basic)
|
||||
|
||||
if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
|
||||
b, err := base64.StdEncoding.DecodeString(auth[l+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cred := string(b)
|
||||
for i := 0; i < len(cred); i++ {
|
||||
if cred[i] == ':' {
|
||||
// Verify credentials
|
||||
valid, err := config.Validator(cred[:i], cred[i+1:], c)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if valid {
|
||||
return next(c)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
realm := defaultRealm
|
||||
if config.Realm != defaultRealm {
|
||||
realm = strconv.Quote(config.Realm)
|
||||
}
|
||||
|
||||
// Need to return `401` for browsers to pop-up login box.
|
||||
c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm)
|
||||
return echo.ErrUnauthorized
|
||||
}
|
||||
}
|
||||
}
|
107
vendor/github.com/labstack/echo/v4/middleware/body_dump.go
generated
vendored
Normal file
107
vendor/github.com/labstack/echo/v4/middleware/body_dump.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// BodyDumpConfig defines the config for BodyDump middleware.
|
||||
BodyDumpConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Handler receives request and response payload.
|
||||
// Required.
|
||||
Handler BodyDumpHandler
|
||||
}
|
||||
|
||||
// BodyDumpHandler receives the request and response payload.
|
||||
BodyDumpHandler func(echo.Context, []byte, []byte)
|
||||
|
||||
bodyDumpResponseWriter struct {
|
||||
io.Writer
|
||||
http.ResponseWriter
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultBodyDumpConfig is the default BodyDump middleware config.
|
||||
DefaultBodyDumpConfig = BodyDumpConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
}
|
||||
)
|
||||
|
||||
// BodyDump returns a BodyDump middleware.
|
||||
//
|
||||
// BodyLimit middleware captures the request and response payload and calls the
|
||||
// registered handler.
|
||||
func BodyDump(handler BodyDumpHandler) echo.MiddlewareFunc {
|
||||
c := DefaultBodyDumpConfig
|
||||
c.Handler = handler
|
||||
return BodyDumpWithConfig(c)
|
||||
}
|
||||
|
||||
// BodyDumpWithConfig returns a BodyDump middleware with config.
|
||||
// See: `BodyDump()`.
|
||||
func BodyDumpWithConfig(config BodyDumpConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Handler == nil {
|
||||
panic("echo: body-dump middleware requires a handler function")
|
||||
}
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultBodyDumpConfig.Skipper
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Request
|
||||
reqBody := []byte{}
|
||||
if c.Request().Body != nil { // Read
|
||||
reqBody, _ = ioutil.ReadAll(c.Request().Body)
|
||||
}
|
||||
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // Reset
|
||||
|
||||
// Response
|
||||
resBody := new(bytes.Buffer)
|
||||
mw := io.MultiWriter(c.Response().Writer, resBody)
|
||||
writer := &bodyDumpResponseWriter{Writer: mw, ResponseWriter: c.Response().Writer}
|
||||
c.Response().Writer = writer
|
||||
|
||||
if err = next(c); err != nil {
|
||||
c.Error(err)
|
||||
}
|
||||
|
||||
// Callback
|
||||
config.Handler(c, reqBody, resBody.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *bodyDumpResponseWriter) WriteHeader(code int) {
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *bodyDumpResponseWriter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
117
vendor/github.com/labstack/echo/v4/middleware/body_limit.go
generated
vendored
Normal file
117
vendor/github.com/labstack/echo/v4/middleware/body_limit.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/bytes"
|
||||
)
|
||||
|
||||
type (
|
||||
// BodyLimitConfig defines the config for BodyLimit middleware.
|
||||
BodyLimitConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Maximum allowed size for a request body, it can be specified
|
||||
// as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P.
|
||||
Limit string `yaml:"limit"`
|
||||
limit int64
|
||||
}
|
||||
|
||||
limitedReader struct {
|
||||
BodyLimitConfig
|
||||
reader io.ReadCloser
|
||||
read int64
|
||||
context echo.Context
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultBodyLimitConfig is the default BodyLimit middleware config.
|
||||
DefaultBodyLimitConfig = BodyLimitConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
}
|
||||
)
|
||||
|
||||
// BodyLimit returns a BodyLimit middleware.
|
||||
//
|
||||
// BodyLimit middleware sets the maximum allowed size for a request body, if the
|
||||
// size exceeds the configured limit, it sends "413 - Request Entity Too Large"
|
||||
// response. The BodyLimit is determined based on both `Content-Length` request
|
||||
// header and actual content read, which makes it super secure.
|
||||
// Limit can be specified as `4x` or `4xB`, where x is one of the multiple from K, M,
|
||||
// G, T or P.
|
||||
func BodyLimit(limit string) echo.MiddlewareFunc {
|
||||
c := DefaultBodyLimitConfig
|
||||
c.Limit = limit
|
||||
return BodyLimitWithConfig(c)
|
||||
}
|
||||
|
||||
// BodyLimitWithConfig returns a BodyLimit middleware with config.
|
||||
// See: `BodyLimit()`.
|
||||
func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultBodyLimitConfig.Skipper
|
||||
}
|
||||
|
||||
limit, err := bytes.Parse(config.Limit)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("echo: invalid body-limit=%s", config.Limit))
|
||||
}
|
||||
config.limit = limit
|
||||
pool := limitedReaderPool(config)
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
|
||||
// Based on content length
|
||||
if req.ContentLength > config.limit {
|
||||
return echo.ErrStatusRequestEntityTooLarge
|
||||
}
|
||||
|
||||
// Based on content read
|
||||
r := pool.Get().(*limitedReader)
|
||||
r.Reset(req.Body, c)
|
||||
defer pool.Put(r)
|
||||
req.Body = r
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *limitedReader) Read(b []byte) (n int, err error) {
|
||||
n, err = r.reader.Read(b)
|
||||
r.read += int64(n)
|
||||
if r.read > r.limit {
|
||||
return n, echo.ErrStatusRequestEntityTooLarge
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *limitedReader) Close() error {
|
||||
return r.reader.Close()
|
||||
}
|
||||
|
||||
func (r *limitedReader) Reset(reader io.ReadCloser, context echo.Context) {
|
||||
r.reader = reader
|
||||
r.context = context
|
||||
r.read = 0
|
||||
}
|
||||
|
||||
func limitedReaderPool(c BodyLimitConfig) sync.Pool {
|
||||
return sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &limitedReader{BodyLimitConfig: c}
|
||||
},
|
||||
}
|
||||
}
|
118
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
Normal file
118
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// GzipConfig defines the config for Gzip middleware.
|
||||
GzipConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Gzip compression level.
|
||||
// Optional. Default value -1.
|
||||
Level int `yaml:"level"`
|
||||
}
|
||||
|
||||
gzipResponseWriter struct {
|
||||
io.Writer
|
||||
http.ResponseWriter
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
gzipScheme = "gzip"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultGzipConfig is the default Gzip middleware config.
|
||||
DefaultGzipConfig = GzipConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Level: -1,
|
||||
}
|
||||
)
|
||||
|
||||
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
||||
// scheme.
|
||||
func Gzip() echo.MiddlewareFunc {
|
||||
return GzipWithConfig(DefaultGzipConfig)
|
||||
}
|
||||
|
||||
// GzipWithConfig return Gzip middleware with config.
|
||||
// See: `Gzip()`.
|
||||
func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultGzipConfig.Skipper
|
||||
}
|
||||
if config.Level == 0 {
|
||||
config.Level = DefaultGzipConfig.Level
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
res := c.Response()
|
||||
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
|
||||
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
|
||||
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
||||
rw := res.Writer
|
||||
w, err := gzip.NewWriterLevel(rw, config.Level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if res.Size == 0 {
|
||||
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
||||
res.Header().Del(echo.HeaderContentEncoding)
|
||||
}
|
||||
// We have to reset response to it's pristine state when
|
||||
// nothing is written to body or error is returned.
|
||||
// See issue #424, #407.
|
||||
res.Writer = rw
|
||||
w.Reset(ioutil.Discard)
|
||||
}
|
||||
w.Close()
|
||||
}()
|
||||
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
||||
res.Writer = grw
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *gzipResponseWriter) WriteHeader(code int) {
|
||||
if code == http.StatusNoContent { // Issue #489
|
||||
w.ResponseWriter.Header().Del(echo.HeaderContentEncoding)
|
||||
}
|
||||
w.Header().Del(echo.HeaderContentLength) // Issue #444
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
||||
if w.Header().Get(echo.HeaderContentType) == "" {
|
||||
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
|
||||
}
|
||||
return w.Writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *gzipResponseWriter) Flush() {
|
||||
w.Writer.(*gzip.Writer).Flush()
|
||||
}
|
||||
|
||||
func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
143
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
Normal file
143
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// CORSConfig defines the config for CORS middleware.
|
||||
CORSConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// AllowOrigin defines a list of origins that may access the resource.
|
||||
// Optional. Default value []string{"*"}.
|
||||
AllowOrigins []string `yaml:"allow_origins"`
|
||||
|
||||
// AllowMethods defines a list methods allowed when accessing the resource.
|
||||
// This is used in response to a preflight request.
|
||||
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
||||
AllowMethods []string `yaml:"allow_methods"`
|
||||
|
||||
// AllowHeaders defines a list of request headers that can be used when
|
||||
// making the actual request. This is in response to a preflight request.
|
||||
// Optional. Default value []string{}.
|
||||
AllowHeaders []string `yaml:"allow_headers"`
|
||||
|
||||
// AllowCredentials indicates whether or not the response to the request
|
||||
// can be exposed when the credentials flag is true. When used as part of
|
||||
// a response to a preflight request, this indicates whether or not the
|
||||
// actual request can be made using credentials.
|
||||
// Optional. Default value false.
|
||||
AllowCredentials bool `yaml:"allow_credentials"`
|
||||
|
||||
// ExposeHeaders defines a whitelist headers that clients are allowed to
|
||||
// access.
|
||||
// Optional. Default value []string{}.
|
||||
ExposeHeaders []string `yaml:"expose_headers"`
|
||||
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached.
|
||||
// Optional. Default value 0.
|
||||
MaxAge int `yaml:"max_age"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultCORSConfig is the default CORS middleware config.
|
||||
DefaultCORSConfig = CORSConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||
}
|
||||
)
|
||||
|
||||
// CORS returns a Cross-Origin Resource Sharing (CORS) middleware.
|
||||
// See: https://developer.mozilla.org/en/docs/Web/HTTP/Access_control_CORS
|
||||
func CORS() echo.MiddlewareFunc {
|
||||
return CORSWithConfig(DefaultCORSConfig)
|
||||
}
|
||||
|
||||
// CORSWithConfig returns a CORS middleware with config.
|
||||
// See: `CORS()`.
|
||||
func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultCORSConfig.Skipper
|
||||
}
|
||||
if len(config.AllowOrigins) == 0 {
|
||||
config.AllowOrigins = DefaultCORSConfig.AllowOrigins
|
||||
}
|
||||
if len(config.AllowMethods) == 0 {
|
||||
config.AllowMethods = DefaultCORSConfig.AllowMethods
|
||||
}
|
||||
|
||||
allowMethods := strings.Join(config.AllowMethods, ",")
|
||||
allowHeaders := strings.Join(config.AllowHeaders, ",")
|
||||
exposeHeaders := strings.Join(config.ExposeHeaders, ",")
|
||||
maxAge := strconv.Itoa(config.MaxAge)
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
origin := req.Header.Get(echo.HeaderOrigin)
|
||||
allowOrigin := ""
|
||||
|
||||
// Check allowed origins
|
||||
for _, o := range config.AllowOrigins {
|
||||
if o == "*" && config.AllowCredentials {
|
||||
allowOrigin = origin
|
||||
break
|
||||
}
|
||||
if o == "*" || o == origin {
|
||||
allowOrigin = o
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Simple request
|
||||
if req.Method != http.MethodOptions {
|
||||
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||
if config.AllowCredentials {
|
||||
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
||||
}
|
||||
if exposeHeaders != "" {
|
||||
res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders)
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Preflight request
|
||||
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
|
||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
|
||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||
res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods)
|
||||
if config.AllowCredentials {
|
||||
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
||||
}
|
||||
if allowHeaders != "" {
|
||||
res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders)
|
||||
} else {
|
||||
h := req.Header.Get(echo.HeaderAccessControlRequestHeaders)
|
||||
if h != "" {
|
||||
res.Header().Set(echo.HeaderAccessControlAllowHeaders, h)
|
||||
}
|
||||
}
|
||||
if config.MaxAge > 0 {
|
||||
res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge)
|
||||
}
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
}
|
210
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
Normal file
210
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
Normal file
@ -0,0 +1,210 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/random"
|
||||
)
|
||||
|
||||
type (
|
||||
// CSRFConfig defines the config for CSRF middleware.
|
||||
CSRFConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// TokenLength is the length of the generated token.
|
||||
TokenLength uint8 `yaml:"token_length"`
|
||||
// Optional. Default value 32.
|
||||
|
||||
// TokenLookup is a string in the form of "<source>:<key>" that is used
|
||||
// to extract token from the request.
|
||||
// Optional. Default value "header:X-CSRF-Token".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "form:<name>"
|
||||
// - "query:<name>"
|
||||
TokenLookup string `yaml:"token_lookup"`
|
||||
|
||||
// Context key to store generated CSRF token into context.
|
||||
// Optional. Default value "csrf".
|
||||
ContextKey string `yaml:"context_key"`
|
||||
|
||||
// Name of the CSRF cookie. This cookie will store CSRF token.
|
||||
// Optional. Default value "csrf".
|
||||
CookieName string `yaml:"cookie_name"`
|
||||
|
||||
// Domain of the CSRF cookie.
|
||||
// Optional. Default value none.
|
||||
CookieDomain string `yaml:"cookie_domain"`
|
||||
|
||||
// Path of the CSRF cookie.
|
||||
// Optional. Default value none.
|
||||
CookiePath string `yaml:"cookie_path"`
|
||||
|
||||
// Max age (in seconds) of the CSRF cookie.
|
||||
// Optional. Default value 86400 (24hr).
|
||||
CookieMaxAge int `yaml:"cookie_max_age"`
|
||||
|
||||
// Indicates if CSRF cookie is secure.
|
||||
// Optional. Default value false.
|
||||
CookieSecure bool `yaml:"cookie_secure"`
|
||||
|
||||
// Indicates if CSRF cookie is HTTP only.
|
||||
// Optional. Default value false.
|
||||
CookieHTTPOnly bool `yaml:"cookie_http_only"`
|
||||
}
|
||||
|
||||
// csrfTokenExtractor defines a function that takes `echo.Context` and returns
|
||||
// either a token or an error.
|
||||
csrfTokenExtractor func(echo.Context) (string, error)
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultCSRFConfig is the default CSRF middleware config.
|
||||
DefaultCSRFConfig = CSRFConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
TokenLength: 32,
|
||||
TokenLookup: "header:" + echo.HeaderXCSRFToken,
|
||||
ContextKey: "csrf",
|
||||
CookieName: "_csrf",
|
||||
CookieMaxAge: 86400,
|
||||
}
|
||||
)
|
||||
|
||||
// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
|
||||
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||
func CSRF() echo.MiddlewareFunc {
|
||||
c := DefaultCSRFConfig
|
||||
return CSRFWithConfig(c)
|
||||
}
|
||||
|
||||
// CSRFWithConfig returns a CSRF middleware with config.
|
||||
// See `CSRF()`.
|
||||
func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultCSRFConfig.Skipper
|
||||
}
|
||||
if config.TokenLength == 0 {
|
||||
config.TokenLength = DefaultCSRFConfig.TokenLength
|
||||
}
|
||||
if config.TokenLookup == "" {
|
||||
config.TokenLookup = DefaultCSRFConfig.TokenLookup
|
||||
}
|
||||
if config.ContextKey == "" {
|
||||
config.ContextKey = DefaultCSRFConfig.ContextKey
|
||||
}
|
||||
if config.CookieName == "" {
|
||||
config.CookieName = DefaultCSRFConfig.CookieName
|
||||
}
|
||||
if config.CookieMaxAge == 0 {
|
||||
config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
|
||||
}
|
||||
|
||||
// Initialize
|
||||
parts := strings.Split(config.TokenLookup, ":")
|
||||
extractor := csrfTokenFromHeader(parts[1])
|
||||
switch parts[0] {
|
||||
case "form":
|
||||
extractor = csrfTokenFromForm(parts[1])
|
||||
case "query":
|
||||
extractor = csrfTokenFromQuery(parts[1])
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
k, err := c.Cookie(config.CookieName)
|
||||
token := ""
|
||||
|
||||
// Generate token
|
||||
if err != nil {
|
||||
token = random.String(config.TokenLength)
|
||||
} else {
|
||||
// Reuse token
|
||||
token = k.Value
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
|
||||
default:
|
||||
// Validate token only for requests which are not defined as 'safe' by RFC7231
|
||||
clientToken, err := extractor(c)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if !validateCSRFToken(token, clientToken) {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
|
||||
}
|
||||
}
|
||||
|
||||
// Set CSRF cookie
|
||||
cookie := new(http.Cookie)
|
||||
cookie.Name = config.CookieName
|
||||
cookie.Value = token
|
||||
if config.CookiePath != "" {
|
||||
cookie.Path = config.CookiePath
|
||||
}
|
||||
if config.CookieDomain != "" {
|
||||
cookie.Domain = config.CookieDomain
|
||||
}
|
||||
cookie.Expires = time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)
|
||||
cookie.Secure = config.CookieSecure
|
||||
cookie.HttpOnly = config.CookieHTTPOnly
|
||||
c.SetCookie(cookie)
|
||||
|
||||
// Store token in the context
|
||||
c.Set(config.ContextKey, token)
|
||||
|
||||
// Protect clients from caching the response
|
||||
c.Response().Header().Add(echo.HeaderVary, echo.HeaderCookie)
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
|
||||
// provided request header.
|
||||
func csrfTokenFromHeader(header string) csrfTokenExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
return c.Request().Header.Get(header), nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfTokenFromForm returns a `csrfTokenExtractor` that extracts token from the
|
||||
// provided form parameter.
|
||||
func csrfTokenFromForm(param string) csrfTokenExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
token := c.FormValue(param)
|
||||
if token == "" {
|
||||
return "", errors.New("missing csrf token in the form parameter")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// csrfTokenFromQuery returns a `csrfTokenExtractor` that extracts token from the
|
||||
// provided query parameter.
|
||||
func csrfTokenFromQuery(param string) csrfTokenExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
token := c.QueryParam(param)
|
||||
if token == "" {
|
||||
return "", errors.New("missing csrf token in the query string")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
func validateCSRFToken(token, clientToken string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(token), []byte(clientToken)) == 1
|
||||
}
|
227
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
Normal file
227
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// JWTConfig defines the config for JWT middleware.
|
||||
JWTConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// BeforeFunc defines a function which is executed just before the middleware.
|
||||
BeforeFunc BeforeFunc
|
||||
|
||||
// SuccessHandler defines a function which is executed for a valid token.
|
||||
SuccessHandler JWTSuccessHandler
|
||||
|
||||
// ErrorHandler defines a function which is executed for an invalid token.
|
||||
// It may be used to define a custom JWT error.
|
||||
ErrorHandler JWTErrorHandler
|
||||
|
||||
// Signing key to validate token.
|
||||
// Required.
|
||||
SigningKey interface{}
|
||||
|
||||
// Signing method, used to check token signing method.
|
||||
// Optional. Default value HS256.
|
||||
SigningMethod string
|
||||
|
||||
// Context key to store user information from the token into context.
|
||||
// Optional. Default value "user".
|
||||
ContextKey string
|
||||
|
||||
// Claims are extendable claims data defining token content.
|
||||
// Optional. Default value jwt.MapClaims
|
||||
Claims jwt.Claims
|
||||
|
||||
// TokenLookup is a string in the form of "<source>:<name>" that is used
|
||||
// to extract token from the request.
|
||||
// Optional. Default value "header:Authorization".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "cookie:<name>"
|
||||
TokenLookup string
|
||||
|
||||
// AuthScheme to be used in the Authorization header.
|
||||
// Optional. Default value "Bearer".
|
||||
AuthScheme string
|
||||
|
||||
keyFunc jwt.Keyfunc
|
||||
}
|
||||
|
||||
// JWTSuccessHandler defines a function which is executed for a valid token.
|
||||
JWTSuccessHandler func(echo.Context)
|
||||
|
||||
// JWTErrorHandler defines a function which is executed for an invalid token.
|
||||
JWTErrorHandler func(error) error
|
||||
|
||||
jwtExtractor func(echo.Context) (string, error)
|
||||
)
|
||||
|
||||
// Algorithms
|
||||
const (
|
||||
AlgorithmHS256 = "HS256"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultJWTConfig is the default JWT auth middleware config.
|
||||
DefaultJWTConfig = JWTConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
SigningMethod: AlgorithmHS256,
|
||||
ContextKey: "user",
|
||||
TokenLookup: "header:" + echo.HeaderAuthorization,
|
||||
AuthScheme: "Bearer",
|
||||
Claims: jwt.MapClaims{},
|
||||
}
|
||||
)
|
||||
|
||||
// JWT returns a JSON Web Token (JWT) auth middleware.
|
||||
//
|
||||
// For valid token, it sets the user in context and calls next handler.
|
||||
// For invalid token, it returns "401 - Unauthorized" error.
|
||||
// For missing token, it returns "400 - Bad Request" error.
|
||||
//
|
||||
// See: https://jwt.io/introduction
|
||||
// See `JWTConfig.TokenLookup`
|
||||
func JWT(key interface{}) echo.MiddlewareFunc {
|
||||
c := DefaultJWTConfig
|
||||
c.SigningKey = key
|
||||
return JWTWithConfig(c)
|
||||
}
|
||||
|
||||
// JWTWithConfig returns a JWT auth middleware with config.
|
||||
// See: `JWT()`.
|
||||
func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultJWTConfig.Skipper
|
||||
}
|
||||
if config.SigningKey == nil {
|
||||
panic("echo: jwt middleware requires signing key")
|
||||
}
|
||||
if config.SigningMethod == "" {
|
||||
config.SigningMethod = DefaultJWTConfig.SigningMethod
|
||||
}
|
||||
if config.ContextKey == "" {
|
||||
config.ContextKey = DefaultJWTConfig.ContextKey
|
||||
}
|
||||
if config.Claims == nil {
|
||||
config.Claims = DefaultJWTConfig.Claims
|
||||
}
|
||||
if config.TokenLookup == "" {
|
||||
config.TokenLookup = DefaultJWTConfig.TokenLookup
|
||||
}
|
||||
if config.AuthScheme == "" {
|
||||
config.AuthScheme = DefaultJWTConfig.AuthScheme
|
||||
}
|
||||
config.keyFunc = func(t *jwt.Token) (interface{}, error) {
|
||||
// Check the signing method
|
||||
if t.Method.Alg() != config.SigningMethod {
|
||||
return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"])
|
||||
}
|
||||
return config.SigningKey, nil
|
||||
}
|
||||
|
||||
// Initialize
|
||||
parts := strings.Split(config.TokenLookup, ":")
|
||||
extractor := jwtFromHeader(parts[1], config.AuthScheme)
|
||||
switch parts[0] {
|
||||
case "query":
|
||||
extractor = jwtFromQuery(parts[1])
|
||||
case "cookie":
|
||||
extractor = jwtFromCookie(parts[1])
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
if config.BeforeFunc != nil {
|
||||
config.BeforeFunc(c)
|
||||
}
|
||||
|
||||
auth, err := extractor(c)
|
||||
if err != nil {
|
||||
if config.ErrorHandler != nil {
|
||||
return config.ErrorHandler(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
token := new(jwt.Token)
|
||||
// Issue #647, #656
|
||||
if _, ok := config.Claims.(jwt.MapClaims); ok {
|
||||
token, err = jwt.Parse(auth, config.keyFunc)
|
||||
} else {
|
||||
t := reflect.ValueOf(config.Claims).Type().Elem()
|
||||
claims := reflect.New(t).Interface().(jwt.Claims)
|
||||
token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc)
|
||||
}
|
||||
if err == nil && token.Valid {
|
||||
// Store user information from token into context.
|
||||
c.Set(config.ContextKey, token)
|
||||
if config.SuccessHandler != nil {
|
||||
config.SuccessHandler(c)
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
if config.ErrorHandler != nil {
|
||||
return config.ErrorHandler(err)
|
||||
}
|
||||
return &echo.HTTPError{
|
||||
Code: http.StatusUnauthorized,
|
||||
Message: "invalid or expired jwt",
|
||||
Internal: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
|
||||
func jwtFromHeader(header string, authScheme string) jwtExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
auth := c.Request().Header.Get(header)
|
||||
l := len(authScheme)
|
||||
if len(auth) > l+1 && auth[:l] == authScheme {
|
||||
return auth[l+1:], nil
|
||||
}
|
||||
return "", ErrJWTMissing
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string.
|
||||
func jwtFromQuery(param string) jwtExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
token := c.QueryParam(param)
|
||||
if token == "" {
|
||||
return "", ErrJWTMissing
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie.
|
||||
func jwtFromCookie(name string) jwtExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
cookie, err := c.Cookie(name)
|
||||
if err != nil {
|
||||
return "", ErrJWTMissing
|
||||
}
|
||||
return cookie.Value, nil
|
||||
}
|
||||
}
|
150
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
Normal file
150
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// KeyAuthConfig defines the config for KeyAuth middleware.
|
||||
KeyAuthConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// KeyLookup is a string in the form of "<source>:<name>" that is used
|
||||
// to extract key from the request.
|
||||
// Optional. Default value "header:Authorization".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "form:<name>"
|
||||
KeyLookup string `yaml:"key_lookup"`
|
||||
|
||||
// AuthScheme to be used in the Authorization header.
|
||||
// Optional. Default value "Bearer".
|
||||
AuthScheme string
|
||||
|
||||
// Validator is a function to validate key.
|
||||
// Required.
|
||||
Validator KeyAuthValidator
|
||||
}
|
||||
|
||||
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
||||
KeyAuthValidator func(string, echo.Context) (bool, error)
|
||||
|
||||
keyExtractor func(echo.Context) (string, error)
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultKeyAuthConfig is the default KeyAuth middleware config.
|
||||
DefaultKeyAuthConfig = KeyAuthConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
KeyLookup: "header:" + echo.HeaderAuthorization,
|
||||
AuthScheme: "Bearer",
|
||||
}
|
||||
)
|
||||
|
||||
// KeyAuth returns an KeyAuth middleware.
|
||||
//
|
||||
// For valid key it calls the next handler.
|
||||
// For invalid key, it sends "401 - Unauthorized" response.
|
||||
// For missing key, it sends "400 - Bad Request" response.
|
||||
func KeyAuth(fn KeyAuthValidator) echo.MiddlewareFunc {
|
||||
c := DefaultKeyAuthConfig
|
||||
c.Validator = fn
|
||||
return KeyAuthWithConfig(c)
|
||||
}
|
||||
|
||||
// KeyAuthWithConfig returns an KeyAuth middleware with config.
|
||||
// See `KeyAuth()`.
|
||||
func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultKeyAuthConfig.Skipper
|
||||
}
|
||||
// Defaults
|
||||
if config.AuthScheme == "" {
|
||||
config.AuthScheme = DefaultKeyAuthConfig.AuthScheme
|
||||
}
|
||||
if config.KeyLookup == "" {
|
||||
config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
|
||||
}
|
||||
if config.Validator == nil {
|
||||
panic("echo: key-auth middleware requires a validator function")
|
||||
}
|
||||
|
||||
// Initialize
|
||||
parts := strings.Split(config.KeyLookup, ":")
|
||||
extractor := keyFromHeader(parts[1], config.AuthScheme)
|
||||
switch parts[0] {
|
||||
case "query":
|
||||
extractor = keyFromQuery(parts[1])
|
||||
case "form":
|
||||
extractor = keyFromForm(parts[1])
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// Extract and verify key
|
||||
key, err := extractor(c)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
valid, err := config.Validator(key, c)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if valid {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
return echo.ErrUnauthorized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// keyFromHeader returns a `keyExtractor` that extracts key from the request header.
|
||||
func keyFromHeader(header string, authScheme string) keyExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
auth := c.Request().Header.Get(header)
|
||||
if auth == "" {
|
||||
return "", errors.New("missing key in request header")
|
||||
}
|
||||
if header == echo.HeaderAuthorization {
|
||||
l := len(authScheme)
|
||||
if len(auth) > l+1 && auth[:l] == authScheme {
|
||||
return auth[l+1:], nil
|
||||
}
|
||||
return "", errors.New("invalid key in the request header")
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
}
|
||||
|
||||
// keyFromQuery returns a `keyExtractor` that extracts key from the query string.
|
||||
func keyFromQuery(param string) keyExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
key := c.QueryParam(param)
|
||||
if key == "" {
|
||||
return "", errors.New("missing key in the query string")
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
// keyFromForm returns a `keyExtractor` that extracts key from the form.
|
||||
func keyFromForm(param string) keyExtractor {
|
||||
return func(c echo.Context) (string, error) {
|
||||
key := c.FormValue(param)
|
||||
if key == "" {
|
||||
return "", errors.New("missing key in the form")
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
}
|
217
vendor/github.com/labstack/echo/v4/middleware/logger.go
generated
vendored
Normal file
217
vendor/github.com/labstack/echo/v4/middleware/logger.go
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/color"
|
||||
"github.com/valyala/fasttemplate"
|
||||
)
|
||||
|
||||
type (
|
||||
// LoggerConfig defines the config for Logger middleware.
|
||||
LoggerConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Tags to constructed the logger format.
|
||||
//
|
||||
// - time_unix
|
||||
// - time_unix_nano
|
||||
// - time_rfc3339
|
||||
// - time_rfc3339_nano
|
||||
// - time_custom
|
||||
// - id (Request ID)
|
||||
// - remote_ip
|
||||
// - uri
|
||||
// - host
|
||||
// - method
|
||||
// - path
|
||||
// - protocol
|
||||
// - referer
|
||||
// - user_agent
|
||||
// - status
|
||||
// - error
|
||||
// - latency (In nanoseconds)
|
||||
// - latency_human (Human readable)
|
||||
// - bytes_in (Bytes received)
|
||||
// - bytes_out (Bytes sent)
|
||||
// - header:<NAME>
|
||||
// - query:<NAME>
|
||||
// - form:<NAME>
|
||||
//
|
||||
// Example "${remote_ip} ${status}"
|
||||
//
|
||||
// Optional. Default value DefaultLoggerConfig.Format.
|
||||
Format string `yaml:"format"`
|
||||
|
||||
// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
|
||||
CustomTimeFormat string `yaml:"custom_time_format"`
|
||||
|
||||
// Output is a writer where logs in JSON format are written.
|
||||
// Optional. Default value os.Stdout.
|
||||
Output io.Writer
|
||||
|
||||
template *fasttemplate.Template
|
||||
colorer *color.Color
|
||||
pool *sync.Pool
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultLoggerConfig is the default Logger middleware config.
|
||||
DefaultLoggerConfig = LoggerConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` +
|
||||
`"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` +
|
||||
`"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` +
|
||||
`,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
|
||||
CustomTimeFormat: "2006-01-02 15:04:05.00000",
|
||||
Output: os.Stdout,
|
||||
colorer: color.New(),
|
||||
}
|
||||
)
|
||||
|
||||
// Logger returns a middleware that logs HTTP requests.
|
||||
func Logger() echo.MiddlewareFunc {
|
||||
return LoggerWithConfig(DefaultLoggerConfig)
|
||||
}
|
||||
|
||||
// LoggerWithConfig returns a Logger middleware with config.
|
||||
// See: `Logger()`.
|
||||
func LoggerWithConfig(config LoggerConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultLoggerConfig.Skipper
|
||||
}
|
||||
if config.Format == "" {
|
||||
config.Format = DefaultLoggerConfig.Format
|
||||
}
|
||||
if config.Output == nil {
|
||||
config.Output = DefaultLoggerConfig.Output
|
||||
}
|
||||
|
||||
config.template = fasttemplate.New(config.Format, "${", "}")
|
||||
config.colorer = color.New()
|
||||
config.colorer.SetOutput(config.Output)
|
||||
config.pool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(make([]byte, 256))
|
||||
},
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
start := time.Now()
|
||||
if err = next(c); err != nil {
|
||||
c.Error(err)
|
||||
}
|
||||
stop := time.Now()
|
||||
buf := config.pool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer config.pool.Put(buf)
|
||||
|
||||
if _, err = config.template.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
|
||||
switch tag {
|
||||
case "time_unix":
|
||||
return buf.WriteString(strconv.FormatInt(time.Now().Unix(), 10))
|
||||
case "time_unix_nano":
|
||||
return buf.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
case "time_rfc3339":
|
||||
return buf.WriteString(time.Now().Format(time.RFC3339))
|
||||
case "time_rfc3339_nano":
|
||||
return buf.WriteString(time.Now().Format(time.RFC3339Nano))
|
||||
case "time_custom":
|
||||
return buf.WriteString(time.Now().Format(config.CustomTimeFormat))
|
||||
case "id":
|
||||
id := req.Header.Get(echo.HeaderXRequestID)
|
||||
if id == "" {
|
||||
id = res.Header().Get(echo.HeaderXRequestID)
|
||||
}
|
||||
return buf.WriteString(id)
|
||||
case "remote_ip":
|
||||
return buf.WriteString(c.RealIP())
|
||||
case "host":
|
||||
return buf.WriteString(req.Host)
|
||||
case "uri":
|
||||
return buf.WriteString(req.RequestURI)
|
||||
case "method":
|
||||
return buf.WriteString(req.Method)
|
||||
case "path":
|
||||
p := req.URL.Path
|
||||
if p == "" {
|
||||
p = "/"
|
||||
}
|
||||
return buf.WriteString(p)
|
||||
case "protocol":
|
||||
return buf.WriteString(req.Proto)
|
||||
case "referer":
|
||||
return buf.WriteString(req.Referer())
|
||||
case "user_agent":
|
||||
return buf.WriteString(req.UserAgent())
|
||||
case "status":
|
||||
n := res.Status
|
||||
s := config.colorer.Green(n)
|
||||
switch {
|
||||
case n >= 500:
|
||||
s = config.colorer.Red(n)
|
||||
case n >= 400:
|
||||
s = config.colorer.Yellow(n)
|
||||
case n >= 300:
|
||||
s = config.colorer.Cyan(n)
|
||||
}
|
||||
return buf.WriteString(s)
|
||||
case "error":
|
||||
if err != nil {
|
||||
return buf.WriteString(err.Error())
|
||||
}
|
||||
case "latency":
|
||||
l := stop.Sub(start)
|
||||
return buf.WriteString(strconv.FormatInt(int64(l), 10))
|
||||
case "latency_human":
|
||||
return buf.WriteString(stop.Sub(start).String())
|
||||
case "bytes_in":
|
||||
cl := req.Header.Get(echo.HeaderContentLength)
|
||||
if cl == "" {
|
||||
cl = "0"
|
||||
}
|
||||
return buf.WriteString(cl)
|
||||
case "bytes_out":
|
||||
return buf.WriteString(strconv.FormatInt(res.Size, 10))
|
||||
default:
|
||||
switch {
|
||||
case strings.HasPrefix(tag, "header:"):
|
||||
return buf.Write([]byte(c.Request().Header.Get(tag[7:])))
|
||||
case strings.HasPrefix(tag, "query:"):
|
||||
return buf.Write([]byte(c.QueryParam(tag[6:])))
|
||||
case strings.HasPrefix(tag, "form:"):
|
||||
return buf.Write([]byte(c.FormValue(tag[5:])))
|
||||
case strings.HasPrefix(tag, "cookie:"):
|
||||
cookie, err := c.Cookie(tag[7:])
|
||||
if err == nil {
|
||||
return buf.Write([]byte(cookie.Value))
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = config.Output.Write(buf.Bytes())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
92
vendor/github.com/labstack/echo/v4/middleware/method_override.go
generated
vendored
Normal file
92
vendor/github.com/labstack/echo/v4/middleware/method_override.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// MethodOverrideConfig defines the config for MethodOverride middleware.
|
||||
MethodOverrideConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Getter is a function that gets overridden method from the request.
|
||||
// Optional. Default values MethodFromHeader(echo.HeaderXHTTPMethodOverride).
|
||||
Getter MethodOverrideGetter
|
||||
}
|
||||
|
||||
// MethodOverrideGetter is a function that gets overridden method from the request
|
||||
MethodOverrideGetter func(echo.Context) string
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultMethodOverrideConfig is the default MethodOverride middleware config.
|
||||
DefaultMethodOverrideConfig = MethodOverrideConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Getter: MethodFromHeader(echo.HeaderXHTTPMethodOverride),
|
||||
}
|
||||
)
|
||||
|
||||
// MethodOverride returns a MethodOverride middleware.
|
||||
// MethodOverride middleware checks for the overridden method from the request and
|
||||
// uses it instead of the original method.
|
||||
//
|
||||
// For security reasons, only `POST` method can be overridden.
|
||||
func MethodOverride() echo.MiddlewareFunc {
|
||||
return MethodOverrideWithConfig(DefaultMethodOverrideConfig)
|
||||
}
|
||||
|
||||
// MethodOverrideWithConfig returns a MethodOverride middleware with config.
|
||||
// See: `MethodOverride()`.
|
||||
func MethodOverrideWithConfig(config MethodOverrideConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultMethodOverrideConfig.Skipper
|
||||
}
|
||||
if config.Getter == nil {
|
||||
config.Getter = DefaultMethodOverrideConfig.Getter
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
if req.Method == http.MethodPost {
|
||||
m := config.Getter(c)
|
||||
if m != "" {
|
||||
req.Method = m
|
||||
}
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MethodFromHeader is a `MethodOverrideGetter` that gets overridden method from
|
||||
// the request header.
|
||||
func MethodFromHeader(header string) MethodOverrideGetter {
|
||||
return func(c echo.Context) string {
|
||||
return c.Request().Header.Get(header)
|
||||
}
|
||||
}
|
||||
|
||||
// MethodFromForm is a `MethodOverrideGetter` that gets overridden method from the
|
||||
// form parameter.
|
||||
func MethodFromForm(param string) MethodOverrideGetter {
|
||||
return func(c echo.Context) string {
|
||||
return c.FormValue(param)
|
||||
}
|
||||
}
|
||||
|
||||
// MethodFromQuery is a `MethodOverrideGetter` that gets overridden method from
|
||||
// the query parameter.
|
||||
func MethodFromQuery(param string) MethodOverrideGetter {
|
||||
return func(c echo.Context) string {
|
||||
return c.QueryParam(param)
|
||||
}
|
||||
}
|
38
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
Normal file
38
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// Skipper defines a function to skip middleware. Returning true skips processing
|
||||
// the middleware.
|
||||
Skipper func(echo.Context) bool
|
||||
|
||||
// BeforeFunc defines a function which is executed just before the middleware.
|
||||
BeforeFunc func(echo.Context)
|
||||
)
|
||||
|
||||
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
||||
groups := pattern.FindAllStringSubmatch(input, -1)
|
||||
if groups == nil {
|
||||
return nil
|
||||
}
|
||||
values := groups[0][1:]
|
||||
replace := make([]string, 2*len(values))
|
||||
for i, v := range values {
|
||||
j := 2 * i
|
||||
replace[j] = "$" + strconv.Itoa(i+1)
|
||||
replace[j+1] = v
|
||||
}
|
||||
return strings.NewReplacer(replace...)
|
||||
}
|
||||
|
||||
// DefaultSkipper returns false which processes the middleware.
|
||||
func DefaultSkipper(echo.Context) bool {
|
||||
return false
|
||||
}
|
258
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
Normal file
258
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// TODO: Handle TLS proxy
|
||||
|
||||
type (
|
||||
// ProxyConfig defines the config for Proxy middleware.
|
||||
ProxyConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Balancer defines a load balancing technique.
|
||||
// Required.
|
||||
Balancer ProxyBalancer
|
||||
|
||||
// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
|
||||
// retrieved by index e.g. $1, $2 and so on.
|
||||
// Examples:
|
||||
// "/old": "/new",
|
||||
// "/api/*": "/$1",
|
||||
// "/js/*": "/public/javascripts/$1",
|
||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||
Rewrite map[string]string
|
||||
|
||||
// Context key to store selected ProxyTarget into context.
|
||||
// Optional. Default value "target".
|
||||
ContextKey string
|
||||
|
||||
// To customize the transport to remote.
|
||||
// Examples: If custom TLS certificates are required.
|
||||
Transport http.RoundTripper
|
||||
|
||||
rewriteRegex map[*regexp.Regexp]string
|
||||
}
|
||||
|
||||
// ProxyTarget defines the upstream target.
|
||||
ProxyTarget struct {
|
||||
Name string
|
||||
URL *url.URL
|
||||
Meta echo.Map
|
||||
}
|
||||
|
||||
// ProxyBalancer defines an interface to implement a load balancing technique.
|
||||
ProxyBalancer interface {
|
||||
AddTarget(*ProxyTarget) bool
|
||||
RemoveTarget(string) bool
|
||||
Next(echo.Context) *ProxyTarget
|
||||
}
|
||||
|
||||
commonBalancer struct {
|
||||
targets []*ProxyTarget
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
// RandomBalancer implements a random load balancing technique.
|
||||
randomBalancer struct {
|
||||
*commonBalancer
|
||||
random *rand.Rand
|
||||
}
|
||||
|
||||
// RoundRobinBalancer implements a round-robin load balancing technique.
|
||||
roundRobinBalancer struct {
|
||||
*commonBalancer
|
||||
i uint32
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultProxyConfig is the default Proxy middleware config.
|
||||
DefaultProxyConfig = ProxyConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
ContextKey: "target",
|
||||
}
|
||||
)
|
||||
|
||||
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
in, _, err := c.Response().Hijack()
|
||||
if err != nil {
|
||||
c.Error(fmt.Errorf("proxy raw, hijack error=%v, url=%s", t.URL, err))
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := net.Dial("tcp", t.URL.Host)
|
||||
if err != nil {
|
||||
he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err))
|
||||
c.Error(he)
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
// Write header
|
||||
err = r.Write(out)
|
||||
if err != nil {
|
||||
he := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err))
|
||||
c.Error(he)
|
||||
return
|
||||
}
|
||||
|
||||
errCh := make(chan error, 2)
|
||||
cp := func(dst io.Writer, src io.Reader) {
|
||||
_, err = io.Copy(dst, src)
|
||||
errCh <- err
|
||||
}
|
||||
|
||||
go cp(out, in)
|
||||
go cp(in, out)
|
||||
err = <-errCh
|
||||
if err != nil && err != io.EOF {
|
||||
c.Logger().Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// NewRandomBalancer returns a random proxy balancer.
|
||||
func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
|
||||
b := &randomBalancer{commonBalancer: new(commonBalancer)}
|
||||
b.targets = targets
|
||||
return b
|
||||
}
|
||||
|
||||
// NewRoundRobinBalancer returns a round-robin proxy balancer.
|
||||
func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
|
||||
b := &roundRobinBalancer{commonBalancer: new(commonBalancer)}
|
||||
b.targets = targets
|
||||
return b
|
||||
}
|
||||
|
||||
// AddTarget adds an upstream target to the list.
|
||||
func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
|
||||
for _, t := range b.targets {
|
||||
if t.Name == target.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
b.targets = append(b.targets, target)
|
||||
return true
|
||||
}
|
||||
|
||||
// RemoveTarget removes an upstream target from the list.
|
||||
func (b *commonBalancer) RemoveTarget(name string) bool {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
for i, t := range b.targets {
|
||||
if t.Name == name {
|
||||
b.targets = append(b.targets[:i], b.targets[i+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Next randomly returns an upstream target.
|
||||
func (b *randomBalancer) Next(c echo.Context) *ProxyTarget {
|
||||
if b.random == nil {
|
||||
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
}
|
||||
b.mutex.RLock()
|
||||
defer b.mutex.RUnlock()
|
||||
return b.targets[b.random.Intn(len(b.targets))]
|
||||
}
|
||||
|
||||
// Next returns an upstream target using round-robin technique.
|
||||
func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget {
|
||||
b.i = b.i % uint32(len(b.targets))
|
||||
t := b.targets[b.i]
|
||||
atomic.AddUint32(&b.i, 1)
|
||||
return t
|
||||
}
|
||||
|
||||
// Proxy returns a Proxy middleware.
|
||||
//
|
||||
// Proxy middleware forwards the request to upstream server using a configured load balancing technique.
|
||||
func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
|
||||
c := DefaultProxyConfig
|
||||
c.Balancer = balancer
|
||||
return ProxyWithConfig(c)
|
||||
}
|
||||
|
||||
// ProxyWithConfig returns a Proxy middleware with config.
|
||||
// See: `Proxy()`
|
||||
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultLoggerConfig.Skipper
|
||||
}
|
||||
if config.Balancer == nil {
|
||||
panic("echo: proxy middleware requires balancer")
|
||||
}
|
||||
config.rewriteRegex = map[*regexp.Regexp]string{}
|
||||
|
||||
// Initialize
|
||||
for k, v := range config.Rewrite {
|
||||
k = strings.Replace(k, "*", "(\\S*)", -1)
|
||||
config.rewriteRegex[regexp.MustCompile(k)] = v
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
tgt := config.Balancer.Next(c)
|
||||
c.Set(config.ContextKey, tgt)
|
||||
|
||||
// Rewrite
|
||||
for k, v := range config.rewriteRegex {
|
||||
replacer := captureTokens(k, req.URL.Path)
|
||||
if replacer != nil {
|
||||
req.URL.Path = replacer.Replace(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Fix header
|
||||
if req.Header.Get(echo.HeaderXRealIP) == "" {
|
||||
req.Header.Set(echo.HeaderXRealIP, c.RealIP())
|
||||
}
|
||||
if req.Header.Get(echo.HeaderXForwardedProto) == "" {
|
||||
req.Header.Set(echo.HeaderXForwardedProto, c.Scheme())
|
||||
}
|
||||
if c.IsWebSocket() && req.Header.Get(echo.HeaderXForwardedFor) == "" { // For HTTP, it is automatically set by Go HTTP reverse proxy.
|
||||
req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
|
||||
}
|
||||
|
||||
// Proxy
|
||||
switch {
|
||||
case c.IsWebSocket():
|
||||
proxyRaw(tgt, c).ServeHTTP(res, req)
|
||||
case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
|
||||
default:
|
||||
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
25
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go
generated
vendored
Normal file
25
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// +build go1.11
|
||||
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
|
||||
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
|
||||
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
|
||||
desc := tgt.URL.String()
|
||||
if tgt.Name != "" {
|
||||
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
|
||||
}
|
||||
c.Logger().Errorf("remote %s unreachable, could not forward: %v", desc, err)
|
||||
c.Error(echo.NewHTTPError(http.StatusServiceUnavailable))
|
||||
}
|
||||
proxy.Transport = config.Transport
|
||||
return proxy
|
||||
}
|
14
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11_n.go
generated
vendored
Normal file
14
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11_n.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// +build !go1.11
|
||||
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
|
||||
return httputil.NewSingleHostReverseProxy(t.URL)
|
||||
}
|
81
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
Normal file
81
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// RecoverConfig defines the config for Recover middleware.
|
||||
RecoverConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Size of the stack to be printed.
|
||||
// Optional. Default value 4KB.
|
||||
StackSize int `yaml:"stack_size"`
|
||||
|
||||
// DisableStackAll disables formatting stack traces of all other goroutines
|
||||
// into buffer after the trace for the current goroutine.
|
||||
// Optional. Default value false.
|
||||
DisableStackAll bool `yaml:"disable_stack_all"`
|
||||
|
||||
// DisablePrintStack disables printing stack trace.
|
||||
// Optional. Default value as false.
|
||||
DisablePrintStack bool `yaml:"disable_print_stack"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultRecoverConfig is the default Recover middleware config.
|
||||
DefaultRecoverConfig = RecoverConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
StackSize: 4 << 10, // 4 KB
|
||||
DisableStackAll: false,
|
||||
DisablePrintStack: false,
|
||||
}
|
||||
)
|
||||
|
||||
// Recover returns a middleware which recovers from panics anywhere in the chain
|
||||
// and handles the control to the centralized HTTPErrorHandler.
|
||||
func Recover() echo.MiddlewareFunc {
|
||||
return RecoverWithConfig(DefaultRecoverConfig)
|
||||
}
|
||||
|
||||
// RecoverWithConfig returns a Recover middleware with config.
|
||||
// See: `Recover()`.
|
||||
func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultRecoverConfig.Skipper
|
||||
}
|
||||
if config.StackSize == 0 {
|
||||
config.StackSize = DefaultRecoverConfig.StackSize
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err, ok := r.(error)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%v", r)
|
||||
}
|
||||
stack := make([]byte, config.StackSize)
|
||||
length := runtime.Stack(stack, !config.DisableStackAll)
|
||||
if !config.DisablePrintStack {
|
||||
c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
|
||||
}
|
||||
c.Error(err)
|
||||
}
|
||||
}()
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
153
vendor/github.com/labstack/echo/v4/middleware/redirect.go
generated
vendored
Normal file
153
vendor/github.com/labstack/echo/v4/middleware/redirect.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// RedirectConfig defines the config for Redirect middleware.
|
||||
type RedirectConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper
|
||||
|
||||
// Status code to be used when redirecting the request.
|
||||
// Optional. Default value http.StatusMovedPermanently.
|
||||
Code int `yaml:"code"`
|
||||
}
|
||||
|
||||
// redirectLogic represents a function that given a scheme, host and uri
|
||||
// can both: 1) determine if redirect is needed (will set ok accordingly) and
|
||||
// 2) return the appropriate redirect url.
|
||||
type redirectLogic func(scheme, host, uri string) (ok bool, url string)
|
||||
|
||||
const www = "www"
|
||||
|
||||
// DefaultRedirectConfig is the default Redirect middleware config.
|
||||
var DefaultRedirectConfig = RedirectConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Code: http.StatusMovedPermanently,
|
||||
}
|
||||
|
||||
// HTTPSRedirect redirects http requests to https.
|
||||
// For example, http://labstack.com will be redirect to https://labstack.com.
|
||||
//
|
||||
// Usage `Echo#Pre(HTTPSRedirect())`
|
||||
func HTTPSRedirect() echo.MiddlewareFunc {
|
||||
return HTTPSRedirectWithConfig(DefaultRedirectConfig)
|
||||
}
|
||||
|
||||
// HTTPSRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||
// See `HTTPSRedirect()`.
|
||||
func HTTPSRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
|
||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||
if ok = scheme != "https"; ok {
|
||||
url = "https://" + host + uri
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// HTTPSWWWRedirect redirects http requests to https www.
|
||||
// For example, http://labstack.com will be redirect to https://www.labstack.com.
|
||||
//
|
||||
// Usage `Echo#Pre(HTTPSWWWRedirect())`
|
||||
func HTTPSWWWRedirect() echo.MiddlewareFunc {
|
||||
return HTTPSWWWRedirectWithConfig(DefaultRedirectConfig)
|
||||
}
|
||||
|
||||
// HTTPSWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||
// See `HTTPSWWWRedirect()`.
|
||||
func HTTPSWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
|
||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||
if ok = scheme != "https" && host[:3] != www; ok {
|
||||
url = "https://www." + host + uri
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// HTTPSNonWWWRedirect redirects http requests to https non www.
|
||||
// For example, http://www.labstack.com will be redirect to https://labstack.com.
|
||||
//
|
||||
// Usage `Echo#Pre(HTTPSNonWWWRedirect())`
|
||||
func HTTPSNonWWWRedirect() echo.MiddlewareFunc {
|
||||
return HTTPSNonWWWRedirectWithConfig(DefaultRedirectConfig)
|
||||
}
|
||||
|
||||
// HTTPSNonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||
// See `HTTPSNonWWWRedirect()`.
|
||||
func HTTPSNonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
|
||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||
if ok = scheme != "https"; ok {
|
||||
if host[:3] == www {
|
||||
host = host[4:]
|
||||
}
|
||||
url = "https://" + host + uri
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// WWWRedirect redirects non www requests to www.
|
||||
// For example, http://labstack.com will be redirect to http://www.labstack.com.
|
||||
//
|
||||
// Usage `Echo#Pre(WWWRedirect())`
|
||||
func WWWRedirect() echo.MiddlewareFunc {
|
||||
return WWWRedirectWithConfig(DefaultRedirectConfig)
|
||||
}
|
||||
|
||||
// WWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||
// See `WWWRedirect()`.
|
||||
func WWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
|
||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||
if ok = host[:3] != www; ok {
|
||||
url = scheme + "://www." + host + uri
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// NonWWWRedirect redirects www requests to non www.
|
||||
// For example, http://www.labstack.com will be redirect to http://labstack.com.
|
||||
//
|
||||
// Usage `Echo#Pre(NonWWWRedirect())`
|
||||
func NonWWWRedirect() echo.MiddlewareFunc {
|
||||
return NonWWWRedirectWithConfig(DefaultRedirectConfig)
|
||||
}
|
||||
|
||||
// NonWWWRedirectWithConfig returns an HTTPSRedirect middleware with config.
|
||||
// See `NonWWWRedirect()`.
|
||||
func NonWWWRedirectWithConfig(config RedirectConfig) echo.MiddlewareFunc {
|
||||
return redirect(config, func(scheme, host, uri string) (ok bool, url string) {
|
||||
if ok = host[:3] == www; ok {
|
||||
url = scheme + "://" + host[4:] + uri
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func redirect(config RedirectConfig, cb redirectLogic) echo.MiddlewareFunc {
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultTrailingSlashConfig.Skipper
|
||||
}
|
||||
if config.Code == 0 {
|
||||
config.Code = DefaultRedirectConfig.Code
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req, scheme := c.Request(), c.Scheme()
|
||||
host := req.Host
|
||||
if ok, url := cb(scheme, host, req.RequestURI); ok {
|
||||
return c.Redirect(config.Code, url)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
64
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
Normal file
64
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/random"
|
||||
)
|
||||
|
||||
type (
|
||||
// RequestIDConfig defines the config for RequestID middleware.
|
||||
RequestIDConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Generator defines a function to generate an ID.
|
||||
// Optional. Default value random.String(32).
|
||||
Generator func() string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultRequestIDConfig is the default RequestID middleware config.
|
||||
DefaultRequestIDConfig = RequestIDConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Generator: generator,
|
||||
}
|
||||
)
|
||||
|
||||
// RequestID returns a X-Request-ID middleware.
|
||||
func RequestID() echo.MiddlewareFunc {
|
||||
return RequestIDWithConfig(DefaultRequestIDConfig)
|
||||
}
|
||||
|
||||
// RequestIDWithConfig returns a X-Request-ID middleware with config.
|
||||
func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultRequestIDConfig.Skipper
|
||||
}
|
||||
if config.Generator == nil {
|
||||
config.Generator = generator
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
rid := req.Header.Get(echo.HeaderXRequestID)
|
||||
if rid == "" {
|
||||
rid = config.Generator()
|
||||
}
|
||||
res.Header().Set(echo.HeaderXRequestID, rid)
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generator() string {
|
||||
return random.String(32)
|
||||
}
|
84
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
Normal file
84
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// RewriteConfig defines the config for Rewrite middleware.
|
||||
RewriteConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Rules defines the URL path rewrite rules. The values captured in asterisk can be
|
||||
// retrieved by index e.g. $1, $2 and so on.
|
||||
// Example:
|
||||
// "/old": "/new",
|
||||
// "/api/*": "/$1",
|
||||
// "/js/*": "/public/javascripts/$1",
|
||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||
// Required.
|
||||
Rules map[string]string `yaml:"rules"`
|
||||
|
||||
rulesRegex map[*regexp.Regexp]string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultRewriteConfig is the default Rewrite middleware config.
|
||||
DefaultRewriteConfig = RewriteConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
}
|
||||
)
|
||||
|
||||
// Rewrite returns a Rewrite middleware.
|
||||
//
|
||||
// Rewrite middleware rewrites the URL path based on the provided rules.
|
||||
func Rewrite(rules map[string]string) echo.MiddlewareFunc {
|
||||
c := DefaultRewriteConfig
|
||||
c.Rules = rules
|
||||
return RewriteWithConfig(c)
|
||||
}
|
||||
|
||||
// RewriteWithConfig returns a Rewrite middleware with config.
|
||||
// See: `Rewrite()`.
|
||||
func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Rules == nil {
|
||||
panic("echo: rewrite middleware requires url path rewrite rules")
|
||||
}
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultBodyDumpConfig.Skipper
|
||||
}
|
||||
config.rulesRegex = map[*regexp.Regexp]string{}
|
||||
|
||||
// Initialize
|
||||
for k, v := range config.Rules {
|
||||
k = strings.Replace(k, "*", "(.*)", -1)
|
||||
k = k + "$"
|
||||
config.rulesRegex[regexp.MustCompile(k)] = v
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
|
||||
// Rewrite
|
||||
for k, v := range config.rulesRegex {
|
||||
replacer := captureTokens(k, req.URL.Path)
|
||||
if replacer != nil {
|
||||
req.URL.Path = replacer.Replace(v)
|
||||
break
|
||||
}
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
116
vendor/github.com/labstack/echo/v4/middleware/secure.go
generated
vendored
Normal file
116
vendor/github.com/labstack/echo/v4/middleware/secure.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// SecureConfig defines the config for Secure middleware.
|
||||
SecureConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// XSSProtection provides protection against cross-site scripting attack (XSS)
|
||||
// by setting the `X-XSS-Protection` header.
|
||||
// Optional. Default value "1; mode=block".
|
||||
XSSProtection string `yaml:"xss_protection"`
|
||||
|
||||
// ContentTypeNosniff provides protection against overriding Content-Type
|
||||
// header by setting the `X-Content-Type-Options` header.
|
||||
// Optional. Default value "nosniff".
|
||||
ContentTypeNosniff string `yaml:"content_type_nosniff"`
|
||||
|
||||
// XFrameOptions can be used to indicate whether or not a browser should
|
||||
// be allowed to render a page in a <frame>, <iframe> or <object> .
|
||||
// Sites can use this to avoid clickjacking attacks, by ensuring that their
|
||||
// content is not embedded into other sites.provides protection against
|
||||
// clickjacking.
|
||||
// Optional. Default value "SAMEORIGIN".
|
||||
// Possible values:
|
||||
// - "SAMEORIGIN" - The page can only be displayed in a frame on the same origin as the page itself.
|
||||
// - "DENY" - The page cannot be displayed in a frame, regardless of the site attempting to do so.
|
||||
// - "ALLOW-FROM uri" - The page can only be displayed in a frame on the specified origin.
|
||||
XFrameOptions string `yaml:"x_frame_options"`
|
||||
|
||||
// HSTSMaxAge sets the `Strict-Transport-Security` header to indicate how
|
||||
// long (in seconds) browsers should remember that this site is only to
|
||||
// be accessed using HTTPS. This reduces your exposure to some SSL-stripping
|
||||
// man-in-the-middle (MITM) attacks.
|
||||
// Optional. Default value 0.
|
||||
HSTSMaxAge int `yaml:"hsts_max_age"`
|
||||
|
||||
// HSTSExcludeSubdomains won't include subdomains tag in the `Strict Transport Security`
|
||||
// header, excluding all subdomains from security policy. It has no effect
|
||||
// unless HSTSMaxAge is set to a non-zero value.
|
||||
// Optional. Default value false.
|
||||
HSTSExcludeSubdomains bool `yaml:"hsts_exclude_subdomains"`
|
||||
|
||||
// ContentSecurityPolicy sets the `Content-Security-Policy` header providing
|
||||
// security against cross-site scripting (XSS), clickjacking and other code
|
||||
// injection attacks resulting from execution of malicious content in the
|
||||
// trusted web page context.
|
||||
// Optional. Default value "".
|
||||
ContentSecurityPolicy string `yaml:"content_security_policy"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultSecureConfig is the default Secure middleware config.
|
||||
DefaultSecureConfig = SecureConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
XSSProtection: "1; mode=block",
|
||||
ContentTypeNosniff: "nosniff",
|
||||
XFrameOptions: "SAMEORIGIN",
|
||||
}
|
||||
)
|
||||
|
||||
// Secure returns a Secure middleware.
|
||||
// Secure middleware provides protection against cross-site scripting (XSS) attack,
|
||||
// content type sniffing, clickjacking, insecure connection and other code injection
|
||||
// attacks.
|
||||
func Secure() echo.MiddlewareFunc {
|
||||
return SecureWithConfig(DefaultSecureConfig)
|
||||
}
|
||||
|
||||
// SecureWithConfig returns a Secure middleware with config.
|
||||
// See: `Secure()`.
|
||||
func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultSecureConfig.Skipper
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
res := c.Response()
|
||||
|
||||
if config.XSSProtection != "" {
|
||||
res.Header().Set(echo.HeaderXXSSProtection, config.XSSProtection)
|
||||
}
|
||||
if config.ContentTypeNosniff != "" {
|
||||
res.Header().Set(echo.HeaderXContentTypeOptions, config.ContentTypeNosniff)
|
||||
}
|
||||
if config.XFrameOptions != "" {
|
||||
res.Header().Set(echo.HeaderXFrameOptions, config.XFrameOptions)
|
||||
}
|
||||
if (c.IsTLS() || (req.Header.Get(echo.HeaderXForwardedProto) == "https")) && config.HSTSMaxAge != 0 {
|
||||
subdomains := ""
|
||||
if !config.HSTSExcludeSubdomains {
|
||||
subdomains = "; includeSubdomains"
|
||||
}
|
||||
res.Header().Set(echo.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", config.HSTSMaxAge, subdomains))
|
||||
}
|
||||
if config.ContentSecurityPolicy != "" {
|
||||
res.Header().Set(echo.HeaderContentSecurityPolicy, config.ContentSecurityPolicy)
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
119
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
Normal file
119
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type (
|
||||
// TrailingSlashConfig defines the config for TrailingSlash middleware.
|
||||
TrailingSlashConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Status code to be used when redirecting the request.
|
||||
// Optional, but when provided the request is redirected using this code.
|
||||
RedirectCode int `yaml:"redirect_code"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTrailingSlashConfig is the default TrailingSlash middleware config.
|
||||
DefaultTrailingSlashConfig = TrailingSlashConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
}
|
||||
)
|
||||
|
||||
// AddTrailingSlash returns a root level (before router) middleware which adds a
|
||||
// trailing slash to the request `URL#Path`.
|
||||
//
|
||||
// Usage `Echo#Pre(AddTrailingSlash())`
|
||||
func AddTrailingSlash() echo.MiddlewareFunc {
|
||||
return AddTrailingSlashWithConfig(DefaultTrailingSlashConfig)
|
||||
}
|
||||
|
||||
// AddTrailingSlashWithConfig returns a AddTrailingSlash middleware with config.
|
||||
// See `AddTrailingSlash()`.
|
||||
func AddTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultTrailingSlashConfig.Skipper
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
url := req.URL
|
||||
path := url.Path
|
||||
qs := c.QueryString()
|
||||
if path != "/" && path[len(path)-1] != '/' {
|
||||
path += "/"
|
||||
uri := path
|
||||
if qs != "" {
|
||||
uri += "?" + qs
|
||||
}
|
||||
|
||||
// Redirect
|
||||
if config.RedirectCode != 0 {
|
||||
return c.Redirect(config.RedirectCode, uri)
|
||||
}
|
||||
|
||||
// Forward
|
||||
req.RequestURI = uri
|
||||
url.Path = path
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveTrailingSlash returns a root level (before router) middleware which removes
|
||||
// a trailing slash from the request URI.
|
||||
//
|
||||
// Usage `Echo#Pre(RemoveTrailingSlash())`
|
||||
func RemoveTrailingSlash() echo.MiddlewareFunc {
|
||||
return RemoveTrailingSlashWithConfig(TrailingSlashConfig{})
|
||||
}
|
||||
|
||||
// RemoveTrailingSlashWithConfig returns a RemoveTrailingSlash middleware with config.
|
||||
// See `RemoveTrailingSlash()`.
|
||||
func RemoveTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultTrailingSlashConfig.Skipper
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
req := c.Request()
|
||||
url := req.URL
|
||||
path := url.Path
|
||||
qs := c.QueryString()
|
||||
l := len(path) - 1
|
||||
if l >= 0 && path != "/" && path[l] == '/' {
|
||||
path = path[:l]
|
||||
uri := path
|
||||
if qs != "" {
|
||||
uri += "?" + qs
|
||||
}
|
||||
|
||||
// Redirect
|
||||
if config.RedirectCode != 0 {
|
||||
return c.Redirect(config.RedirectCode, uri)
|
||||
}
|
||||
|
||||
// Forward
|
||||
req.RequestURI = uri
|
||||
url.Path = path
|
||||
}
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
229
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
Normal file
229
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/bytes"
|
||||
)
|
||||
|
||||
type (
|
||||
// StaticConfig defines the config for Static middleware.
|
||||
StaticConfig struct {
|
||||
// Skipper defines a function to skip middleware.
|
||||
Skipper Skipper
|
||||
|
||||
// Root directory from where the static content is served.
|
||||
// Required.
|
||||
Root string `yaml:"root"`
|
||||
|
||||
// Index file for serving a directory.
|
||||
// Optional. Default value "index.html".
|
||||
Index string `yaml:"index"`
|
||||
|
||||
// Enable HTML5 mode by forwarding all not-found requests to root so that
|
||||
// SPA (single-page application) can handle the routing.
|
||||
// Optional. Default value false.
|
||||
HTML5 bool `yaml:"html5"`
|
||||
|
||||
// Enable directory browsing.
|
||||
// Optional. Default value false.
|
||||
Browse bool `yaml:"browse"`
|
||||
}
|
||||
)
|
||||
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>{{ .Name }}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Menlo, Consolas, monospace;
|
||||
padding: 48px;
|
||||
}
|
||||
header {
|
||||
padding: 4px 16px;
|
||||
font-size: 24px;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 20px 0 0 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
li {
|
||||
width: 300px;
|
||||
padding: 16px;
|
||||
}
|
||||
li a {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
li span {
|
||||
color: #707070;
|
||||
font-size: 12px;
|
||||
}
|
||||
li a:hover {
|
||||
opacity: 0.50;
|
||||
}
|
||||
.dir {
|
||||
color: #E91E63;
|
||||
}
|
||||
.file {
|
||||
color: #673AB7;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{{ .Name }}
|
||||
</header>
|
||||
<ul>
|
||||
{{ range .Files }}
|
||||
<li>
|
||||
{{ if .Dir }}
|
||||
{{ $name := print .Name "/" }}
|
||||
<a class="dir" href="{{ $name }}">{{ $name }}</a>
|
||||
{{ else }}
|
||||
<a class="file" href="{{ .Name }}">{{ .Name }}</a>
|
||||
<span>{{ .Size }}</span>
|
||||
{{ end }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
var (
|
||||
// DefaultStaticConfig is the default Static middleware config.
|
||||
DefaultStaticConfig = StaticConfig{
|
||||
Skipper: DefaultSkipper,
|
||||
Index: "index.html",
|
||||
}
|
||||
)
|
||||
|
||||
// Static returns a Static middleware to serves static content from the provided
|
||||
// root directory.
|
||||
func Static(root string) echo.MiddlewareFunc {
|
||||
c := DefaultStaticConfig
|
||||
c.Root = root
|
||||
return StaticWithConfig(c)
|
||||
}
|
||||
|
||||
// StaticWithConfig returns a Static middleware with config.
|
||||
// See `Static()`.
|
||||
func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
||||
// Defaults
|
||||
if config.Root == "" {
|
||||
config.Root = "." // For security we want to restrict to CWD.
|
||||
}
|
||||
if config.Skipper == nil {
|
||||
config.Skipper = DefaultStaticConfig.Skipper
|
||||
}
|
||||
if config.Index == "" {
|
||||
config.Index = DefaultStaticConfig.Index
|
||||
}
|
||||
|
||||
// Index template
|
||||
t, err := template.New("index").Parse(html)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("echo: %v", err))
|
||||
}
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) (err error) {
|
||||
if config.Skipper(c) {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
p := c.Request().URL.Path
|
||||
if strings.HasSuffix(c.Path(), "*") { // When serving from a group, e.g. `/static*`.
|
||||
p = c.Param("*")
|
||||
}
|
||||
p, err = url.PathUnescape(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
|
||||
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = next(c); err != nil {
|
||||
if he, ok := err.(*echo.HTTPError); ok {
|
||||
if config.HTML5 && he.Code == http.StatusNotFound {
|
||||
return c.File(filepath.Join(config.Root, config.Index))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
index := filepath.Join(name, config.Index)
|
||||
fi, err = os.Stat(index)
|
||||
|
||||
if err != nil {
|
||||
if config.Browse {
|
||||
return listDir(t, name, c.Response())
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return next(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return c.File(index)
|
||||
}
|
||||
|
||||
return c.File(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listDir(t *template.Template, name string, res *echo.Response) (err error) {
|
||||
file, err := os.Open(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
files, err := file.Readdir(-1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create directory index
|
||||
res.Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
|
||||
data := struct {
|
||||
Name string
|
||||
Files []interface{}
|
||||
}{
|
||||
Name: name,
|
||||
}
|
||||
for _, f := range files {
|
||||
data.Files = append(data.Files, struct {
|
||||
Name string
|
||||
Dir bool
|
||||
Size string
|
||||
}{f.Name(), f.IsDir(), bytes.Format(f.Size())})
|
||||
}
|
||||
return t.Execute(res, data)
|
||||
}
|
101
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
Normal file
101
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
package echo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
// Response wraps an http.ResponseWriter and implements its interface to be used
|
||||
// by an HTTP handler to construct an HTTP response.
|
||||
// See: https://golang.org/pkg/net/http/#ResponseWriter
|
||||
Response struct {
|
||||
echo *Echo
|
||||
beforeFuncs []func()
|
||||
afterFuncs []func()
|
||||
Writer http.ResponseWriter
|
||||
Status int
|
||||
Size int64
|
||||
Committed bool
|
||||
}
|
||||
)
|
||||
|
||||
// NewResponse creates a new instance of Response.
|
||||
func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
|
||||
return &Response{Writer: w, echo: e}
|
||||
}
|
||||
|
||||
// Header returns the header map for the writer that will be sent by
|
||||
// WriteHeader. Changing the header after a call to WriteHeader (or Write) has
|
||||
// no effect unless the modified headers were declared as trailers by setting
|
||||
// the "Trailer" header before the call to WriteHeader (see example)
|
||||
// To suppress implicit response headers, set their value to nil.
|
||||
// Example: https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||
func (r *Response) Header() http.Header {
|
||||
return r.Writer.Header()
|
||||
}
|
||||
|
||||
// Before registers a function which is called just before the response is written.
|
||||
func (r *Response) Before(fn func()) {
|
||||
r.beforeFuncs = append(r.beforeFuncs, fn)
|
||||
}
|
||||
|
||||
// After registers a function which is called just after the response is written.
|
||||
// If the `Content-Length` is unknown, none of the after function is executed.
|
||||
func (r *Response) After(fn func()) {
|
||||
r.afterFuncs = append(r.afterFuncs, fn)
|
||||
}
|
||||
|
||||
// WriteHeader sends an HTTP response header with status code. If WriteHeader is
|
||||
// not called explicitly, the first call to Write will trigger an implicit
|
||||
// WriteHeader(http.StatusOK). Thus explicit calls to WriteHeader are mainly
|
||||
// used to send error codes.
|
||||
func (r *Response) WriteHeader(code int) {
|
||||
if r.Committed {
|
||||
r.echo.Logger.Warn("response already committed")
|
||||
return
|
||||
}
|
||||
for _, fn := range r.beforeFuncs {
|
||||
fn()
|
||||
}
|
||||
r.Status = code
|
||||
r.Writer.WriteHeader(code)
|
||||
r.Committed = true
|
||||
}
|
||||
|
||||
// Write writes the data to the connection as part of an HTTP reply.
|
||||
func (r *Response) Write(b []byte) (n int, err error) {
|
||||
if !r.Committed {
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err = r.Writer.Write(b)
|
||||
r.Size += int64(n)
|
||||
for _, fn := range r.afterFuncs {
|
||||
fn()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Flush implements the http.Flusher interface to allow an HTTP handler to flush
|
||||
// buffered data to the client.
|
||||
// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
|
||||
func (r *Response) Flush() {
|
||||
r.Writer.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// Hijack implements the http.Hijacker interface to allow an HTTP handler to
|
||||
// take over the connection.
|
||||
// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
|
||||
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return r.Writer.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
func (r *Response) reset(w http.ResponseWriter) {
|
||||
r.beforeFuncs = nil
|
||||
r.afterFuncs = nil
|
||||
r.Writer = w
|
||||
r.Size = 0
|
||||
r.Status = http.StatusOK
|
||||
r.Committed = false
|
||||
}
|
435
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
Normal file
435
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
Normal file
@ -0,0 +1,435 @@
|
||||
package echo
|
||||
|
||||
import "net/http"
|
||||
|
||||
type (
|
||||
// Router is the registry of all registered routes for an `Echo` instance for
|
||||
// request matching and URL path parameter parsing.
|
||||
Router struct {
|
||||
tree *node
|
||||
routes map[string]*Route
|
||||
echo *Echo
|
||||
}
|
||||
node struct {
|
||||
kind kind
|
||||
label byte
|
||||
prefix string
|
||||
parent *node
|
||||
children children
|
||||
ppath string
|
||||
pnames []string
|
||||
methodHandler *methodHandler
|
||||
}
|
||||
kind uint8
|
||||
children []*node
|
||||
methodHandler struct {
|
||||
connect HandlerFunc
|
||||
delete HandlerFunc
|
||||
get HandlerFunc
|
||||
head HandlerFunc
|
||||
options HandlerFunc
|
||||
patch HandlerFunc
|
||||
post HandlerFunc
|
||||
propfind HandlerFunc
|
||||
put HandlerFunc
|
||||
trace HandlerFunc
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
skind kind = iota
|
||||
pkind
|
||||
akind
|
||||
)
|
||||
|
||||
// NewRouter returns a new Router instance.
|
||||
func NewRouter(e *Echo) *Router {
|
||||
return &Router{
|
||||
tree: &node{
|
||||
methodHandler: new(methodHandler),
|
||||
},
|
||||
routes: map[string]*Route{},
|
||||
echo: e,
|
||||
}
|
||||
}
|
||||
|
||||
// Add registers a new route for method and path with matching handler.
|
||||
func (r *Router) Add(method, path string, h HandlerFunc) {
|
||||
// Validate path
|
||||
if path == "" {
|
||||
panic("echo: path cannot be empty")
|
||||
}
|
||||
if path[0] != '/' {
|
||||
path = "/" + path
|
||||
}
|
||||
pnames := []string{} // Param names
|
||||
ppath := path // Pristine path
|
||||
|
||||
for i, l := 0, len(path); i < l; i++ {
|
||||
if path[i] == ':' {
|
||||
j := i + 1
|
||||
|
||||
r.insert(method, path[:i], nil, skind, "", nil)
|
||||
for ; i < l && path[i] != '/'; i++ {
|
||||
}
|
||||
|
||||
pnames = append(pnames, path[j:i])
|
||||
path = path[:j] + path[i:]
|
||||
i, l = j, len(path)
|
||||
|
||||
if i == l {
|
||||
r.insert(method, path[:i], h, pkind, ppath, pnames)
|
||||
return
|
||||
}
|
||||
r.insert(method, path[:i], nil, pkind, "", nil)
|
||||
} else if path[i] == '*' {
|
||||
r.insert(method, path[:i], nil, skind, "", nil)
|
||||
pnames = append(pnames, "*")
|
||||
r.insert(method, path[:i+1], h, akind, ppath, pnames)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
r.insert(method, path, h, skind, ppath, pnames)
|
||||
}
|
||||
|
||||
func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
|
||||
// Adjust max param
|
||||
l := len(pnames)
|
||||
if *r.echo.maxParam < l {
|
||||
*r.echo.maxParam = l
|
||||
}
|
||||
|
||||
cn := r.tree // Current node as root
|
||||
if cn == nil {
|
||||
panic("echo: invalid method")
|
||||
}
|
||||
search := path
|
||||
|
||||
for {
|
||||
sl := len(search)
|
||||
pl := len(cn.prefix)
|
||||
l := 0
|
||||
|
||||
// LCP
|
||||
max := pl
|
||||
if sl < max {
|
||||
max = sl
|
||||
}
|
||||
for ; l < max && search[l] == cn.prefix[l]; l++ {
|
||||
}
|
||||
|
||||
if l == 0 {
|
||||
// At root node
|
||||
cn.label = search[0]
|
||||
cn.prefix = search
|
||||
if h != nil {
|
||||
cn.kind = t
|
||||
cn.addHandler(method, h)
|
||||
cn.ppath = ppath
|
||||
cn.pnames = pnames
|
||||
}
|
||||
} else if l < pl {
|
||||
// Split node
|
||||
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
|
||||
|
||||
// Reset parent node
|
||||
cn.kind = skind
|
||||
cn.label = cn.prefix[0]
|
||||
cn.prefix = cn.prefix[:l]
|
||||
cn.children = nil
|
||||
cn.methodHandler = new(methodHandler)
|
||||
cn.ppath = ""
|
||||
cn.pnames = nil
|
||||
|
||||
cn.addChild(n)
|
||||
|
||||
if l == sl {
|
||||
// At parent node
|
||||
cn.kind = t
|
||||
cn.addHandler(method, h)
|
||||
cn.ppath = ppath
|
||||
cn.pnames = pnames
|
||||
} else {
|
||||
// Create child node
|
||||
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
|
||||
n.addHandler(method, h)
|
||||
cn.addChild(n)
|
||||
}
|
||||
} else if l < sl {
|
||||
search = search[l:]
|
||||
c := cn.findChildWithLabel(search[0])
|
||||
if c != nil {
|
||||
// Go deeper
|
||||
cn = c
|
||||
continue
|
||||
}
|
||||
// Create child node
|
||||
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
|
||||
n.addHandler(method, h)
|
||||
cn.addChild(n)
|
||||
} else {
|
||||
// Node already exists
|
||||
if h != nil {
|
||||
cn.addHandler(method, h)
|
||||
cn.ppath = ppath
|
||||
if len(cn.pnames) == 0 { // Issue #729
|
||||
cn.pnames = pnames
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
|
||||
return &node{
|
||||
kind: t,
|
||||
label: pre[0],
|
||||
prefix: pre,
|
||||
parent: p,
|
||||
children: c,
|
||||
ppath: ppath,
|
||||
pnames: pnames,
|
||||
methodHandler: mh,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) addChild(c *node) {
|
||||
n.children = append(n.children, c)
|
||||
}
|
||||
|
||||
func (n *node) findChild(l byte, t kind) *node {
|
||||
for _, c := range n.children {
|
||||
if c.label == l && c.kind == t {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) findChildWithLabel(l byte) *node {
|
||||
for _, c := range n.children {
|
||||
if c.label == l {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) findChildByKind(t kind) *node {
|
||||
for _, c := range n.children {
|
||||
if c.kind == t {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) addHandler(method string, h HandlerFunc) {
|
||||
switch method {
|
||||
case http.MethodConnect:
|
||||
n.methodHandler.connect = h
|
||||
case http.MethodDelete:
|
||||
n.methodHandler.delete = h
|
||||
case http.MethodGet:
|
||||
n.methodHandler.get = h
|
||||
case http.MethodHead:
|
||||
n.methodHandler.head = h
|
||||
case http.MethodOptions:
|
||||
n.methodHandler.options = h
|
||||
case http.MethodPatch:
|
||||
n.methodHandler.patch = h
|
||||
case http.MethodPost:
|
||||
n.methodHandler.post = h
|
||||
case PROPFIND:
|
||||
n.methodHandler.propfind = h
|
||||
case http.MethodPut:
|
||||
n.methodHandler.put = h
|
||||
case http.MethodTrace:
|
||||
n.methodHandler.trace = h
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) findHandler(method string) HandlerFunc {
|
||||
switch method {
|
||||
case http.MethodConnect:
|
||||
return n.methodHandler.connect
|
||||
case http.MethodDelete:
|
||||
return n.methodHandler.delete
|
||||
case http.MethodGet:
|
||||
return n.methodHandler.get
|
||||
case http.MethodHead:
|
||||
return n.methodHandler.head
|
||||
case http.MethodOptions:
|
||||
return n.methodHandler.options
|
||||
case http.MethodPatch:
|
||||
return n.methodHandler.patch
|
||||
case http.MethodPost:
|
||||
return n.methodHandler.post
|
||||
case PROPFIND:
|
||||
return n.methodHandler.propfind
|
||||
case http.MethodPut:
|
||||
return n.methodHandler.put
|
||||
case http.MethodTrace:
|
||||
return n.methodHandler.trace
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) checkMethodNotAllowed() HandlerFunc {
|
||||
for _, m := range methods {
|
||||
if h := n.findHandler(m); h != nil {
|
||||
return MethodNotAllowedHandler
|
||||
}
|
||||
}
|
||||
return NotFoundHandler
|
||||
}
|
||||
|
||||
// Find lookup a handler registered for method and path. It also parses URL for path
|
||||
// parameters and load them into context.
|
||||
//
|
||||
// For performance:
|
||||
//
|
||||
// - Get context from `Echo#AcquireContext()`
|
||||
// - Reset it `Context#Reset()`
|
||||
// - Return it `Echo#ReleaseContext()`.
|
||||
func (r *Router) Find(method, path string, c Context) {
|
||||
ctx := c.(*context)
|
||||
ctx.path = path
|
||||
cn := r.tree // Current node as root
|
||||
|
||||
var (
|
||||
search = path
|
||||
child *node // Child node
|
||||
n int // Param counter
|
||||
nk kind // Next kind
|
||||
nn *node // Next node
|
||||
ns string // Next search
|
||||
pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
|
||||
)
|
||||
|
||||
// Search order static > param > any
|
||||
for {
|
||||
if search == "" {
|
||||
break
|
||||
}
|
||||
|
||||
pl := 0 // Prefix length
|
||||
l := 0 // LCP length
|
||||
|
||||
if cn.label != ':' {
|
||||
sl := len(search)
|
||||
pl = len(cn.prefix)
|
||||
|
||||
// LCP
|
||||
max := pl
|
||||
if sl < max {
|
||||
max = sl
|
||||
}
|
||||
for ; l < max && search[l] == cn.prefix[l]; l++ {
|
||||
}
|
||||
}
|
||||
|
||||
if l == pl {
|
||||
// Continue search
|
||||
search = search[l:]
|
||||
} else {
|
||||
cn = nn
|
||||
search = ns
|
||||
if nk == pkind {
|
||||
goto Param
|
||||
} else if nk == akind {
|
||||
goto Any
|
||||
}
|
||||
// Not found
|
||||
return
|
||||
}
|
||||
|
||||
if search == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// Static node
|
||||
if child = cn.findChild(search[0], skind); child != nil {
|
||||
// Save next
|
||||
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
||||
nk = pkind
|
||||
nn = cn
|
||||
ns = search
|
||||
}
|
||||
cn = child
|
||||
continue
|
||||
}
|
||||
|
||||
// Param node
|
||||
Param:
|
||||
if child = cn.findChildByKind(pkind); child != nil {
|
||||
// Issue #378
|
||||
if len(pvalues) == n {
|
||||
continue
|
||||
}
|
||||
|
||||
// Save next
|
||||
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
||||
nk = akind
|
||||
nn = cn
|
||||
ns = search
|
||||
}
|
||||
|
||||
cn = child
|
||||
i, l := 0, len(search)
|
||||
for ; i < l && search[i] != '/'; i++ {
|
||||
}
|
||||
pvalues[n] = search[:i]
|
||||
n++
|
||||
search = search[i:]
|
||||
continue
|
||||
}
|
||||
|
||||
// Any node
|
||||
Any:
|
||||
if cn = cn.findChildByKind(akind); cn == nil {
|
||||
if nn != nil {
|
||||
cn = nn
|
||||
nn = cn.parent // Next (Issue #954)
|
||||
search = ns
|
||||
if nk == pkind {
|
||||
goto Param
|
||||
} else if nk == akind {
|
||||
goto Any
|
||||
}
|
||||
}
|
||||
// Not found
|
||||
return
|
||||
}
|
||||
pvalues[len(cn.pnames)-1] = search
|
||||
break
|
||||
}
|
||||
|
||||
ctx.handler = cn.findHandler(method)
|
||||
ctx.path = cn.ppath
|
||||
ctx.pnames = cn.pnames
|
||||
|
||||
// NOTE: Slow zone...
|
||||
if ctx.handler == nil {
|
||||
ctx.handler = cn.checkMethodNotAllowed()
|
||||
|
||||
// Dig further for any, might have an empty value for *, e.g.
|
||||
// serving a directory. Issue #207.
|
||||
if cn = cn.findChildByKind(akind); cn == nil {
|
||||
return
|
||||
}
|
||||
if h := cn.findHandler(method); h != nil {
|
||||
ctx.handler = h
|
||||
} else {
|
||||
ctx.handler = cn.checkMethodNotAllowed()
|
||||
}
|
||||
ctx.path = cn.ppath
|
||||
ctx.pnames = cn.pnames
|
||||
pvalues[len(cn.pnames)-1] = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user