Skip to content

Commit 243f8e5

Browse files
committed
basic request response work
Signed-off-by: seven <[email protected]>
1 parent ce618f8 commit 243f8e5

File tree

8 files changed

+314
-104
lines changed

8 files changed

+314
-104
lines changed

src/common/index.ts

Whitespace-only changes.

src/index.ts

Lines changed: 71 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,69 @@
1-
import { Express, Request, Response } from 'express';
1+
import { Express } from 'express';
2+
import { Writable } from 'stream';
23
import { Context, Event, ServerlessAdapter } from './types';
3-
import serverlessHandler from './serverlessHandler';
4+
import sendRequest from './sendRequest';
5+
import ServerlessRequest from './serverlessRequest';
6+
import ServerlessResponse from './serverlessResponse';
7+
import { IncomingHttpHeaders } from 'http';
48

5-
// const CONTEXT_HEADER_NAME = 'x-fc-http-context';
9+
const constructFrameworkContext = (event: Event, context: Context) => {
10+
console.log('constructFrameworkContext', event, context);
11+
const request = new ServerlessRequest({
12+
method: event.httpMethod,
13+
headers: event.headers,
14+
body: event.body,
15+
remoteAddress: '',
16+
url: event.path,
17+
isBase64Encoded: event.isBase64Encoded,
18+
});
19+
const response = new ServerlessResponse(request);
20+
return { request, response };
21+
};
622

7-
// const getRequestHeaders = (ctx: { request: Request }) => {
8-
// const request = ctx.request;
9-
// const headers = { ...request.headers };
10-
// return headers;
11-
// };
12-
//
13-
// const getSocketPath = (): string => {
14-
// const socketPathSuffix = Math.random().toString(36).substring(2, 15);
15-
// if (/^win/.test(process.platform)) {
16-
// const path = require('path');
17-
// return path.join('\\\\?\\pipe', process.cwd(), `server-${socketPathSuffix}`);
18-
// } else {
19-
// return `/tmp/server-${socketPathSuffix}.sock`;
20-
// }
21-
// };
23+
const waitForStreamComplete = (stream: Writable): Promise<Writable> => {
24+
if (stream.writableFinished || stream.writableEnded) {
25+
return Promise.resolve(stream);
26+
}
2227

23-
// const getBody = async (request: Request): Promise<string> => {
24-
// return new Promise((resolve, reject) => {
25-
// if (!request.on) {
26-
// resolve('');
27-
// }
28-
// try {
29-
// getRawBody(request).then(resolve, reject);
30-
// } catch (e) {
31-
// reject(e);
32-
// }
33-
// });
34-
// };
35-
//
36-
// const makeResolver = (ctx) => {
37-
// return data => {
38-
// const response = ctx.response;
39-
// if (response.setStatusCode) {
40-
// response.setStatusCode(data.statusCode);
41-
// } else {
42-
// response.status = data.statusCode;
43-
// response.statusCode = data.statusCode;
44-
// }
45-
// for (const key in data.headers) {
46-
// if (data.headers.hasOwnProperty(key)) {
47-
// const value = data.headers[key];
48-
// response.setHeader(key, value);
49-
// }
50-
// }
51-
// for (const key in data.multiValueHeaders) {
52-
// const value = data.multiValueHeaders[key]
53-
// response.setHeader(key, value)
54-
// }
55-
// if (response.send) {
56-
// response.send(data.body);
57-
// } else {
58-
// response.end(data.body);
59-
// }
60-
// };
61-
// }
62-
//
28+
return new Promise((resolve, reject) => {
29+
stream.once('error', complete);
30+
stream.once('end', complete);
31+
stream.once('finish', complete);
6332

64-
const constructFrameworkContext = (event: Event, context: Context) => {
65-
console.log('constructFrameworkContext', event, context);
33+
let isComplete = false;
34+
35+
function complete(err?: Error) {
36+
if (isComplete) {
37+
return;
38+
}
39+
40+
isComplete = true;
41+
42+
stream.removeListener('error', complete);
43+
stream.removeListener('end', complete);
44+
stream.removeListener('finish', complete);
45+
46+
if (err) {
47+
reject(err);
48+
} else {
49+
resolve(stream);
50+
}
51+
}
52+
});
53+
};
54+
55+
const buildResponse = ({
56+
request,
57+
response,
58+
}: {
59+
request: ServerlessRequest;
60+
response: ServerlessResponse;
61+
}) => {
6662
return {
67-
request: {
68-
...event,
69-
get: (name: string) => event.headers[name.toLowerCase()],
70-
} as unknown as Request,
71-
response: {} as unknown as Response,
63+
statusCode: response.statusCode,
64+
body: ServerlessResponse.body(response).toString(request.isBase64Encoded ? 'base64' : 'utf8'),
65+
headers: response.headers,
66+
isBase64Encoded: request.isBase64Encoded,
7267
};
7368
};
7469

@@ -77,18 +72,22 @@ const serverlessAdapter: ServerlessAdapter = (app: Express) => {
7772
const ctx = constructFrameworkContext(event, context);
7873

7974
try {
80-
return (await serverlessHandler(app, ctx)) as unknown as {
81-
statusCode: number;
82-
body: unknown;
83-
};
75+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
76+
// @ts-expect-error
77+
await sendRequest(app, ctx.request, ctx.response);
78+
await waitForStreamComplete(ctx.response);
79+
return buildResponse(ctx);
8480
} catch (err) {
8581
const errorResponse = { statusCode: 500, body: (err as Error).message };
8682
console.log('Error occurred during request handling:', err);
87-
return errorResponse as unknown as { statusCode: number; body: unknown };
83+
return errorResponse as unknown as {
84+
statusCode: number;
85+
body: string;
86+
headers: IncomingHttpHeaders;
87+
isBase64Encoded: boolean;
88+
};
8889
}
8990
};
9091
};
9192

9293
export default serverlessAdapter;
93-
94-
export const hello = () => 'hello';

src/sendRequest.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Express } from 'express';
2+
import { IncomingMessage, ServerResponse } from 'node:http';
3+
4+
const sendRequest = async (app: Express, request: IncomingMessage, response: ServerResponse) => {
5+
app(request, response);
6+
};
7+
8+
export default sendRequest;

src/serverlessHandler.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/serverlessRequest.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http
2+
3+
import { IncomingMessage } from 'http';
4+
import { Socket } from 'net';
5+
6+
const HTTPS_PORT = 443;
7+
8+
interface ServerlessRequestOptions {
9+
method: string;
10+
url: string;
11+
headers: { [key: string]: string };
12+
body: Buffer | string;
13+
remoteAddress: string;
14+
isBase64Encoded: boolean;
15+
}
16+
17+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
18+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
19+
const NO_OP: (...args: any[]) => any = () => void 0;
20+
21+
export default class ServerlessRequest extends IncomingMessage {
22+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
23+
// @ts-expect-error
24+
ip: string;
25+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
26+
// @ts-expect-error
27+
body: Buffer | string;
28+
29+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
30+
// @ts-expect-error
31+
isBase64Encoded: boolean;
32+
33+
constructor({
34+
method,
35+
url,
36+
headers,
37+
body,
38+
remoteAddress,
39+
isBase64Encoded,
40+
}: ServerlessRequestOptions) {
41+
super({
42+
encrypted: true,
43+
readable: false,
44+
remoteAddress,
45+
address: () => ({ port: HTTPS_PORT }),
46+
end: NO_OP,
47+
destroy: NO_OP,
48+
} as unknown as Socket);
49+
50+
// IncomingMessage has a lot of logic for when to lowercase or alias well-known header names,
51+
// so we delegate to that logic here
52+
const headerEntries = Object.entries(headers);
53+
const rawHeaders = new Array(headerEntries.length * 2);
54+
for (let i = 0; i < headerEntries.length; i++) {
55+
rawHeaders[i * 2] = headerEntries[i][0];
56+
rawHeaders[i * 2 + 1] = headerEntries[i][1];
57+
}
58+
59+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
60+
// @ts-expect-error
61+
this._addHeaderLines(rawHeaders, rawHeaders.length);
62+
63+
Object.assign(this, {
64+
ip: remoteAddress,
65+
complete: true,
66+
httpVersion: '1.1',
67+
httpVersionMajor: '1',
68+
httpVersionMinor: '1',
69+
method,
70+
body,
71+
url,
72+
isBase64Encoded,
73+
});
74+
75+
this._read = () => {
76+
this.push(body);
77+
this.push(null);
78+
};
79+
}
80+
}

0 commit comments

Comments
 (0)