mirror of
https://github.com/soypat/lneto.git
synced 2026-09-01 04:19:05 +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()
|
length, present, err := exch.RequestContentLength()
|
||||||
if !present {
|
if !present {
|
||||||
dst.Reset(nil)
|
dst.Reset(nil, 0)
|
||||||
return nil // No length is no body, RFC 9112 6.3.
|
return nil // No length is no body, RFC 9112 6.3.
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -454,7 +454,7 @@ func (exch *Exchange) RequestParseForm(dst *httpraw.Form, buf []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst.Reset(buf)
|
dst.Reset(buf, 0)
|
||||||
return dst.Parse()
|
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
|
// 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".
|
// 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 {
|
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..
|
// 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
|
// undecoded, until [Form.Decode] rewrites them in place. The caller bounds the
|
||||||
// data: Form parses the buffer it is handed and reads nothing more.
|
// data: Form parses the buffer it is handed and reads nothing more.
|
||||||
type Form struct {
|
type Form struct {
|
||||||
buf []byte
|
kv KVBuffer
|
||||||
kvs []argsKV
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Form) EnableBufferGrowth(enableGrowth bool) { f.kv.EnableBufferGrowth(enableGrowth) }
|
||||||
|
|
||||||
// Reset discards parsed pairs and sets the buffer to parse in place.
|
// Reset discards parsed pairs and sets the buffer to parse in place.
|
||||||
// If buf is nil the current buffer is reused.
|
// If buf is nil the current buffer is reused.
|
||||||
func (f *Form) Reset(buf []byte) {
|
func (f *Form) Reset(buf []byte, capKV int) {
|
||||||
if buf == nil {
|
f.kv.Reset(buf, capKV)
|
||||||
buf = f.buf[:0]
|
|
||||||
}
|
|
||||||
*f = Form{
|
|
||||||
buf: buf,
|
|
||||||
kvs: f.kvs[:0],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes copies the argument bytes to the Form's underlying buffer and parses them.
|
// ParseBytes copies the argument bytes to the Form's underlying buffer and parses them.
|
||||||
func (f *Form) ParseBytes(b []byte) error {
|
func (f *Form) ParseBytes(b []byte) error {
|
||||||
f.Reset(nil)
|
f.Reset(nil, 0)
|
||||||
f.buf = append(f.buf[:0], b...)
|
err := f.kv.ReadFromBytes(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return f.Parse()
|
return f.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses the form's buffer in place.
|
// Parse parses the form's buffer in place.
|
||||||
func (f *Form) Parse() error {
|
func (f *Form) Parse() error {
|
||||||
f.kvs = f.kvs[:0]
|
f.kv.discardKVs()
|
||||||
key, value, rest := NextQueryPair(f.buf)
|
key, value, rest := NextQueryPair(f.kv.buf)
|
||||||
for key != nil {
|
for key != nil {
|
||||||
kv := argsKV{key: bytes2tok(f.buf, key)}
|
if !f.kv.setInternal(key, value) {
|
||||||
if value != nil {
|
return errOOM
|
||||||
kv.value = bytes2tok(f.buf, value)
|
|
||||||
}
|
}
|
||||||
f.kvs = append(f.kvs, kv)
|
|
||||||
key, value, rest = NextQueryPair(rest)
|
key, value, rest = NextQueryPair(rest)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -50,66 +46,51 @@ func (f *Form) Parse() error {
|
|||||||
// '+' with the bytes they encode. Decoding only shrinks, so no memory is added.
|
// '+' with the bytes they encode. Decoding only shrinks, so no memory is added.
|
||||||
func (f *Form) Decode() error {
|
func (f *Form) Decode() error {
|
||||||
const plusAsSpace = true // Form encoded data, unlike a path.
|
const plusAsSpace = true // Form encoded data, unlike a path.
|
||||||
for i := range f.kvs {
|
nkvs := f.kv.Len()
|
||||||
kv := &f.kvs[i]
|
for i := range nkvs {
|
||||||
n, err := CopyDecodedPercentURL(tok2bytes(f.buf, kv.key), tok2bytes(f.buf, kv.key), plusAsSpace)
|
k, v := f.kv.At(i)
|
||||||
|
nk, err := CopyDecodedPercentURL(k, k, plusAsSpace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
} else if len(v) == 0 {
|
||||||
kv.key.len = tokint(n)
|
if nk != len(k) {
|
||||||
if !kv.HasValue() {
|
f.kv.setAt(i, k, v)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n, err = CopyDecodedPercentURL(tok2bytes(f.buf, kv.value), tok2bytes(f.buf, kv.value), plusAsSpace)
|
nv, err := CopyDecodedPercentURL(v, v, plusAsSpace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
kv.value.len = tokint(n)
|
if nk != len(k) || nv != len(v) {
|
||||||
|
f.kv.setAt(i, k[:nk], v[:nv])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the amount of key-value pairs parsed.
|
// 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 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="
|
// pair with no '=', i.e: "ok" in "ok&q=go", which distinguishes it from "ok="
|
||||||
// where the value is present and empty.
|
// where the value is present and empty.
|
||||||
func (f *Form) Pair(i int) (key, value []byte) {
|
func (f *Form) Pair(i int) (key, value []byte) {
|
||||||
kv := f.kvs[i]
|
return f.kv.At(i)
|
||||||
key = tok2bytes(f.buf, kv.key)
|
|
||||||
if kv.HasValue() {
|
|
||||||
value = tok2bytes(f.buf, kv.value)
|
|
||||||
}
|
|
||||||
return key, value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value of the first pair matching key, nil if absent or if the
|
// 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
|
// pair has no value. Bytes are compared as stored, so call [Form.Decode] first
|
||||||
// when keys may be encoded.
|
// when keys may be encoded.
|
||||||
func (f *Form) Get(key string) []byte {
|
func (f *Form) Get(key string) []byte { return f.kv.Get(key) }
|
||||||
for i := range f.kvs {
|
|
||||||
gotKey, value := f.Pair(i)
|
|
||||||
if b2s(gotKey) == key {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has returns true if key is present, with or without a value.
|
// Has returns true if key is present, with or without a value.
|
||||||
func (f *Form) Has(key string) bool {
|
func (f *Form) Has(key string) bool { return f.kv.Present(key) }
|
||||||
for i := range f.kvs {
|
|
||||||
if b2s(tok2bytes(f.buf, f.kvs[i].key)) == key {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendKeyValues appends the form's wire representation to dst and returns it.
|
// AppendKeyValues appends the form's wire representation to dst and returns it.
|
||||||
func (f *Form) AppendKeyValues(dst []byte) []byte {
|
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)
|
key, value := f.Pair(i)
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
dst = append(dst, '&')
|
dst = append(dst, '&')
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func TestFormParseReuseNoAlloc(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
allocs := testing.AllocsPerRun(100, func() {
|
allocs := testing.AllocsPerRun(100, func() {
|
||||||
f.Reset(body)
|
f.Reset(body, 0)
|
||||||
f.Parse()
|
f.Parse()
|
||||||
})
|
})
|
||||||
if allocs != 0 {
|
if allocs != 0 {
|
||||||
|
|||||||
+81
-140
@@ -3,7 +3,6 @@ package httpraw
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"slices"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,6 +28,7 @@ const (
|
|||||||
flagConnClose
|
flagConnClose
|
||||||
flagNoHTTP11
|
flagNoHTTP11
|
||||||
flagMangledBuffer // set when header fields appended to buffer via Add,Set calls
|
flagMangledBuffer // set when header fields appended to buffer via Add,Set calls
|
||||||
|
flagKVAppended // set after KV appended to buffer outside Read methods.
|
||||||
flagReaderEOF
|
flagReaderEOF
|
||||||
// set if [Header.SetStatus] or [Header.SetStatusInt] has been called.
|
// set if [Header.SetStatus] or [Header.SetStatusInt] has been called.
|
||||||
FlagStatusSet
|
FlagStatusSet
|
||||||
@@ -57,30 +57,27 @@ type Header struct {
|
|||||||
// Response fields.
|
// Response fields.
|
||||||
statusCode headerSlice
|
statusCode headerSlice
|
||||||
statusText headerSlice
|
statusText headerSlice
|
||||||
|
_ noCopy
|
||||||
flags Flags
|
|
||||||
_ noCopy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags returns [Flags] to signal status code has been set, Connection:Close or other useful signals provided by flags.
|
// 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
|
// ConfigBufferGrowth configures the memory the header may use. Setting
|
||||||
// outlives [Header.Reset]. Call before parsing/reading.
|
// outlives [Header.Reset]. Call before parsing/reading.
|
||||||
//
|
//
|
||||||
// enableBufferGrowth enables growing both the header buffer and the header key/value pair slice.
|
// enableBufferGrowth enables growing both the header buffer and the header key/value pair slice.
|
||||||
func (h *Header) ConfigBufferGrowth(enableBufferGrowth bool) {
|
func (h *Header) ConfigBufferGrowth(enableBufferGrowth bool) {
|
||||||
if !enableBufferGrowth {
|
h.hbuf.kv.EnableBufferGrowth(enableBufferGrowth)
|
||||||
h.flags |= flagNoBufferGrow
|
|
||||||
} else {
|
|
||||||
h.flags &^= flagNoBufferGrow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes copies the bytes into buffer and parses the HTTP header. It fails if HTTP header data is incomplete.
|
// 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 {
|
func (h *Header) ParseBytes(asResponse bool, b []byte) error {
|
||||||
h.Reset(nil, 0)
|
h.Reset(nil, 0)
|
||||||
h.hbuf.readFromBytes(b)
|
err := h.hbuf.kv.ReadFromBytes(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return h.parse(asResponse)
|
return h.parse(asResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +85,7 @@ func (h *Header) ParseBytes(asResponse bool, b []byte) error {
|
|||||||
// It fails if HTTP data is incomplete.
|
// It fails if HTTP data is incomplete.
|
||||||
func (h *Header) Parse(asResponse bool) error {
|
func (h *Header) Parse(asResponse bool) error {
|
||||||
debuglog("http:parse:reset")
|
debuglog("http:parse:reset")
|
||||||
h.Reset(h.hbuf.buf, 0)
|
h.Reset(h.hbuf.kv.buf, 0)
|
||||||
debuglog("http:parse:start")
|
debuglog("http:parse:start")
|
||||||
return h.parse(asResponse)
|
return h.parse(asResponse)
|
||||||
}
|
}
|
||||||
@@ -110,9 +107,10 @@ func (h *Header) Parse(asResponse bool) error {
|
|||||||
// return err
|
// return err
|
||||||
// }
|
// }
|
||||||
func (h *Header) TryParse(asResponse bool) (needMoreData bool, err error) {
|
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
|
return false, errAlreadyParsed
|
||||||
} else if h.flags.HasAny(flagMangledBuffer) {
|
} else if flags.HasAny(flagMangledBuffer) {
|
||||||
return false, errMangledBuffer
|
return false, errMangledBuffer
|
||||||
}
|
}
|
||||||
if asResponse && h.statusCode.len == 0 || !asResponse && h.requestTarget.start == 0 {
|
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
|
return err == ErrNeedMoreData, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = h.parseNextHeaders(h.flags)
|
err = h.parseNextHeaders(flags)
|
||||||
return err == ErrNeedMoreData, err
|
return err == ErrNeedMoreData, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParsingSuccess returns true if TryParse was successful, that is to say it returned needMoreData==false and err==nil.
|
// ParsingSuccess returns true if TryParse was successful, that is to say it returned needMoreData==false and err==nil.
|
||||||
func (h *Header) ParsingSuccess() bool {
|
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.
|
// ReadFromLimited reads at most maxBytesToRead from reader and appends them to underlying buffer.
|
||||||
// Used to accumulate HTTP header for later parsing with [Header.TryParse].
|
// 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.
|
// 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) {
|
func (h *Header) ReadFromLimited(r io.Reader, maxBytesToRead int) (int, error) {
|
||||||
if maxBytesToRead <= 0 {
|
return h.hbuf.kv.ReadLimited(r, maxBytesToRead)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFromBytes appends argument buffer to underlying buffer.
|
// ReadFromBytes appends argument buffer to underlying buffer.
|
||||||
// Used to accumulate HTTP header for later parsing with [Header.TryParse].
|
// Used to accumulate HTTP header for later parsing with [Header.TryParse].
|
||||||
func (h *Header) ReadFromBytes(b []byte) (int, error) {
|
func (h *Header) ReadFromBytes(b []byte) error {
|
||||||
if len(b) == 0 {
|
return h.hbuf.kv.ReadFromBytes(b)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BufferReceived returns the amoung of bytes read during calls to Read* methods.
|
// BufferReceived returns the amoung of bytes read during calls to Read* methods.
|
||||||
// Returns 0 if buffer is invalid/mangled.
|
// Returns 0 if buffer is invalid/mangled.
|
||||||
func (h *Header) BufferReceived() int {
|
func (h *Header) BufferReceived() int {
|
||||||
if h.flags.HasAny(flagMangledBuffer | flagOOMReached) {
|
if h.Flags().HasAny(flagMangledBuffer | flagOOMReached) {
|
||||||
return 0
|
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.
|
// 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.
|
// 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.
|
// BufferParsed returns 0 if the buffer is invalid/mangled or if no header data has been parsed succesfully.
|
||||||
func (h *Header) BufferParsed() int {
|
func (h *Header) BufferParsed() int {
|
||||||
if h.flags.HasAny(flagMangledBuffer | flagOOMReached) {
|
if h.Flags().HasAny(flagMangledBuffer | flagOOMReached) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return h.hbuf.off
|
return h.hbuf.off
|
||||||
@@ -202,13 +162,13 @@ func (h *Header) BufferParsed() int {
|
|||||||
|
|
||||||
// BufferRaw returns the undeerlying buffer as stored currently in memory.
|
// 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].
|
// 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 returns the raw memory used.
|
||||||
//
|
//
|
||||||
// BufferUsed + BufferFree == BufferCapacity
|
// BufferUsed + BufferFree == BufferCapacity
|
||||||
func (h *Header) BufferUsed() int {
|
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.
|
// BufferFree returns amount of bytes free in underlying buffer.
|
||||||
@@ -222,29 +182,12 @@ func (h *Header) BufferFree() int {
|
|||||||
//
|
//
|
||||||
// BufferUsed + BufferFree == BufferCapacity
|
// BufferUsed + BufferFree == BufferCapacity
|
||||||
func (h *Header) BufferCapacity() int {
|
func (h *Header) BufferCapacity() int {
|
||||||
return cap(h.hbuf.buf)
|
return cap(h.hbuf.kv.BufferRaw())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEach iterates over header key-value field tuples.
|
// ForEach iterates over header key-value field tuples.
|
||||||
func (h *Header) ForEach(cb func(key, value []byte) error) error {
|
func (h *Header) ForEach(cb func(key, value []byte) bool) {
|
||||||
return h.hbuf.forEach(cb)
|
h.hbuf.kv.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset discards all parsed data and sets the buffer data to buf. This method
|
// 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
|
const persistentFlags = flagNoBufferGrow
|
||||||
debuglog("http:reset:hbuf")
|
debuglog("http:reset:hbuf")
|
||||||
h.hbuf.reset(buf, numHeaderCapacity)
|
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")
|
panic("small buffer and flagNoBufferGrow set")
|
||||||
}
|
}
|
||||||
*h = Header{
|
*h = Header{hbuf: h.hbuf}
|
||||||
hbuf: h.hbuf,
|
|
||||||
flags: h.flags & persistentFlags,
|
|
||||||
}
|
|
||||||
debuglog("http:reset:done")
|
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.
|
// 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) {
|
func (h *Header) Body() ([]byte, error) {
|
||||||
debuglog("http:body")
|
debuglog("http:body")
|
||||||
if h.flags.HasAny(flagMangledBuffer) {
|
flags := h.Flags()
|
||||||
|
if flags.HasAny(flagMangledBuffer) {
|
||||||
return nil, errMangledBuffer
|
return nil, errMangledBuffer
|
||||||
} else if h.flags.HasAny(flagDoneParsingHeader) {
|
} else if flags.HasAny(flagDoneParsingHeader) {
|
||||||
return h.hbuf.buf[h.hbuf.off:], nil
|
return h.BufferRaw()[h.hbuf.off:], nil
|
||||||
}
|
}
|
||||||
return nil, errUnparsed
|
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.
|
// Set sets a key-value pair in the HTTP header.
|
||||||
// Calling Set mangles the buffer.
|
// Calling Set mangles the buffer.
|
||||||
func (h *Header) Set(key, value string) {
|
func (h *Header) Set(key, value string) (enoughSpace bool) {
|
||||||
useKv := h.takeReusableSlot(key)
|
return h.hbuf.kv.Set(key, value)
|
||||||
if useKv == nil {
|
|
||||||
h.appendHeader(key, value)
|
// useKv := h.takeReusableSlot(key)
|
||||||
} else {
|
// if useKv == nil {
|
||||||
useKv.value = h.reuseOrAppend(useKv.value, value)
|
// h.appendHeader(key, value)
|
||||||
}
|
// } else {
|
||||||
|
// useKv.value = h.reuseOrAppend(useKv.value, value)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// takeReusableSlot returns the valid key-value entry for key with the largest
|
// takeReusableSlot returns the valid key-value entry for key with the largest
|
||||||
// value buffer (best candidate for in-place reuse) and invalidates any other
|
// value buffer (best candidate for in-place reuse) and invalidates any other
|
||||||
// entries sharing the key. Returns nil if the key is not present.
|
// entries sharing the key. Returns nil if the key is not present.
|
||||||
func (h *Header) takeReusableSlot(key string) *argsKV {
|
func (h *Header) takeReusableSlot(key string) *argsKV {
|
||||||
hb := &h.hbuf
|
// hb := &h.hbuf
|
||||||
var useKv *argsKV
|
var useKv *argsKV
|
||||||
for i := 0; i < len(hb.headers); i++ {
|
// for i := 0; i < len(hb.headers); i++ {
|
||||||
// Search for key-value with largest buffer for value to store value reusing buffer.
|
// // Search for key-value with largest buffer for value to store value reusing buffer.
|
||||||
gotkv := &hb.headers[i]
|
// gotkv := &hb.headers[i]
|
||||||
if gotkv.isValid() && b2s(hb.musttoken(gotkv.key)) == key {
|
// if gotkv.isValidHeader() && b2s(hb.musttoken(gotkv.key)) == key {
|
||||||
if useKv == nil {
|
// if useKv == nil {
|
||||||
useKv = gotkv
|
// useKv = gotkv
|
||||||
} else if gotkv.value.len > useKv.value.len {
|
// } else if gotkv.value.len > useKv.value.len {
|
||||||
useKv.invalidate()
|
// useKv.invalidate()
|
||||||
useKv = gotkv
|
// useKv = gotkv
|
||||||
} else {
|
// } else {
|
||||||
gotkv.invalidate()
|
// gotkv.invalidate()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return useKv
|
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.
|
// 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 {
|
func (h *Header) Get(key string) []byte {
|
||||||
debuglog("http:get:start")
|
return h.hbuf.kv.Get(key)
|
||||||
kv := h.peekHeader(key)
|
// debuglog("http:get:start")
|
||||||
if kv.isValid() {
|
// kv := h.peekHeader(key)
|
||||||
debuglog("http:get:found")
|
// if kv.isValidHeader() {
|
||||||
return h.hbuf.musttoken(kv.value)
|
// debuglog("http:get:found")
|
||||||
}
|
// return h.hbuf.musttoken(kv.value)
|
||||||
debuglog("http:get:notfound")
|
// }
|
||||||
return nil
|
// debuglog("http:get:notfound")
|
||||||
|
// return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFold gets the first value whose key matches key under ASCII case-insensitive
|
// 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
|
// Use [Header.Get] for exact match and [Header.ForEach] to find multiple values
|
||||||
// corresponding to same key.
|
// corresponding to same key.
|
||||||
func (h *Header) GetFold(key string) []byte {
|
func (h *Header) GetFold(key string) []byte {
|
||||||
hb := &h.hbuf
|
nh := h.hbuf.kv.Len()
|
||||||
for i := 0; i < len(hb.headers); i++ {
|
for i := range nh {
|
||||||
kv := hb.headers[i]
|
if asciiEqualFold(key, b2s(h.hbuf.kv.AtKey(i))) {
|
||||||
if kv.isValid() && asciiEqualFold(b2s(hb.musttoken(kv.key)), key) {
|
return h.hbuf.kv.AtValue(i)
|
||||||
return hb.musttoken(kv.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -387,9 +330,9 @@ func asciiEqualFold(a, b string) bool {
|
|||||||
|
|
||||||
// NormalizeKeys normalizes all header keys. i.e: CONTENT-type -> Content-Type
|
// NormalizeKeys normalizes all header keys. i.e: CONTENT-type -> Content-Type
|
||||||
func (h *Header) NormalizeKeys() {
|
func (h *Header) NormalizeKeys() {
|
||||||
for _, kv := range h.hbuf.headers {
|
for i, kv := range h.hbuf.kv.kvs {
|
||||||
if kv.isValid() {
|
if kv.isValidHeader() {
|
||||||
NormalizeHeaderKey(h.hbuf.musttoken(kv.key))
|
NormalizeHeaderKey(h.hbuf.kv.AtKey(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,19 +450,19 @@ func (h *Header) Status() (code, statusText []byte) {
|
|||||||
if h.statusCode.len == 0 {
|
if h.statusCode.len == 0 {
|
||||||
return nil, nil
|
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".
|
// SetStatus sets the response header's status code and status text. i.e: "200" "OK".
|
||||||
func (h *Header) SetStatus(code, statusText string) {
|
func (h *Header) SetStatus(code, statusText string) {
|
||||||
h.flags |= FlagStatusSet
|
h.hbuf.kv.flags |= FlagStatusSet
|
||||||
h.statusCode = h.reuseOrAppend(h.statusCode, code)
|
h.statusCode = h.reuseOrAppend(h.statusCode, code)
|
||||||
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetStatusInt is identical to [Header.SetStatus] but performs integer to text conversion for status code.
|
// SetStatusInt is identical to [Header.SetStatus] but performs integer to text conversion for status code.
|
||||||
func (h *Header) SetStatusInt(code int64, statusText string) {
|
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.statusCode = h.reuseOrAppendInt(h.statusCode, code, 10)
|
||||||
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
h.statusText = h.reuseOrAppend(h.statusText, statusText)
|
||||||
}
|
}
|
||||||
@@ -528,13 +471,13 @@ func (h *Header) getNonEmptyValue(s headerSlice) []byte {
|
|||||||
if s.len == 0 {
|
if s.len == 0 {
|
||||||
return nil // If empty then value is invalid, return nil.
|
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.
|
// AppendRequest appends the request header representation to the buffer and returns the result.
|
||||||
func (h *Header) AppendRequest(dst []byte) ([]byte, error) {
|
func (h *Header) AppendRequest(dst []byte) ([]byte, error) {
|
||||||
proto := h.Protocol()
|
proto := h.Protocol()
|
||||||
if h.flags.HasAny(flagOOMReached) {
|
if h.hbuf.kv.flags.HasAny(flagOOMReached) {
|
||||||
return dst, errOOM
|
return dst, errOOM
|
||||||
} else if h.requestTarget.len == 0 || h.method.len == 0 {
|
} else if h.requestTarget.len == 0 || h.method.len == 0 {
|
||||||
return dst, errNeedMethodURI
|
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"
|
// 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) {
|
func (h *Header) AppendResponseNoHeaders(dst []byte) ([]byte, error) {
|
||||||
proto := h.Protocol()
|
proto := h.Protocol()
|
||||||
if h.flags.HasAny(flagOOMReached) {
|
if h.hbuf.kv.flags.HasAny(flagOOMReached) {
|
||||||
return dst, errOOM
|
return dst, errOOM
|
||||||
} else if h.statusCode.len == 0 || h.statusText.len == 0 {
|
} else if h.statusCode.len == 0 || h.statusText.len == 0 {
|
||||||
return dst, errBadStatusCodeTxt
|
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.
|
// 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.
|
// Does not append extra \r\n to end. Appends nothing if contains no headers.
|
||||||
func (h *Header) AppendHeaders(dst []byte) []byte {
|
func (h *Header) AppendHeaders(dst []byte) []byte {
|
||||||
for i, n := 0, len(h.hbuf.headers); i < n; i++ {
|
for i, kv := range h.hbuf.kv.kvs {
|
||||||
kv := &h.hbuf.headers[i]
|
if kv.isValidHeader() {
|
||||||
if kv.isValid() {
|
k, v := h.hbuf.kv.At(i)
|
||||||
key := h.hbuf.musttoken(kv.key)
|
dst = appendHeaderLine(dst, b2s(k), b2s(v))
|
||||||
value := h.hbuf.musttoken(kv.value)
|
|
||||||
dst = appendHeaderLine(dst, b2s(key), b2s(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
|||||||
+204
-20
@@ -1,7 +1,9 @@
|
|||||||
package httpraw
|
package httpraw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/soypat/lneto/internal"
|
"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) {
|
func (mb *KVBuffer) Reset(buf []byte, kvCap int) {
|
||||||
if buf == nil {
|
if buf == nil {
|
||||||
mb.buf = mb.buf[:0]
|
mb.buf = mb.buf[:0]
|
||||||
@@ -41,15 +89,19 @@ func (mb *KVBuffer) CopyFrom(src *KVBuffer) {
|
|||||||
mb.kvs = append(mb.kvs[:0], src.kvs...)
|
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 {
|
func (mb *KVBuffer) Get(key string) []byte {
|
||||||
v := mb.getIdx(key)
|
i := mb.getIdx(key)
|
||||||
if v < 0 {
|
if i < 0 {
|
||||||
return nil
|
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) {
|
func (c *KVBuffer) ForEach(cb func(key, value []byte) bool) {
|
||||||
nc := len(c.kvs)
|
nc := len(c.kvs)
|
||||||
for i := range nc {
|
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
|
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)
|
mb.appendPair(key, value)
|
||||||
return mb.getIdx(key) >= 0
|
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() {
|
if !mb.canAddOneKV() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
mb.flags |= flagKVAppended
|
||||||
mb.kvs = append(mb.kvs, argsKV{
|
mb.kvs = append(mb.kvs, argsKV{
|
||||||
key: mb.slice(key),
|
key: mb.slice(key),
|
||||||
value: mb.slice(value),
|
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) Len() int { return len(mb.kvs) }
|
||||||
func (mb *KVBuffer) At(i int) (key, value []byte) {
|
func (mb *KVBuffer) At(i int) (key, value []byte) {
|
||||||
kv := mb.kvs[i]
|
kv := mb.kvs[i]
|
||||||
|
if !kv.HasValue() {
|
||||||
|
return mb.musttoken(kv.key), nil
|
||||||
|
}
|
||||||
return mb.musttoken(kv.key), mb.musttoken(kv.value)
|
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) setAt(i int, k, v []byte) {
|
||||||
func (mb *KVBuffer) AtValue(i int) (key []byte) { return mb.musttoken(mb.kvs[i].value) }
|
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 {
|
func (mb *KVBuffer) getIdx(key string) int {
|
||||||
for i, kv := range mb.kvs {
|
for i, kv := range mb.kvs {
|
||||||
@@ -106,12 +232,21 @@ func (mb *KVBuffer) getInvalidIdx() int {
|
|||||||
return -1
|
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
|
// 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
|
// permitted. It accounts for the byte-0 reservation on an empty buffer (see
|
||||||
// mustAppendSlice). It returns false and sets flagOOMReached when the space
|
// mustAppendSlice). It returns false and sets flagOOMReached when the space
|
||||||
// cannot be guaranteed: a tokint offset overflow, or a full buffer with
|
// cannot be guaranteed: a tokint offset overflow, or a full buffer with
|
||||||
// flagNoBufferGrow set.
|
// flagNoBufferGrow set.
|
||||||
func (mb *KVBuffer) reserve(need int) bool {
|
func (mb *KVBuffer) reserve(need int) (enoughSpace bool) {
|
||||||
if len(mb.buf) == 0 {
|
if len(mb.buf) == 0 {
|
||||||
need++ // mustAppend* reserves byte 0 on an empty buffer.
|
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 {
|
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)) {
|
if !mb.canAddOneKV() || !mb.reserve(len(key)+len(value)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
k := mb.mustAppendSlice(key)
|
mb.flags |= flagKVAppended
|
||||||
v := mb.mustAppendSlice(value)
|
|
||||||
debuglog("http:appendhdr:grow-hdrs")
|
|
||||||
mb.kvs = append(mb.kvs, argsKV{
|
mb.kvs = append(mb.kvs, argsKV{
|
||||||
key: k,
|
key: mb.mustAppendSlice(key),
|
||||||
value: v,
|
value: mb.mustAppendSlice(value),
|
||||||
})
|
})
|
||||||
return true
|
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
|
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)])
|
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 {
|
func (mb *KVBuffer) slice(value []byte) headerSlice {
|
||||||
|
if value == nil {
|
||||||
|
return headerSlice{}
|
||||||
|
}
|
||||||
return bytes2tok(mb.buf, value)
|
return bytes2tok(mb.buf, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,3 +324,32 @@ func (mb KVBuffer) musttoken(slice headerSlice) []byte {
|
|||||||
return tok2bytes(mb.buf, slice)
|
return tok2bytes(mb.buf, slice)
|
||||||
}
|
}
|
||||||
func (mb *KVBuffer) noKV() argsKV { return argsKV{} }
|
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
|
const maxBufLen = 0xffff
|
||||||
|
|
||||||
type headerBuf struct {
|
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[: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.
|
// offset into buf for parsing.
|
||||||
off int
|
off int
|
||||||
// args contains key-value store.
|
// args contains key-value store.
|
||||||
headers []argsKV
|
// headers []argsKV
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sets the buffer data and discards all parsed data. The field table is
|
// 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
|
// 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.
|
// reused across requests settles on its largest buffer and stops allocating.
|
||||||
func (h *headerBuf) reset(buf []byte, numHeaderCapacity int) {
|
func (h *headerBuf) reset(buf []byte, numHeaderCapacity int) {
|
||||||
if buf == nil {
|
h.kv.Reset(buf, numHeaderCapacity)
|
||||||
buf = h.buf[:0] // Reuse buffer but discard raw data on nil input.
|
h.off = 0
|
||||||
}
|
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type scannerState struct {
|
type scannerState struct {
|
||||||
@@ -109,20 +88,22 @@ func (h *Header) parse(asResponse bool) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
debuglog("http:firstline:done")
|
debuglog("http:firstline:done")
|
||||||
err = h.parseNextHeaders(h.flags)
|
err = h.parseNextHeaders(h.Flags())
|
||||||
debuglog("http:headers:done")
|
debuglog("http:headers:done")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) parseFirstLine(asResponse bool) (err error) {
|
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.
|
return errBufferTooLarge // Offsets would overflow uint16 tokint.
|
||||||
}
|
}
|
||||||
|
flags := h.Flags()
|
||||||
if asResponse {
|
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 {
|
} 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,39 +111,33 @@ func (h *Header) parseNextHeaders(flags Flags) error {
|
|||||||
var ss scannerState
|
var ss scannerState
|
||||||
h.hbuf.parseNextHeaders(&ss, flags)
|
h.hbuf.parseNextHeaders(&ss, flags)
|
||||||
if ss.err != nil {
|
if ss.err != nil {
|
||||||
h.flags |= flagConnClose
|
h.hbuf.kv.flags |= flagConnClose
|
||||||
return ss.err
|
return ss.err
|
||||||
}
|
}
|
||||||
h.flags |= flagDoneParsingHeader
|
h.hbuf.kv.flags |= flagDoneParsingHeader
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hb *headerBuf) readFromBytes(b []byte) {
|
func (hb *headerBuf) free() int { return hb.kv.free() }
|
||||||
hb.buf = append(hb.buf, b...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hb *headerBuf) free() int { return cap(hb.buf) - len(hb.buf) }
|
|
||||||
|
|
||||||
func (hb *headerBuf) parseNextHeaders(ss *scannerState, flags Flags) {
|
func (hb *headerBuf) parseNextHeaders(ss *scannerState, flags Flags) {
|
||||||
debuglog("http:nexthdr:loop")
|
debuglog("http:nexthdr:loop")
|
||||||
for kv := hb.next(ss); kv.isValid(); kv = hb.next(ss) {
|
for kv := hb.next(ss); kv.isValidHeader(); kv = hb.next(ss) {
|
||||||
if len(hb.headers) == cap(hb.headers) && flags.HasAny(flagNoBufferGrow) {
|
if !hb.kv.canAddOneKV() {
|
||||||
// Refuse to grow the headers slice: the caller granted this much
|
|
||||||
// memory and no more, see [Header.Reset].
|
|
||||||
ss.err = ErrHeaderTooMany
|
ss.err = ErrHeaderTooMany
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hb.headers = append(hb.headers, kv)
|
hb.kv.kvs = append(hb.kv.kvs, kv)
|
||||||
}
|
}
|
||||||
debuglog("http:nexthdr:done")
|
debuglog("http:nexthdr:done")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hb *headerBuf) offBuf() []byte {
|
func (hb *headerBuf) offBuf() []byte {
|
||||||
return hb.buf[hb.off:]
|
return hb.kv.buf[hb.off:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hb *headerBuf) skipLeadingCRLF() {
|
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++
|
hb.off++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +147,7 @@ func (hb *headerBuf) scanLine() []byte {
|
|||||||
if len(buf) > 0 && buf[len(buf)-1] == '\r' {
|
if len(buf) > 0 && buf[len(buf)-1] == '\r' {
|
||||||
buf = buf[:len(buf)-1] // exclude carriage return.
|
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.
|
hb.off++ // consume newline.
|
||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
@@ -206,8 +181,8 @@ func (hb *headerBuf) parseFirstLineRequest(initFlags Flags) (method, uri, proto
|
|||||||
reqURIEnd := bytes.IndexByte(b[methodEnd+1:], ' ')
|
reqURIEnd := bytes.IndexByte(b[methodEnd+1:], ' ')
|
||||||
if reqURIEnd > 0 {
|
if reqURIEnd > 0 {
|
||||||
reqURIEnd += methodEnd + 1
|
reqURIEnd += methodEnd + 1
|
||||||
uri = hb.slice(b[methodEnd+1 : reqURIEnd])
|
uri = hb.kv.slice(b[methodEnd+1 : reqURIEnd])
|
||||||
proto = hb.slice(b[reqURIEnd+1:]) // Skip space before protocol.
|
proto = hb.kv.slice(b[reqURIEnd+1:]) // Skip space before protocol.
|
||||||
if b2s(b[reqURIEnd+1:]) != strHTTP11 {
|
if b2s(b[reqURIEnd+1:]) != strHTTP11 {
|
||||||
flags |= flagNoHTTP11
|
flags |= flagNoHTTP11
|
||||||
}
|
}
|
||||||
@@ -216,9 +191,9 @@ func (hb *headerBuf) parseFirstLineRequest(initFlags Flags) (method, uri, proto
|
|||||||
} else {
|
} else {
|
||||||
// No version provided.
|
// No version provided.
|
||||||
flags |= flagNoHTTP11
|
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
|
return method, uri, proto, flags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,66 +235,19 @@ func (hb *headerBuf) parseFirstLineResponse(initFlags Flags) (statusCode, status
|
|||||||
return statusCode, statusText, flags, errBadStatusCode
|
return statusCode, statusText, flags, errBadStatusCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statusCode = hb.slice(code)
|
statusCode = hb.kv.slice(code)
|
||||||
if codeEnd < len(b) {
|
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")
|
debuglog("http:resp:done")
|
||||||
return statusCode, statusText, flags, nil
|
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 (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 {
|
func (h *Header) reuseOrAppend(tok headerSlice, value string) headerSlice {
|
||||||
if tok.len > tokint(len(value)) {
|
if tok.len > tokint(len(value)) {
|
||||||
copy(h.hbuf.musttoken(tok), value)
|
copy(h.hbuf.kv.musttoken(tok), value)
|
||||||
tok.len = tokint(len(value))
|
tok.len = tokint(len(value))
|
||||||
return tok
|
return tok
|
||||||
}
|
}
|
||||||
@@ -332,7 +260,7 @@ func (h *Header) appendSlice(value string) headerSlice {
|
|||||||
return headerSlice{}
|
return headerSlice{}
|
||||||
}
|
}
|
||||||
h.flags |= flagMangledBuffer
|
h.flags |= flagMangledBuffer
|
||||||
return h.hbuf.mustAppendSlice(value)
|
return h.hbuf.kv.mustAppendSlice(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) appendHeader(key, value string) {
|
func (h *Header) appendHeader(key, value string) {
|
||||||
@@ -437,7 +365,7 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
|||||||
ss.nextColon = -1
|
ss.nextColon = -1
|
||||||
ss.nextNewLine = -1
|
ss.nextNewLine = -1
|
||||||
}
|
}
|
||||||
buf := hb.buf[hb.off:]
|
buf := hb.kv.buf[hb.off:]
|
||||||
blen := len(buf)
|
blen := len(buf)
|
||||||
if blen >= 2 && buf[0] == '\r' && buf[1] == '\n' {
|
if blen >= 2 && buf[0] == '\r' && buf[1] == '\n' {
|
||||||
hb.off += 2
|
hb.off += 2
|
||||||
@@ -484,7 +412,7 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
|||||||
|
|
||||||
// Ready to store key..
|
// Ready to store key..
|
||||||
var resultKV argsKV
|
var resultKV argsKV
|
||||||
resultKV.key = hb.slice(buf[:n])
|
resultKV.key = hb.kv.slice(buf[:n])
|
||||||
n++ // consume colon.
|
n++ // consume colon.
|
||||||
for len(buf) > n && buf[n] == ' ' {
|
for len(buf) > n && buf[n] == ' ' {
|
||||||
n++ // Trim leading spaces.
|
n++ // Trim leading spaces.
|
||||||
@@ -511,18 +439,19 @@ func (hb *headerBuf) next(ss *scannerState) argsKV {
|
|||||||
if valueEnd > valueStart && buf[valueEnd-1] == '\r' {
|
if valueEnd > valueStart && buf[valueEnd-1] == '\r' {
|
||||||
valueEnd-- // Trim \r character if present before value.
|
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
|
hb.off += n
|
||||||
return resultKV
|
return resultKV
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectionClose returns true if 'Connection: close' header is set or if a invalid header was found.
|
// ConnectionClose returns true if 'Connection: close' header is set or if a invalid header was found.
|
||||||
func (h *Header) ConnectionClose() bool {
|
func (h *Header) ConnectionClose() bool {
|
||||||
closed := h.flags.HasAny(flagConnClose) ||
|
flags := h.Flags()
|
||||||
h.hasHeaderValue(headerConnection, strClose) ||
|
closed := flags.HasAny(flagConnClose) ||
|
||||||
(h.flags.HasAny(flagNoHTTP11) && !h.hasHeaderValue(headerConnection, "keep-alive"))
|
h.hbuf.kv.HasKeyValue(headerConnection, strClose) ||
|
||||||
|
(flags.HasAny(flagNoHTTP11) && !h.hbuf.kv.HasKeyValue(headerConnection, "keep-alive"))
|
||||||
if closed {
|
if closed {
|
||||||
h.flags |= flagConnClose
|
h.hbuf.kv.flags |= flagConnClose
|
||||||
}
|
}
|
||||||
return closed
|
return closed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,6 +331,14 @@ func TestCookie_ParseBytes(t *testing.T) {
|
|||||||
if string(c.Get("Path")) != "/" {
|
if string(c.Get("Path")) != "/" {
|
||||||
t.Errorf("Path = %q; want /", 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") {
|
if !c.HasKeyOrSingleValue("Secure") {
|
||||||
t.Error("expected Secure flag")
|
t.Error("expected Secure flag")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user