Documentation
¶
Overview ¶
Package webserver provides a HTTP server: - Gracefully handles shutdown - Applies best practices such as setting up timeouts - Routing powered by Gorilla Mux - HTTP server powered by Go built-in HTTP server - Observability is first-class:
- Logging powered Sypl
- Telemetry powered by Open Telemetry
- Metrics powered by ExpVar
- Built-in useful handlers such as liveness, and readiness.
Index ¶
- Variables
- type IServer
- type Logging
- type Option
- func WithHandlers(handlers ...handler.Handler) Option
- func WithLogging(console, request, filepath string) Option
- func WithMetrics(metrics ...metric.Metric) Option
- func WithMetricsFunc(name string, v interface{}) Option
- func WithReadiness(readinessState ...*handler.ReadinessDeterminer) Option
- func WithRouter(router *mux.Router) Option
- func WithTelemetry(t *telemetry.Telemetry) Option
- func WithTimeout(read, request, inflight, tasks, write time.Duration) Option
- func WithoutHandlers() Option
- func WithoutLogging() Option
- func WithoutMetrics() Option
- func WithoutTelemetry() Option
- type Server
- type Timeout
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrRequesTimeout = customerror.NewFailedToError( "finish request, timed out", customerror.WithStatusCode(http.StatusRequestTimeout), )
ErrRequesTimeout indicates a request failed to finish, it timed out.
Functions ¶
This section is empty.
Types ¶
type IServer ¶
type IServer interface {
// GetLogger returns the server logger.
GetLogger() sypl.ISypl
// GetRouter returns the server router.
GetRouter() *mux.Router
GetTelemetry() telemetry.ITelemetry
// Start the server.
Start() error
}
IServer defines what a server does.
func New ¶
New is the web server factory. It returns a web server with observability: - Metrics: `cmdline`, `memstats`, and `server`. - Telemetry: `stdout` exporter. - Logging: `error`, no file. - Pre-loaded handlers (Liveness, OK, and Stop).
Example ¶
Example showing how to use the Web Server.
package main
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/saucelabs/randomness"
"github.com/saucelabs/webserver"
"github.com/saucelabs/webserver/handler"
)
const serverName = "test-server"
// Logs, and exit.
func logAndExit(msg string) {
fmt.Println(msg)
os.Exit(1)
}
// Call.
//
//nolint:noctx
func callAndExpect(port int, url string, sc int, expectedBodyContains string) (int, string) {
c := http.Client{Timeout: time.Duration(10) * time.Second}
resp, err := c.Get(fmt.Sprintf("http://0.0.0.0:%d/%s", port, url))
if err != nil {
logAndExit(err.Error())
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logAndExit(err.Error())
}
if sc != 0 {
if resp.StatusCode != sc {
logAndExit(fmt.Sprintf("Expect %v got %v\n", sc, resp.StatusCode))
}
}
bodyS := string(body)
if expectedBodyContains != "" {
if !strings.Contains(bodyS, expectedBodyContains) {
logAndExit(fmt.Sprintf("Expect %v got %v\n", expectedBodyContains, bodyS))
}
}
return resp.StatusCode, bodyS
}
//////
// Examples.
//////
// Example showing how to use the Web Server.
func main() {
// Probe results accumulator. Golang's example requires some output to be
// tested against. This accumulator will serve it.
probeResults := []string{}
probeResultsLocker := sync.Mutex{}
// Part of the readiness simulation.
readinessFlag := handler.NewReadinessDeterminer("tunnel")
// Golang's example are like tests, it's a bad practice to have a hardcoded
// port because of the possibility of collision. Generate a random port.
r, err := randomness.New(3000, 7000, 10, true)
if err != nil {
logAndExit(err.Error())
}
port := r.MustGenerate()
// Setup server settings some options.
testServer, err := webserver.New(serverName, fmt.Sprintf("0.0.0.0:%d", port),
webserver.WithoutMetrics(),
webserver.WithoutTelemetry(),
// Sets server readiness.
webserver.WithReadiness(readinessFlag),
)
if err != nil {
logAndExit(err.Error())
}
// Start server, non-blocking way.
go func() {
if err := testServer.Start(); err != nil {
if errors.Is(err, http.ErrServerClosed) {
fmt.Println("server stopped")
} else {
logAndExit(err.Error())
}
}
}()
// Ensures enough time for the server to be up, and ready - just for testing.
time.Sleep(3 * time.Second)
// Simulates a Readiness probe, for example, Kubernetes.
go func() {
for {
_, body := callAndExpect(int(port), "/readiness", 0, "")
probeResultsLocker.Lock()
probeResults = append(probeResults, body)
probeResultsLocker.Unlock()
// Probe wait time.
time.Sleep(500 * time.Millisecond)
}
}()
// Simulates some action which indicates server is ready, example data was
// loaded from DB, or got updated data from another service.
go func() {
time.Sleep(2 * time.Second)
readinessFlag.SetReadiness(true)
}()
// Hold the server online for testing.
time.Sleep(5 * time.Second)
// Satisfies Golang example output need.
probeResultsLocker.Lock()
fmt.Println(strings.Contains(strings.Join(probeResults, ","), "OK"))
probeResultsLocker.Unlock()
}
Output: true
type Logging ¶
type Logging struct {
// ConsoleLevel defines the level for the `Console` output, default: "error".
ConsoleLevel string `json:"console_level" validate:"required,gte=3,oneof=none fatal error info warn debug trace"`
// RequestLevel defines the level for logging requests, default: "error".
RequestLevel string `json:"request_level" validate:"required,gte=3,oneof=none fatal error info warn debug trace"`
// Filepath is the file path to optionally write logs, default: ""
Filepath string `json:"filepath" validate:"omitempty,gte=3"`
}
Logging settings.
type Option ¶
type Option func(s *Server)
Option allows to define options for the Server.
func WithHandlers ¶ added in v0.0.5
WithHandlers adds handlers to the list of pre-loaded handlers.
NOTE: Use `handler.New` to bring your own handler.
func WithLogging ¶ added in v0.0.5
WithLogging sets logging configuration.
NOTE: Set filepath to "" to disabled that.
func WithMetrics ¶
WithMetrics adds metrics to the list of pre-loaded metrics.
NOTE: Use `metric.New` to bring your own metric.
func WithMetricsFunc ¶ added in v0.0.5
WithMetricsFunc provides a quick way to add metrics.
func WithReadiness ¶
func WithReadiness(readinessState ...*handler.ReadinessDeterminer) Option
WithReadiness adds server readiness. Multiple readinesses determiners can be passed. In this case, only if ALL are ready, the server will be considered ready.
NOTE: Use `handler.NewReadinessDeterminer` to bring your own determiner.
func WithRouter ¶ added in v0.0.5
WithRouter sets the base router.
func WithTelemetry ¶
WithTelemetry sets telemetry.
NOTE: Use `telemetry.New` to bring your own telemetry.
func WithTimeout ¶
WithTimeout sets the maximum duration for each individual timeouts.
func WithoutHandlers ¶ added in v0.0.5
func WithoutHandlers() Option
WithoutHandlers disable the default pre-loaded handlers: - Liveness handler (`GET /liveness`) - OK handler (`GET /`) - Stop handler (`GET /stop`).
type Server ¶
type Server struct {
// Address is a TCP address to listen on, default: ":4446".
Address string `json:"address" validate:"tcp_addr"`
// EnableMetrics controls whether metrics are enable, or not, default: true.
EnableMetrics bool `json:"enable_metrics"`
// EnableTelemetry controls whether telemetry are enable, or not,
// default: true.
EnableTelemetry bool `json:"enable_telemetry"`
// Name of the server.
Name string `json:"name" validate:"required,gte=3"`
// Logging fine-control.
*Logging `json:"logging" validate:"required"`
// Timeouts fine-control.
*Timeout `json:"timeout" validate:"required"`
// contains filtered or unexported fields
}
Server definition.
func (*Server) GetRouter ¶
GetRouter returns the server base router. Use it do add your own handlers.
func (*Server) GetTelemetry ¶
func (s *Server) GetTelemetry() telemetry.ITelemetry
GetTelemetry returns telemetry.
type Timeout ¶
type Timeout struct {
// ReadTimeout max duration for READING the entire request, including the
// body, default: 3s.
ReadTimeout time.Duration `json:"read_timeout"`
// RequestTimeout max duration to WAIT BEFORE CANCELING A REQUEST,
// default: 1s.
//
// NOTE: It's automatically validated against other timeouts, and needs to
// be smaller.
RequestTimeout time.Duration `json:"request_timeout" validate:"ltfield=ReadTimeout"`
// ShutdownInFlightTimeout max duration to WAIT IN-FLIGHT REQUESTS,
// default: 3s.
ShutdownInFlightTimeout time.Duration `json:"shutdown_in_flight_timeout"`
// ShutdownTaskTimeout max duration TO WAIT for tasks such as flush cache,
// files, and telemetry, default: 10s.
ShutdownTaskTimeout time.Duration `json:"shutdown_task_timeout"`
// ShutdownTimeout max duration for WRITING the response, default: 3s.
WriteTimeout time.Duration `json:"write_timeout"`
}
Timeout definition.
Directories
¶
| Path | Synopsis |
|---|---|
|
package handler provides a collection of useful/built-in handlers.
|
package handler provides a collection of useful/built-in handlers. |
|
internal
|
|
|
expvar
Package expvar provides a standardized interface to public variables, such as operation counters in servers.
|
Package expvar provides a standardized interface to public variables, such as operation counters in servers. |
|
logger
Package logger provides server's logging powered by Sypl.
|
Package logger provides server's logging powered by Sypl. |
|
middleware
Package middleware offers common, and usual middlewares.
|
Package middleware offers common, and usual middlewares. |
|
validation
Package validation provides data validation.
|
Package validation provides data validation. |
|
package metric provides a collection of useful/built-in metrics.
|
package metric provides a collection of useful/built-in metrics. |
|
Package telemetry provides server's telemetry powered by Open Telemetry.
|
Package telemetry provides server's telemetry powered by Open Telemetry. |