aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Erik Dubbelboer <erik@dubbelboer.com> 2024-02-11 14:55:31 +0800
committerGravatar GitHub <noreply@github.com> 2024-02-11 07:55:31 +0100
commit332726634240b82456ce8563cd7aa4027612ce36 (patch)
tree64a7b1862d9d6089e7521a7a2c59f8ea19436fe0
parentBump dependencies (#1718) (diff)
downloadfasthttp-332726634240b82456ce8563cd7aa4027612ce36.tar.gz
fasthttp-332726634240b82456ce8563cd7aa4027612ce36.tar.bz2
fasthttp-332726634240b82456ce8563cd7aa4027612ce36.zip
Follow RFCs 7230 and 9112 for HTTP versions (#1710)
Require that HTTP versions match the following pattern: HTTP/[0-9]\.[0-9]
-rw-r--r--header.go36
-rw-r--r--header_test.go14
-rw-r--r--http_test.go15
-rw-r--r--strings.go1
4 files changed, 34 insertions, 32 deletions
diff --git a/header.go b/header.go
index ac279d7..bdee768 100644
--- a/header.go
+++ b/header.go
@@ -2870,24 +2870,40 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
h.method = append(h.method[:0], b[:n]...)
b = b[n+1:]
- protoStr := strHTTP11
// parse requestURI
n = bytes.LastIndexByte(b, ' ')
- switch {
- case n < 0:
- h.noHTTP11 = true
- n = len(b)
- protoStr = strHTTP10
- case n == 0:
+ if n < 0 {
+ return 0, fmt.Errorf("cannot find whitespace in the first line of request %q", buf)
+ } else if n == 0 {
if h.secureErrorLogMessage {
return 0, fmt.Errorf("requestURI cannot be empty")
}
return 0, fmt.Errorf("requestURI cannot be empty in %q", buf)
- case !bytes.Equal(b[n+1:], strHTTP11):
- h.noHTTP11 = true
- protoStr = b[n+1:]
}
+ protoStr := b[n+1:]
+
+ // Follow RFCs 7230 and 9112 and require that HTTP versions match the following pattern: HTTP/[0-9]\.[0-9]
+ if len(protoStr) != len(strHTTP11) {
+ if h.secureErrorLogMessage {
+ return 0, fmt.Errorf("unsupported HTTP version %q", protoStr)
+ }
+ return 0, fmt.Errorf("unsupported HTTP version %q in %q", protoStr, buf)
+ }
+ if !bytes.HasPrefix(protoStr, strHTTP11[:5]) {
+ if h.secureErrorLogMessage {
+ return 0, fmt.Errorf("unsupported HTTP version %q", protoStr)
+ }
+ return 0, fmt.Errorf("unsupported HTTP version %q in %q", protoStr, buf)
+ }
+ if protoStr[5] < '0' || protoStr[5] > '9' || protoStr[7] < '0' || protoStr[7] > '9' {
+ if h.secureErrorLogMessage {
+ return 0, fmt.Errorf("unsupported HTTP version %q", protoStr)
+ }
+ return 0, fmt.Errorf("unsupported HTTP version %q in %q", protoStr, buf)
+ }
+
+ h.noHTTP11 = !bytes.Equal(protoStr, strHTTP11)
h.proto = append(h.proto[:0], protoStr...)
h.requestURI = append(h.requestURI[:0], b[:n]...)
diff --git a/header_test.go b/header_test.go
index 163fdd7..c0f98dc 100644
--- a/header_test.go
+++ b/header_test.go
@@ -1341,11 +1341,6 @@ func TestRequestHeaderHTTPVer(t *testing.T) {
// non-http/1.1
testRequestHeaderHTTPVer(t, "GET / HTTP/1.0\r\nHost: aa.com\r\n\r\n", true)
testRequestHeaderHTTPVer(t, "GET / HTTP/0.9\r\nHost: aa.com\r\n\r\n", true)
- testRequestHeaderHTTPVer(t, "GET / foobar\r\nHost: aa.com\r\n\r\n", true)
-
- // empty http version
- testRequestHeaderHTTPVer(t, "GET /\r\nHost: aaa.com\r\n\r\n", true)
- testRequestHeaderHTTPVer(t, "GET / \r\nHost: aaa.com\r\n\r\n", true)
// http/1.1
testRequestHeaderHTTPVer(t, "GET / HTTP/1.1\r\nHost: a.com\r\n\r\n", false)
@@ -1365,6 +1360,8 @@ func testResponseHeaderHTTPVer(t *testing.T, s string, connectionClose bool) {
}
func testRequestHeaderHTTPVer(t *testing.T, s string, connectionClose bool) {
+ t.Helper()
+
var h RequestHeader
r := bytes.NewBufferString(s)
@@ -2641,10 +2638,6 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
testRequestHeaderReadSuccess(t, h, "GET http://gooGle.com/foO/%20bar?xxx#aaa HTTP/1.1\r\nHost: aa.cOM\r\n\r\ntrail",
-2, "http://gooGle.com/foO/%20bar?xxx#aaa", "aa.cOM", "", "", nil)
- // no protocol in the first line
- testRequestHeaderReadSuccess(t, h, "GET /foo/bar\r\nHost: google.com\r\n\r\nisdD",
- -2, "/foo/bar", "google.com", "", "", nil)
-
// blank lines before the first line
testRequestHeaderReadSuccess(t, h, "\r\n\n\r\nGET /aaa HTTP/1.1\r\nHost: aaa.com\r\n\r\nsss",
-2, "/aaa", "aaa.com", "", "", nil)
@@ -2713,6 +2706,9 @@ func TestResponseHeaderReadError(t *testing.T) {
// forbidden trailer
testResponseHeaderReadError(t, h, "HTTP/1.1 200 OK\r\nContent-Length: -1\r\nTrailer: Foo, Content-Length\r\n\r\n")
+
+ // no protocol in the first line
+ testResponseHeaderReadError(t, h, "GET /foo/bar\r\nHost: google.com\r\n\r\nisdD")
}
func TestResponseHeaderReadErrorSecureLog(t *testing.T) {
diff --git a/http_test.go b/http_test.go
index 098990a..a82f23e 100644
--- a/http_test.go
+++ b/http_test.go
@@ -3,7 +3,6 @@ package fasthttp
import (
"bufio"
"bytes"
- "encoding/base64"
"errors"
"fmt"
"io"
@@ -22,23 +21,15 @@ import (
func TestInvalidTrailers(t *testing.T) {
t.Parallel()
- if err := (&Response{}).Read(bufio.NewReader(bytes.NewReader([]byte{0x20, 0x30, 0x0a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x3a, 0xff, 0x0a, 0x0a, 0x30, 0x0d, 0x0a, 0x30}))); !errors.Is(err, io.EOF) {
+ if err := (&Response{}).Read(bufio.NewReader(strings.NewReader(" 0\nTransfer-Encoding:\xff\n\n0\r\n0"))); !errors.Is(err, io.EOF) {
t.Fatalf("%#v", err)
}
- if err := (&Response{}).Read(bufio.NewReader(bytes.NewReader([]byte{0xff, 0x20, 0x0a, 0x54, 0x52, 0x61, 0x49, 0x4c, 0x65, 0x52, 0x3a, 0x2c, 0x0a, 0x0a}))); !errors.Is(err, errEmptyInt) {
+ if err := (&Response{}).Read(bufio.NewReader(strings.NewReader("\xff \nTRaILeR:,\n\n"))); !errors.Is(err, errEmptyInt) {
t.Fatal(err)
}
- if err := (&Response{}).Read(bufio.NewReader(bytes.NewReader([]byte{0x54, 0x52, 0x61, 0x49, 0x4c, 0x65, 0x52, 0x3a, 0x2c, 0x0a, 0x0a}))); !strings.Contains(err.Error(), "cannot find whitespace in the first line of response") {
+ if err := (&Response{}).Read(bufio.NewReader(strings.NewReader("TRaILeR:,\n\n"))); !strings.Contains(err.Error(), "cannot find whitespace in the first line of response") {
t.Fatal(err)
}
- if err := (&Request{}).Read(bufio.NewReader(bytes.NewReader([]byte{0xff, 0x20, 0x0a, 0x54, 0x52, 0x61, 0x49, 0x4c, 0x65, 0x52, 0x3a, 0x2c, 0x0a, 0x0a}))); !strings.Contains(err.Error(), "contain forbidden trailer") {
- t.Fatal(err)
- }
-
- b, _ := base64.StdEncoding.DecodeString("tCAKIDoKCToKICAKCToKICAKCToKIAogOgoJOgogIAoJOgovIC8vOi4KOh0KVFJhSUxlUjo9HT09HQpUUmFJTGVSOicQAApUUmFJTGVSOj0gHSAKCT09HQoKOgoKCgo=")
- if err := (&Request{}).Read(bufio.NewReader(bytes.NewReader(b))); !strings.Contains(err.Error(), "error when reading request headers: invalid header key") {
- t.Fatalf("%#v", err)
- }
}
func TestResponseEmptyTransferEncoding(t *testing.T) {
diff --git a/strings.go b/strings.go
index 3cec8ed..3374678 100644
--- a/strings.go
+++ b/strings.go
@@ -19,7 +19,6 @@ var (
strCRLF = []byte("\r\n")
strHTTP = []byte("http")
strHTTPS = []byte("https")
- strHTTP10 = []byte("HTTP/1.0")
strHTTP11 = []byte("HTTP/1.1")
strColon = []byte(":")
strColonSlashSlash = []byte("://")