capstan

package module
v0.0.0-...-152fb42 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2024 License: NCSA Imports: 40 Imported by: 3

README

Capstan: A Convenience Framework for Golang

Capstan is a framework that aims to simplify complex use cases that reach beyond basic HTTP handlers, such as dependency management, and reduces the amount of code required as handlers grow and evolve beyond what is possible with the standard library. It provides helpers for common (and not so common) tasks, including graceful shutdowns, rebinding mount points, and more. Capstan uses go-chi for its route muxer. Philosophically, Capstan is inspired in part by Flask and Flask-Classy.

Capstan is not for you if you require handlers that retain compatibility with net/http. Though this could (and arguably should) be rememdied by using the context package, there are no plans currently to reimplement Capstan in this fashion. Be ware! The Capstan API may appear simple at first blush, but it has a depth and complexity that may not be viable for all projects. If you need a simple API server, consider using go-chi directly.

Bear in mind that the project is still very much in its infancy, and APIs are subject to change. There are numerous rough patches and sharp objects in the code.

Features

Capstan's implementation comprises these features (and some anti-features):

  • Controller-centric MVC. At present, it's largely "VC" as there is no defined model implementation. Capstan is very much "bring your own model."
  • Requests are designed to call method-named functions per controller per path. This means that individual functions are named after and define an HTTP verb, such as Get or Post. Controllers therefore should be highly focused to a single endpoint. (This isn't strictly necessary; see below.)
  • Custom methods can be defined by implementing either the Binder interface, MapperHandler interface (work-in-progress), or by naming functions according to the pattern <Method><Name><SlashState>. This will become clearer later in the documentation once it's written.
  • Multiple renderers are supported through the Templater interface. We primarily encourage the use of pongo2 but provide wrappers for Go's built in template rendering library. (Note: Using anything but pongo2 may limit features available to templates, and using multiple template types in a single project will present unique challenges and difficulties that, while not insurmountable, are unnecessary and will be fraught with sleepless nights.)
  • Renderers may be defined globally or per-controller. If a renderer is defined globally, all controller bindings that don't define a renderer will use the global one by default.
  • Capstan's pongo2 wrappers can make use of our VFS layer that integrates with Embedder for embedding assets into the compiled binary. The template engine is therefore capable of reading templates and other assets directly from disk, the binary distribution, or from a cascade of VFS loaders with the option to override template sources at any point in the loader's process. ZIP files are also supported. As such, when combined with VFS, it's possible to layer reads from multiple sources (e.g. the binary, a ZIP file, or the file system).
  • Built-in TLS configuration helpers are provided. ACME support is a work in progress which will allow use of services like Let's Encrypt.
  • Capstan can listen on Unix sockets for use with local front end proxies like nginx or HAProxy. Doing so does not provide noticeable performance improvements over using TCP sockets. It may improve security somewhat by reducing your application's attack surface, particularly when launched behind a front-facing proxy (such as nginx or HAProxy).
  • Supports remapping endpoints (called "rebinding" in Capstan parlance) without restarting the service. Endpoints may be deleted (Router.Unmount) or renamed (Router.ReplacePath).
  • Optional websocket endpoint handlers as part of the controller semantics.
  • Graceful shutdown and in-place replacement/restart for seamless upgrades. Not presently supported on Windows and likely never will be. I don't know enough about Windows' internals to know if this is even possible.
  • URL mapper that exposes URL resolution for templates. In pongo2, this is provided via the functions url() and external(). URLs are typically mapped as [<prefix>.]<name>.<suffix>; for example, if you have a controller IndexController and were accessing its Get method, you would retrieve the mapped URL by calling url("IndexController:get"). Custom url() binding is provided by Router.ManualBind. The <prefix> is dictated by router groups or through manual declaration at controller bind time. Naming conflicts can be resolved during controller initialization manually, by providing a symbolic name for the controller, or automatically through a numeric suffix (e.g. IndexController2). If you're familiar with python-flask you'll be familiar with this mechanic.
  • Trailing slashes can be controlled via the ?, ! or + syntax when declaring controller configuration. ? indicates a trailing slash is optional. ! indicates a trailing slash must not be provided (and a 404 is generated). + indicates a trailing slash is mandatory for subroute declarations (but is typically unnecessary). Otherwise, if a route is declared with a trailing slash it is considered mandatory and requests made without the trailing slash will result in a 404. Optional slashes (indicated by a trailing ?) can either have a slash-provided or slash-absent default state; if a ? is immediately preceded by a slash, e.g. /?, a redirection route will be created that directs requests minus the trailing slash to a route that has the trailing slash appended. Likewise, if there is no slash preceding the ?, the redirection route will direct requests with a trailing / to the route without it. You should use optional slash routes sparingly since this does necessitate the generation of (potentially) superfluous routes and may present performance implications do to the additional route resolution required during the request cycle.
  • Typed route variables. This requires a syntax differing slightly from go-chi. Type enforcement is not presently implemented (but likely will be). We use < and > to delineate route variables and deliberately introduce incompatibilities with go-chi to prevent confusion. Variable types are declared with a :<type> suffix, e.g. /path/<name:string>, which would create a parameterized variable of the name name and type string. Regex is passed through to go-chi. Beware: go-chi may eventually be replaced by our own route resolver, hence another reason for introducing semantic incompatibilities.
  • Route groups wrap go-chi's groups and provide methods namespacing them from other routes both for url() template function(s) and for isolation.
  • Supports any middleware supported by go-chi and provides slight modifications for middleware supplied by go-chi (such as supporting the NO_COLOR envvar for go-chi's logger).
  • Supports before and after response bindings for cases where middleware may be too low level and where the route context needs to be fully configured prior to or after handling the endpoint.
  • Supports signals (albeit currently limited in scope) via signals (v2.0+) allowing for client applications to bind to specific events or modify the request state prior to processing. See signals.go via the master branch for a current list of supported signals. Signals are isolated per-application and application container but are exposed globally to make usage somewhat easier. Support is becoming more robust as Capstan matures.
  • Custom error page support with context helpers.
  • Extension support that includes a few out-of-the-box extensions, such as CSRF protection, session management, and a rudimetary authentication extension that should be viable for most circumstances.
  • Fully-featured dependency injection.
  • Limited support for privilege reduction; e.g., setting Capstan's RunAs configuration option and running initially as the root user will provoke Capstan into first binding to its configured ports then dropping privileges. This is useful for listening on privileged ports (<1024), such as port 80 and 443. Although Capstan is fully capable of hosting applications directly, it is advisable to use reverse proxies in front of a Capstan application.
  • Open port scanner for automatically picking open ports. Useful for writing installers that need to find a free port in order to start a visual browser-based installer.
  • Multiple application support for running instances of Capstan applications under the same server platform. It's Capstans all the way down!
  • The Capstan Server can be configured to use a default application to which all unmatched requests will be routed.
  • Capstan applications expose a ServeHTTP method thereby implementing the net/http.Handler interface and can be hosted under any Go HTTP server or framework that accepts such interfaces.
  • Capstan provides application containers for grouping together related applications or for isolating application groups from other applications.
  • Restartable application support is forthcoming; this will manage and maintain extension lifecycles alongside providing opportunities to dynamically reload application, extension, and related configurations.
  • Middleware can be utilized either per-application or at the Capstan server level. If applied to applications, middleware will only trigger when the application is handled; if applied to the server, middleware will trigger for every request.
  • Additional utilities. See the utils subpackage.

Sample Application

package main

import (
    "git.destrealm.org/go/capstan"
)

type IndexController struct {
    capstan.BaseController
    App capstan.Application `inject:"app"`
}

func (c *IndexController) Index(ctx capstan.Context) error {
    srv, _ := c.App.Server()
    return ctx.WriteString("Hello! Listening on address: "+srv.Config().ListenAddress)
}

func main() {
    srv := capstan.Defaults()
    app := srv.Application()

    app.Bind(&IndexController{
        BaseController: capstan.BaseController{
            Path: "/",
        },
    })

    srv.Listen()
}

Documentation

The Capstan source distribution contains documentation under the docs/ subdirectory. Presently, this documentation is somewhat sparse but the basic-usage.md file contains a beginner's guide adequate enough to get started. There is also a small but mostly complete discussion on Capstan's dependency injection framework.

License

Capstan is licensed under the fairly liberal and highly permissive NCSA license. We prefer this license as it combines the best of BSD and MIT licenses while also providing coverage for associated documentation and other works that are not strictly considered original source code. Consequently, all Capstan documentation is likewise covered under the same license as the codebase itself.

As with BSD-like licenses, attribution is, of course, required.

Documentation

Index

Constants

View Source
const (
	HostnameOverridesWhenEmpty hostnameBehavior = iota
	HostnameNeverOverrides
	HostnameAlwaysOverrides
)

Variables

View Source
var DefaultMappableNodeFunc = func() mappableNode {
	return &binaryTree{}
}

DefaultMappableNodeFunc is the default mappableNode generator used whenever a mappableNodeFunc has not been assigned to the radix trie.

View Source
var NewScanner = utils.NewScanner

NewScanner pseudo-alias for utils.NewScanner. Used by CLI to abstract away OS differences when scanning end-of-line ("\n" vs "\r\n").

View Source
var NoColor string

NoColor flag. If set to anything but the empty string, this will enable NO_COLOR processing.

View Source
var SigAfterResponse = signals.New((OnAfterResponse)(nil))

SigAfterResponse emits after the route has been handled and a response is in-flight but before any error handling code is processed.

This signal will only emit with an error condition if and only if there is no error prior to handling the route's endpoint. Error conditions raised by other signals, by connection upgrades, or redirects will not be caught by this signal. To catch all error conditions, use SigRouteError.

View Source
var SigApplicationInterrupt = signals.New((OnApplicationInterrupt)(nil))

SigApplicationInterrupt emits when the application has received an interrupt request. Such requests may be dispatched via the OS sending us a SIGINT or, if the host application overrides the DefaultInterruptHandler, this will emit when the host application decides to halt execution.

This signal emits before SigApplicationShutdownHook.

View Source
var SigApplicationReady = signals.New((OnApplicationReady)(nil))

SigApplicationReady emits when Application.Ready() is called but before any listener startup actually takes effect from the main application.. This is useful for extending Capstan's behavior just prior to the actual application startup and is triggered for every subapplication when initialization is complete.

View Source
var SigApplicationShutdownHook = signals.New((OnApplicationShutdownHook)(nil))

SigApplicationShutdownHook emits for each shutdown hook that is executed. This can be used to halt execution of a specific hook or to abort processing of all shutdown hooks.

View Source
var SigAttached = signals.New((OnAttached)(nil))

SigAttached emits when a new application is attached to the currently running Server instance.

View Source
var SigBeforeResponse = signals.New((OnBeforeResponse)(nil))

SigBeforeResponse emits after the response context and headers have been setup but before any potential protocol upgrades are made (e.g. WebSockets).

View Source
var SigBindRoute = signals.New((OnBindRoute)(nil))

SigBindRoute emits for each invocation of bindRoute (see funcs.go). This provides preliminary access to the router and muxer state prior to configuration.

View Source
var SigConnectionUpgraded = signals.New((OnConnectionUpgraded)(nil))

SigConnectionUpgraded emits when the route has been configured to produce a connection upgrade header, such as for WebSockets. This signal will not be raised if an error occurs during the upgrade process. To catch errors from a failed connection upgrade, you will need to bind to SigRouteError.

View Source
var SigContextWrite = signals.New((OnContextWrite)(nil))
View Source
var SigEachRoute = signals.New((OnEachRoute)(nil))

SigEachRoute emits for each method handler bound per route.

In Capstan's semantics, routes are defined individually by path *and* by method. For example, the end point "/demo" would have two separate routes initialized if its controller contains both Get and Post methods (one for each of GET and POST). This signal will emit separately for each of these.

Capstan uses this signal internally to expose the renderer's convenience function for attaching template functions and definitions.

This signal emits for routes configured with ManualBind.

View Source
var SigEndRouteHandler = signals.New((OnEndRouteHandler)(nil))

SigEndRouteHandler emits when the route has finished all code paths and is preparing to exit. It is not possible to modify the response, response headers, nor will changing any Route attributes provide expected results. This signal is exclusively for any cleanup code that needs to be run by extensions that have hooked into the route signaling.

View Source
var SigExtensionsPostInit = signals.New((OnExtensionsPostInit)(nil))
View Source
var SigManualRouteSetup = signals.New((OnManualRouteSetup)(nil))

SigManulRouteSetup emits when routes are manually bound using ManualBind().

This is an uncommon trigger since most routes will be configured via their controllers but some users may have a need to manually configure a route.

There is no way to differentiate between routes configured via a controller and routes configured via ManualBind; use this signal when catching the latter.

View Source
var SigRouteComplete = signals.New((OnRouteComplete)(nil))

SigRouteComplete emits for each route after the setup, bindRoute, and URL mapping has been finalized.

This signal is also emitted when manually bound routes are completed.

View Source
var SigRouteError = signals.New((OnRouteError)(nil))

SigRouteError emits immediately after an error condition has been raised but will not emit if the handled route's endpoint has returned nil (this is in contrast with the actual code which does process nil return values from route endpoints).

View Source
var SigRouteFlowControl = signals.New((OnRouteFlowControl)(nil))

SigRouteFlowControl emits just prior to handling router flow control events like redirects, context errors, or actual controller errors. This is somewhat anlogous to SigRouteError but happens before the actual error value itself has been extracted.

View Source
var SigRouteSetup = signals.New((OnRouteSetup)(nil))

SigRouteSetup emits after the route has been setup and method handlers have been configured.

View Source
var SigRouteWriteError = signals.New((OnRouteWriteError)(nil))

SigRouteWriteError emits when an error condition has been reached but prior to the error being written to the HTTP client. This signal may be used to modify the results of the generated error (such as customizing responses without having access to the template system or other internals or configurations).

View Source
var SigServerInterrupt = signals.New((OnServerInterrupt)(nil))

SigServerInterrupt emits when the server has received an interrupt request. Such requests may be dispatched via the OS sending us a SIGINT or, if the framework implementor overrides the DefaultInterruptHandler, this will emit when the framework implementor decides to halt execution.

This signal emits before SigApplicationShutdownHook.

View Source
var SigServerListening = signals.New((OnServerListening)(nil))

SigServerListening emits immediatley prior to the call site where the server begins listening to its configured socket(s).

View Source
var SigServerReady = signals.New((OnServerReady)(nil))

SigServerReady emits when the Server instance is ready is called but before any listener startup actually takes effect from the main application.. This is useful for extending Capstan's behavior just prior to the actual application startup and is triggered for every subapplication when initialization is complete.

View Source
var SigServerShutdownHook = signals.New((OnServerShutdownHook)(nil))

SigServerShutdownHook emits for each shutdown hook that is executed. This can be used to halt execution of a specific hook or to abort processing of all shutdown hooks.

View Source
var SigStartRouteHandler = signals.New((OnStartRouteHandler)(nil))

SigStartRouteHandler emits when the ServeHTTP Route method is called prior to any internal state handling and after the Context is first initialized. This can be used to modify route parameters prior to processing or modify the Context.

Functions

func AddPreLaunchFunc

func AddPreLaunchFunc(fn PreLaunchFunc)

func ArrayMapperFunc

func ArrayMapperFunc() mappableNode

ArrayMapperFunc returns a new, initialized arrayNode.

func AttachTemplateFunc

func AttachTemplateFunc(app Application, name string, fn interface{})

AttachTemplateFunc attaches a function or callable to the global template context using the provided name to the given application.

func AttachTemplateVar

func AttachTemplateVar(app Application, name string, val interface{})

AttachTemplateVar attaches a variable to the global template context with the specified name and value to the given application.

func BinaryTreeMapperFunc

func BinaryTreeMapperFunc() mappableNode

BinaryTreeMapperFunc returns a new binary tree.

func Defaults

func Defaults() *server

Defaults returns a Capstan instance with all configurates set to their default values and listening on port 8080 across all interfaces.

func InitNetwork

func InitNetwork() (errc chan error, close chan struct{}, err error)

func Listen

func Listen(ctx context.Context, network, addr string) (net.Listener, error)

func MakeID

func MakeID(s ...string) string

func New

func New(conf ServerConfig, appconf ApplicationConfig) *server

New returns a new Capstan server instance pre-initialized with a default application using appconf for its environment.

This is equivalent to calling NewServer() followed by SetDefaultApplication().

func NewApplication

func NewApplication(conf ApplicationConfig) *application

NewApplication generates a new Capstan application that can be bound to either a Capstan Server instance or directed through net/http as a handler.

func NewApplicationContainer

func NewApplicationContainer(config ApplicationConfig) *applicationContainer

func NewExtension

func NewExtension(name, provides string) *extension

NewExtension returns a new, mostly-unintialized extension containing the name and the service(s) provided by this extension. Metadata beyond the values controlled by this function's arguments must be filled in separately by the caller.

func NewMappedLoader

func NewMappedLoader(def http.Handler) *mappedLoader

NewMappedLoader returns a map-backed loader for multi-application support. Map-backed loaders can only map hostnames to handlers and cannot map base paths.

The application loader interface is defined as an internal API interface (see internal/api/application.go for ApplicationLoader).

func NewMappedNode

func NewMappedNode() *mappedNode

NewMappedNode returns a new, initialized mappedNode.

func NewMultiAppProxy

func NewMultiAppProxy(loader api.ApplicationLoader) *multiAppProxy

func NewProxyTrie

func NewProxyTrie() *proxyTrie

NewProxyTrie returns a root radix trie node.

func NewRadixLoader

func NewRadixLoader(def http.Handler) *radixLoader

NewRadixLoader returns a loader backed by a radix trie providing longest matching prefix support. If you need to match both the hostname and the base path for routing incoming requests per-application, use this loader.

Be aware that there are some limitations with assigning handlers via longest matching prefixes. In particular, the radix loader is NOT path aware, meaning that a request containing the hostname + base path assignment of "example.com/store" will, by its nature, also match "example.com/stores" and any derivative thereafter.

The intent behind this loader is to match the first hostname + base path segment of the incoming request, pass it along to the assigned application, and allow that application to determine whether the request should be handled or an error should be returned.

TODO: To fix the above, it should (eventually) be possible to assign chained loaders or multiple matches to a tree; this way, if the first (longest?) match returns an error, the next match in the tree can be tried, in order, until the tree is exhausted. This need not be a literal tree either; it could be a slice of returned matches.

func NewServer

func NewServer(addr string) *server

NewServer returns a Capstan server instance with no configured routers or applications.

Call the returned server instance's .Config() method to modify server attributes prior to .Run().

func NewTrieNode

func NewTrieNode(label rune, prefix string, fn mappableNodeFunc) *proxyTrie

NewTrieNode returns a new radix trie node for use within a radix trie root node.

func NewURLMapperContainer

func NewURLMapperContainer(container Attachable) *urlContainer

func ParseRoute

func ParseRoute(route string) *routeProps

ParseRoute processes the specified route, processes it, and returns a routeProps struct describing the route using go-chi syntax.

Route parsing is somewhat analogous to go-chi but uses a different syntax. In particular, route variables are delineated by "<" and ">" rather than "{" and "}" as per go-chi. Further, besides regex types, Capstan routes also define string, float, float32, float64, int, int32, and int64 types. Route syntax is:

String:

/<varname:string>

Integer types:

/<varname:int32>

Regular expressions:

/<varname:regex>

func Relaunch

func Relaunch() error

Relaunch the current binary inheriting all socket connection file descriptors.

This should be called from a goroutine. Parent/child process management is not handled by this function.

func Restart

func Restart() error

Restart the current binary without inheriting socket file descriptors. For blank slate reinitialization, this is the best method to call. The parent process environment, current working directory, command line arguments, and STDIN, STDOUT, and STDERR are inherited by the child process.

Consider using Router.Rebind() instead of Restart unless you need to listen on different ports.

Be aware that there appears to be a bug with fsnotify in that O_CLOEXEC on its file descriptors isn't honored. You should consider closing any fsnotify watchers that are active before this call if you don't want to leak descriptors.

This should be called from a goroutine. Parent/child process management is not handled by this function.

func StopListening

func StopListening(listener net.Listener) error

func URLForGen

func URLForGen(app Application, external bool) forGen

URLForGen returns a URLFor generator for each application. This will be attached to the global renderer first, if configured, and then to each application or application container.

Types

type Application

type Application interface {
	Attachable
	ApplicationSignaller

	// Init the application. Although implementation-dependent, if called
	// multiple times via the default Capstan application type, this will
	// re-initialize most application state.
	Init()

	// Destroy releases any resources associated with the application type and
	// assists the garbage collector in releasing referencers held to the
	// application.
	Destroy()

	// Bind a controller to the current application.
	Bind(controller Controller) error

	// BindGroup returns a RouteGroup bound to the specified `path`. This is
	// useful if you intend to group related routes together.
	BindGroup(path string) *RouterGroup

	// ReplaceController accepts the original controller, from, replacing it
	// with a new controller, to.
	ReplaceController(from, to Controller) error

	// ReplacePath `from` with a new path `to`. `from` must match the path
	// against which the controller was originally bound.
	ReplacePath(from, to string)

	// Unmount the specified path removing it from the router.
	Unmount(path string)

	// UnmountController from the router.
	UnmountController(controller Controller) error

	// UnmountGroup bound to the specified path.
	UnmountGroup(path string) error

	// AttachMiddleware to the current application. Middleware attached in this
	// manner will be applicable to all routes handled by this application.
	AttachMiddleware(MiddlewareFunc)

	// SetMiddleware on the current application to use the specified
	// http.Handler as part of the middleware chain.
	SetMiddleware(middleware ...func(http.Handler) http.Handler) Application

	// SetDefaultRenderer for the current application. The pongo2 renderer is
	// recommended.
	SetDefaultRenderer(renderer render.Renderer) Application

	// SetURLMapperFunc to the function specified. If this value isn't set,
	// NewURLMapper is used instead, which generates a basic URL-mapper specific
	// to a given application.
	SetURLMapperFunc(URLMapperFunc)

	// URLs returns a URLMapper instance associated with this application.
	URLs() URLMapper

	// Dependencies returns the Application's DependencyMapper.
	Dependencies() DependencyMapper

	// Extensions returns the Application's ExtensionManager.
	Extensions() *ExtensionManager

	// Logger returns the logger currently associated with the application.
	Logger() *logging.Log

	// SetLogger to the specified logger.
	SetLogger(logger *logging.Log)

	// Container returns the Attachable type to which this application instance
	// is bound. Will return nil if the application is not attached to a
	// container.
	Container() Attachable

	// SetContainer to the specified Attachable type.
	SetContainer(Attachable)

	// Router returns the router currently associated with the application,
	// exposing exported router-specific calls. This can be useful for
	// manipulatin routes directly.
	Router() *Router
}

type ApplicationConfig

type ApplicationConfig struct {
	// Name of the environment (or applciation host if using multiapp move).
	Name string

	// Version identifier.
	Version string

	// Description for application users (typically usage output).
	Description string

	// LogLevel default for all configured loggers.
	LogLevel logging.Level

	// EnvironmentMode enables or disables mode optimizations (such as
	// production mode) and controls default middleware configuration, such as
	// for error handling. This can also be configured via the
	// CAPSTAN_ENVIRONMENT or `ProductionEnvVar`.
	EnvironmentMode string

	// ModeEnvVar configures the environment variable toggle for
	// `EnvironmentMode`.
	ModeEnvVar string

	// DisableModeEnvVar disables the mode setting environment variable. If
	// true, the environment mode must be set via `EnvironmentMode` directly.
	DisableModeEnvVar bool

	// NoColor disables all ANSI control characters that generate color output.
	// This may be set externally or via the NO_COLOR environment variable.
	NoColor bool

	// WorkingDir configures the working directory for the application's default
	// VFS. If this is unset the current working directory will be used instead.
	WorkingDir string

	// NoDefaultVFS inhibits the application's initialization of a default VFS
	// based on the value of WorkingDir. Useful for applications that may be
	// distributed as a self-contained binary or those that are making use of a
	// ZIP file for application state.
	NoDefaultVFS bool

	// Hostname to use for the application's fully-qualified domain name. This
	// value is mostly used by the URL mapper to produce links that include the
	// HTTP scheme, host, and path rather than the path alone, but it will also
	// be used if TLSServerName is unset (minus any port declaration).
	//
	// If this value isn't set, ListenAddress or ListenAddressTLS is used
	// instead.
	Hostname string

	// RequireHostname for this application. If the hostname does not match the
	// connection is rejected with a 404.
	RequireHostname bool

	// DefaultHostname configures a hostname to use by default. If this is the
	// empty string, this will be set to "localhost." If AllowServerHostname is
	// true, this will be set to the listen address configured by the server (if
	// and only if the application instance is launched by capstan.Server).
	DefaultHostname string

	// BasePath enforces running the current application from the specified
	// subdirectory. Setting this will change *all* generated routes, including
	// those using url() from within templates, and redirections. This must be
	// set if you wish to have the application attached to a subdirectory, e.g.
	// example.com/app.
	BasePath string

	// Protocol is used in conjunction with Hostname to determine the FQDN for
	// the server. Ordinarily, this will be set automatically to HTTP or HTTPS.
	// To force HTTP or HTTPS, set this value.
	//
	// NoProtocolLinks (below) will override this setting, as will listening on
	// both standard HTTP and enabling TLS.
	//
	// This controls application behavior. To modify server behavior, set
	// ServerConfig.PreferTLS to true.
	Protocol string

	// NoProtocolLinks strips the scheme from generated FQDN links, e.g.
	// https://example.org/some/path would be rendered as
	// //example.org/some/path. If both ListenAddress and ListenAddressTLS are
	// specified, this option is automatically enabled.
	//
	// This controls application behavior only.
	NoProtocolLinks bool

	// RootLogger for the application. If this isn't set the capstan.Server log
	// will be used instead. If the application instance isn't being launched
	// via a capstan.Server this will use the go/logging root logger.
	RootLogger *logging.Log

	// ExtensionLogger overrides the extension manager's default logger (which
	// derives from the RootLogger of the application).
	ExtensionLogger *logging.Log

	// RequestLog for logging requests.
	RequestLog *logging.Log

	// RealIP middleware toggle. Handles either X-Forwarded-For or X-Real-IP
	// headers.
	RealIP bool

	// ProxyCount, specific to the host's environment, indicating how many
	// proxies exist between the Capstan application and the client. This allows
	// for correctly parsing the X-Forwarded-For header.
	ProxyCount int

	// Recoverer enables the go-chi recoverer middleware for panic management.
	// Automatically enabled if `Production` is true.
	Recoverer bool

	// Server configuration. This is populated when the application is attached
	// to a configured Capstan server either via capstan.New or when calling
	// AttachApplication() via a Capstan instance.
	Server *ServerConfig

	// ErrorConfig controls how Capstan handles errors.
	ErrorConfig *ErrorConfig

	// Session to use for this server instance. If this is nil, no session
	// backend will be configured and any calls made to Context.Session() will
	// return nil.
	Session *session.SessionConfig

	SetupFunc func(Attachable)
}

ApplicationConfig per-application. In stand alone mode, this will contain generalized configurations for each application. Otherwise, in multiapp mode, this will be used for each application. This allows Capstan to track configurations for each application hosted by a single Capstan instance.

type ApplicationContainer

type ApplicationContainer interface {
	Attachable

	DisableWithHandler(http.Handler)
	Enable()
	EnableWithHandler(http.Handler)

	Logger() *logging.Log
	SetLogger(logger *logging.Log)
}

type ApplicationSignaller

type ApplicationSignaller interface {
	// Signals returns the Application's signal container. Useful for plugins
	// that need to bind to specific events.
	Signals() *applicationSignals
}

type Attachable

type Attachable interface {
	// ID returns a unique, stable identifier for the current application.
	//
	// Note that this differs from Name() in that Name() returns a symbolic name
	// that may not be unique to the current application.
	//
	// Identifiers are derived from a combination of the application name and
	// its calculated host path. As such, identifiers are stable only insofar as
	// their configuration provided the domain and the mount point do not
	// change.
	ID() string

	// Name returns the application's symbolic name.
	//
	// Note that this differs from ID in that ID will return a unique, stable
	// identifier for the current application based on the application's host
	// path and name (if any).
	Name() string

	// Description returns a descriptive value for an application, such as its
	// purpose or what it does.
	//
	// For user-specific descriptive values, consider using the application meta
	// attributes.
	Description() string

	// Hostname returns the calculated hostname for the current application.
	Hostname() string

	HostnameFunc(PathSpecFunc)

	// Path returns the calculated path for the current application.
	Path() string

	// PathFunc adds a function for calculating the Attachable's returned
	// pathspec. Although implementation-dependent, this should affect the
	// output of HostPath() as well.
	//
	// This may override the output of Path() or may augment it. See
	// implementation for specific details.
	PathFunc(PathSpecFunc)

	// Protocol to use for the schema+FQDN when generating links via this
	// Attachable. This value honors the values of NoProtocolLinks and Protocol
	// from ApplicationConfig.
	Protocol() string

	// HostPath returns the full hostname + path for the current application.
	HostPath() string

	// HostPathFunc adds a function fo calculating the Attachable's returned
	// hostpathspec. Although implementation-dependent, this will usually
	// overwrite the default behavior of HostPath().
	HostPathFunc(PathSpecFunc)

	// Config returns the Application's current configuration.
	Config() *ApplicationConfig

	// Meta returns the full user metadata associated with this application.
	//
	// User metadata can contain things like symbolic names for an application,
	// user-specified descriptions, etc., which applications or application
	// hosts can use instead of the fixed name and description (used for
	// development).
	Meta() map[string]string

	// SetMeta sets a user metadata key to the specified value.
	SetMeta(key, value string)

	// MetaValue extracts the user metadata value associated with `key`.
	MetaValue(key string) string

	// Ready finalizes the Application state and readies it for servicing
	// requests.
	//
	// This will be called automatically when Application is attached to a
	// Server instance. When running Applications via services that are unaware
	// of its ready state, this is called during the first request serivced by
	// ServeHTTP.
	Ready()

	// ServeHTTP must be implmeented by Application types as they can be wrapped
	// by external tools.
	ServeHTTP(http.ResponseWriter, *http.Request)

	// SetServer configures the Application to reference the specified Server
	// instance as its parent. This is only applicable for servers that
	// implement the capstan Server interface.
	SetServer(Server)

	// Server returns the Server instance to which this Application is bound. If
	// the Application was *not* bound to a server instance, this will return
	// the tuple (nil, false).
	Server() (Server, bool)

	// RegisterShutdownHook to be called just before application shutdown.
	RegisterShutdownHook(hook ShutdownHook)

	// VFS returns the current application base path as a VFS-encapsulated file
	// system.
	//
	// If the application's base path is configured to point to a .zip file or
	// other supported archive, this will return a VFS-encapsulated immutable
	// file system.
	VFS() vfs.VFS

	// SetVFS configures the application to use the specified VFS layer.
	SetVFS(fs vfs.VFS)
}

type BaseController

type BaseController struct {

	// Middleware defined for this controller.
	Middleware Middleware

	// Name to assign to this controller. Leave empty to automatically deduce
	// the name from the implementing struct.
	Name string

	// Path against which this controller will bind its routes. This may have
	// special meaning for index and other methods (e.g. custom).
	Path string

	// Prefix to prepend to the controller's symbolic name. This may be useful
	// for namespacing controller references for reversing the URL in templates.
	Prefix string

	// OptionalSlash indicates the trailing slash is optional. It is usually
	// advised to set this via the Path component.
	OptionalSlash bool

	// Endpoints is a list of custom endpoints attached to this controller.
	Endpoints map[string]RouteFlag

	// MandatoryNoSlash indicates the trailing slash must not be present. It is
	// usually advised to set this via the Path component.
	MandatoryNoSlash bool

	// MandatorySlash indicates the trailing slash is required. It is usually
	// advised to set this via the Path component.
	MandatorySlash bool

	// SlashIsDefault indicates the presence or absence of the trailing slash is
	// the default state. This flag only holds meaning when OptionalSlash is set
	// to true. It is usually advised to set this via the Path component by
	// including a slash followed by a question mark ("/?") to indicate that the
	// slash is optional and the default redirection target.
	SlashIsDefault bool

	// NoInterpolation disables path interpolation and mandates the flags
	// (above) to configure the route slash states. This is useful for routes
	// where the special characters used to communicate slash behavior are
	// required as part of the route and should be accepted as literals.
	NoInterpolation bool

	// WebSocketPath, if set, will attach the WebSocket() method to the
	// specified path underneath the path defined for this controller.
	// Recommended.
	//
	// Leave this blank if your controller is intended strictly for websocket
	// handling.
	WebSocketPath string

	// Upgrader is the configured websocket upgrader via gorilla/websocket. This
	// provides both the configuration details for websocket connections in
	// addition to bound methods for upgrading the connection. If this is nil
	// and the controller implements the WebSocketHandler interface, the default
	// upgrader will be used instead.
	Upgrader *websocket.Upgrader

	// Renderer reference for this controller. This may be unique to each
	// controller or may be set globally via a helper.
	Renderer render.Renderer

	// Index path override. This may contain a path component, a single slash
	// indicating the path terminates with this index, a slash followed by a
	// question mark which indicates the slash is optional, or a slash followed
	// by an exclamation point indicating the route must not terminate with a
	// slash.
	Index string

	// Get overrides the default path for GET requests, appending the path
	// component specified here.
	Get string

	// Post overrides the default path for POST requests, appending the path
	// component specified here.
	Post string

	// Put overrides the default path for PUT requests, appending the path
	// component specified here.
	Put string

	// Patch overrides the default path for PATCH requests, appending the path
	// component specified here.
	Patch string

	// Delete overrides the default path for DELETE requests, appending the path
	// component specified here.
	Delete string

	// Head overrides the default path for HEAD requests, appending the path
	// component specified here.
	Head string
	// contains filtered or unexported fields
}

BaseController is the controller type used to pass options to the embedded controller struct used by callees.

func (BaseController) SetEndpoints

func (bc BaseController) SetEndpoints(ep map[string]RouteFlag)

func (BaseController) SetIndex

func (bc BaseController) SetIndex(p string)

func (BaseController) SetMandatoryNoSlash

func (bc BaseController) SetMandatoryNoSlash(s bool)

func (BaseController) SetMandatorySlash

func (bc BaseController) SetMandatorySlash(s bool)

func (BaseController) SetMiddleware

func (bc BaseController) SetMiddleware(mw Middleware)

func (BaseController) SetName

func (bc BaseController) SetName(name string)

func (BaseController) SetNoInterpolation

func (bc BaseController) SetNoInterpolation(s bool)

func (BaseController) SetOptionalSlash

func (bc BaseController) SetOptionalSlash(s bool)

func (BaseController) SetPath

func (bc BaseController) SetPath(p string)

func (BaseController) SetPrefix

func (bc BaseController) SetPrefix(p string)

func (BaseController) SetRenderer

func (bc BaseController) SetRenderer(r render.Renderer)

func (BaseController) SetSlashIsDefault

func (bc BaseController) SetSlashIsDefault(s bool)

func (BaseController) SetUpgrader

func (bc BaseController) SetUpgrader(up *websocket.Upgrader)

func (BaseController) SetWebSocketPath

func (bc BaseController) SetWebSocketPath(p string)

type Binder

type Binder interface {
	Bind(*Router)
}

Binder controllers are those that wish to implement or handle binding themselves. Consequently, a *Router is passed as the Bind() function's sole argument such that the controller may use it to interface directly with the router instance or may call Router.Mux() to use the muxer directly. If an implementation uses the latter, the underlying configured go-chi instance is exposed for external management.

type Config

type Config struct {
	ApplicationConfig
	ServerConfig
}

func (*Config) Application

func (c *Config) Application() *ApplicationConfig

func (*Config) Server

func (c *Config) Server() *ServerConfig

type ConnectHandler

type ConnectHandler interface {
	Connect(Context) error
}

ConnectHandler is called on a CONNECT request.

type ConnectionManager

type ConnectionManager interface {
	// AddListener to the current list of connections prior to service start.
	//
	// Cavaet emptor: Most listeners (TCP, UDP, UNIX domains) can be implemented
	// in conjunction with graceful restart support. However, not all features
	// may be available. If your platform does not implement syscall.Conn for a
	// given listener type, it may be necessary to implement this type yourself.
	AddListener(net.Listener)

	// AttachListener behaves similarly to AddListener with the exception that
	// it must be called on a previously running Server instance.
	//
	// Listeners attached via this function will be added to the list of active
	// listeners, running servers, will be configured with specific options
	// where appropriate (such as timeouts), and will dispatch a goroutine for
	// listening until service termination.
	AttachListener(net.Listener)

	// AttachNamedListener behaves similarly to AttachListener with the
	// exception that there are only three named types and calling this method
	// will overwrite previously configured listeners.
	//
	// Named listeners are defined as either "tls," "tcp," or UNIX domain
	// listeners which use the aliases "sock," "socket," or "file."
	//
	// At present "udp" is not supported but will be in the future.
	AttachNamedListener(string, net.Listener)

	// Close all configured listeners, gracefully terminating connections or
	// waiting ServerConfig.ShutdownTimeout seconds before closing active
	// connections.
	Close()

	// CloseNamed listeners using the same aliases as defined by
	// AttachNamedListener.
	CloseNamed(string) error

	// ListenHTTP creates a new net.Listener for standard unencrypted HTTP
	// connections. The argument should contain both the address and port.
	ListenHTTP(string) (net.Listener, error)

	// ListenSocket creates a new UNIX domain socket to use as a net.Listener.
	// The argument should be a path with write access to the process under
	// which Capstan will be running.
	ListenSocket(string) (net.Listener, error)

	// ListenTLS creates a new TLS-over-TCP/IP new.Listener, pre-configured with
	// safe cryptographic defaults and will use the certificates specified under
	// ServerConfig.
	ListenTLS(string) (net.Listener, error)

	// HasSocket returns true if Capstan has been configured to listen over a
	// UNIX domain socket.
	HasSocket() bool

	// HasTCP returns true if Capstan was configured to listen over TCP.
	HasTCP() bool

	// HasTLS returns true if Capstan was configured to listen over
	// TLS-over-TCP.
	HasTLS() bool
}

ConnectionManager defines connectivity-related APIs for controlling listeners and instructing a service to listen on a specific address.

Capstan's underlying server type defines these functions internally but they are not directly exposed via the Server interface. To extricate a ConnectionManager type from Server, it is necessary to call Server.Connections.

Concrete implementations of these functions are defined on the *server instance in server_listen.go.

type ContentType

type ContentType string

ContentType determines the content type for a request. This is used primarily by the Capstan Context entity to determine how the response should be generated.

const (
	TextPlain          ContentType = "text/plain"
	TextHTML           ContentType = "text/html; charset=utf-8"
	ApplicationJSON    ContentType = "application/json; charset=utf-8"
	URLEncodedFormData ContentType = "application/x-www-form-urlencoded"
	MultiPartFormData  ContentType = "multipart/form-data"
)

type Context

type Context interface {
	Float(string) (float64, error)
	FloatDefault(string, float64) float64
	Int(string) (int64, error)
	IntDefault(string, int64) int64
	Param(string) string
	ParamDefault(string, string) string
	Params(string) []string
	String(string) (string, error)
	StringDefault(string, string) string
	In(interface{}) error
	JSON(interface{}) error
	ParamTypes() map[string]string
	Render(string, interface{}) error
	Renderer() (render.ContextRenderer, error)
	SetRenderer(render.Renderer)
	Headers() http.Header
	Method() string
	Request() *http.Request
	Response() http.ResponseWriter
	MetaResponse() MetaResponseWriter
	RequestContext(key string, value interface{})
	Route() *Route
	SetCookie(*http.Cookie)
	WebSocket() *websocket.Conn
	SetWebSocket(*websocket.Conn)
	Session() Session
	URLFor(string) *URLBuilder
	Write([]byte) (int, error)
	WriteHeader(int)
	WriteJSON(interface{}) error
	WriteString(string) error
	ContentType(ContentType)
	HasError() error
	Panic(string)
	Error() string
	Unwrap() error
	SetError(error)
	Code() int
	SetCode(int)
	AttachErrorFunc(func(Context) error)
	AttachSuccessFunc(func(Context) error)
	Context() context.Context
	// contains filtered or unexported methods
}

func MakeContext

func MakeContext(w http.ResponseWriter, r *http.Request) Context

MakeContext returns an "empty" Context instance suitable for use within middleware and other sources, provided it is populated with an http.ResponseWriter and http.Request. In this case, Type is the empty string, and ParamTypes is an empty map.

func MakeRouteContext

func MakeRouteContext(w http.ResponseWriter, r *http.Request, rt *Route, app Application) Context

MakeRouteContext creates a new Context using the specified Route as its source of truth for internal values like parameter types, the internal route handler, and the Context's renderer.

This is primarily used by Router as its context generator function.

type Controller

type Controller interface {
	SetMiddleware(Middleware)

	SetName(string)

	SetMandatoryNoSlash(bool)

	SetMandatorySlash(bool)

	SetOptionalSlash(bool)

	SetSlashIsDefault(bool)

	SetNoInterpolation(bool)

	SetPath(string)

	SetPrefix(string)

	SetIndex(string)

	SetWebSocketPath(string)

	SetUpgrader(*websocket.Upgrader)

	SetRenderer(render.Renderer)

	SetEndpoints(map[string]RouteFlag)
	// contains filtered or unexported methods
}

type DeferStack

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

func (*DeferStack) Add

func (s *DeferStack) Add(fn func(Context))

type DeleteHandler

type DeleteHandler interface {
	Delete(Context) error
}

DeleteHandler is called on a DELETE request. See the Context interface for convenience methods for reading the request body (including JSON requests).

type DependencyMapper

type DependencyMapper interface {
	// Apply dependencies to the target interface type.
	Apply(target interface{})

	// ApplyLater applies the dependency map to the specified target when Bind()
	// is called at a later point.
	ApplyLater(target interface{})

	// Bind the delayed-load dependencies attached via ApplyLater().
	Bind()

	ClearDelayed()

	// Copy returns a complete copy of this DependencyMapper.
	Copy() DependencyMapper

	// Destroy all resource handles associated with this DependencyMapper.
	Destroy()

	// Map the dependendency to the specified name.
	Map(name string, dependency interface{})

	// Register the dependency as the specified name.
	Register(name string, dependency interface{})

	// SetErrOnNil causes the dependency mapper to panic if a dependency is
	// requested via a tag or type that has not been configured in the
	// dependency mapper or if the type is nil.
	//
	// By default, if a dependency hasn't been configured or was deliberately
	// set to nil, the struct target will likewise be set as nil. Since this can
	// introduce surprising bugs, it may be useful to enable this feature during
	// development to catch common mistakes like misspelled `inject` tags or
	// missing dependencies.
	SetErrOnNil(value bool)
}

func NewDependencyMapper

func NewDependencyMapper() DependencyMapper

NewDependencyMapper returns a new DependencyMapper pre-initialized with the appropriate internal data types. This function should be used in lieu of creating the type and initializing it manually, particularly since Capstan's API is likely to remain in a state of flux for some time.

func NewDependencyMapperOpts

func NewDependencyMapperOpts(logger *logging.Log) DependencyMapper

type Endpoint

type Endpoint func(Context) error

Endpoint defines functions that serve as Capstan endpoints. These are usually members of a struct and there's currently no provision for creating "bare" endpoints.

type ErrorConfig

type ErrorConfig struct {
	// DisableJSON errors. If true, this will return the default error template
	// regardless of the content type.
	DisableJSON bool

	// DisableErrorHandling. If true, Capstan will return only the error code.
	// Useful for configurations that expect to handle error pages via a
	// frontend proxy, like HAProxy or nginx.
	DisableErrorHandling bool

	// DisableBacktrace will inhibit printing the backtrace whenever an HTTP 500
	// is encountered, which is performed using specialized middleware.
	//
	// Backtrace print support is only enabled if the CAPSTAN_ENVIRONMENT (or
	// configuration) is set to "development" and this value is not set to true.
	DisableBacktrace bool

	// ForceContentType will forcibly switch the content type of the error
	// response to the content-type string specified here. This is useful for
	// API servers that will always respond with a specific content type (e.g.
	// application/json) or if you don't want the content type to be adjusted
	// dynamically.
	ForceContentType string

	// LogErrors, if true, will log all errors passed to or created by the
	// context.
	LogErrors bool

	// ErrorMap maps byte slices to their respective error codes. This is useful
	// for simple error pages that require no additional logic.
	ErrorMap map[int][]byte
}

type Extension

type Extension interface {
	// Name for this extension; symbolic.
	Name() string

	// Provides indicates the dependency fulfilled by this extension.
	Provides() string

	// Requires indicates which dependency or dependencies must be loaded prior
	// to this extension.
	Requires() []string

	// Init this extension. This is called after the extension has been loaded;
	// for extensions that have dependencies on other extensions, this is called
	// after the dependency has been loaded. Circular dependencies will result
	// in a panic.
	Init(app Application) error
}

type ExtensionDeinitializer

type ExtensionDeinitializer interface {
	// Deinit this extension. This is called before Capstan finalizes all
	// connections and shuts down. Extensions that implement this interface will
	// be given an opportunity to perform teardowns required.
	//
	// De-initialization methods will be called from separate goroutines and
	// will block application exit until all Deinit() functions have returned.
	Deinit(app Application)
}

type ExtensionManager

type ExtensionManager struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

func NewExtensionManager

func NewExtensionManager(app Application) *ExtensionManager

func (*ExtensionManager) ByName

func (m *ExtensionManager) ByName(name string) Extension

ByName returns the extension registered by `name`.

If no such extension is registerd, this will return nil.

func (*ExtensionManager) Init

func (*ExtensionManager) Register

func (m *ExtensionManager) Register(extension Extension) error

Register the supplied extension.

func (*ExtensionManager) Reload

func (m *ExtensionManager) Reload(app Application)

func (*ExtensionManager) Restart

func (m *ExtensionManager) Restart(app Application)

func (*ExtensionManager) Start

func (m *ExtensionManager) Start(app Application)

func (*ExtensionManager) Stop

func (m *ExtensionManager) Stop(app Application)

func (*ExtensionManager) WhoProvides

func (m *ExtensionManager) WhoProvides(provides string) Extension

WhoProvides returns the extension that provides the feature set described by `provides`.

If no such extension is registered, this will return nil.

type GetHandler

type GetHandler interface {
	Get(Context) error
}

GetHandler is called on a GET request. See the Context interface for convenience methods that may be used for extricating GET or URL/positional parameters.

type HTTPServer

type HTTPServer struct {
	http.Server
	// contains filtered or unexported fields
}

func (*HTTPServer) Listener

func (h *HTTPServer) Listener() net.Listener

func (*HTTPServer) Serve

func (h *HTTPServer) Serve(listener net.Listener) error

type HeadHandler

type HeadHandler interface {
	Head(Context) error
}

HeadHandler is called on a HEAD request.

type IndexHandler

type IndexHandler interface {
	Index(Context) error
}

IndexHandler is a special type of handler that responds to the route's index. The index may be automatically determined based on the last path component that does not contain a positional variable. For example, the route `/auth/<user:string>` would map a given IndexHandler to the path `/auth/`.

IndexPath can be used to override the location of the path utilized by this handler.

type InterruptHandler

type InterruptHandler func(func(), chan struct{})

InterruptHandler types wrap an interrupt channel that is used to cancel Capstan's listener loop. These can be triggered by system signals or the likes.

InterruptHandler accepts two arguments: The first is the callback function to fire on interrupt; the second is a channel that serves as an alternative interrupt request for shutdown notifications that originated from sources other than signals.

type Listener

type Listener interface {
	net.Listener
	syscall.Conn
}

type ListenerTLSConfig

type ListenerTLSConfig struct {
	// CertPath is the location of a TLS certificate on the file system.
	CertPath string
	// KeyPath is the location of the server's private key on the file system.
	KeyPath string
	// CertBytes is a PEM-encoded certificate.
	CertBytes []byte
	// KeyBytes is a PEM-encoded private key.
	KeyBytes []byte
}

type MapValues

type MapValues = render.MapValues

MapValues is a top-level pseudo-alias for Capstan's wrapper type render.MapValues wrapping the pongo2 Context.

type MapperHandler

type MapperHandler interface {
	Mapper() map[string]RouteMap
}

MapperHandler returns a map of a route (map key) and the endpoint + method (RouteMap) it should be bound to. MapperHandlers are controllers that define their own route-to-endpoint handling routines.

type MetaResponseWriter

type MetaResponseWriter interface {
	http.ResponseWriter
	BytesTotal() int
	Code() int
	Context() Context
	Error() error
	HasError() bool
	SetContext(Context)
	SetError(error)
}

func NewMetaResponseWriter

func NewMetaResponseWriter(w http.ResponseWriter, r *http.Request) MetaResponseWriter

type Middleware

type Middleware chi.Middlewares

Middleware is a pseudo-alias for go-chi's awkwardly-named "Middlewares."

type MiddlewareExtension

type MiddlewareExtension interface {
	Extension

	// Middleware should return any middleware required by this extension.
	Middleware() []func(http.Handler) http.Handler
}

type MiddlewareFunc

type MiddlewareFunc func(http.Handler) http.Handler

MiddlewareFunc defines the type used by middleware declarations. This is provided mostly for convenience as middleware will generally define the returned handler function directly rather than assigning it to a type.

type MiddlewareHandler

type MiddlewareHandler interface {
	Middleware() []func(http.Handler) http.Handler
}

MiddlewareHandler, when implemented, allows controllers to return middleware that utilizes attached fields and methods as internal context. This is useful if your middleware requires access to a database or other dependencies (including injected dependencies).

type MultiAppProxyHandler

type MultiAppProxyHandler interface {
	ProxyHandler
	Loader() api.ApplicationLoader
}

MultiAppProxyHandler defines the interface to expose for types supporting multi-application loading.

type NoopController

type NoopController struct {
	BaseController
}

func (*NoopController) Connect

func (c *NoopController) Connect(ctx Context) error

func (*NoopController) Delete

func (c *NoopController) Delete(ctx Context) error

func (*NoopController) Get

func (c *NoopController) Get(ctx Context) error

func (*NoopController) Head

func (c *NoopController) Head(ctx Context) error

func (*NoopController) Index

func (c *NoopController) Index(ctx Context) error

func (*NoopController) Options

func (c *NoopController) Options(ctx Context) error

func (*NoopController) Patch

func (c *NoopController) Patch(ctx Context) error

func (*NoopController) Post

func (c *NoopController) Post(ctx Context) error

func (*NoopController) Put

func (c *NoopController) Put(ctx Context) error

func (*NoopController) Trace

func (c *NoopController) Trace(ctx Context) error

type OnAfterResponse

type OnAfterResponse func(Context, *DeferStack, signals.Context, error)

OnAfterResponse accepts the Capstan Context and a possible error value. Note that the error value may be nil, capstan.Context, capstan/errors.Redirect, capstan/errors.Internal, or of error.

type OnApplicationInterrupt

type OnApplicationInterrupt func(Application)

OnApplicationInterrupt receives a copy of the current application (this is the master instance) followed by the WaitGroup currently responsible for holding the interrupt state from exiting the listener loop.

type OnApplicationReady

type OnApplicationReady func(Application)

OnApplicationReady accepts the current application at the time of call. Return values from subordinate callees are ignored.

type OnApplicationShutdownHook

type OnApplicationShutdownHook func(Application, ShutdownHook) signals.Context

OnApplicationShutdownHook accepts the current application followed by the currently executing shutdown hook. This signal must also return a context.

If the signal context returns an abort code, execution of the current shutdown hook will not proceed.

If the signal context returns a stop code (with MustStop), no shutdown hooks will be executed.

type OnAttached

type OnAttached func(Attachable, Server)

OnAttached accepts two arguments: The current application, and the Server instance to which the Application was attached.

type OnBeforeResponse

type OnBeforeResponse func(Context, *Route, *DeferStack, signals.Context)

OnBeforeResponse accepts the Capstan Context and Route. If a signals.Context value is returned and its Error() value is set with a code >= 400, this will skip immediately to the route's error handling.

type OnBindRoute

type OnBindRoute func(*Router, *Route, chi.Router) signals.Context

OnBindRoute accepts as its arguments the router context, the route currently being processed, and the assigned muxer.

type OnConnectionUpgraded

type OnConnectionUpgraded func(Context, signals.Context)

OnConnectionUpgraded accepts the capstan.Context and its state immediately after a successful upgrade.

type OnContextWrite

type OnContextWrite func(Context, []byte)

type OnEachRoute

type OnEachRoute func(*Router, *Route)

OnEachRoute accepts the router context and the Route currently being processed. This is triggered before bindRoute is called (meaning the Route is not yet attached to any muxer) and before the route is completely configured.

type OnEndRouteHandler

type OnEndRouteHandler func(*Route, Context, signals.Context)

OnEndRouteHandler accepts a capstan.Route, capstan.Context, and the current signals.Context as returned by OnStartRouteHandler.

The capstan.Route cannot be modified in this phase.

type OnExtensionsPostInit

type OnExtensionsPostInit func(*ExtensionManager, Application)

type OnManualRouteSetup

type OnManualRouteSetup func(*Router, *Route)

type OnRouteComplete

type OnRouteComplete func(*Router, *Route)

OnRouteComplete accepts the router context and the Route currently processed.

type OnRouteError

type OnRouteError func(*Route, Context, signals.Context, error)

OnRouteError accepts the capstan.Route, capstan.Context, and the error value encountered at the time of the error.

Error returns from calls to this signal *always* indicate an error condition caused by an error value returned by the source controller. Such errors can be those returned directly by typical error handling (strict Go `error` type) or by errors wrapped with the go/error package whose .Code() is unset.

As there is presently no clear way to determine whether the underlying error condition absolutely is from a software failure or being used by the controller for flow control, this signal should be used with a degree of caution if the intent is to catch *all* flow control events.

type OnRouteFlowControl

type OnRouteFlowControl func(*Route, Context, signals.Context, error)

OnRouteFlowControl accepts the capstan.Route, capstan.Context, and the error value encountered at the time of the error.

Error returns from routes do not always indicate an error condition. The error type must be checked to see if it is also of type capstan.Context, capstan/errors.Redirect, capstan/errors.Internal, or of type error.

If an error condition has occurred and a capstan.Context type was returned, it may be necessary to introspect the Context.Code() value for a value >= 400 as well as Context.HasError(). If a capstan.Context has been returned and there is an error condition, both Context.HasError() and Context.Error() will return useful information.

type OnRouteSetup

type OnRouteSetup func(*Router, []*Route)

OnRouteSetup accepts the router context and a slice of all Route structs derived from the controller as it is processed. Routes will be generated for each HTTP method, each special struct method, and any bind routes that were configured at runtime.

type OnRouteWriteError

type OnRouteWriteError func(*Route, Context, error) signals.Context

OnRouteWriteError accepts the capstan.Route, capstan.Context, and the error condition that caused the return status.

This signal may return a signals.Context. If the signals.Context returns an error, the error value initialize passed to the route error writer will be replaced with the contents of this error and any modifications made to capstan.Conext may be reflected in the returned response. If either MustAbort() or MustStop() are set on the signals.Context, the error writer will exit without writing anything.

Setting MustStop() is suggested if callers plan to perform their own error writing directly. MustAbort() is not guaranteed to produce expected results in the near term and its semantics in the function calling OnRouteWriteError may change.

type OnServerInterrupt

type OnServerInterrupt func(Server, sync.WaitGroup)

type OnServerListening

type OnServerListening func(Server)

OnServerListening accepts the current server instance at the time of call.

type OnServerReady

type OnServerReady func(Server)

OnServerReady accepts the current application at the time of call. Return values from subordinate callees are ignored.

type OnServerShutdownHook

type OnServerShutdownHook func(Server, ShutdownHook) signals.Context

OnServerShutdownHook accepts the current server followed by the currently executing shutdown hook. This signal must also return a context.

If the signal context returns an abort code, execution of the current shutdown hook will not proceed.

If the signal context returns a stop code (with MustStop), no shutdown hooks will be executed.

type OnStartRouteHandler

type OnStartRouteHandler func(*Route, Context, *DeferStack, signals.Context)

OnStartRouteHandler accepts the Capstan Route, Context, *DeferStack, and a signals.Context. If the signals.Context returned here has MustAbort flagged, this will immediately pass control to the route's error handling without making any changes to the context values as will happen if signals.Context's Error is set. Setting MustAbort() is useful if the caller has modified the capstan.Context directly to reflect a specific error or redirect condition but does not wish for the signal to overwrite these values.

Only call signals.Context.SetError() if you actually intend for the signal handler to set an error condition on the request.

Access to the raw net/http.ResponseWriter and net/http.Request can be had through the Context.Response() and Context.Request() methods.

type OptionsHandler

type OptionsHandler interface {
	Options(Context) error
}

OptionsHandler is called on an OPTIONS request.

type PassthroughFunc

type PassthroughFunc func(Context) http.Handler

type PassthroughHandler

type PassthroughHandler interface {
	Passthrough(http.ResponseWriter, *http.Request)
}

PassthroughHandler handles all HTTP methods, passing them through to the implementation of the Passthrough method defined by the handler.

BaseController definitions for per-method path locations are not honored by the PassthroughHandler.

type PatchHandler

type PatchHandler interface {
	Patch(Context) error
}

PatchHandler is called on a PATCH request. See the Context interface for convenience methods for reading the request body (including JSON requests).

type PathSpecChain

type PathSpecChain struct {
	Func PathSpecFunc
	Next *PathSpecChain
}

type PathSpecFunc

type PathSpecFunc func(Attachable, string) string

type PostHandler

type PostHandler interface {
	Post(Context) error
}

PostHandler is called on a POST request. See the Context interface for convenience methods for reading the request body (including JSON requests).

type PreLaunchFunc

type PreLaunchFunc func() error

type ProxyHandler

type ProxyHandler interface {
	// ServeHTTP allows ProxyHandler to implement http.Handler.
	ServeHTTP(http.ResponseWriter, *http.Request)

	// Switch the current router to a new router instance.
	Switch(chi.Router)
}

ProxyHandler is the interface that must be implemented by types that intend to be used as proxy handlers. Proxy handlers may be comparatively simple, such as the `proxy` type, or they may be more complex and implement host-lookup functionality such as the `multiAppProxy` type.

Proxies are required in order to support rebinding and endpoint deletion since go-chi doesn't currently allow us to overwrite or delete endpoints directly. So, what we do instead, is to regenerate the go-chi bindings when a rebind or endpoint deletion is requested, call Switch() on the proxy, and "switch" to the new chi.Router.

Multiple proxies are arranged in a hierarchical structure, such as for multiapp support. In this case, the multiapp proxy handles dispatching requests based on the incoming domain, path, or domain + path, and then passes it to the underlying `proxy` which performs the rest of the work. This allows us to Switch() on a per-application bases, as required, while still supporting multiple applications within the same Capstan-hosted instance.

type PutHandler

type PutHandler interface {
	Put(Context) error
}

PutHandler is called on a PUT request. See the Context interface for convenience methods for reading the request body (including JSON requests).

type ReadyExtension

type ReadyExtension interface {
	Ready(Application) error
}

ReadyExtension must be implemented by extensions that support ready-state notification and further initialization, inferring that it may depend on other extenions to be initialized prior to finalizing its startup.

Ready() is always called on extensions that implement this interface after all registered extensions have had their Init() method called. Further, if an extension declares a list of dependencies in its Requires() slice, these dependencies must also be available before Ready() is called (otherwise Ready() is skipped).

type ReloadFunc

type ReloadFunc func(Application)

ReloadFunc is a function type that is passed in to AddReloadFunc and invoked on application Reload().

type Reloadable

type Reloadable interface {
	// Reload the application. This reinitializes the application's state in
	// addition to calling any reload functions.
	Reload()

	// AddReloadFunc attaches a function to invoke on Reload(). This can be used
	// for reconfiguring the application, reloading configurations from source
	// files, or reconfiguring application attributes.
	AddReloadFunc(ReloadFunc)
}

type ReloadableExtension

type ReloadableExtension interface {
	Reload(Application)
}

ReloadableExtension types support a Reload() method that will be called when the application itself has been informed of a reload. Reloads may indicate changes of platform or application configuration. If extensions expose their own separate configuration, these should be re-read when Reload() is called.

type Restartable

type Restartable interface {
	// Restart is typically an alias to Stop() followed by Start() but is
	// provided as a separate function for Restartables in the event special
	// treatment is required for a proper restart.
	Restart()

	// Start is an initializer that should be called whenever the subordinate
	// service is to be started. This may apply to applications, application
	// containers, and extensions.
	Start()

	// Stop is a de-initializer that should be called whenever the subordinate
	// service is to be stopped. This may apply to applications, application
	// containers, and extensions.
	Stop()
}

type RestartableExtension

type RestartableExtension interface {
	Restart(Application) error
	Start(Application) error
	Stop(Application) error
}

RestartableExtensions are extensions that can be started, stopped, or restarted independent of the application state (see ReloadableExtension).

type Route

type Route struct {

	// Name of this handler. This is a symbolic name
	Name string

	// Path derived from route descriptor.
	Path string

	// CapPath contains the original Capstan-formatted path for this route.
	CapPath string

	// BasePath as indicated by the router. This is used by the URL builder but
	// may provide informational context to controllers indicating that this
	// route isn't mounted at the site root.
	BasePath string

	// CapBasePath is similar to BasePath with the exception that it contains
	// Capstan-formatted path information.
	CapBasePath string

	// MandatoryNoSlash strips any trailing slash from the route and creates a
	// single route with no trailing slash. This enforces the absense of a
	// slash. No redirection may occur from routes with a trailing slash to
	// routes without a slash when flagged with this feature.
	//
	// Mandatory absense of a slash is enforced when the route is defined with a
	// trailing exclamation point, e.g. "/route!". Such routes may also include
	// a trailing slash to ensure the meaning is clearer (e.g. "not slash" or
	// "/route/!").
	MandatoryNoSlash bool

	// MandatorySlash is the precise opposite of MandatoryNoSlash: Routes are
	// required to terminate with a trailing slash and no redirection may occur
	// from routes without a slash.
	//
	// Routes with a mandatory slash must terminate with a trailing slash, e.g.
	// "/route/".
	MandatorySlash bool

	// OptionalSlash creates two separate routing table entries: One with a
	// trailing slash and one without. If the route ends with a slash, this is
	// considered the route's default state, and requests to this route without
	// a trailing slash will be redirected to a route of the same name with the
	// slash appended. Likewise, the converse is true.
	//
	// Optional slashes may be delineated with a terminating question mark, e.g.
	// "/route/?" for routes that will redirect to a trailing slash or "/route?"
	// for those that do not. The question mark itself is optional for routes
	// defined without a trailing slash and may be omitted, e.g., "/route" and
	// "/route?" define the same behavior. The question mark is advisable to
	// make clear the developer's intention, and may be required in some
	// contexts (such as defining the behavior of an index route).
	//
	// Note: A trailing slash, e.g. "/route/" indicates that the route must
	// terminate with a *mandatory* slash. Hence, a terminating ? is required
	// when defining a default redirection state from "/route" to "/route/",
	// e.g. "/route/?".
	//
	// This section will be clarified in the documentation.
	OptionalSlash bool

	// SlashIsDefault indicates that the default route state is to terminate
	// with a slash. The value of this flag is only applicable if OptionalSlash
	// is true.
	SlashIsDefault bool

	// RouteTimeout indicates this route should enforce a timeout of the
	// specified duration, in seconds. If ForceTimeout is true, the handler will
	// exit after RouteTimeout seconds. If ForceTimeout is false, then timeout
	// handling must be managed by the controller (and will be ignored
	// otherwise).
	RouteTimeout int

	// ForceTimeout will forcibly exit the handler after RouteTimeout seconds or
	// do nothing until the controller endpoint managed by this route exists of
	// its own volition.
	ForceTimeout bool

	// Paths for multiple routes that may be handled by the same controller.
	Paths map[string][]string

	// ContentType is a convenience mechanism for setting the content type of
	// the route if it is one of text/plain, text/json, or application/json. You
	// should use this instead of setting the headers directly.
	//
	// This value is mostly intended for API endpoints that generate JSON
	// output.
	ContentType string

	// Handler for passthrough routes. If this is non-nil, this will override
	// all other configurations.
	Handler http.HandlerFunc

	// Headers used as route defaults.
	Headers http.Header

	// Prefix to prepend to the handler's name to limit naming collisions when
	// reverse-mapping URLs.
	Prefix string

	// Suffix to append to the handler's name. This will typically be the
	// request method but may be overridden here. If this value isn't set, the
	// request method is used instead.
	Suffix string

	// ParamTypes maps parameter names to their type for use by *Param() context
	// functions.
	ParamTypes map[string]string

	// Method associated with this route.
	Method string

	// Middleware for this route.
	//
	// TODO: Eventually support redeclaration of middleware types such that it
	// accepts a Context. This will require also wrapping external middleware,
	// such as that which ships with chi and others.
	Middleware []func(http.Handler) http.Handler

	// Endpoint function.
	Endpoint Endpoint

	// Controller reference.
	Controller Controller

	// Upgrader for websocket connections.
	Upgrader *websocket.Upgrader

	// Renderer defines a controller-specific renderer to use for this handler.
	// If this is nil, the global renderer will be used instead.
	Renderer render.Renderer
	// contains filtered or unexported fields
}

Route descriptor ultimately used for constructing chi routes. This encapsulates the actual user code handler and passes in the appropriate arguments.

func (*Route) Copy

func (rt *Route) Copy() *Route

Copy and return a new instance of the current route.

Beware: Non-value types are only copied as pointers to their original values. This means that maps local to the Route struct are not copied by value.

func (*Route) ForName

func (rt *Route) ForName() string

ForName returns the symbolic name of the current route.

func (*Route) FullPath

func (rt *Route) FullPath() string

func (*Route) ServeHTTP

func (rt *Route) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP provides an HTTP handler implementation that makes routes compatible with Go's default HTTP server.

This function is somewhat messy and handles the entirety of Capstan's request handling logic. Refactoring is welcome.

func (*Route) WriteError

func (rt *Route) WriteError(ctx Context, err error)

type RouteFlag

type RouteFlag struct {
	Method   string
	Endpoint string
}

type RouteMap

type RouteMap struct {
	Method   string
	Endpoint Endpoint
}

RouteMap is currently unused. It will be includes as part of the Mapper() handler for controllers that define their own route map.

type Router

type Router struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

func (*Router) BasePath

func (r *Router) BasePath() string

BasePath returns the base path for the given router.

The application path is ignored for routers that are contained by a router group.

TODO: Validate whether the group's base path needs to be considered.

func (*Router) Bind

func (r *Router) Bind(endpoint Controller) error

Bind the specified endpoint.

This method is the core of the router and handles all method routes directly and farms out other route types to separate functions (also located in this file). Bind() will also setup routes and map them.

The use of route.Copy() may appear confusing at first blush, but its purpose is to create a copy of (most of) the route and its data, which is then used to bind a method/endpoint tuple with go-chi. route.Copy() only performs a shallow copy; slices and map pointers are replicated across all dependents.

func (*Router) Group

func (r *Router) Group(path string) *RouterGroup

Group creates and returns a new router group.

func (*Router) ManualBind

func (r *Router) ManualBind(method, path, suffix string, controller Controller, endpoint Endpoint)

ManualBind is used with Bind() routes to manually bind a given `endpoint` to the specified `path` with the HTTP request `method` and URLFor `suffix` provided. The controller must also be specified to correctly assemble the route.

func (*Router) Middleware

func (r *Router) Middleware(middleware ...func(http.Handler) http.Handler) *Router

Middleware configures Router-global middleware for all routes bound to this Router.

func (*Router) Mux

func (r *Router) Mux() chi.Router

Mux returns the chi.Router mux associated with this Router instance.

func (*Router) Proxy

func (r *Router) Proxy() ProxyHandler

Proxy returns the ProxyHandler associated with this Router instance.

func (*Router) Rebind

func (r *Router) Rebind() chi.Router

Rebind() incrementally rebinds all routes associated with this Router. This is useful when remapping a route's endpoint. A new muxer is returned with all the bindings set. This function should be used with the proxy Switch method.

When a Rebind is called, the routes are not immediately rebound. Instead, a new muxer is created with the same configuration already used by the existing muxer and all routes associated with this Router are bound, individually, to this muxer. Only when the routes have successfully been bound will the muxer be returned.

FIXME: This needs to work across subrouters as well.

func (*Router) ReplacePath

func (r *Router) ReplacePath(from, to string)

ReplacePath swaps a route endpoint for the route associated with `from` to the new endpoint `to`. Rebind is triggered if this call is successful.

This method also switches the proxy handler to the one returned by Rebind.

func (*Router) ReservedFunctions

func (r *Router) ReservedFunctions(reserved ...string)

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, rq *http.Request)

func (*Router) SetProxy

func (r *Router) SetProxy(p ProxyHandler)

SetProxy changes the proxy for this router.

func (*Router) SetRenderer

func (r *Router) SetRenderer(renderer render.Renderer)

SetRender configures a global render.Renderer for all endpoints handled by this router. This may be overridden by renderer specified via the BaseController options for any given route. If this is never set, the Context.Render() becomes a noop and returns the noop implementation of Renderer.

func (*Router) SetupMuxer

func (r *Router) SetupMuxer()

func (*Router) Static

func (r *Router) Static(urlroot, path string)

Static binds the path described by `urlroot` to the filesystem `path` as a source for hosting static assets.

Being as this makes use of the Golang default http.FileServer implementation, it's worth considering a reverse proxy for better performance.

func (*Router) StaticVFS

func (r *Router) StaticVFS(urlroot, path string, fs vfs.FS)

Static binds the path described by `urlroot` to the filesystem `path` as contained in the virtual filesystem implementation `fs` as a source for hosting static assets. This should work with any VFS implementation compatible with vfs.FileSystem but is primarily intended to work with Embedder.

Being as this makes use of the Golang default http.FileServer implementation, it's worth considering a reverse proxy for better performance.

func (*Router) URLs

func (r *Router) URLs() URLMapper

URLs retrieves the Router's configured URLMapper.

func (*Router) Unmount

func (r *Router) Unmount(path string)

Unmount removes the route associated with `path` from the router. This calls Rebind if successful.

This method also switches the proxy handler to the one returned by Rebind.

type RouterGroup

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

func NewRouterGroup

func NewRouterGroup(path string, parent *Router) *RouterGroup

func (*RouterGroup) Add

func (rg *RouterGroup) Add(fn func(*Router))

func (*RouterGroup) BasePath

func (rg *RouterGroup) BasePath() string

func (*RouterGroup) ForName

func (rg *RouterGroup) ForName() string

ForName returns the symbolic name of the current router group.

TODO: This would need to be modified if support for group nesting is ever supplied.

func (*RouterGroup) Header

func (rg *RouterGroup) Header() http.Header

func (*RouterGroup) Middleware

func (rg *RouterGroup) Middleware(middleware chi.Middlewares)

func (*RouterGroup) Name

func (rg *RouterGroup) Name(name string) *RouterGroup

func (*RouterGroup) Options

func (rg *RouterGroup) Options() *RouterOptions

func (*RouterGroup) Rebase

func (rg *RouterGroup) Rebase(path string)

func (*RouterGroup) Rebind

func (rg *RouterGroup) Rebind() chi.Router

func (*RouterGroup) RebindSelf

func (rg *RouterGroup) RebindSelf()

func (*RouterGroup) ReplacePath

func (rg *RouterGroup) ReplacePath(from, to string)

func (*RouterGroup) WithRenderer

func (rg *RouterGroup) WithRenderer(renderer render.Renderer) *RouterGroup

type RouterOptions

type RouterOptions struct {
	ContentType string
}

type Server

type Server interface {
	// Attach to the current server instance.
	Attach(Attachable)

	// AttachMiddleware to the current server instance. This middleware will
	// execute regardless of the attached applications.
	AttachMiddleware(MiddlewareFunc)

	// Destroy attached to the current server instance. This removes
	// it from the application roster and the internal routing table.
	Destroy(Attachable)

	// Application returns the master/default application. When running in
	// multi-application mode, this will return the master application. When
	// running in standalone mode, this will return the default application.
	Default() Attachable

	// SetDefault to which all requests not destined for a previously
	// mapped location (subdomain or path) will be routed.
	SetDefault(Attachable)

	// Get retrieves the specified application or application container.
	Get(name string) (Attachable, bool)

	// Get application retrieves the specified application, returning the tuple
	// (nil, false) if the specified application name doesn't exist or is not an
	// application.
	GetApplication(name string) (Application, bool)

	// Config returns the current server configuration.
	Config() *ServerConfig

	// Connections returns the current server type as a ConnectionManager
	// interface. This interface is defined as part of the Capstan API (see
	// sources).
	Connections() ConnectionManager

	// Each iterates over each application registered to this server instance
	// and invokes the callback function `fn` which should accept the
	// Application as its only argument.
	Each(fn func(Attachable))

	// Signals returns a pointer to the signals container associated with this
	// server instance.
	Signals() *serverSignals

	// RegisterShutdownHook registers the specified hook to invoke when the
	// Server is shutting down. This hook is invoked when the server is
	// terminated via SIGINT, SIGTERM, or via graceful restart when the parent
	// process has been terminated.
	RegisterShutdownHook(hook ShutdownHook)

	// Logger returns the logging instance associated with the Server.
	Logger() *logging.Log

	// Ready is invoked whenever the server has reached its Ready state. This
	// may be called to preemptively trigger a ready state for all attached
	// applications.
	Ready()

	// Listen instructs the server to start listening.
	Listen() error

	// Stop may be called to interrupt a running server programmatically.
	Stop()

	// With configures a context to use for server shutdown.
	With(context.Context) Server

	// Reload accepts a ServerConfig instance, reconfigures the service, and
	// reinitializes it. Changes to listener sockets will not take effect unles
	// the Server has restarted.
	Reload(config *ServerConfig)
}

type ServerConfig

type ServerConfig struct {
	// ListenAddress controls what host/IP and port combination the application
	// will listen on.
	//
	// Leaving this value as the empty string will disable listening support for
	// standard HTTP connections.
	ListenAddress string

	// ListenAddressTLS controls what host/IP and port combination the
	// application will listen on for TLS-enabled connections.
	//
	// Leaving this value as the empty string will disable TLS support.
	//
	// When using multi-app mode with multiple domains, Capstan doesn't
	// currently allow for hosting multiple certificates directly. This means
	// you will either need to generate a certificate where the subjectAltName
	// contains all of the domains hosted behind a multiapp instance or you will
	// need to run Capstan behind a reverse HTTP proxy like nginx.
	ListenAddressTLS string

	// PreferTLS controls URL scheme generation. If true, links to
	// Capstan-hosted applications will be prefixed with "https" if
	// ListenAddressTLS is set.
	PreferTLS bool

	// ListenSocket controls whether or not Capstan will listen on a UNIX domain
	// socket for incoming connections. This may be useful for controlling
	// access to the application when running a proxy (such as nginx) on the
	// same host. Domain sockets should only be used when proxying connections
	// to a Capstan application and generally don't improve performance.
	ListenSocket string

	// Context may be used to override the default context.Context for the
	// configured server instance. If this value is nil, the context used by the
	// server will be context.Background.
	Context context.Context

	// RunAs the specified user. If this value is specified, Capstan will engage
	// additional checks to validate that the application is running as a user
	// with elevated permissions and then drop these permissions to run as the
	// user specified here.
	//
	// Additionally, if it appears the ListenAddress or ListenAddressTLS are
	// declared to use privileged ports, a separate net.Listener will be created
	// prior to dropping privilege.
	RunAs string

	// RunAsGroup specifies the group under which the Capstan application will
	// run. If this is unset (the empty string) Capstan will attempt to run the
	// application as the RunAs user's default group.
	RunAsGroup string

	// PIDFile, if set, enables support for writing the process ID to the
	// specified location and will enable support for additional tooling if
	// used.
	PIDFile string

	// SIGHUPFunc, if set, calls the configured function if a SIGHUP is
	// received. Linux/BSD/macOS only.
	SIGHUPFunc func()

	// ReloadOnSIGHUP will reload the process on SIGHUP if true by forking the
	// process and passing its descriptors to the child, effectively reloading
	// the entire process using the existing configuration. If this is false,
	// SIGHUP is ignored. Regardless of this setting, SIGHUPFunc will be called
	// whenever a SIGHUP is received and SIGHUPFunc is not nil.
	ReloadOnSIGHUP bool

	// ParentClosesOnSIGUSR1, if true, will signal the child process with
	// SIGUSR1 when the parent has closed.
	ParentClosesOnSIGUSR1 bool

	// SocketReloadEnvvar defines the environment variable to examine for
	// graceful restart and reloads. If this value isn't set CAPSTAN_SOCKETS
	// will be used instead.
	SocketReloadEnvvar string

	// InterruptHandler defines a function that should be used to handle
	// interrupt events.
	//
	// By default, this uses the internal api.DefaultInterruptHandler to install
	// a handler for SIGINT support. If you're calling Capstan from another
	// application that already installs such an interrupt handler, you will
	// want to override this.
	InterruptHandler InterruptHandler

	// Certificates defines the certificate key pairs to use for TLS
	// connections. These must match Hostname values for all applications
	// configured in this instance (wildcards are acceptable).
	//
	// When using multi-application mode, it is preferrable to run Capstan
	// behind a proxy like nginx.
	Certificates []tls.Certificate

	// MinVersion for TLS. The default is TLS1.1 if this value is zero, as per
	// https://www.globalsign.com/en/blog/disable-tls-10-and-all-ssl-versions/
	MinVersion uint16

	// MaxVersion for TLS. Default is TLS1.3.
	MaxVersion uint16

	// TLSConfig allows overriding the deduced TLS configuration using
	// individual options (above) for TLS setup.
	TLSConfig *tls.Config

	// HeaderTimeout instructs the server to wait HeaderTimeout seconds when
	// waiting to read an incoming request header. Default: 60 seconds.
	HeaderTimeout int

	// IdleConnectionTimeout limits how long an HTTP keep-alive connection may
	// be held open for repeat connections. Default: 10 seconds. This may
	// eventually be set dynamically.
	IdleConnectionTimeout int

	// MaxRequestDuration imposes an upper bound on how many seconds a single
	// handler is allowed to process a request before it is terminated. This may
	// require cooperation from the handler in order to interrupt potentially
	// long-running code. Failure to do so may "leak" goroutines by allowing
	// attackers to repeatedly spawn process-intensive code that ignores context
	// cancellation. Default: 30 seconds.
	MaxRequestDuration int

	// MaxHeaderLength defines the maximum request header size, in bytes. By
	// default, this is 1MiB if otherwise unset..
	MaxHeaderLength int

	// ShutdownTimeout dictates how long the server will wait for connections to
	// close (or complete) until terminating. Default: 10 seconds.
	ShutdownTimeout int

	// RequestTimeout enables the request timeout middleware if set to a
	// non-zero value.
	RequestTimeout int

	// Loader informs Capstan which application loader should be used. By
	// default, this will be a radix trie implementation that matches the
	// longest prefix between both domain and path components. Provided is an
	// alternative, and simpler, implementation that is backed by a map but does
	// not provide longest prefix matching.
	//
	// If Capstan is invoked via capstan.New this is a no-op until
	// AttachApplication is called.
	Loader api.ApplicationLoader

	// Hostname for this server instance.
	//
	// This value is usually ignored (see OverrideHostname below) but can be
	// used to override the value of the application hostname().
	//
	// If the Attachable type is not an application and is instead an
	// application container, the same rules apply.
	Hostname string

	// OverrideHostname configures the server instance to override application
	// hostnames based on: HostnameNeverOverrides, HostnameOverridesWhenEmpty,
	// or HostnameAlwaysOverrides.
	//
	// If this value is set to HostnameOverridesWhenEmpty, the application
	// hostname will be changed if it is set to the emptystring. This is the
	// default.
	//
	// If this value is set to HostnameNeverOverrides the application hostname
	// will be left alone.
	//
	// If this value is set to HostnameAlwaysOverrides, this will always replace
	// the application hostname with the one associated with this server
	// instance.
	//
	// The server hostname is first derived from the value of Hostname; if this
	// is unset then the hostname will be derived from the value of
	// ListenAddress (minus the port).
	OverrideHostname hostnameBehavior

	// Logger sets the default logger for this server instance. If this value is
	// nil, then Capstan will configure the logger itself.
	Logger *logging.Log

	// LogAllRoutingErrors instructs the server to log *all* routing errors for
	// every application to which it is bound.
	LogAllRoutingErrors bool
}

ServerConfig controls basic server configuration, such as the listening host and port, as well as a few per-application (when running in multiapp mode) tweaks, such as domain name and TLS support.

func (*ServerConfig) CalculatedListenAddress

func (sc *ServerConfig) CalculatedListenAddress() string

CalculatedListenAddress returns the calculated address for Capstan to listen on. If this wasn't set by the caller, this will return a sensible default value. It is up to the caller whether this default should be used (or not).

func (*ServerConfig) CalculatedListenAddressTLS

func (sc *ServerConfig) CalculatedListenAddressTLS() string

CalculatedListenAddressTLS returns the calculated address for Capstan to listen on via TLS. If this wasn't set by the caller, this will return a sensible default value. It is up to the caller whether this default should be used (or not)k

func (*ServerConfig) CalculatedProtocol

func (sc *ServerConfig) CalculatedProtocol() string

CalculatedProtocol returns the anticipated HTTP scheme based on the values of ListenAddressTLS and PreferTLS. If both ListenAddressTLS is set and PreferTLS is true, this will return "https;" otherwise "http" is returned.

type Session

type Session interface {
	// Add a key to the session. Depending on the backend, this may call
	// Restore(). This should not be used with the serializer methods, and
	// implementations should enforce such behavior.
	Add(key string, value interface{})

	// Get a key from the session.
	Get(key string) interface{}

	// GetString returns `key` from the session as a string. If the value cannot
	// be coerced into a string type the empty string is returned instead.
	GetString(key string) string

	// Serialize data to the session. Useful for storing predefined structs,
	// etc. Backends may use whatever serialization method they prefer, but most
	// will probably marshal the input to JSON. This method should NOT be used
	// in conjunection with Add() or Get().
	//
	// Serialize() is provided for more strongly typing session data.
	Serialize() ([]byte, error)

	// Unserialize data from the session. The same caveats apply here that apply
	// to Serialize(), and this should not be used in conjunection with sessions
	// that are using Add() and Get().
	Unserialize(string) error

	// Restore sessions using Add() and Get(). Restore() isn't called by the
	// session registry as it is up to implementations to decide precisely how
	// restoration functionality should interact with the serializers.
	Restore() map[string]interface{}

	// Save the session or its metadata to a cookie. For cookie-backed sessions,
	// this will save the entire session state to a cookie; for others, this
	// should save a unique identifier that allows the session to be restored
	// from elsewhere.
	//
	// This method will be called in the BeforeResponse handler to ensure that
	// cookie data is written out to the HTTP headers and is called regardless
	// of persistence method (Add/Get vs Serialize/Unserialize).
	Save()
}

type SessionBackend

type SessionBackend = session.SessionBackend

type SessionConfig

type SessionConfig = session.SessionConfig

type SessionFactory

type SessionFactory func(Context) Session

type SessionHandler

type SessionHandler interface {
	Session(Context) Session
	SetFactory(SessionFactory)
}

SessionHandler defines an interface that must be implemented by extensions that implement (or re-implement) the built in session.

type ShutdownHook

type ShutdownHook func() error

ShutdownHook types are registered to be called whenever the application is terminated normally.

type TraceHandler

type TraceHandler interface {
	Trace(Context) error
}

TraceHandler is called on a TRACE request.

type URLBuilder

type URLBuilder struct {
	External bool
	Scheme   string
	Host     string
	// contains filtered or unexported fields
}

func (*URLBuilder) Asset

func (b *URLBuilder) Asset(asset string)

func (*URLBuilder) Encode

func (b *URLBuilder) Encode() string

func (*URLBuilder) Param

func (b *URLBuilder) Param(name, value string) *URLBuilder

Param attaches a parameter to the internal parameters map. Parameters that are named in the URL pathspec will be expanded as path components; others will be treated as query string parameters.

func (*URLBuilder) Params

func (b *URLBuilder) Params(params url.Values) *URLBuilder

Params overwrites the internal parameters map with the one specified. Parameters that are named in the URL pathspec will be expanded as path components; others will be treated as query string parameters.

func (*URLBuilder) SetQuery

func (b *URLBuilder) SetQuery(query string) error

type URLMapper

type URLMapper interface {
	For(string) *URLBuilder
	Has(*Route) bool
	Map(*Route)
	Remove(*Route)
	SetStatic(string)
}

func NewURLMapper

func NewURLMapper(app Application) URLMapper

NewURLMapper returns a new urlMapper configured to use the specified config. We use a pointer to config.Config as some changes to URL behavior may be set dynamically depending on how the application is running. For example, enabling just HTTP or TLS will switch the protocol scheme between "http" and "https," respectively, or enabling both will remove the scheme entirely and use "relative" protocols (e.g. "//example.org/some/path").

type URLMapperFunc

type URLMapperFunc func(Application) URLMapper

URLMapperFunc returns a function that generates a new URLMapper instance.

type VFS

type VFS interface {
}

type VFSFileSystem

type VFSFileSystem struct {
}

type VFSFilebox

type VFSFilebox struct {
}

type VFSPassThrough

type VFSPassThrough struct {
}

type VFSRegistry

type VFSRegistry struct {
	Registry map[string][]VFS
	// contains filtered or unexported fields
}

VFSRegistry works by storing available virtual file systems and a series of upgrade/downgrade listeners. As a new VFS is added as an upgrade, metadata related to it is passed to all registered upgraders which may then elect to utilize the new VFS (or not). If an error or panic occurs when attempting to access a VFS, the downgrade callbacks are triggered for that VFS automatically and the VFS reverts to its prior state.

As an example, assume you have an application that is distributed with migration scripts for a database embedded into the binary. The VFS, by default, will utilize these migrations by default. However, also assume that you provide migration upgrades that can occur out-of-band from the binary. When you distribute these upgrades, your application can instruct the VFSRegistry to upgrade the migrations listener, which will pass along another VFS instance that reads from the updated .zip. This may be configured as a passthrough VFS that will first attempt to read files from the .zip and then fall back to those integrated into the binary.

Another example: Assume you download a new theme that extends one of the core templates distributed with your binary. You need access to both the new theme's .zip and the integrated templates; you can have a VFS passthrough that will read from both the .zip and the built-in theme separately, allowing you to upgrade the new theme with add on .zips. If a new add on .zip doesn't work to your liking, you could remove it, and when the VFS layer panics from the missing file, it will downgrade itself until it either reverts to one of the previous .zip files (my-theme-v2.zip to my-theme-v1.zip, for example) or until it reverts to reading from the binary directly.

Alternatively, you may also set up priorities such that files that exist in the current working directory can override those contained in archives or vice-versa.

func (*VFSRegistry) OnDowngrade

func (r *VFSRegistry) OnDowngrade(fn downgradeFunc, passthrough bool)

func (*VFSRegistry) OnUpgrade

func (r *VFSRegistry) OnUpgrade(fn upgradeFunc, passthrough bool)

type VFSZipFile

type VFSZipFile struct {
}

type WebSocketHandler

type WebSocketHandler interface {
	WebSocket(Context) error
}

WebSocketHandler receives websocket upgrade requests. This is largely a convenience option that wraps gorilla/websocket.

Directories

Path Synopsis
cmd
capstan Module
examples
auth Module
telemetry Module
api
lib
sys

Jump to

Keyboard shortcuts

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