From a2d123e27338d5839735562276dca253dae4b403 Mon Sep 17 00:00:00 2001 From: kaituo Date: Mon, 7 Jul 2025 21:29:32 +0000 Subject: [PATCH 01/11] Record http requests from the same test in one log file --- internal/record/recording_https_proxy.go | 58 ++++++++++--------- internal/replay/replay_http_server.go | 71 ++++++++++++++++++++---- internal/store/store.go | 34 ++++-------- 3 files changed, 101 insertions(+), 62 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index 6ec51c8..cdaa21b 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -32,7 +32,6 @@ import ( ) type RecordingHTTPSProxy struct { - prevRequestSHA string config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -40,17 +39,12 @@ type RecordingHTTPSProxy struct { func NewRecordingHTTPSProxy(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *RecordingHTTPSProxy { return &RecordingHTTPSProxy{ - prevRequestSHA: store.HeadSHA, config: cfg, recordingDir: recordingDir, redactor: redactor, } } -func (r *RecordingHTTPSProxy) ResetChain() { - r.prevRequestSHA = store.HeadSHA -} - func (r *RecordingHTTPSProxy) Start() error { addr := fmt.Sprintf(":%d", r.config.SourcePort) server := &http.Server{ @@ -70,7 +64,7 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req } fmt.Printf("Recording request: %s %s\n", req.Method, req.URL.String()) - recReq, err := r.recordRequest(req) + recReq, err := r.redactRequest(req) if err != nil { fmt.Printf("Error recording request: %v\n", err) http.Error(w, fmt.Sprintf("Error recording request: %v", err), http.StatusInternalServerError) @@ -96,7 +90,7 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req return } - err = r.recordResponse(resp, fileName, respBody) + err = r.recordResponse(recReq, resp, fileName, respBody) if err != nil { fmt.Printf("Error recording response: %v\n", err) @@ -105,8 +99,8 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req } } -func (r *RecordingHTTPSProxy) recordRequest(req *http.Request) (*store.RecordedRequest, error) { - recordedRequest, err := store.NewRecordedRequest(req, r.prevRequestSHA, *r.config) +func (r *RecordingHTTPSProxy) redactRequest(req *http.Request) (*store.RecordedRequest, error) { + recordedRequest, err := store.NewRecordedRequest(req, *r.config) if err != nil { return recordedRequest, err } @@ -117,17 +111,6 @@ func (r *RecordingHTTPSProxy) recordRequest(req *http.Request) (*store.RecordedR r.redactor.Headers(recordedRequest.Header) recordedRequest.Request = r.redactor.String(recordedRequest.Request) recordedRequest.Body = r.redactor.Bytes(recordedRequest.Body) - - fileName, err := recordedRequest.GetRecordingFileName() - if err != nil { - fmt.Printf("Invalid recording file name: %v\n", err) - return recordedRequest, err - } - recordPath := filepath.Join(r.recordingDir, fileName+".req") - err = os.WriteFile(recordPath, []byte(recordedRequest.Serialize()), 0644) - if err != nil { - return recordedRequest, err - } return recordedRequest, nil } @@ -178,17 +161,40 @@ func (r *RecordingHTTPSProxy) proxyRequest(w http.ResponseWriter, req *http.Requ return resp, respBodyBytes, nil } -func (r *RecordingHTTPSProxy) recordResponse(resp *http.Response, fileName string, body []byte) error { +func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp *http.Response, fileName string, body []byte) error { recordedResponse, err := store.NewRecordedResponse(resp, body) if err != nil { return err } + recordPath := filepath.Join(r.recordingDir, fileName+".http.log") + shaSum := recReq.ComputeSum() - recordedResponse.Body = r.redactor.Bytes(recordedResponse.Body) + // Open the file in append mode, create it if it doesn't exist. + file, err := os.OpenFile(recordPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + + fmt.Printf("Writing request to: %s\n", recordPath) + serializedReq := recReq.Serialize() + _, err = file.WriteString(fmt.Sprintf("%s.req %d\n", shaSum, len(serializedReq))) + if err != nil { + return err + } + _, err = file.WriteString(serializedReq) + if err != nil { + return err + } - recordPath := filepath.Join(r.recordingDir, fileName+".resp") fmt.Printf("Writing response to: %s\n", recordPath) - err = os.WriteFile(recordPath, []byte(recordedResponse.Serialize()), 0644) + recordedResponse.Body = r.redactor.Bytes(recordedResponse.Body) + serializedResp := recordedResponse.Serialize() + _, err = file.WriteString(fmt.Sprintf("\n%s.resp %d\n", shaSum, len(serializedResp))) + if err != nil { + return err + } + _, err = file.WriteString(serializedResp) if err != nil { return err } @@ -230,7 +236,7 @@ func (r *RecordingHTTPSProxy) proxyWebsocket(w http.ResponseWriter, req *http.Re go pumpWebsocket(clientConn, conn, c, quit, ">") go pumpWebsocket(conn, clientConn, c, quit, "<") - recordPath := filepath.Join(r.recordingDir, fileName+".websocket") + recordPath := filepath.Join(r.recordingDir, fileName+".websocket.log") f, err := os.Create(recordPath) if err != nil { fmt.Printf("Error creating websocket recording file: %v\n", err) diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index a3d735f..295483d 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -24,6 +24,8 @@ import ( "strconv" "strings" "unicode" + "bufio" + "io" "github.com/google/test-server/internal/config" "github.com/google/test-server/internal/redact" @@ -32,7 +34,6 @@ import ( ) type ReplayHTTPServer struct { - prevRequestSHA string config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -40,7 +41,6 @@ type ReplayHTTPServer struct { func NewReplayHTTPServer(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *ReplayHTTPServer { return &ReplayHTTPServer{ - prevRequestSHA: store.HeadSHA, config: cfg, recordingDir: recordingDir, redactor: redactor, @@ -92,7 +92,8 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques return } fmt.Printf("Replaying http request: %s\n", redactedReq.Request) - resp, err := r.loadResponse(fileName) + shaSum := redactedReq.ComputeSum() + resp, err := r.loadResponse(fileName, shaSum) if err != nil { fmt.Printf("Error loading response: %v\n", err) http.Error(w, fmt.Sprintf("Error loading response: %v", err), http.StatusInternalServerError) @@ -107,7 +108,7 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques } func (r *ReplayHTTPServer) createRedactedRequest(req *http.Request) (*store.RecordedRequest, error) { - recordedRequest, err := store.NewRecordedRequest(req, r.prevRequestSHA, *r.config) + recordedRequest, err := store.NewRecordedRequest(req, *r.config) if err != nil { return nil, err } @@ -122,14 +123,60 @@ func (r *ReplayHTTPServer) createRedactedRequest(req *http.Request) (*store.Reco return recordedRequest, nil } -func (r *ReplayHTTPServer) loadResponse(fileName string) (*store.RecordedResponse, error) { - responseFile := filepath.Join(r.recordingDir, fileName+".resp") - fmt.Printf("loading response from : %s\n", responseFile) - responseData, err := os.ReadFile(responseFile) +func (r *ReplayHTTPServer) loadResponse(fileName string, shaSum string) (*store.RecordedResponse, error) { + // 1. Open the replay log file for reading. + filePath := filepath.Join(r.recordingDir, fileName+".http.log") + fmt.Printf("loading response from : %s with shaSum: %s\n", filePath, shaSum) + file, err := os.Open(filePath) if err != nil { - return nil, err + return nil, fmt.Errorf("could not open file %s: %w", filePath, err) + } + defer file.Close() + + reader := bufio.NewReader(file) + expectedKey := shaSum + ".resp" + // 2. Scan the file line by line using the reader directly. + for { + // Read one line, including the newline character. + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + return nil, fmt.Errorf("response with shaSum %s not found in file", shaSum) + } + return nil, fmt.Errorf("error while reading file: %w", err) + } + + // Trim whitespace, including the trailing newline, from the line. + trimmedLine := strings.TrimSpace(line) + + parts := strings.Fields(trimmedLine) + if len(parts) != 2 { + continue + } + + fileKey := parts[0] + sizeStr := parts[1] + + // 3. Check if the key on this line matches our target shaSum. + if fileKey == expectedKey { + size, err := strconv.Atoi(sizeStr) + if err != nil { + return nil, fmt.Errorf("invalid size format on delimiter line: '%s'", trimmedLine) + } + fmt.Printf("Bytes to load: %d\n", size) + + if size < 0 { + return nil, fmt.Errorf("invalid negative size on delimiter line: '%s'", trimmedLine) + } + + // 4. We can read the exact number of bytes for the payload. + data := make([]byte, size) + if _, err := io.ReadFull(reader, data); err != nil { + return nil, fmt.Errorf("failed to read %d bytes after delimiter: %w", size, err) + } + return store.DeserializeResponse(data) + } } - return store.DeserializeResponse(responseData) } func (r *ReplayHTTPServer) writeResponse(w http.ResponseWriter, resp *store.RecordedResponse) error { @@ -175,8 +222,8 @@ func (r *ReplayHTTPServer) proxyWebsocket(w http.ResponseWriter, req *http.Reque replayWebsocket(clientConn, chunks) } -func (r *ReplayHTTPServer) loadWebsocketChunks(sha string) ([]string, error) { - responseFile := filepath.Join(r.recordingDir, sha+".websocket") +func (r *ReplayHTTPServer) loadWebsocketChunks(fileName string) ([]string, error) { + responseFile := filepath.Join(r.recordingDir, fileName+".websocket.log") fmt.Printf("loading websocket response from : %s\n", responseFile) bytes, err := os.ReadFile(responseFile) var chunks = make([]string, 0) diff --git a/internal/store/store.go b/internal/store/store.go index 25b9090..9364cd0 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -30,21 +30,17 @@ import ( "github.com/google/test-server/internal/config" ) -// A sha of an invalid RecordRequest to be used as the head of all chains. -const HeadSHA = "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d" - type RecordedRequest struct { - Request string - Header http.Header - Body []byte - PreviousRequest string // The sha256 sum of the previous request in the chain. - ServerAddress string - Port int64 - Protocol string + Request string + Header http.Header + Body []byte + ServerAddress string + Port int64 + Protocol string } // NewRecordedRequest creates a RecordedRequest from an http.Request. -func NewRecordedRequest(req *http.Request, previousRequest string, cfg config.EndpointConfig) (*RecordedRequest, error) { +func NewRecordedRequest(req *http.Request, cfg config.EndpointConfig) (*RecordedRequest, error) { // Read the body. body, err := readBody(req) if err != nil { @@ -62,7 +58,6 @@ func NewRecordedRequest(req *http.Request, previousRequest string, cfg config.En Request: request, Header: header, Body: body, - PreviousRequest: previousRequest, ServerAddress: cfg.TargetHost, Port: cfg.TargetPort, Protocol: cfg.TargetType, @@ -110,8 +105,7 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { // Serialize the request. // // The serialization format is as follows: -// - The first line is the sha256 of the previous request as a hex string. -// - Next is the server address. +// - The first line is the server address. // - Next is the port. // - Next is the protocol. // - Next is a line of 80 asterisks. @@ -120,11 +114,7 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { // - Next, there are 2 empty lines. // - The rest of the file is the body content. func (r *RecordedRequest) Serialize() string { - var builder strings.Builder - - // Format the SHA256 sum of the previous request. - builder.WriteString(r.PreviousRequest) - builder.WriteString("\n") + var builder bytes.Buffer builder.WriteString(fmt.Sprintf("Server Address: %s\n", r.ServerAddress)) @@ -152,7 +142,7 @@ func (r *RecordedRequest) Serialize() string { } builder.WriteString("\n\n") - builder.WriteString(string(r.Body)) + builder.Write(r.Body) return builder.String() } @@ -163,9 +153,6 @@ func Deserialize(data string) (*RecordedRequest, error) { if len(lines) < 6 { return nil, fmt.Errorf("invalid serialized data: not enough lines") } - - previousRequest := lines[0] - serverAddress := strings.TrimPrefix(lines[1], "Server Address: ") portString := strings.TrimPrefix(lines[2], "Port: ") protocol := strings.TrimPrefix(lines[3], "Protocol: ") @@ -207,7 +194,6 @@ func Deserialize(data string) (*RecordedRequest, error) { Request: request, Header: headers, Body: body, - PreviousRequest: previousRequest, ServerAddress: serverAddress, Port: int64(port), Protocol: protocol, From 0b975f9035e36f93cc2ae32bde7db5f0edce502e Mon Sep 17 00:00:00 2001 From: kaituo Date: Mon, 7 Jul 2025 21:39:15 +0000 Subject: [PATCH 02/11] fix test --- internal/store/store_test.go | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/internal/store/store_test.go b/internal/store/store_test.go index b37ff6e..c198389 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -38,12 +38,11 @@ func TestRecordedRequest_Serialize(t *testing.T) { Request: "", Header: http.Header{}, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\n\n\n\n", + expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\n\n\n\n", }, { name: "Request with headers", @@ -54,12 +53,11 @@ func TestRecordedRequest_Serialize(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\nAccept: application/xml\nContent-Type: application/json\n\n\n", + expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\nAccept: application/xml\nContent-Type: application/json\n\n\n", }, { name: "Request with body", @@ -67,25 +65,11 @@ func TestRecordedRequest_Serialize(t *testing.T) { Request: "POST /data HTTP/1.1", Header: http.Header{}, Body: []byte("{\"key\": \"value\"}"), - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nPOST /data HTTP/1.1\n\n\n{\"key\": \"value\"}", - }, - { - name: "Request with previous request SHA256 sum", - request: RecordedRequest{ - Request: "GET / HTTP/1.1", - Header: http.Header{}, - Body: []byte{}, - PreviousRequest: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", - ServerAddress: "", - Port: 0, - Protocol: "", - }, - expected: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\n\n\n", + expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\nPOST /data HTTP/1.1\n\n\n{\"key\": \"value\"}", }, } @@ -121,7 +105,6 @@ func TestNewRecordedRequest(t *testing.T) { Request: "POST http://example.com/test HTTP/1.1", Header: http.Header{"Content-Type": []string{"application/json"}}, Body: []byte("test body"), - PreviousRequest: HeadSHA, ServerAddress: "example.com", Port: 443, Protocol: "https", @@ -143,7 +126,6 @@ func TestNewRecordedRequest(t *testing.T) { Request: "GET http://example.com/test HTTP/1.1", Header: http.Header{}, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "example.com", Port: 443, Protocol: "https", @@ -168,7 +150,7 @@ func TestNewRecordedRequest(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - recordedRequest, err := NewRecordedRequest(tc.request, HeadSHA, tc.cfg) + recordedRequest, err := NewRecordedRequest(tc.request, tc.cfg) if tc.expectedErr { require.Error(t, err) @@ -179,7 +161,6 @@ func TestNewRecordedRequest(t *testing.T) { require.Equal(t, tc.expected.Request, recordedRequest.Request) require.Equal(t, tc.expected.Header, recordedRequest.Header) require.Equal(t, tc.expected.Body, recordedRequest.Body) - require.Equal(t, tc.expected.PreviousRequest, recordedRequest.PreviousRequest) }) } } @@ -200,7 +181,6 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -220,7 +200,6 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Authorization": []string{"Bearer token"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -238,7 +217,6 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Accept": []string{"application/xml"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -257,7 +235,6 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -289,7 +266,6 @@ func TestRecordedRequest_Deserialize(t *testing.T) { Request: "GET / HTTP/1.1", Header: http.Header{"Accept": []string{"application/xml"}, "Content-Type": []string{"application/json"}}, Body: []byte("{\"key\": \"value\"}"), - PreviousRequest: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", ServerAddress: "example.com", Port: 8080, Protocol: "http", @@ -344,7 +320,6 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{"random test name"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -360,7 +335,6 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{""}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -376,7 +350,6 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{"../invalid_name"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -393,7 +366,6 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, - PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", From dc6e326017ae7beb0907ae025166fcd931771d65 Mon Sep 17 00:00:00 2001 From: kaituo Date: Mon, 7 Jul 2025 21:48:11 +0000 Subject: [PATCH 03/11] fix test --- internal/replay/replay_http_server.go | 3 --- internal/store/store_test.go | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index 295483d..c280dbf 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -145,10 +145,7 @@ func (r *ReplayHTTPServer) loadResponse(fileName string, shaSum string) (*store. } return nil, fmt.Errorf("error while reading file: %w", err) } - - // Trim whitespace, including the trailing newline, from the line. trimmedLine := strings.TrimSpace(line) - parts := strings.Fields(trimmedLine) if len(parts) != 2 { continue diff --git a/internal/store/store_test.go b/internal/store/store_test.go index c198389..9c70909 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -339,7 +339,7 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { Port: 0, Protocol: "", }, - expected: "f824dd099907ed4549822de827b075a7578baadebf08c5bc7303ead90a8f9ff7", + expected: "b1c71b9c6adb2be16273a2f468e163f4110f73909546430cb19c40c2d61fdc4e", expectedErr: false, }, { @@ -370,7 +370,7 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { Port: 0, Protocol: "", }, - expected: "fc060aea9a2bf35da16ed18c6be577ca64d0f91d681d5db385082df61ecf4ccf", + expected: "aa6623bf83220756d0bb2ec0cfd30bfae64d44feea5c3f48ee28d1988e9579bf", expectedErr: false, }, } From 4db8bd4e0973c47a665a06124db55f56ffde91f6 Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 00:34:32 +0000 Subject: [PATCH 04/11] revert previous request's shaSum --- internal/record/recording_https_proxy.go | 8 ++++- internal/replay/replay_http_server.go | 4 ++- internal/store/store.go | 19 +++++++++-- internal/store/store_test.go | 42 ++++++++++++++++++++---- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index cdaa21b..1e70ed4 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -32,6 +32,7 @@ import ( ) type RecordingHTTPSProxy struct { + prevRequestSHA string config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -39,12 +40,17 @@ type RecordingHTTPSProxy struct { func NewRecordingHTTPSProxy(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *RecordingHTTPSProxy { return &RecordingHTTPSProxy{ + prevRequestSHA: store.HeadSHA, config: cfg, recordingDir: recordingDir, redactor: redactor, } } +func (r *RecordingHTTPSProxy) ResetChain() { + r.prevRequestSHA = store.HeadSHA +} + func (r *RecordingHTTPSProxy) Start() error { addr := fmt.Sprintf(":%d", r.config.SourcePort) server := &http.Server{ @@ -100,7 +106,7 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req } func (r *RecordingHTTPSProxy) redactRequest(req *http.Request) (*store.RecordedRequest, error) { - recordedRequest, err := store.NewRecordedRequest(req, *r.config) + recordedRequest, err := store.NewRecordedRequest(req, r.prevRequestSHA, *r.config) if err != nil { return recordedRequest, err } diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index c280dbf..07f6d82 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -34,6 +34,7 @@ import ( ) type ReplayHTTPServer struct { + prevRequestSHA string config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -41,6 +42,7 @@ type ReplayHTTPServer struct { func NewReplayHTTPServer(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *ReplayHTTPServer { return &ReplayHTTPServer{ + prevRequestSHA: store.HeadSHA, config: cfg, recordingDir: recordingDir, redactor: redactor, @@ -108,7 +110,7 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques } func (r *ReplayHTTPServer) createRedactedRequest(req *http.Request) (*store.RecordedRequest, error) { - recordedRequest, err := store.NewRecordedRequest(req, *r.config) + recordedRequest, err := store.NewRecordedRequest(req, r.prevRequestSHA, *r.config) if err != nil { return nil, err } diff --git a/internal/store/store.go b/internal/store/store.go index 9364cd0..1e754ed 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -30,17 +30,20 @@ import ( "github.com/google/test-server/internal/config" ) +const HeadSHA = "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d" + type RecordedRequest struct { Request string Header http.Header - Body []byte + Body []byte + PreviousRequest string // The sha256 sum of the previous request in the chain. ServerAddress string Port int64 Protocol string } // NewRecordedRequest creates a RecordedRequest from an http.Request. -func NewRecordedRequest(req *http.Request, cfg config.EndpointConfig) (*RecordedRequest, error) { +func NewRecordedRequest(req *http.Request, previousRequest string, cfg config.EndpointConfig) (*RecordedRequest, error) { // Read the body. body, err := readBody(req) if err != nil { @@ -58,6 +61,7 @@ func NewRecordedRequest(req *http.Request, cfg config.EndpointConfig) (*Recorded Request: request, Header: header, Body: body, + PreviousRequest: previousRequest, ServerAddress: cfg.TargetHost, Port: cfg.TargetPort, Protocol: cfg.TargetType, @@ -105,7 +109,8 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { // Serialize the request. // // The serialization format is as follows: -// - The first line is the server address. +// - The first line is the sha256 of the previous request as a hex string. +// - Next is the server address. // - Next is the port. // - Next is the protocol. // - Next is a line of 80 asterisks. @@ -116,6 +121,10 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { func (r *RecordedRequest) Serialize() string { var builder bytes.Buffer + // Format the SHA256 sum of the previous request. + builder.WriteString(r.PreviousRequest) + builder.WriteString("\n") + builder.WriteString(fmt.Sprintf("Server Address: %s\n", r.ServerAddress)) builder.WriteString(fmt.Sprintf("Port: %d\n", r.Port)) @@ -153,6 +162,9 @@ func Deserialize(data string) (*RecordedRequest, error) { if len(lines) < 6 { return nil, fmt.Errorf("invalid serialized data: not enough lines") } + + previousRequest := lines[0] + serverAddress := strings.TrimPrefix(lines[1], "Server Address: ") portString := strings.TrimPrefix(lines[2], "Port: ") protocol := strings.TrimPrefix(lines[3], "Protocol: ") @@ -194,6 +206,7 @@ func Deserialize(data string) (*RecordedRequest, error) { Request: request, Header: headers, Body: body, + PreviousRequest: previousRequest, ServerAddress: serverAddress, Port: int64(port), Protocol: protocol, diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 9c70909..7e247f5 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -38,11 +38,12 @@ func TestRecordedRequest_Serialize(t *testing.T) { Request: "", Header: http.Header{}, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\n\n\n\n", + expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\n\n\n\n", }, { name: "Request with headers", @@ -53,11 +54,12 @@ func TestRecordedRequest_Serialize(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\nAccept: application/xml\nContent-Type: application/json\n\n\n", + expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\nAccept: application/xml\nContent-Type: application/json\n\n\n", }, { name: "Request with body", @@ -65,11 +67,25 @@ func TestRecordedRequest_Serialize(t *testing.T) { Request: "POST /data HTTP/1.1", Header: http.Header{}, Body: []byte("{\"key\": \"value\"}"), + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: "Server Address: \nPort: 0\nProtocol: \n********************************************************************************\nPOST /data HTTP/1.1\n\n\n{\"key\": \"value\"}", + expected: HeadSHA + "\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nPOST /data HTTP/1.1\n\n\n{\"key\": \"value\"}", + }, + { + name: "Request with previous request SHA256 sum", + request: RecordedRequest{ + Request: "GET / HTTP/1.1", + Header: http.Header{}, + Body: []byte{}, + PreviousRequest: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + ServerAddress: "", + Port: 0, + Protocol: "", + }, + expected: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nServer Address: \nPort: 0\nProtocol: \n********************************************************************************\nGET / HTTP/1.1\n\n\n", }, } @@ -105,6 +121,7 @@ func TestNewRecordedRequest(t *testing.T) { Request: "POST http://example.com/test HTTP/1.1", Header: http.Header{"Content-Type": []string{"application/json"}}, Body: []byte("test body"), + PreviousRequest: HeadSHA, ServerAddress: "example.com", Port: 443, Protocol: "https", @@ -126,6 +143,7 @@ func TestNewRecordedRequest(t *testing.T) { Request: "GET http://example.com/test HTTP/1.1", Header: http.Header{}, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "example.com", Port: 443, Protocol: "https", @@ -150,7 +168,7 @@ func TestNewRecordedRequest(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - recordedRequest, err := NewRecordedRequest(tc.request, tc.cfg) + recordedRequest, err := NewRecordedRequest(tc.request, HeadSHA, tc.cfg) if tc.expectedErr { require.Error(t, err) @@ -161,6 +179,7 @@ func TestNewRecordedRequest(t *testing.T) { require.Equal(t, tc.expected.Request, recordedRequest.Request) require.Equal(t, tc.expected.Header, recordedRequest.Header) require.Equal(t, tc.expected.Body, recordedRequest.Body) + require.Equal(t, tc.expected.PreviousRequest, recordedRequest.PreviousRequest) }) } } @@ -181,6 +200,7 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -200,6 +220,7 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Authorization": []string{"Bearer token"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -217,6 +238,7 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Accept": []string{"application/xml"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -235,6 +257,7 @@ func TestRecordedRequest_RedactHeaders(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -266,6 +289,7 @@ func TestRecordedRequest_Deserialize(t *testing.T) { Request: "GET / HTTP/1.1", Header: http.Header{"Accept": []string{"application/xml"}, "Content-Type": []string{"application/json"}}, Body: []byte("{\"key\": \"value\"}"), + PreviousRequest: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", ServerAddress: "example.com", Port: 8080, Protocol: "http", @@ -320,6 +344,7 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{"random test name"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -335,11 +360,12 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{""}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: "b1c71b9c6adb2be16273a2f468e163f4110f73909546430cb19c40c2d61fdc4e", + expected: "f824dd099907ed4549822de827b075a7578baadebf08c5bc7303ead90a8f9ff7", expectedErr: false, }, { @@ -350,6 +376,7 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Test-Name": []string{"../invalid_name"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", @@ -366,11 +393,12 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { "Content-Type": []string{"application/json"}, }, Body: []byte{}, + PreviousRequest: HeadSHA, ServerAddress: "", Port: 0, Protocol: "", }, - expected: "aa6623bf83220756d0bb2ec0cfd30bfae64d44feea5c3f48ee28d1988e9579bf", + expected: "fc060aea9a2bf35da16ed18c6be577ca64d0f91d681d5db385082df61ecf4ccf", expectedErr: false, }, } @@ -391,4 +419,4 @@ type errorReader struct{} func (e *errorReader) Read(p []byte) (n int, err error) { return 0, fmt.Errorf("simulated error") -} +} \ No newline at end of file From f87bd26f26933fa0f69c47eb748ecd1849a5e9ab Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 03:34:57 +0000 Subject: [PATCH 05/11] handle replay file with single request or multiple requests --- internal/record/recording_https_proxy.go | 15 ++++++++++++--- internal/replay/replay_http_server.go | 6 ++++++ internal/store/store.go | 19 ++++++++++--------- internal/store/store_test.go | 2 +- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index 1e70ed4..6eca1e9 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -175,8 +175,16 @@ func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp recordPath := filepath.Join(r.recordingDir, fileName+".http.log") shaSum := recReq.ComputeSum() - // Open the file in append mode, create it if it doesn't exist. - file, err := os.OpenFile(recordPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + var fileMode int + if fileName == shaSum { + // This is a single-request file, so overwrite it completely. + fileMode = os.O_TRUNC + } else { + // This file groups the requests by test-name, so append to it. + fileMode = os.O_APPEND + } + file, err := os.OpenFile(recordPath, fileMode|os.O_CREATE|os.O_WRONLY , 0644) if err != nil { return err } @@ -204,7 +212,8 @@ func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp if err != nil { return err } - + // Update previous request's sha sum. + r.prevRequestSHA = shaSum return nil } diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index 07f6d82..fb91efc 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -107,6 +107,12 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques fmt.Printf("Error writing response: %v\n", err) panic(err) } + + if fileName == shaSum { + r.ResetChain + } + // Update previous request's sha sum. + r.prevRequestSHA = shaSum } func (r *ReplayHTTPServer) createRedactedRequest(req *http.Request) (*store.RecordedRequest, error) { diff --git a/internal/store/store.go b/internal/store/store.go index 1e754ed..60e5997 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -33,13 +33,13 @@ import ( const HeadSHA = "b4d6e60a9b97e7b98c63df9308728c5c88c0b40c398046772c63447b94608b4d" type RecordedRequest struct { - Request string - Header http.Header + Request string + Header http.Header Body []byte - PreviousRequest string // The sha256 sum of the previous request in the chain. - ServerAddress string - Port int64 - Protocol string + PreviousRequest string // The sha256 sum of the previous request in the chain. + ServerAddress string + Port int64 + Protocol string } // NewRecordedRequest creates a RecordedRequest from an http.Request. @@ -101,7 +101,8 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { return "", fmt.Errorf("test name: %s contains illegal sequence '../'", testName) } if testName != "" { - return testName, nil + fileName := strings.ReplaceAll(testName, " ", "_") + return fileName, nil } return r.ComputeSum(), nil } @@ -119,7 +120,7 @@ func (r *RecordedRequest) GetRecordingFileName() (string, error) { // - Next, there are 2 empty lines. // - The rest of the file is the body content. func (r *RecordedRequest) Serialize() string { - var builder bytes.Buffer + var builder strings.Builder // Format the SHA256 sum of the previous request. builder.WriteString(r.PreviousRequest) @@ -151,7 +152,7 @@ func (r *RecordedRequest) Serialize() string { } builder.WriteString("\n\n") - builder.Write(r.Body) + builder.WriteString(string(r.Body)) return builder.String() } diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 7e247f5..b37ff6e 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -419,4 +419,4 @@ type errorReader struct{} func (e *errorReader) Read(p []byte) (n int, err error) { return 0, fmt.Errorf("simulated error") -} \ No newline at end of file +} From 571f761f56387e871592ebc0e81750c416f4f37b Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 03:42:28 +0000 Subject: [PATCH 06/11] fix build --- internal/replay/replay_http_server.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index fb91efc..0766468 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -107,10 +107,6 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques fmt.Printf("Error writing response: %v\n", err) panic(err) } - - if fileName == shaSum { - r.ResetChain - } // Update previous request's sha sum. r.prevRequestSHA = shaSum } From d5f887cec5c14b82d82ff7e8b5d7265fc4506e29 Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 03:43:22 +0000 Subject: [PATCH 07/11] fix build --- internal/store/store_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/store/store_test.go b/internal/store/store_test.go index b37ff6e..0552807 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -349,7 +349,7 @@ func TestRecordedRequest_GetRecordFileName(t *testing.T) { Port: 0, Protocol: "", }, - expected: "random test name", + expected: "random_test_name", expectedErr: false, }, { From 902c58f01d4faa77a08187acd8124d051257e441 Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 16:15:23 +0000 Subject: [PATCH 08/11] minor change --- internal/record/recording_https_proxy.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index 6eca1e9..d6a7d52 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -97,12 +97,13 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req } err = r.recordResponse(recReq, resp, fileName, respBody) - if err != nil { fmt.Printf("Error recording response: %v\n", err) http.Error(w, fmt.Sprintf("Error recording response: %v", err), http.StatusInternalServerError) return } + // Update previous request's sha sum. + r.prevRequestSHA = shaSum } func (r *RecordingHTTPSProxy) redactRequest(req *http.Request) (*store.RecordedRequest, error) { @@ -212,8 +213,6 @@ func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp if err != nil { return err } - // Update previous request's sha sum. - r.prevRequestSHA = shaSum return nil } From d433c259caf47d259fc6261c9e43beabd0511fc8 Mon Sep 17 00:00:00 2001 From: kaituo Date: Tue, 8 Jul 2025 16:18:45 +0000 Subject: [PATCH 09/11] fix build --- internal/record/recording_https_proxy.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index d6a7d52..ba56d44 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -95,8 +95,8 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req http.Error(w, fmt.Sprintf("Error proxying request: %v", err), http.StatusInternalServerError) return } - - err = r.recordResponse(recReq, resp, fileName, respBody) + shaSum := recReq.ComputeSum() + err = r.recordResponse(recReq, resp, fileName, shaSum, respBody) if err != nil { fmt.Printf("Error recording response: %v\n", err) http.Error(w, fmt.Sprintf("Error recording response: %v", err), http.StatusInternalServerError) @@ -168,14 +168,12 @@ func (r *RecordingHTTPSProxy) proxyRequest(w http.ResponseWriter, req *http.Requ return resp, respBodyBytes, nil } -func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp *http.Response, fileName string, body []byte) error { +func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp *http.Response, fileName string, shaSum string, body []byte) error { recordedResponse, err := store.NewRecordedResponse(resp, body) if err != nil { return err } recordPath := filepath.Join(r.recordingDir, fileName+".http.log") - shaSum := recReq.ComputeSum() - var fileMode int if fileName == shaSum { From 6d2860cca700934b57646ee2c3f739140d2408f8 Mon Sep 17 00:00:00 2001 From: kaituo Date: Sat, 12 Jul 2025 01:46:30 +0000 Subject: [PATCH 10/11] Introduce seenFiles map to reset test state when a given file first time appear --- internal/record/recording_https_proxy.go | 18 ++++++---- internal/replay/replay_http_server.go | 42 ++++++++++++++---------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index ba56d44..50afc9b 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -33,6 +33,7 @@ import ( type RecordingHTTPSProxy struct { prevRequestSHA string + seenFiles map[string]struct{} config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -41,6 +42,7 @@ type RecordingHTTPSProxy struct { func NewRecordingHTTPSProxy(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *RecordingHTTPSProxy { return &RecordingHTTPSProxy{ prevRequestSHA: store.HeadSHA, + seenFiles: make(map[string]struct{}), config: cfg, recordingDir: recordingDir, redactor: redactor, @@ -82,6 +84,10 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req http.Error(w, fmt.Sprintf("Invalid recording file name: %v", err), http.StatusInternalServerError) return } + if _, ok := r.seenFiles[fileName]; !ok { + // Reset to HeadSHA when first time seen a request from the given file. + recReq.PreviousRequest=store.HeadSHA + } if req.Header.Get("Upgrade") == "websocket" { fmt.Printf("Upgrading connection to websocket...\n") @@ -102,8 +108,8 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req http.Error(w, fmt.Sprintf("Error recording response: %v", err), http.StatusInternalServerError) return } - // Update previous request's sha sum. r.prevRequestSHA = shaSum + r.seenFiles[fileName] = struct{}{} } func (r *RecordingHTTPSProxy) redactRequest(req *http.Request) (*store.RecordedRequest, error) { @@ -175,12 +181,10 @@ func (r *RecordingHTTPSProxy) recordResponse(recReq *store.RecordedRequest, resp } recordPath := filepath.Join(r.recordingDir, fileName+".http.log") - var fileMode int - if fileName == shaSum { - // This is a single-request file, so overwrite it completely. - fileMode = os.O_TRUNC - } else { - // This file groups the requests by test-name, so append to it. + // Default to overwriting the file, assuming it's a new file to record. + fileMode := os.O_TRUNC + // If we've seen requests with the same file name before, change the mode to append. + if _, ok := r.seenFiles[fileName]; ok { fileMode = os.O_APPEND } file, err := os.OpenFile(recordPath, fileMode|os.O_CREATE|os.O_WRONLY , 0644) diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index 0766468..62817af 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -35,6 +35,7 @@ import ( type ReplayHTTPServer struct { prevRequestSHA string + seenFiles map[string]struct{} config *config.EndpointConfig recordingDir string redactor *redact.Redact @@ -43,6 +44,7 @@ type ReplayHTTPServer struct { func NewReplayHTTPServer(cfg *config.EndpointConfig, recordingDir string, redactor *redact.Redact) *ReplayHTTPServer { return &ReplayHTTPServer{ prevRequestSHA: store.HeadSHA, + seenFiles: make(map[string]struct{}), config: cfg, recordingDir: recordingDir, redactor: redactor, @@ -80,6 +82,10 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques http.Error(w, fmt.Sprintf("Invalid recording file name: %v", err), http.StatusInternalServerError) return } + if _, ok := r.seenFiles[fileName]; !ok { + // Reset to HeadSHA when first time seen request from the given file. + redactedReq.PreviousRequest=store.HeadSHA + } if req.Header.Get("Upgrade") == "websocket" { fmt.Printf("Upgrading connection to websocket...\n") @@ -107,8 +113,8 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques fmt.Printf("Error writing response: %v\n", err) panic(err) } - // Update previous request's sha sum. r.prevRequestSHA = shaSum + r.seenFiles[fileName] = struct{}{} } func (r *ReplayHTTPServer) createRedactedRequest(req *http.Request) (*store.RecordedRequest, error) { @@ -158,24 +164,26 @@ func (r *ReplayHTTPServer) loadResponse(fileName string, shaSum string) (*store. fileKey := parts[0] sizeStr := parts[1] - // 3. Check if the key on this line matches our target shaSum. - if fileKey == expectedKey { - size, err := strconv.Atoi(sizeStr) - if err != nil { - return nil, fmt.Errorf("invalid size format on delimiter line: '%s'", trimmedLine) - } - fmt.Printf("Bytes to load: %d\n", size) - - if size < 0 { - return nil, fmt.Errorf("invalid negative size on delimiter line: '%s'", trimmedLine) - } + size, err := strconv.Atoi(sizeStr) + if err != nil { + return nil, fmt.Errorf("invalid size format on delimiter line: '%s'", trimmedLine) + } + fmt.Printf("Bytes to load: %d\n", size) + if size < 0 { + return nil, fmt.Errorf("invalid negative size on delimiter line: '%s'", trimmedLine) + } - // 4. We can read the exact number of bytes for the payload. - data := make([]byte, size) - if _, err := io.ReadFull(reader, data); err != nil { - return nil, fmt.Errorf("failed to read %d bytes after delimiter: %w", size, err) - } + // 3. Read the exact number of bytes for the payload. + data := make([]byte, size) + if _, err := io.ReadFull(reader, data); err != nil { + return nil, fmt.Errorf("failed to read %d bytes after delimiter: %w", size, err) + } + + // 4. Return the response when it matches our target shaSum. + if fileKey == expectedKey { return store.DeserializeResponse(data) + } else { + continue } } } From 558dc6b3b79e13dfaa8d176e706802fae64626a7 Mon Sep 17 00:00:00 2001 From: kaituo Date: Mon, 21 Jul 2025 22:45:27 +0000 Subject: [PATCH 11/11] skip reset shasum head when test name is not set --- internal/record/recording_https_proxy.go | 4 +++- internal/replay/replay_http_server.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/record/recording_https_proxy.go b/internal/record/recording_https_proxy.go index 50afc9b..b0c180a 100644 --- a/internal/record/recording_https_proxy.go +++ b/internal/record/recording_https_proxy.go @@ -108,7 +108,9 @@ func (r *RecordingHTTPSProxy) handleRequest(w http.ResponseWriter, req *http.Req http.Error(w, fmt.Sprintf("Error recording response: %v", err), http.StatusInternalServerError) return } - r.prevRequestSHA = shaSum + if (fileName != shaSum) { + r.prevRequestSHA = shaSum + } r.seenFiles[fileName] = struct{}{} } diff --git a/internal/replay/replay_http_server.go b/internal/replay/replay_http_server.go index 62817af..f799915 100644 --- a/internal/replay/replay_http_server.go +++ b/internal/replay/replay_http_server.go @@ -113,7 +113,9 @@ func (r *ReplayHTTPServer) handleRequest(w http.ResponseWriter, req *http.Reques fmt.Printf("Error writing response: %v\n", err) panic(err) } - r.prevRequestSHA = shaSum + if (fileName != shaSum) { + r.prevRequestSHA = shaSum + } r.seenFiles[fileName] = struct{}{} }