mirror of
https://github.com/soypat/lneto.git
synced 2026-09-04 13:59:04 +00:00
add http/httphi (#171)
* add http/httphi * begin adding httphi tests * claude found neat bugs * add low level Handle function and more tests * more tests, run go generate * add Hijacker-like functionality * improve locking and acquisition of Exchanges in reconfiguring * several bugfixes, add internal.IntLen, round up http-linux example with new router API * small nit * add benchmarks * add query handling * remove ForEach pattern, allocates in TinyGo * massive documentation push and code reordering in files * Router.Handle returns error after being torn down * run go fix * rework Mux interface to receive a string request path * add MethodFrom * minor doc nit * fail on incomplete staging * add raw buffer access * add streaming API distinct from Exchange * begin adding multipart form logic * finish rounding up multipart form parsing * remove status type * first Multipart approach * begin adding readMultiPart * add Exchange.ReadMultiparts reimagining of clanker slop * ai insists with backoffs * simplify clanker slop * apply go fix * add a pattern argument to Mux * explicit header key/value alloc and add ExchangeConfig * fix tests after excplicit header alloc change * fix examples * run go fix * expose rawsock as experimental package (will use for external benchmarks) * remove backoff from form parsing * @MDr164 suggestions get potential fixes * apply go fix * add examples * add README.md * fix rawsock tinygo implementation * apply @MDr164 various fixes * update documentation on ContentLength methods and fix bug in Form reset on empty body * fix tests * add fuzz tests * run go fix * io.ErrNoProgress on parsing form spin * run go fix * remove backoff assumption from Router * httphi.Handle rejects unsupported protocols * go format router.go * add kvbuffer * rewrite Cookie with KVBuffer * mid refactor of KVBuffer into Header * work on KVBuffer exhausted semantics * add Go's ServeMux Request.PathValue access semantics to Exchange, Mux and MuxSlice * add PathValue example * document all the things; improve req Query semantics; add Form.EnableBufferGrowth * unexport kvBuffer * add Exchange.PathValueAppend * use stdlib in example instead of rawsock * remove rawsock from http example * add darwin arch rawsock * fix example * rename Router.TeardownGoroutines to Shutdown matching http.Server.Shutdown * rename types and identifiers * @MDr164 Content-Type and Transfer-Encoding bug catches
This commit is contained in:
committed by
Patricio Whittingslow
parent
a3f2742abf
commit
5c54030f19
+43
-61
@@ -7,105 +7,89 @@ import (
|
||||
// Cookie implements cookie key-value parsing. Methods function similarly to eponymous [Header] methods.
|
||||
// Cookie represents a single-line Cookie header value in a HTTP header, much like the standard library Cookie.
|
||||
type Cookie struct {
|
||||
buf []byte
|
||||
kvs []argsKV // first key-value pair is the data Key/Value pair.
|
||||
kv kvBuffer
|
||||
}
|
||||
|
||||
// EnableBufferGrowth allows the cookie's buffer to grow past what [Cookie.Reset] was
|
||||
// handed. See [kvBuffer.EnableBufferGrowth].
|
||||
func (c *Cookie) EnableBufferGrowth(enableBufferGrowth bool) {
|
||||
c.kv.EnableBufferGrowth(enableBufferGrowth)
|
||||
}
|
||||
|
||||
// Reset functions very similarly to [Header.Reset]. Can be used for in-place cookie parsing.
|
||||
func (c *Cookie) Reset(buf []byte) {
|
||||
if buf == nil {
|
||||
buf = c.buf[:0]
|
||||
}
|
||||
*c = Cookie{
|
||||
buf: buf,
|
||||
kvs: c.kvs[:0],
|
||||
}
|
||||
func (c *Cookie) Reset(buf []byte, capKV int) { c.kv.Reset(buf, capKV) }
|
||||
|
||||
func (c *Cookie) valid() bool {
|
||||
return len(c.kv.kvs) > 0 && c.kv.kvs[0].key.len > 0
|
||||
}
|
||||
|
||||
// Name returns the first cookie key which is commonly referred to as the cookie's name. Returns nil if not found.
|
||||
func (c *Cookie) Name() []byte {
|
||||
if len(c.kvs) == 0 || c.kvs[0].key.len == 0 {
|
||||
if !c.valid() {
|
||||
return nil
|
||||
}
|
||||
return tok2bytes(c.buf, c.kvs[0].key)
|
||||
return c.kv.AtKey(0)
|
||||
}
|
||||
|
||||
// Value returns the first cookie value associated with the name. Returns nil if not found.
|
||||
func (c *Cookie) Value() []byte {
|
||||
if len(c.kvs) == 0 || c.kvs[0].value.len == 0 {
|
||||
if !c.valid() {
|
||||
return nil
|
||||
}
|
||||
return tok2bytes(c.buf, c.kvs[0].value)
|
||||
return c.kv.AtValue(0)
|
||||
}
|
||||
|
||||
// ParseBytes copies the argument bytes to the Cookie's underlying buffer and parses the cookie.
|
||||
func (c *Cookie) ParseBytes(cookie []byte) error {
|
||||
c.Reset(nil)
|
||||
c.buf = append(c.buf[:0], cookie...)
|
||||
c.Reset(nil, 0)
|
||||
c.kv.buf = append(c.kv.buf[:0], cookie...)
|
||||
return c.Parse()
|
||||
}
|
||||
|
||||
// CopyFrom makes a copy of the argument cookie to the receiver dst argument. No memory is shared between cookies.
|
||||
func (dst *Cookie) CopyFrom(c Cookie) {
|
||||
dst.buf = append(dst.buf[:0], c.buf...)
|
||||
dst.kvs = append(dst.kvs[:0], c.kvs...)
|
||||
}
|
||||
func (dst *Cookie) CopyFrom(c Cookie) { dst.kv.CopyFrom(&c.kv) }
|
||||
|
||||
// Parse parses the cookie's buffer in place.
|
||||
func (c *Cookie) Parse() error {
|
||||
if len(c.kvs) > 0 {
|
||||
if c.kv.Len() > 0 {
|
||||
return errCookiesParsed
|
||||
}
|
||||
off := 0
|
||||
for {
|
||||
k, v, n := parseCookie(c.buf[off:])
|
||||
k, v, n := parseCookie(c.kv.buf[off:])
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
c.kvs = append(c.kvs, argsKV{
|
||||
key: bytes2tok(c.buf, k),
|
||||
value: bytes2tok(c.buf, v),
|
||||
})
|
||||
if !c.kv.setInternal(k, v) {
|
||||
return ErrBufferExhausted
|
||||
}
|
||||
|
||||
off += n
|
||||
}
|
||||
if len(c.kvs) == 0 {
|
||||
if c.kv.Len() == 0 {
|
||||
return errNoCookies
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cookie) ForEach(cb func(key, value []byte) error) error {
|
||||
nc := len(c.kvs)
|
||||
for i := range nc {
|
||||
kv := c.kvs[i]
|
||||
key := tok2bytes(c.buf, kv.key)
|
||||
value := tok2bytes(c.buf, kv.value)
|
||||
err := cb(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// ForEach iterates over the cookie's key-value pairs, stopping on the first
|
||||
// error returned by cb and returning it.
|
||||
func (c *Cookie) ForEach(cb func(key, value []byte) bool) {
|
||||
c.kv.ForEach(cb)
|
||||
}
|
||||
|
||||
// Get gets a cookie's value from its key. Use HasValueOrKey to check if a key or single-valued cookie is present in the cookie.
|
||||
func (c *Cookie) Get(key string) []byte {
|
||||
nc := len(c.kvs)
|
||||
for i := range nc {
|
||||
kv := c.kvs[i]
|
||||
if b2s(tok2bytes(c.buf, kv.key)) == key {
|
||||
return tok2bytes(c.buf, kv.value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
nc := len(c.kvs)
|
||||
for i := range nc {
|
||||
kv := c.kvs[i]
|
||||
if kv.key.len == 0 && b2s(tok2bytes(c.buf, kv.value)) == keyOrSingleValue ||
|
||||
b2s(tok2bytes(c.buf, kv.key)) == 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
|
||||
}
|
||||
}
|
||||
@@ -158,16 +142,14 @@ func (c *Cookie) String() string {
|
||||
|
||||
// AppendKeyValues appends the HTTP header value of the cookie expected after the "Cookie:" string. Does not include trailing \r\n's.
|
||||
func (c *Cookie) AppendKeyValues(dst []byte) []byte {
|
||||
nc := len(c.kvs)
|
||||
nc := c.kv.Len()
|
||||
for i := range nc {
|
||||
kv := c.kvs[i]
|
||||
key := tok2bytes(c.buf, kv.key)
|
||||
value := tok2bytes(c.buf, kv.value)
|
||||
if len(key) != 0 {
|
||||
dst = append(dst, key...)
|
||||
k, v := c.kv.At(i)
|
||||
if len(k) != 0 {
|
||||
dst = append(dst, k...)
|
||||
dst = append(dst, '=')
|
||||
}
|
||||
dst = append(dst, value...)
|
||||
dst = append(dst, v...)
|
||||
if i+1 < nc {
|
||||
dst = append(dst, ';', ' ')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user