From 78b1d0e168036e9bf42d6c171af411bddea2bf77 Mon Sep 17 00:00:00 2001 From: Adam Ludes Date: Tue, 15 Oct 2024 20:22:57 +0200 Subject: [PATCH 1/3] fix imposter base path bug Signed-off-by: Adam Ludes --- internal/server/http/imposter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/server/http/imposter.go b/internal/server/http/imposter.go index e5ae638..44b5371 100644 --- a/internal/server/http/imposter.go +++ b/internal/server/http/imposter.go @@ -201,9 +201,9 @@ func (i ImposterFs) unmarshalImposters(imposterConfig ImposterConfig) ([]Imposte return nil, fmt.Errorf("%w: error while unmarshalling imposter's file %s", parseError, imposterConfig.FilePath) } - for i := range imposters { - imposters[i].BasePath = filepath.Dir(imposterConfig.FilePath) - imposters[i].Path = imposterConfig.FilePath + for idx := range imposters { + imposters[idx].BasePath = filepath.Dir(filepath.Join(i.path, imposterConfig.FilePath)) + imposters[idx].Path = imposterConfig.FilePath } return imposters, nil From d6fee49968a464ab743fe5b9c020b471d58e912e Mon Sep 17 00:00:00 2001 From: Adam Ludes Date: Tue, 15 Oct 2024 20:24:57 +0200 Subject: [PATCH 2/3] preload imposter body into memory Signed-off-by: Adam Ludes --- internal/server/http/handler.go | 30 +++++----------------------- internal/server/http/handler_test.go | 10 ++++++++-- internal/server/http/imposter.go | 30 ++++++++++++++++++++++++++++ internal/server/http/server.go | 1 + 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/internal/server/http/handler.go b/internal/server/http/handler.go index cab0ae0..c440397 100644 --- a/internal/server/http/handler.go +++ b/internal/server/http/handler.go @@ -1,14 +1,12 @@ package http import ( - "io" "log" "net/http" - "os" "time" ) -// ImposterHandler create specific handler for the received imposter +// Handler create specific handler for the received imposter func ImposterHandler(i Imposter) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { res := i.NextResponse() @@ -17,7 +15,7 @@ func ImposterHandler(i Imposter) http.HandlerFunc { } writeHeaders(res, w) w.WriteHeader(res.Status) - writeBody(i, res, w) + writeBody(res, w) } } @@ -31,27 +29,9 @@ func writeHeaders(r Response, w http.ResponseWriter) { } } -func writeBody(i Imposter, r Response, w http.ResponseWriter) { - wb := []byte(r.Body) - - if r.BodyFile != nil { - bodyFile := i.CalculateFilePath(*r.BodyFile) - wb = fetchBodyFromFile(bodyFile) - } - w.Write(wb) -} - -func fetchBodyFromFile(bodyFile string) (bytes []byte) { - if _, err := os.Stat(bodyFile); os.IsNotExist(err) { - log.Printf("the body file %s not found\n", bodyFile) - return - } - - f, _ := os.Open(bodyFile) - defer f.Close() - bytes, err := io.ReadAll(f) +func writeBody(r Response, w http.ResponseWriter) { + _, err := w.Write(r.BodyData) if err != nil { - log.Printf("imposible read the file %s: %v\n", bodyFile, err) + log.Printf("error writing body: %v\n", err) } - return } diff --git a/internal/server/http/handler_test.go b/internal/server/http/handler_test.go index ba87c9e..38d4ce3 100644 --- a/internal/server/http/handler_test.go +++ b/internal/server/http/handler_test.go @@ -59,6 +59,7 @@ func TestImposterHandler(t *testing.T) { assert.NoError(t, err) rec := httptest.NewRecorder() + tt.imposter.PopulateBodyData() handler := ImposterHandler(tt.imposter) handler.ServeHTTP(rec, req) @@ -95,6 +96,7 @@ func TestInvalidRequestWithSchema(t *testing.T) { req, err := http.NewRequest("POST", "/gophers", bytes.NewBuffer(tt.request)) assert.Nil(t, err) rec := httptest.NewRecorder() + tt.imposter.PopulateBodyData() handler := ImposterHandler(tt.imposter) handler.ServeHTTP(rec, req) @@ -125,6 +127,7 @@ func TestImposterHandler_MultipleRequests(t *testing.T) { }, } + imp.PopulateBodyData() handler := ImposterHandler(imp) // First request @@ -141,12 +144,15 @@ func TestImposterHandler_MultipleRequests(t *testing.T) { }) t.Run("idempotent", func(t *testing.T) { - handler := ImposterHandler(Imposter{ + imp := Imposter{ Request: Request{Method: "POST", Endpoint: "/gophers"}, Response: Responses{ {Status: http.StatusAccepted, Body: "Accepted"}, }, - }) + } + imp.PopulateBodyData() + + handler := ImposterHandler(imp) // First request rec := httptest.NewRecorder() diff --git a/internal/server/http/imposter.go b/internal/server/http/imposter.go index 44b5371..43c5682 100644 --- a/internal/server/http/imposter.go +++ b/internal/server/http/imposter.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "io/fs" + "log" "os" "path" "path/filepath" @@ -57,6 +58,34 @@ func (i *Imposter) CalculateFilePath(filePath string) string { return path.Join(i.BasePath, filePath) } +func (i *Imposter) PopulateBodyData() { + for idx, resp := range i.Response { + resp.BodyData = []byte(resp.Body) + + if resp.BodyFile != nil { + bodyFile := i.CalculateFilePath(*resp.BodyFile) + resp.BodyData = fetchBodyFromFile(bodyFile) + } + + i.Response[idx] = resp + } +} + +func fetchBodyFromFile(bodyFile string) (bytes []byte) { + if _, err := os.Stat(bodyFile); os.IsNotExist(err) { + log.Printf("the body file %s not found\n", bodyFile) + return + } + + f, _ := os.Open(bodyFile) + defer f.Close() + bytes, err := io.ReadAll(f) + if err != nil { + log.Printf("imposible read the file %s: %v\n", bodyFile, err) + } + return +} + // Request represent the structure of real request type Request struct { Method string `json:"method"` @@ -71,6 +100,7 @@ type Response struct { Status int `json:"status"` Body string `json:"body"` BodyFile *string `json:"bodyFile" yaml:"bodyFile"` + BodyData []byte `json:"-" yaml:"-"` Headers *map[string]string `json:"headers"` Delay ResponseDelay `json:"delay" yaml:"delay"` } diff --git a/internal/server/http/server.go b/internal/server/http/server.go index d334e84..d5a18d3 100644 --- a/internal/server/http/server.go +++ b/internal/server/http/server.go @@ -155,6 +155,7 @@ func (s *Server) Shutdown() error { func (s *Server) addImposterHandler(imposters []Imposter) { for _, imposter := range imposters { + imposter.PopulateBodyData() r := s.router.HandleFunc(imposter.Request.Endpoint, ImposterHandler(imposter)). Methods(imposter.Request.Method). MatcherFunc(MatcherBySchema(imposter)) From 980cd9a75daffdbf51dc3fa8177e6adefba62304 Mon Sep 17 00:00:00 2001 From: Adam Ludes Date: Tue, 15 Oct 2024 20:31:57 +0200 Subject: [PATCH 3/3] Improve error logging in fetchBodyFromFile function Signed-off-by: Adam Ludes --- internal/server/http/imposter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/server/http/imposter.go b/internal/server/http/imposter.go index 43c5682..e6a7f58 100644 --- a/internal/server/http/imposter.go +++ b/internal/server/http/imposter.go @@ -73,7 +73,7 @@ func (i *Imposter) PopulateBodyData() { func fetchBodyFromFile(bodyFile string) (bytes []byte) { if _, err := os.Stat(bodyFile); os.IsNotExist(err) { - log.Printf("the body file %s not found\n", bodyFile) + log.Printf("ERROR: the body file %s not found\n", bodyFile) return } @@ -81,7 +81,7 @@ func fetchBodyFromFile(bodyFile string) (bytes []byte) { defer f.Close() bytes, err := io.ReadAll(f) if err != nil { - log.Printf("imposible read the file %s: %v\n", bodyFile, err) + log.Printf("ERROR: imposible read the file %s: %v\n", bodyFile, err) } return }