mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
internal/httpsfv: implement parsing support for Dictionary and List type.
This change implements the Parse functions for the Dictionary and List type. At this point, we should be able to use internal/httpsfv package to extract information from any HTTP SFV that follows RFC 8941. In future changes, we will add additional types introduced in RFC 9651 to achieve feature parity with it. Additionally, we will add Parse functions for all the HTTP SFV types, such that users of the package do not need to do their own type assertions and conversions. Note that the Dictionary and List type do not have a consume function. This is because both types never appear as a child of other types, meaning it is guaranteed to always consume its entire string input. For go/golang#75500 Change-Id: I376dca274d920a4bea276ebb4d49a9cd768c79fe Reviewed-on: https://go-review.googlesource.com/c/net/+/707100 Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com>
This commit is contained in:
committed by
Nicholas Husin
parent
7d8cfcee6c
commit
f2e909b982
@@ -48,24 +48,56 @@ func countLeftWhitespace(s string) int {
|
||||
return i
|
||||
}
|
||||
|
||||
// TODO(nsh): Implement other consume functions that will be needed to fully
|
||||
// deal with all possible HTTP SFV, specifically:
|
||||
// - consumeDictionary(s string, f func(key, val, param string)) (consumed, rest string, ok bool)
|
||||
// For example, given `a=123,b;a="a", i`, ConsumeDictionary will call f() 3 times
|
||||
// with the following args:
|
||||
// - key: `a`, val: `123`, param: ``
|
||||
// - key: `b`, val: ``, param:`;a="a"`
|
||||
// - key: `i`, val: ``, param: ``
|
||||
//
|
||||
// - consumeList(s string, f func(member, param string)) (consumed, rest string, ok bool)
|
||||
// For example, given `123.456;i, ("foo" "bar"; lvl=2); lvl=1`, ConsumeList will
|
||||
// call f() 2 times with the following args:
|
||||
// - member: `123.456`, param: `i`
|
||||
// - member: `("foo" "bar"; lvl=2)`, param: `; lvl=1`
|
||||
|
||||
// TODO(nsh): Implement corresponding parse functions for all consume functions
|
||||
// that exists.
|
||||
|
||||
// ParseList is used to parse a string that represents a list in an
|
||||
// HTTP Structured Field Values.
|
||||
//
|
||||
// Given a string that represents a list, it will call the given function using
|
||||
// each of the members and parameters contained in the list. This allows the
|
||||
// caller to extract information out of the list.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not a list. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-list.
|
||||
func ParseList(s string, f func(member, param string)) (ok bool) {
|
||||
for len(s) != 0 {
|
||||
var member, param string
|
||||
if len(s) != 0 && s[0] == '(' {
|
||||
if member, s, ok = consumeBareInnerList(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
if member, s, ok = consumeBareItem(s); !ok {
|
||||
return ok
|
||||
}
|
||||
}
|
||||
if param, s, ok = consumeParameter(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
if f != nil {
|
||||
f(member, param)
|
||||
}
|
||||
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
if s[0] != ',' {
|
||||
return false
|
||||
}
|
||||
s = s[1:]
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// consumeBareInnerList consumes an inner list
|
||||
// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
|
||||
// except for the inner list's top-most parameter.
|
||||
@@ -151,6 +183,58 @@ func ParseItem(s string, f func(bareItem, param string)) (ok bool) {
|
||||
return rest == "" && ok
|
||||
}
|
||||
|
||||
// ParseDictionary is used to parse a string that represents a dictionary in an
|
||||
// HTTP Structured Field Values.
|
||||
//
|
||||
// Given a string that represents a dictionary, it will call the given function
|
||||
// using each of the keys, values, and parameters contained in the dictionary.
|
||||
// This allows the caller to extract information out of the dictionary.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not a dictionary. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-dictionary.
|
||||
func ParseDictionary(s string, f func(key, val, param string)) (ok bool) {
|
||||
for len(s) != 0 {
|
||||
var key, val, param string
|
||||
val = "?1" // Default value for empty val is boolean true.
|
||||
if key, s, ok = consumeKey(s); !ok {
|
||||
return ok
|
||||
}
|
||||
if len(s) != 0 && s[0] == '=' {
|
||||
s = s[1:]
|
||||
if len(s) != 0 && s[0] == '(' {
|
||||
if val, s, ok = consumeBareInnerList(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
if val, s, ok = consumeBareItem(s); !ok {
|
||||
return ok
|
||||
}
|
||||
}
|
||||
}
|
||||
if param, s, ok = consumeParameter(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
if f != nil {
|
||||
f(key, val, param)
|
||||
}
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
if s[0] == ',' {
|
||||
s = s[1:]
|
||||
}
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#parse-param.
|
||||
func consumeParameter(s string, f func(key, val string)) (consumed, rest string, ok bool) {
|
||||
rest = s
|
||||
|
||||
Reference in New Issue
Block a user