|
|
@@ -17,6 +17,7 @@ package http2
|
|
|
|
|
|
import (
|
|
|
"bufio"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"net/http"
|
|
|
@@ -158,17 +159,60 @@ func (s SettingID) String() string {
|
|
|
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
|
|
}
|
|
|
|
|
|
-func validHeader(v string) bool {
|
|
|
+var (
|
|
|
+ errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
|
|
+ errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
|
|
+)
|
|
|
+
|
|
|
+// validHeaderFieldName reports whether v is a valid header field name (key).
|
|
|
+// RFC 7230 says:
|
|
|
+// header-field = field-name ":" OWS field-value OWS
|
|
|
+// field-name = token
|
|
|
+// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
|
|
+// "^" / "_" / "
|
|
|
+// Further, http2 says:
|
|
|
+// "Just as in HTTP/1.x, header field names are strings of ASCII
|
|
|
+// characters that are compared in a case-insensitive
|
|
|
+// fashion. However, header field names MUST be converted to
|
|
|
+// lowercase prior to their encoding in HTTP/2. "
|
|
|
+func validHeaderFieldName(v string) bool {
|
|
|
if len(v) == 0 {
|
|
|
return false
|
|
|
}
|
|
|
for _, r := range v {
|
|
|
- // "Just as in HTTP/1.x, header field names are
|
|
|
- // strings of ASCII characters that are compared in a
|
|
|
- // case-insensitive fashion. However, header field
|
|
|
- // names MUST be converted to lowercase prior to their
|
|
|
- // encoding in HTTP/2. "
|
|
|
- if r >= 127 || ('A' <= r && r <= 'Z') {
|
|
|
+ if int(r) >= len(isTokenTable) || ('A' <= r && r <= 'Z') {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if !isTokenTable[byte(r)] {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// validHeaderFieldValue reports whether v is a valid header field value.
|
|
|
+//
|
|
|
+// RFC 7230 says:
|
|
|
+// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
|
+// field-vchar = VCHAR / obs-text
|
|
|
+// obs-text = %x80-FF
|
|
|
+// VCHAR = "any visible [USASCII] character"
|
|
|
+//
|
|
|
+// http2 further says: "Similarly, HTTP/2 allows header field values
|
|
|
+// that are not valid. While most of the values that can be encoded
|
|
|
+// will not alter header field parsing, carriage return (CR, ASCII
|
|
|
+// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII
|
|
|
+// 0x0) might be exploited by an attacker if they are translated
|
|
|
+// verbatim. Any request or response that contains a character not
|
|
|
+// permitted in a header field value MUST be treated as malformed
|
|
|
+// (Section 8.1.2.6). Valid characters are defined by the
|
|
|
+// field-content ABNF rule in Section 3.2 of [RFC7230]."
|
|
|
+//
|
|
|
+// This function does not (yet?) properly handle the rejection of
|
|
|
+// strings that begin or end with SP or HTAB.
|
|
|
+func validHeaderFieldValue(v string) bool {
|
|
|
+ for i := 0; i < len(v); i++ {
|
|
|
+ if b := v[i]; b < ' ' && b != '\t' {
|
|
|
return false
|
|
|
}
|
|
|
}
|
|
|
@@ -296,3 +340,83 @@ func (e *httpError) Timeout() bool { return e.timeout }
|
|
|
func (e *httpError) Temporary() bool { return true }
|
|
|
|
|
|
var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
|
|
|
+
|
|
|
+var isTokenTable = [127]bool{
|
|
|
+ '!': true,
|
|
|
+ '#': true,
|
|
|
+ '$': true,
|
|
|
+ '%': true,
|
|
|
+ '&': true,
|
|
|
+ '\'': true,
|
|
|
+ '*': true,
|
|
|
+ '+': true,
|
|
|
+ '-': true,
|
|
|
+ '.': true,
|
|
|
+ '0': true,
|
|
|
+ '1': true,
|
|
|
+ '2': true,
|
|
|
+ '3': true,
|
|
|
+ '4': true,
|
|
|
+ '5': true,
|
|
|
+ '6': true,
|
|
|
+ '7': true,
|
|
|
+ '8': true,
|
|
|
+ '9': true,
|
|
|
+ 'A': true,
|
|
|
+ 'B': true,
|
|
|
+ 'C': true,
|
|
|
+ 'D': true,
|
|
|
+ 'E': true,
|
|
|
+ 'F': true,
|
|
|
+ 'G': true,
|
|
|
+ 'H': true,
|
|
|
+ 'I': true,
|
|
|
+ 'J': true,
|
|
|
+ 'K': true,
|
|
|
+ 'L': true,
|
|
|
+ 'M': true,
|
|
|
+ 'N': true,
|
|
|
+ 'O': true,
|
|
|
+ 'P': true,
|
|
|
+ 'Q': true,
|
|
|
+ 'R': true,
|
|
|
+ 'S': true,
|
|
|
+ 'T': true,
|
|
|
+ 'U': true,
|
|
|
+ 'W': true,
|
|
|
+ 'V': true,
|
|
|
+ 'X': true,
|
|
|
+ 'Y': true,
|
|
|
+ 'Z': true,
|
|
|
+ '^': true,
|
|
|
+ '_': true,
|
|
|
+ '`': true,
|
|
|
+ 'a': true,
|
|
|
+ 'b': true,
|
|
|
+ 'c': true,
|
|
|
+ 'd': true,
|
|
|
+ 'e': true,
|
|
|
+ 'f': true,
|
|
|
+ 'g': true,
|
|
|
+ 'h': true,
|
|
|
+ 'i': true,
|
|
|
+ 'j': true,
|
|
|
+ 'k': true,
|
|
|
+ 'l': true,
|
|
|
+ 'm': true,
|
|
|
+ 'n': true,
|
|
|
+ 'o': true,
|
|
|
+ 'p': true,
|
|
|
+ 'q': true,
|
|
|
+ 'r': true,
|
|
|
+ 's': true,
|
|
|
+ 't': true,
|
|
|
+ 'u': true,
|
|
|
+ 'v': true,
|
|
|
+ 'w': true,
|
|
|
+ 'x': true,
|
|
|
+ 'y': true,
|
|
|
+ 'z': true,
|
|
|
+ '|': true,
|
|
|
+ '~': true,
|
|
|
+}
|