diff --git a/pkg/api/echo.go b/pkg/api/echo.go new file mode 100644 index 0000000..25ed7e5 --- /dev/null +++ b/pkg/api/echo.go @@ -0,0 +1,79 @@ +package api + +import ( + "bytes" + "context" + "io/ioutil" + "net/http" + "github.com/stefanprodan/k8s-podinfo/pkg/version" + "go.uber.org/zap" +) + +func (s *Server) echoHandler(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + s.logger.Error("reading the request body failed", zap.Error(err)) + s.ErrorResponse(w, r, "invalid request body", http.StatusBadRequest) + return + } + + if len(s.config.BackendURL) > 0 { + backendReq, err := http.NewRequest("POST", s.config.BackendURL, bytes.NewReader(body)) + if err != nil { + s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL)) + s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError) + return + } + + // forward headers + copyTracingHeaders(r, backendReq) + + backendReq.Header.Set("X-API-Version", version.VERSION) + backendReq.Header.Set("X-API-Revision", version.REVISION) + + ctx, cancel := context.WithTimeout(backendReq.Context(), s.config.HttpClientTimeout) + defer cancel() + + // call backend + resp, err := http.DefaultClient.Do(backendReq.WithContext(ctx)) + if err != nil { + s.logger.Error("backend call failed", zap.Error(err), zap.String("url", s.config.BackendURL)) + s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError) + return + } + + defer resp.Body.Close() + + // copy error status from backend and exit + if resp.StatusCode >= 400 { + s.ErrorResponse(w, r, "backend error", resp.StatusCode) + return + } + + // forward the received body + rbody, err := ioutil.ReadAll(resp.Body) + if err != nil { + s.logger.Error( + "reading the backend request body failed", + zap.Error(err), + zap.String("url", s.config.BackendURL)) + s.ErrorResponse(w, r, "backend call failed", http.StatusInternalServerError) + return + } + + s.logger.Debug( + "payload received from backend", + zap.String("response", string(rbody)), + zap.String("url", s.config.BackendURL)) + + w.Header().Set("X-Color", s.config.UIColor) + w.WriteHeader(http.StatusAccepted) + w.Write(rbody) + } else { + w.Header().Set("X-Color", s.config.UIColor) + w.WriteHeader(http.StatusAccepted) + w.Write(body) + } +} + + diff --git a/pkg/api/echo_test.go b/pkg/api/echo_test.go new file mode 100644 index 0000000..343789e --- /dev/null +++ b/pkg/api/echo_test.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "testing" + "strings" +) + +func TestEchoHandler(t *testing.T) { + expected := `{"test": true}` + req, err := http.NewRequest("POST", "/api/echo", strings.NewReader(expected)) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + srv := NewMockServer() + handler := http.HandlerFunc(srv.echoHandler) + + handler.ServeHTTP(rr, req) + + // Check the status code is what we expect. + if status := rr.Code; status != http.StatusAccepted { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusAccepted) + } + + // Check the response body is what we expect. + if rr.Body.String() != expected { + t.Fatalf("handler returned unexpected body:\ngot \n%v \nwant \n%s", + rr.Body.String(), expected) + } +}