@@ -9,9 +9,24 @@ import {
9
9
EndpointExecRequest ,
10
10
EndpointProxyRequest ,
11
11
EndpointResponse ,
12
+ SpawnResult ,
12
13
} from "./types" ;
13
14
import { info , log } from "./log" ;
14
15
import { ChildProcess , spawn } from "child_process" ;
16
+ import { APIGatewayProxyEventV2 } from "aws-lambda" ;
17
+
18
+ function wsify ( url ?: URL ) : URL | undefined {
19
+ if ( ! url ) return undefined ;
20
+
21
+ const wsUrl = new URL ( url . toString ( ) ) ;
22
+ wsUrl . protocol = url . protocol . replace ( "http" , "ws" ) ;
23
+
24
+ if ( ! wsUrl . protocol . startsWith ( "ws" ) ) {
25
+ return undefined ;
26
+ }
27
+
28
+ return wsUrl ;
29
+ }
15
30
16
31
function convertHeaders (
17
32
headers : RawAxiosResponseHeaders | AxiosResponseHeaders
@@ -78,12 +93,9 @@ const waitForEndpoint = async (
78
93
79
94
export const endpointSpawn = async (
80
95
handler : string ,
81
- env ?: NodeJS . ProcessEnv
82
- ) : Promise < {
83
- childProcess ?: ChildProcess ;
84
- bin ?: string ;
85
- endpoint ?: URL ;
86
- } > => {
96
+ env ?: NodeJS . ProcessEnv ,
97
+ detached : boolean = true
98
+ ) : Promise < SpawnResult > => {
87
99
// handler is in the format of
88
100
// - `{some-bin}@http ://localhost:{the-bins-port} (will start some-bin, and forward requests to the http server)
89
101
// - `http://localhost:{some-port}` (will forward the request to the http server)
@@ -109,20 +121,26 @@ export const endpointSpawn = async (
109
121
110
122
const cmds = bin . split ( " " ) ;
111
123
childProcess = spawn ( cmds [ 0 ] , cmds . slice ( 1 ) , {
112
- detached : true ,
124
+ detached,
113
125
stdio : "inherit" ,
114
126
env : env ,
115
127
} ) ;
116
128
117
- // TODO Decide if we should do this...
118
- childProcess . unref ( ) ;
129
+ if ( detached ) {
130
+ childProcess . unref ( ) ;
131
+ }
119
132
120
133
log ( "Started child process" , { cmds, pid : childProcess . pid } ) ;
121
134
}
122
135
123
136
endpoint = endpoint ? new URL ( endpoint ) : undefined ;
124
137
125
- return { childProcess, bin, endpoint } ;
138
+ return {
139
+ childProcess,
140
+ bin,
141
+ endpoint,
142
+ wsEndpoint : wsify ( endpoint ) ,
143
+ } ;
126
144
} ;
127
145
128
146
export const endpointExec = async ( {
@@ -133,12 +151,21 @@ export const endpointExec = async ({
133
151
} : EndpointExecRequest ) : Promise < EndpointResponse > => {
134
152
const { execa } = await import ( "execa" ) ;
135
153
136
- const { stdout } = await execa ( {
154
+ const timeout = deadline - Date . now ( ) ;
155
+
156
+ info ( `Running: \`${ bin } \`` ) ;
157
+
158
+ const subprocess = execa ( {
137
159
stderr : [ "inherit" ] ,
138
160
} ) `${ bin } ${ event } ` ;
139
161
140
- // TODO: handle deadline
141
- info ( "TODO: need to handle deadline" , { deadline } ) ;
162
+ setTimeout ( ( ) => {
163
+ subprocess . kill (
164
+ Error ( `${ bin } took longer than ${ timeout } milliseconds to start.` )
165
+ ) ;
166
+ } , timeout ) ;
167
+
168
+ const { stdout } = await subprocess ;
142
169
143
170
const payload = JSON . parse ( stdout ) ;
144
171
@@ -154,25 +181,36 @@ export const endpointProxy = async ({
154
181
event,
155
182
deadline,
156
183
} : EndpointProxyRequest ) : Promise < EndpointResponse > => {
184
+ const rawEvent = JSON . parse ( event ) as Partial < APIGatewayProxyEventV2 > ;
185
+
157
186
const {
158
187
requestContext,
159
188
rawPath,
160
189
rawQueryString,
161
190
headers : rawHeaders ,
162
191
body : rawBody ,
163
192
isBase64Encoded,
164
- } = event ;
193
+ } = rawEvent ;
194
+
195
+ if ( ! requestContext ) {
196
+ throw new Error ( "No request context found in event" ) ;
197
+ }
198
+
165
199
const method = requestContext . http . method ;
166
200
167
201
log ( "Waiting for endpoint to start" , { endpoint, deadline } ) ;
168
202
const { timeout } = await waitForEndpoint ( endpoint , deadline ) ;
169
203
170
204
if ( ! timeout ) {
171
205
throw new Error (
172
- `${ endpoint . toString ( ) } took longer than ${ deadline } milliseconds to start.`
206
+ `${ endpoint . toString ( ) } took longer than ${ timeout } milliseconds to start.`
173
207
) ;
174
208
}
175
209
210
+ if ( ! rawPath ) {
211
+ throw new Error ( "No path found in event" ) ;
212
+ }
213
+
176
214
const url = new URL ( rawPath , endpoint ) ;
177
215
if ( rawQueryString ) {
178
216
url . search = new URLSearchParams ( rawQueryString ) . toString ( ) ;
@@ -181,7 +219,7 @@ export const endpointProxy = async ({
181
219
const decodedBody =
182
220
isBase64Encoded && rawBody ? Buffer . from ( rawBody , "base64" ) : rawBody ;
183
221
184
- log ( "Proxying request" , { url, method, rawHeaders, decodedBody , timeout } ) ;
222
+ log ( "Proxying request" , { url, method, rawHeaders, timeout } ) ;
185
223
186
224
let response : AxiosResponse < any , any > | undefined = undefined ;
187
225
try {
@@ -207,7 +245,7 @@ export const endpointProxy = async ({
207
245
208
246
const { data : rawData , headers : rawResponseHeaders } = response ;
209
247
210
- log ( "Proxy request complete" , { url, method, rawResponseHeaders, rawData } ) ;
248
+ log ( "Proxy request complete" , { url, method, rawResponseHeaders } ) ;
211
249
212
250
return {
213
251
requestId,
0 commit comments