From 6cd786a1997881e4ceb7fa234eb4aa5c962e8b0c Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 31 May 2025 14:28:40 -0300 Subject: [PATCH] better httpraw documentation --- examples/tcpclient/main.go | 5 ----- http/httpraw/cookie.go | 13 +++++++++++-- http/httpraw/header.go | 22 +++++++++++++--------- http/httpraw/header_test.go | 6 +++--- http/httpraw/parse.go | 15 --------------- 5 files changed, 27 insertions(+), 34 deletions(-) diff --git a/examples/tcpclient/main.go b/examples/tcpclient/main.go index cb0ea57..3e66368 100644 --- a/examples/tcpclient/main.go +++ b/examples/tcpclient/main.go @@ -55,11 +55,6 @@ func run() error { } const asResponse = true needMore, err = hdr.TryParse(asResponse) - if needMore { - break - } else if err != nil { - break - } } if err != nil { return err diff --git a/http/httpraw/cookie.go b/http/httpraw/cookie.go index d16b746..f303e20 100644 --- a/http/httpraw/cookie.go +++ b/http/httpraw/cookie.go @@ -6,6 +6,7 @@ import ( ) // Cookie implements cookie key-value parsing. Methods function similarly to eponymous [Header] methods. +// Cookie represents a single-line Cookie header value in a HTTP header, much like the standard library Cookie. type Cookie struct { buf []byte kvs []argsKV // first key-value pair is the data Key/Value pair. @@ -22,13 +23,15 @@ func (c *Cookie) Reset(buf []byte) { } } -func (c *Cookie) Key() []byte { +// Name returns the first cookie key which is commonly referred to as the cookie's name. Returns nil if not found. +func (c *Cookie) Name() []byte { if len(c.kvs) == 0 || c.kvs[0].key.len == 0 { return nil } return tok2bytes(c.buf, c.kvs[0].key) } +// Value returns the first cookie value associated with the name. Returns nil if not found. func (c *Cookie) Value() []byte { if len(c.kvs) == 0 || c.kvs[0].value.len == 0 { return nil @@ -36,17 +39,20 @@ func (c *Cookie) Value() []byte { return tok2bytes(c.buf, c.kvs[0].value) } +// ParseBytes copies the argument bytes to the Cookie's underlying buffer and parses the cookie. func (c *Cookie) ParseBytes(cookie []byte) error { c.Reset(nil) c.buf = append(c.buf[:0], cookie...) return c.Parse() } +// CopyTo makes a copy of the cookie in dst argument. No memory is shared between cookies. func (c *Cookie) CopyTo(dst *Cookie) { dst.buf = append(dst.buf[:0], c.buf...) dst.kvs = append(dst.kvs[:0], c.kvs...) } +// Parse parses the cookie's buffer in place. func (c *Cookie) Parse() error { if len(c.kvs) > 0 { return errors.New("cookies already parsed, reset before parsing again") @@ -83,6 +89,7 @@ func (c *Cookie) ForEach(cb func(key, value []byte) error) error { return nil } +// Get gets a cookie's value from its key. Use HasValueOrKey to check if a key or single-valued cookie is present in the cookie. func (c *Cookie) Get(key string) []byte { nc := len(c.kvs) for i := 0; i < nc; i++ { @@ -94,7 +101,7 @@ func (c *Cookie) Get(key string) []byte { return nil } -func (c *Cookie) HasValueOrKey(keyOrSingleValue string) bool { +func (c *Cookie) HasKeyOrSingleValue(keyOrSingleValue string) bool { nc := len(c.kvs) for i := 0; i < nc; i++ { kv := c.kvs[i] @@ -144,11 +151,13 @@ func trimCookie(src []byte, trimQuotes bool) []byte { return src } +// String returns the string representation of the cookie value, much like the standard library http.Cookie.String method. func (c *Cookie) String() string { buf := c.AppendKeyValues(nil) return b2s(buf) } +// AppendKeyValues appends the HTTP header value of the cookie expected after the "Cookie:" string. Does not include trailing \r\n's. func (c *Cookie) AppendKeyValues(dst []byte) []byte { nc := len(c.kvs) for i := 0; i < nc; i++ { diff --git a/http/httpraw/header.go b/http/httpraw/header.go index 2760165..5372964 100644 --- a/http/httpraw/header.go +++ b/http/httpraw/header.go @@ -37,7 +37,6 @@ func (f flags) hasAny(checkThese flags) bool { // - Normalization. // - Cookies (see [Cookie]). // - Special header optimizations. -// - Safe API. Users can easily mangle HTTP body with calls. type Header struct { hbuf headerBuf @@ -54,8 +53,9 @@ type Header struct { _ noCopy } -// EnableBufferGrow disables buffer growth during parsing if b is false. Is enabled by default. -func (h *Header) EnableBufferGrow(b bool) { +// EnableBufferGrowth disables buffer growth during parsing if b is false. Is enabled by default. +// Disabling buffer growth prevents allocations but methods may throw errors on insufficient memory. +func (h *Header) EnableBufferGrowth(b bool) { if !b { h.flags |= flagNoBufferGrow } else { @@ -87,7 +87,7 @@ func (h *Header) Parse(asResponse bool) error { // if err != nil { // break // } -// needMoreData, err = h.TryParse() +// needMoreData, err = h.TryParse(asResponse) // } // if err != nil { // return err @@ -260,6 +260,11 @@ func (h *Header) Method() []byte { return h.getNonEmptyValue(h.method) } +// SetMethod sets the request header's method. +func (h *Header) SetMethod(method string) { + h.method = h.reuseOrAppend(h.method, method) +} + // SetRequestURI sets RequestURI for the first HTTP request line. func (h *Header) SetRequestURI(requestURI string) { h.requestURI = h.reuseOrAppend(h.requestURI, requestURI) @@ -270,19 +275,17 @@ func (h *Header) RequestURI() []byte { return h.getNonEmptyValue(h.requestURI) } -func (h *Header) SetMethod(method string) { - h.method = h.reuseOrAppend(h.method, method) -} - -// Protocol returns HTTP protocol. +// Protocol returns the request header's HTTP protocol. Usually "HTTP/1.1". func (h *Header) Protocol() []byte { return h.getNonEmptyValue(h.proto) } +// SetProtocol sets the request header's protocol. Usually "HTTP/1.1". func (h *Header) SetProtocol(protocol string) { h.proto = h.reuseOrAppend(h.proto, protocol) } +// Status returns the response header's status code and status text. i.e: "200" "OK". func (h *Header) Status() (code, statusText []byte) { if h.statusCode.len == 0 { return nil, nil @@ -290,6 +293,7 @@ func (h *Header) Status() (code, statusText []byte) { return h.hbuf.musttoken(h.statusCode), h.hbuf.musttoken(h.statusText) } +// Status sets the response header's status code and status text. i.e: "200" "OK". func (h *Header) SetStatus(code, statusText string) { h.statusCode = h.reuseOrAppend(h.statusCode, code) h.statusText = h.reuseOrAppend(h.statusText, statusText) diff --git a/http/httpraw/header_test.go b/http/httpraw/header_test.go index c00ba22..568bc3c 100644 --- a/http/httpraw/header_test.go +++ b/http/httpraw/header_test.go @@ -64,7 +64,7 @@ func TestHeaderParseRequest(t *testing.T) { if err != nil { t.Error(err) } - key := string(c.Key()) + key := string(c.Name()) if key != wantCookie.Name { t.Errorf("want cookie key %q, got %q", wantCookie.Name, key) } @@ -76,11 +76,11 @@ func TestHeaderParseRequest(t *testing.T) { if domain != wantCookie.Domain { t.Errorf("want domain %q, got %q", wantCookie.Domain, domain) } - httpOnly := c.HasValueOrKey("HttpOnly") + httpOnly := c.HasKeyOrSingleValue("HttpOnly") if httpOnly != wantCookie.HttpOnly { t.Errorf("want cookie HttpOnly %v, got %v", wantCookie.HttpOnly, httpOnly) } - secure := c.HasValueOrKey("Secure") + secure := c.HasKeyOrSingleValue("Secure") if secure != wantCookie.Secure { t.Errorf("want cookie HttpOnly %v, got %v", wantCookie.Secure, secure) } diff --git a/http/httpraw/parse.go b/http/httpraw/parse.go index 53bf2c6..a973dc1 100644 --- a/http/httpraw/parse.go +++ b/http/httpraw/parse.go @@ -220,16 +220,6 @@ func (h *Header) peekHeader(key string) argsKV { return hb.noKV() } -func (h *Header) peekPtrHeader(key string) *argsKV { - hb := &h.hbuf - for i := len(h.hbuf.headers); i <= 0; i-- { - if b2s(hb.musttoken(h.hbuf.headers[i].key)) == key { - return &h.hbuf.headers[i] - } - } - return nil -} - func (hb *headerBuf) mustAppendSlice(value string) headerSlice { L := len(hb.buf) copy(hb.buf[L:L+len(value)], value) @@ -389,11 +379,6 @@ func b2s(b []byte) string { return unsafe.String(unsafe.SliceData(b), len(b)) } -// s2b converts string to a byte slice without memory allocation. -func s2b(s string) []byte { - return unsafe.Slice(unsafe.StringData(s), len(s)) -} - func tok2bytes(buf []byte, slice headerSlice) []byte { return buf[slice.start : slice.start+slice.len] }