Inconsistency between lambdas & http-direct: multiple headers

I have a Datomic Ions app where I need to include multiple cookies in a response. Here’s an example of my response map.

{
   :status 200
   :headers {
             "content-type" "application/json"
             "Set-cookie" "b=cookie"
             "set-cookie" "a=cookie" }
   :body "{\"data\": \"stuff\"}"
   }

When deployed via :lambdas, I get this response.

< HTTP/2 200
< date: Mon, 29 Jun 2020 20:11:38 GMT
< content-type: application/json
< content-length: 314
< x-amzn-requestid: ---
< set-cookie: a=cookie
< set-cookie: b=cookie
< x-amz-apigw-id: ---
< x-amzn-trace-id: Root=---;Sampled=0

When deployed via :http-direct, I get this response.

< HTTP/2 200
< content-type: application/json
< content-length: 264
< date: Sun, 28 Jun 2020 07:30:54 GMT
< x-amzn-requestid: ---
< x-amzn-remapped-content-length: 264
< set-cookie: b=cookie
< x-amz-apigw-id: ---
< x-amzn-remapped-server: Jetty(9.4.24.v20191120)
< x-amzn-remapped-date: Sun, 28 Jun 2020 07:30:53 GMT
< x-cache: Miss from cloudfront

Note that only one of the set-cookie headers was included.

[Edit] formatting.

2 Likes

We’ve also been unable to set multiple cookies using a default configuration of ring.middleware.cookies with HTTP Direct. Luckily we haven’t actually needed to, but it did come as a surprise.

IIRC, an exception was generated somewhere in Jetty after the HTTP Direct handler returned a response with {:cookies [,,,]}, expecting {:cookies ",,,"}. Our workaround would likely make many on this forum blush and I’d love to get rid of it!

I’d be happy to gather additional details and provide more concrete information to anyone looking further into this.

2 Likes

The specs around HTTP Direct seem to be much more restrictive than apigw/ionize.

I for one would love to hear what your solution is to this problem.

My situation (set a single cookie) seems related and is as follows. I receive this error in datomic ion lambda log:

 "Msg": "http-endpoint respond failed: http://public.domain:8184/login",
    "Ex": {
        "Via": [
            {
                "Type": "java.lang.ClassCastException",
                "Message": "class clojure.lang.LazySeq cannot be cast to class java.lang.String (clojure.lang.LazySeq is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')",
                "At": [
                    "cognitect.http_endpoint.jetty$respond_bbuf_STAR_",
                    "invokeStatic",
                    "jetty.clj",
                    342
                ]
            }
        ],
        "Trace": [
            [
                "cognitect.http_endpoint.jetty$respond_bbuf_STAR_",
                "invokeStatic",
                "jetty.clj",
                342
            ],
            [
                "cognitect.http_endpoint.jetty$respond_bbuf_STAR_",
                "invoke",
                "jetty.clj",
                327
            ],
            [
                "cognitect.http_endpoint.jetty$respond_bbuf",
                "invokeStatic",
                "jetty.clj",
                354
            ],
            [
                "cognitect.http_endpoint.jetty$respond_bbuf",
                "invoke",
                "jetty.clj",
                351
            ],
            [
                "cognitect.http_endpoint.Endpoint",
                "respond",
                "http_endpoint.clj",
                142
            ],

I’m using http-kit locally for dev server, which works correctly for the same configuration that makes the code fail on an ion behind an API-gateway integration handler.

This library provides a solution.

             ;; datomic ions cookies workaround
             ;; require '[net.icbink.expand-headers.core :refer (wrap-expand-headers)]
             net.icbink./expand_headers {:git/url "https://github.com/euccastro/expand-headers.git"
                                         :sha     "b2b0364422d71b8f233148942618b7b16da38ecf"}

Use in this order for middleware:

{:middleware [...
              wrap-expand-headers
              ring-cookies/wrap-cookies]}

Works both locally (in http-kit) and on ion.