mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-13 15:46:49 +03:00
Validate request method
Use the same validation as net/http. Fixes https://github.com/valyala/fasthttp/issues/1803
This commit is contained in:
@@ -10,3 +10,4 @@ const quotedArgShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01
|
|||||||
const quotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
const quotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
||||||
const validHeaderFieldByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00"
|
const validHeaderFieldByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00"
|
||||||
const validHeaderValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
const validHeaderValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
|
||||||
|
const validMethodValueByteTable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
|||||||
@@ -146,6 +146,101 @@ func main() {
|
|||||||
return table
|
return table
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
validMethodValueByteTable := [256]byte{
|
||||||
|
/*
|
||||||
|
Same as net/http
|
||||||
|
|
||||||
|
Method = "OPTIONS" ; Section 9.2
|
||||||
|
| "GET" ; Section 9.3
|
||||||
|
| "HEAD" ; Section 9.4
|
||||||
|
| "POST" ; Section 9.5
|
||||||
|
| "PUT" ; Section 9.6
|
||||||
|
| "DELETE" ; Section 9.7
|
||||||
|
| "TRACE" ; Section 9.8
|
||||||
|
| "CONNECT" ; Section 9.9
|
||||||
|
| extension-method
|
||||||
|
extension-method = token
|
||||||
|
token = 1*<any CHAR except CTLs or separators>
|
||||||
|
*/
|
||||||
|
'!': 1,
|
||||||
|
'#': 1,
|
||||||
|
'$': 1,
|
||||||
|
'%': 1,
|
||||||
|
'&': 1,
|
||||||
|
'\'': 1,
|
||||||
|
'*': 1,
|
||||||
|
'+': 1,
|
||||||
|
'-': 1,
|
||||||
|
'.': 1,
|
||||||
|
'0': 1,
|
||||||
|
'1': 1,
|
||||||
|
'2': 1,
|
||||||
|
'3': 1,
|
||||||
|
'4': 1,
|
||||||
|
'5': 1,
|
||||||
|
'6': 1,
|
||||||
|
'7': 1,
|
||||||
|
'8': 1,
|
||||||
|
'9': 1,
|
||||||
|
'A': 1,
|
||||||
|
'B': 1,
|
||||||
|
'C': 1,
|
||||||
|
'D': 1,
|
||||||
|
'E': 1,
|
||||||
|
'F': 1,
|
||||||
|
'G': 1,
|
||||||
|
'H': 1,
|
||||||
|
'I': 1,
|
||||||
|
'J': 1,
|
||||||
|
'K': 1,
|
||||||
|
'L': 1,
|
||||||
|
'M': 1,
|
||||||
|
'N': 1,
|
||||||
|
'O': 1,
|
||||||
|
'P': 1,
|
||||||
|
'Q': 1,
|
||||||
|
'R': 1,
|
||||||
|
'S': 1,
|
||||||
|
'T': 1,
|
||||||
|
'U': 1,
|
||||||
|
'W': 1,
|
||||||
|
'V': 1,
|
||||||
|
'X': 1,
|
||||||
|
'Y': 1,
|
||||||
|
'Z': 1,
|
||||||
|
'^': 1,
|
||||||
|
'_': 1,
|
||||||
|
'`': 1,
|
||||||
|
'a': 1,
|
||||||
|
'b': 1,
|
||||||
|
'c': 1,
|
||||||
|
'd': 1,
|
||||||
|
'e': 1,
|
||||||
|
'f': 1,
|
||||||
|
'g': 1,
|
||||||
|
'h': 1,
|
||||||
|
'i': 1,
|
||||||
|
'j': 1,
|
||||||
|
'k': 1,
|
||||||
|
'l': 1,
|
||||||
|
'm': 1,
|
||||||
|
'n': 1,
|
||||||
|
'o': 1,
|
||||||
|
'p': 1,
|
||||||
|
'q': 1,
|
||||||
|
'r': 1,
|
||||||
|
's': 1,
|
||||||
|
't': 1,
|
||||||
|
'u': 1,
|
||||||
|
'v': 1,
|
||||||
|
'w': 1,
|
||||||
|
'x': 1,
|
||||||
|
'y': 1,
|
||||||
|
'z': 1,
|
||||||
|
'|': 1,
|
||||||
|
'~': 1,
|
||||||
|
}
|
||||||
|
|
||||||
w := bytes.NewBufferString(pre)
|
w := bytes.NewBufferString(pre)
|
||||||
fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable)
|
fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable)
|
||||||
fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable)
|
fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable)
|
||||||
@@ -154,6 +249,7 @@ func main() {
|
|||||||
fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
|
fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
|
||||||
fmt.Fprintf(w, "const validHeaderFieldByteTable = %q\n", validHeaderFieldByteTable)
|
fmt.Fprintf(w, "const validHeaderFieldByteTable = %q\n", validHeaderFieldByteTable)
|
||||||
fmt.Fprintf(w, "const validHeaderValueByteTable = %q\n", validHeaderValueByteTable)
|
fmt.Fprintf(w, "const validHeaderValueByteTable = %q\n", validHeaderValueByteTable)
|
||||||
|
fmt.Fprintf(w, "const validMethodValueByteTable = %q\n", validMethodValueByteTable)
|
||||||
|
|
||||||
if err := os.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
|
if err := os.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|||||||
@@ -2855,6 +2855,15 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
|
|||||||
return len(buf) - len(bNext), nil
|
return len(buf) - len(bNext), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidMethod(method []byte) bool {
|
||||||
|
for _, ch := range method {
|
||||||
|
if validMethodValueByteTable[ch] == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
|
func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
|
||||||
bNext := buf
|
bNext := buf
|
||||||
var b []byte
|
var b []byte
|
||||||
@@ -2874,6 +2883,14 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
|
|||||||
return 0, fmt.Errorf("cannot find http request method in %q", buf)
|
return 0, fmt.Errorf("cannot find http request method in %q", buf)
|
||||||
}
|
}
|
||||||
h.method = append(h.method[:0], b[:n]...)
|
h.method = append(h.method[:0], b[:n]...)
|
||||||
|
|
||||||
|
if !isValidMethod(h.method) {
|
||||||
|
if h.secureErrorLogMessage {
|
||||||
|
return 0, errors.New("unsupported http request method")
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("unsupported http request method %q in %q", h.method, buf)
|
||||||
|
}
|
||||||
|
|
||||||
b = b[n+1:]
|
b = b[n+1:]
|
||||||
|
|
||||||
// parse requestURI
|
// parse requestURI
|
||||||
|
|||||||
+3
-4
@@ -2668,10 +2668,6 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
|
|||||||
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456",
|
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456",
|
||||||
-2, "/abc", "aa.com", "", "adv")
|
-2, "/abc", "aa.com", "", "adv")
|
||||||
|
|
||||||
// invalid method
|
|
||||||
testRequestHeaderReadSuccess(t, h, "POST /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\nmnbv",
|
|
||||||
-2, "/foo/bar", "google.com", "", "")
|
|
||||||
|
|
||||||
// put request
|
// put request
|
||||||
testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere",
|
testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere",
|
||||||
123, "/faa", "aaa.com", "", "aaa")
|
123, "/faa", "aaa.com", "", "aaa")
|
||||||
@@ -2767,6 +2763,9 @@ func TestRequestHeaderReadError(t *testing.T) {
|
|||||||
|
|
||||||
// Zero-length header
|
// Zero-length header
|
||||||
testRequestHeaderReadError(t, h, "GET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
|
testRequestHeaderReadError(t, h, "GET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
|
||||||
|
|
||||||
|
// Invalid method
|
||||||
|
testRequestHeaderReadError(t, h, "G(ET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequestHeaderReadSecuredError(t *testing.T) {
|
func TestRequestHeaderReadSecuredError(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user