mirror of
https://github.com/soypat/lneto.git
synced 2026-08-17 21:24:04 +00:00
remove ForEach pattern, allocates in TinyGo
This commit is contained in:
+25
-16
@@ -372,21 +372,32 @@ func (h *Header) RequestPath() []byte {
|
||||
return uri[:query]
|
||||
}
|
||||
|
||||
// ForEachQuery iterates over the request URI's query string key-value pairs as
|
||||
// they appear on the wire, percent-encoded and with '+' undecoded. fn returns
|
||||
// true to continue iterating, false to stop.
|
||||
//
|
||||
// A pair with no '=' yields a nil value, i.e: "debug" in "?debug&q=go", which
|
||||
// distinguishes it from "?debug=" where the value is present and empty. Empty
|
||||
// sequences are skipped, so "?&&q=go&" yields a single pair. Only '&' separates
|
||||
// pairs and only the first '=' splits a pair.
|
||||
func (h *Header) ForEachQuery(fn func(rawkey, rawval []byte) bool) {
|
||||
// RequestQuery returns the request URI's query string as it appears on the
|
||||
// wire, percent-encoded and with '+' undecoded, i.e: "q=go" for "/search?q=go".
|
||||
// Returns nil if the URI has no query string. Iterate it with [NextQueryPair].
|
||||
func (h *Header) RequestQuery() []byte {
|
||||
uri := h.RequestURI()
|
||||
start := bytes.IndexByte(uri, '?')
|
||||
if start < 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
query := uri[start+1:]
|
||||
return uri[start+1:]
|
||||
}
|
||||
|
||||
// NextQueryPair splits the leading key-value pair off a query string and returns
|
||||
// what remains of it. Loop until rawkey is nil:
|
||||
//
|
||||
// rawkey, rawval, rest := httpraw.NextQueryPair(h.RequestQuery())
|
||||
// for rawkey != nil {
|
||||
// // use rawkey, rawval.
|
||||
// rawkey, rawval, rest = httpraw.NextQueryPair(rest)
|
||||
// }
|
||||
//
|
||||
// A pair with no '=' yields a nil rawval, i.e: "debug" in "?debug&q=go", which
|
||||
// distinguishes it from "?debug=" where the value is present and empty. Empty
|
||||
// sequences are skipped, so "?&&q=go&" yields a single pair. Only '&' separates
|
||||
// pairs and only the first '=' splits a pair.
|
||||
func NextQueryPair(query []byte) (rawkey, rawval, rest []byte) {
|
||||
for len(query) > 0 {
|
||||
pair := query
|
||||
amp := bytes.IndexByte(query, '&')
|
||||
@@ -398,14 +409,12 @@ func (h *Header) ForEachQuery(fn func(rawkey, rawval []byte) bool) {
|
||||
if len(pair) == 0 {
|
||||
continue // Empty sequence, see WHATWG URL urlencoded parsing.
|
||||
}
|
||||
key, value := pair, []byte(nil)
|
||||
if eq := bytes.IndexByte(pair, '='); eq >= 0 {
|
||||
key, value = pair[:eq], pair[eq+1:]
|
||||
}
|
||||
if !fn(key, value) {
|
||||
return
|
||||
return pair[:eq], pair[eq+1:], query
|
||||
}
|
||||
return pair, nil, query
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// Protocol returns the request header's HTTP protocol. Usually "HTTP/1.1".
|
||||
|
||||
+20
-17
@@ -173,7 +173,7 @@ func TestHeaderRequestPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderForEachQuery(t *testing.T) {
|
||||
func TestNextQueryPair(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
uri string
|
||||
want string // "key=value" pairs joined by '|'; nil value shown as "key".
|
||||
@@ -196,7 +196,8 @@ func TestHeaderForEachQuery(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got []byte
|
||||
h.ForEachQuery(func(rawkey, rawval []byte) bool {
|
||||
rawkey, rawval, rest := NextQueryPair(h.RequestQuery())
|
||||
for rawkey != nil {
|
||||
if len(got) > 0 {
|
||||
got = append(got, '|')
|
||||
}
|
||||
@@ -205,28 +206,30 @@ func TestHeaderForEachQuery(t *testing.T) {
|
||||
got = append(got, '=')
|
||||
got = append(got, rawval...)
|
||||
}
|
||||
return true
|
||||
})
|
||||
rawkey, rawval, rest = NextQueryPair(rest)
|
||||
}
|
||||
if string(got) != test.want {
|
||||
t.Errorf("uri %q: want %q, got %q", test.uri, test.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn returning false stops iteration.
|
||||
func TestHeaderForEachQueryStop(t *testing.T) {
|
||||
var h Header
|
||||
err := h.ParseBytes(false, []byte("GET /x?a=1&b=2&c=3 HTTP/1.1\r\nHost: h\r\n\r\n"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// A nil key ends iteration, and stopping early is just not looping again.
|
||||
func TestNextQueryPairEnd(t *testing.T) {
|
||||
rawkey, rawval, rest := NextQueryPair([]byte("a=1&b=2"))
|
||||
if string(rawkey) != "a" || string(rawval) != "1" || string(rest) != "b=2" {
|
||||
t.Fatalf("want a=1 with rest b=2, got %q=%q rest %q", rawkey, rawval, rest)
|
||||
}
|
||||
visited := 0
|
||||
h.ForEachQuery(func(rawkey, rawval []byte) bool {
|
||||
visited++
|
||||
return string(rawkey) != "b"
|
||||
})
|
||||
if visited != 2 {
|
||||
t.Errorf("want iteration stopped after 2 pairs, visited %d", visited)
|
||||
rawkey, _, rest = NextQueryPair(rest)
|
||||
if string(rawkey) != "b" || len(rest) != 0 {
|
||||
t.Fatalf("want b with empty rest, got %q rest %q", rawkey, rest)
|
||||
}
|
||||
if rawkey, _, _ = NextQueryPair(rest); rawkey != nil {
|
||||
t.Fatalf("want nil key at end of query, got %q", rawkey)
|
||||
}
|
||||
// Trailing separators yield no pair rather than an empty one.
|
||||
if rawkey, _, _ = NextQueryPair([]byte("&&")); rawkey != nil {
|
||||
t.Fatalf("want nil key for empty sequences, got %q", rawkey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user