0

I have an ALB with a Lambda as a target. The lambda is a simple Python one and the only thing it does, is a return of a hardcoded dict. The response is a valid response for ALB and includes Etag header:

import json
def lambda_handler(event, context):
    result = {
        'statusCode': 200,
        'statusDescription': '200 OK',
        'isBase64Encoded': False,
        'body': event['body'],
        'headers': {
            'Etag': 'someetag-1',
            'Content-Type': 'application/json'
        }
    }
    print(event)
    print(result)
    return result

When I send a POST request, I get a response with 200 status code:

$ curl -X POST \
>   http://test-alb-000000000000.us-east-1.elb.amazonaws.com \
>   -H 'Content-Type: application/json' \
>   -d '{"some": "body"}'
{"some": "body"}

However, when I provide one of If-Unmodified-Since, If-Match headers I receive 412 Precondition Failed:

$ curl -X POST \
>   http://test-alb-000000000000.us-east-1.elb.amazonaws.com \
>   -H 'Content-Type: application/json' \
>   -H 'If-Match: someetag' \
>   -d '{"some": "body"}'
<html>
<head><title>412 Precondition Failed</title></head>
<body bgcolor="white">
<center><h1>412 Precondition Failed</h1></center>
</body>
</html>

From lambda logs I can see however that the lambda was executed and finished properly for both requests:

START RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f Version: $LATEST
{'requestContext': {'elb': {'targetGroupArn': 'arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6'}}, 'httpMethod': 'POST', 'path': '/', 'queryStringParameters': {}, 'headers': {'accept': '*/*', 'content-length': '16', 'content-type': 'application/json', 'host': 'test-alb-000000000000.us-east-1.elb.amazonaws.com', 'user-agent': 'curl/7.54.0', 'x-amzn-trace-id': 'Root=1-7c349c62-54934927ad90ded6e9cb166e', 'x-forwarded-for': '157.12.13.14', 'x-forwarded-port': '80', 'x-forwarded-proto': 'http'}, 'body': '{"some": "body"}', 'isBase64Encoded': False}
{'statusCode': 200, 'statusDescription': '200 OK', 'isBase64Encoded': False, 'body': '{"some": "body"}', 'headers': {'Etag': 'someetag-1', 'Content-Type': 'application/json'}}
END RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f
REPORT RequestId: 91ea1dd7-9ad4-430d-b9d0-36d6d857455f Duration: 5.45 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 55 MB Init Duration: 113.41 ms
XRAY TraceId: 1-5d832f3d-54934927ad90ded6e9cb166e SegmentId: 458533393206659a Sampled: false


START RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4 Version: $LATEST
{'requestContext': {'elb': {'targetGroupArn': 'arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6'}}, 'httpMethod': 'POST', 'path': '/', 'queryStringParameters': {}, 'headers': {'accept': '*/*', 'content-length': '16', 'content-type': 'application/json', 'host': 'test-alb-000000000000.us-east-1.elb.amazonaws.com', 'if-match': 'someetag', 'user-agent': 'curl/7.54.0', 'x-amzn-trace-id': 'Root=1-5d832f44-54934927ad90ded6e9cb166e', 'x-forwarded-for': '157.12.13.14', 'x-forwarded-port': '80', 'x-forwarded-proto': 'http'}, 'body': '{"some": "body"}', 'isBase64Encoded': False}
{'statusCode': 200, 'statusDescription': '200 OK', 'isBase64Encoded': False, 'body': '{"some": "body"}', 'headers': {'Etag': 'someetag-1', 'Content-Type': 'application/json'}}
END RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4
REPORT RequestId: d2c41e28-8ffd-4a20-b9ee-97aac9a2a7e4 Duration: 7.13 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 56 MB
XRAY TraceId: 1-5d832f44-54934927ad90ded6e9cb166e SegmentId: 7a6071cb33eb8af2 Sampled: false

ALB access logs for a request without If-Match header:

http 2019-09-19T07:32:06.546111Z app/test-alb/4c92127c7308 157.12.13.14:32255 - 0.007 0.277 0.000 200 200 180 186 "POST http://test-alb-00000000.us-east-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.54.0" - - arn:aws:elasticloadbalancing:us-east-1:000000000000:targetgroup/TargetGroupName/f27b55fbd94d7be6 "Root=1-7c349c62-54934927ad90ded6e9cb166e" "-" "-" 0 2019-09-19T07:32:06.262000Z "forward" "-" "-"

ALB access logs for a request with If-Match header:

http 2019-09-19T07:32:14.645790Z app/test-alb/4c92127c7308 157.12.13.14:5361 - 0.008 0.059 0.000 412 - 200 317 "POST http://test-alb-00000000.us-east-1.elb.amazonaws.com:80/ HTTP/1.1" "curl/7.54.0" - - - "-" "-" "-" - 2019-09-19T07:32:14.579000Z "-" "-" "-"

The same 412 Precondition Failed response is returned any time I use If-Unmodified-Since or If-Match. The value of those headers doesn't matter. Also, it doesn't matter whether the lambda returns Etag, Last-Modified headers or none of them. Basically, any time one of conditional headers If-Unmodified-Since or If-Match is present, the ALB returns 412 Precondition Failed.

How I can properly use If-Unmodified-Since and If-Match headers with ALB and Lambda? Also, I don't understand why the access logs doesn't say that the request was forwarded to the target when actually it was forwarded (Lambda was executed and finished properly). This problem does not occur with API Gateway but I need to use ALB in my case.

Pawel
  • 1
  • 1
  • You are performing a conditional request against a resource that lacks both a `Last-Modified` and an `ETag`... so conditional requests in the real world should never happen. The behavior seems buggy, but what happens if your generated response actually contains the necessary headers? – Michael - sqlbot Sep 18 '19 at 09:57
  • I thought about it and I checked it already - behavior is the same if the lambda returns `Last-Modified` header. Also, if I have ALB, it should just route the requests, not act on conditional headers I think. It's not a cache, it's not Cloudfront. – Pawel Sep 18 '19 at 11:11
  • I am inclined to agree that the balancer should stay out of the way. [RFC-7232](https://tools.ietf.org/html/rfc7232#section-4.2) does not seem to suggest that 412 is a sensible response for `GET` or `HEAD`. But... in order to properly replicate the issue and offer assistance, I'd suggest we need to eliminate the variables and see (1) the code for a Lambda function that correctly returns `Last-Modified` and/or `ETag` as appropriate, with values that are syntactically correct... and an example `curl` request that replicates the bad behavior. – Michael - sqlbot Sep 18 '19 at 13:02
  • I did that already before and it didn't work at all. But I agree, I will update the question here with a code that returns a header and `curl` examples. – Pawel Sep 18 '19 at 13:52
  • @Michael-sqlbot, I've updated the example as you suggested. It does not matter much though, ALB is returning the same response anytime `Last-Modified` and/or `ETag` are present. – Pawel Sep 19 '19 at 08:02
  • Probably not relevant, but just in case... the value of ETag [must be quoted](https://tools.ietf.org/html/rfc7232#section-2.3) in the header, `'Etag': '"someetag-1"'` – Michael - sqlbot Sep 19 '19 at 11:50

0 Answers0