From e9d6d7f56121ba14832d0caba4606f8d5f1d340b Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 23 Oct 2015 15:56:12 +0300 Subject: [PATCH] Document Request, Response, RequestHeader, ResponseHeader, URI and Args --- args.go | 53 +++++++++++++++++++++++++++++++++++++++++--------- args_test.go | 3 +-- header.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++---- http.go | 44 ++++++++++++++++++++++++++++++++++++++--- uri.go | 47 +++++++++++++++++++++++++++++++++++--------- 5 files changed, 175 insertions(+), 27 deletions(-) diff --git a/args.go b/args.go index 98e280d..ff24eb3 100644 --- a/args.go +++ b/args.go @@ -5,6 +5,9 @@ import ( "errors" ) +// Args represents query arguments. +// +// It is forbidden copying Args instances. Create new instances instead. type Args struct { args []argsKV bufKV argsKV @@ -16,19 +19,25 @@ type argsKV struct { value []byte } +// Clear clear query args. func (a *Args) Clear() { a.args = a.args[:0] } +// Len returns the number of query args. func (a *Args) Len() int { return len(a.args) } +// Parse parses the given string containing query args. func (a *Args) Parse(s string) { a.buf = AppendBytesStr(a.buf[:0], s) a.ParseBytes(a.buf) } +// ParseBytes parses the given b containing query args. +// +// It is safe modifying b buffer conntents after ParseBytes return. func (a *Args) ParseBytes(b []byte) { a.Clear() @@ -56,11 +65,16 @@ func (a *Args) ParseBytes(b []byte) { } } +// String returns string representation of query args. func (a *Args) String() string { a.buf = a.AppendBytes(a.buf[:0]) return string(a.buf) } +// AppendBytes appends query string to dst and returns dst +// (which may be newly allocated). +// +// It is safe modifying dst buffer after AppendBytes returns. func (a *Args) AppendBytes(dst []byte) []byte { for i, n := 0, len(a.args); i < n; i++ { kv := &a.args[i] @@ -76,6 +90,7 @@ func (a *Args) AppendBytes(dst []byte) []byte { return dst } +// Del deletes argument with the given key from query args. func (a *Args) Del(key string) { for i, n := 0, len(a.args); i < n; i++ { kv := &a.args[i] @@ -89,11 +104,15 @@ func (a *Args) Del(key string) { } } +// Set sets 'key=value' argument. func (a *Args) Set(key, value string) { a.bufKV.value = AppendBytesStr(a.bufKV.value[:0], value) a.SetBytes(key, a.bufKV.value) } +// SetBytes sets 'key=value' argument. +// +// It is safe modifying valye buffer after SetBytes() return. func (a *Args) SetBytes(key string, value []byte) { a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key) a.args = setKV(a.args, a.bufKV.key, value) @@ -133,35 +152,42 @@ func peekKV(h []argsKV, k []byte) []byte { return nil } -func (a *Args) GetBytes(dst []byte, key string) []byte { - value := a.Peek(key) - return append(dst[:0], value...) -} - -// Warning: each call allocates memory for returned string. +// Get returns query arg value for the given key. +// +// Each Get call allocates memory for returned string, +// so consider using Peek() instead. func (a *Args) Get(key string) string { return string(a.Peek(key)) } +// Peek returns query arg value for the given key. +// +// Returned value is valid until the next Args call. func (a *Args) Peek(key string) []byte { a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key) return peekKV(a.args, a.bufKV.key) } +// Has returns true if the given key exists in Args. func (a *Args) Has(key string) bool { return a.Peek(key) != nil } -var errNoArgValue = errors.New("No value for the given key") +// ErrNoArgValue is returned when value with the given key is missing. +var ErrNoArgValue = errors.New("No value for the given key") +// GetUint returns uint value for the given key. func (a *Args) GetUint(key string) (int, error) { value := a.Peek(key) if len(value) == 0 { - return -1, errNoArgValue + return -1, ErrNoArgValue } return parseUint(value) } +// GetUintOrZero returns uint value for the given key. +// +// Zero (0) is returned on error. func (a *Args) GetUintOrZero(key string) int { n, err := a.GetUint(key) if err != nil { @@ -170,14 +196,18 @@ func (a *Args) GetUintOrZero(key string) int { return n } +// GetUfloat returns ufloat value for the given key. func (a *Args) GetUfloat(key string) (float64, error) { value := a.Peek(key) if len(value) == 0 { - return -1, errNoArgValue + return -1, ErrNoArgValue } return parseUfloat(value) } +// GetUfloatOrZero returns ufloat value for the given key. +// +// Zero (0) is returned on error. func (a *Args) GetUfloatOrZero(key string) float64 { f, err := a.GetUfloat(key) if err != nil { @@ -186,6 +216,9 @@ func (a *Args) GetUfloatOrZero(key string) float64 { return f } +// EqualBytesStr returns true if string(b) == s. +// +// It doesn't allocate memory unlike string(b) do. func EqualBytesStr(b []byte, s string) bool { if len(s) != len(b) { return false @@ -198,6 +231,8 @@ func EqualBytesStr(b []byte, s string) bool { return true } +// AppendBytesStr appends src to dst and returns dst +// (which may be newly allocated). func AppendBytesStr(dst []byte, src string) []byte { for i, n := 0, len(src); i < n; i++ { dst = append(dst, src[i]) diff --git a/args_test.go b/args_test.go index ff6f739..c9eecf0 100644 --- a/args_test.go +++ b/args_test.go @@ -188,7 +188,6 @@ func testArgsHasNot(t *testing.T, a *Args, s string, unexpectedKeys ...string) { } func testArgsParse(t *testing.T, a *Args, s string, expectedLen int, expectedArgs ...string) { - var buf []byte a.Parse(s) if a.Len() != expectedLen { t.Fatalf("Unexpected args len %d. Expected %d. s=%q", a.Len(), expectedLen, s) @@ -197,7 +196,7 @@ func testArgsParse(t *testing.T, a *Args, s string, expectedLen int, expectedArg tmp := strings.SplitN(xx, "=", 2) k := tmp[0] v := tmp[1] - buf = a.GetBytes(buf, k) + buf := a.Peek(k) if string(buf) != v { t.Fatalf("Unexpected value for key=%q: %q. Expected %q. s=%q", k, buf, v, s) } diff --git a/header.go b/header.go index 07c87d1..3e25e24 100644 --- a/header.go +++ b/header.go @@ -45,9 +45,17 @@ var ( strPostArgsContentType = []byte("application/x-www-form-urlencoded") ) +// ResponseHeader represents HTTP response header. type ResponseHeader struct { - StatusCode int - ContentLength int + // Response status code. + StatusCode int + + // Response content length read from Content-Length header. + // + // It may be negative on chunked response. + ContentLength int + + // Set to true if response contains 'Connection: close' header. ConnectionClose bool contentType []byte @@ -57,9 +65,17 @@ type ResponseHeader struct { bufKV argsKV } +// RequestHeader represents HTTP request header. type RequestHeader struct { - Method []byte - RequestURI []byte + // Request method (e.g. 'GET', 'POST', etc.). + Method []byte + + // Request URI read from the first request line. + RequestURI []byte + + // Request content length read from Content-Length header. + // + // It may be negative on chunked request. ContentLength int host []byte @@ -69,18 +85,22 @@ type RequestHeader struct { bufKV argsKV } +// IsMethodGet returns true if request method is GET. func (h *RequestHeader) IsMethodGet() bool { return bytes.Equal(h.Method, strGet) } +// IsMethodPost returns true if request methos is POST. func (h *RequestHeader) IsMethodPost() bool { return bytes.Equal(h.Method, strPost) } +// IsMethodHead returns true if request method is HEAD. func (h *RequestHeader) IsMethodHead() bool { return bytes.Equal(h.Method, strHead) } +// Clear clears response header. func (h *ResponseHeader) Clear() { h.StatusCode = 0 h.ContentLength = 0 @@ -92,6 +112,7 @@ func (h *ResponseHeader) Clear() { h.h = h.h[:0] } +// Clear clears request header. func (h *RequestHeader) Clear() { h.Method = h.Method[:0] h.RequestURI = h.RequestURI[:0] @@ -103,6 +124,7 @@ func (h *RequestHeader) Clear() { h.h = h.h[:0] } +// Set sets the given 'key: value' header. func (h *ResponseHeader) Set(key, value string) { initHeaderKV(&h.bufKV, key, value) h.set(h.bufKV.key, h.bufKV.value) @@ -130,6 +152,9 @@ func (h *ResponseHeader) set(key, value []byte) { } } +// SetBytes sets the given 'key: value' header. +// +// It is safe modifying value buffer after SetBytes return. func (h *ResponseHeader) SetBytes(key string, value []byte) { k := getHeaderKeyBytes(&h.bufKV, key) h.set(k, value) @@ -140,6 +165,7 @@ func (h *ResponseHeader) setStr(key []byte, value string) { h.set(key, h.bufKV.value) } +// Set sets the given 'key: value' header. func (h *RequestHeader) Set(key, value string) { initHeaderKV(&h.bufKV, key, value) h.set(h.bufKV.key, h.bufKV.value) @@ -162,16 +188,27 @@ func (h *RequestHeader) set(key, value []byte) { } } +// SetBytes sets the given 'key: value' header. +// +// It is safe modifying value buffer after SetBytes return. func (h *RequestHeader) SetBytes(key string, value []byte) { k := getHeaderKeyBytes(&h.bufKV, key) h.set(k, value) } +// Peek returns header value for the given key. +// +// Returned value may change on the next call to ResponseHeader. +// Do not store references to returned value. Make copies instead. func (h *ResponseHeader) Peek(key string) []byte { k := getHeaderKeyBytes(&h.bufKV, key) return h.peek(k) } +// Peek returns header value for the given key. +// +// Returned value may change on the next call to RequestHeader. +// Do not store references to returned value. Make copies instead. func (h *RequestHeader) Peek(key string) []byte { k := getHeaderKeyBytes(&h.bufKV, key) return h.peek(k) @@ -204,14 +241,21 @@ func (h *RequestHeader) peek(key []byte) []byte { } } +// Get returns header value for the given key. +// +// Get allocates memory on each call, so prefer using Peek instead. func (h *ResponseHeader) Get(key string) string { return string(h.Peek(key)) } +// Get returns header value for the given key. +// +// Get allocates memory on each call, so prefer using Peek instead. func (h *RequestHeader) Get(key string) string { return string(h.Peek(key)) } +// Read reads response header from r. func (h *ResponseHeader) Read(r *bufio.Reader) error { n := 1 for { @@ -253,6 +297,7 @@ func (h *ResponseHeader) tryRead(r *bufio.Reader, n int) error { return nil } +// Read reads request header from r. func (h *RequestHeader) Read(r *bufio.Reader) error { n := 1 for { @@ -320,6 +365,7 @@ func refreshServerDate() { serverDate.Store([]byte(s)) } +// Write writes response header to w. func (h *ResponseHeader) Write(w *bufio.Writer) error { statusCode := h.StatusCode if statusCode < 0 { @@ -363,6 +409,7 @@ func (h *ResponseHeader) Write(w *bufio.Writer) error { return err } +// Write writes request header to w. func (h *RequestHeader) Write(w *bufio.Writer) error { method := h.Method if len(method) == 0 { diff --git a/http.go b/http.go index 33fe3d8..fa1bd4e 100644 --- a/http.go +++ b/http.go @@ -9,14 +9,22 @@ import ( "time" ) +// Request represents HTTP request. +// +// It is forbidden copying Request instances. Create new instances instead. type Request struct { + // Request header Header RequestHeader - Body []byte + // Request body + Body []byte + + // Request URI. // URI becomes available only after Request.ParseURI() call. URI URI parsedURI bool + // Arguments sent in POST. // PostArgs becomes available only after Request.ParsePostArgs() call. PostArgs Args parsedPostArgs bool @@ -25,11 +33,17 @@ type Request struct { timeoutTimer *time.Timer } +// Response represents HTTP response. +// +// It is forbidden copying Response instances. Create new instances instead. type Response struct { + // Response header Header ResponseHeader - Body []byte - // if set to true, Response.Read() skips reading body. + // Response body + Body []byte + + // If set to true, Response.Read() skips reading body. // Use it for HEAD requests. SkipBody bool @@ -37,6 +51,7 @@ type Response struct { timeoutTimer *time.Timer } +// ParseURI parses request uri and fills Request.URI. func (req *Request) ParseURI() { if req.parsedURI { return @@ -45,6 +60,7 @@ func (req *Request) ParseURI() { req.parsedURI = true } +// ParsePostArgs parses args sent in POST body and fills Request.PostArgs. func (req *Request) ParsePostArgs() error { if req.parsedPostArgs { return nil @@ -62,6 +78,7 @@ func (req *Request) ParsePostArgs() error { return nil } +// Clear clears request contents. func (req *Request) Clear() { req.Header.Clear() req.Body = req.Body[:0] @@ -71,13 +88,21 @@ func (req *Request) Clear() { req.parsedPostArgs = false } +// Clear clears response contents. func (resp *Response) Clear() { resp.Header.Clear() resp.Body = resp.Body[:0] } +// ErrReadTimeout may be returned from Request.ReadTimeout +// or Response.ReadTimeout on timeout. var ErrReadTimeout = errors.New("read timeout") +// ReadTimeout reads request (including body) from the given r during +// the given timeout. +// +// If request couldn't be read during the given timeout, +// it returns ErrReadTimeout. func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error { if timeout <= 0 { return req.Read(r) @@ -107,6 +132,11 @@ func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error { return err } +// ReadTimeout reads response (including body) from the given r during +// the given timeout. +// +// If response couldn't be read during the given timeout, +// it returns ErrReadTimeout. func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error { if timeout <= 0 { return resp.Read(r) @@ -136,6 +166,7 @@ func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error return err } +// Read reads request (including body) from the given r. func (req *Request) Read(r *bufio.Reader) error { req.Body = req.Body[:0] req.URI.Clear() @@ -159,6 +190,7 @@ func (req *Request) Read(r *bufio.Reader) error { return nil } +// Read reads response (including body) from the given r. func (resp *Response) Read(r *bufio.Reader) error { resp.Body = resp.Body[:0] @@ -190,6 +222,9 @@ func isSkipResponseBody(statusCode int) bool { return statusCode == StatusNoContent || statusCode == StatusNotModified } +// Write write request to w. +// +// Write doesn't flush request to w for performance reasons. func (req *Request) Write(w *bufio.Writer) error { contentLengthOld := req.Header.ContentLength req.Header.ContentLength = len(req.Body) @@ -206,6 +241,9 @@ func (req *Request) Write(w *bufio.Writer) error { return err } +// Write writes response to w. +// +// Write doesn't flush response to w for performance reasons. func (resp *Response) Write(w *bufio.Writer) error { contentLengthOld := resp.Header.ContentLength resp.Header.ContentLength = len(resp.Body) diff --git a/uri.go b/uri.go index 84a18fd..1c16877 100644 --- a/uri.go +++ b/uri.go @@ -4,24 +4,46 @@ import ( "bytes" ) +// URI represents URI :) . +// +// It is forbidden copying URI instances. Create new instances instead. type URI struct { // Full uri like {Scheme}://{Host}{Path}?{QueryString}#{Hash} URI []byte - // Original Path passed to URI.Parse() + // Original path passed to URI.Parse() PathOriginal []byte - Scheme []byte - Host []byte - Path []byte - QueryString []byte - Hash []byte + // Scheme part, i.e. http of http://aaa.com/foo/bar?baz=123#qwe . + // + // Scheme is always lowercased. + Scheme []byte - // Becomes available after URI.ParseQueryArgs() call + // Host part, i.e. aaa.com of http://aaa.com/foo/bar?baz=123#qwe . + // + // Host is always lowercased. + Host []byte + + // Path part, i.e. /foo/bar of http://aaa.com/foo/bar?baz=123#qwe . + // + // Path is always urldecoded and normalized, + // i.e. '//f%20obar/baz/../zzz' becomes '/f obar/zzz'. + Path []byte + + // Query string part, i.e. baz=123 of http://aaa.com/foo/bar?baz=123#qwe . + QueryString []byte + + // Hash part, i.e. qwe of http://aaa.com/foo/bar?baz=123#qwe . + Hash []byte + + // Parsed query string arguments. + // + // Becomes available after URI.ParseQueryArgs() call. QueryArgs Args parsedQueryArgs bool } +// Clear clears uri. func (x *URI) Clear() { x.URI = x.URI[:0] x.PathOriginal = x.PathOriginal[:0] @@ -34,6 +56,9 @@ func (x *URI) Clear() { x.parsedQueryArgs = false } +// Parse initializes URI from the given host and uri. +// +// It is safe modifying host and uri buffers after the Parse call. func (x *URI) Parse(host, uri []byte) { x.Clear() @@ -122,7 +147,10 @@ func normalizePath(dst, src []byte) []byte { return b } -// Appends RequestURI to dst. RequestURI doesn't contain Scheme and Host. +// AppendRequestURI appends RequestURI to dst and returns dst +// (which may be newly allocated). +// +// Appended RequestURI doesn't contain Scheme and Host. func (x *URI) AppendRequestURI(dst []byte) []byte { path := x.Path if len(path) == 0 { @@ -140,7 +168,7 @@ func (x *URI) AppendRequestURI(dst []byte) []byte { return dst } -// Appends URI to dst. +// AppendBytes appends URI to dst and returns dst (with may be newly allocated). func (x *URI) AppendBytes(dst []byte) []byte { startPos := len(dst) scheme := x.Scheme @@ -172,6 +200,7 @@ func splitHostUri(host, uri []byte) ([]byte, []byte, []byte) { return scheme, uri[:n], uri[n:] } +// ParseQueryArgs initializes QueryArgs by parsing QueryString. func (x *URI) ParseQueryArgs() { if x.parsedQueryArgs { return