hermes

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Feb 7, 2020 License: MIT Imports: 16 Imported by: 5

README

CircleCI codecov GoDoc Go Report Card

Hermes

This is one more web development framework for GO using the fasthttp as base. Or, maybe, it is just a set of utility of helpful functions that apply some sugar to the fasthttp "daily" usage.

Extra Features

Routing

Routable is described as an interface which have the Delete, Get, Head, Options, Patch, Post, Put methods for routing. This aims to replace the fasthttprouter implementation.

In order to get things going, the below example shows how to define an endpoint serving a GET method:

router := hermes.DefaultRouter()

router.Get("/api/v1/user", func(req hermes.Request, res hermes.Response) hermes.Result {
	res.Data("the user is: Snake Eyes")
})
Grouping

When dealing with routes, groups are awesome!

router := hermes.DefaultRouter()

apiGroup := router.Prefix("/api") // This retuns a `Routable` that can be used
                                  // to create other subgroups or define routes.

apiv1 := apiGroup.Prefix("/v1")   // This is what we define as a subgroup.
apiv1.Get(                        // Now a definition of the route itself.
	"/user",
	func(req hermes.Request, res hermes.Response) hermes.Result {
		return res.Data("the user is: Snake Eyes")
	},
)
// many other routes using `apiv1` ...

There is no difference from using, or not, grouping into the routes definition. Internally, the implementation ends up joining all the routes of the group with the group prefix. Hence, groups will not affect performance.

Middlewares

In order to provide a more flexible API, Middleware supports were added.

Middlewares can implement some verification or extension logic and decide whether or not continue to run the "next" middleware/handler.

router := hermes.DefaultRouter()

apiGroup := router.Prefix("/api")

apiv1 := apiGroup.Prefix("/v1")

apiv1.Use(func(req hermes.Request, res hermes.Response, next hermes.Handler) hermes.Result {
	// This is a middleware that could do something smart...
	return next(req, res)
})

apiv1.Get(
	"/user",
	func(req hermes.Request, res hermes.Response) hermes.Result {
		return res.Data("the user is: Snake Eyes")
	},
)

Yet, you can also define multiple middlewares for each route and their priority will be from the left to the right.

router := hermes.DefaultRouter()

apiGroup := router.Prefix("/api")
apiv1 := apiGroup.Prefix("/v1")
apiv1.With(
	func(req hermes.Request, res hermes.Response, next hermes.Handler) hermes.Result {
		// This is a middleware that could do something smart...
		return next(ctx)
	},
	func(req hermes.Request, res hermes.Response, next hermes.Handler) hermes.Result {
		// This is a second middleware for the endpoint...
		return next(ctx)
	},
).Get(
	"/user",
	func(req hermes.Request, res hermes.Response) hermes.Result {
		return res.Data("the user is: Snake Eyes")
	},
)

Middlewares are also supported on groups:

router := hermes.DefaultRouter()

apiGroup := router.Prefix("/api").With(func(req hermes.Request, res hermes.Resonse, next hermes.Handler) hermes.Result {
	// This is a middleware that could do something smart...
	return next(ctx)
})
apiv1 := apiGroup.Prefix("/v1").With(func(req hermes.Request, res hermes.Resonse, next hermes.Handler) hermes.Result {
	// Yet another middleware applied just for this subgroup...
	return next(ctx)
})
apiv1.With(func(req hermes.Request, res hermes.Resonse, next hermes.Handler) hermes.Result {
	// This is a middleware that is applied just for this endpoint
	return next(ctx)
}).Get(
	"/user",
	func(req hermes.Request, res hermes.Resonse) hermes.Result {
		return res.Data("the user is: Snake Eyes")
	},
)

Again, there is no performance difference when using middlewares in a specific route or in a whole group. The internal implementation will append both middleware definitions into one big sequence of middlewares for each route.

Thin JSON layer

For simple sake of ease the use of sending and receiving JSON objects res.Data and req.Data methods were added.

Sending a JSON

The following is an example of sending a JSON document:

router := hermes.DefaultRouter()

apiv1 := router.Prefix("/api/v1")
apiv1.Get(
	"/user",
	func(req hermes.Request, res hermes.Response) hermes.Result {
		return res.Data(map[string]interface{}{
			"name": "Snake Eyes",
			"email": "s.eyes@gijoe.com",
		})
	},
)
Receiving a JSON

The following is an example of receiving a JSON document:

router := hermes.DefaultRouter()

apiv1 := router.Group("/api/v1")
apiv1.Post(
	"/user",
	func(req hermes.Request, res hermes.Response) hermes.Result {
		user := make(map[string]interface{})
		if err := req.Data(&user); err != nil {
			return res.Status(400).Data(map[string]interface{}{
				"error": "user data invalid"
			})
		}
		// To process the user information
	},
)

fasthttprouter

buaazp/fasthttprouter forks julienschmidt/httprouter adding support for the valyala/fasthttp.

The implementation is very efficient. However, sometimes we could not find a way to place our routes the exact way we wanted to. In order to solve this problem, we implemented our own version (unfortunately less effective).

Documentation

Index

Constants

View Source
const (
	StatusContinue           = 100 // RFC 7231, 6.2.1
	StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
	StatusProcessing         = 102 // RFC 2518, 10.1

	StatusOK                   = 200 // RFC 7231, 6.3.1
	StatusCreated              = 201 // RFC 7231, 6.3.2
	StatusAccepted             = 202 // RFC 7231, 6.3.3
	StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
	StatusNoContent            = 204 // RFC 7231, 6.3.5
	StatusResetContent         = 205 // RFC 7231, 6.3.6
	StatusPartialContent       = 206 // RFC 7233, 4.1
	StatusMultiStatus          = 207 // RFC 4918, 11.1
	StatusAlreadyReported      = 208 // RFC 5842, 7.1
	StatusIMUsed               = 226 // RFC 3229, 10.4.1

	StatusMultipleChoices  = 300 // RFC 7231, 6.4.1
	StatusMovedPermanently = 301 // RFC 7231, 6.4.2
	StatusFound            = 302 // RFC 7231, 6.4.3
	StatusSeeOther         = 303 // RFC 7231, 6.4.4
	StatusNotModified      = 304 // RFC 7232, 4.1
	StatusUseProxy         = 305 // RFC 7231, 6.4.5

	StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
	StatusPermanentRedirect = 308 // RFC 7538, 3

	StatusBadRequest                   = 400 // RFC 7231, 6.5.1
	StatusUnauthorized                 = 401 // RFC 7235, 3.1
	StatusPaymentRequired              = 402 // RFC 7231, 6.5.2
	StatusForbidden                    = 403 // RFC 7231, 6.5.3
	StatusNotFound                     = 404 // RFC 7231, 6.5.4
	StatusMethodNotAllowed             = 405 // RFC 7231, 6.5.5
	StatusNotAcceptable                = 406 // RFC 7231, 6.5.6
	StatusProxyAuthRequired            = 407 // RFC 7235, 3.2
	StatusRequestTimeout               = 408 // RFC 7231, 6.5.7
	StatusConflict                     = 409 // RFC 7231, 6.5.8
	StatusGone                         = 410 // RFC 7231, 6.5.9
	StatusLengthRequired               = 411 // RFC 7231, 6.5.10
	StatusPreconditionFailed           = 412 // RFC 7232, 4.2
	StatusRequestEntityTooLarge        = 413 // RFC 7231, 6.5.11
	StatusRequestURITooLong            = 414 // RFC 7231, 6.5.12
	StatusUnsupportedMediaType         = 415 // RFC 7231, 6.5.13
	StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
	StatusExpectationFailed            = 417 // RFC 7231, 6.5.14
	StatusTeapot                       = 418 // RFC 7168, 2.3.3
	StatusUnprocessableEntity          = 422 // RFC 4918, 11.2
	StatusLocked                       = 423 // RFC 4918, 11.3
	StatusFailedDependency             = 424 // RFC 4918, 11.4
	StatusUpgradeRequired              = 426 // RFC 7231, 6.5.15
	StatusPreconditionRequired         = 428 // RFC 6585, 3
	StatusTooManyRequests              = 429 // RFC 6585, 4
	StatusRequestHeaderFieldsTooLarge  = 431 // RFC 6585, 5
	StatusUnavailableForLegalReasons   = 451 // RFC 7725, 3

	StatusInternalServerError           = 500 // RFC 7231, 6.6.1
	StatusNotImplemented                = 501 // RFC 7231, 6.6.2
	StatusBadGateway                    = 502 // RFC 7231, 6.6.3
	StatusServiceUnavailable            = 503 // RFC 7231, 6.6.4
	StatusGatewayTimeout                = 504 // RFC 7231, 6.6.5
	StatusHTTPVersionNotSupported       = 505 // RFC 7231, 6.6.6
	StatusVariantAlsoNegotiates         = 506 // RFC 2295, 8.1
	StatusInsufficientStorage           = 507 // RFC 4918, 11.5
	StatusLoopDetected                  = 508 // RFC 5842, 7.2
	StatusNotExtended                   = 510 // RFC 2774, 7
	StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)

HTTP status codes were stolen from valyala/fasthttp.

Variables

View Source
var (
	InternalServerErrorCode    = "internal-server-error"
	InternalServerErrorMessage = "We encountered an internal error or misconfiguration and was unable to complete your request."

	NotFoundErrorCode    = "not-found"
	NotFoundErrorMessage = "We could not find the resource you requested."

	MethodNotAllowedErrorCode    = "method-not-allowed"
	MethodNotAllowedErrorMessage = "We believe that the used request method is inappropriate for the resource you requested."
)

Functions

func ReleaseRequest added in v1.3.0

func ReleaseRequest(req *BaseRequest)

func ReleaseResponse added in v1.3.0

func ReleaseResponse(res *BaseResponse)

func StatusMessage added in v1.1.0

func StatusMessage(statusCode int) string

StatusMessage returns HTTP status message for the given status code.

Types

type Application added in v1.0.0

type Application struct {
	Configuration ApplicationConfig
	// contains filtered or unexported fields
}

func NewApplication added in v1.0.0

func NewApplication(config ApplicationConfig, router Router) *Application

func (*Application) Name added in v1.0.0

func (app *Application) Name() string

func (*Application) Restart added in v1.0.0

func (app *Application) Restart() error

func (*Application) Start added in v1.0.0

func (app *Application) Start() error

func (*Application) Stop added in v1.0.0

func (app *Application) Stop() error

type ApplicationConfig added in v1.0.0

type ApplicationConfig struct {
	Name           string
	ServiceStarter rscsrv.ServiceStarter
	HTTP           FasthttpServiceConfiguration
}

type BaseRequest added in v1.3.0

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

func AcquireRequest added in v1.3.0

func AcquireRequest(ctx context.Context, r *fasthttp.RequestCtx) *BaseRequest

func (*BaseRequest) Context added in v1.3.0

func (req *BaseRequest) Context() context.Context

func (*BaseRequest) Cookie added in v1.3.0

func (req *BaseRequest) Cookie(name string) []byte

func (*BaseRequest) Data added in v1.3.0

func (req *BaseRequest) Data(dst interface{}) error

func (*BaseRequest) Header added in v1.3.0

func (req *BaseRequest) Header(name string) []byte

func (*BaseRequest) Host added in v1.3.0

func (req *BaseRequest) Host() []byte

func (*BaseRequest) IsJSON added in v1.3.0

func (req *BaseRequest) IsJSON() bool

func (*BaseRequest) Method added in v1.3.0

func (req *BaseRequest) Method() []byte

func (*BaseRequest) Param added in v1.3.0

func (req *BaseRequest) Param(name string) string

func (*BaseRequest) Path added in v1.3.0

func (req *BaseRequest) Path() []byte

func (*BaseRequest) Post added in v1.3.0

func (req *BaseRequest) Post(name string) []byte

func (*BaseRequest) PostMulti added in v1.3.0

func (req *BaseRequest) PostMulti(name string) [][]byte

func (*BaseRequest) Query added in v1.3.0

func (req *BaseRequest) Query(name string) []byte

func (*BaseRequest) QueryMulti added in v1.3.0

func (req *BaseRequest) QueryMulti(name string) [][]byte

func (*BaseRequest) Raw added in v1.3.0

func (req *BaseRequest) Raw() *fasthttp.RequestCtx

func (*BaseRequest) URI added in v1.3.0

func (req *BaseRequest) URI() *fasthttp.URI

func (*BaseRequest) WantsJSON added in v1.3.0

func (req *BaseRequest) WantsJSON() bool

func (*BaseRequest) WithContext added in v1.3.0

func (req *BaseRequest) WithContext(ctx context.Context) Request

type BaseResponse added in v1.3.0

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

func AcquireResponse added in v1.3.0

func AcquireResponse(r *fasthttp.RequestCtx) *BaseResponse

func (*BaseResponse) Cookie added in v1.3.0

func (res *BaseResponse) Cookie(cookie *fasthttp.Cookie) Response

func (*BaseResponse) Data added in v1.3.0

func (res *BaseResponse) Data(data interface{}) Result

func (*BaseResponse) End added in v1.3.0

func (res *BaseResponse) End() Result

func (*BaseResponse) Error added in v1.3.0

func (res *BaseResponse) Error(err error, options ...interface{}) Result

func (*BaseResponse) File added in v1.3.0

func (res *BaseResponse) File(filepath string) Result

func (*BaseResponse) FileDownload added in v1.3.0

func (res *BaseResponse) FileDownload(filepath, filename string) Result

func (*BaseResponse) Header added in v1.3.0

func (res *BaseResponse) Header(name, value string) Response

func (*BaseResponse) Redirect added in v1.3.0

func (res *BaseResponse) Redirect(uri string, code int) Result

func (*BaseResponse) Status added in v1.3.0

func (res *BaseResponse) Status(status int) Response

type FasthttpService

type FasthttpService struct {
	Configuration FasthttpServiceConfiguration
	Server        fasthttp.Server
	// contains filtered or unexported fields
}

FasthttpService implements the server for starting

func (*FasthttpService) ApplyConfiguration

func (service *FasthttpService) ApplyConfiguration(configuration interface{}) error

ApplyConfiguration checks if the passing interface is a `FasthttpServiceConfiguration` and applies its configuration to the service.

func (*FasthttpService) LoadConfiguration

func (service *FasthttpService) LoadConfiguration() (interface{}, error)

LoadConfiguration does not do anything in this implementation. This methods is just a placeholder to be overwritten on its usage.

func (*FasthttpService) Restart

func (service *FasthttpService) Restart() error

Restart returns an error due to fasthttp not being able to stop the service.

func (*FasthttpService) Start

func (service *FasthttpService) Start() error

Start ListenAndServe the server. This method is blocking because it uses the fasthttp.ListenAndServe implementation.

func (*FasthttpService) Stop

func (service *FasthttpService) Stop() error

Stop closes the listener and waits the `Start` to stop.

type FasthttpServiceConfiguration

type FasthttpServiceConfiguration struct {
	Bind string
	TLS  *FasthttpServiceConfigurationTLS
}

FasthttpServiceConfiguration keeps all the configuration needed to start the `FasthttpService`.

type FasthttpServiceConfigurationTLS added in v1.1.0

type FasthttpServiceConfigurationTLS struct {
	CertFile string
	KeyFile  string
}

FasthttpServiceConfigurationTLS keeps the configuration for starting a TLS server.

type Handler

type Handler func(req Request, res Response) Result

type Middleware

type Middleware func(req Request, res Response, next Handler) Result

Middleware is an interface for adding middleware to a Router instance

type QueryString added in v1.1.0

type QueryString interface {
	String(string, ...string) string
	Int(string, ...int) int
	Int64(string, ...int64) int64
	Float(string, ...float64) float64
	Bool(string, ...bool) bool
}

func ParseQuery added in v1.1.0

func ParseQuery(req Request) QueryString

type Request added in v1.0.0

type Request interface {
	// Path returns the path of the current URL
	Path() []byte

	// Method returns the HTTP method
	Method() []byte

	// IsJSON return weather request body is application/json
	IsJSON() bool

	// WantsJSON return weather request accepts application/json
	WantsJSON() bool

	// URI returns the raw URI
	URI() *fasthttp.URI

	// Header return a header value by name. If the header is not found
	// an empty string will be returned.
	Header(name string) []byte

	// Host returns the host of the request.
	Host() []byte

	// Param grabs route param by name
	Param(name string) string

	// Query grabs input from the query string by name
	Query(name string) []byte

	// QueryMulti grabs multiple input from the query string by name
	QueryMulti(name string) [][]byte

	// Data unmarshals request body to dst
	Data(dst interface{}) error

	// Post grabs input from the post data by name
	Post(name string) []byte

	// PostMulti grabs multiple input from the post data by name
	PostMulti(name string) [][]byte

	// Cookie grabs input from cookies by name
	Cookie(name string) []byte

	// Context returns the context.Context of the current request
	Context() context.Context

	// WithContext returns a shallow copy of the request with a new context
	WithContext(ctx context.Context) Request

	// Raw returns the fasthttp.RequestCtx of the current request
	Raw() *fasthttp.RequestCtx
}

Request is used to retrieve data from an HTTP request

type Response added in v1.0.0

type Response interface {
	// Cookie sets an HTTP cookie on the response
	// See also `fasthttp.AcquireCookie`
	Cookie(cookie *fasthttp.Cookie) Response

	// Status sets the HTTP status code of the response. This can only be called once.
	Status(status int) Response

	// Header adds an HTTP header to the response
	Header(name, value string) Response

	// Data responds with data provided
	//
	// Most types will converted to a string representation except structs,
	// arrays and maps which will be serialized to JSON.
	Data(data interface{}) Result

	// Error sends the default 500 response
	Error(error, ...interface{}) Result

	File(filepath string) Result

	FileDownload(filepath, filename string) Result

	// Redirect redirects the client to a URL
	Redirect(uri string, code int) Result

	// End ends the response chain
	End() Result
}

Response is used to send data to the client

type Result added in v1.0.0

type Result interface {
	Data(data interface{}) Result
	Error(error) Result
	Redirect(uri string, code int) Result
	File(filepath string) Result
	FileDownload(filepath, filename string) Result
	// End release the resources
	End()
}

Result is used to finish a request

type Routable

type Routable interface {
	Delete(path string, handler Handler)
	Get(path string, handler Handler)
	Head(path string, handler Handler)
	Options(path string, handler Handler)
	Patch(path string, handler Handler)
	Post(path string, handler Handler)
	Put(path string, handler Handler)

	Prefix(path string) Routable
	Group(func(Routable))

	Use(...Middleware)
	With(...Middleware) Routable
}

type Router

type Router interface {
	Routable

	Handler() fasthttp.RequestHandler
}

func DefaultRouter added in v1.0.0

func DefaultRouter() Router

func NewRouter

func NewRouter(config RouterConfig) Router

type RouterConfig added in v1.0.0

type RouterConfig struct {
	NotFound         Handler
	MethodNotAllowed Handler
}

type Service

type Service interface {
	rscsrv.Service
	rscsrv.StartableWithContext
}

Service TODO

func NewService added in v1.4.0

func NewService(config ServiceConfig) Service

NewService TODO

type ServiceConfig added in v1.4.0

type ServiceConfig struct {
	Name   string
	Router Router

	Bind string
	TLS  *FasthttpServiceConfigurationTLS
}

ServiceConfig TODO

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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