diff --git a/api/pkg/apis/v1alpha1/vendors/margo/device-agent-vendor.go b/api/pkg/apis/v1alpha1/vendors/margo/device-agent-vendor.go index 500cf84fb..3e7d7d004 100644 --- a/api/pkg/apis/v1alpha1/vendors/margo/device-agent-vendor.go +++ b/api/pkg/apis/v1alpha1/vendors/margo/device-agent-vendor.go @@ -751,15 +751,13 @@ func (self *DeviceAgentVendor) downloadBundle(request v1alpha2.COARequest) v1alp "Serving bundle for device %s with verified digest %s (%d bytes)", deviceClientId, actualDigest, len(bundleData)) + // Set headers directly in fasthttp context + setFastHTTPResponseHeaders(request.Context, "application/vnd.margo.bundle.v1+tar+gzip", actualDigest) + // Return with proper headers return createSuccessResponseWithHeaders(span, "application/vnd.margo.bundle.v1+tar+gzip", - map[string]string{ - "Content-Type": "application/vnd.margo.bundle.v1+tar+gzip", - "Cache-Control": "public, max-age=31536000, immutable", - "ETag": fmt.Sprintf("\"%s\"", actualDigest), // Quoted ETag - "Vary": "Accept-Encoding", - }, + nil, v1alpha2.OK, &bundleData, ) @@ -900,15 +898,13 @@ func (self *DeviceAgentVendor) downloadDeployment(request v1alpha2.COARequest) v "Serving deployment %s with verified digest %s (%d bytes)", deploymentId, actualDigest, len(yamlContent)) + // Set headers directly in fasthttp context + setFastHTTPResponseHeaders(request.Context, "application/yaml", actualDigest) + // Return with proper headers return createSuccessResponseWithHeaders(span, "application/yaml", - map[string]string{ - "Content-Type": "application/yaml", - "Cache-Control": "public, max-age=31536000, immutable", - "ETag": fmt.Sprintf("\"%s\"", actualDigest), // Quoted ETag - "Vary": "Accept-Encoding", - }, + nil, v1alpha2.OK, &yamlContent, ) diff --git a/api/pkg/apis/v1alpha1/vendors/margo/helpers.go b/api/pkg/apis/v1alpha1/vendors/margo/helpers.go index b143ce3f6..8520c832b 100644 --- a/api/pkg/apis/v1alpha1/vendors/margo/helpers.go +++ b/api/pkg/apis/v1alpha1/vendors/margo/helpers.go @@ -7,6 +7,7 @@ import ( "time" "github.com/eclipse-symphony/symphony/coa/pkg/logger" + "github.com/valyala/fasthttp" "gopkg.in/yaml.v2" "github.com/eclipse-symphony/symphony/coa/pkg/apis/v1alpha2" @@ -15,6 +16,8 @@ import ( "go.opentelemetry.io/otel/trace" ) +var helperVendorLogger = logger.NewLogger("coa.runtime") + // Helper method for error responses func createErrorResponse(logger logger.Logger, span trace.Span, err error, message string, errorType v1alpha2.State) v1alpha2.COAResponse { logger.InfofCtx(context.Background(), "err: %s, msg: %s", err.Error(), message) @@ -322,3 +325,21 @@ func createSuccessResponseWithHeadersSimple[T any]( return observ_utils.CloseSpanWithCOAResponse(span, coaResponse), nil } + +// setFastHTTPResponseHeaders sets standard cache and artifact headers (ETag, Cache-Control, Content-Type) directly on FastHTTP context +func setFastHTTPResponseHeaders( + ctx context.Context, + contentType string, + digest string, +) { + if fhCtx, ok := ctx.Value(v1alpha2.COAFastHTTPContextKey).(*fasthttp.RequestCtx); ok { + fhCtx.Response.Header.Set("Content-Type", contentType) + fhCtx.Response.Header.Set("Cache-Control", "public, max-age=31536000, immutable") + fhCtx.Response.Header.Set("ETag", fmt.Sprintf("\"%s\"", digest)) // Quoted ETag per spec + fhCtx.Response.Header.Set("Vary", "Accept-Encoding") + + helperVendorLogger.InfofCtx(ctx, "Set response headers directly - ETag: %s", digest) + } else { + helperVendorLogger.WarnfCtx(ctx, "Could not access fasthttp context to set headers") + } +}