mirror of
https://github.com/buger/goreplay.git
synced 2024-04-21 12:32:02 +00:00
Allow all HTTP methods to have body (#479)
HTTP specification in section 4.3 (Message Body) states: "A message-body MUST NOT be included in a request if the specification of the request method (section 5.1.1)" Section 5.1.1 has links to the description of each method (sections 9.2 to 9.9). None of those sections prohibit the transfer of a message body, it just points out how each method should be treated server-side.
This commit is contained in:
committed by
Leonid Bugaev
parent
17f6c59061
commit
d309650589
@@ -239,31 +239,26 @@ func (t *TCPMessage) checkIfComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
// If one GET, OPTIONS, or HEAD request
|
||||
if t.methodType == httpMethodWithoutBody {
|
||||
switch t.bodyType {
|
||||
case httpBodyEmpty:
|
||||
t.complete = true
|
||||
} else {
|
||||
switch t.bodyType {
|
||||
case httpBodyEmpty:
|
||||
case httpBodyContentLength:
|
||||
if t.contentLength == 0 || t.contentLength == t.BodySize() {
|
||||
t.complete = true
|
||||
case httpBodyContentLength:
|
||||
if t.contentLength == 0 || t.contentLength == t.BodySize() {
|
||||
t.complete = true
|
||||
}
|
||||
case httpBodyChunked:
|
||||
lastPacket := t.packets[len(t.packets)-1]
|
||||
if bytes.LastIndex(lastPacket.Data, bChunkEnd) != -1 {
|
||||
t.complete = true
|
||||
}
|
||||
default:
|
||||
if len(t.packets) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
case httpBodyChunked:
|
||||
lastPacket := t.packets[len(t.packets)-1]
|
||||
if bytes.LastIndex(lastPacket.Data, bChunkEnd) != -1 {
|
||||
t.complete = true
|
||||
}
|
||||
default:
|
||||
if len(t.packets) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
last := t.packets[len(t.packets)-1]
|
||||
if last.IsFIN {
|
||||
t.complete = true
|
||||
}
|
||||
last := t.packets[len(t.packets)-1]
|
||||
if last.IsFIN {
|
||||
t.complete = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,19 +266,11 @@ func (t *TCPMessage) checkIfComplete() {
|
||||
type httpMethodType uint8
|
||||
|
||||
const (
|
||||
httpMethodNotSet httpMethodType = 0
|
||||
httpMethodWithBody httpMethodType = 1
|
||||
httpMethodWithoutBody httpMethodType = 2
|
||||
httpMethodNotFound httpMethodType = 3
|
||||
httpMethodNotSet httpMethodType = 0
|
||||
httpMethodKnown httpMethodType = 1
|
||||
httpMethodNotFound httpMethodType = 2
|
||||
)
|
||||
|
||||
var methodsWithBody = [][]byte{
|
||||
[]byte("POST"),
|
||||
[]byte("PUT"),
|
||||
[]byte("PATCH"),
|
||||
[]byte("CONNECT"),
|
||||
}
|
||||
|
||||
func (t *TCPMessage) updateMethodType() {
|
||||
// if there is cache
|
||||
if t.methodType != httpMethodNotSet && t.methodType != httpMethodNotFound {
|
||||
@@ -300,11 +287,7 @@ func (t *TCPMessage) updateMethodType() {
|
||||
}
|
||||
|
||||
if t.IsIncoming {
|
||||
var method []byte
|
||||
|
||||
if mIdx := bytes.IndexByte(d[:8], ' '); mIdx != -1 {
|
||||
method = d[:mIdx]
|
||||
|
||||
// Check that after method we have absolute or relative path
|
||||
switch d[mIdx+1] {
|
||||
case '/', 'h', '*':
|
||||
@@ -317,21 +300,14 @@ func (t *TCPMessage) updateMethodType() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, m := range methodsWithBody {
|
||||
if len(m) == len(method) && bytes.Equal(m, method) {
|
||||
t.methodType = httpMethodWithBody
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.methodType = httpMethodWithoutBody
|
||||
t.methodType = httpMethodKnown
|
||||
} else {
|
||||
if !bytes.Equal(d[:6], []byte("HTTP/1")) {
|
||||
t.methodType = httpMethodNotFound
|
||||
return
|
||||
}
|
||||
|
||||
t.methodType = httpMethodWithBody
|
||||
t.methodType = httpMethodKnown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,10 +355,7 @@ func (t *TCPMessage) updateBodyType() {
|
||||
switch t.methodType {
|
||||
case httpMethodNotFound:
|
||||
return
|
||||
case httpMethodWithoutBody:
|
||||
t.bodyType = httpBodyEmpty
|
||||
return
|
||||
case httpMethodWithBody:
|
||||
case httpMethodKnown:
|
||||
if len(lengthB) > 0 {
|
||||
t.contentLength, _ = strconv.Atoi(string(lengthB))
|
||||
|
||||
@@ -424,10 +397,6 @@ func (t *TCPMessage) check100Continue() {
|
||||
return
|
||||
}
|
||||
|
||||
if t.methodType != httpMethodWithBody {
|
||||
return
|
||||
}
|
||||
|
||||
if t.seqMissing || t.headerPacket == -1 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -170,16 +170,16 @@ func TestTCPMessageMethodType(t *testing.T) {
|
||||
payload string
|
||||
expectedMethodType httpMethodType
|
||||
}{
|
||||
{true, "GET / HTTP/1.1\r\n\r\n", httpMethodWithoutBody},
|
||||
{true, "GET * HTTP/1.1\r\n\r\n", httpMethodWithoutBody},
|
||||
{true, "UNKNOWN / HTTP/1.1\r\n\r\n", httpMethodWithoutBody},
|
||||
{true, "GET http://example.com HTTP/1.1\r\n\r\n", httpMethodWithoutBody},
|
||||
{true, "POST / HTTP/1.1\r\n\r\n", httpMethodWithBody},
|
||||
{true, "PUT / HTTP/1.1\r\n\r\n", httpMethodWithBody},
|
||||
{true, "GET / HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "GET * HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "UNKNOWN / HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "GET http://example.com HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "POST / HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "PUT / HTTP/1.1\r\n\r\n", httpMethodKnown},
|
||||
{true, "GET zxc HTTP/1.1\r\n\r\n", httpMethodNotFound},
|
||||
{true, "GET / HTTP\r\n\r\n", httpMethodNotFound},
|
||||
{true, "VERYLONGMETHOD / HTTP/1.1\r\n\r\n", httpMethodNotFound},
|
||||
{false, "HTTP/1.1 200 OK\r\n\r\n", httpMethodWithBody},
|
||||
{false, "HTTP/1.1 200 OK\r\n\r\n", httpMethodKnown},
|
||||
{false, "HTTP /1.1 200 OK\r\n\r\n", httpMethodNotFound},
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ func TestTCPMessageBodyType(t *testing.T) {
|
||||
expectedBodyType httpBodyType
|
||||
}{
|
||||
{true, "GET / HTTP/1.1\r\n\r\n", httpBodyEmpty},
|
||||
{true, "GET / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab", httpBodyContentLength},
|
||||
{true, "POST / HTTP/1.1\r\n\r\n", httpBodyEmpty},
|
||||
{true, "POST / HTTP/1.1\r\nUser-Agent: zxc\r\n\r\n", httpBodyEmpty},
|
||||
{false, "HTTP/1.1 200 OK\r\n\r\n", httpBodyEmpty},
|
||||
|
||||
Reference in New Issue
Block a user