diff --git a/.travis.yml b/.travis.yml index b892143..d4c697c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ dist: focal language: go go: 1.14 -script: sudo -E bash -c "source /etc/profile && eval '$(gimme 1.14)' && go test ./... -v -timeout 120s" +script: sudo -E bash -c "source /etc/profile && eval '$(gimme 1.14)' && go test ./... -v -timeout 120s -race" before_install: - sudo apt-get install libpcap-dev -y diff --git a/middleware_test.go b/middleware_test.go index b1220a7..c5ed1b3 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -1,225 +1,226 @@ package main -// import ( -// "bytes" -// "crypto/rand" -// "encoding/hex" -// "io" -// "net/http" -// "net/http/httptest" -// "strings" -// "sync" -// "testing" -// "time" +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "net/http" + "net/http/httptest" + "net/http/httputil" + "strings" + "sync" + "testing" + "time" -// "github.com/buger/goreplay/capture" -// "github.com/buger/goreplay/proto" -// ) + "github.com/buger/goreplay/capture" + "github.com/buger/goreplay/proto" +) -// type fakeServiceCb func(string, int, []byte) +type fakeServiceCb func(string, int, []byte) -// // Simple service that generate token on request, and require this token for accesing to secure area -// func NewFakeSecureService(wg *sync.WaitGroup, cb fakeServiceCb) *httptest.Server { -// active_tokens := make([]string, 0) -// var mu sync.Mutex +// Simple service that generate token on request, and require this token for accesing to secure area +func NewFakeSecureService(wg *sync.WaitGroup, cb fakeServiceCb) *httptest.Server { + activeTokens := make([]string, 0) + var mu sync.Mutex -// server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { -// mu.Lock() -// defer mu.Unlock() + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + mu.Lock() + defer mu.Unlock() -// switch req.URL.Path { -// case "/token": -// // Generate random token -// token_length := 10 -// buf := make([]byte, token_length) -// rand.Read(buf) -// token := hex.EncodeToString(buf) -// active_tokens = append(active_tokens, token) + switch req.URL.Path { + case "/token": + // Generate random token + tokenLength := 10 + buf := make([]byte, tokenLength) + rand.Read(buf) + token := hex.EncodeToString(buf) + activeTokens = append(activeTokens, token) -// w.Write([]byte(token)) + w.Write([]byte(token)) -// cb(req.URL.Path, 200, []byte(token)) -// case "/secure": -// token := req.URL.Query().Get("token") -// token_found := false + cb(req.URL.Path, 200, []byte(token)) + case "/secure": + token := req.URL.Query().Get("token") + tokenFound := false -// for _, t := range active_tokens { -// if t == token { -// token_found = true -// break -// } -// } + for _, t := range activeTokens { + if t == token { + tokenFound = true + break + } + } -// if token_found { -// w.WriteHeader(http.StatusAccepted) -// cb(req.URL.Path, 202, []byte(nil)) -// } else { -// w.WriteHeader(http.StatusForbidden) -// cb(req.URL.Path, 403, []byte(nil)) -// } -// } + if tokenFound { + w.WriteHeader(http.StatusAccepted) + cb(req.URL.Path, 202, nil) + } else { + w.WriteHeader(http.StatusForbidden) + cb(req.URL.Path, 403, nil) + } + } -// wg.Done() -// })) + wg.Done() + })) -// return server -// } + return server +} -// func TestFakeSecureService(t *testing.T) { -// var resp, token []byte +func TestFakeSecureService(t *testing.T) { + var resp, token []byte -// wg := new(sync.WaitGroup) + wg := new(sync.WaitGroup) -// server := NewFakeSecureService(wg, func(path string, status int, resp []byte) { -// }) -// defer server.Close() + server := NewFakeSecureService(wg, func(path string, status int, resp []byte) { + }) + defer server.Close() -// wg.Add(3) + wg.Add(3) -// client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: true}) -// resp, _ = client.Get("/token") -// token = proto.Body(resp) + client := NewHTTPClient(&HTTPOutputConfig{}).Client + rep, _ := client.Get(server.URL + "/token") + resp, _ = httputil.DumpResponse(rep, true) + token = proto.Body(resp) -// // Right token -// resp, _ = client.Get("/secure?token=" + string(token)) -// if !bytes.Equal(proto.Status(resp), []byte("202")) { -// t.Error("Valid token should return status 202:", string(proto.Status(resp))) -// } + // Right token + rep, _ = client.Get(server.URL + "/secure?token=" + string(token)) + resp, _ = httputil.DumpResponse(rep, true) + if !bytes.Equal(proto.Status(resp), []byte("202")) { + t.Error("Valid token should return status 202:", string(proto.Status(resp))) + } -// // Wrong tokens forbidden -// resp, _ = client.Get("/secure?token=wrong") -// if !bytes.Equal(proto.Status(resp), []byte("403")) { -// t.Error("Wrong token should returns status 403:", string(proto.Status(resp))) -// } + // Wrong tokens forbidden + rep, _ = client.Get(server.URL + "/secure?token=wrong") + resp, _ = httputil.DumpResponse(rep, true) + if !bytes.Equal(proto.Status(resp), []byte("403")) { + t.Error("Wrong token should returns status 403:", string(proto.Status(resp))) + } -// wg.Wait() -// } + wg.Wait() +} -// func TestEchoMiddleware(t *testing.T) { -// wg := new(sync.WaitGroup) +func TestEchoMiddleware(t *testing.T) { + wg := new(sync.WaitGroup) -// from := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// w.Header().Set("Env", "prod") -// w.Header().Set("RequestPath", r.URL.Path) -// wg.Done() -// })) -// defer from.Close() + from := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Env", "prod") + w.Header().Set("RequestPath", r.URL.Path) + wg.Done() + })) + defer from.Close() -// to := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// w.Header().Set("Env", "test") -// w.Header().Set("RequestPath", r.URL.Path) -// wg.Done() -// })) -// defer to.Close() + to := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Env", "test") + w.Header().Set("RequestPath", r.URL.Path) + wg.Done() + })) + defer to.Close() -// quit := make(chan int) + // Catch traffic from one service + fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) + conf := RAWInputConfig{ + Engine: capture.EnginePcap, + Expire: testRawExpire, + Protocol: ProtocolHTTP, + TrackResponse: true, + } + input := NewRAWInput(fromAddr, conf) -// // Catch traffic from one service -// fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) -// conf := RAWInputConfig{ -// engine: capture.EnginePcap, -// expire: testRawExpire, -// protocol: ProtocolHTTP, -// trackResponse: true, -// } -// input := NewRAWInput(fromAddr, conf) + // And redirect to another + output := NewHTTPOutput(to.URL, &HTTPOutputConfig{}) -// // And redirect to another -// output := NewHTTPOutput(to.URL, &HTTPOutputConfig{Debug: false}) + plugins := &InOutPlugins{ + Inputs: []PluginReader{input}, + Outputs: []PluginWriter{output}, + } + plugins.All = append(plugins.All, input, output) -// plugins := &InOutPlugins{ -// Inputs: []io.Reader{input}, -// Outputs: []io.Writer{output}, -// } -// plugins.All = append(plugins.All, input, output) + // Start Gor + emitter := NewEmitter() + emitter.Start(plugins, "echo -n && GOR_TEST=true && ./examples/middleware/echo.sh") -// // Start Gor -// emitter := NewEmitter(quit) -// go emitter.Start(plugins, "echo -n && GOR_TEST=true && ./examples/middleware/echo.sh") + // Wait till middleware initialization + time.Sleep(100 * time.Millisecond) -// // Wait till middleware initialization -// time.Sleep(100 * time.Millisecond) + // Should receive 2 requests from original + 2 from replayed + client := NewHTTPClient(output.(*HTTPOutput).config).Client -// // Should receive 2 requests from original + 2 from replayed -// client := NewHTTPClient(from.URL, &HTTPClientConfig{Debug: false}) + for i := 0; i < 10; i++ { + wg.Add(2) + // Request should be echoed + client.Get(to.URL + "/a") + time.Sleep(5 * time.Millisecond) + client.Get(to.URL + "/b") + time.Sleep(5 * time.Millisecond) + } -// for i := 0; i < 10; i++ { -// wg.Add(2) -// // Request should be echoed -// client.Get("/a") -// time.Sleep(5 * time.Millisecond) -// client.Get("/b") -// time.Sleep(5 * time.Millisecond) -// } + wg.Wait() + emitter.Close() +} -// wg.Wait() -// emitter.Close() -// } +func TestTokenMiddleware(t *testing.T) { + var resp, token []byte -// func TestTokenMiddleware(t *testing.T) { -// var resp, token []byte + wg := new(sync.WaitGroup) -// wg := new(sync.WaitGroup) + from := NewFakeSecureService(wg, func(path string, status int, tok []byte) { + time.Sleep(10 * time.Millisecond) + }) + defer from.Close() -// from := NewFakeSecureService(wg, func(path string, status int, tok []byte) { -// time.Sleep(10 * time.Millisecond) -// }) -// defer from.Close() + to := NewFakeSecureService(wg, func(path string, status int, tok []byte) { + switch path { + case "/secure": + if status != 202 { + t.Error("Server should receive valid rewritten token") + } + } -// to := NewFakeSecureService(wg, func(path string, status int, tok []byte) { -// switch path { -// case "/secure": -// if status != 202 { -// t.Error("Server should receive valid rewritten token") -// } -// } + time.Sleep(10 * time.Millisecond) + }) + defer to.Close() -// time.Sleep(10 * time.Millisecond) -// }) -// defer to.Close() + Settings.Middleware = "echo -n && GOR_TEST=true && go run ./examples/middleware/token_modifier.go" -// quit := make(chan int) + fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) + conf := RAWInputConfig{ + Engine: capture.EnginePcap, + Expire: testRawExpire, + Protocol: ProtocolHTTP, + TrackResponse: true, + } + // Catch traffic from one service + input := NewRAWInput(fromAddr, conf) -// Settings.middleware = "echo -n && GOR_TEST=true && go run ./examples/middleware/token_modifier.go" + // And redirect to another + output := NewHTTPOutput(to.URL, &HTTPOutputConfig{}) -// fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) -// conf := RAWInputConfig{ -// engine: capture.EnginePcap, -// expire: testRawExpire, -// protocol: ProtocolHTTP, -// trackResponse: true, -// } -// // Catch traffic from one service -// input := NewRAWInput(fromAddr, conf) + plugins := &InOutPlugins{ + Inputs: []PluginReader{input}, + Outputs: []PluginWriter{output}, + } + plugins.All = append(plugins.All, input, output) -// // And redirect to another -// output := NewHTTPOutput(to.URL, &HTTPOutputConfig{Debug: true}) + // Start Gor + emitter := NewEmitter() + emitter.Start(plugins, Settings.Middleware) -// plugins := &InOutPlugins{ -// Inputs: []io.Reader{input}, -// Outputs: []io.Writer{output}, -// } -// plugins.All = append(plugins.All, input, output) + // Should receive 2 requests from original + 2 from replayed + wg.Add(2) -// // Start Gor -// emitter := NewEmitter(quit) -// go emitter.Start(plugins, Settings.middleware) + client := NewHTTPClient(&HTTPOutputConfig{}).Client -// // Should receive 2 requests from original + 2 from replayed -// wg.Add(2) + // Sending traffic to original service + rep, _ := client.Get(to.URL + "/token") + resp, _ = httputil.DumpResponse(rep, true) + token = proto.Body(resp) -// client := NewHTTPClient(from.URL, &HTTPClientConfig{Debug: true}) + rep, _ = client.Get(to.URL + "/secure?token=" + string(token)) + resp, _ = httputil.DumpResponse(rep, true) + if !bytes.Equal(proto.Status(resp), []byte("202")) { + t.Error("Valid token should return 202:", proto.Status(resp)) + } -// // Sending traffic to original service -// resp, _ = client.Get("/token") -// token = proto.Body(resp) - -// resp, _ = client.Get("/secure?token=" + string(token)) -// if !bytes.Equal(proto.Status(resp), []byte("202")) { -// t.Error("Valid token should return 202:", proto.Status(resp)) -// } - -// wg.Wait() -// emitter.Close() -// Settings.middleware = "" -// } + wg.Wait() + emitter.Close() + Settings.Middleware = "" +}