webrick

package module
v0.0.0-...-2e8c01c Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

go-ruby-webrick/webrick

webrick — go-ruby-webrick

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the deterministic core of Ruby's WEBrick HTTP server — MRI 4.0.x's WEBrick request parse, response build, HTTPStatus table + error pages, the servlet / mount dispatch model, and the HTTPUtils helpers — without any Ruby runtime, and without doing any socket I/O itself.

It is the WEBrick backend for go-embedded-ruby and builds on go-ruby-net-http for the shared HTTP/1.1 message vocabulary, a sibling of go-ruby-rack, go-ruby-regexp and go-ruby-erb.

The TCP accept loop, the thread-per-connection model and the FileHandler filesystem reads are host-side seams. Parsing a raw request byte stream, building the response bytes, the status table and the default error pages, and the longest-prefix mount dispatch are all deterministic and need no interpreter, so they live here as pure Go. TCPServer.accept, the worker threads, and reading a file off disk for FileHandler are the host's job (rbgo supplies them): hand ParseRequest everything read from the socket, route with HTTPServer.Service, and write Response.Bytes back.

Features

Faithful port of WEBrick's request parse + response build, validated byte-for-byte against the webrick gem on every supported platform:

  • HTTPRequestParseRequest turns a raw request byte stream into the request_method / unparsed_uri / path / query_string / http_version, the folded multi-value header model, host / port, the body (Content-Length and chunked, with extensions + trailers), the parsed query (GET query string or x-www-form-urlencoded body), the cookies, the Accept* q-value lists, keep_alive?, and [] header access.
  • HTTPResponseResponse.Bytes runs setup_header + send_header + send_body: the status_line, the WEBrick header capitalisation (WWW-Authenticate / TE / word boundaries), Content-Length vs chunked framing, the Keep-Alive / Connection decision, set_redirect, Set-Cookie, and the default error-page HTML. (The non-deterministic Date header is the host's to add.)
  • HTTPStatus — the exact StatusMessage code→reason table, the Info/Success/Redirect/ClientError/ServerError category hierarchy, and the Status exception family (NotFound / Forbidden / …) servlets raise.
  • HTTPServer mount modelMount / MountProc / Unmount plus the Service dispatch: longest-prefix path match at a path boundary → servlet, with script_name / path_info split (MountTable).
  • ServletsAbstractServlet (the do_<METHOD> contract, HEAD→GET, OPTIONS Allow list, MethodNotAllowed), ProcServlet (mount_proc), and FileHandler's pure path-resolution core (the path_info→file mapping, directory descent, index search, nondisclosure / Windows-ambiguous checks) over a host-supplied FileSystem seam.
  • HTTPUtilsEscape/Unescape, EscapeForm/UnescapeForm, EscapePath, Escape8bit, MimeType (the DefaultMimeTypes table), ParseQuery, ParseHeader, SplitHeaderValue, ParseQValues, ParseRangeHeader, NormalizePath, Dequote/Quote, plus Cookie and HTMLEscape.

CGO-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three operating systems (Linux, macOS, Windows).

Install

go get github.com/go-ruby-webrick/webrick

Usage

package main

import (
	"fmt"

	webrick "github.com/go-ruby-webrick/webrick"
)

func main() {
	cfg := webrick.DefaultConfig()
	cfg.ServerName = "example.com"

	// Route by longest-prefix mount, exactly like WEBrick::HTTPServer.
	srv := webrick.NewHTTPServer(cfg)
	srv.MountProc("/hello", func(req *webrick.Request, res *webrick.Response) *webrick.Status {
		res.SetStatus(200)
		res.SetContentType("text/plain")
		res.Body = []byte("hi " + req.Path)
		return nil
	})

	// The host reads a request off the socket and hands the bytes here:
	req, st := webrick.ParseRequest([]byte(
		"GET /hello?x=1 HTTP/1.1\r\nHost: example.com\r\n\r\n"), cfg)
	if st != nil { /* turn the raised status into an error response */ }

	res := webrick.NewResponse(cfg)
	res.SetRequest(req)
	if st := srv.Service(req, res); st != nil {
		res.SetError(st) // default error page + status
	}

	wire, _ := res.Bytes() // ... the host writes these bytes to the socket.
	fmt.Printf("%q\n", wire)
}

The socket / thread / filesystem seams

This library is the deterministic core only; the I/O is the host's:

Stage Owner This library
TCPServer.accept, worker threads host (rbgo)
read request bytes from the socket host (rbgo)
parse the request this library ParseRequest([]byte, *Config)
route to a servlet this library HTTPServer.Service (longest-prefix mount)
FileHandler file resolution this library FileHandler.ResolveFile (over a FileSystem)
read the resolved file off disk host (rbgo)
build the response bytes this library Response.Bytes()
write response bytes to the socket host (rbgo)

ParseRequest and Response.Bytes never touch the network, a file, or a clock, so the core is fully deterministic and testable in isolation — exactly how the host drives it from any byte transport. (The Date response header and the FileHandler file body are the two host-supplied, non-deterministic pieces.)

Tests & coverage

The suite pairs deterministic, ruby-free tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential MRI oracle: the same requests are parsed here and by the system ruby (WEBrick::HTTPRequest#parse), responses are built here and by WEBrick::HTTPResponse (setup_header/send_header/send_body, with the non-deterministic Date deleted on both sides), and the HTTPUtils helpers, the status table and the error pages are compared byte-for-byte. The oracle scripts $stdout.binmode so Windows text-mode never pollutes the bytes, and skip themselves where ruby (or the webrick gem) is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-webrick/webrick authors.

Documentation

Overview

Package webrick is a pure-Go (no cgo) reimplementation of the deterministic, interpreter-independent core of Ruby's WEBrick HTTP server: the HTTP request parse, the response build, the HTTPStatus table + error pages, the servlet / mount dispatch model, and the HTTPUtils helpers. The TCP accept loop, the thread-per-connection model and the FileHandler filesystem reads are host-side seams (rbgo wires them); everything here is pure compute and is validated byte-for-byte against MRI's `webrick` gem.

Index

Constants

View Source
const (
	CR   = "\r"
	LF   = "\n"
	CRLF = "\r\n"
)

CR, LF and CRLF mirror the WEBrick module constants.

View Source
const VERSION = "1.9.2"

VERSION is the WEBrick version this port mirrors (WEBrick::VERSION).

Variables

View Source
var DefaultMimeTypes = map[string]string{
	"ai":          "application/postscript",
	"asc":         "text/plain",
	"avi":         "video/x-msvideo",
	"avif":        "image/avif",
	"bin":         "application/octet-stream",
	"bmp":         "image/bmp",
	"class":       "application/octet-stream",
	"cer":         "application/pkix-cert",
	"crl":         "application/pkix-crl",
	"crt":         "application/x-x509-ca-cert",
	"css":         "text/css",
	"dms":         "application/octet-stream",
	"doc":         "application/msword",
	"dvi":         "application/x-dvi",
	"eps":         "application/postscript",
	"etx":         "text/x-setext",
	"exe":         "application/octet-stream",
	"gif":         "image/gif",
	"htm":         "text/html",
	"html":        "text/html",
	"ico":         "image/x-icon",
	"jpe":         "image/jpeg",
	"jpeg":        "image/jpeg",
	"jpg":         "image/jpeg",
	"js":          "application/javascript",
	"json":        "application/json",
	"lha":         "application/octet-stream",
	"lzh":         "application/octet-stream",
	"mjs":         "application/javascript",
	"mov":         "video/quicktime",
	"mp4":         "video/mp4",
	"mpe":         "video/mpeg",
	"mpeg":        "video/mpeg",
	"mpg":         "video/mpeg",
	"otf":         "font/otf",
	"pbm":         "image/x-portable-bitmap",
	"pdf":         "application/pdf",
	"pgm":         "image/x-portable-graymap",
	"png":         "image/png",
	"pnm":         "image/x-portable-anymap",
	"ppm":         "image/x-portable-pixmap",
	"ppt":         "application/vnd.ms-powerpoint",
	"ps":          "application/postscript",
	"qt":          "video/quicktime",
	"ras":         "image/x-cmu-raster",
	"rb":          "text/plain",
	"rd":          "text/plain",
	"rtf":         "application/rtf",
	"sgm":         "text/sgml",
	"sgml":        "text/sgml",
	"svg":         "image/svg+xml",
	"tif":         "image/tiff",
	"tiff":        "image/tiff",
	"ttc":         "font/collection",
	"ttf":         "font/ttf",
	"txt":         "text/plain",
	"wasm":        "application/wasm",
	"webm":        "video/webm",
	"webmanifest": "application/manifest+json",
	"webp":        "image/webp",
	"woff":        "font/woff",
	"woff2":       "font/woff2",
	"xbm":         "image/x-xbitmap",
	"xhtml":       "text/html",
	"xls":         "application/vnd.ms-excel",
	"xml":         "text/xml",
	"xpm":         "image/x-xpixmap",
	"xwd":         "image/x-xwindowdump",
	"zip":         "application/zip",
}

DefaultMimeTypes is the exact WEBrick::HTTPUtils::DefaultMimeTypes table.

Functions

func Dequote

func Dequote(str string) string

Dequote removes surrounding quotes and backslash escapes from str (WEBrick::HTTPUtils.dequote).

func Escape

func Escape(s string) string

Escape percent-encodes HTTP reserved and unwise characters in s (WEBrick::HTTPUtils.escape).

func Escape8bit

func Escape8bit(s string) string

Escape8bit percent-encodes the non-ASCII bytes of s (WEBrick::HTTPUtils.escape8bit).

func EscapeForm

func EscapeForm(s string) string

EscapeForm percent-encodes form-reserved characters and maps space to '+' (WEBrick::HTTPUtils.escape_form).

func EscapePath

func EscapePath(s string) string

EscapePath escapes each "/segment" of s with the pchar set, mirroring WEBrick::HTTPUtils.escape_path: the path is split on '/' and each component is escaped, so the slashes themselves are preserved. A path with no '/' yields "" (the scan matches nothing), exactly like MRI.

func HTMLEscape

func HTMLEscape(s string) string

HTMLEscape is the Go port of WEBrick::HTMLUtils.escape: it escapes &, ", > and < (in that exact replacement order) so a value is safe inside HTML. A nil-equivalent (here the empty string is the only nil-equivalent) yields "".

func IsClientError

func IsClientError(code int) bool

IsClientError reports a 4xx status (WEBrick::HTTPStatus.client_error?).

func IsError

func IsError(code int) bool

IsError reports a 4xx or 5xx status (WEBrick::HTTPStatus.error?).

func IsInfo

func IsInfo(code int) bool

IsInfo reports a 1xx status (WEBrick::HTTPStatus.info?).

func IsRedirect

func IsRedirect(code int) bool

IsRedirect reports a 3xx status (WEBrick::HTTPStatus.redirect?).

func IsServerError

func IsServerError(code int) bool

IsServerError reports a 5xx status (WEBrick::HTTPStatus.server_error?).

func IsSuccess

func IsSuccess(code int) bool

IsSuccess reports a 2xx status (WEBrick::HTTPStatus.success?).

func MimeType

func MimeType(filename string, mimeTab map[string]string) string

MimeType returns the MIME type for filename from mimeTab, mirroring WEBrick::HTTPUtils.mime_type: it tries the ".ext" suffix, then the ".ext.lang" double suffix, each lowercased, falling back to "application/octet-stream".

func NormalizePath

func NormalizePath(path string) (string, bool)

NormalizePath collapses redundant slashes and resolves "." and ".." segments, mirroring WEBrick::HTTPUtils.normalize_path. It returns ok=false when the path does not start with '/' or escapes above the root (the two `raise "abnormal path"` cases).

func ParseHeader

func ParseHeader(raw string) (*Header, *Status)

ParseHeader is the Go port of WEBrick::HTTPUtils.parse_header: it parses a raw header block (each line CRLF-terminated) into a Header, folding obs-fold continuation lines (leading space/tab) into the prior field, downcasing field names, and trimming leading/trailing whitespace from each value. It returns a *Status (BadRequest) on a malformed line, matching MRI's raise. The folded, trimmed values are then loaded into the shared net-http header codec.

func ParseQValues

func ParseQValues(value string) []string

ParseQValues parses a q-value list (an Accept-style header) into the values ordered by descending q, mirroring WEBrick::HTTPUtils.parse_qvalues. A nil (empty) value yields an empty slice.

func ParseRequest

func ParseRequest(raw []byte, config *Config) (*Request, *Status)

ParseRequest is the Go port of WEBrick::HTTPRequest#parse over a complete request byte stream: the request line, the header block, the cookies and Accept q-value lists, the request URI (path / host / port / query), the keep-alive decision, and the body (Content-Length or chunked). The socket / peer addresses are a host seam, so addr-derived host defaulting falls back to the config ServerName/Port. It returns a *Status on any malformed input, exactly the WEBrick::HTTPStatus error MRI would raise.

func Quote

func Quote(str string) string

Quote wraps str in double quotes, mirroring WEBrick::HTTPUtils.quote exactly — including its quirk: the replacement string `"\\\1"` is the two bytes backslash + 0x01 (the `\1` is a string escape, not a regex backreference), so every '"' or '\\' in str is replaced by "\\\x01" (the original char is dropped). This reproduces MRI byte-for-byte.

func ReasonPhrase

func ReasonPhrase(code int) string

ReasonPhrase returns the reason phrase for code, or "" if the code is not in the table (WEBrick::HTTPStatus.reason_phrase: StatusMessage[code], nil-safe).

func SplitHeaderValue

func SplitHeaderValue(str string) []string

SplitHeaderValue splits a header value into its comma-separated elements, honouring quoted strings (so a comma inside "..." does not split), mirroring WEBrick::HTTPUtils.split_header_value's scan %r'\G((?:"(?:\\.|[^"])+?"|[^",]++)+)(?:,[ \t]*|\Z)'.

func Unescape

func Unescape(s string) string

Unescape decodes every %XX in s (WEBrick::HTTPUtils.unescape).

func UnescapeForm

func UnescapeForm(s string) string

UnescapeForm maps '+' to space then decodes %XX (WEBrick::HTTPUtils.unescape_form).

Types

type AbstractServlet

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

AbstractServlet is the Go port of WEBrick::HTTPServlet::AbstractServlet: it holds the per-method handler table and implements the service dispatch (do_<METHOD>, with HEAD aliased to GET and a default OPTIONS that reports the Allow list). Concrete servlets register handlers via Handle.

func NewAbstractServlet

func NewAbstractServlet() *AbstractServlet

NewAbstractServlet returns an AbstractServlet with WEBrick's default GET (NotFound), HEAD (-> GET) and OPTIONS (Allow) handlers installed.

func (*AbstractServlet) Handle

func (s *AbstractServlet) Handle(method string, fn HandlerFunc)

Handle registers fn as the do_<method> handler (method uppercased, '-' kept as '_' in MRI's method name but here keyed by the raw uppercase method).

func (*AbstractServlet) Service

func (s *AbstractServlet) Service(req *Request, res *Response) *Status

Service dispatches req to the registered handler, mirroring AbstractServlet#service: HEAD falls through to GET; OPTIONS defaults to the Allow list; an unhandled method raises MethodNotAllowed.

type ByteRange

type ByteRange struct {
	First int
	Last  int
}

ByteRange is one element of a parsed Range header. First/Last follow MRI's integer-range convention: Last == -1 means "to the end"; a First < 0 with Last == -1 is a suffix length ("-N").

func ParseRangeHeader

func ParseRangeHeader(ranges string) ([]ByteRange, bool)

ParseRangeHeader parses a "bytes=..." Range header into byte ranges, mirroring WEBrick::HTTPUtils.parse_range_header. Each Range has First/Last; a missing bound is -1 (open-ended) following the Ruby `..-1` convention, and a suffix-length range "-N" is First=-N, Last=-1. It returns ok=false when the header is not a bytes= range, and a nil slice with ok=false on a bad spec.

type Config

type Config struct {
	// Port is the server's logical port (WEBrick::Config::HTTP[:Port], default
	// 80), used to default the request URI port and in error-page addresses.
	Port int
	// ServerName is the default host (Utils.getservername); used when neither a
	// Host header nor a socket address supplies one.
	ServerName string
	// ServerSoftware is the Server header / error-page signature.
	ServerSoftware string
	// HTTPVersion is the server's protocol version (default 1.1).
	HTTPVersion HTTPVersion
	// MimeTypes maps a lowercased file suffix to a MIME type (DefaultMimeTypes).
	MimeTypes map[string]string
	// DirectoryIndex is the FileHandler index-file search order.
	DirectoryIndex []string
	// Escape8bitURI mirrors :Escape8bitURI; when set the unparsed URI is
	// escape8bit'd before parsing.
	Escape8bitURI bool
}

Config is the pure-Go port of the WEBrick::Config::HTTP defaults relevant to the request/response/servlet core. Networking-only keys (BindAddress, Port listen socket, MaxClients, the various callbacks, SSL) are host-side seams and are not modelled here; the fields below are the ones the codec actually reads.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the WEBrick::Config::HTTP defaults for the keys this library reads. ServerName defaults to "" (the host fills it, as MRI does via Utils.getservername at runtime); ServerSoftware mirrors the "WEBrick/<ver> (Ruby/<ver>/<date>)" template with the version known here.

type Cookie struct {
	Name    string
	Value   string
	Version int
	Domain  string
	Path    string
	Secure  bool
	Comment string
	MaxAge  *int   // nil = unset
	Expires string // already formatted (httpdate or raw); "" = unset
	Port    string
}

Cookie is the Go port of WEBrick::Cookie: a name/value pair plus the optional attributes (Version / Domain / Path / Secure / Comment / Max-Age / Expires). Its String method serialises the cookie exactly as Cookie#to_s does, in the same attribute order, which is the bytes a Set-Cookie response header carries.

func NewCookie

func NewCookie(name, value string) *Cookie

NewCookie creates a cookie with the given name and value (Cookie.new); the version defaults to 0 (a Netscape cookie).

func ParseCookies

func ParseCookies(str string) []*Cookie

ParseCookies parses a Cookie request-header value into cookies, mirroring WEBrick::Cookie.parse: split on /;\s+/, with the $Version / $Path / $Domain / $Port directives applying to the cookies that follow.

func (*Cookie) String

func (c *Cookie) String() string

String renders the cookie for an HTTP header, mirroring Cookie#to_s: the attributes are appended in the order Version, Domain, Expires, Max-Age, Comment, Path, Secure, each only when set.

type FileHandler

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

FileHandler is the Go port of WEBrick::HTTPServlet::FileHandler's pure path-resolution core. It maps a request's path_info to a filesystem path under root, using the supplied FileSystem for stat decisions. The actual file read (res.body = File.open(...)) is a host seam: ResolveFile returns the resolved filesystem path, and the host opens and streams it.

func NewFileHandler

func NewFileHandler(root string, fs FileSystem, options FileHandlerOptions) *FileHandler

NewFileHandler creates a FileHandler serving files under root, resolving via fs (FileHandler#initialize). root should already be an absolute/expanded path (File.expand_path(root) is the host's responsibility, like the FS).

func (*FileHandler) ResolveFile

func (h *FileHandler) ResolveFile(pathInfo string) (FileResolution, *Status)

ResolveFile is the Go port of FileHandler#set_filename: it walks pathInfo segment by segment from root, descending into directories, and either resolves a concrete file (Found, with Filename set), resolves a directory's index file, or stops at a directory (Found=false, the dir-listing case the host handles). It returns a *Status (NotFound) when a segment names a missing file, or when a resolved basename is a nondisclosure / Windows-ambiguous name.

prevent_directory_traversal (File.expand_path on path_info) and the file read are host seams; pathInfo is expected already normalised (the request path is NormalizePath'd, and ".." cannot survive that).

type FileHandlerOptions

type FileHandlerOptions struct {
	// NondisclosureName is the glob list of names never served ([".ht*","*~"]).
	NondisclosureName []string
	// DirectoryIndex is the index-file search order (from Config).
	DirectoryIndex []string
}

FileHandlerOptions ports the FileHandler config keys the path resolver reads.

func DefaultFileHandlerOptions

func DefaultFileHandlerOptions(config *Config) FileHandlerOptions

DefaultFileHandlerOptions returns WEBrick's FileHandler defaults.

type FileResolution

type FileResolution struct {
	Found      bool
	Filename   string
	ScriptName string
	PathInfo   string
}

FileResolution is the outcome of ResolveFile: Filename is the resolved filesystem path (when Found), ScriptName/PathInfo are the updated CGI vars (script_name accumulates the consumed segments, path_info the remainder).

type FileSystem

type FileSystem interface {
	// IsDir reports whether the given expanded path is a directory
	// (File.directory?).
	IsDir(p string) bool
	// IsFile reports whether the given expanded path is a regular file
	// (File.file?).
	IsFile(p string) bool
}

FileSystem is the host-side seam for FileHandler: the actual stat/read of the filesystem. FileHandler's path-resolution logic (the path_info -> file mapping, the directory-walk, the index-file search, the nondisclosure and Windows-ambiguous checks) is pure compute and lives here; the host supplies a FileSystem so rbgo wires the real os filesystem (or any virtual FS) in.

type HTTPServer

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

HTTPServer is the pure-Go port of the WEBrick::HTTPServer mount/dispatch model: the mount registry plus the service() dispatch (longest-prefix path match to a servlet). The TCPServer.accept loop, the thread-per-connection model, the access log and the socket I/O are host-side seams (rbgo wires them); this type owns only the deterministic routing.

func NewHTTPServer

func NewHTTPServer(config *Config) *HTTPServer

NewHTTPServer creates a server with the given config (defaults applied), mirroring HTTPServer#initialize sans the networking setup.

func (*HTTPServer) Mount

func (s *HTTPServer) Mount(dir string, servlet Servlet)

Mount mounts servlet on dir (HTTPServer#mount).

func (*HTTPServer) MountProc

func (s *HTTPServer) MountProc(dir string, proc HandlerFunc)

MountProc mounts a proc on dir (HTTPServer#mount_proc).

func (*HTTPServer) SearchServlet

func (s *HTTPServer) SearchServlet(path string) (servlet Servlet, scriptName, pathInfo string, ok bool)

SearchServlet finds the servlet for path, mirroring HTTPServer#search_servlet: it returns the longest-prefix-matched servlet plus the script_name (the matched mount prefix) and path_info (the remainder). ok=false when nothing matches.

func (*HTTPServer) Service

func (s *HTTPServer) Service(req *Request, res *Response) *Status

Service is the Go port of HTTPServer#service: asterisk-form OPTIONS is answered with the default Allow list; otherwise the request path is matched to a servlet by longest prefix, script_name/path_info are filled in, and the servlet's Service is invoked. It returns a *Status to "raise" (NotFound when no servlet matches, OK after an asterisk OPTIONS, or whatever the servlet raises); nil means the response was filled normally.

func (*HTTPServer) Unmount

func (s *HTTPServer) Unmount(dir string)

Unmount removes the servlet at dir (HTTPServer#unmount / #umount).

type HTTPVersion

type HTTPVersion struct {
	Major int
	Minor int
}

HTTPVersion is the pure-Go port of WEBrick::HTTPVersion: a comparable (major, minor) HTTP protocol version. It mirrors the parse, the to_s formatting, and the Comparable <=> ordering MRI exposes.

func ParseHTTPVersion

func ParseHTTPVersion(version string) (HTTPVersion, error)

ParseHTTPVersion parses a "major.minor" string into an HTTPVersion, mirroring WEBrick::HTTPVersion#initialize: only the exact /^(\d+)\.(\d+)$/ form is accepted, anything else raises ArgumentError (here an error).

func (HTTPVersion) Compare

func (v HTTPVersion) Compare(other HTTPVersion) int

Compare reports -1, 0 or +1 ordering against other, mirroring WEBrick::HTTPVersion#<=>: compare major first, then minor.

func (HTTPVersion) Less

func (v HTTPVersion) Less(other HTTPVersion) bool

Less reports v < other (the HTTP-version ordering used throughout WEBrick).

func (HTTPVersion) String

func (v HTTPVersion) String() string

String formats the version as "major.minor" (WEBrick::HTTPVersion#to_s).

type HandlerFunc

type HandlerFunc func(req *Request, res *Response) *Status

HandlerFunc is a do_<METHOD> implementation.

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

Header is the parsed request-header model produced by ParseHeader: a downcased field name maps to its ordered list of values, like the Hash WEBrick::HTTPUtils.parse_header returns (each value an element of a SplitHeader/CookieHeader array). The multi-value store is the shared HTTP/1.1 header codec from go-ruby-net-http (nethttp.Header: downcased keys, ordered, multi-value, the same Net::HTTPHeader port WEBrick's parse_header builds); WEBrick's cookie-specific "; " join is layered on top in Get.

func (*Header) Each

func (h *Header) Each(fn func(name, value string))

Each iterates fields in insertion order, calling fn with the downcased name and the joined value (nil-equivalent fields are skipped — HTTPRequest#each yields nil but callers treat it as absent).

func (*Header) Get

func (h *Header) Get(field string) (string, bool)

Get returns the joined value list for the downcased field, with the join separator WEBrick uses: "; " for the cookie header (CookieHeader#join), ", " for all others (SplitHeader#join). It returns ok=false (and "") when the field is absent, matching HTTPRequest#[] returning nil for an empty list.

func (*Header) Has

func (h *Header) Has(field string) bool

Has reports whether the downcased field is present (net-http Key?).

func (*Header) Values

func (h *Header) Values(field string) []string

Values returns a copy of the raw value list for the downcased field, or nil (the reused net-http GetFields).

type InvalidHeaderError

type InvalidHeaderError struct{ Field, Value string }

InvalidHeaderError corresponds to HTTPResponse::InvalidHeader.

func (*InvalidHeaderError) Error

func (e *InvalidHeaderError) Error() string

type MountTable

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

MountTable is the Go port of WEBrick::HTTPServer::MountTable: the registry of mount-point -> servlet, with the longest-prefix scanner. A path matches a mount prefix only at a path boundary (the prefix is followed by '/' or end of path), and the longest matching prefix wins.

func NewMountTable

func NewMountTable() *MountTable

NewMountTable returns an empty mount table.

func (*MountTable) Delete

func (m *MountTable) Delete(dir string) (Servlet, bool)

Delete removes the mount at dir (MountTable#delete).

func (*MountTable) Get

func (m *MountTable) Get(dir string) (Servlet, bool)

Get returns the servlet mounted exactly at the normalised dir (MountTable#[]).

func (*MountTable) Scan

func (m *MountTable) Scan(path string) (scriptName, pathInfo string, ok bool)

Scan finds the longest mount prefix matching path at a path boundary, returning the matched prefix (script_name), the remainder (path_info), and whether anything matched, mirroring MountTable#scan over the compiled \A(<prefix>|...)(?=/|\z) scanner. Keys are tried longest-first.

func (*MountTable) Set

func (m *MountTable) Set(dir string, val Servlet)

Set mounts val at dir (MountTable#[]=). dir is normalised (trailing slashes stripped) so "/foo/" and "/foo" are the same mount point.

type ProcServlet

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

ProcServlet is the Go port of WEBrick::HTTPServlet::ProcHandler: a single proc mounted via mount_proc, dispatched for GET, POST and PUT (the do_GET alias chain). Other methods fall through to AbstractServlet (MethodNotAllowed / OPTIONS).

func NewProcServlet

func NewProcServlet(proc HandlerFunc) *ProcServlet

NewProcServlet wraps proc as a ProcHandler.

func (*ProcServlet) Service

func (p *ProcServlet) Service(req *Request, res *Response) *Status

Service dispatches GET/POST/PUT to the proc, mirroring ProcHandler's do_GET/do_POST/do_PUT aliases; HEAD maps to the proc too (via do_GET); OPTIONS reports Allow; anything else is MethodNotAllowed.

type Query

type Query struct {
	Order []string
	// contains filtered or unexported fields
}

Query is the parsed query map: a key maps to its (chained) QueryItem. Like MRI's Hash it preserves first-insertion order via Order.

func ParseQuery

func ParseQuery(str string) *Query

ParseQuery is the Go port of WEBrick::HTTPUtils.parse_query: it splits str on '&' or ';', form-unescapes each key and value, and builds the query map with repeated keys chained. An empty element is skipped; a key with no '=' has an empty value.

func (*Query) Get

func (q *Query) Get(key string) (string, bool)

Get returns the first value for key (Hash#[] of a FormData, whose String identity is its first value), and ok=false when absent.

func (*Query) Item

func (q *Query) Item(key string) *QueryItem

Item returns the QueryItem for key (with its full duplicate chain), or nil.

func (*Query) Len

func (q *Query) Len() int

Len reports the number of distinct keys.

type QueryItem

type QueryItem struct {
	Name  string
	Value string
	Next  *QueryItem
}

QueryItem is one entry of a parsed query string: a name and its value (the FormData string in MRI). Repeated keys chain their values in Next, mirroring FormData#append_data.

func (*QueryItem) List

func (q *QueryItem) List() []string

List returns all values for this query item, including chained duplicates (FormData#list / #to_ary).

type Request

type Request struct {
	RequestLine   string
	RequestMethod string
	UnparsedURI   string
	HTTPVersion   HTTPVersion

	Path        string
	ScriptName  string
	PathInfo    string
	QueryString string

	RawHeader []string

	Cookies []*Cookie

	Accept         []string
	AcceptCharset  []string
	AcceptEncoding []string
	AcceptLanguage []string

	Body []byte
	// contains filtered or unexported fields
}

Request is the Go port of WEBrick::HTTPRequest: the parsed request line, header model, decoded body, query and cookies. Unlike MRI it does not read from a socket itself — the host hands ParseRequest the complete request byte stream (the read seam), and Request exposes the same accessors WEBrick servlets use (request_method, path, query, cookies, keep_alive?, []).

func (*Request) ContentLength

func (r *Request) ContentLength() (int, bool)

ContentLength returns the parsed Content-Length (HTTPRequest#content_length). It returns ok=false when the header is absent.

func (*Request) ContentType

func (r *Request) ContentType() string

ContentType returns the Content-Type header (HTTPRequest#content_type).

func (*Request) EachHeader

func (r *Request) EachHeader(fn func(name, value string))

EachHeader iterates the request headers (HTTPRequest#each).

func (*Request) Header

func (r *Request) Header(name string) (string, bool)

Header returns a header value with an ok flag (HTTPRequest#[]).

func (*Request) Host

func (r *Request) Host() string

Host returns the request host (HTTPRequest#host).

func (*Request) KeepAlive

func (r *Request) KeepAlive() bool

KeepAlive reports whether this connection should be kept alive (HTTPRequest#keep_alive?).

func (*Request) Port

func (r *Request) Port() int

Port returns the request port (HTTPRequest#port).

func (*Request) Query

func (r *Request) Query() *Query

Query returns the parsed query (HTTPRequest#query): for GET/HEAD it parses the query string; for an x-www-form-urlencoded body it parses the body; for multipart it is not modelled (the host handles multipart streaming). The result is memoised.

type Response

type Response struct {
	HTTPVersion  HTTPVersion
	Status       int
	ReasonPhrase string

	Body    []byte
	Cookies []*Cookie

	RequestMethod      string
	RequestHTTPVersion HTTPVersion

	Filename string
	// contains filtered or unexported fields
}

Response is the Go port of WEBrick::HTTPResponse: the status, the header map, the body, and the cookies a servlet fills in. Its Bytes method runs MRI's setup_header + send_header + send_body and returns the exact response byte stream — the payload of the host's write seam (the host writes these bytes to the socket).

func NewResponse

func NewResponse(config *Config) *Response

NewResponse creates a response with WEBrick's defaults (status 200, the server's HTTP version, keep-alive on), matching HTTPResponse#initialize.

func (*Response) Bytes

func (r *Response) Bytes() ([]byte, error)

Bytes runs setup_header + send_header + send_body and returns the response byte stream (HTTPResponse#send_response's payload). It returns an error only if a header value contains CR/LF (HTTPResponse::InvalidHeader). HEAD requests suppress the body, exactly as send_body does.

func (*Response) Chunked

func (r *Response) Chunked() bool

Chunked reports whether the body will be chunked (HTTPResponse#chunked?).

func (*Response) Delete

func (r *Response) Delete(field string)

Delete removes a response header field.

func (*Response) Get

func (r *Response) Get(field string) (string, bool)

Get returns the response header value for field (HTTPResponse#[]).

func (*Response) KeepAlive

func (r *Response) KeepAlive() bool

KeepAlive reports the keep-alive state (HTTPResponse#keep_alive?).

func (*Response) Set

func (r *Response) Set(field, value string)

Set sets the response header field to value (HTTPResponse#[]=). Setting Transfer-Encoding to "chunked" toggles chunked mode, exactly as MRI's []=.

func (*Response) SetChunked

func (r *Response) SetChunked(v bool)

SetChunked enables or disables chunked transfer encoding (HTTPResponse#chunked=).

func (*Response) SetContentLength

func (r *Response) SetContentLength(n int)

SetContentLength sets the Content-Length header (HTTPResponse#content_length=).

func (*Response) SetContentType

func (r *Response) SetContentType(typ string)

SetContentType sets the Content-Type header (HTTPResponse#content_type=).

func (*Response) SetError

func (r *Response) SetError(err error)

SetError fills the response from an error, mirroring HTTPResponse#set_error: an HTTPStatus::Status sets that status (clearing keep-alive for an error code); anything else becomes 500. It then sets the ISO-8859-1 content type and generates the default error-page HTML.

func (*Response) SetRedirect

func (r *Response) SetRedirect(status *Status, url string) *Status

SetRedirect sets a redirect to url with the given redirect Status (the HTTPStatus::Redirect subclass), mirroring HTTPResponse#set_redirect: it sets the HTML body, the Location header, and returns the status to "raise".

func (*Response) SetRequest

func (r *Response) SetRequest(req *Request)

SetRequest wires the request method, HTTP version and request-URI host/port into the response, mirroring the assignments HTTPServer#run makes before servicing (res.request_method = ..., res.request_uri = ...). The request URI host/port feed set_error's default address; pass them from the parsed Request (req.Host()/req.Port()).

func (*Response) SetStatus

func (r *Response) SetStatus(status int)

SetStatus sets the status code and updates the reason phrase (HTTPResponse#status=).

func (*Response) StatusLine

func (r *Response) StatusLine() string

StatusLine returns the response status line, mirroring HTTPResponse#status_line: "HTTP/<ver> <status> <reason>".rstrip + CRLF (so a nil reason yields no trailing space).

type Servlet

type Servlet interface {
	// Service dispatches req to the matching do_<METHOD>, mirroring
	// AbstractServlet#service. It returns a *Status to "raise" (e.g.
	// MethodNotAllowed for an unhandled method), or nil on success.
	Service(req *Request, res *Response) *Status
}

Servlet is the Go port of the WEBrick::HTTPServlet::AbstractServlet service contract: Service dispatches a request to the do_<METHOD> handler. A servlet raising a *Status returns it as the error (the server turns it into the response). The default handlers (do_GET -> NotFound, do_HEAD -> do_GET, do_OPTIONS -> Allow list) are provided by AbstractServlet which concrete servlets embed.

type Status

type Status struct {
	Code         int
	ReasonPhrase string
	Category     StatusCategory
	Message      string
}

Status is the Go port of a WEBrick::HTTPStatus::Status exception: it carries the status code, its frozen reason phrase, and an optional message (the `raise HTTPStatus::NotFound, "..."` argument). Servlets "raise" a Status by returning it as an error; the server turns it into the response.

func NewStatus

func NewStatus(code int, message string) *Status

NewStatus builds the Status exception for code with an optional message, mirroring `raise HTTPStatus[code], msg`. It returns nil for a code outside the StatusMessage table (CodeToError has no entry).

func StatusBadRequest

func StatusBadRequest(msg ...string) *Status

StatusBadRequest is HTTPStatus::BadRequest (400).

func StatusError

func StatusError(code int) *Status

StatusError builds the named status exceptions WEBrick servlets raise. These are the constructors for WEBrick::HTTPStatus::NotFound, ::Forbidden, etc.

func StatusForbidden

func StatusForbidden(msg ...string) *Status

StatusForbidden is HTTPStatus::Forbidden (403).

func StatusFound

func StatusFound(msg ...string) *Status

StatusFound is HTTPStatus::Found (302).

func StatusInternalServerError

func StatusInternalServerError(msg ...string) *Status

StatusInternalServerError is HTTPStatus::InternalServerError (500).

func StatusLengthRequired

func StatusLengthRequired(msg ...string) *Status

StatusLengthRequired is HTTPStatus::LengthRequired (411).

func StatusMethodNotAllowed

func StatusMethodNotAllowed(msg ...string) *Status

StatusMethodNotAllowed is HTTPStatus::MethodNotAllowed (405).

func StatusMovedPermanently

func StatusMovedPermanently(msg ...string) *Status

StatusMovedPermanently is HTTPStatus::MovedPermanently (301).

func StatusNotFound

func StatusNotFound(msg ...string) *Status

StatusNotFound is HTTPStatus::NotFound (404).

func StatusNotImplemented

func StatusNotImplemented(msg ...string) *Status

StatusNotImplemented is HTTPStatus::NotImplemented (501).

func StatusNotModified

func StatusNotModified(msg ...string) *Status

StatusNotModified is HTTPStatus::NotModified (304).

func StatusOK

func StatusOK(msg ...string) *Status

StatusOK is HTTPStatus::OK (200).

func StatusRequestEntityTooLarge

func StatusRequestEntityTooLarge(msg ...string) *Status

StatusRequestEntityTooLarge is HTTPStatus::RequestEntityTooLarge (413).

func StatusTemporaryRedirect

func StatusTemporaryRedirect(msg ...string) *Status

StatusTemporaryRedirect is HTTPStatus::TemporaryRedirect (307).

func (*Status) Error

func (s *Status) Error() string

Error implements the error interface, returning the raised message if any, else the reason phrase (matching a bare `raise HTTPStatus::NotFound`).

func (*Status) ToI

func (s *Status) ToI() int

ToI returns the status code (WEBrick::HTTPStatus::Status#to_i / #code).

type StatusCategory

type StatusCategory int

StatusCategory names the HTTPStatus class-hierarchy node a code belongs to, matching the WEBrick::HTTPStatus parent classes.

const (
	// CategoryInfo is WEBrick::HTTPStatus::Info (1xx).
	CategoryInfo StatusCategory = iota
	// CategorySuccess is WEBrick::HTTPStatus::Success (2xx).
	CategorySuccess
	// CategoryRedirect is WEBrick::HTTPStatus::Redirect (3xx).
	CategoryRedirect
	// CategoryClientError is WEBrick::HTTPStatus::ClientError (4xx).
	CategoryClientError
	// CategoryServerError is WEBrick::HTTPStatus::ServerError (5xx).
	CategoryServerError
)

Jump to

Keyboard shortcuts

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