Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly handle requests with headers consisting of multiple packets #281

Merged
merged 1 commit into from May 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 55 additions & 6 deletions raw_socket_listener/tcp_message.go
Expand Up @@ -122,6 +122,40 @@ func (t *TCPMessage) AddPacket(packet *TCPPacket) {
}
}

// Check if there is missing packet
func (t *TCPMessage) isSeqMissing() bool {
if len(t.packets) == 1 {
return false
}

for i, p := range t.packets {
// If final packet
if len(t.packets) == i + 1 {
return false
}
np := t.packets[i + 1]

if np.Seq != p.Seq + uint32(len(p.Data)) {
return true
}
}

return false
}

var EmptyLine = []byte("\r\n\r\n")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported var EmptyLine should have comment or be unexported

var ChunkEnd = []byte("0\r\n\r\n")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported var ChunkEnd should have comment or be unexported


func (t *TCPMessage) isHeadersReceived() bool {
for _, p := range t.packets {
if bytes.LastIndex(p.Data, EmptyLine) != -1 {
return true
}
}

return false
}

// isMultipart returns true if message contains from multiple tcp packets
func (t *TCPMessage) IsFinished() bool {
payload := t.packets[0].Data
Expand All @@ -135,16 +169,23 @@ func (t *TCPMessage) IsFinished() bool {
if t.IsIncoming {
// If one GET, OPTIONS, or HEAD request
if bytes.Equal(m, []byte("GET ")) || bytes.Equal(m, []byte("OPTI")) || bytes.Equal(m, []byte("HEAD")) {
return true
if !t.isSeqMissing() && t.isHeadersReceived() {
return true
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if block ends with a return statement, so drop this else and outdent its block

return false
}
} else {
// Sometimes header comes after the body :(
if bytes.Equal(m, []byte("POST")) || bytes.Equal(m, []byte("PUT ")) || bytes.Equal(m, []byte("PATC")) {
if length := proto.Header(payload, []byte("Content-Length")); len(length) > 0 {
l, _ := strconv.Atoi(string(length))

// If content-length equal current body length
if l > 0 && l == t.BodySize() {
return true
if t.isHeadersReceived() {
if length := proto.Header(payload, []byte("Content-Length")); len(length) > 0 {
l, _ := strconv.Atoi(string(length))

// If content-length equal current body length
if l > 0 && l == t.BodySize() {
return true
}
}
}
}
Expand All @@ -156,6 +197,10 @@ func (t *TCPMessage) IsFinished() bool {
return false
}

if !bytes.Equal(m, []byte("HTTP")) {
return false
}

if length := proto.Header(payload, []byte("Content-Length")); len(length) > 0 {
if length[0] == '0' {
return true
Expand All @@ -170,6 +215,10 @@ func (t *TCPMessage) IsFinished() bool {
} else {
if enc := proto.Header(payload, []byte("Transfer-Encoding")); len(enc) == 0 {
return true
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)

if len(t.packets) > 1 && bytes.LastIndex(t.packets[len(t.packets) - 1].Data, ChunkEnd) != -1 {
return true
}
}
}
}
Expand Down
38 changes: 37 additions & 1 deletion raw_socket_listener/tcp_message_test.go
Expand Up @@ -84,7 +84,7 @@ func TestTCPMessageIsFinished(t *testing.T) {
methodsWithoutBodies := []string{"GET", "OPTIONS", "HEAD"}

for _, m := range methodsWithoutBodies {
msg := buildMessage(buildPacket(true, 1, 1, []byte(m+" / HTTP/1.1")))
msg := buildMessage(buildPacket(true, 1, 1, []byte(m+" / HTTP/1.1\r\n\r\n")))

if !msg.IsFinished() {
t.Error(m, " request should be finished")
Expand Down Expand Up @@ -153,3 +153,39 @@ func TestTCPMessageIsFinished(t *testing.T) {
t.Error("Should not mark not valid Content-Length respones as finished")
}
}

func TestTCPMessageIsSeqMissing(t *testing.T) {
p1 := buildPacket(false, 1, 1, []byte("HTTP/1.1 200 OK\r\n"))
p2 := buildPacket(false, 1, p1.Seq + uint32(len(p1.Data)), []byte("Content-Length: 10\r\n\r\n"))
p3 := buildPacket(false, 1, p2.Seq + uint32(len(p2.Data)), []byte("a"))

msg := buildMessage(p1)
if msg.isSeqMissing() {
t.Error("Should be complete if have only 1 packet")
}

msg.AddPacket(p3)
if !msg.isSeqMissing() {
t.Error("Should be incomplete because missing middle component")
}

msg.AddPacket(p2)
if msg.isSeqMissing() {
t.Error("Should be complete once missing packet added")
}
}

func TestTCPMessageIsHeadersReceived(t *testing.T) {
p1 := buildPacket(false, 1, 1, []byte("HTTP/1.1 200 OK\r\n"))
p2 := buildPacket(false, 1, p1.Seq + uint32(len(p1.Data)), []byte("Content-Length: 10\r\n\r\n"))

msg := buildMessage(p1)
if msg.isHeadersReceived() {
t.Error("Should be complete if have only 1 packet")
}

msg.AddPacket(p2)
if !msg.isHeadersReceived() {
t.Error("Should found double new line: headers received")
}
}