1
-
2
1
const cp = require ( 'child_process' ) ;
3
2
const WebSocket = require ( 'ws' ) ;
4
3
const http = require ( 'http' ) ;
5
4
const types = require ( '../lib/messageTypes' ) ;
6
5
6
+ // parse handler name
7
7
const handlerEnv = process . env . _HANDLER && process . env . _HANDLER . indexOf ( '.' ) > - 1 ? process . env . _HANDLER . split ( '.' ) : [ null , 'defaultHandler' ] ; // eslint-disable-line
8
8
const HANDLER_NAME = handlerEnv [ 1 ] ; // eslint-disable-line
9
9
10
10
let child ;
11
11
let childSocket ;
12
12
let brokerSocket ;
13
13
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 ) ;
17
17
}
18
18
19
19
function runAsProxy ( ) {
20
20
if ( ! process . env . DEBUGGER_ACTIVE || process . env . DEBUGGER_ACTIVE === 'false' ) return ;
21
+ console . log ( '[AWS Lambda Debugger] Debugger Status: ACTIVE' ) ;
21
22
let childResolver ;
22
23
let debuggerUrl ;
23
24
const childPromise = new Promise ( ( resolve ) => { childResolver = resolve ; } ) ;
@@ -36,6 +37,7 @@ function runAsProxy() {
36
37
const message = JSON . parse ( messageString ) ;
37
38
switch ( message . type ) {
38
39
case types . CHILD_READY : {
40
+ console . log ( '[AWS Lambda Debugger] Child ready.' ) ;
39
41
debuggerUrl = message . debuggerUrl ; // eslint-disable-line
40
42
childResolver ( ) ;
41
43
break ;
@@ -46,6 +48,7 @@ function runAsProxy() {
46
48
}
47
49
} ) ;
48
50
51
+ // set child stream encodings
49
52
child . stdout . setEncoding ( 'utf8' ) ;
50
53
child . stderr . setEncoding ( 'utf8' ) ;
51
54
}
@@ -59,6 +62,7 @@ function runAsProxy() {
59
62
// this is a requirement to keep function from running to full timeout
60
63
context . callbackWaitsForEmptyEventLoop = false ; // eslint-disable-line
61
64
65
+ // handle shims for callback and callbackWaitsForEmptyEventLoop
62
66
function childMessageHandler ( messageString ) {
63
67
const message = JSON . parse ( messageString ) ;
64
68
switch ( message . type ) {
@@ -94,9 +98,14 @@ function runAsProxy() {
94
98
brokerSocket . on ( 'message' , ( messageString ) => {
95
99
const message = JSON . parse ( messageString ) ;
96
100
switch ( message . type ) {
101
+ // handle notification from broker that user has connected
97
102
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
99
106
childSocket = new WebSocket ( debuggerUrl ) ;
107
+
108
+ // proxy V8 debugger messages from child
100
109
childSocket . on ( 'message' , ( rawInspectorMessage ) => {
101
110
if ( brokerSocket . readyState === WebSocket . OPEN ) {
102
111
brokerSocket . send ( JSON . stringify ( {
@@ -105,9 +114,11 @@ function runAsProxy() {
105
114
} ) ) ;
106
115
}
107
116
} ) ;
117
+
118
+ // send invoke message to child
108
119
childSocket . on ( 'open' , ( ) => {
109
120
child . send ( JSON . stringify ( {
110
- timestamp : currentTimeInMillis ( ) ,
121
+ timestamp : currentTimeInMilliseconds ( ) ,
111
122
remainingTime : context . getRemainingTimeInMillis ( ) ,
112
123
type : types . INVOKE_HANDLER ,
113
124
event,
@@ -116,6 +127,8 @@ function runAsProxy() {
116
127
} ) ;
117
128
break ;
118
129
}
130
+
131
+ // proxy V8 debugger messages to child
119
132
case types . V8_INSPECTOR_MESSAGE : {
120
133
if ( childSocket . readyState === WebSocket . OPEN ) {
121
134
childSocket . send ( message . payload ) ;
@@ -128,6 +141,7 @@ function runAsProxy() {
128
141
}
129
142
} ) ;
130
143
144
+ // clean up on broker socket close
131
145
brokerSocket . on ( 'close' , ( ) => {
132
146
if ( childSocket && childSocket . readyState === WebSocket . OPEN ) {
133
147
childSocket . close ( ) ;
@@ -138,15 +152,17 @@ function runAsProxy() {
138
152
}
139
153
140
154
function runAsChild ( ) {
155
+ // shim callback
141
156
const callback = ( err , response ) =>
142
157
process . send ( JSON . stringify ( { type : types . LAMBDA_CALLBACK , err, response } ) ) ;
143
158
144
159
// handle messages from proxy
145
160
process . on ( 'message' , ( messageString ) => {
146
161
const message = JSON . parse ( messageString ) ;
147
162
switch ( message . type ) {
163
+ // handle invoke message from proxy
148
164
case types . INVOKE_HANDLER : {
149
- // shimming context
165
+ // shim context
150
166
let _callbackWaitsForEmptyEventLoop = true ; // eslint-disable-line
151
167
Object . defineProperties ( message . context , {
152
168
callbackWaitsForEmptyEventLoop : {
@@ -160,8 +176,11 @@ function runAsChild() {
160
176
}
161
177
}
162
178
} ) ;
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.
163
182
message . context . getRemainingTimeInMillis =
164
- ( ) => message . remainingTime - ( currentTimeInMillis ( ) - message . timestamp ) ;
183
+ ( ) => message . remainingTime - ( currentTimeInMilliseconds ( ) - message . timestamp ) ;
165
184
message . context . done = ( err , response ) =>
166
185
process . send ( JSON . stringify ( {
167
186
type : types . LAMBDA_CALLBACK ,
@@ -173,12 +192,18 @@ function runAsChild() {
173
192
message . context . fail = err => message . context . done ( err , null ) ;
174
193
175
194
// 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 } ` ) ;
177
196
setTimeout ( // this is a hack to get around the delay before the debugger fully kicks in
178
197
( ) => {
198
+ const handler = module . parent . exports [ HANDLER_NAME ] ;
179
199
debugger ;
180
200
// *** 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
+ }
182
207
} ,
183
208
1000
184
209
) ;
@@ -197,7 +222,7 @@ function runAsChild() {
197
222
responseString += chunk ;
198
223
} ) ;
199
224
responseStream . on ( 'end' , ( ) => {
200
- console . log ( responseString ) ;
225
+ console . log ( `[AWS Lambda Debugger] Child process info: ${ responseString } ` ) ;
201
226
const response = JSON . parse ( responseString ) ;
202
227
process . send ( JSON . stringify ( {
203
228
type : types . CHILD_READY ,
@@ -207,6 +232,7 @@ function runAsChild() {
207
232
} ) ;
208
233
}
209
234
235
+ // process.send only exists on a child process
210
236
if ( process . send ) {
211
237
runAsChild ( ) ;
212
238
} else {
0 commit comments