From d309650589a56db2bfa93c533337e3db1cfb0425 Mon Sep 17 00:00:00 2001 From: Aggelos Avgerinos Date: Tue, 22 Aug 2017 17:28:21 +0300 Subject: [PATCH] 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. --- raw_socket_listener/tcp_message.go | 77 ++++++++----------------- raw_socket_listener/tcp_message_test.go | 15 ++--- 2 files changed, 31 insertions(+), 61 deletions(-) diff --git a/raw_socket_listener/tcp_message.go b/raw_socket_listener/tcp_message.go index dbda8b5..02f8384 100644 --- a/raw_socket_listener/tcp_message.go +++ b/raw_socket_listener/tcp_message.go @@ -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 } diff --git a/raw_socket_listener/tcp_message_test.go b/raw_socket_listener/tcp_message_test.go index 92f61ae..e80156d 100644 --- a/raw_socket_listener/tcp_message_test.go +++ b/raw_socket_listener/tcp_message_test.go @@ -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},