webserver

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Feb 22, 2022 License: MIT Imports: 18 Imported by: 0

README

webserver

webserver provides a web server:

  • Gracefully handles shutdown
  • Applies best practices such as setting up timeouts
  • Routing powered by Gorilla Mux
  • Logging powered Sypl
  • HTTP server powered by Go built-in HTTP server
  • Observability is first-class:
    • Telemetry powered by Open Telemetry
    • Metrics powered by ExpVar
    • Built-in useful handlers such as liveness, and readiness

Install

$ go get github.com/saucelabs/webserver

Specific version

Example: $ go get github.com/saucelabs/webserver@v1.2.3

Usage

See example_test.go, and webserver_test.go file.

Documentation

Run $ make doc or check out online.

Development

Check out CONTRIBUTION.

Release
  1. Update CHANGELOG accordingly.
  2. Once changes from MR are merged.
  3. Tag and release.

Roadmap

Check out CHANGELOG.

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

Examples

Constants

This section is empty.

Variables

View Source
var ErrRequesTimeout = customerror.NewFailedToError(
	"finish request, timed out",
	customerror.WithStatusCode(http.StatusRequestTimeout),
)

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

	// Start the server.
	Start() error
}

IServer defines what a server does.

func New

func New(
	name, address string,
	opts ...Option,
) (IServer, error)

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"
)

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{}

	// Part of the readiness simulation.
	readinessFlag := false

	// Allows to safely modify `probeResults` and `readinessFlag` from
	// concurrent routines.
	var readinessFlagLocker sync.Mutex
	var probeResultsLocker sync.Mutex

	// 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(func() error {
			readinessFlagLocker.Lock()
			defer readinessFlagLocker.Unlock()

			if !readinessFlag {
				// Returning any error means server isn't ready.
				return errors.New("Not ready")
			}

			return nil
		}),
	)
	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)

		readinessFlagLocker.Lock()
		defer readinessFlagLocker.Unlock()

		readinessFlag = 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 NewBasic

func NewBasic(name, address string, opts ...Option) (IServer, error)

NewBasic returns a basic web server without observability: - Metrics - Telemetry - Logging - Pre-loaded handlers (Liveness, Readiness, OK, and Stop).

type Logging

type Logging struct {
	// ConsoleLevel defines the level for the `Console` output.
	ConsoleLevel string `json:"console_level" validate:"required,gte=3,oneof=none fatal error info warn debug trace"`

	// RequestLevel defines the level for logging requests.
	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.
	Filepath string `json:"filepath" validate:"omitempty,gte=3"`
}

Logging definition.

type Option

type Option func(s *Server)

Option allows to define options for the Server.

func WithLoggingOptions

func WithLoggingOptions(console, request, filepath string) Option

WithLoggingOptions sets logging configuration.

NOTE: Set filepath to "" to disabled that.

func WithMetrics

func WithMetrics(name string, v interface{}) Option

WithMetrics provides a quick way to publish static metric values.

func WithMetricsRaw

func WithMetricsRaw(name string, metrics expvar.Var) Option

WithMetricsRaw allows to publishes metrics based on exp vars. It's useful for cases such as counters. It gives full control over what's being exposed.

func WithPreLoadedHandlers

func WithPreLoadedHandlers(handlers ...handler.Handler) Option

WithPreLoadedHandlers adds handlers to the list of pre-loaded handlers.

NOTE: Use `handler.New` to bring your own handler.

func WithReadiness

func WithReadiness(readinessFunc handler.ReadinessFunc) Option

WithReadiness sets server readiness. Returning any non-nil error means server isn't ready.

func WithTelemetry

func WithTelemetry(t *telemetry.Telemetry) Option

WithTelemetry sets telemetry.

NOTE: Use `telemetry.New` to bring your own telemetry.

SEE: https://opentelemetry.io/vendors

func WithTimeout

func WithTimeout(read, request, inflight, tasks, write time.Duration) Option

WithTimeout sets the maximum duration for each individual timeouts.

func WithoutLogging

func WithoutLogging() Option

WithoutLogging() disables logging.

func WithoutMetrics

func WithoutMetrics() Option

WithoutMetrics disables metrics.

func WithoutPreLoadedHandlers

func WithoutPreLoadedHandlers() Option

WithoutPreLoadedHandlers disable the default pre-loaded handlers: - OK handler (`GET /`) - Liveness handler (`GET /liveness`) - Readiness handler (`GET /readiness`) - Stop handler (`GET /stop`) - Metrics handler (`GET /debug/vars`).

func WithoutTelemetry

func WithoutTelemetry() Option

WithoutTelemetry disables telemetry.

type Server

type Server struct {
	// Address is a TCP address to listen on, default: ":4446".
	Address string `json:"address" validate:"tcp_addr"`

	// Name of the server.
	Name string `json:"name" validate:"required,gte=3"`

	// 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"`

	// 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) GetLogger

func (s *Server) GetLogger() sypl.ISypl

GetLogger returns the server logger.

func (*Server) GetRouter

func (s *Server) GetRouter() *mux.Router

GetRouter returns the server router.

func (*Server) GetTelemetry

func (s *Server) GetTelemetry() telemetry.ITelemetry

GetTelemetry returns telemetry.

func (*Server) Start

func (s *Server) Start() error

Start the server.

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 is the standard Golang's expvar package with one modification: it removes the bad practice of using `init()`, instead exposes: - `Start`: does everything the old `init` does - `PublishCmdLine`: publishes command line information - `PublishMemStats`: publishes memory statistics - `RegisterHandler`: registers the standard endpoint: `GET /debug/vars`.
Package expvar is the standard Golang's expvar package with one modification: it removes the bad practice of using `init()`, instead exposes: - `Start`: does everything the old `init` does - `PublishCmdLine`: publishes command line information - `PublishMemStats`: publishes memory statistics - `RegisterHandler`: registers the standard endpoint: `GET /debug/vars`.
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 telemetry provides server's telemetry powered by Open Telemetry.
Package telemetry provides server's telemetry powered by Open Telemetry.

Jump to

Keyboard shortcuts

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