diff --git a/backend/controller/controller.go b/backend/controller/controller.go index 1c28519982..1ca3d3aa48 100644 --- a/backend/controller/controller.go +++ b/backend/controller/controller.go @@ -170,9 +170,9 @@ func New(ctx context.Context, db *dal.DAL, config Config, runnerScaling scaling. } type HTTPResponse struct { - Status int - Headers map[string][]string - Body json.RawMessage + Status int `json:"status"` + Headers map[string][]string `json:"headers"` + Body json.RawMessage `json:"body"` } // ServeHTTP handles ingress routes. @@ -248,11 +248,17 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + responseBody, err = ingress.ResponseBodyForContentType(response.Headers, response.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + for k, v := range response.Headers { w.Header()[k] = v } w.WriteHeader(response.Status) - responseBody = response.Body + } else { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json; charset=utf-8") diff --git a/backend/controller/ingress/ingress.go b/backend/controller/ingress/ingress.go index de9754ed99..8454900ecb 100644 --- a/backend/controller/ingress/ingress.go +++ b/backend/controller/ingress/ingress.go @@ -61,6 +61,19 @@ func matchSegments(pattern, urlPath string, onMatch func(segment, value string)) return true } +func ResponseBodyForContentType(headers map[string][]string, body []byte) ([]byte, error) { + contentType, hasContentType := headers["Content-Type"] + if !hasContentType || len(contentType) == 0 || contentType[0] == "" || !strings.HasPrefix(contentType[0], "text/") { + return body, nil + } + + var htmlContent string + if err := json.Unmarshal(body, &htmlContent); err != nil { + return nil, err + } + return []byte(htmlContent), nil +} + func ValidateCallBody(body []byte, verbRef *schema.VerbRef, sch *schema.Schema) error { verb := sch.ResolveVerbRef(verbRef) if verb == nil { diff --git a/examples/go/httpingress/httpingress.go b/examples/go/httpingress/httpingress.go index 2af1e04b14..7c08412fe8 100644 --- a/examples/go/httpingress/httpingress.go +++ b/examples/go/httpingress/httpingress.go @@ -102,3 +102,15 @@ func Delete(ctx context.Context, req builtin.HttpRequest[DeleteRequest]) (builti Body: DeleteResponse{}, }, nil } + +type HtmlRequest struct{} + +//ftl:verb +//ftl:ingress http GET /http/html +func Html(ctx context.Context, req builtin.HttpRequest[HtmlRequest]) (builtin.HttpResponse[string], error) { + return builtin.HttpResponse[string]{ + Status: 200, + Headers: map[string][]string{"Content-Type": {"text/html; charset=utf-8"}}, + Body: "