servefiles

package module
v3.3.3 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2021 License: MIT Imports: 12 Imported by: 0

README

servefiles

GoDoc Build Status Coverage Status Go Report Card Issues

Serve static files from a Go http server, including performance-enhancing features.

  • Based on the standard net/http ServeFiles, with gzip/brotli and cache performance enhancements.

Please see the GoDoc for more.

Installation

go get -u github.com/daubit/servefiles/v3

MaxAge

User agents can cache responses. This http server enables easy support for two such mechanisms:

  • Conditional requests (using etags) allow the response to be sent only when it has changed
  • MaxAge response headers allow the user agent to cache entities until some expiry time.

Note that conditional requests (RFC7232) and MaxAge caching (RFC7234) can work together as required. Conditional requests still require network round trips, whereas caching removes all network round-trips until the entities reach their expiry time.

SPA support

There is support for serving SPA webpage by using WithSPA() this serves index.html for all resources that do not have a file extension

Gin Adapter

Sub-package gin_adapter provides integration hooks into the Gin web framework. This makes it easy for Gin code to use this asset handler also: see the example in the sub-package for more info.

v3

Version 3 brings Go module support. Also, brotli encoding is supported alongside gzip encoding. Brotli now has widespread implementation in most browsers. You can compress your textual assets (including Javascript, CSS, HTML, SVG etc) using Brotli and/or Gzip as part of your build pipeline, uploading both the original and compressed files to your production server's asset directories. Brotli compression takes longer than Gzip but produces more compact files. Compression is, of course, optional.

Earlier versions

Earlier versions do not support Go modules, nor brotli encoding, although gzip encoding is supported.

Status

This library has been in reliable production use for some time. Versioning follows the well-known semantic version pattern.

Licence

MIT

Documentation

Overview

Package servefiles provides a static asset handler for serving files such as images, stylesheets and javascript code. This is an enhancement to the standard net/http ServeFiles, which is used internally. Care is taken to set headers such that the assets will be efficiently cached by browsers and proxies.

assets := servefiles.NewAssetHandler("./assets/").WithMaxAge(time.Hour)

Assets is an http.Handler and can be used alongside your other handlers.

Gzipped Content

The Assets handler serves gzipped content when the browser indicates it can accept it. But it does not gzip anything on-the-fly. Nor does it create any gzipped files for you.

During the preparation of your web assets, all text files (CSS, JS etc) should be accompanied by their gzipped equivalent; your build process will need to do this. The Assets handler will first look for the gzipped file, which it will serve if present. Otherwise it will serve the 'normal' file.

This has many benefits: fewer bytes are read from the disk, a smaller memory footprint is needed in the server, less data copying happens, fewer bytes are sent across the network, etc.

You should not attempt to gzip already-compressed files, such as PNG, JPEG, SVGZ, etc.

Very small files (e.g. less than 1kb) gain little from compression because they may be small enough to fit within a single TCP packet, so don't bother with them. (They might even grow in size when gzipped.)

Conditional Request Support

The Assets handler sets 'Etag' headers for the responses of the assets it finds. Modern browsers need this: they are then able to send conditional requests that very often shrink responses to a simple 304 Not Modified. This improves the experience for users and leaves your server free to do more of other things.

The Etag value is calculated from the file size and modification timestamp, a commonly used approach. Strong or weak tags are used for plain or gzipped files respectively (the reason is that a given file can be compressed with different levels of compression, a weak Etag indicates there is not a strict match for the file's content).

For further information see RFC7232 https://tools.ietf.org/html/rfc7232.

Cache Control

To go even further, the 'far-future' technique can and should often be used. Set a long expiry time, e.g. ten years via `time.Hour * 24 * 365 * 10`. Browsers will cache such assets and not make requests for them for the next ten years (or whatever). Not even conditional requests are made. There is clearly a big benefit in page load times after the first visit.

No in-memory caching is performed server-side. This is needed less due to far-future caching being supported, but might be added in future.

For further information see RFC7234 https://tools.ietf.org/html/rfc7234.

Path Stripping

The Assets handler can optionally strip some path segments from the URL before selecting the asset to be served.

This means, for example, that the URL

http://example.com/e3b1cf/css/style1.css

can map to the asset files

./assets/css/style1.css
./assets/css/style1.css.gz

without the /e3b1cf/ segment. The benefit of this is that you can use a unique number or hash in that segment (chosen for example each time your server starts). Each time that number changes, browsers will see the asset files as being new, and they will later drop old versions from their cache regardless of their ten-year lifespan.

So you get the far-future lifespan combined with being able to push out changed assets as often as you need to.

SPA support

There is support for serving SPA webpage by using WithSPA() this serves index.html for all resources that do not have a file extension

Example Usage

To serve files with a ten-year expiry, this creates a suitably-configured handler:

assets := servefiles.NewAssetHandler("./assets/").StripOff(1).WithMaxAge(10 * 365 * 24 * time.Hour)

The first parameter names the local directory that holds the asset files. It can be absolute or relative to the directory in which the server process is started.

Notice here the StripOff parameter is 1, so the first segment of the URL path gets discarded. A larger number is permitted.

The WithMaxAge parameter is the maximum age to be specified in the cache-control headers. It can be any duration from zero upwards.

Index

Examples

Constants

View Source
const (
	Directory code = 0
	Continue  code = 100
	//OK                 code = 200
	//NotModified        code = 304
	Forbidden          code = 403
	NotFound           code = 404
	ServiceUnavailable code = 503
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Assets

type Assets struct {
	// Choose a number greater than zero to strip off some leading segments from the URL path. This helps if
	// you want, say, a sequence number in the URL so that only has the effect of managing far-future cache
	// control. Use zero for default behaviour.
	UnwantedPrefixSegments int

	// Set the expiry duration for assets. This will be set via headers in the response. This should never be
	// negative. Use zero to disable asset caching in clients and proxies.
	MaxAge time.Duration

	// Configurable http.Handler which is called when no matching route is found. If it is not set,
	// http.NotFound is used.
	NotFound http.Handler

	Spa bool
	// contains filtered or unexported fields
}

Assets sets the options for asset handling. Use AssetHandler to create the handler(s) you need.

func NewAssetHandler

func NewAssetHandler(assetPath string) *Assets

NewAssetHandler creates an Assets value. The parameter is the directory containing the asset files; this can be absolute or relative to the directory in which the server process is started.

This function cleans (i.e. normalises) the asset path.

Example
// A simple webserver

// where the assets are stored (replace as required)
localPath := "./assets"

// how long we allow user agents to cache assets
// (this is in addition to conditional requests, see
// RFC7234 https://tools.ietf.org/html/rfc7234#section-5.2.2.8)
maxAge := time.Hour

h := servefiles.NewAssetHandler(localPath).WithMaxAge(maxAge)

log.Fatal(http.ListenAndServe(":8080", h))
Output:

func NewAssetHandlerFS

func NewAssetHandlerFS(fs afero.Fs) *Assets

NewAssetHandlerFS creates an Assets value for a given filesystem.

Example
// A simple webserver

// where the assets are stored (replace as required)
fs := afero.NewOsFs()

// how long we allow user agents to cache assets
// (this is in addition to conditional requests, see
// RFC7234 https://tools.ietf.org/html/rfc7234#section-5.2.2.8)
maxAge := time.Hour

h := servefiles.NewAssetHandlerFS(fs).WithMaxAge(maxAge)

log.Fatal(http.ListenAndServe(":8080", h))
Output:

func (*Assets) ServeHTTP

func (a *Assets) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements the http.Handler interface. Note that it (a) handles headers for compression, expiry etc, and then (b) calls the standard http.ServeHTTP handler for each request. This ensures that it follows all the standard logic paths implemented there, including conditional requests and content negotiation.

func (Assets) StripOff

func (a Assets) StripOff(unwantedPrefixSegments int) *Assets

StripOff alters the handler to strip off a specified number of segments from the path before looking for the matching asset. For example, if StripOff(2) has been applied, the requested path "/a/b/c/d/doc.js" would be shortened to "c/d/doc.js".

The returned handler is a new copy of the original one.

func (Assets) WithMaxAge

func (a Assets) WithMaxAge(maxAge time.Duration) *Assets

WithMaxAge alters the handler to set the specified max age on the served assets.

The returned handler is a new copy of the original one.

func (Assets) WithNotFound

func (a Assets) WithNotFound(notFound http.Handler) *Assets

WithNotFound alters the handler so that 404-not found cases are passed to a specified handler. Without this, the default handler is the one provided in the net/http package.

The returned handler is a new copy of the original one.

func (Assets) WithSPA

func (a Assets) WithSPA() *Assets

WithSPA alters the handler so that all requestet files without a file extention instead return index.html

The returned handler is a new copy of the original one.

type Printer

type Printer func(format string, v ...interface{})

Printer is something that allows formatted printing. This is only used for diagnostics.

var Debugf Printer = func(format string, v ...interface{}) {}

Debugf is a function that allows diagnostics to be emitted. By default it does very little and has almost no impact. Set it to some other function (e.g. using log.Printf) to see the diagnostics.

Directories

Path Synopsis
This package provides an example webserver.
This package provides an example webserver.

Jump to

Keyboard shortcuts

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