Inject response header

What if you require a routing policy to inject a header from an inbound request into a response header?

Setup

This guide assumes that you have installed Gloo Edge into the gloo-system namespace and that glooctl is installed on your machine. We will also use the jq command line utility to pretty print JSON strings.

We will need an upstream service to serve as the target for the requests that we will send to test the Gloo Edge configurations in this tutorial. To this end, we will use the publicly available Postman Echo service. It exposes a set of endpoints that are very useful for inspecting both the requests sent upstream and the resulting responses; please refer to the official documentation for more information about the service.

Let’s create a static upstream to represent the postman-echo.com remote service.

apiVersion: gloo.solo.io/v1
kind: Upstream
metadata:
  name: postman-echo
  namespace: gloo-system
spec:
  static:
    hosts:
    - addr: postman-echo.com
      port: 80

Let’s also create a simple Virtual Service that matches any path and routes all traffic to our Upstream:


apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: inject-response-header
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matchers:
       - prefix: /
      routeAction:
        single:
          upstream:
            name: postman-echo
            namespace: gloo-system

Let’s test that the configuration was correctly picked up by Gloo Edge by executing the following command:

curl -v -H "x-solo-hdr1: val1" $(glooctl proxy url)/get -i

You should get a response with status 200 and a JSON body similar to the one below. Note that the x-solo-hdr1 header is in the payload response from postman-echo, but it is not included in the response headers reported by curl.

HTTP/1.1 200 OK
date: Thu, 03 Mar 2022 22:29:21 GMT
content-type: application/json; charset=utf-8
content-length: 349
vary: Accept-Encoding
x-envoy-upstream-service-time: 42
server: envoy

{"args":{},"headers":{"x-forwarded-proto":"http","x-forwarded-port":"80","host":"35.185.51.108","x-amzn-trace-id":"Root=1-62214141-5de08d0b3bae549e7cea830e","user-agent":"curl/7.77.0","accept":"*/*","x-solo-hdr1":"val1","x-request-id":"3ab8790f-d392-4e37-93f1-ecb0e0d6ce41","x-envoy-expected-rq-timeout-ms":"15000"},"url":"http://35.185.51.108/get"}

Injecting the response header

As you can see from the response above, the upstream service echoes the JSON payload we included in our request inside the data response body attribute. We will now configure Gloo Edge to change the response status to 400 if the data.error attribute is present; otherwise, the original status code should be preserved.

Update the Virtual Service

To implement this behavior, we need to add a responseTransformation stanza to our original Virtual Service definition. Note that the request_header function is used in an Inja template to extract the value of the x-solo-hdr header from the request. Then it injects that value into a new response header x-solo-resp-hdr1.

apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: inject-response-header
  namespace: gloo-system
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matchers:
       - prefix: /
      routeAction:
        single:
          upstream:
            name: postman-echo
            namespace: gloo-system
    options:
      transformations:
        responseTransformation:
          transformationTemplate:
            headers:
              x-solo-resp-hdr1:
                text: '{{ request_header("x-solo-hdr1") }}'

Test the modified configuration

We’ll test our modified Virtual Service by issuing the same curl command as before:

curl -H "x-solo-hdr1: val1" $(glooctl proxy url)/get -i

This should yield something similar to the following output. Note that in the curl response, there is a new response header: x-solo-resp-hdr1: val1

HTTP/1.1 200 OK
date: Thu, 03 Mar 2022 22:40:27 GMT
content-type: application/json; charset=utf-8
content-length: 349
vary: Accept-Encoding
x-envoy-upstream-service-time: 45
x-solo-resp-hdr1: val1
server: envoy

{"args":{},"headers":{"x-forwarded-proto":"http","x-forwarded-port":"80","host":"35.185.51.108","x-amzn-trace-id":"Root=1-622143db-7397fdb03f893d082bfa5028","user-agent":"curl/7.77.0","accept":"*/*","x-solo-hdr1":"val1","x-request-id":"ea032e6b-5536-49b9-a5d6-55f70bf480a5","x-envoy-expected-rq-timeout-ms":"15000"},"url":"http://35.185.51.108/get"}

Congratulations! You have successfully used a response transformation to inject the value of a request header into a new response header.

Cleanup

To cleanup the resources created in this tutorial you can run the following commands:

kubectl delete virtualservice -n gloo-system inject-response-header
kubectl delete upstream -n gloo-system postman-echo