http

package
v0.0.0-...-243ae3c Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 26 Imported by: 0

README

http

  // create a new server
	s := http.NewServer(c, "example")

  // go built-in http/Handler
	s.Mux().HandleFunc("/test/one", func(w gohttp.ResponseWriter, r *gohttp.Request) {
		_, _ = w.Write([]byte(`"one"`))
	})

  // generic POST handler with error helpers
	s.HandleRequest("/test/two", func(r *gohttp.Request) (any, error) {
		return map[string]string{"result": "one"}, nil
	})

	// using API library (see forego/api/)
	s.MustRegisterAPI(c, "/api/my", &MyAPI{})
  
  // listen to a port
	addr, err := s.Listen(c, "127.0.0.1:0")
  if err != nil {
    panic(err)
  }

  // wait for graceful shutdown
	shutdown.WaitForSignal(c, cf)

Server

	s := http.NewServer(c, "example")

Creates a new server, it will internally setup several middleware which provide:

  • logging (can be overridden with s.OnRequest
  • context tags (ua for user agent, path, http.addr and http.remote)
  • ServeMux (can be used with s.Mux())
  • /live which always return 204 (OK No Content)
  • /ready which by default returns 204 (can be changed with s.SetReady())
  • /openapi.json serialized from s.OpenAPI
  • optional /docs page you can register to render an interactive Scalar reference (see below)
Mux()

Returns the internal ServeMux, which can be then used to add new paths to the server using go built-in http.Handler

HandleRequest(pattern string, fn func(*http.Request) (any, error))
	s.HandleRequest("/test/two", func(r *gohttp.Request) (any, error) {
		c := r.Context()
		req, err := io.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
		return map[string]any{
			"size": len(req),
			"path": r.URL.Path,
		}, nil
	})

Helper which creates a handler for the given function, which:

  • passes the incoming request to the given function
  • if error is returned, the trackingID is returned with a 500
  • if error is a http.Error, and the code is <500, then the error message is returned as well
  • if no response (e.g. function returns nil), return 200 with an empty body
  • otherwise set the response to application/json and send data ([]byte, enc.Node, or any JSON-marshalable value)
  • optionally gzip if more than 16KB and request accepts gzip

An entry is created in s.OpenAPI for the given path, and the *openapi.PathInfo is returned for further tweaking

RegisterAPI(c, "/path", obj) and MustRegisterAPI(c, "/path", obj)
  err = s.RegisterAPI(c, "/api/my", &MyApi)

Uses the api library to parse the given object, and expose the API.

It also update s.OpenAPI accordingly.

Serve a documentation page

Once your handlers populate s.OpenAPI, we recommend wiring a tiny HTML page that embeds Scalar API Reference for a polished, zero-maintenance reader:

s.HandleFunc("/docs", func(w http.ResponseWriter, r *http.Request) {
	html := `<!DOCTYPE html>
<html>
<head>
    <title>API Documentation</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
    <script id="api-reference" data-url="/openapi.json"></script>
    <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
</body>
</html>`
	w.Header().Set("Content-Type", "text/html")
	_, _ = w.Write([]byte(html))
})

Scalar pulls from the auto-generated /openapi.json, so updates to your API surface show up immediately.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultClient = Client{}
View Source
var Metrics = struct {
	Request *prom.Histogram
}{
	Request: prom.Register("http_request", &prom.Histogram{
		Buckets: prom.DefaultBuckets,
		Labels:  []string{"method", "path", "code"},
	}),
}

Functions

func ErrorCode

func ErrorCode(err error, def int) int

func HandleRequest

func HandleRequest[Req any](
	c ctx.C,
	s *Server,
	path string,
	f func(ctx.C, Req, http.ResponseWriter) error,
) *openapi.PathItem

func NewRequest

func NewRequest(c ctx.C, method string, url string, body io.Reader) (*http.Request, error)

func TestAPI

func TestAPI[T Doable, UID any](c ctx.C, uid UID, obj T) error

TODO

Types

type Call

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

func (*Call) SetBody

func (this *Call) SetBody(b []byte)

func (*Call) Write

func (this *Call) Write(b []byte)

type CallHandler

type CallHandler struct {
	Path        string
	MaxLength   int           // default is 1Mb
	ReadTimeout time.Duration // how long to wait for the request to be completed default is 30s
	Handler     func(c ctx.C, call *Call) error
}

func (CallHandler) Register

func (this CallHandler) Register(s *Server)

type Client

type Client struct {
	BaseUrl *url.URL
	// contains filtered or unexported fields
}

TODO circuit breaker!

func (Client) API

func (this Client) API(c ctx.C, obj Doable, path string) error

func (Client) Do

func (this Client) Do(r *http.Request) (*http.Response, error)

func (Client) Get

func (this Client) Get(c ctx.C, url string) ([]byte, error)

func (Client) Post

func (this Client) Post(c ctx.C, url string, body []byte) ([]byte, error)

type Doable

type Doable interface {
	api.Op
}

type Error

type Error struct {
	Code int
	Err  error
}

func NewErrorf

func NewErrorf(c ctx.C, code int, f string, args ...any) Error

func (Error) Error

func (this Error) Error() string

func (Error) Unwrap

func (this Error) Unwrap() error

type Request

type Request = http.Request

type ResponseWriter

type ResponseWriter = http.ResponseWriter

type Server

type Server struct {
	OpenAPI *openapi.Service

	// ReadTimeout is the maximum duration for reading the entire
	// request, including the body. A zero or negative value means
	// there will be no timeout.
	//
	// Because ReadTimeout does not let Handlers make per-request
	// decisions on each request body's acceptable deadline or
	// upload rate, most users will prefer to use
	// ReadHeaderTimeout. It is valid to use them both.
	ReadTimeout time.Duration

	// ReadHeaderTimeout is the amount of time allowed to read
	// request headers. The connection's read deadline is reset
	// after reading the headers and the Handler can decide what
	// is considered too slow for the body. If zero, the value of
	// ReadTimeout is used. If negative, or if zero and ReadTimeout
	// is zero or negative, there is no timeout.
	ReadHeaderTimeout time.Duration

	// WriteTimeout is the maximum duration before timing out
	// writes of the response. It is reset whenever a new
	// request's header is read. Like ReadTimeout, it does not
	// let Handlers make decisions on a per-request basis.
	// A zero or negative value means there will be no timeout.
	WriteTimeout time.Duration
	// contains filtered or unexported fields
}

func NewServer

func NewServer(c ctx.C) *Server

func (*Server) Handle

func (this *Server) Handle(path string, h http.Handler) *openapi.Path

func (*Server) HandleFunc

func (this *Server) HandleFunc(path string, h http.HandlerFunc) *openapi.Path

func (*Server) HandleRequest

func (this *Server) HandleRequest(path string, f func(r *Request) (any, error)) *openapi.PathItem

Setup the given request as JSON, and add it to `s.OpenAPI` for the given path as POST, returns the openapi.PathItem

func (*Server) HandleStream

func (this *Server) HandleStream(path string, f StreamFunc) *openapi.PathItem

Setup the given streaming function, ignore the method, request body can be nil

func (*Server) Listen

func (this *Server) Listen(c ctx.C, addr string) (*net.TCPAddr, error)

func (*Server) MustRegisterAPI

func (s *Server) MustRegisterAPI(c ctx.C, path string, obj Doable) *openapi.PathItem

func (*Server) MustRegisterStreamingAPI

func (s *Server) MustRegisterStreamingAPI(c ctx.C, path string, obj Streamable) *openapi.PathItem

func (Server) Mux

func (this Server) Mux() *http.ServeMux

deprecated use Handle and HandleFunc directly on Server

func (*Server) RegisterAPI

func (s *Server) RegisterAPI(c ctx.C, path string, obj Doable) (*openapi.PathItem, error)

func (*Server) RegisterStreamingAPI

func (s *Server) RegisterStreamingAPI(c ctx.C, path string, obj Streamable) (*openapi.PathItem, error)

func (*Server) ServeHTTP

func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Server) SetReady

func (this *Server) SetReady(code int)

func (*Server) SetupMonitoring

func (this *Server) SetupMonitoring(c ctx.C, prefix string)

register /pprof/ and /goroutines under the given prefix

func (*Server) SetupPrometheus

func (this *Server) SetupPrometheus(c ctx.C, path string)

func (*Server) Use

func (this *Server) Use(mw func(http.Handler) http.Handler)

Use wraps the server handler with the given middleware. Middleware is applied in the order of calls (last call = outermost wrapper).

type StreamFunc

type StreamFunc func(c ctx.C, in io.Reader, emit func(c ctx.C, obj any) error) error

a streaming function with an option request body `in` and a function which sends chunks to the client

type Streamable

type Streamable interface {
	api.StreamingOp
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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