diff --git a/pkg/service/handlers/handlers_proxy.go b/pkg/service/handlers/handlers_proxy.go
index 79a37e1..152fbf4 100644
--- a/pkg/service/handlers/handlers_proxy.go
+++ b/pkg/service/handlers/handlers_proxy.go
@@ -48,6 +48,13 @@ func (s *Server) ServeProxy(target *url.URL) http.HandlerFunc {
lp.RecordEnabled = s.recordEnabled
lp.SetRecorder(s.recorder)
+ // Capture request body for recording, as it will be consumed by the proxy
+ var reqBody []byte
+ if r.Body != nil {
+ reqBody, _ = io.ReadAll(r.Body)
+ r.Body = io.NopCloser(bytes.NewBuffer(reqBody))
+ }
+
rp := httputil.NewSingleHostReverseProxy(target)
rp.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
@@ -75,6 +82,11 @@ func (s *Server) ServeProxy(target *url.URL) http.HandlerFunc {
res.Header["ETag"] = etags
}
+ // Restore captured request body for the recorder
+ if reqBody != nil {
+ res.Request.Body = io.NopCloser(bytes.NewBuffer(reqBody))
+ }
+
lp.LogResponse(res)
return nil
diff --git a/pkg/service/handlers/handlers_proxy_test.go b/pkg/service/handlers/handlers_proxy_test.go
new file mode 100644
index 0000000..06c3fff
--- /dev/null
+++ b/pkg/service/handlers/handlers_proxy_test.go
@@ -0,0 +1,96 @@
+package handlers
+
+import (
+ "bytes"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/gesellix/bose-soundtouch/pkg/service/datastore"
+ "github.com/gesellix/bose-soundtouch/pkg/service/proxy"
+)
+
+func TestHandleProxyRequest_RequestBodyRecording(t *testing.T) {
+ t.Setenv("RECORDER_ASYNC", "false")
+ tmpDir, err := os.MkdirTemp("", "proxy-request-body-test")
+ if err != nil {
+ t.Fatalf("failed to create temp dir: %v", err)
+ }
+ defer os.RemoveAll(tmpDir)
+
+ // Start a backend server to receive the proxied request
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Read the body to ensure it's consumed
+ _, _ = io.ReadAll(r.Body)
+ w.Header().Set("Content-Type", "application/xml")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("ok"))
+ }))
+ defer backend.Close()
+
+ ds := datastore.NewDataStore(filepath.Join(tmpDir, "test.db"))
+ server := NewServer(ds, nil, "http://localhost:8000", false, false, false, false)
+ server.recordEnabled = true
+ server.proxyLogBody = true
+ recorder := proxy.NewRecorder(tmpDir)
+ server.SetRecorder(recorder)
+
+ // Create a proxy request to the backend
+ requestBody := "data"
+ targetURL := backend.URL
+ proxyPath := "/proxy/" + targetURL
+ req := httptest.NewRequest("POST", proxyPath, bytes.NewBufferString(requestBody))
+ req.Header.Set("Content-Type", "application/xml")
+
+ w := httptest.NewRecorder()
+ server.HandleProxyRequest(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200, got %d", w.Code)
+ }
+
+ // Verify that the interaction was recorded and contains the request body
+ sessionID := recorder.SessionID
+
+ // The recorder uses sanitized segments for the directory.
+ // Since the target URL is http://127.0.0.1:PORT, the path is empty,
+ // so it should be in the "root" directory under the category.
+
+ // We'll search recursively to be sure
+ foundBody := false
+ err = filepath.Walk(filepath.Join(tmpDir, "interactions", sessionID), func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if !info.IsDir() && strings.HasSuffix(path, ".http") {
+ content, err := os.ReadFile(path)
+ if err != nil {
+ return err
+ }
+ if strings.Contains(string(content), requestBody) {
+ foundBody = true
+ }
+ }
+ return nil
+ })
+
+ if err != nil {
+ t.Fatalf("failed to walk interactions dir: %v", err)
+ }
+
+ if !foundBody {
+ t.Errorf("request body %q not found in any recorded interaction file", requestBody)
+ // List all files found for debugging
+ _ = filepath.Walk(filepath.Join(tmpDir, "interactions", sessionID), func(path string, info os.FileInfo, err error) error {
+ if !info.IsDir() {
+ content, _ := os.ReadFile(path)
+ t.Logf("Found file %s with content:\n%s", path, string(content))
+ }
+ return nil
+ })
+ }
+}