dherault / serverless-offline

Emulate AWS λ and API Gateway locally when developing your Serverless project
MIT License
5.16k stars 794 forks source link

Lambda functions decode event.payload as utf8 strings for requests with Content-type application/octet-stream #1775

Closed amperioromano closed 1 month ago

amperioromano commented 3 months ago

Bug Report

If we have a lambda function that has content type application/octet-stream, the eventpayload received in the lambda function is a string that comes from decoding the payload buffer with encoding utf8. That makes is impossible to convert it back to binary.

Current Behavior

Sample Code

service: my-service

plugins:
  - serverless-offline

provider:
  runtime: nodejs18.x
  stage: dev

functions:
  hello:
    events:
      - http:
          method: post
          path: hello
          request:
            contentHandling: CONVERT_TO_TEXT
    handler: handler.hello
"use strict"

const { stringify } = JSON

exports.hello = async function hello(event) {

  // event.payload should be a string decoded with binary encoding when the content-type is application/octet-stream
  return {
    body: stringify({ foo: "bar" }),
    statusCode: 200,
  }
}

Expected behavior/code

event.payload should be a string decoded with binary encoding when the content-type is application/octet-stream, but it looks it is utf8.

Environment

Possible Solution

I think the error is in the HttpServer https://github.com/dherault/serverless-offline/blob/c85a19272c59ad9e7cf1aea74e3442c7607b533a/src/events/alb/HttpServer.js#L176 where it always encodes the payload like this

       // Payload processing
      const encoding = detectEncoding(request)

      request.payload = request.payload && request.payload.toString(encoding)

detectEncoding implementation is like https://github.com/dherault/serverless-offline/blob/c85a19272c59ad9e7cf1aea74e3442c7607b533a/src/utils/index.js#L26

// Detect the toString encoding from the request headers content-type
// enhance if further content types need to be non utf8 encoded.
export function detectEncoding(request) {
  const contentType = request.headers["content-type"]

  return typeof contentType === "string" &&
    contentType.includes("multipart/form-data")
    ? "binary"
    : "utf8"
}

It should consider the application/octet-stream in the same way as multipart/form-data, as it is not possible to convert back the string to the original binary otherwise.