Documentation
¶
Overview ¶
Package qh implements the QH (Quite Ok HTTP) protocol.
Index ¶
- Constants
- Variables
- func AppendUvarint(buf []byte, v uint64) []byte
- func Compress(data []byte, encoding Encoding) ([]byte, error)
- func DebugRequest(data []byte) string
- func DebugResponse(data []byte) string
- func DecodeStatusCode(compact uint8) int
- func Decompress(data []byte, encoding Encoding, maxSize int) ([]byte, error)
- func IsRequestComplete(data []byte) (bool, error)
- func IsResponseComplete(data []byte) (bool, error)
- func ReadUvarint(buf []byte, offset int) (uint64, int, error)
- type Client
- func (c *Client) Close() error
- func (c *Client) Connect(addr string, _ io.Writer) error
- func (c *Client) DELETE(host, path string, headers map[string]string) (*Response, error)
- func (c *Client) GET(host, path string, headers map[string]string) (*Response, error)
- func (c *Client) HEAD(host, path string, headers map[string]string) (*Response, error)
- func (c *Client) PATCH(host, path string, body []byte, headers map[string]string) (*Response, error)
- func (c *Client) POST(host, path string, body []byte, headers map[string]string) (*Response, error)
- func (c *Client) PUT(host, path string, body []byte, headers map[string]string) (*Response, error)
- func (c *Client) Request(req *Request, redirectCount int) (*Response, error)
- type ClientOption
- type Encoding
- type Handler
- type Method
- type Request
- type Response
- type Server
- type ServerOption
Constants ¶
const ( // 1xx Informational responses StatusContinue = 100 StatusSwitchingProtocols = 101 StatusProcessing = 102 StatusEarlyHints = 103 // 2xx Success responses StatusOK = 200 StatusCreated = 201 StatusAccepted = 202 StatusNoContent = 204 StatusResetContent = 205 StatusPartialContent = 206 StatusMultiStatus = 207 StatusAlreadyReported = 208 StatusIMUsed = 226 // 3xx Redirection responses StatusMultipleChoices = 300 StatusMovedPermanently = 301 StatusFound = 302 StatusSeeOther = 303 StatusNotModified = 304 StatusUseProxy = 305 StatusTemporaryRedirect = 307 StatusPermanentRedirect = 308 // 4xx Client Error responses StatusBadRequest = 400 StatusPaymentRequired = 402 StatusForbidden = 403 StatusNotFound = 404 StatusMethodNotAllowed = 405 StatusNotAcceptable = 406 StatusProxyAuthRequired = 407 StatusRequestTimeout = 408 StatusConflict = 409 StatusGone = 410 StatusLengthRequired = 411 StatusPreconditionFailed = 412 StatusPayloadTooLarge = 413 StatusURITooLong = 414 StatusUnsupportedMediaType = 415 StatusRangeNotSatisfiable = 416 StatusExpectationFailed = 417 StatusUnprocessableEntity = 422 StatusTooManyRequests = 429 // 5xx Server Error responses StatusInternalServerError = 500 StatusBadGateway = 502 StatusGatewayTimeout = 504 StatusQHVersionNotSupported = 505 )
Status code constants. These mirror standard HTTP status codes and are used in QH responses.
const ( // CustomHeader is a special header ID (0) used to indicate custom headers CustomHeader byte = 0 )
const (
DefaultAcceptEncoding = "zstd, br, gzip"
)
const ( // Version is the current QH protocol version number. // This value is encoded in the first byte of both requests and responses. Version = 0 )
Variables ¶
var CompactToStatus map[uint8]int
CompactToStatus is the reverse mapping for decoding compact codes to HTTP status. Exported for external consumers (e.g., Wireshark plugin).
var RequestHeaderStaticTable = map[byte]headerEntry{
0x01: {"sec-ch-ua-mobile", "?0"},
0x02: {"sec-ch-ua-platform", "\"Windows\""},
0x03: {"accept", "*/*"},
0x04: {"accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"},
0x05: {"x-requested-with", "XMLHttpRequest"},
0x06: {"content-type", "application/json; charset=UTF-8"},
0x07: {"content-type", "text/plain;charset=UTF-8"},
0x08: {"sec-ch-ua-arch", "\"x86\""},
0x09: {"sec-ch-ua-bitness", "\"64\""},
0x0A: {"sec-gpc", "1"},
0x0B: {"connection", "keep-alive"},
0x0C: {"accept-language", "en-US,en;q=0.5"},
0x0D: {"accept-encoding", "gzip, deflate, br, zstd"},
0x0E: {"content-type", "application/json"},
0x0F: {"sec-fetch-mode", "cors"},
0x10: {"content-type", "application/x-www-form-urlencoded"},
0x11: {"sec-fetch-site", "cross-site"},
0x12: {"sec-fetch-site", "same-origin"},
0x13: {"sec-fetch-dest", "script"},
0x14: {"cache-control", "no-cache"},
0x15: {"pragma", "no-cache"},
0x16: {"sec-fetch-dest", "empty"},
0x17: {"sec-fetch-mode", "no-cors"},
0x18: {"cache-control", "no-cache, no-store"},
0x19: {"accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"},
0x1A: {"upgrade-insecure-requests", "1"},
0x1B: {"content-type", "text/plain"},
0x1C: {"content-type", "application/json; charset=utf-8"},
0x1D: {"accept", "application/json"},
0x1E: {"sec-purpose", "prefetch;prerender"},
0x1F: {"accept", "image/avif,image/jxl,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"},
0x20: {"sec-fetch-dest", "image"},
0x21: {"sec-fetch-site", "same-site"},
0x22: {"sec-purpose", "prefetch"},
0x23: {"accept", "image/webp,*/*"},
0x24: {"accept", "application/json, text/plain, */*"},
0x25: {"accept", "application/json, text/javascript, */*; q=0.01"},
0x26: {"content-encoding", "gzip"},
0x27: {"service-worker", "script"},
0x28: {"content-type", "application/x-www-form-urlencoded; charset=UTF-8"},
0x29: {"content-type", "application/json+protobuf"},
0x2A: {"sec-fetch-mode", "same-origin"},
0x2B: {"access-control-request-method", "POST"},
0x2C: {"sec-fetch-dest", "style"},
0x2D: {"accept", "application/signed-exchange;v=b3;q=0.7,*/*;q=0.8"},
0x2E: {"accept", "text/html"},
0x2F: {"accept", "application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8"},
0x30: {"sec-fetch-dest", "font"},
0x31: {"accept", "text/event-stream"},
0x32: {"sec-fetch-mode", "navigate"},
0x33: {"content-length", "0"},
0x34: {"connection", "Upgrade"},
0x35: {"upgrade", "websocket"},
0x36: {"cache-control", "max-age=0"},
0x37: {"range", "bytes=0-"},
0x38: {"sec-fetch-dest", "document"},
0x39: {"sec-fetch-mode", "websocket"},
0x3A: {"sec-fetch-user", "?1"},
0x3B: {"access-control-request-method", "GET"},
0x3C: {"sec-ch-ua-mobile", "?1"},
0x3D: {"sec-ch-ua-platform", "\"macOS\""},
0x3E: {"sec-ch-ua-platform", "\"Linux\""},
0x3F: {"sec-ch-ua-platform", "\"Android\""},
0x40: {"sec-ch-ua-arch", "\"arm\""},
0x41: {"user-agent", ""},
0x42: {"sec-ch-ua-mobile", ""},
0x43: {"sec-ch-ua-platform", ""},
0x44: {"sec-ch-ua", ""},
0x45: {"accept", ""},
0x46: {"content-type", ""},
0x47: {"sec-ch-ua-platform-version", ""},
0x48: {"sec-ch-ua-arch", ""},
0x49: {"connection", ""},
0x4A: {"host", ""},
0x4B: {"sec-gpc", ""},
0x4C: {"accept-language", ""},
0x4D: {"sec-fetch-mode", ""},
0x4E: {"sec-fetch-site", ""},
0x4F: {"sec-fetch-dest", ""},
0x50: {"accept-encoding", ""},
0x51: {"referer", ""},
0x52: {"authorization", ""},
0x53: {"origin", ""},
0x54: {"cookie", ""},
0x55: {"cache-control", ""},
0x56: {"pragma", ""},
0x57: {"sec-purpose", ""},
0x58: {"content-length", ""},
0x59: {"prefer", ""},
0x5A: {"content-encoding", ""},
0x5B: {"access-control-request-method", ""},
0x5C: {"access-control-request-headers", ""},
0x5D: {"range", ""},
0x5E: {"sec-websocket-version", ""},
0x5F: {"upgrade", ""},
0x60: {"if-none-match", ""},
0x61: {"sec-websocket-extensions", ""},
0x62: {"sec-websocket-key", ""},
0x63: {"if-modified-since", ""},
0x64: {"x-payment", ""},
}
DECODING: Maps request header IDs to entries (check entry.value: empty=Format2, non-empty=Format1)
var ResponseHeaderStaticTable = map[byte]headerEntry{}/* 190 elements not displayed */
DECODING: Maps response header IDs to entries (check entry.value: empty=Format2, non-empty=Format1)
Functions ¶
func AppendUvarint ¶
func DebugRequest ¶ added in v0.0.5
DebugRequest returns a human-readable annotated representation of a QH request in wire format. It displays each field with its byte offset, hex values, and decoded description.
func DebugResponse ¶ added in v0.0.5
DebugResponse returns a human-readable annotated representation of a QH response in wire format. It displays each field with its byte offset, hex values, and decoded description.
func DecodeStatusCode ¶
convert compact format to HTTP status code
func IsRequestComplete ¶
func IsResponseComplete ¶
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a QH protocol client that manages connections to QH servers. It supports connection establishment with optional 0-RTT via DNS-based key exchange, automatic response decompression, and redirect handling.
func NewClient ¶
func NewClient(opts ...ClientOption) *Client
NewClient creates a new QH client with the specified options.
func (*Client) Close ¶
Close closes the client connection and releases associated resources. After calling Close, the client should not be used for further requests.
func (*Client) Connect ¶
Connect establishes a connection to a QH server at the specified address. The address should be in "host:port" format.
Connect performs concurrent DNS lookups to resolve the hostname and optionally retrieve the server's public key from a DNS TXT record (at _qotp.<host>) for 0-RTT connection establishment. If no valid DNS key is found, Connect falls back to a standard in-band key exchange handshake.
func (*Client) DELETE ¶
DELETE performs a DELETE request to the specified host and path. Returns the server's response or an error if the request fails.
func (*Client) GET ¶
GET performs a GET request to the specified host and path. Returns the server's response or an error if the request fails.
func (*Client) HEAD ¶
HEAD performs a HEAD request to the specified host and path. Returns only headers without a response body.
func (*Client) PATCH ¶
func (c *Client) PATCH( host, path string, body []byte, headers map[string]string, ) (*Response, error)
PATCH performs a PATCH request with the given body to the specified host and path. The Content-Length header is automatically set based on the body size.
func (*Client) POST ¶
func (c *Client) POST( host, path string, body []byte, headers map[string]string, ) (*Response, error)
POST performs a POST request with the given body to the specified host and path. The Content-Length header is automatically set based on the body size.
func (*Client) PUT ¶
PUT performs a PUT request with the given body to the specified host and path. The Content-Length header is automatically set based on the body size.
func (*Client) Request ¶
Request sends a QH request and returns the response. The redirectCount parameter tracks the number of redirects followed and should typically be 0 for initial requests.
This method handles automatic decompression of responses if the server uses compression and the client advertised support via Accept-Encoding.
type ClientOption ¶ added in v0.0.5
type ClientOption func(*Client)
ClientOption is a functional option for configuring a Client.
func WithClientKeyLogWriter ¶ added in v0.0.5
func WithClientKeyLogWriter(w io.Writer) ClientOption
func WithMaxRedirects ¶ added in v0.0.5
func WithMaxRedirects(limit int) ClientOption
WithMaxRedirects sets the maximum number of redirects to follow. If this limit is exceeded, the request will return an error. Default is 10.
func WithMaxResponseSize ¶ added in v0.0.5
func WithMaxResponseSize(size int) ClientOption
WithMaxResponseSize sets the maximum allowed response size in bytes. Responses exceeding this limit will return an error. Default is 50MB.
type Encoding ¶
type Encoding string
Encoding represents a compression encoding type used in Content-Encoding and Accept-Encoding headers.
type Handler ¶
Handler is a function type that processes QH requests and returns responses. Handlers are registered with the server to handle specific path and method combinations.
type Method ¶
type Method int
Method represents a QH method encoded as an integer for compact wire format. Methods are encoded in 3 bits.
const ( GET Method = iota // GET retrieves a resource POST // POST submits data to be processed PUT // PUT replaces a resource PATCH // PATCH partially modifies a resource DELETE // DELETE removes a resource HEAD // HEAD retrieves headers only OPTIONS // OPTIONS describes communication options )
QH method constants for use in QH requests. These are encoded as 3-bit values in the wire format.
type Request ¶
type Request struct {
Method Method // QH method (GET, POST, etc.)
Host string // Target hostname
Path string // Request path (e.g., "/api/users")
Version uint8 // Protocol version number
Headers map[string]string // Request headers as key-value pairs
Body []byte // Optional request body
}
Request represents a QH protocol request message. It contains the QH method, target host and path, protocol version, headers as key-value pairs, and an optional body.
func ParseRequest ¶
func (*Request) Format ¶
Format encodes a QH request into wire format bytes using varint length prefixes.
Wire format structure:
- 1 byte: Version (2 bits) | Method (3 bits) | Reserved (3 bits)
- varint: host length, followed by host bytes
- varint: path length, followed by path bytes
- varint: headers length, followed by encoded headers
- varint: body length, followed by body bytes
type Response ¶
type Response struct {
Version uint8 // Protocol version number
StatusCode int // QH status code
Headers map[string]string // Response headers as key-value pairs
Body []byte // Response body content
}
Response represents a QH protocol response message. It contains the protocol version, QH status code, headers, and body.
func JSONResponse ¶
JSONResponse creates an application/json response with the given status code and body. This is a convenience method for returning JSON responses.
func NewResponse ¶
NewResponse creates a new Response with the given status code, body, and headers. Any headers provided will override auto-generated headers.
func ParseResponse ¶
func TextResponse ¶
TextResponse creates a text/plain response with the given status code and body. This is a convenience method for returning plain text responses.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a QH protocol server that listens for incoming connections and routes requests to registered handlers. It supports automatic response compression and configurable request size limits.
func NewServer ¶
func NewServer(opts ...ServerOption) *Server
NewServer creates a new QH server with the specified options.
func (*Server) HandleFunc ¶
HandleFunc registers a handler for a given path and method.
type ServerOption ¶ added in v0.0.5
type ServerOption func(*Server)
ServerOption is a functional option for configuring a Server.
func WithMaxRequestSize ¶ added in v0.0.5
func WithMaxRequestSize(size int) ServerOption
WithMaxRequestSize sets the maximum allowed request size in bytes. Requests exceeding this limit will receive a 413 Payload Too Large response. Default is 10MB.
func WithMinCompressionSize ¶ added in v0.0.5
func WithMinCompressionSize(size int) ServerOption
WithMinCompressionSize sets the minimum response body size for compression. Responses smaller than this threshold will not be compressed. Default is 1KB.
func WithSupportedEncodings ¶ added in v0.0.5
func WithSupportedEncodings(encodings []Encoding) ServerOption
WithSupportedEncodings sets the compression encodings the server supports. The server will use the first client-preferred encoding that the server also supports. Default is [Zstd, Brotli, Gzip].
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
client
command
|
|
|
client-concurrent
command
|
|
|
server
command
|
|
|
server-concurrent
command
|