reb

package module
v0.0.0-...-47c985b Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2020 License: Apache-2.0 Imports: 30 Imported by: 0

README

Caddy plugin

Motivation

The goal of this plugin is to enable a high availability setup for explorers. Given that we are a blockchain application, meaning every instance might have a different view of what it considers to be the active chain and therefore "the truth", we need to overcome some extra obstacles. Most importantly, the definition of a "healthy node" is more strict. We can't just say any node which responds is healthy, for instance, a node which has been down for a really long time will need some additional time to sync up before it should serve requests again. Although is is perfectly fine for this node to reply, it does not have the latest blocks, or the chain might have forked in the meantime. The easy setup, which runs every explorer on it's own DNS, puts the burden of defining healthy nodes on the clients. Rather than picking any node which they can get a reply from, They need to connect to all nodes which reply, get their block heights, count how many times a certain hight occurs, and only mark instances on that height as healthy. Failure to do this, (and randomly sending requests to different nodes), can lead to an inconsistent view of the data by the client, where (recent) transactions might appear and disappear, for example when a new instance is syncing. On the other hand, a regular HA approach where the client connects to a single DNS, which is set up with multiple instances with failover is not ideal either, since such setups generally don't allow this custom type of healthcheck, but just care whether the instance is responding or not. To this end this caddy plugin has been created, which is a modification of the standard caddy proxy plugin. Most importantly, the healtcheck code has been updated to work as described above. This means that, an application needs to connect to just one server, the server running the caddy which has this plugin. Note that this means that this caddy is now a single point of failure. Traditional HA still needs to be applied now, but this custom plugin does allow all the healthcheck logic to be offloaded to the server side.

Building

To create a caddy with this module enabled, create a directory and add the following main.go file:

package main

import (
	"fmt"

	"github.com/caddyserver/caddy"
	"github.com/caddyserver/caddy/caddy/caddymain"
	"github.com/caddyserver/caddy/caddyhttp/httpserver"

	_ "github.com/threefoldtech/rivine/caddy-plugin"
)

func main() {
	httpserver.RegisterDevDirective("reb", "proxy")
	caddymain.Run()
}

Now run the following commands:

go mod init caddy
go get github.com/caddyserver/caddy
go install

You will now have a caddy server with this plugin enabled in your $GOPATH.

Notice that we registered our plugin in the main function here. According to the official caddy docs, it is recommended to manually register the plugin, by inserting the name in the plugin list. This approach still works though, especially if only 1 plugin is used, but it does print a warning at startup.

You can now use the reb directive like you would normally use the proxy directive.

Documentation

Overview

Package proxy is middleware that proxies HTTP requests.

Index

Constants

View Source
const CustomStatusContextCancelled = 499

Variables

This section is empty.

Functions

func RegisterPolicy

func RegisterPolicy(name string, policy func(string) Policy)

RegisterPolicy adds a custom policy to the proxy.

Types

type First

type First struct{}

First is a policy that selects the first available host

func (*First) Select

func (r *First) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects the first available host from the pool

type Header struct {
	// The name of the request header, the value of which will determine
	// how the request is routed
	Name string
}

Header is a policy that selects based on a hash of the given header

func (*Header) Select

func (r *Header) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects the host based on hashing the header value

type HostPool

type HostPool []*UpstreamHost

HostPool is a collection of UpstreamHosts.

type IPHash

type IPHash struct{}

IPHash is a policy that selects hosts based on hashing the request IP

func (*IPHash) Select

func (r *IPHash) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects an up host from the pool based on hashing the request IP

type LeastConn

type LeastConn struct{}

LeastConn is a policy that selects the host with the least connections.

func (*LeastConn) Select

func (r *LeastConn) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects the up host with the least number of connections in the pool. If more than one host has the same least number of connections, one of the hosts is chosen at random.

type Policy

type Policy interface {
	Select(pool HostPool, r *http.Request) *UpstreamHost
}

Policy decides how a host will be selected from a pool.

type Proxy

type Proxy struct {
	Next      httpserver.Handler
	Upstreams []Upstream
}

Proxy represents a middleware instance that can proxy requests.

func (Proxy) ServeHTTP

func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)

ServeHTTP satisfies the httpserver.Handler interface.

type Random

type Random struct{}

Random is a policy that selects up hosts from a pool at random.

func (*Random) Select

func (r *Random) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects an up host at random from the specified pool.

type ReverseProxy

type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
	// contains filtered or unexported fields
}

ReverseProxy is an HTTP Handler that takes an incoming request and sends it to another server, proxying the response back to the client.

func NewSingleHostReverseProxy

func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int, timeout, fallbackDelay time.Duration) *ReverseProxy

NewSingleHostReverseProxy returns a new ReverseProxy that rewrites URLs to the scheme, host, and base path provided in target. If the target's path is "/base" and the incoming request was for "/dir", the target request will be for /base/dir. Without logic: target's path is "/", incoming is "/api/messages", without is "/api", then the target request will be for /messages.

func (*ReverseProxy) ServeHTTP

func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error

ServeHTTP serves the proxied request to the upstream by performing a roundtrip. It is designed to handle websocket connection upgrades as well.

func (*ReverseProxy) UseInsecureTransport

func (rp *ReverseProxy) UseInsecureTransport()

UseInsecureTransport is used to facilitate HTTPS proxying when it is OK for upstream to be using a bad certificate, since this transport skips verification.

func (*ReverseProxy) UseOwnCACertificates

func (rp *ReverseProxy) UseOwnCACertificates(CaCertPool *x509.CertPool)

UseOwnCertificate is used to facilitate HTTPS proxying with locally provided certificate.

type RoundRobin

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

RoundRobin is a policy that selects hosts based on round-robin ordering.

func (*RoundRobin) Select

func (r *RoundRobin) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects an up host from the pool using a round-robin ordering scheme.

type URIHash

type URIHash struct{}

URIHash is a policy that selects the host based on hashing the request URI

func (*URIHash) Select

func (r *URIHash) Select(pool HostPool, request *http.Request) *UpstreamHost

Select selects the host based on hashing the URI

type Upstream

type Upstream interface {
	// The path this upstream host should be routed on
	From() string

	// Selects an upstream host to be routed to. It
	// should return a suitable upstream host, or nil
	// if no such hosts are available.
	Select(*http.Request) *UpstreamHost

	// Checks if subpath is not an ignored path
	AllowedPath(string) bool

	// Gets the duration of the headstart the first
	// connection is given in the Go standard library's
	// implementation of "Happy Eyeballs" when DualStack
	// is enabled in net.Dialer.
	GetFallbackDelay() time.Duration

	// Gets how long to try selecting upstream hosts
	// in the case of cascading failures.
	GetTryDuration() time.Duration

	// Gets how long to wait between selecting upstream
	// hosts in the case of cascading failures.
	GetTryInterval() time.Duration

	// Gets the number of upstream hosts.
	GetHostCount() int

	// Gets how long to wait before timing out
	// the request
	GetTimeout() time.Duration

	// Stops the upstream from proxying requests to shutdown goroutines cleanly.
	Stop() error
}

Upstream manages a pool of proxy upstream hosts.

func NewStaticUpstreams

func NewStaticUpstreams(c caddyfile.Dispenser, host string) ([]Upstream, error)

NewStaticUpstreams parses the configuration input and sets up static upstreams for the proxy middleware. The host string parameter, if not empty, is used for setting the upstream Host header for the health checks if the upstream header config requires it.

type UpstreamHost

type UpstreamHost struct {
	// This field is read & written to concurrently, so all access must use
	// atomic operations.
	Conns             int64 // must be first field to be 64-bit aligned on 32-bit systems
	MaxConns          int64
	Name              string // hostname of this upstream host
	UpstreamHeaders   http.Header
	DownstreamHeaders http.Header
	FailTimeout       time.Duration
	CheckDown         UpstreamHostDownFunc
	WithoutPathPrefix string
	ReverseProxy      *ReverseProxy
	Fails             int32
	// This is an int32 so that we can use atomic operations to do concurrent
	// reads & writes to this value.  The default value of 0 indicates that it
	// is healthy and any non-zero value indicates unhealthy.
	Unhealthy                    int32
	HealthCheckResult            atomic.Value
	UpstreamHeaderReplacements   headerReplacements
	DownstreamHeaderReplacements headerReplacements
}

UpstreamHost represents a single proxy upstream

func (*UpstreamHost) Available

func (uh *UpstreamHost) Available() bool

Available checks whether the upstream host is available for proxying to

func (*UpstreamHost) Down

func (uh *UpstreamHost) Down() bool

Down checks whether the upstream host is down or not. Down will try to use uh.CheckDown first, and will fall back to some default criteria if necessary.

func (*UpstreamHost) Full

func (uh *UpstreamHost) Full() bool

Full checks whether the upstream host has reached its maximum connections

type UpstreamHostDownFunc

type UpstreamHostDownFunc func(*UpstreamHost) bool

UpstreamHostDownFunc can be used to customize how Down behaves.

Jump to

Keyboard shortcuts

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