Documentation
¶
Overview ¶
Package http provides an HTTP server comprised of multiple HTTP services
Index ¶
- Constants
- func AppleAppSiteAssociationHandler(cfg *AppleAppSiteAssociationConfig, opts ...Option) http.HandlerFunc
- func LivenessHandler(opts ...Option) http.Handler
- func ReadinessHandler(registry healthcheck.Registry, opts ...Option) http.Handler
- func RegisterHTTPServer(i do.Injector, serviceName string)
- func RootLevelAssetsHandler(assetsDir string) http.HandlerFunc
- func VersionHandler(opts ...Option) http.Handler
- type APIServer
- type AppleAppSiteAssociationConfig
- type Config
- type Option
- type Server
Constants ¶
const ( // LivenessPath answers whether the process is alive. It is deliberately the // cheapest handler in the module: a liveness probe that consults a // dependency turns that dependency's outage into a restart loop, which is // the one response guaranteed not to fix it. LivenessPath = "/healthz" // ReadinessPath answers whether the process should be sent traffic, which is // the question the health registry exists to answer. A component reporting // down takes the process out of the load balancer's rotation and leaves it // running, so it can recover and rejoin. ReadinessPath = "/readyz" // VersionPath serves the build metadata the binary was stamped with. VersionPath = "/version" )
const AppleAppSiteAssociationPath = "/.well-known/apple-app-site-association"
AppleAppSiteAssociationPath is the well-known path iOS fetches to discover a domain's Universal Link configuration. Apple requires it be served over HTTPS with no redirects and a JSON content type.
See https://developer.apple.com/documentation/xcode/supporting-associated-domains.
Variables ¶
This section is empty.
Functions ¶
func AppleAppSiteAssociationHandler ¶
func AppleAppSiteAssociationHandler(cfg *AppleAppSiteAssociationConfig, opts ...Option) http.HandlerFunc
AppleAppSiteAssociationHandler returns an http.HandlerFunc that serves the apple-app-site-association document described by cfg. Register it at AppleAppSiteAssociationPath; NewHTTPServer does so automatically when Config.AppleAppSiteAssociation is enabled, so this is only needed to serve the document from somewhere else (a different mux, a CDN origin, etc).
A config that is empty or malformed yields a handler that responds 404, so callers never have to branch on whether the feature is configured.
func LivenessHandler ¶
LivenessHandler reports that the process is up, and nothing else.
It is exported so a service that serves its probes from its own mux — one that binds a separate operational listener, say — gets the same answers this server would have given.
func ReadinessHandler ¶
func ReadinessHandler(registry healthcheck.Registry, opts ...Option) http.Handler
ReadinessHandler runs every checker in the registry and reports the aggregate: 200 when all of them are up, 503 when any is down. The body is the per-component breakdown either way, so an operator reading the probe's response learns which component is the reason.
A nil registry checks nothing and therefore always reports ready, which is what a registry with no checkers registered would have said too.
The checks run on the request's context, so a probe that gives up disconnects the checks with it, and each individual check is bounded by the registry's own timeout.
func RegisterHTTPServer ¶
RegisterHTTPServer registers a Server with the injector. The serviceName parameter is passed directly rather than injected, since string is too generic a type to resolve unambiguously from the injector.
The server it builds serves the operational routes: VersionPath always, and the two probe paths when a healthcheck.Registry is registered. This is the wire-it-all-up path, so it opts into what a hand-built server is asked to opt into — a caller who wants the probes elsewhere, or not at all, calls NewHTTPServer with the options it wants instead.
func RootLevelAssetsHandler ¶
func RootLevelAssetsHandler(assetsDir string) http.HandlerFunc
RootLevelAssetsHandler returns an http.HandlerFunc that serves static files from assetsDir. It only serves root-level files (no subdirectories) and guards against path traversal. Register with router.Get("/*", RootLevelAssetsHandler(assetsDir)) as the last route.
func VersionHandler ¶
VersionHandler serves the build metadata from the version package, which is whatever the binary's -ldflags stamped in and "unknown" for whatever they did not.
Types ¶
type APIServer ¶
type APIServer struct {
// contains filtered or unexported fields
}
APIServer is our API http server. It is exported, and returned by NewHTTPServer, so a caller can depend on the server it built rather than on the Server seam — matching server/grpc, whose NewGRPCServer has always returned its own *Server.
func NewHTTPServer ¶
func NewHTTPServer( ctx context.Context, serverSettings *Config, router *routing.Router, opts ...Option, ) (*APIServer, error)
NewHTTPServer builds a new server instance.
serverSettings may be nil, which is treated as a zero-valued Config. The config is validated: a server that never binds because Port was unset should fail here rather than at the first request that does not arrive.
The service name comes from WithServiceName, not a positional argument, so it sits with the other observability wiring — and matches the gRPC sibling.
WithHealthRegistry and WithVersionEndpoint mount the operational routes on the router's backend, outside the OpenAPI document. Without them the router is left exactly as it was handed over.
func (*APIServer) Serve ¶
Serve serves HTTP traffic until Shutdown is called or ctx is done.
It returns the failure rather than panicking through a hard-wired panicker: a library cannot decide that a bind failure should take the host process down, and a caller that wants that can still do it from the returned error. A graceful close reports nil.
func (*APIServer) Shutdown ¶
Shutdown drains in-flight requests and flushes the spans they produced.
It flushes but does not shut the tracer provider down. The provider was handed to this server, not built by it, and shutting it down closes the exporter for everything else that shares it: the gRPC sibling that is still draining, and every background loop whose Close runs after ingress stops. A server that shut it down made itself the last thing in the process that could be traced, which is the opposite of what a shutdown wants — the shutdown is the part worth tracing. Whoever built the provider shuts it down; observability.Pillars.Shutdown is that owner, and service.Service runs it last.
type AppleAppSiteAssociationConfig ¶
type AppleAppSiteAssociationConfig struct {
// TeamID is the Apple Developer Team ID (e.g. "ABCD1234XY").
TeamID string `env:"TEAM_ID" json:"teamID,omitempty" yaml:"teamID,omitempty"`
// BundleID is the iOS app bundle identifier (e.g. "com.example.ios").
BundleID string `env:"BUNDLE_ID" json:"bundleID,omitempty" yaml:"bundleID,omitempty"`
// Paths restricts which URL paths open the app, as Apple component patterns
// (e.g. "/invitations/*"). Empty grants every path on the domain, which is what a
// service with no opinion wants; set it when only part of the site should deep-link
// into the app.
Paths []string `env:"PATHS" json:"paths,omitempty" yaml:"paths,omitempty"`
// WebCredentials adds the webcredentials service to the document, which is what
// lets iOS offer Password AutoFill and shared credentials for this domain. It is
// off by default: a domain claims it only when the app's entitlements list a
// matching "webcredentials:" associated domain.
WebCredentials bool `env:"WEB_CREDENTIALS" json:"webCredentials,omitempty" yaml:"webCredentials,omitempty"`
// contains filtered or unexported fields
}
AppleAppSiteAssociationConfig holds the configuration for the apple-app-site-association file iOS uses for Universal Links. It is optional: when TeamID and BundleID are both empty the file is not served at all, so services without an iOS app are unaffected. When either is set, both are required.
func (*AppleAppSiteAssociationConfig) Enabled ¶
func (cfg *AppleAppSiteAssociationConfig) Enabled() bool
Enabled indicates whether the apple-app-site-association file should be served, which requires both identifiers to be present and well-formed. A malformed config reports disabled here and an error from ValidateWithContext, so a service that skips validation serves nothing rather than a document iOS would reject.
func (*AppleAppSiteAssociationConfig) ValidateWithContext ¶
func (cfg *AppleAppSiteAssociationConfig) ValidateWithContext(ctx context.Context) error
ValidateWithContext validates an AppleAppSiteAssociationConfig struct. An entirely empty config is valid (the feature is simply off); a partially filled one is not.
Setting any field counts as intent to serve the document, including Paths or WebCredentials alone. That matters because those two are inert without the identifiers: a config that scopes paths but whose TeamID never made it out of the environment would otherwise validate clean and then quietly serve nothing.
type Config ¶
type Config struct {
// AppleAppSiteAssociation, when populated, causes the server to serve the
// apple-app-site-association file at AppleAppSiteAssociationPath.
AppleAppSiteAssociation *AppleAppSiteAssociationConfig `` /* 133-byte string literal not displayed */
SSLCertificateFile string `env:"SSL_CERTIFICATE_FILEPATH" json:"sslCertificate,omitempty" yaml:"sslCertificate,omitempty"`
SSLCertificateKeyFile string `env:"SSL_CERTIFICATE_KEY_FILEPATH" json:"sslCertificateKey,omitempty" yaml:"sslCertificateKey,omitempty"`
StartupDeadline time.Duration `env:"STARTUP_DEADLINE" json:"startupDeadline,omitempty" yaml:"startupDeadline,omitempty"`
ReadTimeout time.Duration `env:"READ_TIMEOUT" json:"readTimeout,omitempty" yaml:"readTimeout,omitempty"`
WriteTimeout time.Duration `env:"WRITE_TIMEOUT" json:"writeTimeout,omitempty" yaml:"writeTimeout,omitempty"`
IdleTimeout time.Duration `env:"IDLE_TIMEOUT" json:"idleTimeout,omitempty" yaml:"idleTimeout,omitempty"`
Port uint16 `env:"PORT" json:"port,omitempty" yaml:"port,omitempty"`
// contains filtered or unexported fields
}
Config describes the settings pertinent to the HTTP serving portion of the service.
func (*Config) ValidateWithContext ¶
ValidateWithContext validates a Config struct.
Neither Port nor StartupDeadline is Required, because zero is meaningful for both: port 0 asks the OS for an ephemeral port, and a zero StartupDeadline means the bind is unbounded. They were Required while nothing called this, which is how the rules came to reject configurations the server accepts.
The timeouts are checked for sign instead: a negative one is always a mistake, and net/http reads it as "no timeout" rather than rejecting it.
type Option ¶
type Option func(*options)
Option configures the server this package constructs. The zero configuration works: an absent logger logs nowhere and an absent tracer provider traces nowhere.
func WithHealthRegistry ¶
func WithHealthRegistry(registry healthcheck.Registry) Option
WithHealthRegistry mounts LivenessPath and ReadinessPath, backed by the given registry. It names the same registry the gRPC sibling's option of the same name takes, so both transports answer from one set of checkers rather than from two that can disagree.
The routes are opted into rather than always mounted: a service that already serves its own probes at these paths would otherwise find them registered twice, which most muxes answer with a panic. A nil registry mounts nothing.
They are registered while the server is being constructed, so a caller that installs global middleware on the router does so before building the server — which is the ordering routing.Backend already documents, since most muxes refuse middleware added after the first route.
func WithServiceName ¶
WithServiceName names the server's logger and instrumentation scope. It matches the gRPC sibling's option of the same name.
func WithTracerProvider ¶
WithTracerProvider attaches a tracer provider, enabling spans on served requests.
func WithVersionEndpoint ¶
func WithVersionEndpoint() Option
WithVersionEndpoint mounts VersionPath, serving the build metadata the binary was stamped with.
It is separate from WithHealthRegistry because it answers a different question and carries a different decision: the commit a process is running is useful to an operator and is also information about the deployment, so a service exposing it on a public listener says so rather than inheriting it.
type Server ¶
type Server interface {
// Serve binds the listener and serves until Shutdown is called or the
// context is done. A graceful close reports no error.
Serve(ctx context.Context) error
// Shutdown drains in-flight requests, then flushes the spans they
// produced. It does not shut the tracer provider down; see the method.
Shutdown(ctx context.Context) error
Router() *routing.Router
}