Skip to content

Commit adb2589

Browse files
committed
Support async handlers. More logging and comments in proxy.
Signed-off-by: Rob Ribeiro <[email protected]>
1 parent 16044d6 commit adb2589

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

package-lock.json

+9-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proxy/index.js

+37-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
21
const cp = require('child_process');
32
const WebSocket = require('ws');
43
const http = require('http');
54
const types = require('../lib/messageTypes');
65

6+
// parse handler name
77
const handlerEnv = process.env._HANDLER && process.env._HANDLER.indexOf('.') > -1 ? process.env._HANDLER.split('.') : [null, 'defaultHandler']; // eslint-disable-line
88
const HANDLER_NAME = handlerEnv[1];// eslint-disable-line
99

1010
let child;
1111
let childSocket;
1212
let brokerSocket;
1313

14-
function currentTimeInMillis () {
15-
const hrtime = process.hrtime()
16-
return hrtime[0] * 1000 + Math.floor(hrtime[1] / 1000000)
14+
function currentTimeInMilliseconds() {
15+
const hrtime = process.hrtime();
16+
return (hrtime[0] * 1000) + Math.floor(hrtime[1] / 1000000);
1717
}
1818

1919
function runAsProxy() {
2020
if (!process.env.DEBUGGER_ACTIVE || process.env.DEBUGGER_ACTIVE === 'false') return;
21+
console.log('[AWS Lambda Debugger] Debugger Status: ACTIVE');
2122
let childResolver;
2223
let debuggerUrl;
2324
const childPromise = new Promise((resolve) => { childResolver = resolve; });
@@ -36,6 +37,7 @@ function runAsProxy() {
3637
const message = JSON.parse(messageString);
3738
switch (message.type) {
3839
case types.CHILD_READY: {
40+
console.log('[AWS Lambda Debugger] Child ready.');
3941
debuggerUrl = message.debuggerUrl; // eslint-disable-line
4042
childResolver();
4143
break;
@@ -46,6 +48,7 @@ function runAsProxy() {
4648
}
4749
});
4850

51+
// set child stream encodings
4952
child.stdout.setEncoding('utf8');
5053
child.stderr.setEncoding('utf8');
5154
}
@@ -59,6 +62,7 @@ function runAsProxy() {
5962
// this is a requirement to keep function from running to full timeout
6063
context.callbackWaitsForEmptyEventLoop = false; // eslint-disable-line
6164

65+
// handle shims for callback and callbackWaitsForEmptyEventLoop
6266
function childMessageHandler(messageString) {
6367
const message = JSON.parse(messageString);
6468
switch (message.type) {
@@ -94,9 +98,14 @@ function runAsProxy() {
9498
brokerSocket.on('message', (messageString) => {
9599
const message = JSON.parse(messageString);
96100
switch (message.type) {
101+
// handle notification from broker that user has connected
97102
case types.USER_CONNECTED: {
98-
console.log('user connected via broker. invoking child.');
103+
console.log('[AWS Lambda Debugger] User connected via broker. Invoking child.');
104+
105+
// open socket to child's debugger endpoint
99106
childSocket = new WebSocket(debuggerUrl);
107+
108+
// proxy V8 debugger messages from child
100109
childSocket.on('message', (rawInspectorMessage) => {
101110
if (brokerSocket.readyState === WebSocket.OPEN) {
102111
brokerSocket.send(JSON.stringify({
@@ -105,9 +114,11 @@ function runAsProxy() {
105114
}));
106115
}
107116
});
117+
118+
// send invoke message to child
108119
childSocket.on('open', () => {
109120
child.send(JSON.stringify({
110-
timestamp: currentTimeInMillis(),
121+
timestamp: currentTimeInMilliseconds(),
111122
remainingTime: context.getRemainingTimeInMillis(),
112123
type: types.INVOKE_HANDLER,
113124
event,
@@ -116,6 +127,8 @@ function runAsProxy() {
116127
});
117128
break;
118129
}
130+
131+
// proxy V8 debugger messages to child
119132
case types.V8_INSPECTOR_MESSAGE: {
120133
if (childSocket.readyState === WebSocket.OPEN) {
121134
childSocket.send(message.payload);
@@ -128,6 +141,7 @@ function runAsProxy() {
128141
}
129142
});
130143

144+
// clean up on broker socket close
131145
brokerSocket.on('close', () => {
132146
if (childSocket && childSocket.readyState === WebSocket.OPEN) {
133147
childSocket.close();
@@ -138,15 +152,17 @@ function runAsProxy() {
138152
}
139153

140154
function runAsChild() {
155+
// shim callback
141156
const callback = (err, response) =>
142157
process.send(JSON.stringify({ type: types.LAMBDA_CALLBACK, err, response }));
143158

144159
// handle messages from proxy
145160
process.on('message', (messageString) => {
146161
const message = JSON.parse(messageString);
147162
switch (message.type) {
163+
// handle invoke message from proxy
148164
case types.INVOKE_HANDLER: {
149-
// shimming context
165+
// shim context
150166
let _callbackWaitsForEmptyEventLoop = true; // eslint-disable-line
151167
Object.defineProperties(message.context, {
152168
callbackWaitsForEmptyEventLoop: {
@@ -160,8 +176,11 @@ function runAsChild() {
160176
}
161177
}
162178
});
179+
// emulate getRemainingTimeInMillis by taking the known remaining time and subtracting the
180+
// delta between now and when that known remaining time was captured. Result should be
181+
// very close since both parent and child share the same system clock.
163182
message.context.getRemainingTimeInMillis =
164-
() => message.remainingTime - (currentTimeInMillis() - message.timestamp);
183+
() => message.remainingTime - (currentTimeInMilliseconds() - message.timestamp);
165184
message.context.done = (err, response) =>
166185
process.send(JSON.stringify({
167186
type: types.LAMBDA_CALLBACK,
@@ -173,12 +192,18 @@ function runAsChild() {
173192
message.context.fail = err => message.context.done(err, null);
174193

175194
// get ready for the user
176-
console.log(`Current AWS request ID: ${message.context.awsRequestId}`);
195+
console.log(`[AWS Lambda Debugger] Current AWS request ID: ${message.context.awsRequestId}`);
177196
setTimeout( // this is a hack to get around the delay before the debugger fully kicks in
178197
() => {
198+
const handler = module.parent.exports[HANDLER_NAME];
179199
debugger;
180200
// *** STEP INTO THE FOLLOWING LINE TO BEGIN STEPPING THROUGH YOUR FUNCTION ***
181-
module.parent.exports[HANDLER_NAME](message.event, message.context, callback);
201+
const response = handler(message.event, message.context, callback);
202+
// handle promise returns - required for async handler support
203+
if (response instanceof Promise) {
204+
response.then(message.context.succeed);
205+
response.catch(message.context.fail);
206+
}
182207
},
183208
1000
184209
);
@@ -197,7 +222,7 @@ function runAsChild() {
197222
responseString += chunk;
198223
});
199224
responseStream.on('end', () => {
200-
console.log(responseString);
225+
console.log(`[AWS Lambda Debugger] Child process info: ${responseString}`);
201226
const response = JSON.parse(responseString);
202227
process.send(JSON.stringify({
203228
type: types.CHILD_READY,
@@ -207,6 +232,7 @@ function runAsChild() {
207232
});
208233
}
209234

235+
// process.send only exists on a child process
210236
if (process.send) {
211237
runAsChild();
212238
} else {

0 commit comments

Comments
 (0)