Merge branch 'master' into pr/308

This commit is contained in:
Leonid Bugaev
2016-06-27 16:18:43 +03:00
6 changed files with 176 additions and 97 deletions
+4
View File
@@ -2,6 +2,10 @@
#
# `xxd` utility included into vim-common package
# It allow hex decoding/encoding
#
# This example may broke if you request contains `null` string, you may consider using pipes instead.
# See: https://github.com/buger/gor/issues/309
#
function log {
# Logging to stderr, because stdout/stdin used for data transfer
+39 -12
View File
@@ -123,7 +123,7 @@ func headerIndex(payload []byte, name []byte) int {
// header return value and positions of header/value start/end.
// If not found, value will be blank, and headerStart will be -1
// Do not support multi-line headers.
func header(payload []byte, name []byte) (value []byte, headerStart, valueStart, headerEnd int) {
func header(payload []byte, name []byte) (value []byte, headerStart, headerEnd, valueStart, valueEnd int) {
headerStart = headerIndex(payload, name)
if headerStart == -1 {
@@ -131,24 +131,38 @@ func header(payload []byte, name []byte) (value []byte, headerStart, valueStart,
}
valueStart = headerStart + len(name) + 1 // Skip ":" after header name
if payload[valueStart] == ' ' { // Ignore empty space after ':'
valueStart++
}
headerEnd = valueStart + bytes.IndexByte(payload[valueStart:], '\n')
if payload[headerEnd-1] == '\r' {
headerEnd -= 1
for valueStart < headerEnd { // Ignore empty space after ':'
if payload[valueStart] == ' ' {
valueStart++
} else {
break
}
}
value = payload[valueStart:headerEnd]
valueEnd = valueStart + bytes.IndexByte(payload[valueStart:], '\n')
if payload[headerEnd-1] == '\r' {
valueEnd--
}
// ignore empty space at end of header value
for valueStart < valueEnd {
if payload[valueEnd-1] == ' ' {
valueEnd--
} else {
break
}
}
value = payload[valueStart:valueEnd]
return
}
// Header returns header value, if header not found, value will be blank
func Header(payload, name []byte) []byte {
val, _, _, _ := header(payload, name)
val, _, _, _, _ := header(payload, name)
return val
}
@@ -156,11 +170,11 @@ func Header(payload, name []byte) []byte {
// SetHeader sets header value. If header not found it creates new one.
// Returns modified request payload
func SetHeader(payload, name, value []byte) []byte {
_, hs, vs, he := header(payload, name)
_, hs, _, vs, ve := header(payload, name)
if hs != -1 {
// If header found we just repace its value
return byteutils.Replace(payload, vs, he, value)
// If header found we just replace its value
return byteutils.Replace(payload, vs, ve, value)
}
return AddHeader(payload, name, value)
@@ -180,6 +194,19 @@ func AddHeader(payload, name, value []byte) []byte {
return byteutils.Insert(payload, mimeStart, header)
}
// DelHeader takes http payload and removes header name from headers section
// Returns modified request payload
func DeleteHeader(payload, name[]byte) []byte {
_, hs, he, _, _ := header(payload, name)
if hs != -1 {
newHeader := make([]byte, len(payload) - (he - hs) - 1)
copy(newHeader[:hs], payload[:hs])
copy(newHeader[hs:], payload[he + 1:])
return newHeader
}
return payload
}
// Body returns request/response body
func Body(payload []byte) []byte {
// 4 -> len(EMPTY_LINE)
+27 -1
View File
@@ -16,6 +16,13 @@ func TestHeader(t *testing.T) {
t.Error("Should find header value")
}
// Value with space at end
payload = []byte("POST /post HTTP/1.1\r\nContent-Length: 7 \r\nHost: www.w3.org\r\n\r\na=1&b=2")
if val = Header(payload, []byte("Content-Length")); !bytes.Equal(val, []byte("7")) {
t.Error("Should find header value without space after 7")
}
// Value without space at start
payload = []byte("POST /post HTTP/1.1\r\nContent-Length:7\r\nHost: www.w3.org\r\n\r\na=1&b=2")
@@ -38,7 +45,7 @@ func TestHeader(t *testing.T) {
}
// Header not found
if _, headerStart, _, _ = header(payload, []byte("Not-Found")); headerStart != -1 {
if _, headerStart, _, _, _ = header(payload, []byte("Not-Found")); headerStart != -1 {
t.Error("Should not found header")
}
@@ -97,6 +104,25 @@ func TestSetHeader(t *testing.T) {
}
}
func TestDeleteHeader(t *testing.T) {
var payload, payloadAfter []byte
payload = []byte("POST /post HTTP/1.1\r\nUser-Agent: Gor\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2")
payloadAfter = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2")
if payload = DeleteHeader(payload, []byte("User-Agent")); !bytes.Equal(payload, payloadAfter) {
t.Error("Should delete header if found", string(payload), string(payloadAfter))
}
//Whitespace at end of User-Agent
payload = []byte("POST /post HTTP/1.1\r\nUser-Agent: Gor \r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2")
payloadAfter = []byte("POST /post HTTP/1.1\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2")
if payload = DeleteHeader(payload, []byte("User-Agent")); !bytes.Equal(payload, payloadAfter) {
t.Error("Should delete header if found", string(payload), string(payloadAfter))
}
}
func TestPath(t *testing.T) {
var path, payload []byte
+42 -35
View File
@@ -28,6 +28,7 @@ import (
"strings"
"sync"
"time"
"github.com/buger/gor/proto"
)
var _ = fmt.Println
@@ -351,16 +352,30 @@ func (t *Listener) readPcap() {
continue
}
if decoder == layers.LinkTypeEthernet {
// Skip ethernet layer, 14 bytes
data = packet.Data()[14:]
} else if decoder == layers.LinkTypeNull || decoder == layers.LinkTypeLoop {
data = packet.Data()[4:]
} else {
log.Println("Unknown packet layer", packet)
break
// We should remove network layer before parsing TCP/IP data
var of int
switch decoder {
case layers.LinkTypeEthernet:
of = 14
case layers.LinkTypePPP:
of = 1
case layers.LinkTypeFDDI:
of = 13
case layers.LinkTypeNull:
of = 4
case layers.LinkTypeLoop:
of = 4
case layers.LinkTypeRaw:
of = 0
case layers.LinkTypeLinuxSLL:
of = 16
default:
log.Println("Unknown packet layer", packet)
break
}
data = packet.Data()[of:]
version := uint8(data[0]) >> 4
if version == 4 {
@@ -501,9 +516,6 @@ func (t *Listener) isValidPacket(buf []byte) bool {
return false
}
var bExpect100ContinueCheck = []byte("Expect: 100-continue")
var bPOST = []byte("POST")
// Trying to add packet to existing message or creating new message
//
// For TCP message unique id is Acknowledgment number (see tcp_packet.go)
@@ -575,35 +587,30 @@ func (t *Listener) processTCPPacket(packet *TCPPacket) {
message.AddPacket(packet)
// Handling Expect: 100-continue requests
if len(packet.Data) > 4 && bytes.Equal(packet.Data[0:4], bPOST) {
// reading last 20 bytes (not counting CRLF): last header value (if no body presented)
if bytes.Equal(packet.Data[len(packet.Data)-24:len(packet.Data)-4], bExpect100ContinueCheck) {
seq := packet.Seq + uint32(len(packet.Data))
t.seqWithData[seq] = packet.Ack
message.DataSeq = seq
if message.Is100Continue() {
seq := packet.Seq + uint32(len(packet.Data))
t.seqWithData[seq] = packet.Ack
message.DataSeq = seq
// In case if sequence packet came first
for _, m := range t.messages {
if m.Seq == seq {
t.deleteMessage(m)
if m.AssocMessage != nil {
message.AssocMessage = m.AssocMessage
}
// log.Println("2: Adding ack alias:", m.Ack, packet.Ack)
t.ackAliases[m.Ack] = packet.Ack
// In case if sequence packet came first
for _, m := range t.messages {
if m.Seq == seq {
t.deleteMessage(m)
if m.AssocMessage != nil {
message.AssocMessage = m.AssocMessage
}
// log.Println("2: Adding ack alias:", m.Ack, packet.Ack)
t.ackAliases[m.Ack] = packet.Ack
for _, pkt := range m.packets {
pkt.UpdateAck(packet.Ack)
message.AddPacket(pkt)
}
for _, pkt := range m.packets {
pkt.UpdateAck(packet.Ack)
message.AddPacket(pkt)
}
}
// Removing `Expect: 100-continue` header
packet.Data = append(packet.Data[:len(packet.Data)-24], packet.Data[len(packet.Data)-2:]...)
// log.Println(string(packet.Data))
}
// Removing `Expect: 100-continue` header
packet.Data = proto.DeleteHeader(packet.Data, bExpectHeader)
}
// log.Println("Received message:", string(message.Bytes()), message.ID(), t.messages)
+35 -49
View File
@@ -108,9 +108,7 @@ func TestRawListenerResponse(t *testing.T) {
}
}
func TestRawListener100Continue(t *testing.T) {
var req, resp *TCPMessage
func TestShort100Continue(t *testing.T) {
listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond)
defer listener.Close()
@@ -124,50 +122,13 @@ func TestRawListener100Continue(t *testing.T) {
// panic(int(uint32(len(reqPacket1.Data)) + uint32(len(reqPacket2.Data)) + uint32(len(reqPacket3.Data))))
respPacket2 := buildPacket(false, reqPacket3.Seq+1 /* len of data */, 2, []byte("HTTP/1.1 200 OK\r\n"))
listener.packetsChan <- reqPacket1.Dump()
listener.packetsChan <- reqPacket2.Dump()
listener.packetsChan <- reqPacket3.Dump()
result := []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab")
listener.packetsChan <- respPacket1.Dump()
listener.packetsChan <- respPacket2.Dump()
select {
case req = <-listener.messagesChan:
break
case <-time.After(11 * time.Millisecond):
t.Error("Should return request after expire time")
return
}
if !bytes.Equal(req.Bytes(), []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab")) {
t.Error("Should receive full message", string(req.Bytes()))
}
if !req.IsIncoming {
t.Error("Should be request")
}
select {
case resp = <-listener.messagesChan:
break
case <-time.After(21 * time.Millisecond):
t.Error("Should return response after expire time")
return
}
if resp.IsIncoming {
t.Error("Should be response")
}
if !bytes.Equal(resp.UUID(), req.UUID()) {
t.Error("Resp and Req UUID should be equal")
}
testRawListener100Continue(t, listener, result, reqPacket1, reqPacket2, reqPacket3, respPacket1, respPacket2)
}
// Response comes before Request
func TestRawListener100ContinueWrongOrder(t *testing.T) {
var req, resp *TCPMessage
func Test100ContinueWrongOrder(t *testing.T) {
listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond)
defer listener.Close()
@@ -181,12 +142,37 @@ func TestRawListener100ContinueWrongOrder(t *testing.T) {
// panic(int(uint32(len(reqPacket1.Data)) + uint32(len(reqPacket2.Data)) + uint32(len(reqPacket3.Data))))
respPacket2 := buildPacket(false, reqPacket3.Seq+1 /* len of data */, 2, []byte("HTTP/1.1 200 OK\r\n"))
listener.packetsChan <- respPacket1.Dump()
listener.packetsChan <- respPacket2.Dump()
result := []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab")
listener.packetsChan <- reqPacket1.Dump()
listener.packetsChan <- reqPacket2.Dump()
listener.packetsChan <- reqPacket3.Dump()
testRawListener100Continue(t, listener, result, respPacket1, respPacket2, reqPacket1, reqPacket2, reqPacket3)
}
func TestAlt100ContinueHeaderOrder(t *testing.T) {
listener := NewListener("", "0", EnginePcap, true, 10*time.Millisecond)
defer listener.Close()
reqPacket1 := buildPacket(true, 1, 1, []byte("POST / HTTP/1.1\r\nExpect: 100-continue\r\nContent-Length: 2\r\n\r\n"))
// Packet with data have different Seq
reqPacket2 := buildPacket(true, 2, reqPacket1.Seq+uint32(len(reqPacket1.Data)), []byte("a"))
reqPacket3 := buildPacket(true, 2, reqPacket2.Seq+1, []byte("b"))
respPacket1 := buildPacket(false, 10, 3, []byte("HTTP/1.1 100 Continue\r\n"))
// panic(int(uint32(len(reqPacket1.Data)) + uint32(len(reqPacket2.Data)) + uint32(len(reqPacket3.Data))))
respPacket2 := buildPacket(false, reqPacket3.Seq+1 /* len of data */, 2, []byte("HTTP/1.1 200 OK\r\n"))
result := []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab")
testRawListener100Continue(t, listener, result, reqPacket1, reqPacket2, reqPacket3, respPacket1, respPacket2)
}
func testRawListener100Continue(t *testing.T, listener *Listener, result []byte, packets ...*TCPPacket) {
var req, resp *TCPMessage
for _, p := range packets {
listener.packetsChan <- p.Dump()
}
select {
case req = <-listener.messagesChan:
@@ -196,7 +182,7 @@ func TestRawListener100ContinueWrongOrder(t *testing.T) {
return
}
if !bytes.Equal(req.Bytes(), []byte("POST / HTTP/1.1\r\nContent-Length: 2\r\n\r\nab")) {
if !bytes.Equal(req.Bytes(), result) {
t.Error("Should receive full message", string(req.Bytes()))
}
+29
View File
@@ -227,6 +227,35 @@ func (t *TCPMessage) IsFinished() bool {
return false
}
var bExpectHeader = []byte("Expect:")
var bExpect100Value = []byte("100-continue")
var bPOST = []byte("POST")
var bCRLFx2 = []byte("\r\n\r\n")
func (t *TCPMessage) Is100Continue() bool {
d := t.packets[0].Data
if len(d) < 25 {
return false
}
if !bytes.Equal(d[0:4], bPOST) {
return false
}
// reading last 4 bytes for double CRLF
if !bytes.Equal(d[len(d)-4:], bCRLFx2) {
return false
}
// look for an expect:100-continue header
if !bytes.Equal(bExpect100Value, proto.Header(d, bExpectHeader)) {
return false
}
return true
}
func (t *TCPMessage) UUID() []byte {
var key []byte