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 ¶
- Constants
- 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 WithReadiness(readinessDeterminers ...*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
- type Server
- type Timeout
Examples ¶
Constants ¶
const ( MIMEHTML = "text/html" MIMEJSON = "application/json" MIMEMSGPACK = "application/x-msgpack" MIMEMSGPACK2 = "application/msgpack" MIMEMultipartPOSTForm = "multipart/form-data" MIMEPlain = "text/plain" MIMEPOSTForm = "application/x-www-form-urlencoded" MIMEPROTOBUF = "application/x-protobuf" MIMEXML = "application/xml" MIMEXML2 = "text/xml" MIMEYAML = "application/x-yaml" )
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 // Stop the server. Stop(sig os.Signal) error }
IServer defines what a server does.
func New ¶
New returns a basic web server without: - logging - telemetry - metrics - pre-loaded handlers (Liveness, OK, and Stop).
Example ¶
Example showing how to use the Web Server.
package main import ( "errors" "fmt" "io" "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. // //nolint:forbidigo func logAndExit(msg string) { fmt.Println(msg) os.Exit(1) } 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 := io.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 } 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), // 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
func NewDefault ¶ added in v0.0.9
NewDefault returns a web server with observability: - Metrics: `cmdline`, `memstats`, and `server` - Telemetry: `stdout` provider - Logging: `error`, no file - Pre-loaded handlers (Liveness, OK, and Stop) - Versioned router: `/api/v1`.
Example ¶
package main import ( "errors" "fmt" "io" "net/http" "os" "strings" "time" "github.com/saucelabs/randomness" "github.com/saucelabs/webserver" ) const serverName = "test-server" // Logs, and exit. // //nolint:forbidigo func logAndExit(msg string) { fmt.Println(msg) os.Exit(1) } 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 := io.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 } func main() { // 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.NewDefault(serverName, fmt.Sprintf("0.0.0.0:%d", port)) 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(1 * time.Second) responseCode, body := callAndExpect(int(port), "/api/v1/", 200, "OK") fmt.Println(responseCode) fmt.Println(body) }
Output: 200 OK
type Logging ¶
type Logging struct { // ConsoleLevel defines the level for the `Console` output, default: "none". 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: "none". 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 sets 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 sets the list of pre-loaded metrics.
NOTE: Use `metric.New` to bring your own metric.
func WithReadiness ¶
func WithReadiness(readinessDeterminers ...*handler.ReadinessDeterminer) Option
WithReadiness sets 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.
type Server ¶
type Server struct { // Address is a TCP address to listen on. Address string `json:"address" validate:"required,hostname_port"` // EnableMetrics controls whether metrics are enable, or not, default: false. EnableMetrics bool `json:"enable_metrics"` // EnableTelemetry controls whether telemetry are enable, or not, // default: false. 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
|
|
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. |