sharedhttpcache

package module
v0.0.0-...-24c5762 Latest Latest
Warning

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

Go to latest
Published: May 5, 2020 License: MIT Imports: 18 Imported by: 0

README

HTTP caching

GoDoc Travis CI Codecov GoReport

The goal of this project is to make a RFC 7234 compliant shared caching server in Go. Tho the main goal is to have a out-of-the-box working caching server it is also important that the functionality is exported so it can be used as library in bigger projects.

Features

  • Fully RFC7234 compliant (excluding optional features)
  • Flexible configuration
  • Multi layer system
  • Customizable logging

Usage

TODO make command line usage section for the standalone cache server

Examples

For library examples please go the the godoc page

Validation

To validate the cache a testing harness like https://github.com/http-tests/cache-tests can be used.

TODO

  • Adding tests, both unit and integration
  • Store partial responses
  • Combining Partial Content
  • Calculating Heuristic Freshness based on past behavior
  • Add informational headers about cache hit's ect.
  • Add websocket support
  • Add HTTP/2 push support
  • Add optional RFC7239 support
  • http cache-aware server-push link
  • Add Cache-Control extensions (Or at least make a callback so someone can from outside the package)
  • Add metrics (prometheus)
  • Add user triggered cache invalidation
  • Add advanced cache replacement policies to inmemory layer
  • Add disk storage layer
  • Add redis storage layer
  • Add s3 storage layer

Documentation

Overview

Example

Example demonstrate the most basic setup for a cache controller where a single origin server is used

package main

import (
	"fmt"
	"net/http"

	"github.com/dylandreimerink/sharedhttpcache/layer"

	"github.com/dylandreimerink/sharedhttpcache"
)

func main() {

	controller := &sharedhttpcache.CacheController{
		DefaultForwardConfig: &sharedhttpcache.ForwardConfig{
			Host: "example.com",
			TLS:  true,
		},
		Layers: []layer.CacheLayer{
			layer.NewInMemoryCacheLayer(128 * 1024 * 1024), // 128MB of in-memory(RAM) storage
		},
	}

	server := &http.Server{
		Handler: controller,
	}

	err := server.ListenAndServe()
	if err != nil {
		fmt.Printf("Server exited with error: %s", err.Error())
	}
}
Output:

Example (Http2)

ExampleHTTP2 demonstrates how to enable HTTP/2 on the connection from the client to the cache server and from the cache server to the origin. It is not required to have both connections support HTTP/2 one or the other will also work

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log"
	"net/http"

	"github.com/dylandreimerink/sharedhttpcache/layer"
	"golang.org/x/net/http2"

	"github.com/dylandreimerink/sharedhttpcache"
)

func main() {

	systemCertPool, err := x509.SystemCertPool()
	if err != nil {
		panic(err)
	}

	http2Transport := &http2.Transport{
		TLSClientConfig: &tls.Config{
			RootCAs: systemCertPool,
		},
	}

	controller := &sharedhttpcache.CacheController{
		DefaultForwardConfig: &sharedhttpcache.ForwardConfig{
			Host: "example.com",
			TLS:  true,
		},
		DefaultTransport: http2Transport,
		Layers: []layer.CacheLayer{
			layer.NewInMemoryCacheLayer(128 * 1024 * 1024), // 128MB of in-memory(RAM) storage
		},
	}

	certPem := []byte(`-----BEGIN CERTIFICATE-----
MIICGTCCAcCgAwIBAgIIelEInVZKyIUwCgYIKoZIzj0EAwIwVDELMAkGA1UEBhMC
TkwxEjAQBgNVBAMMCWxvY2FsaG9zdDETMBEGA1UECAwKU29tZS1TdGF0ZTEcMBoG
A1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0xOTExMDQyMjUwMTVaFw0yOTEx
MDEyMjUwMTVaMFQxCzAJBgNVBAYTAk5MMRIwEAYDVQQDDAlsb2NhbGhvc3QxEzAR
BgNVBAgMClNvbWUtU3RhdGUxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQw8ez8+AGVCZWeWXRBy5EvbeqBVQ1G
8o6zYzVhgN13tY0sCKYaBro7oooyGat1JkcE7CoPE3Gv4n+hOujCd9Yto3wwejAd
BgNVHQ4EFgQUUbULEj2qxKQ7NHcIS1XSw+wPtMEwHwYDVR0jBBgwFoAUUbULEj2q
xKQ7NHcIS1XSw+wPtMEwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCA6gwHQYDVR0l
BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAoGCCqGSM49BAMCA0cAMEQCIAEzF98B
zeZlCSU4EFZO5ZaO+YrqJGQOppWOtQcZgei7AiAS5E2ZhciP5xQEjmE0j3D9CNSG
7DrIoXvr+qKh/1/hcA==
-----END CERTIFICATE-----`)
	keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEII7nelmettK1JFHWoSmVHLGa0ho/2nE6bQbiASvKqQXHoAoGCCqGSM49
AwEHoUQDQgAEMPHs/PgBlQmVnll0QcuRL23qgVUNRvKOs2M1YYDdd7WNLAimGga6
O6KKMhmrdSZHBOwqDxNxr+J/oTrownfWLQ==
-----END EC PRIVATE KEY-----`)

	cert, err := tls.X509KeyPair(certPem, keyPem)
	if err != nil {
		log.Fatal(err)
	}

	server := &http.Server{
		Addr:    ":4443",
		Handler: controller,

		//Use a secure TLS config (As of 2019)
		TLSConfig: &tls.Config{
			Certificates:             []tls.Certificate{cert},
			MinVersion:               tls.VersionTLS12,
			CurvePreferences:         []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
				tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			},
		},
	}

	err = http2.ConfigureServer(server, nil)
	if err != nil {
		panic(err)
	}

	err = server.ListenAndServeTLS("", "")
	if err != nil {
		fmt.Printf("Server exited with error: %s", err.Error())
	}
}
Output:

Example (MultiOrigin)

ExampleMultiOrigin demonstrates how the cache can forward to multiple origin server. The decision which origin server to use in this example is based on the Host header in the request

package main

import (
	"fmt"
	"net/http"

	"github.com/dylandreimerink/sharedhttpcache/layer"

	"github.com/dylandreimerink/sharedhttpcache"
)

func main() {

	originServers := map[string]*sharedhttpcache.ForwardConfig{
		"example.com": &sharedhttpcache.ForwardConfig{
			Host: "2606:2800:220:1:248:1893:25c8:1946",
			TLS:  true,
		},
		"theuselessweb.com": &sharedhttpcache.ForwardConfig{
			Host: "3.121.157.244",
			TLS:  false,
		},
	}

	controller := &sharedhttpcache.CacheController{
		ForwardConfigResolver: sharedhttpcache.ForwardConfigResolverFunc(func(req *http.Request) *sharedhttpcache.ForwardConfig {
			return originServers[req.Host]
		}),
		Layers: []layer.CacheLayer{
			layer.NewInMemoryCacheLayer(128 * 1024 * 1024), // 128MB of in-memory(RAM) storage
		},
	}

	server := &http.Server{
		Handler: controller,
	}

	err := server.ListenAndServe()
	if err != nil {
		fmt.Printf("Server exited with error: %s", err.Error())
	}
}
Output:

Index

Examples

Constants

View Source
const (
	AgeHeader          = "Age"
	CacheControlHeader = "Cache-Control"
	ExpiresHeader      = "Expires"
	DateHeader         = "Date"
	VaryHeader         = "Vary"

	NoCacheDirective         = "no-cache"
	NoStoreDirective         = "no-store"
	MustRevalidateDirective  = "must-revalidate"
	ProxyRevalidateDirective = "proxy-revalidate"
	SMaxAgeDirective         = "s-maxage"
	MaxAgeDirective          = "max-age"
	PublicDirective          = "public"
	PrivateDirective         = "private"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type CacheConfig

type CacheConfig struct {

	//CacheableMethods is a list of request methods for which responses may be cached.
	// It is not advisable to cache unsafe methods like POST. Tho it is possible to do so
	// Note that unsafe methods will not be cached even if they are in this list as per section 4 of RFC7234
	//
	// WARNING values must be uppercase, no case conversion is done at runtime
	CacheableMethods []string

	//SafeMethods is a list of "safe" request methods as defined in section 4.2.1 of RFC7231
	// Any method not in this list is considered unsafe and will not be cached
	// If a request is made with a unsafe method it may cause invalidation as per section 4.4 of RFC7234
	SafeMethods []string

	//StatusCodeDefaultExpirationTimes is a map of times index by the http response code
	//
	// These times will be used as default expiration time unless the response contains a header which specifies a different
	//
	// Not all responses should be cached for the same duration.
	// A 307 Temporary Redirect for example should be cached for less time than a 301 Moved Permanently
	//
	// Codes not appearing in this list will be considered NOT understood and thus make a response uncacheable according to section 3 of RFC7234.
	StatusCodeDefaultExpirationTimes map[int]time.Duration

	//CacheableFileExtensions is a list of cacheable file extensions
	// File extensions are used instead of MIME types because the same file extension can have separate MIME types
	// It is advised to only use static file types like stylesheets or images and not dynamic content like html
	CacheableFileExtensions []string

	//CacheIncompleteResponses enables or disables the optional feature mentioned in section 3.1 of RFC7234
	// Caching of incomplete requests will cache responses with status code 206 (Partial Content)
	//
	// Note that this carries a performance impact because ranges have to be accounted for when storing and querying the cached content
	CacheIncompleteResponses bool

	//CombinePartialResponses enables or disables the optional feature mentioned in section 3.3 of RFC7234
	// When this feature is enabled and incomplete responses are enabled
	// the caching server attempts to combine multiple incomplete responses into a complete response.
	//
	// Note that this carries a performance impact because at every time a new incomplete range is received reconstruction of the full resource will be attempted
	CombinePartialResponses bool

	//If ServeStaleOnError is true the cache will attempt to serve a stale response in case revalidation fails because the origin server returned a 5xx code or is unreachable
	//This setting respects the Cache-Control header of the client and server.
	ServeStaleOnError bool

	//If HTTPWarnings is true warnings as described in section 5.5 of RFC7234 will be added to HTTP responses
	// This is a option because the feature will be removed from future HTTP specs https://github.com/httpwg/http-core/issues/139
	HTTPWarnings bool
}

CacheConfig defines a config for how the cache should behave A different cache config can be used on a per request basis like for different websites or pages

func NewCacheConfig

func NewCacheConfig() *CacheConfig

NewCacheConfig creates a new CacheConfig struct which is configures with good defaults which satisfy RFC7234

type CacheConfigResolver

type CacheConfigResolver interface {

	//GetCacheConfig is called to resolve a CacheConfig depending on the request
	// If nil is returned the default config will be used
	GetCacheConfig(req *http.Request) *CacheConfig
}

A CacheConfigResolver resolves which cache config to use for which request. Different websites or even different pages on the same site can have different cache settings

type CacheController

type CacheController struct {

	//The default config is used if no host could be matched
	// if nil on first usage the default config CacheConfig from NewCacheConfig will be used
	DefaultCacheConfig *CacheConfig

	//CacheConfigResolver can optionally be set.
	// If not nil the CacheConfigResolver will be used to determine which cache config to use for a given request
	// If the CacheConfigResolver is not set the default config will always be used
	CacheConfigResolver CacheConfigResolver

	//The default transport used to contact the origin server
	// If nil the http.DefaultTransport will be used
	DefaultTransport http.RoundTripper

	//TransportResolver can optionally be set.
	// If not nil the TransportResolver will be used to determine which transport to use for a given request
	// If the TransportResolver is not set the default transport will always be used
	TransportResolver TransportResolver

	//The default config used to forward requests to the origin server
	// If nil all requests using the default config will return a 503 error
	DefaultForwardConfig *ForwardConfig

	//ForwardConfigResolver can optionally be set.
	// If not nil the ForwardConfigResolver will be used to determin which forwardConfig to use for a given request
	// If the ForwardConfigResolver is not set the DefaultForwardConfig will be used
	ForwardConfigResolver ForwardConfigResolver

	//The storage layers which will be searched, the layers are searched in order
	// Layers should be arranged from fastest to slowest
	// Faster caching layers typically have less capacity and thus will replace content sooner
	Layers []layer.CacheLayer

	//The Logger which will be used for logging
	// if nil the default logger will be used
	Logger *logrus.Logger
}

The CacheController is the high level interface for a cache. The cache controller calls the caching logic and handles storing and retrieving of cached responses.

func (*CacheController) ServeHTTP

func (controller *CacheController) ServeHTTP(resp http.ResponseWriter, req *http.Request)

type ForwardConfig

type ForwardConfig struct {
	//Can be a Hostname or a IP address and optionally the tcp port
	// if no port is specified the default http or https port is used based on the TLS variable
	Host string

	//If a https (http over TLS) connection should be used
	TLS bool
}

The ForwardConfig holds information about how to forward traffic to the origin server

type ForwardConfigResolver

type ForwardConfigResolver interface {

	//GetForwardConfig is called to resolve a ForwardConfig depending on the request
	// If nil is returned the default forwardConfig will be used
	GetForwardConfig(req *http.Request) *ForwardConfig
}

A ForwardConfigResolver resolves which forward config should be used for a particulair request

type ForwardConfigResolverFunc

type ForwardConfigResolverFunc func(req *http.Request) *ForwardConfig

The ForwardConfigResolverFunc type is an adapter to allow the use of ordinary functions as ForwardConfigResolver

func (ForwardConfigResolverFunc) GetForwardConfig

func (resolver ForwardConfigResolverFunc) GetForwardConfig(req *http.Request) *ForwardConfig

GetForwardConfig calls the underlying function to resolve a forward config from a request

type TransportResolver

type TransportResolver interface {

	//GetTransport is called to resolve a CacheConfig depending on the request
	// If nil is returned the default transport will be used
	GetTransport(req *http.Request) http.RoundTripper
}

A TransportResolver resolves which transport should be used for a particulair request

type TransportResolverFunc

type TransportResolverFunc func(req *http.Request) http.RoundTripper

The TransportResolverFunc type is an adapter to allow the use of ordinary functions as TransportResolver

func (TransportResolverFunc) GetTransport

func (resolver TransportResolverFunc) GetTransport(req *http.Request) http.RoundTripper

GetForwardConfig calls the underlying function to resolve a round tripper from a request

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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