mirror of
https://github.com/soypat/lneto.git
synced 2026-08-12 10:53:44 +00:00
mid refactor of KVBuffer into Header
This commit is contained in:
@@ -434,7 +434,7 @@ func (exch *Exchange) RequestParseForm(dst *httpraw.Form, buf []byte) error {
|
||||
|
||||
length, present, err := exch.RequestContentLength()
|
||||
if !present {
|
||||
dst.Reset(nil)
|
||||
dst.Reset(nil, 0)
|
||||
return nil // No length is no body, RFC 9112 6.3.
|
||||
} else if err != nil {
|
||||
return err
|
||||
@@ -454,7 +454,7 @@ func (exch *Exchange) RequestParseForm(dst *httpraw.Form, buf []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
dst.Reset(buf)
|
||||
dst.Reset(buf, 0)
|
||||
return dst.Parse()
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -77,8 +77,17 @@ func (c *Cookie) Get(key string) []byte { return c.kv.Get(key) }
|
||||
|
||||
// HasKeyOrSingleValue returns true if the cookie contains a pair with the given
|
||||
// key or a valueless attribute with the given text, i.e: "Secure" or "HttpOnly".
|
||||
// It cannot defer to [KVBuffer.Present]: parseCookie stores a valueless
|
||||
// attribute with an empty key and the text as the value, so a key-only lookup
|
||||
// would never match one.
|
||||
func (c *Cookie) HasKeyOrSingleValue(keyOrSingleValue string) bool {
|
||||
return c.kv.Present(keyOrSingleValue)
|
||||
for i, nc := 0, c.kv.Len(); i < nc; i++ {
|
||||
k, v := c.kv.At(i)
|
||||
if (len(k) == 0 && b2s(v) == keyOrSingleValue) || b2s(k) == keyOrSingleValue {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseCookie parses a cookie inside cookie buffer and adds it to cookie buffer..
|
||||
|
||||
+32
-51
@@ -8,39 +8,35 @@ package httpraw
|
||||
// undecoded, until [Form.Decode] rewrites them in place. The caller bounds the
|
||||
// data: Form parses the buffer it is handed and reads nothing more.
|
||||
type Form struct {
|
||||
buf []byte
|
||||
kvs []argsKV
|
||||
kv KVBuffer
|
||||
}
|
||||
|
||||
func (f *Form) EnableBufferGrowth(enableGrowth bool) { f.kv.EnableBufferGrowth(enableGrowth) }
|
||||
|
||||
// Reset discards parsed pairs and sets the buffer to parse in place.
|
||||
// If buf is nil the current buffer is reused.
|
||||
func (f *Form) Reset(buf []byte) {
|
||||
if buf == nil {
|
||||
buf = f.buf[:0]
|
||||
}
|
||||
*f = Form{
|
||||
buf: buf,
|
||||
kvs: f.kvs[:0],
|
||||
}
|
||||
func (f *Form) Reset(buf []byte, capKV int) {
|
||||
f.kv.Reset(buf, capKV)
|
||||
}
|
||||
|
||||
// ParseBytes copies the argument bytes to the Form's underlying buffer and parses them.
|
||||
func (f *Form) ParseBytes(b []byte) error {
|
||||
f.Reset(nil)
|
||||
f.buf = append(f.buf[:0], b...)
|
||||
f.Reset(nil, 0)
|
||||
err := f.kv.ReadFromBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.Parse()
|
||||
}
|
||||
|
||||
// Parse parses the form's buffer in place.
|
||||
func (f *Form) Parse() error {
|
||||
f.kvs = f.kvs[:0]
|
||||
key, value, rest := NextQueryPair(f.buf)
|
||||
f.kv.discardKVs()
|
||||
key, value, rest := NextQueryPair(f.kv.buf)
|
||||
for key != nil {
|
||||
kv := argsKV{key: bytes2tok(f.buf, key)}
|
||||
if value != nil {
|
||||
kv.value = bytes2tok(f.buf, value)
|
||||
if !f.kv.setInternal(key, value) {
|
||||
return errOOM
|
||||
}
|
||||
f.kvs = append(f.kvs, kv)
|
||||
key, value, rest = NextQueryPair(rest)
|
||||
}
|
||||
return nil
|
||||
@@ -50,66 +46,51 @@ func (f *Form) Parse() error {
|
||||
// '+' with the bytes they encode. Decoding only shrinks, so no memory is added.
|
||||
func (f *Form) Decode() error {
|
||||
const plusAsSpace = true // Form encoded data, unlike a path.
|
||||
for i := range f.kvs {
|
||||
kv := &f.kvs[i]
|
||||
n, err := CopyDecodedPercentURL(tok2bytes(f.buf, kv.key), tok2bytes(f.buf, kv.key), plusAsSpace)
|
||||
nkvs := f.kv.Len()
|
||||
for i := range nkvs {
|
||||
k, v := f.kv.At(i)
|
||||
nk, err := CopyDecodedPercentURL(k, k, plusAsSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kv.key.len = tokint(n)
|
||||
if !kv.HasValue() {
|
||||
} else if len(v) == 0 {
|
||||
if nk != len(k) {
|
||||
f.kv.setAt(i, k, v)
|
||||
}
|
||||
continue
|
||||
}
|
||||
n, err = CopyDecodedPercentURL(tok2bytes(f.buf, kv.value), tok2bytes(f.buf, kv.value), plusAsSpace)
|
||||
nv, err := CopyDecodedPercentURL(v, v, plusAsSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kv.value.len = tokint(n)
|
||||
if nk != len(k) || nv != len(v) {
|
||||
f.kv.setAt(i, k[:nk], v[:nv])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the amount of key-value pairs parsed.
|
||||
func (f *Form) Len() int { return len(f.kvs) }
|
||||
func (f *Form) Len() int { return f.kv.Len() }
|
||||
|
||||
// Pair returns the i'th key-value pair in wire order. The value is nil for a
|
||||
// pair with no '=', i.e: "ok" in "ok&q=go", which distinguishes it from "ok="
|
||||
// where the value is present and empty.
|
||||
func (f *Form) Pair(i int) (key, value []byte) {
|
||||
kv := f.kvs[i]
|
||||
key = tok2bytes(f.buf, kv.key)
|
||||
if kv.HasValue() {
|
||||
value = tok2bytes(f.buf, kv.value)
|
||||
}
|
||||
return key, value
|
||||
return f.kv.At(i)
|
||||
}
|
||||
|
||||
// Get returns the value of the first pair matching key, nil if absent or if the
|
||||
// pair has no value. Bytes are compared as stored, so call [Form.Decode] first
|
||||
// when keys may be encoded.
|
||||
func (f *Form) Get(key string) []byte {
|
||||
for i := range f.kvs {
|
||||
gotKey, value := f.Pair(i)
|
||||
if b2s(gotKey) == key {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (f *Form) Get(key string) []byte { return f.kv.Get(key) }
|
||||
|
||||
// Has returns true if key is present, with or without a value.
|
||||
func (f *Form) Has(key string) bool {
|
||||
for i := range f.kvs {
|
||||
if b2s(tok2bytes(f.buf, f.kvs[i].key)) == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (f *Form) Has(key string) bool { return f.kv.Present(key) }
|
||||
|
||||
// AppendKeyValues appends the form's wire representation to dst and returns it.
|
||||
func (f *Form) AppendKeyValues(dst []byte) []byte {
|
||||
for i := range f.kvs {
|
||||
nkv := f.kv.Len()
|
||||
for i := range nkv {
|
||||
key, value := f.Pair(i)
|
||||
if i > 0 {
|
||||
dst = append(dst, '&')
|
||||
|
||||
@@ -125,7 +125,7 @@ func TestFormParseReuseNoAlloc(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
allocs := testing.AllocsPerRun(100, func() {
|
||||
f.Reset(body)
|
||||
f.Reset(body, 0)
|
||||
f.Parse()
|
||||
})
|
||||
if allocs != 0 {
|
||||
|
||||
+81
-140
@@ -3,7 +3,6 @@ package httpraw
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -29,6 +28,7 @@ const (
|
||||
flagConnClose
|
||||
flagNoHTTP11
|
||||
flagMangledBuffer // set when header fields appended to buffer via Add,Set calls
|
||||
flagKVAppended // set after KV appended to buffer outside Read methods.
|
||||
flagReaderEOF
|
||||
// set if [Header.SetStatus] or [Header.SetStatusInt] has been called.
|
||||
FlagStatusSet
|
||||
@@ -57,30 +57,27 @@ type Header struct {
|
||||
// Response fields.
|
||||
statusCode headerSlice
|
||||
statusText headerSlice
|
||||
|
||||
flags Flags
|
||||
_ noCopy
|
||||
_ noCopy
|
||||
}
|
||||
|
||||
// Flags returns [Flags] to signal status code has been set, Connection:Close or other useful signals provided by flags.
|
||||
func (h *Header) Flags() Flags { return h.flags }
|
||||
func (h *Header) Flags() Flags { return h.hbuf.kv.flags }
|
||||
|
||||
// ConfigBufferGrowth configures the memory the header may use. Setting
|
||||
// outlives [Header.Reset]. Call before parsing/reading.
|
||||
//
|
||||
// enableBufferGrowth enables growing both the header buffer and the header key/value pair slice.
|
||||
func (h *Header) ConfigBufferGrowth(enableBufferGrowth bool) {
|
||||
if !enableBufferGrowth {
|
||||
h.flags |= flagNoBufferGrow
|
||||
} else {
|
||||
h.flags &^= flagNoBufferGrow
|
||||
}
|
||||
h.hbuf.kv.EnableBufferGrowth(enableBufferGrowth)
|
||||
}
|
||||
|
||||
// ParseBytes copies the bytes into buffer and parses the HTTP header. It fails if HTTP header data is incomplete.
|
||||
func (h *Header) ParseBytes(asResponse bool, b []byte) error {
|
||||
h.Reset(nil, 0)
|
||||
h.hbuf.readFromBytes(b)
|
||||
err := h.hbuf.kv.ReadFromBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.parse(asResponse)
|
||||
}
|
||||
|
||||
@@ -88,7 +85,7 @@ func (h *Header) ParseBytes(asResponse bool, b []byte) error {
|
||||
// It fails if HTTP data is incomplete.
|
||||
func (h *Header) Parse(asResponse bool) error {
|
||||
debuglog("http:parse:reset")
|
||||
h.Reset(h.hbuf.buf, 0)
|
||||
h.Reset(h.hbuf.kv.buf, 0)
|
||||
debuglog("http:parse:start")
|
||||
return h.parse(asResponse)
|
||||
}
|
||||
@@ -110,9 +107,10 @@ func (h *Header) Parse(asResponse bool) error {
|
||||
// return err
|
||||
// }
|
||||
func (h *Header) TryParse(asResponse bool) (needMoreData bool, err error) {
|
||||
if h.flags.HasAny(flagDoneParsingHeader) {
|
||||
flags := h.Flags()
|
||||
if flags.HasAny(flagDoneParsingHeader) {
|
||||
return false, errAlreadyParsed
|
||||
} else if h.flags.HasAny(flagMangledBuffer) {
|
||||
} else if flags.HasAny(flagMangledBuffer) {
|
||||
return false, errMangledBuffer
|
||||
}
|
||||
if asResponse && h.statusCode.len == 0 || !asResponse && h.requestTarget.start == 0 {
|
||||
@@ -121,80 +119,42 @@ func (h *Header) TryParse(asResponse bool) (needMoreData bool, err error) {
|
||||
return err == ErrNeedMoreData, err
|
||||
}
|
||||
}
|
||||
err = h.parseNextHeaders(h.flags)
|
||||
err = h.parseNextHeaders(flags)
|
||||
return err == ErrNeedMoreData, err
|
||||
}
|
||||
|
||||
// ParsingSuccess returns true if TryParse was successful, that is to say it returned needMoreData==false and err==nil.
|
||||
func (h *Header) ParsingSuccess() bool {
|
||||
return h.flags.HasAny(flagDoneParsingHeader)
|
||||
return h.Flags().HasAny(flagDoneParsingHeader)
|
||||
}
|
||||
|
||||
// ReadFromLimited reads at most maxBytesToRead from reader and appends them to underlying buffer.
|
||||
// Used to accumulate HTTP header for later parsing with [Header.TryParse].
|
||||
// If read is successful (read length>0) and reader returns [io.EOF] then ReadFromLimited will return a nil error.
|
||||
func (h *Header) ReadFromLimited(r io.Reader, maxBytesToRead int) (int, error) {
|
||||
if maxBytesToRead <= 0 {
|
||||
return 0, ErrSmallHeaderBuffer
|
||||
} else if h.flags.HasAny(flagMangledBuffer) {
|
||||
return 0, errMangledBuffer
|
||||
} else if h.flags.HasAny(flagReaderEOF) {
|
||||
return 0, io.EOF // Now we do return EOF.
|
||||
}
|
||||
free := h.BufferFree()
|
||||
if free < maxBytesToRead {
|
||||
if h.flags.HasAny(flagNoBufferGrow) {
|
||||
return 0, ErrSmallHeaderBuffer
|
||||
}
|
||||
h.hbuf.buf = slices.Grow(h.hbuf.buf, maxBytesToRead)
|
||||
}
|
||||
blen := len(h.hbuf.buf)
|
||||
b := h.hbuf.buf[blen:min(blen+maxBytesToRead, cap(h.hbuf.buf))]
|
||||
if len(b) == 0 {
|
||||
return 0, ErrSmallHeaderBuffer
|
||||
}
|
||||
n, err := r.Read(b)
|
||||
if err != nil && err == io.EOF {
|
||||
h.flags |= flagReaderEOF
|
||||
if n > 0 {
|
||||
err = nil // Nil-out error if read was succesful so as to not spook readers.
|
||||
}
|
||||
}
|
||||
h.hbuf.buf = h.hbuf.buf[:blen+n]
|
||||
return n, err
|
||||
return h.hbuf.kv.ReadLimited(r, maxBytesToRead)
|
||||
}
|
||||
|
||||
// ReadFromBytes appends argument buffer to underlying buffer.
|
||||
// Used to accumulate HTTP header for later parsing with [Header.TryParse].
|
||||
func (h *Header) ReadFromBytes(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, ErrSmallHeaderBuffer
|
||||
}
|
||||
free := h.BufferFree()
|
||||
if free < len(b) {
|
||||
if h.flags.HasAny(flagNoBufferGrow) {
|
||||
return 0, ErrSmallHeaderBuffer
|
||||
}
|
||||
h.hbuf.buf = slices.Grow(h.hbuf.buf, len(b))
|
||||
}
|
||||
h.hbuf.readFromBytes(b)
|
||||
return len(b), nil
|
||||
func (h *Header) ReadFromBytes(b []byte) error {
|
||||
return h.hbuf.kv.ReadFromBytes(b)
|
||||
}
|
||||
|
||||
// BufferReceived returns the amoung of bytes read during calls to Read* methods.
|
||||
// Returns 0 if buffer is invalid/mangled.
|
||||
func (h *Header) BufferReceived() int {
|
||||
if h.flags.HasAny(flagMangledBuffer | flagOOMReached) {
|
||||
if h.Flags().HasAny(flagMangledBuffer | flagOOMReached) {
|
||||
return 0
|
||||
}
|
||||
return len(h.hbuf.buf)
|
||||
return len(h.hbuf.kv.BufferRaw())
|
||||
}
|
||||
|
||||
// BufferParsed returns the amount of bytes parsed during a call to Parse* methods.
|
||||
// If the Parse* method completed without error then BufferParsed returns the header's length including the final "\r\n\r\n" text.
|
||||
// BufferParsed returns 0 if the buffer is invalid/mangled or if no header data has been parsed succesfully.
|
||||
func (h *Header) BufferParsed() int {
|
||||
if h.flags.HasAny(flagMangledBuffer | flagOOMReached) {
|
||||
if h.Flags().HasAny(flagMangledBuffer | flagOOMReached) {
|
||||
return 0
|
||||
}
|
||||
return h.hbuf.off
|
||||
@@ -202,13 +162,13 @@ func (h *Header) BufferParsed() int {
|
||||
|
||||
// BufferRaw returns the undeerlying buffer as stored currently in memory.
|
||||
// The length of the returned buffer is the used portion. Capacity of returned slice is [Header.BufferCapacity].
|
||||
func (h *Header) BufferRaw() []byte { return h.hbuf.buf }
|
||||
func (h *Header) BufferRaw() []byte { return h.hbuf.kv.BufferRaw() }
|
||||
|
||||
// BufferUsed returns the raw memory used.
|
||||
//
|
||||
// BufferUsed + BufferFree == BufferCapacity
|
||||
func (h *Header) BufferUsed() int {
|
||||
return len(h.hbuf.buf)
|
||||
return len(h.hbuf.kv.BufferRaw())
|
||||
}
|
||||
|
||||
// BufferFree returns amount of bytes free in underlying buffer.
|
||||
@@ -222,29 +182,12 @@ func (h *Header) BufferFree() int {
|
||||
//
|
||||
// BufferUsed + BufferFree == BufferCapacity
|
||||
func (h *Header) BufferCapacity() int {
|
||||
return cap(h.hbuf.buf)
|
||||
return cap(h.hbuf.kv.BufferRaw())
|
||||
}
|
||||
|
||||
// ForEach iterates over header key-value field tuples.
|
||||
func (h *Header) ForEach(cb func(key, value []byte) error) error {
|
||||
return h.hbuf.forEach(cb)
|
||||
}
|
||||
|
||||
func (hb *headerBuf) forEach(cb func(key, value []byte) error) error {
|
||||
nh := len(hb.headers)
|
||||
for i := range nh {
|
||||
kv := hb.headers[i]
|
||||
if !kv.isValid() {
|
||||
continue
|
||||
}
|
||||
key := hb.musttoken(kv.key)
|
||||
value := hb.musttoken(kv.value)
|
||||
err := cb(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
func (h *Header) ForEach(cb func(key, value []byte) bool) {
|
||||
h.hbuf.kv.ForEach(cb)
|
||||
}
|
||||
|
||||
// Reset discards all parsed data and sets the buffer data to buf. This method
|
||||
@@ -259,23 +202,21 @@ func (h *Header) Reset(buf []byte, numHeaderCapacity int) {
|
||||
const persistentFlags = flagNoBufferGrow
|
||||
debuglog("http:reset:hbuf")
|
||||
h.hbuf.reset(buf, numHeaderCapacity)
|
||||
if h.flags.HasAny(flagNoBufferGrow) && cap(h.hbuf.buf) < 32 {
|
||||
if h.Flags().HasAny(flagNoBufferGrow) && h.BufferCapacity() < 32 {
|
||||
panic("small buffer and flagNoBufferGrow set")
|
||||
}
|
||||
*h = Header{
|
||||
hbuf: h.hbuf,
|
||||
flags: h.flags & persistentFlags,
|
||||
}
|
||||
*h = Header{hbuf: h.hbuf}
|
||||
debuglog("http:reset:done")
|
||||
}
|
||||
|
||||
// Body returns the surplus data following headers. It is only valid as long as Parse* or Reset methods are not called.
|
||||
func (h *Header) Body() ([]byte, error) {
|
||||
debuglog("http:body")
|
||||
if h.flags.HasAny(flagMangledBuffer) {
|
||||
flags := h.Flags()
|
||||
if flags.HasAny(flagMangledBuffer) {
|
||||
return nil, errMangledBuffer
|
||||
} else if h.flags.HasAny(flagDoneParsingHeader) {
|
||||
return h.hbuf.buf[h.hbuf.off:], nil
|
||||
} else if flags.HasAny(flagDoneParsingHeader) {
|
||||
return h.BufferRaw()[h.hbuf.off:], nil
|
||||
}
|
||||
return nil, errUnparsed
|
||||
}
|
||||
@@ -303,48 +244,51 @@ func (h *Header) SetInt(key string, value int64, base int) {
|
||||
|
||||
// Set sets a key-value pair in the HTTP header.
|
||||
// Calling Set mangles the buffer.
|
||||
func (h *Header) Set(key, value string) {
|
||||
useKv := h.takeReusableSlot(key)
|
||||
if useKv == nil {
|
||||
h.appendHeader(key, value)
|
||||
} else {
|
||||
useKv.value = h.reuseOrAppend(useKv.value, value)
|
||||
}
|
||||
func (h *Header) Set(key, value string) (enoughSpace bool) {
|
||||
return h.hbuf.kv.Set(key, value)
|
||||
|
||||
// useKv := h.takeReusableSlot(key)
|
||||
// if useKv == nil {
|
||||
// h.appendHeader(key, value)
|
||||
// } else {
|
||||
// useKv.value = h.reuseOrAppend(useKv.value, value)
|
||||
// }
|
||||
}
|
||||
|
||||
// takeReusableSlot returns the valid key-value entry for key with the largest
|
||||
// value buffer (best candidate for in-place reuse) and invalidates any other
|
||||
// entries sharing the key. Returns nil if the key is not present.
|
||||
func (h *Header) takeReusableSlot(key string) *argsKV {
|
||||
hb := &h.hbuf
|
||||
// hb := &h.hbuf
|
||||
var useKv *argsKV
|
||||
for i := 0; i < len(hb.headers); i++ {
|
||||
// Search for key-value with largest buffer for value to store value reusing buffer.
|
||||
gotkv := &hb.headers[i]
|
||||
if gotkv.isValid() && b2s(hb.musttoken(gotkv.key)) == key {
|
||||
if useKv == nil {
|
||||
useKv = gotkv
|
||||
} else if gotkv.value.len > useKv.value.len {
|
||||
useKv.invalidate()
|
||||
useKv = gotkv
|
||||
} else {
|
||||
gotkv.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
// for i := 0; i < len(hb.headers); i++ {
|
||||
// // Search for key-value with largest buffer for value to store value reusing buffer.
|
||||
// gotkv := &hb.headers[i]
|
||||
// if gotkv.isValidHeader() && b2s(hb.musttoken(gotkv.key)) == key {
|
||||
// if useKv == nil {
|
||||
// useKv = gotkv
|
||||
// } else if gotkv.value.len > useKv.value.len {
|
||||
// useKv.invalidate()
|
||||
// useKv = gotkv
|
||||
// } else {
|
||||
// gotkv.invalidate()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return useKv
|
||||
}
|
||||
|
||||
// Get gets the first exact-match value of a key found in the headers. Use [Header.ForEach] to find multiple values corresponding to same key.
|
||||
func (h *Header) Get(key string) []byte {
|
||||
debuglog("http:get:start")
|
||||
kv := h.peekHeader(key)
|
||||
if kv.isValid() {
|
||||
debuglog("http:get:found")
|
||||
return h.hbuf.musttoken(kv.value)
|
||||
}
|
||||
debuglog("http:get:notfound")
|
||||
return nil
|
||||
return h.hbuf.kv.Get(key)
|
||||
// debuglog("http:get:start")
|
||||
// kv := h.peekHeader(key)
|
||||
// if kv.isValidHeader() {
|
||||
// debuglog("http:get:found")
|
||||
// return h.hbuf.musttoken(kv.value)
|
||||
// }
|
||||
// debuglog("http:get:notfound")
|
||||
// return nil
|
||||
}
|
||||
|
||||
// GetFold gets the first value whose key matches key under ASCII case-insensitive
|
||||
@@ -352,11 +296,10 @@ func (h *Header) Get(key string) []byte {
|
||||
// Use [Header.Get] for exact match and [Header.ForEach] to find multiple values
|
||||
// corresponding to same key.
|
||||
func (h *Header) GetFold(key string) []byte {
|
||||
hb := &h.hbuf
|
||||
for i := 0; i < len(hb.headers); i++ {
|
||||
kv := hb.headers[i]
|
||||
if kv.isValid() && asciiEqualFold(b2s(hb.musttoken(kv.key)), key) {
|
||||
return hb.musttoken(kv.value)
|
||||
nh := h.hbuf.kv.Len()
|
||||
for i := range nh {
|
||||
if asciiEqualFold(key, b2s(h.hbuf.kv.AtKey(i))) {
|
||||
return h.hbuf.kv.AtValue(i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -387,9 +330,9 @@ func asciiEqualFold(a, b string) bool {
|
||||
|
||||
// NormalizeKeys normalizes all header keys. i.e: CONTENT-type -> Content-Type
|
||||
func (h *Header) NormalizeKeys() {
|
||||
for _, kv := range h.hbuf.headers {
|
||||
if kv.isValid() {
|
||||
NormalizeHeaderKey(h.hbuf.musttoken(kv.key))
|
||||
for i, kv := range h.hbuf.kv.kvs {
|
||||
if kv.isValidHeader() {
|
||||
NormalizeHeaderKey(h.hbuf.kv.AtKey(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,19 +450,19 @@ func (h *Header) Status() (code, statusText []byte) {
|
||||
if h.statusCode.len == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return h.hbuf.musttoken(h.statusCode), h.hbuf.musttoken(h.statusText)
|
||||
return h.hbuf.kv.musttoken(h.statusCode), h.hbuf.kv.musttoken(h.statusText)
|
||||
}
|
||||
|
||||
// SetStatus sets the response header's status code and status text. i.e: "200" "OK".
|
||||
func (h *Header) SetStatus(code, statusText string) {
|
||||
h.flags |= FlagStatusSet
|
||||
h.hbuf.kv.flags |= FlagStatusSet
|
||||
h.statusCode = h.reuseOrAppend(h.statusCode, code)
|
||||
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
||||
}
|
||||
|
||||
// SetStatusInt is identical to [Header.SetStatus] but performs integer to text conversion for status code.
|
||||
func (h *Header) SetStatusInt(code int64, statusText string) {
|
||||
h.flags |= FlagStatusSet
|
||||
h.hbuf.kv.flags |= FlagStatusSet
|
||||
h.statusCode = h.reuseOrAppendInt(h.statusCode, code, 10)
|
||||
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
||||
}
|
||||
@@ -528,13 +471,13 @@ func (h *Header) getNonEmptyValue(s headerSlice) []byte {
|
||||
if s.len == 0 {
|
||||
return nil // If empty then value is invalid, return nil.
|
||||
}
|
||||
return h.hbuf.musttoken(s)
|
||||
return h.hbuf.kv.musttoken(s)
|
||||
}
|
||||
|
||||
// AppendRequest appends the request header representation to the buffer and returns the result.
|
||||
func (h *Header) AppendRequest(dst []byte) ([]byte, error) {
|
||||
proto := h.Protocol()
|
||||
if h.flags.HasAny(flagOOMReached) {
|
||||
if h.hbuf.kv.flags.HasAny(flagOOMReached) {
|
||||
return dst, errOOM
|
||||
} else if h.requestTarget.len == 0 || h.method.len == 0 {
|
||||
return dst, errNeedMethodURI
|
||||
@@ -574,7 +517,7 @@ func (h *Header) AppendResponse(dst []byte) ([]byte, error) {
|
||||
// AppendResponseNoHeaders appends the first line of the response containing protocol and status code/text: i.e: "HTTP/1.1 200 OK\r\n"
|
||||
func (h *Header) AppendResponseNoHeaders(dst []byte) ([]byte, error) {
|
||||
proto := h.Protocol()
|
||||
if h.flags.HasAny(flagOOMReached) {
|
||||
if h.hbuf.kv.flags.HasAny(flagOOMReached) {
|
||||
return dst, errOOM
|
||||
} else if h.statusCode.len == 0 || h.statusText.len == 0 {
|
||||
return dst, errBadStatusCodeTxt
|
||||
@@ -595,12 +538,10 @@ func (h *Header) AppendResponseNoHeaders(dst []byte) ([]byte, error) {
|
||||
// AppendHeaders appends headers to buffer. Use AppendRequest and AppendResponse over this.
|
||||
// Does not append extra \r\n to end. Appends nothing if contains no headers.
|
||||
func (h *Header) AppendHeaders(dst []byte) []byte {
|
||||
for i, n := 0, len(h.hbuf.headers); i < n; i++ {
|
||||
kv := &h.hbuf.headers[i]
|
||||
if kv.isValid() {
|
||||
key := h.hbuf.musttoken(kv.key)
|
||||
value := h.hbuf.musttoken(kv.value)
|
||||
dst = appendHeaderLine(dst, b2s(key), b2s(value))
|
||||
for i, kv := range h.hbuf.kv.kvs {
|
||||
if kv.isValidHeader() {
|
||||
k, v := h.hbuf.kv.At(i)
|
||||
dst = appendHeaderLine(dst, b2s(k), b2s(v))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
|
||||
+204
-20
@@ -1,7 +1,9 @@
|
||||
package httpraw
|
||||
|
||||
import (
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
@@ -26,6 +28,52 @@ func (mb *KVBuffer) EnableBufferGrowth(enableGrowth bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) discardKVs() { mb.kvs = mb.kvs[:0] }
|
||||
|
||||
func (mb *KVBuffer) BufferGrowthEnabled() bool { return !mb.flags.HasAny(flagNoBufferGrow) }
|
||||
|
||||
func (mb *KVBuffer) ReadFromBytes(buf []byte) error {
|
||||
if mb.flags.HasAny(flagMangledBuffer) {
|
||||
return errMangledBuffer
|
||||
} else if len(buf)+cap(mb.buf) > maxBufLen {
|
||||
return errOOM
|
||||
}
|
||||
free := mb.free()
|
||||
if len(buf) > free && !mb.BufferGrowthEnabled() {
|
||||
return errOOM
|
||||
}
|
||||
mb.buf = append(mb.buf, buf...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) ReadLimited(r io.Reader, limit int) (int, error) {
|
||||
if mb.flags.HasAny(flagMangledBuffer) {
|
||||
return 0, errMangledBuffer
|
||||
} else if mb.flags.HasAny(flagReaderEOF) {
|
||||
return 0, io.EOF
|
||||
} else if limit <= 0 {
|
||||
return 0, io.ErrNoProgress
|
||||
} else if len(mb.buf) >= maxBufLen {
|
||||
return 0, errOOM
|
||||
}
|
||||
free := mb.free()
|
||||
if limit > free {
|
||||
if !mb.BufferGrowthEnabled() {
|
||||
return 0, errOOM
|
||||
}
|
||||
mb.buf = slices.Grow(mb.buf, limit)
|
||||
}
|
||||
n, err := r.Read(mb.buf[len(mb.buf):min(len(mb.buf)+limit, maxBufLen)])
|
||||
mb.buf = mb.buf[:len(mb.buf)+n]
|
||||
if err != nil {
|
||||
if n > 0 && err == io.EOF {
|
||||
mb.flags |= flagReaderEOF
|
||||
err = nil // Nil out EOF to not scare off readers.
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) Reset(buf []byte, kvCap int) {
|
||||
if buf == nil {
|
||||
mb.buf = mb.buf[:0]
|
||||
@@ -41,15 +89,19 @@ func (mb *KVBuffer) CopyFrom(src *KVBuffer) {
|
||||
mb.kvs = append(mb.kvs[:0], src.kvs...)
|
||||
}
|
||||
|
||||
// Get returns the value of the first pair matching key.
|
||||
// Bytes are compared as stored, so if using a Form call [Form.Decode] first when keys may be encoded.
|
||||
// Returns nil for an absent key and for a valueless pair alike, so use
|
||||
// [KVBuffer.Present] to tell the two apart.
|
||||
func (mb *KVBuffer) Get(key string) []byte {
|
||||
v := mb.getIdx(key)
|
||||
if v < 0 {
|
||||
i := mb.getIdx(key)
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
return mb.musttoken(mb.kvs[v].value)
|
||||
return mb.AtValue(i)
|
||||
}
|
||||
|
||||
// ForEach iterates over the cookie's key-value pairs until cb returns false.
|
||||
// ForEach iterates over the cookie's key-value pairs as stored until cb returns false.
|
||||
func (c *KVBuffer) ForEach(cb func(key, value []byte) bool) {
|
||||
nc := len(c.kvs)
|
||||
for i := range nc {
|
||||
@@ -61,18 +113,76 @@ func (c *KVBuffer) ForEach(cb func(key, value []byte) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) Present(key string) bool {
|
||||
// Has returns true if key is present, with or without a value.
|
||||
func (mb *KVBuffer) Present(key string) bool { // TODO: rename to Has.
|
||||
return mb.getIdx(key) >= 0
|
||||
}
|
||||
func (mb *KVBuffer) Add(key, value string) bool {
|
||||
|
||||
// Has returns true if key is present, with or without a value.
|
||||
func (mb *KVBuffer) HasKeyValue(key, value string) bool {
|
||||
idx := mb.getIdx(key)
|
||||
if idx >= 0 {
|
||||
return b2s(mb.musttoken(mb.kvs[idx].value)) == value
|
||||
}
|
||||
return false
|
||||
}
|
||||
func (mb *KVBuffer) Add(key, value string) (enoughSpace bool) {
|
||||
mb.appendPair(key, value)
|
||||
return mb.getIdx(key) >= 0
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) setInternal(key, value []byte) bool {
|
||||
// Set replaces key's value and invalidates every other pair sharing the key, so
|
||||
// a following [KVBuffer.Get] sees exactly one value.
|
||||
//
|
||||
// It rewrites in place when it can: of the pairs it would invalidate it keeps
|
||||
// the smallest whose key and value regions both still hold the new pair,
|
||||
// leaving the roomier regions for a later Set. When none fits the pair is
|
||||
// appended with [KVBuffer.Add] and the invalidated regions are stranded, since
|
||||
// nothing here compacts the buffer.
|
||||
func (mb *KVBuffer) Set(key, value string) (enoughSpace bool) {
|
||||
reuse := -1
|
||||
for i := range mb.kvs {
|
||||
kv := &mb.kvs[i]
|
||||
if !kv.isValid() || b2s(mb.musttoken(kv.key)) != key {
|
||||
continue
|
||||
}
|
||||
// A valueless pair holds no value region, so reusing one would write the
|
||||
// value over byte 0. Let it fall through to Add, which gives the pair a
|
||||
// real region and keeps "ok" distinct from "ok=".
|
||||
fits := kv.HasValue() && int(kv.key.len) >= len(key) && int(kv.value.len) >= len(value)
|
||||
if fits && (reuse < 0 || kv.size() < mb.kvs[reuse].size()) {
|
||||
if reuse >= 0 {
|
||||
mb.kvs[reuse].invalidate() // Superseded by a tighter fit.
|
||||
}
|
||||
reuse = i
|
||||
continue
|
||||
}
|
||||
kv.invalidate()
|
||||
}
|
||||
if reuse < 0 {
|
||||
return mb.Add(key, value)
|
||||
}
|
||||
mb.overwriteAt(reuse, key, value)
|
||||
return true
|
||||
}
|
||||
|
||||
// overwriteAt writes key and value over the regions pair i already owns. The
|
||||
// caller must have checked both fit; the bytes freed by a shorter pair are
|
||||
// stranded, not reclaimed.
|
||||
func (mb *KVBuffer) overwriteAt(i int, key, value string) {
|
||||
mb.flags |= flagMangledBuffer
|
||||
kv := &mb.kvs[i]
|
||||
copy(mb.buf[kv.key.start:], key)
|
||||
kv.key.len = tokint(len(key))
|
||||
copy(mb.buf[kv.value.start:], value)
|
||||
kv.value.len = tokint(len(value))
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) setInternal(key, value []byte) (enoughSpace bool) {
|
||||
if !mb.canAddOneKV() {
|
||||
return false
|
||||
}
|
||||
mb.flags |= flagKVAppended
|
||||
mb.kvs = append(mb.kvs, argsKV{
|
||||
key: mb.slice(key),
|
||||
value: mb.slice(value),
|
||||
@@ -83,10 +193,26 @@ func (mb *KVBuffer) setInternal(key, value []byte) bool {
|
||||
func (mb *KVBuffer) Len() int { return len(mb.kvs) }
|
||||
func (mb *KVBuffer) At(i int) (key, value []byte) {
|
||||
kv := mb.kvs[i]
|
||||
if !kv.HasValue() {
|
||||
return mb.musttoken(kv.key), nil
|
||||
}
|
||||
return mb.musttoken(kv.key), mb.musttoken(kv.value)
|
||||
}
|
||||
func (mb *KVBuffer) AtKey(i int) (key []byte) { return mb.musttoken(mb.kvs[i].key) }
|
||||
func (mb *KVBuffer) AtValue(i int) (key []byte) { return mb.musttoken(mb.kvs[i].value) }
|
||||
func (mb *KVBuffer) setAt(i int, k, v []byte) {
|
||||
mb.flags |= flagMangledBuffer
|
||||
mb.kvs[i] = argsKV{
|
||||
key: bytes2tok(mb.buf, k),
|
||||
value: bytes2tok(mb.buf, v),
|
||||
}
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) AtKey(i int) (key []byte) { return mb.musttoken(mb.kvs[i].key) }
|
||||
func (mb *KVBuffer) AtValue(i int) (key []byte) {
|
||||
if !mb.kvs[i].HasValue() {
|
||||
return nil
|
||||
}
|
||||
return mb.musttoken(mb.kvs[i].value)
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) getIdx(key string) int {
|
||||
for i, kv := range mb.kvs {
|
||||
@@ -106,12 +232,21 @@ func (mb *KVBuffer) getInvalidIdx() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) getInvalidOrKeyIdx(key string) int {
|
||||
for i, kv := range mb.kvs {
|
||||
if !kv.isValid() || key == b2s(mb.musttoken(kv.key)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// reserve ensures need free bytes are available in the buffer, growing it when
|
||||
// permitted. It accounts for the byte-0 reservation on an empty buffer (see
|
||||
// mustAppendSlice). It returns false and sets flagOOMReached when the space
|
||||
// cannot be guaranteed: a tokint offset overflow, or a full buffer with
|
||||
// flagNoBufferGrow set.
|
||||
func (mb *KVBuffer) reserve(need int) bool {
|
||||
func (mb *KVBuffer) reserve(need int) (enoughSpace bool) {
|
||||
if len(mb.buf) == 0 {
|
||||
need++ // mustAppend* reserves byte 0 on an empty buffer.
|
||||
}
|
||||
@@ -130,24 +265,31 @@ func (mb *KVBuffer) reserve(need int) bool {
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) appendPair(key, value string) bool {
|
||||
// reserve accounts for the byte-0 reservation mustAppendSlice makes on an
|
||||
// empty buffer, and drops (flagging OOM) rather than panicking when growth
|
||||
// is disabled and space runs out.
|
||||
|
||||
if !mb.canAddOneKV() || !mb.reserve(len(key)+len(value)) {
|
||||
return false
|
||||
}
|
||||
k := mb.mustAppendSlice(key)
|
||||
v := mb.mustAppendSlice(value)
|
||||
debuglog("http:appendhdr:grow-hdrs")
|
||||
mb.flags |= flagKVAppended
|
||||
mb.kvs = append(mb.kvs, argsKV{
|
||||
key: k,
|
||||
value: v,
|
||||
key: mb.mustAppendSlice(key),
|
||||
value: mb.mustAppendSlice(value),
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) canAddOneKV() bool {
|
||||
func (mb *KVBuffer) appendPairInt(key string, value int64, base int) bool {
|
||||
vlen := internal.IntLen(value, base)
|
||||
if !mb.canAddOneKV() || !mb.reserve(len(key)+vlen) {
|
||||
return false
|
||||
}
|
||||
mb.flags |= flagKVAppended
|
||||
mb.kvs = append(mb.kvs, argsKV{
|
||||
key: mb.mustAppendSlice(key),
|
||||
value: mb.mustAppendInt(value, base),
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) canAddOneKV() (enoughSpace bool) {
|
||||
return len(mb.kvs) < cap(mb.kvs) || mb.flags&flagNoBufferGrow == 0
|
||||
}
|
||||
|
||||
@@ -161,7 +303,20 @@ func (mb *KVBuffer) mustAppendSlice(value string) headerSlice {
|
||||
return mb.slice(mb.buf[L : L+len(value)])
|
||||
}
|
||||
|
||||
func (hb *KVBuffer) mustAppendInt(value int64, base int) headerSlice {
|
||||
L := len(hb.buf)
|
||||
if L == 0 {
|
||||
L++ // Valid key-values start after byte 0.
|
||||
}
|
||||
v := strconv.AppendInt(hb.buf[L:L], value, base)
|
||||
hb.buf = hb.buf[:L+len(v)]
|
||||
return hb.slice(hb.buf[L : L+len(v)])
|
||||
}
|
||||
|
||||
func (mb *KVBuffer) slice(value []byte) headerSlice {
|
||||
if value == nil {
|
||||
return headerSlice{}
|
||||
}
|
||||
return bytes2tok(mb.buf, value)
|
||||
}
|
||||
|
||||
@@ -169,3 +324,32 @@ func (mb KVBuffer) musttoken(slice headerSlice) []byte {
|
||||
return tok2bytes(mb.buf, slice)
|
||||
}
|
||||
func (mb *KVBuffer) noKV() argsKV { return argsKV{} }
|
||||
|
||||
type tokint = uint16
|
||||
|
||||
type headerSlice struct {
|
||||
start tokint
|
||||
len tokint
|
||||
}
|
||||
|
||||
type argsKV struct {
|
||||
key headerSlice
|
||||
value headerSlice // value start >0 means value is present.
|
||||
}
|
||||
|
||||
// isValid is for stores parsed in place, where offset 0 is the first key so
|
||||
// only length can signal presence. Empty keys are valid: see valueless cookies.
|
||||
func (kv argsKV) isValid() bool {
|
||||
return kv.key.len > 0 || kv.value.len > 0
|
||||
}
|
||||
|
||||
// isValidHeader is for the append-built [Header] store, where mustAppendSlice
|
||||
// burns byte 0 so a zero offset means absent. Drops offset-0 pairs otherwise.
|
||||
func (kv argsKV) isValidHeader() bool { return kv.key.start > 0 }
|
||||
|
||||
func (kv *argsKV) invalidate() {
|
||||
*kv = argsKV{}
|
||||
}
|
||||
|
||||
// size is the buffer a pair occupies, used to pick the tightest slot to reuse.
|
||||
func (kv argsKV) size() int { return int(kv.key.len) + int(kv.value.len) }
|
||||
|
||||
+36
-107
@@ -49,42 +49,21 @@ var (
|
||||
const maxBufLen = 0xffff
|
||||
|
||||
type headerBuf struct {
|
||||
kv KVBuffer
|
||||
// buf[:len] holds entire HTTP header data, which may be normalized by [flags]. buf[off:len] holds data not yet processed during parsing.
|
||||
buf []byte
|
||||
// buf []byte
|
||||
// offset into buf for parsing.
|
||||
off int
|
||||
// args contains key-value store.
|
||||
headers []argsKV
|
||||
// headers []argsKV
|
||||
}
|
||||
|
||||
// reset sets the buffer data and discards all parsed data. The field table is
|
||||
// grown to match the new buffer's capacity and never shrinks, so a header
|
||||
// reused across requests settles on its largest buffer and stops allocating.
|
||||
func (h *headerBuf) reset(buf []byte, numHeaderCapacity int) {
|
||||
if buf == nil {
|
||||
buf = h.buf[:0] // Reuse buffer but discard raw data on nil input.
|
||||
}
|
||||
if numHeaderCapacity != 0 {
|
||||
internal.SliceReuse(&h.headers, numHeaderCapacity)
|
||||
} else {
|
||||
h.headers = h.headers[:0]
|
||||
}
|
||||
*h = headerBuf{
|
||||
buf: buf,
|
||||
headers: h.headers,
|
||||
}
|
||||
}
|
||||
|
||||
type tokint = uint16
|
||||
|
||||
type headerSlice struct {
|
||||
start tokint
|
||||
len tokint
|
||||
}
|
||||
|
||||
type argsKV struct {
|
||||
key headerSlice
|
||||
value headerSlice // value start >0 means value is present.
|
||||
h.kv.Reset(buf, numHeaderCapacity)
|
||||
h.off = 0
|
||||
}
|
||||
|
||||
type scannerState struct {
|
||||
@@ -109,20 +88,22 @@ func (h *Header) parse(asResponse bool) (err error) {
|
||||
return err
|
||||
}
|
||||
debuglog("http:firstline:done")
|
||||
err = h.parseNextHeaders(h.flags)
|
||||
err = h.parseNextHeaders(h.Flags())
|
||||
debuglog("http:headers:done")
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Header) parseFirstLine(asResponse bool) (err error) {
|
||||
if len(h.hbuf.buf) > maxBufLen {
|
||||
if len(h.hbuf.kv.buf) > maxBufLen {
|
||||
return errBufferTooLarge // Offsets would overflow uint16 tokint.
|
||||
}
|
||||
flags := h.Flags()
|
||||
if asResponse {
|
||||
h.statusCode, h.statusText, h.flags, err = h.hbuf.parseFirstLineResponse(h.flags)
|
||||
h.statusCode, h.statusText, flags, err = h.hbuf.parseFirstLineResponse(flags)
|
||||
} else {
|
||||
h.method, h.requestTarget, h.proto, h.flags, err = h.hbuf.parseFirstLineRequest(h.flags)
|
||||
h.method, h.requestTarget, h.proto, flags, err = h.hbuf.parseFirstLineRequest(flags)
|
||||
}
|
||||
h.hbuf.kv.flags = flags
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -130,39 +111,33 @@ func (h *Header) parseNextHeaders(flags Flags) error {
|
||||
var ss scannerState
|
||||
h.hbuf.parseNextHeaders(&ss, flags)
|
||||
if ss.err != nil {
|
||||
h.flags |= flagConnClose
|
||||
h.hbuf.kv.flags |= flagConnClose
|
||||
return ss.err
|
||||
}
|
||||
h.flags |= flagDoneParsingHeader
|
||||
h.hbuf.kv.flags |= flagDoneParsingHeader
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hb *headerBuf) readFromBytes(b []byte) {
|
||||
hb.buf = append(hb.buf, b...)
|
||||
}
|
||||
|
||||
func (hb *headerBuf) free() int { return cap(hb.buf) - len(hb.buf) }
|
||||
func (hb *headerBuf) free() int { return hb.kv.free() }
|
||||
|
||||
func (hb *headerBuf) parseNextHeaders(ss *scannerState, flags Flags) {
|
||||
debuglog("http:nexthdr:loop")
|
||||
for kv := hb.next(ss); kv.isValid(); kv = hb.next(ss) {
|
||||
if len(hb.headers) == cap(hb.headers) && flags.HasAny(flagNoBufferGrow) {
|
||||
// Refuse to grow the headers slice: the caller granted this much
|
||||
// memory and no more, see [Header.Reset].
|
||||
for kv := hb.next(ss); kv.isValidHeader(); kv = hb.next(ss) {
|
||||
if !hb.kv.canAddOneKV() {
|
||||
ss.err = ErrHeaderTooMany
|
||||
return
|
||||
}
|
||||
hb.headers = append(hb.headers, kv)
|
||||
hb.kv.kvs = append(hb.kv.kvs, kv)
|
||||
}
|
||||
debuglog("http:nexthdr:done")
|
||||
}
|
||||
|
||||
func (hb *headerBuf) offBuf() []byte {
|
||||
return hb.buf[hb.off:]
|
||||
return hb.kv.buf[hb.off:]
|
||||
}
|
||||
|
||||
func (hb *headerBuf) skipLeadingCRLF() {
|
||||
for hb.off < len(hb.buf) && (hb.buf[hb.off] == '\n' || hb.buf[hb.off] == '\r') {
|
||||
for hb.off < len(hb.kv.buf) && (hb.kv.buf[hb.off] == '\n' || hb.kv.buf[hb.off] == '\r') {
|
||||
hb.off++
|
||||
}
|
||||
}
|
||||
@@ -172,7 +147,7 @@ func (hb *headerBuf) scanLine() []byte {
|
||||
if len(buf) > 0 && buf[len(buf)-1] == '\r' {
|
||||
buf = buf[:len(buf)-1] // exclude carriage return.
|
||||
}
|
||||
if hb.off < len(hb.buf) {
|
||||
if hb.off < len(hb.kv.buf) {
|
||||
hb.off++ // consume newline.
|
||||
}
|
||||
return buf
|
||||
@@ -206,8 +181,8 @@ func (hb *headerBuf) parseFirstLineRequest(initFlags Flags) (method, uri, proto
|
||||
reqURIEnd := bytes.IndexByte(b[methodEnd+1:], ' ')
|
||||
if reqURIEnd > 0 {
|
||||
reqURIEnd += methodEnd + 1
|
||||
uri = hb.slice(b[methodEnd+1 : reqURIEnd])
|
||||
proto = hb.slice(b[reqURIEnd+1:]) // Skip space before protocol.
|
||||
uri = hb.kv.slice(b[methodEnd+1 : reqURIEnd])
|
||||
proto = hb.kv.slice(b[reqURIEnd+1:]) // Skip space before protocol.
|
||||
if b2s(b[reqURIEnd+1:]) != strHTTP11 {
|
||||
flags |= flagNoHTTP11
|
||||
}
|
||||
@@ -216,9 +191,9 @@ func (hb *headerBuf) parseFirstLineRequest(initFlags Flags) (method, uri, proto
|
||||
} else {
|
||||
// No version provided.
|
||||
flags |= flagNoHTTP11
|
||||
uri = hb.slice(b[methodEnd+1:])
|
||||
uri = hb.kv.slice(b[methodEnd+1:])
|
||||
}
|
||||
method = hb.slice(b[:methodEnd])
|
||||
method = hb.kv.slice(b[:methodEnd])
|
||||
return method, uri, proto, flags, nil
|
||||
}
|
||||
|
||||
@@ -260,66 +235,19 @@ func (hb *headerBuf) parseFirstLineResponse(initFlags Flags) (statusCode, status
|
||||
return statusCode, statusText, flags, errBadStatusCode
|
||||
}
|
||||
}
|
||||
statusCode = hb.slice(code)
|
||||
statusCode = hb.kv.slice(code)
|
||||
if codeEnd < len(b) {
|
||||
statusText = hb.slice(b[codeEnd+1:]) // Skip space before text.
|
||||
statusText = hb.kv.slice(b[codeEnd+1:]) // Skip space before text.
|
||||
}
|
||||
debuglog("http:resp:done")
|
||||
return statusCode, statusText, flags, nil
|
||||
}
|
||||
|
||||
func (kv argsKV) isValid() bool {
|
||||
return kv.key.start > 0
|
||||
}
|
||||
|
||||
func (kv *argsKV) invalidate() {
|
||||
*kv = argsKV{}
|
||||
}
|
||||
|
||||
func (tb headerBuf) musttoken(slice headerSlice) []byte {
|
||||
return tok2bytes(tb.buf, slice)
|
||||
|
||||
}
|
||||
|
||||
func (tb headerBuf) slice(b []byte) headerSlice {
|
||||
return bytes2tok(tb.buf, b)
|
||||
}
|
||||
|
||||
func (kv argsKV) HasValue() bool { return kv.value.start > 0 }
|
||||
|
||||
func (h *Header) hasHeaderValue(key, value string) bool {
|
||||
kv := h.peekHeader(key)
|
||||
return kv.isValid() && b2s(h.hbuf.musttoken(kv.value)) == value
|
||||
}
|
||||
|
||||
// peekHeader returns header key-value for the given key.
|
||||
//
|
||||
// The returned value is valid until the request is released,
|
||||
// either though ReleaseRequest or your request handler returning.
|
||||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *Header) peekHeader(key string) argsKV {
|
||||
hb := &h.hbuf
|
||||
for i := 0; i < len(h.hbuf.headers); i++ {
|
||||
if b2s(hb.musttoken(h.hbuf.headers[i].key)) == key {
|
||||
return h.hbuf.headers[i]
|
||||
}
|
||||
}
|
||||
return hb.noKV()
|
||||
}
|
||||
|
||||
func (hb *headerBuf) mustAppendSlice(value string) headerSlice {
|
||||
L := len(hb.buf)
|
||||
if L == 0 {
|
||||
L++ // Valid key-values start after 0.
|
||||
}
|
||||
copy(hb.buf[L:L+len(value)], value)
|
||||
hb.buf = hb.buf[:L+len(value)]
|
||||
return hb.slice(hb.buf[L : L+len(value)])
|
||||
}
|
||||
|
||||
func (h *Header) reuseOrAppend(tok headerSlice, value string) headerSlice {
|
||||
if tok.len > tokint(len(value)) {
|
||||
copy(h.hbuf.musttoken(tok), value)
|
||||
copy(h.hbuf.kv.musttoken(tok), value)
|
||||
tok.len = tokint(len(value))
|
||||
return tok
|
||||
}
|
||||
@@ -332,7 +260,7 @@ func (h *Header) appendSlice(value string) headerSlice {
|
||||
return headerSlice{}
|
||||
}
|
||||
h.flags |= flagMangledBuffer
|
||||
return h.hbuf.mustAppendSlice(value)
|
||||
return h.hbuf.kv.mustAppendSlice(value)
|
||||
}
|
||||
|
||||
func (h *Header) appendHeader(key, value string) {
|
||||
@@ -437,7 +365,7 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
||||
ss.nextColon = -1
|
||||
ss.nextNewLine = -1
|
||||
}
|
||||
buf := hb.buf[hb.off:]
|
||||
buf := hb.kv.buf[hb.off:]
|
||||
blen := len(buf)
|
||||
if blen >= 2 && buf[0] == '\r' && buf[1] == '\n' {
|
||||
hb.off += 2
|
||||
@@ -484,7 +412,7 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
||||
|
||||
// Ready to store key..
|
||||
var resultKV argsKV
|
||||
resultKV.key = hb.slice(buf[:n])
|
||||
resultKV.key = hb.kv.slice(buf[:n])
|
||||
n++ // consume colon.
|
||||
for len(buf) > n && buf[n] == ' ' {
|
||||
n++ // Trim leading spaces.
|
||||
@@ -511,18 +439,19 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
||||
if valueEnd > valueStart && buf[valueEnd-1] == '\r' {
|
||||
valueEnd-- // Trim \r character if present before value.
|
||||
}
|
||||
resultKV.value = hb.slice(buf[valueStart:valueEnd])
|
||||
resultKV.value = hb.kv.slice(buf[valueStart:valueEnd])
|
||||
hb.off += n
|
||||
return resultKV
|
||||
}
|
||||
|
||||
// ConnectionClose returns true if 'Connection: close' header is set or if a invalid header was found.
|
||||
func (h *Header) ConnectionClose() bool {
|
||||
closed := h.flags.HasAny(flagConnClose) ||
|
||||
h.hasHeaderValue(headerConnection, strClose) ||
|
||||
(h.flags.HasAny(flagNoHTTP11) && !h.hasHeaderValue(headerConnection, "keep-alive"))
|
||||
flags := h.Flags()
|
||||
closed := flags.HasAny(flagConnClose) ||
|
||||
h.hbuf.kv.HasKeyValue(headerConnection, strClose) ||
|
||||
(flags.HasAny(flagNoHTTP11) && !h.hbuf.kv.HasKeyValue(headerConnection, "keep-alive"))
|
||||
if closed {
|
||||
h.flags |= flagConnClose
|
||||
h.hbuf.kv.flags |= flagConnClose
|
||||
}
|
||||
return closed
|
||||
}
|
||||
|
||||
@@ -331,6 +331,14 @@ func TestCookie_ParseBytes(t *testing.T) {
|
||||
if string(c.Get("Path")) != "/" {
|
||||
t.Errorf("Path = %q; want /", c.Get("Path"))
|
||||
}
|
||||
// The first pair sits at buffer offset 0, which a presence check keyed on
|
||||
// the offset rather than the length reads as absent.
|
||||
if string(c.Get("session")) != "abc123" {
|
||||
t.Errorf("Get(session) = %q; want abc123", c.Get("session"))
|
||||
}
|
||||
if !c.HasKeyOrSingleValue("session") {
|
||||
t.Error("expected first pair to be present by key")
|
||||
}
|
||||
if !c.HasKeyOrSingleValue("Secure") {
|
||||
t.Error("expected Secure flag")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user