Skip to content

Commit

Permalink
Merge pull request #785 from cmuto09/fixing-proxy-response-handler
Browse files Browse the repository at this point in the history
UpdateJSON response detection from stdout in local invocations
  • Loading branch information
dnalborczyk authored Aug 17, 2019
2 parents 7bc44e9 + d4c1772 commit c0cb0b8
Showing 1 changed file with 39 additions and 24 deletions.
63 changes: 39 additions & 24 deletions src/handler-runner/ServerlessInvokeLocalRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,36 +39,37 @@ module.exports = class ServerlessInvokeLocalRunner {
subprocess.stdin.write(`${stringify(event)}\n`)
subprocess.stdin.end()

const newlineRegex = /\r?\n|\r/g
const proxyResponseRegex = /{[\r\n]?\s*(['"])isBase64Encoded(['"])|{[\r\n]?\s*(['"])statusCode(['"])|{[\r\n]?\s*(['"])headers(['"])|{[\r\n]?\s*(['"])body(['"])|{[\r\n]?\s*(['"])principalId(['"])/

let results = ''
let hasDetectedJson = false

subprocess.stdout.on('data', (data) => {
let str = data.toString('utf8')

if (hasDetectedJson) {
let str = data.toString('utf-8')
// Search for the start of the JSON result
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format
const match = proxyResponseRegex.exec(str)
if (match && match.index > -1) {
// If we see a JSON result that looks like it could be a Lambda Proxy response,
// we want to start treating the console output like it is the actual response.
hasDetectedJson = true
// Here we overwrite the existing reults. The last JSON match is the only one we want
// to ensure that we don't accidentally start writing the results just because the
// lambda program itself printed something that matched the regex string. The last match is
// the correct one because it comes from sls invoke local after the lambda code fully executes.
results = trimNewlines(str.slice(match.index))
str = str.slice(0, match.index)
} else if (hasDetectedJson) {
// Assumes that all data after matching the start of the
// JSON result is the rest of the context result.
results += trimNewlines(str)
} else {
// Search for the start of the JSON result
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format
const match = /{[\r\n]?\s*"isBase64Encoded"|{[\r\n]?\s*"statusCode"|{[\r\n]?\s*"headers"|{[\r\n]?\s*"body"|{[\r\n]?\s*"principalId"/.exec(
str,
)

if (match && match.index > -1) {
// The JSON result was in this chunk so slice it out
hasDetectedJson = true
results += trimNewlines(str.slice(match.index))
str = str.slice(0, match.index)
}

if (str.length > 0) {
// The data does not look like JSON and we have not
// detected the start of JSON, so write the
// output to the console instead.
console.log('Proxy Handler could not detect JSON:', str)
}
}
if (str.length > 0) {
// The data does not look like JSON and we have not
// detected the start of JSON, so write the
// output to the console instead.
console.log('Proxy Handler could not detect JSON:', str)
}
})

Expand All @@ -79,7 +80,21 @@ module.exports = class ServerlessInvokeLocalRunner {
subprocess.on('close', (code) => {
if (code.toString() === '0') {
try {
context.succeed(parse(results))
// This is a bit of an odd one. It looks like _process.stdout is chunking
// data to the max buffer size (in my case, 65536) and adding newlines
// between chunks.
//
// In my specific case, I was returning images encoded in the JSON response,
// and these newlines were occurring at regular intervals (every 65536 chars)
// and corrupting the response JSON. Not sure if this is the best way for
// a general solution, but it fixed it in my case.
//
// Upside is that it will handle large JSON payloads correctly,
// downside is that it will strip out any newlines that were supposed
// to be in the response data.
//
// Open to comments about better ways of doing this!
context.succeed(parse(results.replace(newlineRegex, '')))
} catch (ex) {
context.fail(results)
}
Expand Down

0 comments on commit c0cb0b8

Please sign in to comment.