-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prevent Request headers canonicalization (#607)
* Add header extraction with tests * Add prevent canonicalization implementation * Update documentation * Fix linter issues * Remove redundant body read * Avoid to duplicate read data if prevent canonicalization is set to false * Copy https mitm fixes to http mitm * Prevent response body memory leak by making sure to close it when we are done * Add additional request parsing tests * Rely on stdlib header parsing implementation to extract header names
- Loading branch information
Showing
10 changed files
with
508 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,14 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= | ||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= | ||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | ||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package http1parser | ||
|
||
import ( | ||
"errors" | ||
"net/textproto" | ||
"strings" | ||
) | ||
|
||
var ErrBadProto = errors.New("bad protocol") | ||
|
||
// Http1ExtractHeaders is an HTTP/1.0 and HTTP/1.1 header-only parser, | ||
// to extract the original header names for the received request. | ||
// Fully inspired by readMIMEHeader() in | ||
// https://github.com/golang/go/blob/master/src/net/textproto/reader.go | ||
func Http1ExtractHeaders(r *textproto.Reader) ([]string, error) { | ||
// Discard first line, it doesn't contain useful information, and it has | ||
// already been validated in http.ReadRequest() | ||
if _, err := r.ReadLine(); err != nil { | ||
return nil, err | ||
} | ||
|
||
// The first line cannot start with a leading space. | ||
if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { | ||
return nil, ErrBadProto | ||
} | ||
|
||
var headerNames []string | ||
for { | ||
kv, err := r.ReadContinuedLine() | ||
if len(kv) == 0 { | ||
// We have finished to parse the headers if we receive empty | ||
// data without an error | ||
return headerNames, err | ||
} | ||
|
||
// Key ends at first colon. | ||
k, _, ok := strings.Cut(kv, ":") | ||
if !ok { | ||
return nil, ErrBadProto | ||
} | ||
headerNames = append(headerNames, k) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package http1parser_test | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"net/textproto" | ||
"testing" | ||
|
||
"github.com/elazarl/goproxy/internal/http1parser" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestHttp1ExtractHeaders_Empty(t *testing.T) { | ||
http1Data := "POST /index.html HTTP/1.1\r\n" + | ||
"\r\n" | ||
|
||
textParser := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(http1Data)))) | ||
headers, err := http1parser.Http1ExtractHeaders(textParser) | ||
require.NoError(t, err) | ||
assert.Empty(t, headers) | ||
} | ||
|
||
func TestHttp1ExtractHeaders(t *testing.T) { | ||
http1Data := "POST /index.html HTTP/1.1\r\n" + | ||
"Host: www.test.com\r\n" + | ||
"Accept: */ /*\r\n" + | ||
"Content-Length: 17\r\n" + | ||
"lowercase: 3z\r\n" + | ||
"\r\n" + | ||
`{"hello":"world"}` | ||
|
||
textParser := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(http1Data)))) | ||
headers, err := http1parser.Http1ExtractHeaders(textParser) | ||
require.NoError(t, err) | ||
assert.Len(t, headers, 4) | ||
assert.Contains(t, headers, "Content-Length") | ||
assert.Contains(t, headers, "lowercase") | ||
} | ||
|
||
func TestHttp1ExtractHeaders_InvalidData(t *testing.T) { | ||
http1Data := "POST /index.html HTTP/1.1\r\n" + | ||
`{"hello":"world"}` | ||
|
||
textParser := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(http1Data)))) | ||
_, err := http1parser.Http1ExtractHeaders(textParser) | ||
require.Error(t, err) | ||
} |
Oops, something went wrong.