forked from GoogleCloudPlatform/functions-framework-nodejs
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathserver.ts
150 lines (135 loc) · 5.02 KB
/
server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as bodyParser from 'body-parser';
import * as express from 'express';
import * as http from 'http';
import * as onFinished from 'on-finished';
import {HandlerFunction, Request, Response} from './functions';
import {SignatureType} from './types';
import {setLatestRes} from './invoker';
import {legacyPubSubEventMiddleware} from './pubsub_middleware';
import {cloudEventToBackgroundEventMiddleware} from './middleware/cloud_event_to_background_event';
import {backgroundEventToCloudEventMiddleware} from './middleware/background_event_to_cloud_event';
import {wrapUserFunction} from './function_wrappers';
/**
* Creates and configures an Express application and returns an HTTP server
* which will run it.
* @param userFunction User's function.
* @param functionSignatureType Type of user's function signature.
* @param context Optional context object.
* @return HTTP server.
*/
export function getServer(
userFunction: HandlerFunction,
functionSignatureType: SignatureType,
context?: object
): http.Server {
// App to use for function executions.
const app = express();
// Express middleware
// Set request-specific values in the very first middleware.
app.use('/*', (req, res, next) => {
// Set function context in app locals for the lifetime of the request.
res.locals.context = context || {};
setLatestRes(res);
res.locals.functionExecutionFinished = false;
next();
});
/**
* Retains a reference to the raw body buffer to allow access to the raw body
* for things like request signature validation. This is used as the "verify"
* function in body-parser options.
* @param req Express request object.
* @param res Express response object.
* @param buf Buffer to be saved.
*/
function rawBodySaver(req: Request, res: Response, buf: Buffer) {
req.rawBody = buf;
}
// Set limit to a value larger than 32MB, which is maximum limit of higher
// level layers anyway.
const requestLimit = '1024mb';
const defaultBodySavingOptions = {
limit: requestLimit,
verify: rawBodySaver,
};
const cloudEventsBodySavingOptions = {
type: 'application/cloudevents+json',
limit: requestLimit,
verify: rawBodySaver,
};
const rawBodySavingOptions = {
limit: requestLimit,
verify: rawBodySaver,
type: '*/*',
};
// Use extended query string parsing for URL-encoded bodies.
const urlEncodedOptions = {
limit: requestLimit,
verify: rawBodySaver,
extended: true,
};
// Apply middleware
app.use(bodyParser.json(cloudEventsBodySavingOptions));
app.use(bodyParser.json(defaultBodySavingOptions));
app.use(bodyParser.text(defaultBodySavingOptions));
app.use(bodyParser.urlencoded(urlEncodedOptions));
// The parser will process ALL content types so MUST come last.
// Subsequent parsers will be skipped when one is matched.
app.use(bodyParser.raw(rawBodySavingOptions));
app.enable('trust proxy'); // To respect X-Forwarded-For header.
// Disable Express 'x-powered-by' header:
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable('x-powered-by');
if (
functionSignatureType === 'event' ||
functionSignatureType === 'cloudevent'
) {
// If a Pub/Sub subscription is configured to invoke a user's function directly, the request body
// needs to be marshalled into the structure that wrapEventFunction expects. This unblocks local
// development with the Pub/Sub emulator
app.use(legacyPubSubEventMiddleware);
}
if (functionSignatureType === 'event') {
app.use(cloudEventToBackgroundEventMiddleware);
}
if (functionSignatureType === 'cloudevent') {
app.use(backgroundEventToCloudEventMiddleware);
}
if (functionSignatureType === 'http') {
app.use('/favicon.ico|/robots.txt', (req, res) => {
// Neither crawlers nor browsers attempting to pull the icon find the body
// contents particularly useful, so we send nothing in the response body.
res.status(404).send(null);
});
app.use('/*', (req, res, next) => {
onFinished(res, (err, res) => {
res.locals.functionExecutionFinished = true;
});
next();
});
}
// Set up the routes for the user's function
const requestHandler = wrapUserFunction(
userFunction,
functionSignatureType,
context
);
if (functionSignatureType === 'http') {
app.all('/*', requestHandler);
} else {
app.post('/*', requestHandler);
}
return http.createServer(app);
}