package http import ( "bufio" "encoding/json" "errors" "fmt" "io" "io/ioutil" "log" "os" "path" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/up9inc/mizu/tap/api" ) const ( binDir = "bin" patternBin = "*_req.bin" patternDissect = "*.json" msgDissecting = "Dissecting:" msgAnalyzing = "Analyzing:" msgRepresenting = "Representing:" respSuffix = "_res.bin" expectDir = "expect" dissectDir = "dissect" analyzeDir = "analyze" representDir = "represent" testUpdate = "TEST_UPDATE" ) func TestRegister(t *testing.T) { dissector := NewDissector() extension := &api.Extension{} dissector.Register(extension) assert.Equal(t, "http", extension.Protocol.Name) } func TestMacros(t *testing.T) { expectedMacros := map[string]string{ "http": `proto.name == "http" and proto.version.startsWith("1")`, "http2": `proto.name == "http" and proto.version == "2.0"`, "grpc": `proto.name == "http" and proto.version == "2.0" and proto.macro == "grpc"`, } dissector := NewDissector() macros := dissector.Macros() assert.Equal(t, expectedMacros, macros) } func TestPing(t *testing.T) { dissector := NewDissector() dissector.Ping() } func TestDissect(t *testing.T) { _, testUpdateEnabled := os.LookupEnv(testUpdate) expectDirDissect := path.Join(expectDir, dissectDir) if testUpdateEnabled { os.RemoveAll(expectDirDissect) err := os.MkdirAll(expectDirDissect, 0775) assert.Nil(t, err) } dissector := NewDissector() paths, err := filepath.Glob(path.Join(binDir, patternBin)) if err != nil { log.Fatal(err) } options := &api.TrafficFilteringOptions{ IgnoredUserAgents: []string{}, } for _, _path := range paths { basePath := _path[:len(_path)-8] // Channel to verify the output itemChannel := make(chan *api.OutputChannelItem) var emitter api.Emitter = &api.Emitting{ AppStats: &api.AppStats{}, OutputChannel: itemChannel, } var items []*api.OutputChannelItem stop := make(chan bool) go func() { for { select { case <-stop: return case item := <-itemChannel: items = append(items, item) } } }() // Stream level counterPair := &api.CounterPair{ Request: 0, Response: 0, } superIdentifier := &api.SuperIdentifier{} // Request pathClient := _path fmt.Printf("%s %s\n", msgDissecting, pathClient) fileClient, err := os.Open(pathClient) assert.Nil(t, err) bufferClient := bufio.NewReader(fileClient) tcpIDClient := &api.TcpID{ SrcIP: "1", DstIP: "2", SrcPort: "1", DstPort: "2", } reqResMatcher := dissector.NewResponseRequestMatcher() err = dissector.Dissect(bufferClient, true, tcpIDClient, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { panic(err) } // Response pathServer := basePath + respSuffix fmt.Printf("%s %s\n", msgDissecting, pathServer) fileServer, err := os.Open(pathServer) assert.Nil(t, err) bufferServer := bufio.NewReader(fileServer) tcpIDServer := &api.TcpID{ SrcIP: "2", DstIP: "1", SrcPort: "2", DstPort: "1", } err = dissector.Dissect(bufferServer, false, tcpIDServer, counterPair, &api.SuperTimer{}, superIdentifier, emitter, options, reqResMatcher) if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { panic(err) } fileClient.Close() fileServer.Close() pathExpect := path.Join(expectDirDissect, fmt.Sprintf("%s.json", basePath[4:])) time.Sleep(10 * time.Millisecond) stop <- true marshaled, err := json.Marshal(items) assert.Nil(t, err) if testUpdateEnabled { if len(items) > 0 { err = os.WriteFile(pathExpect, marshaled, 0644) assert.Nil(t, err) } } else { if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) { assert.Len(t, items, 0) } else { expectedBytes, err := ioutil.ReadFile(pathExpect) assert.Nil(t, err) assert.JSONEq(t, string(expectedBytes), string(marshaled)) } } } } func TestAnalyze(t *testing.T) { _, testUpdateEnabled := os.LookupEnv(testUpdate) expectDirDissect := path.Join(expectDir, dissectDir) expectDirAnalyze := path.Join(expectDir, analyzeDir) if testUpdateEnabled { os.RemoveAll(expectDirAnalyze) err := os.MkdirAll(expectDirAnalyze, 0775) assert.Nil(t, err) } dissector := NewDissector() paths, err := filepath.Glob(path.Join(expectDirDissect, patternDissect)) if err != nil { log.Fatal(err) } for _, _path := range paths { fmt.Printf("%s %s\n", msgAnalyzing, _path) bytes, err := ioutil.ReadFile(_path) assert.Nil(t, err) var items []*api.OutputChannelItem err = json.Unmarshal(bytes, &items) assert.Nil(t, err) var entries []*api.Entry for _, item := range items { entry := dissector.Analyze(item, "", "", "") entries = append(entries, entry) } pathExpect := path.Join(expectDirAnalyze, filepath.Base(_path)) marshaled, err := json.Marshal(entries) assert.Nil(t, err) if testUpdateEnabled { if len(entries) > 0 { err = os.WriteFile(pathExpect, marshaled, 0644) assert.Nil(t, err) } } else { if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) { assert.Len(t, items, 0) } else { expectedBytes, err := ioutil.ReadFile(pathExpect) assert.Nil(t, err) assert.JSONEq(t, string(expectedBytes), string(marshaled)) } } } } func TestRepresent(t *testing.T) { _, testUpdateEnabled := os.LookupEnv(testUpdate) expectDirAnalyze := path.Join(expectDir, analyzeDir) expectDirRepresent := path.Join(expectDir, representDir) if testUpdateEnabled { os.RemoveAll(expectDirRepresent) err := os.MkdirAll(expectDirRepresent, 0775) assert.Nil(t, err) } dissector := NewDissector() paths, err := filepath.Glob(path.Join(expectDirAnalyze, patternDissect)) if err != nil { log.Fatal(err) } for _, _path := range paths { fmt.Printf("%s %s\n", msgRepresenting, _path) bytes, err := ioutil.ReadFile(_path) assert.Nil(t, err) var entries []*api.Entry err = json.Unmarshal(bytes, &entries) assert.Nil(t, err) var objects []string for _, entry := range entries { object, _, err := dissector.Represent(entry.Request, entry.Response) assert.Nil(t, err) objects = append(objects, string(object)) } pathExpect := path.Join(expectDirRepresent, filepath.Base(_path)) marshaled, err := json.Marshal(objects) assert.Nil(t, err) if testUpdateEnabled { if len(objects) > 0 { err = os.WriteFile(pathExpect, marshaled, 0644) assert.Nil(t, err) } } else { if _, err := os.Stat(pathExpect); errors.Is(err, os.ErrNotExist) { assert.Len(t, objects, 0) } else { expectedBytes, err := ioutil.ReadFile(pathExpect) assert.Nil(t, err) assert.JSONEq(t, string(expectedBytes), string(marshaled)) } } } }