I have a Cloudformation template which serves S3 text and binary content (mp3
, jpeg
, png
) over HTTPS via API Gateway.
I have a series of hello
assets (json
, mp3
, jpg
) which I push to the S3 bucket, being careful to include the correct MIME
type for each.
I then go to the browser to see if I can access the assets via HTTPS.
hello.json
works fine; I can see {"hello": "world"}
in the browser screen.
hello.mp3
also works fine; the browser renders an mp3 player and I can hear "hello world" being spoken.
But hello.jpg
renders a broken image :/
My first thought is that the image itself is broken; but I can curl
it to a local file and the browser will render the file image fine.
My second thought is that somehow there is some mixup between the browser and API Gateway regarding raw binary vs base64 encoded binary data; I know that API Gateway likes to render b64 data on occasion.
It seems there is indeed some mixup here. The jpeg
raw file is 1467 bytes in length, but if I inspect the Chrome Javascript console, the server seems response with a length of more than 1900. I think the server may therefore be returning b64 data as I know b64 files are normally approx 1/3 bigger than the raw files.
So then I think maybe the HTTP request headers are influencing the response type. But no matter what Accept
header values I give to curl
, the server always returns 1467 Content-Length
. I can't get curl
to replicate what the browser is doing with the server.
I am also confused as to why the browser would happily render mp3
files, but not jpg
files from the same endpoint.
Any thoughts as to how to resolve this? Thank you.
---
Outputs: {}
Parameters:
AppName:
Type: String
DomainName:
Type: String
CertificateArn:
Type: String
MemorySizeDefault:
Default: '512'
Type: String
RuntimeVersion:
Default: '3.10'
Type: String
TimeoutDefault:
Default: '5'
Type: String
Resources:
MyWebsite:
Properties:
BucketName: !Sub "${AppName}-bucket"
Type: AWS::S3::Bucket
MyWebsiteDeployment:
DependsOn:
- MyWebsiteMethod
Properties:
RestApiId:
Ref: MyWebsiteRestApi
Type: AWS::ApiGateway::Deployment
MyWebsiteDomain:
Properties:
CertificateArn:
Ref: CertificateArn
DomainName:
Ref: DomainName
Type: AWS::ApiGateway::DomainName
MyWebsiteDomainPathMapping:
DependsOn:
- MyWebsiteDomain
Properties:
DomainName:
Ref: DomainName
RestApiId:
Ref: MyWebsiteRestApi
Stage: prod
Type: AWS::ApiGateway::BasePathMapping
MyWebsiteDomainRecordSet:
Properties:
AliasTarget:
DNSName:
Fn::GetAtt:
- MyWebsiteDomain
- DistributionDomainName
EvaluateTargetHealth: false
HostedZoneId:
Fn::GetAtt:
- MyWebsiteDomain
- DistributionHostedZoneId
HostedZoneName:
Fn::Sub:
- ${prefix}.${suffix}.
- prefix:
Fn::Select:
- 1
- Fn::Split:
- .
- Ref: DomainName
suffix:
Fn::Select:
- 2
- Fn::Split:
- .
- Ref: DomainName
Name:
Ref: DomainName
Type: A
Type: AWS::Route53::RecordSet
MyWebsiteMethod:
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
Credentials:
Fn::GetAtt:
- MyWebsiteRole
- Arn
IntegrationHttpMethod: ANY
IntegrationResponses:
- ResponseParameters:
method.response.header.Content-Type: integration.response.header.Content-Type
StatusCode: 200
- SelectionPattern: '404'
StatusCode: 404
PassthroughBehavior: WHEN_NO_MATCH
RequestParameters:
integration.request.path.proxy: method.request.path.proxy
Type: AWS
Uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:s3:path/${MyWebsite}/{proxy}
MethodResponses:
- ResponseParameters:
method.response.header.Content-Type: true
StatusCode: 200
- StatusCode: 404
RequestParameters:
method.request.path.proxy: true
ResourceId:
Ref: MyWebsiteResource
RestApiId:
Ref: MyWebsiteRestApi
Type: AWS::ApiGateway::Method
MyWebsiteResource:
Properties:
ParentId:
Fn::GetAtt:
- MyWebsiteRestApi
- RootResourceId
PathPart: '{proxy+}'
RestApiId:
Ref: MyWebsiteRestApi
Type: AWS::ApiGateway::Resource
MyWebsiteRestApi:
Properties:
BinaryMediaTypes:
- audio/mpeg
- image/jpeg
- image/x-png
Name:
Fn::Sub: my-website-rest-api-${AWS::StackName}
Type: AWS::ApiGateway::RestApi
MyWebsiteRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- s3:GetObject
Effect: Allow
Resource:
Fn::Sub: arn:aws:s3:::${MyWebsite}/*
Version: '2012-10-17'
PolicyName:
Fn::Sub: my-website-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
MyWebsiteStage:
Properties:
DeploymentId:
Ref: MyWebsiteDeployment
RestApiId:
Ref: MyWebsiteRestApi
StageName: prod
Type: AWS::ApiGateway::Stage
The answer is to replace the entries in BinaryMediaTypes
with a single entry */*
I don't know why this works but it does
The answer is apparently in here -
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
but it doesn't make any sense to me
Anyhow; closed