ship

package module
v5.3.1 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2023 License: Apache-2.0 Imports: 32 Imported by: 26

README

ship Build Status GoDoc License

ship is a flexible, powerful, high performance and minimalist Go Web HTTP router framework supporting Go 1.11+. It is inspired by echo and httprouter. Thanks for those contributors.

Features

  • Support the url parameter.
  • Support the session manager.
  • Support the customized router manager.
  • Support the pre-route and route middlewares.
  • Support the route group builder to build the route.
  • Support the mulit-virtual hosts and the default host.
  • Support the exact, prefix, suffix and regexp hostname.
  • Support the binding of the request data, such as body and query.
  • Support the renderer, such as the HTML template.
  • ......
Components
  • Ship is the pure router framework based on the method and the path, including Middleware, Context, Router, etc.
  • HostManager and HostHandler are the vhost manager and the standard http handler with the vhost manager.
  • Runner is the runner to start the http server with the standard http handler.

Install

go get -u github.com/xgfone/ship/v5

Quick Start

// example.go
package main

import (
	"github.com/xgfone/ship/v5"
	"github.com/xgfone/ship/v5/middleware"
)

func main() {
	router := ship.New()
	router.Use(middleware.Logger(), middleware.Recover()) // Use the middlewares.

	router.Route("/ping").GET(func(c *ship.Context) error {
		return c.JSON(200, map[string]interface{}{"message": "pong"})
	})

	group := router.Group("/group")
	group.Route("/ping").GET(func(c *ship.Context) error {
		return c.Text(200, "group")
	})

	subgroup := group.Group("/subgroup")
	subgroup.Route("/ping").GET(func(c *ship.Context) error {
		return c.Text(200, "subgroup")
	})

	// Start the HTTP server.
	ship.StartServer(":8080", router)
	// or
	// http.ListenAndServe(":8080", router)
}
$ go run example.go
$ curl http://127.0.0.1:8080/ping
{"message":"pong"}

$ curl http://127.0.0.1:8080/group/ping
group

$ curl http://127.0.0.1:8080/group/subgroup/ping
subgroup
Route Path

The route path supports the parameters like :paramName, * or *restParamName.

  • /path/to/route only matches the path /path/to/route.
  • /path/:param1/to matches the path /path/abc/to, /path/xyz/to, etc. And :param1 is equal to abc or xyz.
  • /path/:param1/to/:param2 matches the path /path/p11/to/p21, /path/p12/to/p22, etc. And :parma1 is equal to p11 or p12, and :param2 is equal to p12 or p22.
  • /path/to/* or /path/to/*all matches the path /path/to/abc, /path/to/abc/efg, /path/to/xyz, /path/to/xyz/123, etc. And * or *all is equal to abc, abc/efg, xyz, or xzy/123. Notice: * or *restParamName must be the last one of the route path.
  • /path/:param/to/* matches the path /path/abc/to/efg, /path/abc/to/efg/123, etc. And :param is equal to abc, and * is equal to efg or efg/123

For the parameter, it can be accessed by Context.Param(paramName).

  • For *, the parameter name is *, like Context.Param("*").
  • For *restParamName, the parameter name is restParamName, like Context.Param(restParamName).

API Example

Route Builder
Using CONNECT, HEAD, GET, POST, PUT, PATCH, DELETE and OPTIONS
func main() {
	router := ship.New()
	router.Route("/path/get").GET(getHandler)
	router.Route("/path/put").PUT(putHandler)
	router.Route("/path/head").HEAD(headHandler)
	router.Route("/path/post").POST(postHandler)
	router.Route("/path/patch").PATCH(patchHandler)
	router.Route("/path/delete").DELETE(deleteHandler)
	router.Route("/path/option").OPTIONS(optionHandler)
	router.Route("/path/connect").CONNECT(connectHandler)
	ship.StartServer(":8080", router)
}

Notice: you can register the same handler with more than one method by Route(path string).Method(handler Handler, method ...string).

Cascade the registered routes
func main() {
	router := ship.New()
	router.Route("/path/to").GET(getHandler).POST(postHandler).DELETE(deleteHandler)
	ship.StartServer(":8080", router)
}
Use the mapping of the route methods
func main() {
	router := ship.New()
	router.Route("/path/to").Map(map[string]ship.Handler{
		"GET": getHandler,
		"POST": postHandler,
		"DELETE": deleteHandler,
	})
	ship.StartServer(":8080", router)
}
Name the route

When registering the route, it can be named with a name.

func main() {
	router := ship.New()
	router.Route("/path/:id").Name("get_url").GET(func(c *ship.Context) error {
		fmt.Println(c.URL("get_url", c.Param("id")))
		return nil
	})
	ship.StartServer(":8080", router)
}
Use the route group
package main

import (
	"github.com/xgfone/ship/v5"
	"github.com/xgfone/ship/v5/middleware"
)

// MyAuthMiddleware returns a middleare to authenticate the request.
func MyAuthMiddleware() ship.Middleware {
	return func(next ship.Handler) ship.Handler {
		return func(c *ship.Context) error {
			// TODO: authenticate the request.
			return next(c)
		}
	}
}

func main() {
	router := ship.New()
	router.Use(middleware.Logger(), middleware.Recover())

	// v1 Group, which will inherit the middlewares of the parent router.
	v1 := router.Group("/v1")
	v1.Route("/get").GET(func(c *ship.Context) error { return nil }) // Route: GET /v1/get

	// v2 Group, which won't inherit the middlewares of the parent router.
	v2 := router.Group("/v2").ResetMiddlewares(MyAuthMiddleware())
	v2.Route("/post").POST(func(c *ship.Context) error { return nil }) // Route: POST /v2/post

	// For sub-group of v2 Group.
	v2g := v2.Group("/child")
	v2g.Route("/path").GET(func(c *ship.Context) error { return nil }) // Route: GET /v2/child/path

	ship.StartServer(":8080", router)
}
Filter the unacceptable routes
package main

import (
	"strings"

	"github.com/xgfone/ship/v5"
)

func filter(ri ship.Route) bool {
	if ri.Name == "" || !strings.HasPrefix(ri.Path, "/prefix/") {
		return true
	}
	return false
}

func main() {
	handler := func(c *ship.Context) error { return nil }

	router := ship.New()
	router.RouteFilter = filter // Don't register the router without name.

	router.Group("/prefix").Route("/name").Name("test").GET(handler) // Register the route
	router.Group("/prefix").Route("/noname").GET(handler)            // Don't register the route
	router.Route("/no_group").GET(handler)                           // Don't register the route

	ship.StartServer(":8080", router)
}
Modify the route before registering it
package main

import "github.com/xgfone/ship/v5"

func modifier(ri ship.Route) ship.Route {
	ri.Path = "/prefix" + ri.Path
	return ri
}

func main() {
	handler := func(c *ship.Context) error { return nil }

	router := ship.New()
	router.RouteModifier = modifier
	router.Route("/path").Name("test").GET(handler) // Register the path as "/prefix/path".

	ship.StartServer(":8080", router)
}
Use Middleware
package main

import (
	"fmt"
	"strings"

	"github.com/xgfone/ship/v5"
	"github.com/xgfone/ship/v5/middleware"
)

// RemovePathPrefix returns a middleware to remove the prefix from the request path.
func RemovePathPrefix(prefix string) ship.Middleware {
	if len(prefix) < 2 || prefix[len(prefix)-1] == '/' {
		panic(fmt.Errorf("invalid prefix: '%s'", prefix))
	}

	return func(next ship.Handler) ship.Handler {
		return func(c *ship.Context) error {
			req := c.Request()
			req.URL.Path = strings.TrimPrefix(req.URL.Path, prefix)
			return next(c)
		}
	}
}

func main() {
	router := ship.New()

	// Execute the middlewares before finding the route.
	router.Pre(RemovePathPrefix("/static"))

	// Execute the middlewares after finding the route.
	router.Use(middleware.Logger(), middleware.Recover())

	handler := func(c *ship.Context) error { return nil }
	router.Route("/path1").GET(handler)
	router.Route("/path2").GET(handler)
	router.Route("/path3").GET(handler)

	ship.StartServer(":8080", router)
}
Use the virtual host
package main

import (
	"github.com/xgfone/ship/v5"
)

func main() {
	vhosts := ship.NewHostManagerHandler(nil)

	_default := ship.New()
	_default.Route("/").GET(func(c *ship.Context) error { return c.Text(200, "default") })
	vhosts.SetDefaultHost("", _default)

	// Exact Match Host
	vhost1 := ship.New()
	vhost1.Route("/").GET(func(c *ship.Context) error { return c.Text(200, "vhost1") })
	vhosts.AddHost("www.host1.example.com", vhost1)

	// Suffix Match Host
	vhost2 := ship.New()
	vhost2.Route("/").GET(func(c *ship.Context) error { return c.Text(200, "vhost2") })
	vhosts.AddHost("*.host2.example.com", vhost2)

	// Prefix Match Host
	vhost3 := ship.New()
	vhost3.Route("/").GET(func(c *ship.Context) error { return c.Text(200, "vhost3") })
	vhosts.AddHost("www.host3.*", vhost3)

	// Regexp Match Host by using Go regexp package
	vhost4 := ship.New()
	vhost4.Route("/").GET(func(c *ship.Context) error { return c.Text(200, "vhost4") })
	vhosts.AddHost(`www\.[a-zA-z0-9]+\.example\.com`, vhost4)

	ship.StartServer(":8080", vhosts)
}
$ curl http://127.0.0.1:8080/
default

$ curl http://127.0.0.1:8080/ -H 'Host: www.host1.example.com' # Exact
vhost1

$ curl http://127.0.0.1:8080/ -H 'Host: www.host2.example.com' # Suffix
vhost2

$ curl http://127.0.0.1:8080/ -H 'Host: www.host3.example.com' # Prefix
vhost3

$ curl http://127.0.0.1:8080/ -H 'Host: www.host4.example.com' # Regexp
vhost4
Handle the complex response
package main

import "github.com/xgfone/ship/v5"

func responder(c *ship.Context, args ...interface{}) error {
	switch len(args) {
	case 0:
		return c.NoContent(200)
	case 1:
		switch v := args[0].(type) {
		case int:
			return c.NoContent(v)
		case string:
			return c.Text(200, v)
		}
	case 2:
		switch v0 := args[0].(type) {
		case int:
			return c.Text(v0, "%v", args[1])
		}
	}
	return c.NoContent(500)
}

func main() {
	router := ship.New()
	router.Responder = responder
	router.Route("/path1").GET(func(c *ship.Context) error { return c.Respond() })
	router.Route("/path2").GET(func(c *ship.Context) error { return c.Respond(200) })
	router.Route("/path3").GET(func(c *ship.Context) error { return c.Respond("Hello, World") })
	router.Route("/path4").GET(func(c *ship.Context) error { return c.Respond(200, "Hello, World") })
	ship.StartServer(":8080", router)
}
Bind JSON, XML or Form data from the request payload
package main

import "github.com/xgfone/ship/v5"

// Login is the login information.
type Login struct {
	Username string `json:"username" xml:"username"`
	Password string `json:"password" xml:"password"`
}

func main() {
	router := ship.Default()
	router.Route("/login").POST(func(c *ship.Context) (err error) {
		var login Login
		if err = c.Bind(&login); err != nil {
			return ship.ErrBadRequest.New(err)
		}
		return c.Text(200, "username=%s, password=%s", login.Username, login.Password)
	})

	ship.StartServer(":8080", router)
}
$ curl http://127.0.0.1:8080/login \
    -H 'Content-Type: application/json' \
    -d '{"username":"xgfone","password":"123456"}'
username=xgfone, password=123456

$ curl http://127.0.0.1:8080/login \
    -H 'Content-Type: application/xml' \
    -d '<login><username>xgfone</username><password>123456</password></login>'
username=xgfone, password=123456
Render HTML template

In the directory /path/to/templates, there is a template file named index.tmpl as follow:

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        This is the body content: </pre>{{ . }}</pre>
    </body>
</html>
package main

import (
	"github.com/xgfone/ship/v5"
	"github.com/xgfone/ship/v5/render/template"
)

func main() {
	// It will recursively load all the files in the directory as the templates.
	loader := template.NewDirLoader("/path/to/templates")
	tmplRender := template.NewHTMLTemplateRender(loader)

	router := ship.Default()
	router.Renderer.(*ship.MuxRenderer).Add(".tmpl", tmplRender)
	router.Route("/html").GET(func(c *ship.Context) error {
		return c.RenderOk("index.tmpl", "Hello World")
	})

	// Start the HTTP server.
	ship.StartServer(":8080", router)
}

When accessing http://127.0.0.1:8080/html, it returns

<!DOCTYPE html>
<html>
    <head></head>
    <body>
        This is the body content: </pre>Hello World</pre>
    </body>
</html>

Route Management

ship supply a default implementation based on Radix tree to manage the route with Zero Garbage (See Benchmark), which refers to echo, that's, NewRouter().

You can appoint your own implementation by implementing the interface Router.

type Router interface {
	// Range traverses all the registered routes.
	Range(func(name, path, method string, handler interface{}))

	// Path generates a url path by the path name and parameters.
	//
	// Return "" if there is not the route path named name.
	Path(name string, params ...interface{}) string

	// Add adds the route and returns the number of the parameters
	// if there are the parameters in the route path.
	//
	// name is the name of the path, which is optional and must be unique
	// if not empty.
	//
	// If method is empty, handler is the handler of all the methods supported
	// by the implementation. Or, it is only that of the given method.
	//
	// For the parameter in the path, the format is determined by the implementation.
	Add(name, path, method string, handler interface{}) (paramNum int, err error)

	// Del deletes the given route.
	//
	// If method is empty, deletes all the routes associated with the path.
	// Or, only delete the given method for the path.
	Del(path, method string) (err error)

	// Match matches the route by path and method, puts the path parameters
	// into pnames and pvalues, then returns the handler and the number
	// of the path paramethers.
	//
	// If pnames or pvalues is empty, it will ignore the path paramethers
	// when finding the route handler.
	//
	// Return (nil, 0) if not found the route handler.
	Match(path, method string, pnames, pvalues []string) (handler interface{}, pn int)
}
func main() {
	NewMyRouter := func() (router ship.Router) {
		// TODO: new a Router.
		return
	}

	router := ship.New()
	router.Router = NewMyRouter()
	// ...
}

Benchmark

HP Laptop 14s-dr2014TU
go:     1.16.4
goos:   windows
goarch: amd64
memory: 16GB DDR4-3200
cpu:    11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
Framework Version
github.com/gin-gonic/gin v1.7.2
github.com/labstack/echo/v4 v4.4.0
github.com/xgfone/ship/v5 v5.0.0
Function ops ns/op B/opt allocs/op
BenchmarkEchoStatic-8 43269 27676 2056 157
BenchmarkEchoGitHubAPI-8 29738 40773 2788 203
BenchmarkEchoGplusAPI-8 668731 1967 207 13
BenchmarkEchoParseAPI-8 362774 3369 398 26
BenchmarkGinStatic-8 47384 24037 8267 157
BenchmarkGinGitHubAPI-8 33747 34648 10771 203
BenchmarkGinGplusAPI-8 598628 1830 681 13
BenchmarkGinParseAPI-8 356298 3314 1442 26
BenchmarkShipEchoStatic-8 51788 23219 668 0
BenchmarkShipEchoGitHubAPI-8 32854 35759 1054 0
BenchmarkShipEchoGplusAPI-8 746049 1809 92 0
BenchmarkShipEchoParseAPI-8 396067 3310 174 0
Function ops ns/op B/opt allocs/op
BenchmarkShipWithoutVHost-8 19691887 54.53 0 0
BenchmarkShipWithExactVHost-8 17158249 64.19 0 0
BenchmarkShipWithPrefixVHost-8 13445091 90.81 0 0
BenchmarkShipWithRegexpVHost-8 4668913 248.0 0 0

Documentation

Overview

Package ship has implemented a flexible, powerful, high performance and minimalist Go Web HTTP router framework, which is inspired by echo and httprouter.

Index

Examples

Constants

View Source
const (
	CharsetUTF8 = "charset=UTF-8"
	PROPFIND    = "PROPFIND"
)

Predefine some variables

View Source
const (
	MIMETextXML               = "text/xml"
	MIMETextHTML              = "text/html"
	MIMETextPlain             = "text/plain"
	MIMEApplicationXML        = "application/xml"
	MIMEApplicationJSON       = "application/json"
	MIMEApplicationJavaScript = "application/javascript"
	MIMEApplicationForm       = "application/x-www-form-urlencoded"
	MIMEApplicationProtobuf   = "application/protobuf"
	MIMEApplicationMsgpack    = "application/msgpack"
	MIMEOctetStream           = "application/octet-stream"
	MIMEMultipartForm         = "multipart/form-data"

	MIMETextXMLCharsetUTF8               = MIMETextXML + "; " + CharsetUTF8
	MIMETextHTMLCharsetUTF8              = MIMETextHTML + "; " + CharsetUTF8
	MIMETextPlainCharsetUTF8             = MIMETextPlain + "; " + CharsetUTF8
	MIMEApplicationXMLCharsetUTF8        = MIMEApplicationXML + "; " + CharsetUTF8
	MIMEApplicationJSONCharsetUTF8       = MIMEApplicationJSON + "; " + CharsetUTF8
	MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + CharsetUTF8
)

MIME types

View Source
const (
	HeaderAccept              = "Accept"              // RFC 7231, 5.3.2
	HeaderAcceptCharset       = "Accept-Charset"      // RFC 7231, 5.3.3
	HeaderAcceptEncoding      = "Accept-Encoding"     // RFC 7231, 5.3.4
	HeaderAcceptLanguage      = "Accept-Language"     // RFC 7231, 5.3.5
	HeaderAcceptPatch         = "Accept-Patch"        // RFC 5789, 3.1
	HeaderAcceptRanges        = "Accept-Ranges"       // RFC 7233, 2.3
	HeaderAge                 = "Age"                 // RFC 7234, 5.1
	HeaderAllow               = "Allow"               // RFC 7231, 7.4.1
	HeaderAuthorization       = "Authorization"       // RFC 7235, 4.2
	HeaderCacheControl        = "Cache-Control"       // RFC 7234, 5.2
	HeaderConnection          = "Connection"          // RFC 7230, 6.1
	HeaderContentDisposition  = "Content-Disposition" // RFC 6266
	HeaderContentEncoding     = "Content-Encoding"    // RFC 7231, 3.1.2.2
	HeaderContentLanguage     = "Content-Language"    // RFC 7231, 3.1.3.2
	HeaderContentLength       = "Content-Length"      // RFC 7230, 3.3.2
	HeaderContentLocation     = "Content-Location"    // RFC 7231, 3.1.4.2
	HeaderContentRange        = "Content-Range"       // RFC 7233, 4.2
	HeaderContentType         = "Content-Type"        // RFC 7231, 3.1.1.5
	HeaderCookie              = "Cookie"              // RFC 2109, 4.3.4
	HeaderDate                = "Date"                // RFC 7231, 7.1.1.2
	HeaderETag                = "ETag"                // RFC 7232, 2.3
	HeaderExpect              = "Expect"              // RFC 7231, 5.1.1
	HeaderExpires             = "Expires"             // RFC 7234, 5.3
	HeaderFrom                = "From"                // RFC 7231, 5.5.1
	HeaderHost                = "Host"                // RFC 7230, 5.4
	HeaderIfMatch             = "If-Match"            // RFC 7232, 3.1
	HeaderIfModifiedSince     = "If-Modified-Since"   // RFC 7232, 3.3
	HeaderIfNoneMatch         = "If-None-Match"       // RFC 7232, 3.2
	HeaderIfRange             = "If-Range"            // RFC 7233, 3.2
	HeaderIfUnmodifiedSince   = "If-Unmodified-Since" // RFC 7232, 3.4
	HeaderLastModified        = "Last-Modified"       // RFC 7232, 2.2
	HeaderLink                = "Link"                // RFC 5988
	HeaderLocation            = "Location"            // RFC 7231, 7.1.2
	HeaderMaxForwards         = "Max-Forwards"        // RFC 7231, 5.1.2
	HeaderOrigin              = "Origin"              // RFC 6454
	HeaderPragma              = "Pragma"              // RFC 7234, 5.4
	HeaderProxyAuthenticate   = "Proxy-Authenticate"  // RFC 7235, 4.3
	HeaderProxyAuthorization  = "Proxy-Authorization" // RFC 7235, 4.4
	HeaderRange               = "Range"               // RFC 7233, 3.1
	HeaderReferer             = "Referer"             // RFC 7231, 5.5.2
	HeaderRetryAfter          = "Retry-After"         // RFC 7231, 7.1.3
	HeaderServer              = "Server"              // RFC 7231, 7.4.2
	HeaderSetCookie           = "Set-Cookie"          // RFC 2109, 4.2.2
	HeaderSetCookie2          = "Set-Cookie2"         // RFC 2965
	HeaderTE                  = "TE"                  // RFC 7230, 4.3
	HeaderTrailer             = "Trailer"             // RFC 7230, 4.4
	HeaderTransferEncoding    = "Transfer-Encoding"   // RFC 7230, 3.3.1
	HeaderUpgrade             = "Upgrade"             // RFC 7230, 6.7
	HeaderUserAgent           = "User-Agent"          // RFC 7231, 5.5.3
	HeaderVary                = "Vary"                // RFC 7231, 7.1.4
	HeaderVia                 = "Via"                 // RFC 7230, 5.7.1
	HeaderWarning             = "Warning"             // RFC 7234, 5.5
	HeaderWWWAuthenticate     = "WWW-Authenticate"    // RFC 7235, 4.1
	HeaderForwarded           = "Forwarded"           // RFC 7239
	HeaderXForwardedBy        = "X-Forwarded-By"      // RFC 7239, 5.1
	HeaderXForwardedFor       = "X-Forwarded-For"     // RFC 7239, 5.2
	HeaderXForwardedHost      = "X-Forwarded-Host"    // RFC 7239, 5.3
	HeaderXForwardedPort      = "X-Forwarded-Port"
	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"
	HeaderXServerID           = "X-Server-Id"
	HeaderXRequestID          = "X-Request-Id"
	HeaderXRequestedWith      = "X-Requested-With"

	// Access control
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" // https://www.w3.org/TR/cors/#http-access-control-allow-credentials
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"     // https://www.w3.org/TR/cors/#http-access-control-allow-headers
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"     // https://www.w3.org/TR/cors/#http-access-control-allow-methods
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"      // https://www.w3.org/TR/cors/#http-access-control-allow-origin
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"    // https://www.w3.org/TR/cors/#http-access-control-expose-headers
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"           // https://www.w3.org/TR/cors/#http-access-control-max-age
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"   // https://www.w3.org/TR/cors/#http-access-control-request-headers
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"    // https://www.w3.org/TR/cors/#http-access-control-request-method

	// 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"
)

Headers

Variables

View Source
var (
	ErrMissingContentType  = errors.New("missing the header 'Content-Type'")
	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
	ErrSessionNotExist     = errors.New("session does not exist")
	ErrInvalidSession      = errors.New("invalid session")
)

Some non-HTTP Errors

Some HTTP error.

View Source
var DefaultShip = Default()

DefaultShip is the default global ship.

DefaultSignals is a set of default signals.

View Source
var ErrSkip = errors.New("skip")

ErrSkip is not an error, which is used to suggest that the middeware should skip and return it back to the outer middleware to handle.

View Source
var IsDomainName func(domainName string) bool = isDomainName

IsDomainName is used to check whether the domain name is valid or not. And you can reset it to a customized one.

The default implementation has these limits as follow:

  • The maximum length of the full qualified domain name is equal to 253.
  • The maximum length of the sub-domain name is equal to 63.
  • The valid characters only contain "a-zA-z0-9_-.".
View Source
var MaxMemoryLimit int64 = 32 << 20 // 32MB

MaxMemoryLimit is the maximum memory.

Functions

func AddContentTypeMapping

func AddContentTypeMapping(contentType string, contentTypeSlice []string)

AddContentTypeMapping add a content type mapping to convert contentType to contentTypeSlice, which is used by SetContentType to set the header "Content-Type" to contentTypeSlice by contentType to avoid allocating the memory.

If contentTypeSlice is empty, it is []string{contentType} by default.

func DisalbeRedirect

func DisalbeRedirect(req *http.Request, via []*http.Request) error

DisalbeRedirect is used to disalbe the default redirect behavior of http.Client, that's, http.Client won't handle the redirect response and just return it to the caller.

func InStrings

func InStrings(s string, ss []string) bool

InStrings reports whether s is in the string slice ss or not.

func IsInteger

func IsInteger(s string) bool

IsInteger reports whether s is the integer or not.

func NewRequestWithContext

func NewRequestWithContext(ctx context.Context, method, url string,
	body io.Reader) (*http.Request, error)

NewRequestWithContext is the compatibility of http.NewRequestWithContext.

func PutResponseIntoPool

func PutResponseIntoPool(r *Response)

PutResponseIntoPool puts a Response into the pool.

func SetContentType

func SetContentType(header http.Header, ct string)

SetContentType sets the header "Content-Type" to ct.

func SetContext added in v5.1.2

func SetContext(ctx context.Context, c *Context) (newctx context.Context)

SetContext sets the http request context into the context.

func SetStructFieldToDefault

func SetStructFieldToDefault(v interface{}) (err error)

SetStructFieldToDefault sets the default value of the fields of the pointer to struct v to the value of the tag "default" of the fields when the field value is ZERO.

For the type of the field, it only supports some base types as follow:

string
float32
float64
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
struct
struct slice
interface{ SetDefault(_default interface{}) error }
time.Time      // Format: A. Integer(UTC); B. String(RFC3339)
time.Duration  // Format: A. Integer(ms);  B. String(time.ParseDuration)
pointer to the types above

Notice: If the tag value starts with ".", it represents a field name and the default value of current field is set to the value of that field. But their types must be consistent, or panic.

Example
type Struct struct {
	InnerInt int `default:"123"`
	_        int `default:"-"`
}

type S struct {
	Ignore  bool    `default:"true"`
	Int     int     `default:"123"`
	Int8    int8    `default:"123"`
	Int16   int16   `default:"123"`
	Int32   int32   `default:"123"`
	Int64   int64   `default:"123"`
	Uint    uint    `default:"123"`
	Uint8   uint8   `default:"123"`
	Uint16  uint16  `default:"123"`
	Uint32  uint32  `default:"123"`
	Uint64  uint64  `default:"123"`
	Uintptr uintptr `default:"123"`
	Float32 float32 `default:"1.2"`
	Float64 float64 `default:"1.2"`
	FloatN  float64 `default:".Float64"` // Set the default value to other field
	String  string  `default:"abc"`
	Struct  Struct
	Structs []Struct
	_       int `default:"-"`

	DurationInt time.Duration `default:"1000"`
	DurationStr time.Duration `default:"2s"`
	TimeInt     time.Time     `default:"1618059388"`
	TimeStr     time.Time     `default:"2021-04-10T12:56:28Z"`

	NoneP       *int
	IntPtr      *int           `default:"456"`
	TimePtr     *time.Time     `default:"2021-04-10T12:56:28Z"`
	DurationPtr *time.Duration `default:"3s"`
}

s := S{Structs: make([]Struct, 2)}
err := SetStructFieldToDefault(&s)
fmt.Println(err)

fmt.Println(s.Ignore)
fmt.Println(s.Int)
fmt.Println(s.Int8)
fmt.Println(s.Int16)
fmt.Println(s.Int32)
fmt.Println(s.Int64)
fmt.Println(s.Uint)
fmt.Println(s.Uint8)
fmt.Println(s.Uint16)
fmt.Println(s.Uint32)
fmt.Println(s.Uint64)
fmt.Println(s.Uintptr)
fmt.Println(s.Float32)
fmt.Println(s.Float64)
fmt.Println(s.FloatN)
fmt.Println(s.String)
fmt.Println(s.Struct.InnerInt)
fmt.Println(s.Structs[0].InnerInt)
fmt.Println(s.Structs[1].InnerInt)
fmt.Println(s.DurationInt)
fmt.Println(s.DurationStr)
fmt.Println(s.TimeInt.UTC().Format(time.RFC3339))
fmt.Println(s.TimeStr.UTC().Format(time.RFC3339))
fmt.Println(s.NoneP == nil)
fmt.Println(*s.IntPtr)
fmt.Println(s.TimePtr.UTC().Format(time.RFC3339))
fmt.Println(*s.DurationPtr)
Output:

<nil>
false
123
123
123
123
123
123
123
123
123
123
123
1.2
1.2
1.2
abc
123
123
123
1s
2s
2021-04-10T12:56:28Z
2021-04-10T12:56:28Z
true
456
2021-04-10T12:56:28Z
3s

func SplitHostPort

func SplitHostPort(hostport string) (host, port string)

SplitHostPort separates host and port. If the port is not valid, it returns the entire input as host, and it doesn't check the validity of the host. Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.

Example
var host, port string

host, port = SplitHostPort("www.example.com")
fmt.Printf("Host: %s, Port: %s#\n", host, port)

host, port = SplitHostPort("www.example.com:80")
fmt.Printf("Host: %s, Port: %s#\n", host, port)

host, port = SplitHostPort(":80")
fmt.Printf("Host: %s, Port: %s#\n", host, port)

host, port = SplitHostPort("1.2.3.4:80")
fmt.Printf("Host: %s, Port: %s#\n", host, port)

host, port = SplitHostPort("[fe80::1122:3344:5566:7788]")
fmt.Printf("Host: %s, Port: %s#\n", host, port)

host, port = SplitHostPort("[fe80::1122:3344:5566:7788]:80")
fmt.Printf("Host: %s, Port: %s#\n", host, port)
Output:

Host: www.example.com, Port: #
Host: www.example.com, Port: 80#
Host: , Port: 80#
Host: 1.2.3.4, Port: 80#
Host: fe80::1122:3344:5566:7788, Port: #
Host: fe80::1122:3344:5566:7788, Port: 80#

func StartServer

func StartServer(addr string, handler http.Handler)

StartServer is convenient function to new a runner to start the http server.

func StartServerTLS

func StartServerTLS(addr string, handler http.Handler, certFile, keyFile string)

StartServerTLS is the same as StartServer, and tries to start the http server with the cert and key file. If certFile or keyFile is empty, however, it is equal to StartServer.

func ToHTTPHandler

func ToHTTPHandler(s *Ship, h Handler) http.Handler

ToHTTPHandler converts the Handler to http.Handler

Types

type Binder

type Binder interface {
	// Bind parses the data from http.Request to dst.
	//
	// Notice: dst must be a non-nil pointer.
	Bind(dst interface{}, req *http.Request) error
}

Binder is the interface to bind the value dst to req.

func FormBinder

func FormBinder(maxMemory int64, tag ...string) Binder

FormBinder returns a binder to bind the data to the request body as Form.

Notice: The bound value must be a pointer to a struct with the tag named tag, which is "form" by default.

func JSONBinder

func JSONBinder() Binder

JSONBinder returns a binder to bind the data to the request body as JSON.

func XMLBinder

func XMLBinder() Binder

XMLBinder returns a binder to bind the data to the request body as XML.

type BinderFunc

type BinderFunc func(dst interface{}, req *http.Request) error

BinderFunc is a function type implementing the interface Binder.

func (BinderFunc) Bind

func (f BinderFunc) Bind(dst interface{}, req *http.Request) error

Bind implements the interface Binder.

type BufferAllocator

type BufferAllocator interface {
	AcquireBuffer() *bytes.Buffer
	ReleaseBuffer(*bytes.Buffer)
}

BufferAllocator is used to acquire and release a buffer.

type Context

type Context struct {
	// Route is the route information associated with the route.
	Route Route

	// Any is the any context data associated with the route.
	//
	// Notice: when the new request is coming, they will be reset to nil.
	Any interface{}

	// Data is used to store many key-value pairs about the context.
	//
	// Notice: when the new request is coming, they will be cleaned out.
	Data map[string]interface{}

	// Public Configuration, which are not reset when calling Reset().
	BufferAllocator
	Logger
	Router      Router
	Session     Session
	NotFound    Handler
	Binder      Binder
	Renderer    Renderer
	Defaulter   Defaulter
	Validator   Validator
	Responder   func(*Context, ...interface{}) error
	QueryBinder func(interface{}, url.Values) error
	// contains filtered or unexported fields
}

Context represetns a request and response context.

func GetContext added in v5.1.2

func GetContext(ctx context.Context) *Context

GetContext returns the http reqeust context from the context.

func NewContext

func NewContext(urlParamMaxNum, dataInitCap int) *Context

NewContext returns a new Context.

func (*Context) Accept

func (c *Context) Accept() []string

Accept returns the accepted Content-Type list from the request header "Accept", which are sorted by the q-factor weight from high to low.

If there is no the request header "Accept", return nil.

Notice:

  1. If the value is "*/*", it will be amended as "".
  2. If the value is "<MIME_type>/*", it will be amended as "<MIME_type>/". So it can be used to match the prefix.

func (*Context) AddRespHeader

func (c *Context) AddRespHeader(name, value string)

AddRespHeader appends the value into the response header named name.

func (*Context) Attachment

func (c *Context) Attachment(file string, name string) error

Attachment is the same as File, but sets the header "Content-Disposition" with the type "attachment" to prompt the client to save the file with the name.

If the file does not exist, it returns ErrNotFound.

func (*Context) Bind

func (c *Context) Bind(v interface{}) (err error)

Bind extracts the data information from the request and assigns it to v, then validates whether it is valid or not.

func (*Context) BindQuery

func (c *Context) BindQuery(v interface{}) (err error)

BindQuery extracts the data from the request url query and assigns it to v, then validates whether it is valid or not.

func (*Context) Blob

func (c *Context) Blob(code int, contentType string, b []byte) (err error)

Blob sends a blob response with the status code and the content type.

func (*Context) BlobText

func (c *Context) BlobText(code int, contentType string,
	format string, args ...interface{}) (err error)

BlobText sends a string blob response with the status code and the content type.

func (*Context) BlobXML

func (c *Context) BlobXML(code int, b []byte) (err error)

BlobXML sends an XML blob response with the status code.

func (*Context) Body

func (c *Context) Body() io.ReadCloser

Body returns the reader of the request body.

func (*Context) Charset

func (c *Context) Charset() string

Charset returns the charset of the request content.

Return "" if there is no charset.

func (*Context) ClientIP

func (c *Context) ClientIP() string

ClientIP returns the real client's network address based on `X-Forwarded-For` or `X-Real-Ip` request header. Or returns the remote address.

func (*Context) ContentLength

func (c *Context) ContentLength() int64

ContentLength return the length of the request body.

func (*Context) ContentType

func (c *Context) ContentType() (ct string)

ContentType returns the Content-Type of the request without the charset.

func (*Context) Cookie

func (c *Context) Cookie(name string) *http.Cookie

Cookie returns the named cookie provided in the request.

Return nil if no the cookie named name.

func (*Context) Cookies

func (c *Context) Cookies() []*http.Cookie

Cookies returns the HTTP cookies sent with the request.

func (*Context) DelRespHeader

func (c *Context) DelRespHeader(name string)

DelRespHeader deletes the response header named name.

func (*Context) DelSession

func (c *Context) DelSession(id string) (err error)

DelSession deletes the session from the backend store.

func (*Context) Error

func (c *Context) Error(code int, err error) HTTPServerError

Error returns a http error with the status code.

func (*Context) Execute

func (c *Context) Execute() error

Execute finds the route by the request method and path, then executes the handler of the found route, which is equal to the union of FindRoute and ExecuteRoute.

func (*Context) ExecuteRoute

func (c *Context) ExecuteRoute() error

ExecuteRoute executes the handler of the found route.

Notice: You should call FindRoute before calling this method.

func (*Context) File

func (c *Context) File(file string) (err error)

File sends a file response, and the body is the content of the file.

If not set the Content-Type, it will deduce it from the extension of the file name. If the file does not exist, it returns ErrNotFound.

func (*Context) FindRoute

func (c *Context) FindRoute() (ok bool)

FindRoute finds the route by the request method and path and put it into the field Route of Context.

For the handler registered into the underlying Router, it supports three kinds of types as follow:

  • Route
  • Handler
  • http.Handler
  • http.HandlerFunc

func (*Context) Form

func (c *Context) Form(name string, defaultValue ...string) string

Form returns the form value by the field name.

Return defaultValue instead if the form field name does not exist.

func (*Context) FormFile

func (c *Context) FormFile(name string) (multipart.File, *multipart.FileHeader, error)

FormFile returns the multipart form file by the field name.

func (*Context) Forms

func (c *Context) Forms() (url.Values, error)

Forms returns all the form values.

func (*Context) GetReqHeader

func (c *Context) GetReqHeader(name string, defaultValue ...string) string

GetReqHeader returns the first value of the request header named name.

Return defaultValue instead if the header does not exist.

func (*Context) GetSession

func (c *Context) GetSession(id string) (v interface{}, err error)

GetSession returns the session content by id from the backend store.

If the session id does not exist, it returns ErrSessionNotExist.

func (*Context) HTML

func (c *Context) HTML(code int, htmlfmt string, htmlargs ...interface{}) error

HTML sends an HTML response with the status code.

func (*Context) Header

func (c *Context) Header() http.Header

Header implements the interface http.ResponseWriter, which is the alias of RespHeader.

func (*Context) Host

func (c *Context) Host() string

Host returns the host of the request.

func (*Context) Hostname

func (c *Context) Hostname() string

Hostname returns the hostname of the request.

func (*Context) Inline

func (c *Context) Inline(file string, name string) error

Inline sends a file response as the inline to open the file in the browser.

If the file does not exist, it returns ErrNotFound.

func (*Context) IsAjax

func (c *Context) IsAjax() bool

IsAjax reports whether the request is ajax or not.

func (*Context) IsResponded

func (c *Context) IsResponded() bool

IsResponded reports whether the response is sent or not.

func (*Context) IsTLS

func (c *Context) IsTLS() bool

IsTLS reports whether HTTP connection is TLS or not.

func (*Context) IsWebSocket

func (c *Context) IsWebSocket() bool

IsWebSocket reports whether HTTP connection is WebSocket or not.

func (*Context) JSON

func (c *Context) JSON(code int, v interface{}) (err error)

JSON sends a JSON response with the status code.

func (*Context) Method

func (c *Context) Method() string

Method returns the url method of the request.

func (*Context) MultipartForm

func (c *Context) MultipartForm() (*multipart.Form, error)

MultipartForm returns the multipart form.

func (*Context) MultipartReader

func (c *Context) MultipartReader() (*multipart.Reader, error)

MultipartReader returns the multipart reader from the request.

func (*Context) NoContent

func (c *Context) NoContent(code int) error

NoContent sends a response with the status code and without the body.

func (*Context) Param

func (c *Context) Param(name string) string

Param returns the parameter value in the url path by name.

func (*Context) ParamNames

func (c *Context) ParamNames() []string

ParamNames returns the names of all the URL parameters.

func (*Context) ParamValues

func (c *Context) ParamValues() []string

ParamValues returns the values of all the URL parameters.

func (*Context) Params

func (c *Context) Params() map[string]string

Params returns all the parameters as the key-value map in the url path.

func (*Context) Path

func (c *Context) Path() string

Path returns the url path of the request.

func (*Context) Queries

func (c *Context) Queries() url.Values

Queries returns all the query values.

func (*Context) Query

func (c *Context) Query(name string, defaultValue ...string) string

Query returns the query value by the query name.

Return defaultValue instead if the query name does not exist.

func (*Context) QueryRawString

func (c *Context) QueryRawString() string

QueryRawString returns the URL query string.

func (*Context) Redirect

func (c *Context) Redirect(code int, toURL string) error

Redirect redirects the request to a provided URL with status code.

func (*Context) Referer

func (c *Context) Referer() string

Referer returns the header "Referer" of the request.

func (*Context) RemoteAddr

func (c *Context) RemoteAddr() string

RemoteAddr returns the remote address of the http connection.

func (*Context) Render

func (c *Context) Render(name string, code int, data interface{}) error

Render renders a template named name with data and sends it as the response with status code.

func (*Context) RenderOk

func (c *Context) RenderOk(name string, data interface{}) error

RenderOk is short for c.Render(name, http.StatusOK, data).

func (*Context) ReqHeader

func (c *Context) ReqHeader() http.Header

ReqHeader returns the header of the request.

func (*Context) Request

func (c *Context) Request() *http.Request

Request returns the inner Request.

func (*Context) RequestURI

func (c *Context) RequestURI() string

RequestURI returns the URI of the request.

func (*Context) Reset

func (c *Context) Reset()

Reset resets the context to the initalizing state.

func (*Context) RespHeader

func (c *Context) RespHeader() http.Header

RespHeader returns the header of the response.

func (*Context) Respond

func (c *Context) Respond(args ...interface{}) error

Respond responds the result to the peer by using Ship.Responder.

func (*Context) Response

func (c *Context) Response() *Response

Response returns the inner Response.

func (*Context) ResponseWriter

func (c *Context) ResponseWriter() http.ResponseWriter

ResponseWriter returns the underlying http.ResponseWriter.

func (*Context) Scheme

func (c *Context) Scheme() (scheme string)

Scheme returns the HTTP protocol scheme, `http` or `https`.

func (*Context) SetConnectionClose

func (c *Context) SetConnectionClose()

SetConnectionClose sets the response header "Connection: close" to tell the server to close the connection.

func (*Context) SetContentType

func (c *Context) SetContentType(ct string)

SetContentType sets the response header "Content-Type" to ct,

If ct is "", do nothing.

func (*Context) SetCookie

func (c *Context) SetCookie(cookie *http.Cookie)

SetCookie appends a http cookie to the response header `Set-Cookie`.

func (*Context) SetReqResp

func (c *Context) SetReqResp(r *http.Request, w http.ResponseWriter)

SetReqResp is the same as Reset, but only reset the request and response, not all things.

func (*Context) SetRequest

func (c *Context) SetRequest(req *http.Request)

SetRequest resets the request to req.

func (*Context) SetRespHeader

func (c *Context) SetRespHeader(name, value string)

SetRespHeader sets the response header name to value.

func (*Context) SetResponse

func (c *Context) SetResponse(resp http.ResponseWriter)

SetResponse resets the response to resp, which will ignore nil.

func (*Context) SetSession

func (c *Context) SetSession(id string, value interface{}) (err error)

SetSession sets the session to the backend store.

func (*Context) StatusCode

func (c *Context) StatusCode() int

StatusCode returns the status code of the response.

func (*Context) Stream

func (c *Context) Stream(code int, contentType string, r io.Reader) (err error)

Stream sends a streaming response with the status code and the content type.

func (*Context) Text

func (c *Context) Text(code int, format string, args ...interface{}) error

Text sends a string response with the status code.

func (*Context) URL

func (c *Context) URL(name string, params ...interface{}) string

URL generates a url path by the route path name and provided parameters.

Return "" if there is not the route named name.

func (*Context) UserAgent

func (c *Context) UserAgent() string

UserAgent returns the header "User-Agent" of the request.

func (*Context) Write

func (c *Context) Write(b []byte) (int, error)

Write implements the interface http.ResponseWriter.

It will write the response header with the status code 200 firstly if the header is not sent.

func (*Context) WriteHeader

func (c *Context) WriteHeader(statusCode int)

WriteHeader implements the interface http.ResponseWriter.

func (*Context) XML

func (c *Context) XML(code int, v interface{}) (err error)

XML sends an XML response with the status code.

type Defaulter

type Defaulter interface {
	SetDefault(data interface{}) error
}

Defaulter is used to set the default value if the data or the field of data is ZERO.

func NothingDefaulter

func NothingDefaulter() Defaulter

NothingDefaulter returns a Defaulter that does nothing.

type DefaulterFunc

type DefaulterFunc func(interface{}) error

DefaulterFunc is the function type implementing the interface Defaulter.

func (DefaulterFunc) SetDefault

func (d DefaulterFunc) SetDefault(data interface{}) error

SetDefault implements the interface Defaulter.

type HTTPClientError

type HTTPClientError struct {
	Code   int    `json:"code" xml:"code"`
	Method string `json:"method" xml:"method"`
	URL    string `json:"url" xml:"url"`
	Data   string `json:"data" xml:"data"`
	Err    error  `json:"err" xml:"err"`
}

HTTPClientError represents an response error from the http client.

func NewHTTPClientError

func NewHTTPClientError(method, url string, code int, err error,
	data ...string) HTTPClientError

NewHTTPClientError returns a new HTTPClientError.

func (HTTPClientError) Error

func (e HTTPClientError) Error() string

func (HTTPClientError) String

func (e HTTPClientError) String() string

func (HTTPClientError) Unwrap

func (e HTTPClientError) Unwrap() error

type HTTPServerError

type HTTPServerError struct {
	Code int
	Err  error
	CT   string // Content-Type
}

HTTPServerError represents a server error with HTTP Status Code.

func NewHTTPServerError

func NewHTTPServerError(code int, msg ...string) HTTPServerError

NewHTTPServerError returns a new HTTPServerError.

func (HTTPServerError) Error

func (e HTTPServerError) Error() string

func (HTTPServerError) New

New returns a new HTTPServerError with the new error.

func (HTTPServerError) NewCT

NewCT returns a new HTTPServerError with the new ContentType ct.

func (HTTPServerError) Newf

func (e HTTPServerError) Newf(msg string, args ...interface{}) HTTPServerError

Newf is equal to New(fmt.Errorf(msg, args...)).

func (HTTPServerError) Unwrap

func (e HTTPServerError) Unwrap() error

Unwrap unwraps the inner error.

type Handler

type Handler func(*Context) error

Handler is a handler of the HTTP request.

func FromHTTPHandler

func FromHTTPHandler(h http.Handler) Handler

FromHTTPHandler converts http.Handler to Handler.

func FromHTTPHandlerFunc

func FromHTTPHandlerFunc(h http.HandlerFunc) Handler

FromHTTPHandlerFunc converts http.HandlerFunc to Handler.

func MethodNotAllowedHandler

func MethodNotAllowedHandler(allowedMethods []string) Handler

MethodNotAllowedHandler returns a MethodNotAllowed handler.

func NotFoundHandler

func NotFoundHandler() Handler

NotFoundHandler returns a NotFound handler.

func NothingHandler

func NothingHandler() Handler

NothingHandler returns a Handler doing nothing.

func OkHandler

func OkHandler() Handler

OkHandler returns a Handler only sending the response "200 OK"

func (Handler) HTTPHandler

func (h Handler) HTTPHandler(s *Ship) http.Handler

HTTPHandler converts itself to http.Handler.

type HostHandler

type HostHandler interface {
	http.Handler
	HostManager
}

HostHandler is a http handler to dispatch the request to the host handler by the request host, which may be used to implement the virtual hosts.

type HostManager

type HostManager interface {
	// Len returns the number of the hosts.
	Len() int

	// Range is used to traverse all the hosts.
	Range(func(host string, handler http.Handler))

	// AddHost adds the host and the handler, then returns the added handler.
	//
	// If the host has been added, return the added handler.
	AddHost(host string, handler http.Handler) (http.Handler, error)

	// DelHost deletes the host accurately and returns the handler.
	//
	// If the host does not exist, return nil.
	DelHost(host string) http.Handler

	// GetHost matches the host accurately and returns the handler.
	//
	// If the host does not exist, return nil.
	GetHost(host string) http.Handler

	// MatchHost matches the host by the implemented rules, such as the regular
	// expression, and returns the corresponding handler.
	//
	// If there is no host to match it, return ("", nil).
	MatchHost(host string) (matchedHost string, matchedHandler http.Handler)
}

HostManager is used to manage the domain hosts.

func NewHostManager

func NewHostManager(regexpHostManager HostManager) HostManager

NewHostManager returns a new HostManager implementation, which uses IsDomainName to check whether a host name is the valid domain and supports three kinds of hosts:

  • Exact: a valid domain, such as "www.example.com".
  • Prefix: a valid domain with the suffix ".*", such as "www.example.*".
  • Suffix: a valid domain with the prefix "*.", such as "*.example.com".
  • Regexp: a valid regular expression defined by regexpHostManager.

Notice: if the host name is not any of the exact, prefix and suffix formats, it will be regarded as the regexp host name.

If regexpHostManager is nil, it is NewRegexpHostManager() by default.

func NewLockHostManager

func NewLockHostManager(hm HostManager) HostManager

NewLockHostManager returns a new HostManager based on lock to manage the hosts safely.

func NewRegexpHostManager

func NewRegexpHostManager() HostManager

NewRegexpHostManager returns a new HostManager based on regular expression, which uses the stdlib "regexp" to implement the regular expression syntax.

For the golang regexp syntax, see https://pkg.go.dev/regexp/syntax.

type HostManagerHandler

type HostManagerHandler struct {
	HostManager

	// HandleHTTP is used to handle the matched host and handler.
	//
	// If not found the matched host and handler, matchedHost and matchedHandler
	// are ZERO, that's, "" and nil.
	//
	// Default: w.WriteHeader(404)
	HandleHTTP func(w http.ResponseWriter, r *http.Request,
		matchedHost string, matchedHandler http.Handler)
	// contains filtered or unexported fields
}

HostManagerHandler is an implementation of HostHandler.

func NewHostManagerHandler

func NewHostManagerHandler(hostManager HostManager) *HostManagerHandler

NewHostManagerHandler returns a new HostManagerHandler.

If hostManager is nil, it is NewHostManager(nil) by default.

func (*HostManagerHandler) GetDefaultHost

func (h *HostManagerHandler) GetDefaultHost() (host string, handler http.Handler)

GetDefaultHost returns the default host and handler.

Return ("", nil) if the default host is not set.

func (*HostManagerHandler) ServeHTTP

func (h *HostManagerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the interface http.Handler.

func (*HostManagerHandler) SetDefaultHost

func (h *HostManagerHandler) SetDefaultHost(host string, handler http.Handler)

SetDefaultHost sets the default host and handler.

type Logger

type Logger interface {
	Tracef(format string, args ...interface{})
	Debugf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
}

Logger is logger interface.

Notice: The implementation maybe also has the method { Writer() io.Writer } to get the underlynig writer.

func NewLoggerFromStdlog

func NewLoggerFromStdlog(logger *log.Logger) Logger

NewLoggerFromStdlog converts stdlib log to Logger.

Notice: the returned logger has also implemented the interface { Writer() io.Writer }.

func NewLoggerFromWriter

func NewLoggerFromWriter(w io.Writer, prefix string, flags ...int) Logger

NewLoggerFromWriter returns a new logger by creating a new stdlib log.

Notice: the returned logger has also implemented the interface { Writer() io.Writer }.

type Middleware

type Middleware func(Handler) Handler

Middleware represents a middleware.

func NewMiddleware added in v5.1.4

func NewMiddleware(handle func(http.Handler, http.ResponseWriter, *http.Request) error) Middleware

NewMiddleware returns a common middleware with the handler.

Notice: the wrapped http.Handler has implemented the interface

type interface {
    HandleHTTP(http.ResponseWriter, *http.Request) error
}

So it can be used to wrap the error returned by other middleware handlers.

type MuxBinder

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

MuxBinder is a multiplexer for kinds of Binders based on the request header "Content-Type".

func NewMuxBinder

func NewMuxBinder() *MuxBinder

NewMuxBinder returns a new MuxBinder.

func (*MuxBinder) Add

func (mb *MuxBinder) Add(contentType string, binder Binder)

Add adds a binder to bind the content for the header "Content-Type".

func (*MuxBinder) Bind

func (mb *MuxBinder) Bind(dst interface{}, req *http.Request) error

Bind implements the interface Binder, which looks up the registered binder by the request header "Content-Type" and calls it to bind the value dst to req.

func (*MuxBinder) Del

func (mb *MuxBinder) Del(contentType string)

Del removes the corresponding binder by the header "Content-Type".

func (*MuxBinder) Get

func (mb *MuxBinder) Get(contentType string) Binder

Get returns the corresponding binder by the header "Content-Type".

Return nil if not found.

type MuxRenderer

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

MuxRenderer is a multiplexer for kinds of Renderers.

func NewMuxRenderer

func NewMuxRenderer() *MuxRenderer

NewMuxRenderer returns a new MuxRenderer.

func (*MuxRenderer) Add

func (mr *MuxRenderer) Add(suffix string, renderer Renderer)

Add adds a renderer with a suffix identifier.

func (*MuxRenderer) Del

func (mr *MuxRenderer) Del(suffix string)

Del removes the corresponding renderer by the suffix.

func (*MuxRenderer) Get

func (mr *MuxRenderer) Get(suffix string) Renderer

Get returns the corresponding renderer by the suffix.

Return nil if not found.

func (*MuxRenderer) Render

func (mr *MuxRenderer) Render(w http.ResponseWriter, name string, code int,
	data interface{}) error

Render implements the interface Renderer, which will get the renderer the name suffix then render the content.

type OnceRunner

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

OnceRunner is used to run the task only once, which is different from sync.Once, the second calling does not wait until the first calling finishes.

func NewOnceRunner

func NewOnceRunner(task func()) *OnceRunner

NewOnceRunner returns a new OnceRunner.

func (*OnceRunner) Run

func (r *OnceRunner) Run()

Run runs the task.

type Renderer

type Renderer interface {
	Render(w http.ResponseWriter, name string, code int, data interface{}) error
}

Renderer is the interface to render the response.

type RendererFunc

type RendererFunc func(http.ResponseWriter, string, int, interface{}) error

RendererFunc is the function type implementing the interface Renderer.

func (RendererFunc) Render

func (f RendererFunc) Render(w http.ResponseWriter, name string, code int,
	data interface{}) error

Render implements the interface Renderer.

type Response

type Response struct {
	http.ResponseWriter

	Size   int64
	Wrote  bool
	Status int
}

Response implements the interface http.ResponseWriter.

func GetResponseFromPool

func GetResponseFromPool(w http.ResponseWriter) *Response

GetResponseFromPool returns a Response from the pool.

func NewResponse

func NewResponse(w http.ResponseWriter) *Response

NewResponse returns a new instance of Response.

func (*Response) Flush

func (r *Response) Flush()

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 (*Response) Hijack

func (r *Response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error)

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 (*Response) Push

func (r *Response) Push(target string, opts *http.PushOptions) error

Push implements the http.Pusher interface to support HTTP/2 server push.

See http.Pusher(https://golang.org/pkg/net/http/#Pusher)

func (*Response) Reset

func (r *Response) Reset(w http.ResponseWriter)

Reset resets the response to the initialized status.

func (*Response) SetWriter

func (r *Response) SetWriter(w http.ResponseWriter)

SetWriter resets the writer to w and return itself.

func (*Response) Write

func (r *Response) Write(b []byte) (n int, err error)

Write implements http.ResponseWriter#Writer().

func (*Response) WriteHeader

func (r *Response) WriteHeader(code int)

WriteHeader implements http.ResponseWriter#WriteHeader().

func (*Response) WriteString

func (r *Response) WriteString(s string) (n int, err error)

WriteString implements io.StringWriter.

type Route

type Route struct {
	// Path and Method represent the unique route in a certain host.
	//
	// Path maybe contain the parameters, which is determined by the underlying
	// router. And if Method is empty, it stands for all the methods.
	Path   string `json:"path,omitempty" xml:"path,omitempty"`
	Method string `json:"method,omitempty" xml:"method,omitempty"`

	// Name is the name of the path, which may be empty to represent no name.
	Name string `json:"name,omitempty" xml:"name,omitempty"`

	// Handler is the handler of the route to handle the request.
	Handler Handler `json:"-" xml:"-"`

	// Data is any additional data associated with the route.
	Data interface{} `json:"data,omitempty" xml:"data,omitempty"`
}

Route is used to represent the information of the registered route.

func (Route) String

func (r Route) String() string

type RouteBuilder

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

RouteBuilder is used to build a route.

func (*RouteBuilder) Any

func (r *RouteBuilder) Any(handler Handler) *RouteBuilder

Any registers all the supported methods , which is short for r.Method(handler, "")

func (*RouteBuilder) CONNECT

func (r *RouteBuilder) CONNECT(handler Handler) *RouteBuilder

CONNECT is the short for r.Method(handler, "CONNECT").

func (*RouteBuilder) Clone

func (r *RouteBuilder) Clone() *RouteBuilder

Clone clones a new route builder.

func (*RouteBuilder) DELETE

func (r *RouteBuilder) DELETE(handler Handler) *RouteBuilder

DELETE is the short for r.Method(handler, "DELETE").

func (*RouteBuilder) Data

func (r *RouteBuilder) Data(data interface{}) *RouteBuilder

Data sets the context data.

func (*RouteBuilder) GET

func (r *RouteBuilder) GET(handler Handler) *RouteBuilder

GET is the short for r.Method(handler, "GET").

func (*RouteBuilder) Group

func (r *RouteBuilder) Group() *RouteGroupBuilder

Group returns the route group builder that the current route belongs to, which maybe return nil.

func (*RouteBuilder) HEAD

func (r *RouteBuilder) HEAD(handler Handler) *RouteBuilder

HEAD is the short for r.Method(handler, "HEAD").

func (*RouteBuilder) Map

func (r *RouteBuilder) Map(method2handlers map[string]Handler) *RouteBuilder

Map registers a group of methods with handlers, which is equal to

for method, handler := range method2handlers {
    r.Method(handler, method)
}

func (*RouteBuilder) Method

func (r *RouteBuilder) Method(handler Handler, methods ...string) *RouteBuilder

Method registers the routes with the handler and methods.

It will panic with it if there is an error when adding the routes.

func (*RouteBuilder) Name

func (r *RouteBuilder) Name(name string) *RouteBuilder

Name sets the route name.

func (*RouteBuilder) OPTIONS

func (r *RouteBuilder) OPTIONS(handler Handler) *RouteBuilder

OPTIONS is the short for r.Method(handler, "OPTIONS").

func (*RouteBuilder) PATCH

func (r *RouteBuilder) PATCH(handler Handler) *RouteBuilder

PATCH is the short for r.Method(handler, "PATCH").

func (*RouteBuilder) POST

func (r *RouteBuilder) POST(handler Handler) *RouteBuilder

POST is the short for r.Method(handler, "POST").

func (*RouteBuilder) PUT

func (r *RouteBuilder) PUT(handler Handler) *RouteBuilder

PUT is the short for r.Method(handler, "PUT").

func (*RouteBuilder) Redirect

func (r *RouteBuilder) Redirect(code int, toURL string) *RouteBuilder

Redirect is used to redirect the request to toURL with 301, 302, 307 or 308.

func (*RouteBuilder) Remove

func (r *RouteBuilder) Remove(method string) *RouteBuilder

Remove removes the route.

If the method is "", it will remove all the routes associated with the path.

func (*RouteBuilder) RemoveAny

func (r *RouteBuilder) RemoveAny() *RouteBuilder

RemoveAny is equal to r.Remove("").

func (*RouteBuilder) RemoveCONNECT

func (r *RouteBuilder) RemoveCONNECT() *RouteBuilder

RemoveCONNECT is equal to r.Remove(http.MethodConnect).

func (*RouteBuilder) RemoveDELETE

func (r *RouteBuilder) RemoveDELETE() *RouteBuilder

RemoveDELETE is equal to r.Remove(http.MethodDelete).

func (*RouteBuilder) RemoveGET

func (r *RouteBuilder) RemoveGET() *RouteBuilder

RemoveGET is equal to r.Remove(http.MethodGet).

func (*RouteBuilder) RemoveHEAD

func (r *RouteBuilder) RemoveHEAD() *RouteBuilder

RemoveHEAD is equal to r.Remove(http.MethodHead).

func (*RouteBuilder) RemoveOPTIONS

func (r *RouteBuilder) RemoveOPTIONS() *RouteBuilder

RemoveOPTIONS is equal to r.Remove(http.MethodOptions).

func (*RouteBuilder) RemovePATCH

func (r *RouteBuilder) RemovePATCH() *RouteBuilder

RemovePATCH is equal to r.Remove(http.MethodPatch).

func (*RouteBuilder) RemovePOST

func (r *RouteBuilder) RemovePOST() *RouteBuilder

RemovePOST is equal to r.Remove(http.MethodPost).

func (*RouteBuilder) RemovePUT

func (r *RouteBuilder) RemovePUT() *RouteBuilder

RemovePUT is equal to r.Remove(http.MethodPut).

func (*RouteBuilder) RemoveTRACE

func (r *RouteBuilder) RemoveTRACE() *RouteBuilder

RemoveTRACE is equal to r.Remove(http.MethodTrace).

func (*RouteBuilder) ResetMiddlewares

func (r *RouteBuilder) ResetMiddlewares(ms ...Middleware) *RouteBuilder

ResetMiddlewares resets the middlewares to ms.

func (*RouteBuilder) Routes

func (r *RouteBuilder) Routes(handler Handler, methods ...string) []Route

Routes builds and returns the routes.

func (*RouteBuilder) Ship

func (r *RouteBuilder) Ship() *Ship

Ship returns the ship that the current route is associated with.

func (*RouteBuilder) Static

func (r *RouteBuilder) Static(dirpath string) *RouteBuilder

Static is the same as StaticFS, but listing the files for a directory.

func (*RouteBuilder) StaticFS

func (r *RouteBuilder) StaticFS(fs http.FileSystem) *RouteBuilder

StaticFS registers a route to serve a static filesystem.

func (*RouteBuilder) TRACE

func (r *RouteBuilder) TRACE(handler Handler) *RouteBuilder

TRACE is the short for r.Method(handler, "TRACE").

func (*RouteBuilder) Use

func (r *RouteBuilder) Use(middlewares ...Middleware) *RouteBuilder

Use appends some middlwares.

type RouteError

type RouteError struct {
	Err error
	Route
}

RouteError represents a route error when adding a route.

func (RouteError) Error

func (re RouteError) Error() string

type RouteGroupBuilder

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

RouteGroupBuilder is a route group to build a set of routes.

func (*RouteGroupBuilder) AddRoutes

func (g *RouteGroupBuilder) AddRoutes(routes ...Route) *RouteGroupBuilder

AddRoutes registers a set of the routes.

It will panic with it if there is an error when adding the routes.

func (*RouteGroupBuilder) Clone

Clone clones itself and returns a new one.

func (*RouteGroupBuilder) Data

func (g *RouteGroupBuilder) Data(data interface{}) *RouteGroupBuilder

Data sets the context data.

func (*RouteGroupBuilder) DelRoutes

func (g *RouteGroupBuilder) DelRoutes(routes ...Route) *RouteGroupBuilder

DelRoutes deletes a set of the registered routes.

It will panic with it if there is an error when deleting the routes.

func (*RouteGroupBuilder) Group

func (g *RouteGroupBuilder) Group(prefix string, middlewares ...Middleware) *RouteGroupBuilder

Group returns a new route sub-group.

If the prefix does not start with "/", it will add "/" as the prefix.

func (*RouteGroupBuilder) ResetMiddlewares

func (g *RouteGroupBuilder) ResetMiddlewares(ms ...Middleware) *RouteGroupBuilder

ResetMiddlewares resets the middlewares of the group to ms.

func (*RouteGroupBuilder) Route

func (g *RouteGroupBuilder) Route(path string) *RouteBuilder

Route returns a new route, which is used to build and register the route.

You should call Method() or its short method to register it.

func (*RouteGroupBuilder) Ship

func (g *RouteGroupBuilder) Ship() *Ship

Ship returns the ship that the current group belongs to.

func (*RouteGroupBuilder) Use

func (g *RouteGroupBuilder) Use(middlewares ...Middleware) *RouteGroupBuilder

Use appends some middlwares into the group.

type Router

type Router = router.Router

Router is the alias of router.Router.

type Runner

type Runner struct {
	Name      string
	Logger    Logger
	Server    *http.Server
	Signals   []os.Signal
	ConnState func(net.Conn, http.ConnState)
	// contains filtered or unexported fields
}

Runner is a HTTP Server runner.

func NewRunner

func NewRunner(handler http.Handler) *Runner

NewRunner returns a new Runner.

If the handler has implemented the interface { GetName() string }, it will set the name to handler.GetName().

If the handler has implemented the interface { GetLogger() Logger }, it will set the logger to handler.GetLogger().

func (r *Runner) Link(other *Runner)

Link registers the shutdown function between itself and other.

func (*Runner) RegisterOnShutdown

func (r *Runner) RegisterOnShutdown(functions ...func())

RegisterOnShutdown registers some shutdown functions to run when the http server is shut down.

func (*Runner) SetLogger

func (r *Runner) SetLogger(logger Logger) *Runner

SetLogger sets the logger to logger and returns itself.

func (*Runner) SetName

func (r *Runner) SetName(name string) *Runner

SetName sets the name to name and returns itself.

func (*Runner) Shutdown

func (r *Runner) Shutdown(ctx context.Context) (err error)

Shutdown stops the HTTP server.

func (*Runner) Start

func (r *Runner) Start(addr string, tlsFiles ...string)

Start starts a HTTP server with addr until it is closed.

If tlsFiles is not nil, it must be certFile and keyFile. For example,

runner := NewRunner()
runner.Start(":80", certFile, keyFile)

func (*Runner) Stop

func (r *Runner) Stop()

Stop is the same as r.Shutdown(context.Background()).

type Session

type Session interface {
	// If the session id does not exist, it should return (nil, nil).
	GetSession(id string) (value interface{}, err error)
	SetSession(id string, value interface{}) error
	DelSession(id string) error
}

Session represents an interface about the session.

func NewMemorySession

func NewMemorySession() Session

NewMemorySession return a Session implementation based on the memory.

type Ship

type Ship struct {
	// Name is the name of the ship.
	//
	// Default: ""
	Name string

	// Prefix is the default prefix of the paths of all the routes.
	//
	// Default: ""
	Prefix string

	// The initialization capacity of Context.Data.
	//
	// Default: 0
	CtxDataInitCap int

	// The maximum number of the url paramters of the route.
	//
	// Default: 4
	URLParamMaxNum int

	// The maximum number of the middlewares.
	//
	// Default: 256
	MiddlewareMaxNum int

	// Router is the route manager to manage all the routes.
	//
	// Default: echo.NewRouter(&echo.Config{RemoveTrailingSlash: true})
	Router Router

	// The default handler when not finding the route.
	//
	// Default: NotFoundHandler()
	NotFound Handler

	// Filter the route if returning true when registering and unregistering it.
	//
	// Default: nil
	RouteFilter func(Route) bool

	// Modify the route before registering and unregistering it.
	//
	// Default: nil
	RouteModifier func(Route) Route

	// HandleError is used to handle the error at last
	// if the handler or middleware returns an error.
	//
	// Default: respond the error to the client if not responding.
	HandleError func(c *Context, err error)

	// Context Settings.
	Session   Session                                     // Default: NewMemorySession()
	Logger    Logger                                      // Default: NewLoggerFromWriter(os.Stderr, "")
	Binder    Binder                                      // Default: nil
	Renderer  Renderer                                    // Default: nil
	Validator Validator                                   // Default: nil
	Defaulter Defaulter                                   // Default: SetStructFieldToDefault
	BindQuery func(dst interface{}, src url.Values) error // Default: BindURLValues(dst, src, "query")
	Responder func(c *Context, args ...interface{}) error // Default: nil
	// contains filtered or unexported fields
}

Ship is an app to be used to manage the router.

func Default

func Default() *Ship

Default returns a new ship with MuxBinder and MuxRenderer as the binder and renderer.

func New

func New() *Ship

New returns a new Ship.

func (*Ship) AcquireBuffer

func (s *Ship) AcquireBuffer() *bytes.Buffer

AcquireBuffer gets a Buffer from the pool.

func (*Ship) AcquireContext

func (s *Ship) AcquireContext(r *http.Request, w http.ResponseWriter) *Context

AcquireContext gets a Context from the pool.

func (*Ship) AddRoute

func (s *Ship) AddRoute(r Route) (err error)

AddRoute registers the route.

func (*Ship) AddRoutes

func (s *Ship) AddRoutes(routes ...Route)

AddRoutes registers a set of the routes.

It will panic with it if there is an error when adding the routes.

func (*Ship) Clone

func (s *Ship) Clone(name string, router Router) *Ship

Clone clones itself to a new one with the new name and the new router.

If router is nil, create a new default one automatically.

func (*Ship) DelRoute

func (s *Ship) DelRoute(r Route) (err error)

DelRoute deletes the registered route, which only uses "Path" and "Method", and others are ignored.

If Method is empty, deletes all the routes associated with the path.

If the route does not exist, do nothing and return nil.

func (*Ship) DelRoutes

func (s *Ship) DelRoutes(routes ...Route)

DelRoutes deletes a set of the registered routes.

It will panic with it if there is an error when deleting the routes.

func (*Ship) GetLogger

func (s *Ship) GetLogger() Logger

GetLogger returns the logger of the ship router.

func (*Ship) GetName

func (s *Ship) GetName() string

GetName returns the name of the ship router.

func (*Ship) Group

func (s *Ship) Group(prefix string) *RouteGroupBuilder

Group returns a new route sub-group with the group prefix.

If the prefix does not start with "/", it will add "/" as the prefix.

func (*Ship) HandleRequest

func (s *Ship) HandleRequest(c *Context) error

HandleRequest is the same as ServeHTTP, but handles the request with the Context.

func (*Ship) NewContext

func (s *Ship) NewContext() *Context

NewContext news a Context.

func (*Ship) Pre

func (s *Ship) Pre(middlewares ...Middleware)

Pre registers the pre-middlewares, which are executed before finding the route.

func (*Ship) ReleaseBuffer

func (s *Ship) ReleaseBuffer(buf *bytes.Buffer)

ReleaseBuffer puts a Buffer into the pool.

func (*Ship) ReleaseContext

func (s *Ship) ReleaseContext(c *Context)

ReleaseContext puts a Context into the pool.

func (*Ship) ResetMiddlewares

func (s *Ship) ResetMiddlewares(middlewares ...Middleware)

ResetMiddlewares resets the global middlewares to mdws.

func (*Ship) ResetPreMiddlewares

func (s *Ship) ResetPreMiddlewares(middlewares ...Middleware)

ResetPreMiddlewares resets the global pre-middlewares to mdws.

func (*Ship) Route

func (s *Ship) Route(path string) *RouteBuilder

Route returns a new route builder.

func (*Ship) Routes

func (s *Ship) Routes() (routes []Route)

Routes returns the information of all the routes.

func (*Ship) ServeHTTP

func (s *Ship) ServeHTTP(resp http.ResponseWriter, req *http.Request)

ServeHTTP implements the interface http.Handler.

func (*Ship) SetBufferSize

func (s *Ship) SetBufferSize(size int)

SetBufferSize resets the size of the buffer. The default is 2048.

func (*Ship) Use

func (s *Ship) Use(middlewares ...Middleware)

Use registers the global middlewares, which must be registered before adding the routes using these middlewares.

type Validator

type Validator interface {
	Validate(data interface{}) error
}

Validator is used to validate the data is valid or not.

func NothingValidator

func NothingValidator() Validator

NothingValidator returns a Validator that does nothing.

type ValidatorFunc

type ValidatorFunc func(data interface{}) error

ValidatorFunc is a function type implementing the interface Validator.

func (ValidatorFunc) Validate

func (v ValidatorFunc) Validate(data interface{}) error

Validate implements the interface Validator.

Directories

Path Synopsis
Package middleware is the collection of the middlewares.
Package middleware is the collection of the middlewares.
render
Package router supplies a router interface ane some implementations.
Package router supplies a router interface ane some implementations.
echo
Package echo supplies a customized Router implementation by referring to github.com/labstack/echo.
Package echo supplies a customized Router implementation by referring to github.com/labstack/echo.

Jump to

Keyboard shortcuts

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