rye

package module
v1.0.8 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2018 License: MIT Imports: 15 Imported by: 10

README

LICENSE Golang Godocs Go Report Card Travis Build Status codecov

rye

A simple library to support http services. Currently, rye provides a middleware handler which can be used to chain http handlers together while providing statsd metrics for use with DataDog or other logging aggregators. In addition, rye comes with various pre-built middleware handlers for enabling functionality such as CORS and rate/CIDR limiting.

Setup

In order to use rye, you should vendor it and the statsd client within your project.

govendor fetch github.com/InVisionApp/rye
govendor fetch github.com/cactus/go-statsd-client/statsd

Why another middleware lib?

  • rye is tiny - the core lib is ~143 lines of code (including comments)!
  • Each middleware gets statsd metrics tracking for free including an overall error counter
  • We wanted to have an easy way to say “run these two middlewares on this endpoint, but only one middleware on this endpoint”
    • Of course, this is doable with negroni and gorilla-mux, but you’d have to use a subrouter with gorilla, which tends to end up in more code
  • Bundled helper methods for standardising JSON response messages
  • Unified way for handlers and middlewares to return more detailed responses via the rye.Response struct (if they chose to do so).
  • Pre-built middlewares for things like CORS support

Example

You can run an example locally to give it a try. The code for the example is here!

cd example
go run rye_example.go

Writing custom middleware handlers

Begin by importing the required libraries:

import (
    "github.com/cactus/go-statsd-client/statsd"
    "github.com/InVisionApp/rye"
)

Create a statsd client (if desired) and create a rye Config in order to pass in optional dependencies:

config := &rye.Config{
    Statter:  statsdClient,
    StatRate: DEFAULT_STATSD_RATE,
}

Create a middleware handler. The purpose of the Handler is to keep Config and to provide an interface for chaining http handlers.

middlewareHandler := rye.NewMWHandler(config)

Set up any global handlers by using the Use() method. Global handlers get pre-pended to the list of your handlers for EVERY endpoint. They are bound to the MWHandler struct. Therefore, you could set up multiple MWHandler structs if you want to have different collections of global handlers.

middlewareHandler.Use(middleware_routelogger)

Build your http handlers using the Handler type from the rye package.

type Handler func(w http.ResponseWriter, r *http.Request) *rye.Response

Here are some example (custom) handlers:

func homeHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
    fmt.Fprint(rw, "Refer to README.md for auth-api API usage")
    return nil
}

func middlewareFirstHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
    fmt.Fprint(rw, "This handler fires first.")
    return nil
}

func errorHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
    return &rye.Response{
        StatusCode: http.StatusInternalServerError,
        Err:        errors.New(message),
    }
}

Finally, to setup your handlers in your API (Example shown using Gorilla):

routes := mux.NewRouter().StrictSlash(true)

routes.Handle("/", middlewareHandler.Handle([]rye.Handler{
    a.middlewareFirstHandler,
    a.homeHandler,
})).Methods("GET")

log.Infof("API server listening on %v", ListenAddress)

srv := &http.Server{
    Addr:    ListenAddress,
    Handler: routes,
}

srv.ListenAndServe()

Statsd Generated by Rye

Rye comes with built-in configurable statsd statistics that you could record to your favorite monitoring system. To configure that, you'll need to set up a Statter based on the github.com/cactus/go-statsd-client and set it in your instantiation of MWHandler through the rye.Config.

When a middleware is called, it's timing is recorded and a counter is recorded associated directly with the http status code returned during the call. Additionally, an errors counter is also sent to the statter which allows you to count any errors that occur with a code equaling or above 500.

Example: If you have a middleware handler you've created with a method named loginHandler, successful calls to that will be recorded to handlers.loginHandler.2xx. Additionally you'll receive stats such as handlers.loginHandler.400 or handlers.loginHandler.500. You also will receive an increase in the errors count.

If you're sending your logs into a system such as DataDog, be aware that your stats from Rye can have prefixes such as statsd.my-service.my-k8s-cluster.handlers.loginHandler.2xx or even statsd.my-service.my-k8s-cluster.errors. Just keep in mind your stats could end up in the destination sink system with prefixes.

Using with Golang 1.7 Context

With Golang 1.7, a new feature has been added that supports a request specific context. This is a great feature that Rye supports out-of-the-box. The tricky part of this is how the context is modified on the request. In Golang, the Context is always available on a Request through http.Request.Context(). Great! However, if you want to add key/value pairs to the context, you will have to add the context to the request before it gets passed to the next Middleware. To support this, the rye.Response has a property called Context. This property takes a properly created context (pulled from the request.Context() function. When you return a rye.Response which has Context, the rye library will craft a new Request and make sure that the next middleware receives that request.

Here's the details of creating a middleware with a proper Context. You must first pull from the current request Context. In the example below, you see ctx := r.Context(). That pulls the current context. Then, you create a NEW context with your additional context key/value. Finally, you return &rye.Response{Context:ctx}

func addContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
    // Retrieve the request's context
    ctx := r.Context()

    // Create a NEW context
    ctx = context.WithValue(ctx,"CONTEXT_KEY","my context value")

    // Return that in the Rye response 
    // Rye will add it to the Request to 
    // pass to the next middleware
    return &rye.Response{Context:ctx}
}

Now in a later middleware, you can easily retrieve the value you set!

func getContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
    // Retrieving the value is easy!
    myVal := r.Context().Value("CONTEXT_KEY")

    // Log it to the server log?
    log.Infof("Context Value: %v", myVal)

    return nil
}

For another simple example, look in the JWT middleware - it adds the JWT into the context for use by other middlewares. It uses the CONTEXT_JWT key to push the JWT token into the Context.

Using built-in middleware handlers

Rye comes with various pre-built middleware handlers. Pre-built middlewares source (and docs) can be found in the package dir following the pattern middleware_*.go.

To use them, specify the constructor of the middleware as one of the middleware handlers when you define your routes:

// example
routes.Handle("/", middlewareHandler.Handle([]rye.Handler{
    rye.MiddlewareCORS(), // to use the CORS middleware (with defaults)
    a.homeHandler,
})).Methods("GET")

OR 

routes.Handle("/", middlewareHandler.Handle([]rye.Handler{
    rye.NewMiddlewareCORS("*", "GET, POST", "X-Access-Token"), // to use specific config when instantiating the middleware handler
    a.homeHandler,
})).Methods("GET")

Serving Static Files

Rye has the ability to add serving static files in the chain. Two handlers have been provided: StaticFilesystem and StaticFile. These middlewares should always be used at the end of the chain. Their configuration is simply based on an absolute path on the server and possibly a skipped path prefix.

The use case here could be a powerful one. Rye allows you to serve a filesystem just as a whole or a single file. Used together you could facilitate an application which does both -> fulfilling the capability to provide a single page application. For example, if you had a webpack application which served static resources and artifacts, you would use the StaticFilesystem to serve those. Then you'd use StaticFile to serve the single page which refers to the single-page application through index.html.

A full sample is provided in the static-examples folder. Here's a snippet from the example using Gorilla:

    pwd, err := os.Getwd()
    if err != nil {
        log.Fatalf("NewStaticFile: Could not get working directory.")
    }

    routes.PathPrefix("/dist/").Handler(middlewareHandler.Handle([]rye.Handler{
        rye.MiddlewareRouteLogger(),
        rye.NewStaticFilesystem(pwd+"/dist/", "/dist/"),
    }))

    routes.PathPrefix("/ui/").Handler(middlewareHandler.Handle([]rye.Handler{
        rye.MiddlewareRouteLogger(),
        rye.NewStaticFile(pwd + "/dist/index.html"),
    }))
Middleware list
Name Description
Access Token Provide Access Token validation
CIDR Provide request IP whitelisting
CORS Provide CORS functionality for routes
Auth Provide Authorization header validation (basic auth, JWT)
Route Logger Provide basic logging for a specific route
Static File Provides serving a single file
Static Filesystem Provides serving a single file
A Note on the JWT Middleware

The JWT Middleware pushes the JWT token onto the Context for use by other middlewares in the chain. This is a convenience that allows any part of your middleware chain quick access to the JWT. Example usage might include a middleware that needs access to your user id or email address stored in the JWT. To access this Context variable, the code is very simple:

func getJWTfromContext(rw http.ResponseWriter, r *http.Request) *rye.Response {
    // Retrieving the value is easy!
    // Just reference the rye.CONTEXT_JWT const as a key
    myVal := r.Context().Value(rye.CONTEXT_JWT)

    // Log it to the server log?
    log.Infof("Context Value: %v", myVal)

    return nil
}

API

Config

This struct is configuration for the MWHandler. It holds references and config to dependencies such as the statsdClient.

type Config struct {
    Statter  statsd.Statter
    StatRate float32
}
MWHandler

This struct is the primary handler container. It holds references to the statsd client.

type MWHandler struct {
    Config Config
}
Constructor
func NewMWHandler(statter statsd.Statter, statrate float32) *MWHandler
Use

This method prepends a global handler for every Handle method you call. Use this multiple times to setup global handlers for every endpoint. Call Use() for each global handler before setting up additional routes.

func (m *MWHandler) Use(handlers Handler)
Handle

This method chains middleware handlers in order and returns a complete http.Handler.

func (m *MWHandler) Handle(handlers []Handler) http.Handler
rye.Response

This struct is utilized by middlewares as a way to share state; ie. a middleware can return a *rye.Response as a way to indicate that further middleware execution should stop (without an error) or return a hard error by setting Err + StatusCode or add to the request Context by returning a non-nil Context.

type Response struct {
    Err           error
    StatusCode    int
    StopExecution bool
    Context       context.Context
}
Handler

This type is used to define an http handler that can be chained using the MWHandler.Handle method. The rye.Response is from the rye package and has facilities to emit StatusCode, bubble up errors and/or stop further middleware execution chain.

type Handler func(w http.ResponseWriter, r *http.Request) *rye.Response

Test stuff

All interfacing with the project is done via make. Targets exist for all primary tasks such as:

  • Testing: make test or make testv (for verbosity)
  • Generate: make generate - this generates based on vendored libraries (from $GOPATH)
  • All (test, build): make all
  • .. and a few others. Run make help to see all available targets.
  • You can also test the project in Docker (and Codeship) by running jet steps

Contributing

Fork the repository, write a PR and we'll consider it!

Special Thanks

Thanks go out to Justin Reyna (InVisionApp.com) for the awesome logo!

Documentation

Overview

Package Rye is a simple library to support http services. Rye provides a middleware handler which can be used to chain http handlers together while providing simple statsd metrics for use with a monitoring solution such as DataDog or other logging aggregators. Rye also provides some additional middleware handlers that are entirely optional but easily consumed using Rye.

Setup

In order to use rye, you should vendor it and the statsd client within your project.

govendor fetch github.com/cactus/go-statsd-client/statsd

# Rye is a private repo, so we should clone it first
mkdir -p $GOPATH/github.com/InVisionApp
cd $GOPATH/github.com/InVisionApp
git clone git@github.com:InVisionApp/rye.git

govendor add github.com/InVisionApp/rye

Writing custom middleware handlers

Begin by importing the required libraries:

import (
	"github.com/cactus/go-statsd-client/statsd"
	"github.com/InVisionApp/rye"
)

Create a statsd client (if desired) and create a rye Config in order to pass in optional dependencies:

config := &rye.Config{
	Statter:	statsdClient,
	StatRate:	DEFAULT_STATSD_RATE,
}

Create a middleware handler. The purpose of the Handler is to keep Config and to provide an interface for chaining http handlers.

middlewareHandler := rye.NewMWHandler(config)

Build your http handlers using the Handler type from the **rye** package.

type Handler func(w http.ResponseWriter, r *http.Request) *rye.Response

Here are some example (custom) handlers:

func homeHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	fmt.Fprint(rw, "Refer to README.md for auth-api API usage")
	return nil
}

func middlewareFirstHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	fmt.Fprint(rw, "This handler fires first.")
	return nil
}

func errorHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	return &rye.Response {
		StatusCode: http.StatusInternalServerError,
		Err:        errors.New(message),
	}
}

Finally, to setup your handlers in your API

routes := mux.NewRouter().StrictSlash(true)

routes.Handle("/", middlewareHandler.Handle(
	[]rye.Handler{
		a.middlewareFirstHandler,
		a.homeHandler,
	})).Methods("GET")

log.Infof("API server listening on %v", ListenAddress)

srv := &http.Server{
	Addr:		ListenAddress,
	Handler:	routes,
}

srv.ListenAndServe()

Statsd Generated by Rye

Rye comes with built-in configurable `statsd` statistics that you could record to your favorite monitoring system. To configure that, you'll need to set up a `Statter` based on the `github.com/cactus/go-statsd-client` and set it in your instantiation of `MWHandler` through the `rye.Config`.

When a middleware is called, it's timing is recorded and a counter is recorded associated directly with the http status code returned during the call. Additionally, an `errors` counter is also sent to the statter which allows you to count any errors that occur with a code equaling or above 500.

Example: If you have a middleware handler you've created with a method named `loginHandler`, successful calls to that will be recorded to `handlers.loginHandler.2xx`. Additionally you'll receive stats such as `handlers.loginHandler.400` or `handlers.loginHandler.500`. You also will receive an increase in the `errors` count.

If you're sending your logs into a system such as DataDog, be aware that your stats from Rye can have prefixes such as `statsd.my-service.my-k8s-cluster.handlers.loginHandler.2xx` or even `statsd.my-service.my-k8s-cluster.errors`. Just keep in mind your stats could end up in the destination sink system with prefixes.

Using With Golang Context

With Golang 1.7, a new feature has been added that supports a request specific context. This is a great feature that Rye supports out-of-the-box. The tricky part of this is how the context is modified on the request. In Golang, the Context is always available on a Request through `http.Request.Context()`. Great! However, if you want to add key/value pairs to the context, you will have to add the context to the request before it gets passed to the next Middleware. To support this, the `rye.Response` has a property called `Context`. This property takes a properly created context (pulled from the `request.Context()` function. When you return a `rye.Response` which has `Context`, the **rye** library will craft a new Request and make sure that the next middleware receives that request.

Here's the details of creating a middleware with a proper `Context`. You must first pull from the current request `Context`. In the example below, you see `ctx := r.Context()`. That pulls the current context. Then, you create a NEW context with your additional context key/value. Finally, you return `&rye.Response{Context:ctx}`

func addContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
	// Retrieve the request's context
	ctx := r.Context()

	// Create a NEW context
	ctx = context.WithValue(ctx,"CONTEXT_KEY","my context value")

	// Return that in the Rye response
	// Rye will add it to the Request to
	// pass to the next middleware
	return &rye.Response{Context:ctx}
}

Now in a later middleware, you can easily retrieve the value you set!

func getContextVar(rw http.ResponseWriter, r *http.Request) *rye.Response {
	// Retrieving the value is easy!
	myVal := r.Context().Value("CONTEXT_KEY")

	// Log it to the server log?
	log.Infof("Context Value: %v", myVal)

	return nil
}

For another simple example, look in the JWT middleware - it adds the JWT into the context for use by other middlewares. It uses the `CONTEXT_JWT` key to push the JWT token into the `Context`.

Using built-in middleware handlers

Rye comes with various pre-built middleware handlers. Pre-built middlewares source (and docs) can be found in the package dir following the pattern `middleware_*.go`.

To use them, specify the constructor of the middleware as one of the middleware handlers when you define your routes:

// example
routes.Handle("/", middlewareHandler.Handle(
	[]rye.Handler{
		rye.MiddlewareCORS(), // to use the CORS middleware (with defaults)
		a.homeHandler,
	})).Methods("GET")

OR

routes.Handle("/", middlewareHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareCORS("*", "GET, POST", "X-Access-Token"), // to use specific config when instantiating the middleware handler
		a.homeHandler,
	})).Methods("GET")

A Note on the JWT Middleware

The JWT Middleware pushes the JWT token onto the Context for use by other middlewares in the chain. This is a convenience that allows any part of your middleware chain quick access to the JWT. Example usage might include a middleware that needs access to your user id or email address stored in the JWT. To access this `Context` variable, the code is very simple:

func getJWTfromContext(rw http.ResponseWriter, r *http.Request) *rye.Response {
	// Retrieving the value is easy!
	// Just reference the rye.CONTEXT_JWT const as a key
	myVal := r.Context().Value(rye.CONTEXT_JWT)

	// Log it to the server log?
	log.Infof("Context Value: %v", myVal)

	return nil
}
Example (Basic)
package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/InVisionApp/rye"
	"github.com/cactus/go-statsd-client/statsd"
	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
)

func main() {
	statsdClient, err := statsd.NewBufferedClient("localhost:12345", "my_service", 1.0, 0)
	if err != nil {
		log.Fatalf("Unable to instantiate statsd client: %v", err.Error())
	}

	config := rye.Config{
		Statter:  statsdClient,
		StatRate: 1.0,
	}

	middlewareHandler := rye.NewMWHandler(config)

	middlewareHandler.Use(beforeAllHandler)

	routes := mux.NewRouter().StrictSlash(true)

	routes.Handle("/", middlewareHandler.Handle([]rye.Handler{
		middlewareFirstHandler,
		homeHandler,
	})).Methods("GET")

	// If you perform a `curl -i http://localhost:8181/cors -H "Origin: *.foo.com"`
	// you will see that the CORS middleware is adding required headers
	routes.Handle("/cors", middlewareHandler.Handle([]rye.Handler{
		rye.MiddlewareCORS(),
		homeHandler,
	})).Methods("GET", "OPTIONS")

	// If you perform an `curl -i http://localhost:8181/jwt \
	// -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
	// you will see that we are allowed through to the handler, if the sample token is changed, we will get a 401
	routes.Handle("/jwt", middlewareHandler.Handle([]rye.Handler{
		rye.NewMiddlewareJWT("secret"),
		getJwtFromContextHandler,
	})).Methods("GET")

	routes.Handle("/error", middlewareHandler.Handle([]rye.Handler{
		middlewareFirstHandler,
		errorHandler,
		homeHandler,
	})).Methods("GET")

	// In order to pass in a context variable, this set of
	// handlers works with "ctx" on the query string
	routes.Handle("/context", middlewareHandler.Handle(
		[]rye.Handler{
			stashContextHandler,
			logContextHandler,
		})).Methods("GET")

	log.Infof("API server listening on %v", "localhost:8181")

	srv := &http.Server{
		Addr:    "localhost:8181",
		Handler: routes,
	}

	srv.ListenAndServe()
}

func beforeAllHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("This handler is called before every endpoint: %+v", r)
	return nil
}

func homeHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Home handler has fired!")

	fmt.Fprint(rw, "This is the home handler")
	return nil
}

func middlewareFirstHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Middleware handler has fired!")
	return nil
}

func errorHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Error handler has fired!")

	message := "This is the error handler"

	return &rye.Response{
		StatusCode: http.StatusInternalServerError,
		Err:        errors.New(message),
	}
}

func stashContextHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Stash Context handler has fired!")

	// Retrieve the request's context
	ctx := r.Context()

	// A query string value to add to the context
	toContext := r.URL.Query().Get("ctx")

	if toContext != "" {
		log.Infof("Adding `query-string-ctx` to request.Context(). Val: %v", toContext)
	} else {
		log.Infof("Adding default `query-string-ctx` value to context")
		toContext = "No value added. Add querystring param `ctx` with a value to get it mirrored through context."
	}

	// Create a NEW context
	ctx = context.WithValue(ctx, "query-string-ctx", toContext)

	// Return that in the Rye response
	// Rye will add it to the Request to
	// pass to the next middleware
	return &rye.Response{Context: ctx}
}

func logContextHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Log Context handler has fired!")

	// Retrieving a context value is EASY in subsequent middlewares
	fromContext := r.Context().Value("query-string-ctx")

	// Reflect that on the http response
	fmt.Fprintf(rw, "Here's the `ctx` query string value you passed. Pulled from context: %v", fromContext)
	return nil
}

// This handler pulls the JWT from the Context and echoes it through the request
func getJwtFromContextHandler(rw http.ResponseWriter, r *http.Request) *rye.Response {
	log.Infof("Log Context handler has fired!")

	jwt := r.Context().Value(rye.CONTEXT_JWT)
	if jwt != nil {
		fmt.Fprintf(rw, "JWT found in Context: %v", jwt)
	}
	return nil
}
Output:

Index

Examples

Constants

View Source
const (
	// CORS Specific constants
	DEFAULT_CORS_ALLOW_ORIGIN  = "*"
	DEFAULT_CORS_ALLOW_METHODS = "POST, GET, OPTIONS, PUT, DELETE"
	DEFAULT_CORS_ALLOW_HEADERS = "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Access-Token"
)
View Source
const AUTH_USERNAME_KEY = "request-username"
View Source
const (
	CONTEXT_JWT = "rye-middlewarejwt-jwt"
)

Variables

This section is empty.

Functions

func MiddlewareCORS

func MiddlewareCORS() func(rw http.ResponseWriter, req *http.Request) *Response

MiddlewareCORS is the struct to represent configuration of the CORS handler.

func MiddlewareRouteLogger

func MiddlewareRouteLogger() func(rw http.ResponseWriter, req *http.Request) *Response

MiddlewareRouteLogger creates a new handler to provide simple logging output for the specific route. You can use this middleware by specifying `rye.MiddlewareRouteLogger` when defining your routes.

Example use case:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.MiddlewareRouteLogger(),
		yourHandler,
	})).Methods("PUT", "OPTIONS")

func NewMiddlewareAccessQueryToken added in v1.0.1

func NewMiddlewareAccessQueryToken(queryParamName string, tokens []string) func(rw http.ResponseWriter, req *http.Request) *Response

NewMiddlewareAccessQueryToken creates a new handler to verify access tokens passed as a query parameter.

Example usage:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareAccessQueryToken(queryParamName, []string{token1, token2}),
		yourHandler,
	})).Methods("POST")

func NewMiddlewareAccessToken

func NewMiddlewareAccessToken(headerName string, tokens []string) func(rw http.ResponseWriter, req *http.Request) *Response

NewMiddlewareAccessToken creates a new handler to verify access tokens passed as a header.

Example usage:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareAccessToken(tokenHeaderName, []string{token1, token2}),
		yourHandler,
	})).Methods("POST")

func NewMiddlewareAuth added in v1.0.6

func NewMiddlewareAuth(authFunc AuthFunc) func(rw http.ResponseWriter, req *http.Request) *Response

func NewMiddlewareCIDR

func NewMiddlewareCIDR(CIDRs []string) func(rw http.ResponseWriter, req *http.Request) *Response

NewMiddlewareCIDR creates a new handler to verify incoming IPs against a set of CIDR Notation strings in a rye chain. For reference on CIDR notation see https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing

Example usage:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareCIDR(CIDRs), // []string of allowed CIDRs
		yourHandler,
	})).Methods("POST")

func NewMiddlewareCORS

func NewMiddlewareCORS(origin, methods, headers string) func(rw http.ResponseWriter, req *http.Request) *Response

NewMiddlewareCORS creates a new handler to support CORS functionality. You can use this middleware by specifying `rye.MiddlewareCORS()` or `rye.NewMiddlewareCORS(origin, methods, headers)` when defining your routes.

Default CORS Values:

DEFAULT_CORS_ALLOW_ORIGIN**: "*"
DEFAULT_CORS_ALLOW_METHODS**: "POST, GET, OPTIONS, PUT, DELETE"
DEFAULT_CORS_ALLOW_HEADERS**: "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Access-Token"

If you are planning to use this in production - you should probably use this middleware *with* params.

Example use case:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.MiddlewareCORS(), // use defaults for allowed origin, headers, methods
		yourHandler,
	})).Methods("PUT", "OPTIONS")

OR:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareCORS("*", "POST, GET", "SomeHeader, AnotherHeader"),
		yourHandler,
	})).Methods("PUT", "OPTIONS")

func NewMiddlewareGetHeader added in v1.0.2

func NewMiddlewareGetHeader(headerName, contextKey string) func(rw http.ResponseWriter, req *http.Request) *Response

NewMiddlewareGetHeader creates a new handler to extract any header and save its value into the context.

headerName: the name of the header you want to extract
contextKey: the value key that you would like to store this header under in the context

Example usage:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareGetHeader(headerName, contextKey),
		yourHandler,
	})).Methods("POST")

func NewMiddlewareJWT

func NewMiddlewareJWT(secret string) func(rw http.ResponseWriter, req *http.Request) *Response

This middleware is deprecated. Use NewMiddlewareAuth with NewJWTAuthFunc instead.

This remains here as a shim for backwards compatibility.

---------------------------------------------------------------------------

This middleware provides JWT verification functionality

You can use this middleware by specifying `rye.NewMiddlewareJWT(shared_secret)` when defining your routes.

This middleware has no default version, it must be configured with a shared secret.

Example use case:

routes.Handle("/some/route", a.Dependencies.MWHandler.Handle(
	[]rye.Handler{
		rye.NewMiddlewareJWT("this is a big secret"),
		yourHandler,
	})).Methods("PUT", "OPTIONS")

Additionally, this middleware puts the JWT token into the context for use by other middlewares in your chain.

Access to that is simple (using the CONTEXT_JWT constant as a key)

func getJWTfromContext(rw http.ResponseWriter, r *http.Request) *rye.Response {

	// Retrieving the value is easy!
	// Just reference the rye.CONTEXT_JWT const as a key
	myVal := r.Context().Value(rye.CONTEXT_JWT)

	// Log it to the server log?
	log.Infof("Context Value: %v", myVal)

	return nil
}

func NewStaticFile added in v1.0.1

func NewStaticFile(path string) func(rw http.ResponseWriter, req *http.Request) *Response

NewStaticFile creates a new handler to serve a file from a path on the local filesystem. The path should be an absolute path -> i.e., it's up to the program using Rye to correctly determine what path it should be serving from. An example is available in the `static_example.go` file which shows setting up a path relative to the go executable.

The purpose of this handler is to serve a specific file for any requests through the route handler. For instance, in the example below, any requests made to `/ui` will always be routed to /dist/index.html. This is important for single page applications which happen to use client-side routers. Therefore, you might have a webpack application with it's entrypoint `/dist/index.html`. That file may point at your `bundle.js`. Every request into the app will need to always be routed to `/dist/index.html`

Example use case:

routes.PathPrefix("/ui/").Handler(middlewareHandler.Handle([]rye.Handler{
	rye.MiddlewareRouteLogger(),
	rye.NewStaticFile(pwd + "/dist/index.html"),
}))

func NewStaticFilesystem added in v1.0.1

func NewStaticFilesystem(path string, stripPrefix string) func(rw http.ResponseWriter, req *http.Request) *Response

NewStaticFilesystem creates a new handler to serve a filesystem from a path on the local filesystem. The path should be an absolute path -> i.e., it's up to the program using Rye to correctly determine what path it should be serving from. An example is available in the `static_example.go` file which shows setting up a path relative to the go executable.

The primary benefit of this is to serve an entire set of files. You can pre-pend typical Rye middlewares to the chain. The static filesystem middleware should always be last in a chain, however. The `stripPrefix` allows you to ignore the prefix on requests so that the proper files will be matched.

Example use case:

routes.PathPrefix("/dist/").Handler(middlewareHandler.Handle([]rye.Handler{
	rye.MiddlewareRouteLogger(),
	rye.NewStaticFilesystem(pwd+"/dist/", "/dist/"),
}))

func WriteJSONResponse

func WriteJSONResponse(rw http.ResponseWriter, statusCode int, content []byte)

WriteJSONResponse writes data and status code to the ResponseWriter

func WriteJSONStatus

func WriteJSONStatus(rw http.ResponseWriter, status, message string, statusCode int)

WriteJSONStatus is a wrapper for WriteJSONResponse that returns a marshalled JSONStatus blob

Types

type AuthFunc added in v1.0.6

type AuthFunc func(context.Context, string) *Response

func NewBasicAuthFunc added in v1.0.6

func NewBasicAuthFunc(userPass map[string]string) AuthFunc

func NewJWTAuthFunc added in v1.0.6

func NewJWTAuthFunc(secret string) AuthFunc

type Config

type Config struct {
	Statter  statsd.Statter
	StatRate float32

	// toggle types of stats sent
	NoErrStats        bool
	NoDurationStats   bool
	NoStatusCodeStats bool

	// Customer Statter for the client
	CustomStatter CustomStatter
}

Config struct allows you to set a reference to a statsd.Statter and include it's stats rate.

type CustomStatter added in v1.0.8

type CustomStatter interface {
	ReportStats(handlerName string, elapsedTime time.Duration, req *http.Request, resp *Response) error
}

CustomStatter allows the client to log any additional statsD metrics Rye computes around the request handler.

type Handler

type Handler func(w http.ResponseWriter, r *http.Request) *Response

Handler is the primary type that any rye middleware must implement to be called in the Handle() function. In order to use this you must return a *rye.Response.

type JSONStatus

type JSONStatus struct {
	Message string `json:"message"`
	Status  string `json:"status"`
}

JSONStatus is a simple container used for conveying status messages.

type MWHandler

type MWHandler struct {
	Config Config
	// contains filtered or unexported fields
}

MWHandler struct is used to configure and access rye's basic functionality.

func NewMWHandler

func NewMWHandler(config Config) *MWHandler

Constructor for new instantiating new rye instances It returns a constructed *MWHandler instance.

func (*MWHandler) Handle

func (m *MWHandler) Handle(customHandlers []Handler) http.Handler

The Handle function is the primary way to set up your chain of middlewares to be called by rye. It returns a http.HandlerFunc from net/http that can be set as a route in your http server.

func (*MWHandler) Use added in v1.0.1

func (m *MWHandler) Use(handler Handler)

Use adds a handler to every request. All handlers set up with use are fired first and then any route specific handlers are called

type Response

type Response struct {
	Err           error
	StatusCode    int
	StopExecution bool
	Context       context.Context
}

Response struct is utilized by middlewares as a way to share state; ie. a middleware can return a *Response as a way to indicate that further middleware execution should stop (without an error) or return a a hard error by setting `Err` + `StatusCode`.

func (*Response) Error

func (r *Response) Error() string

Error bubbles a response error providing an implementation of the Error interface. It returns the error as a string.

Directories

Path Synopsis
fakes
statsdfakes
This file was generated by counterfeiter
This file was generated by counterfeiter

Jump to

Keyboard shortcuts

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