blob: 32ad96a9cf02f58386319da064ee84a7f02ec772 [file] [log] [blame]
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package protocols
import (
"bufio"
"container/list"
"net/http"
"reflect"
"strings"
"testing"
"github.com/apache/skywalking-rover/pkg/profiling/task/network/analyze/base"
)
var unknown, request, response = 0, 1, 2
var finished, notFinished = 1, 0
// nolint
func TestBuildHTTP1(t *testing.T) {
tests := []struct {
name string
events []struct {
dataID int
dataType int
sequence int
finished int
data string
}
http []struct {
start int
end int
}
residueID []int
}{
{
name: "simple",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{1, request, 0, notFinished, ""},
{1, request, 1, notFinished, ""},
{1, request, 2, finished, ""},
{2, response, 0, notFinished, ""},
{2, response, 1, finished, ""},
{3, request, 0, finished, ""},
{4, response, 0, finished, ""},
},
http: []struct {
start int
end int
}{
{1, 2},
{3, 4},
},
residueID: []int{},
},
{
name: "response before request",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{2, response, 0, finished, ""},
{1, request, 1, finished, ""},
{1, request, 0, notFinished, ""},
{3, request, 0, notFinished, ""},
{3, request, 1, notFinished, ""},
{4, response, 1, finished, ""},
{4, response, 0, notFinished, ""},
{3, request, 2, finished, ""},
{5, request, 0, notFinished, ""},
{5, request, 1, notFinished, ""},
{6, response, 1, finished, ""},
{6, response, 0, notFinished, ""},
{5, request, 2, finished, ""},
},
http: []struct {
start int
end int
}{
{1, 2},
{3, 4},
{5, 6},
},
residueID: []int{},
},
{
name: "residue requests",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{1, request, 0, finished, ""},
{2, response, 1, finished, ""},
{2, response, 0, notFinished, ""},
{3, request, 0, finished, ""},
{4, response, 0, notFinished, ""},
{5, request, 1, finished, ""},
{6, response, 0, finished, ""},
},
http: []struct {
start int
end int
}{
{1, 2},
},
residueID: []int{3, 4, 5, 6},
},
{
name: "multiple request",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{1, request, 0, finished, ""},
{2, request, 0, finished, ""},
},
http: []struct {
start int
end int
}{},
residueID: []int{1, 2},
},
{
name: "multiple response",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{1, request, 0, finished, ""},
{3, response, 1, finished, ""},
{4, response, 0, finished, ""},
},
http: []struct {
start int
end int
}{},
residueID: []int{1, 3, 4},
},
{
name: "unfinished response",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{42, response, 0, notFinished, ""},
{42, response, 1, notFinished, ""},
{42, response, 2, finished, ""},
{48, request, 0, finished, ""},
{50, response, 0, notFinished, ""},
{50, response, 1, notFinished, ""},
{50, response, 2, finished, ""},
},
http: []struct {
start int
end int
}{
{48, 50},
},
residueID: []int{42, 42, 42},
},
{
name: "unknown data",
events: []struct {
dataID int
dataType int
sequence int
finished int
data string
}{
{1, unknown, 0, notFinished, "GET / HTTP/1.1\r\n"},
{1, unknown, 1, notFinished, "Host: test.com\n\r\n"},
{2, response, 0, finished, ""},
{3, unknown, 1, notFinished, "Host: test.com\n\r\n"},
{4, response, 0, finished, ""},
{3, unknown, 0, notFinished, "GET / HTTP/1.1\r\n"},
{6, unknown, 1, notFinished, "Host: test.com\n\r\n"},
{5, request, 0, finished, ""},
{6, unknown, 0, notFinished, "HTTP/1.1 200 OK\r\n"},
// request not finished
{7, unknown, 1, notFinished, "Host: test.com\n"},
{8, response, 0, finished, ""},
{7, unknown, 0, notFinished, "GET / HTTP/1.1\r\n"},
{9, request, 0, finished, ""},
{10, response, 0, finished, ""},
},
http: []struct {
start int
end int
}{
{1, 2},
{3, 4},
{5, 6},
{9, 10},
},
residueID: []int{7, 7, 8},
},
}
for _, testCase := range tests {
//t.Run(testCase.name, func(t *testing.T) {
analyzer := NewHTTP1Analyzer().(*HTTP1Analyzer)
l := list.New()
var events = make([]struct {
start, end int
}, 0)
for _, event := range testCase.events {
req, resp := analyzer.buildHTTP1(l, &SocketDataUploadEvent{
DataID: uint64(event.dataID),
MsgType: base.SocketMessageType(event.dataType),
Sequence: uint16(event.sequence),
Finished: uint8(event.finished),
Buffer: bufferConvert(event.data),
DataLen: uint16(len(event.data)),
})
if req != nil && resp != nil {
events = append(events, struct{ start, end int }{start: req.MinDataID(), end: resp.MaxDataID()})
}
}
if !reflect.DeepEqual(testCase.http, events) {
t.Fatalf("excepted http: %v, actual: %v", testCase.http, events)
}
exceptedList := testCase.residueID
if exceptedList == nil {
exceptedList = make([]int, 0)
}
actualList := make([]int, 0)
for element := l.Front(); element != nil; element = element.Next() {
actualList = append(actualList, int(element.Value.(*SocketDataUploadEvent).DataID))
}
if !reflect.DeepEqual(exceptedList, actualList) {
t.Fatalf("excepted residue data list: %v, actual: %v", exceptedList, actualList)
}
//})
}
}
var defaultBuffer [2048]byte
func bufferConvert(data string) [2048]byte {
if data == "" {
return defaultBuffer
}
var buffer [2048]byte
for inx, d := range []byte(data) {
buffer[inx] = d
}
return buffer
}
func TestParseSimpleHTTP1Response(t *testing.T) {
s := `HTTP/1.0 200 OK\r\n`
h := &http.Request{}
analyzer := NewHTTP1Analyzer().(*HTTP1Analyzer)
resp, err := analyzer.tryingToReadResponseWithoutHeaders(bufio.NewReader(strings.NewReader(s)), h)
if err != nil {
t.Fatalf("reading simple response error: %v", err)
}
if resp.Body != nil {
defer resp.Body.Close()
}
}