Skip to content

Commit edb764c

Browse files
nicholashusingopherbot
authored andcommitted
internal/httpsfv: add parsing functionality for types defined in RFC 8941
This change introduces parsing functions for all item types defined in RFC 8941, namely: integers, decimals, strings, tokens, byte sequences, and booleans. At this point, internal/httpsfv should be usable for parsing any RFC 8941-compliant HTTP Structured Field Values. In a future CL, we will add support for parsing display strings and dates, so that this package fully supports RFC 9651. For golang/go#75500 Change-Id: Ib8ad2caa5f6ea4285d00506faa4b8127c2cc9419 Reviewed-on: https://go-review.googlesource.com/c/net/+/708435 Auto-Submit: Nicholas Husin <[email protected]> Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Nicholas Husin <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent fbba2c2 commit edb764c

File tree

2 files changed

+389
-31
lines changed

2 files changed

+389
-31
lines changed

internal/httpsfv/httpsfv.go

Lines changed: 119 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Package httpsfv provide functionality for dealing with HTTP Structured Field
6-
// Values.
5+
// Package httpsfv provides functionality for dealing with HTTP Structured
6+
// Field Values.
77
package httpsfv
88

99
import (
1010
"slices"
11+
"strconv"
12+
"strings"
1113
"unicode/utf8"
1214
)
1315

@@ -70,15 +72,14 @@ func decOctetHex(ch1, ch2 byte) (ch byte, ok bool) {
7072
return ch1<<4 | ch2, true
7173
}
7274

73-
// TODO(nsh): Implement corresponding parse functions for all consume functions
74-
// that exists.
75+
// TODO(nsh): Implement parse functions for date and display string to make
76+
// this package fully support parsing RFC 9651-compliant HTTP SFV.
7577

76-
// ParseList is used to parse a string that represents a list in an
77-
// HTTP Structured Field Values.
78+
// ParseList parses a list from a given HTTP Structured Field Values.
7879
//
79-
// Given a string that represents a list, it will call the given function using
80-
// each of the members and parameters contained in the list. This allows the
81-
// caller to extract information out of the list.
80+
// Given an HTTP SFV string that represents a list, it will call the given
81+
// function using each of the members and parameters contained in the list.
82+
// This allows the caller to extract information out of the list.
8283
//
8384
// This function will return once it encounters the end of the string, or
8485
// something that is not a list. If it cannot consume the entire given
@@ -123,7 +124,7 @@ func ParseList(s string, f func(member, param string)) (ok bool) {
123124
// consumeBareInnerList consumes an inner list
124125
// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
125126
// except for the inner list's top-most parameter.
126-
// For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`
127+
// For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`.
127128
func consumeBareInnerList(s string, f func(bareItem, param string)) (consumed, rest string, ok bool) {
128129
if len(s) == 0 || s[0] != '(' {
129130
return "", s, false
@@ -152,18 +153,18 @@ func consumeBareInnerList(s string, f func(bareItem, param string)) (consumed, r
152153
return s[:len(s)-len(rest)], rest, true
153154
}
154155

155-
// ParseBareInnerList is used to parse a string that represents a bare inner
156-
// list in an HTTP Structured Field Values.
156+
// ParseBareInnerList parses a bare inner list from a given HTTP Structured
157+
// Field Values.
157158
//
158159
// We define a bare inner list as an inner list
159160
// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
160161
// without the top-most parameter of the inner list. For example, given the
161162
// inner list `(a;b c;d);e`, the bare inner list would be `(a;b c;d)`.
162163
//
163-
// Given a string that represents a bare inner list, it will call the given
164-
// function using each of the bare item and parameter within the bare inner
165-
// list. This allows the caller to extract information out of the bare inner
166-
// list.
164+
// Given an HTTP SFV string that represents a bare inner list, it will call the
165+
// given function using each of the bare item and parameter within the bare
166+
// inner list. This allows the caller to extract information out of the bare
167+
// inner list.
167168
//
168169
// This function will return once it encounters the end of the bare inner list,
169170
// or something that is not a bare inner list. If it cannot consume the entire
@@ -188,12 +189,11 @@ func consumeItem(s string, f func(bareItem, param string)) (consumed, rest strin
188189
return s[:len(s)-len(rest)], rest, true
189190
}
190191

191-
// ParseItem is used to parse a string that represents an item in an HTTP
192-
// Structured Field Values.
192+
// ParseItem parses an item from a given HTTP Structured Field Values.
193193
//
194-
// Given a string that represents an item, it will call the given function
195-
// once, with the bare item and the parameter of the item. This allows the
196-
// caller to extract information out of the parameter.
194+
// Given an HTTP SFV string that represents an item, it will call the given
195+
// function once, with the bare item and the parameter of the item. This allows
196+
// the caller to extract information out of the item.
197197
//
198198
// This function will return once it encounters the end of the string, or
199199
// something that is not an item. If it cannot consume the entire given
@@ -205,12 +205,13 @@ func ParseItem(s string, f func(bareItem, param string)) (ok bool) {
205205
return rest == "" && ok
206206
}
207207

208-
// ParseDictionary is used to parse a string that represents a dictionary in an
209-
// HTTP Structured Field Values.
208+
// ParseDictionary parses a dictionary from a given HTTP Structured Field
209+
// Values.
210210
//
211-
// Given a string that represents a dictionary, it will call the given function
212-
// using each of the keys, values, and parameters contained in the dictionary.
213-
// This allows the caller to extract information out of the dictionary.
211+
// Given an HTTP SFV string that represents a dictionary, it will call the
212+
// given function using each of the keys, values, and parameters contained in
213+
// the dictionary. This allows the caller to extract information out of the
214+
// dictionary.
214215
//
215216
// This function will return once it encounters the end of the string, or
216217
// something that is not a dictionary. If it cannot consume the entire given
@@ -286,12 +287,11 @@ func consumeParameter(s string, f func(key, val string)) (consumed, rest string,
286287
return s[:len(s)-len(rest)], rest, true
287288
}
288289

289-
// ParseParameter is used to parse a string that represents a parameter in an
290-
// HTTP Structured Field Values.
290+
// ParseParameter parses a parameter from a given HTTP Structured Field Values.
291291
//
292-
// Given a string that represents a parameter, it will call the given function
293-
// using each of the keys and values contained in the parameter. This allows
294-
// the caller to extract information out of the parameter.
292+
// Given an HTTP SFV string that represents a parameter, it will call the given
293+
// function using each of the keys and values contained in the parameter. This
294+
// allows the caller to extract information out of the parameter.
295295
//
296296
// This function will return once it encounters the end of the string, or
297297
// something that is not a parameter. If it cannot consume the entire given
@@ -366,6 +366,41 @@ func consumeIntegerOrDecimal(s string) (consumed, rest string, ok bool) {
366366
return s[:i], s[i:], true
367367
}
368368

369+
// ParseInteger parses an integer from a given HTTP Structured Field Values.
370+
//
371+
// The entire HTTP SFV string must consist of a valid integer. It returns the
372+
// parsed integer and an ok boolean value, indicating success or not.
373+
//
374+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
375+
func ParseInteger(s string) (parsed int64, ok bool) {
376+
if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
377+
return 0, false
378+
}
379+
if n, err := strconv.ParseInt(s, 10, 64); err == nil {
380+
return n, true
381+
}
382+
return 0, false
383+
}
384+
385+
// ParseDecimal parses a decimal from a given HTTP Structured Field Values.
386+
//
387+
// The entire HTTP SFV string must consist of a valid decimal. It returns the
388+
// parsed decimal and an ok boolean value, indicating success or not.
389+
//
390+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
391+
func ParseDecimal(s string) (parsed float64, ok bool) {
392+
if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
393+
return 0, false
394+
}
395+
if !strings.Contains(s, ".") {
396+
return 0, false
397+
}
398+
if n, err := strconv.ParseFloat(s, 64); err == nil {
399+
return n, true
400+
}
401+
return 0, false
402+
}
403+
369404
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
370405
func consumeString(s string) (consumed, rest string, ok bool) {
371406
if len(s) == 0 || s[0] != '"' {
@@ -392,6 +427,19 @@ func consumeString(s string) (consumed, rest string, ok bool) {
392427
return "", s, false
393428
}
394429

430+
// ParseString parses a Go string from a given HTTP Structured Field Values.
431+
//
432+
// The entire HTTP SFV string must consist of a valid string. It returns the
433+
// parsed string and an ok boolean value, indicating success or not.
434+
//
435+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
436+
func ParseString(s string) (parsed string, ok bool) {
437+
if _, rest, ok := consumeString(s); !ok || rest != "" {
438+
return "", false
439+
}
440+
return s[1 : len(s)-1], true
441+
}
442+
395443
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
396444
func consumeToken(s string) (consumed, rest string, ok bool) {
397445
if len(s) == 0 || (!isAlpha(s[0]) && s[0] != '*') {
@@ -407,6 +455,19 @@ func consumeToken(s string) (consumed, rest string, ok bool) {
407455
return s[:i], s[i:], true
408456
}
409457

458+
// ParseToken parses a token from a given HTTP Structured Field Values.
459+
//
460+
// The entire HTTP SFV string must consist of a valid token. It returns the
461+
// parsed token and an ok boolean value, indicating success or not.
462+
//
463+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
464+
func ParseToken(s string) (parsed string, ok bool) {
465+
if _, rest, ok := consumeToken(s); !ok || rest != "" {
466+
return "", false
467+
}
468+
return s, true
469+
}
470+
410471
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
411472
func consumeByteSequence(s string) (consumed, rest string, ok bool) {
412473
if len(s) == 0 || s[0] != ':' {
@@ -423,6 +484,20 @@ func consumeByteSequence(s string) (consumed, rest string, ok bool) {
423484
return "", s, false
424485
}
425486

487+
// ParseByteSequence parses a byte sequence from a given HTTP Structured Field
488+
// Values.
489+
//
490+
// The entire HTTP SFV string must consist of a valid byte sequence. It returns
491+
// the parsed byte sequence and an ok boolean value, indicating success or not.
492+
//
493+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
494+
func ParseByteSequence(s string) (parsed []byte, ok bool) {
495+
if _, rest, ok := consumeByteSequence(s); !ok || rest != "" {
496+
return nil, false
497+
}
498+
return []byte(s[1 : len(s)-1]), true
499+
}
500+
426501
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
427502
func consumeBoolean(s string) (consumed, rest string, ok bool) {
428503
if len(s) >= 2 && (s[:2] == "?0" || s[:2] == "?1") {
@@ -431,6 +506,19 @@ func consumeBoolean(s string) (consumed, rest string, ok bool) {
431506
return "", s, false
432507
}
433508

509+
// ParseBoolean parses a boolean from a given HTTP Structured Field Values.
510+
//
511+
// The entire HTTP SFV string must consist of a valid boolean. It returns the
512+
// parsed boolean and an ok boolean value, indicating success or not.
513+
//
514+
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
515+
func ParseBoolean(s string) (parsed bool, ok bool) {
516+
if _, rest, ok := consumeBoolean(s); !ok || rest != "" {
517+
return false, false
518+
}
519+
return s == "?1", true
520+
}
521+
434522
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
435523
func consumeDate(s string) (consumed, rest string, ok bool) {
436524
if len(s) == 0 || s[0] != '@' {

0 commit comments

Comments
 (0)