-
Notifications
You must be signed in to change notification settings - Fork 2k
/
Copy pathreq.js
155 lines (136 loc) · 5.6 KB
/
req.js
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
151
152
153
154
155
/**
* Module dependencies
*/
var _ = require('@sailshq/lodash');
var defaultsDeep = require('merge-defaults');// « TODO: Get rid of this
var MockReq = require('./mock-req');// «FUTURE: consolidate that into this file
var parseurl = require('parseurl');
/**
* Factory which builds generic Sails request object (i.e. `req`).
*
* This generic implementation of `req` forms the basis for
* Sails' transport-agnostic support of Connect/Express
* middleware. Used by hooks (i.e. sockets) but also for
* tests-- both at the app-level and in Sails core.
*
* @param {Dictionary} _req
* the properties of this simulated request object that
* have been built up _so far_.
*
* @return {Request} simulated HTTP request object
* @idempotent
*/
module.exports = function buildRequest (_req) {
// Make sure _req is not undefined
_req = _req||{};
// Start our request object, which will be built by inheriting/transforming
// properties of _req and adding some spice of our own
var req;
// Attempt to parse the URL in _req, so that we can get the querystring
// and path. (But if it fails for any reason, ignore the error and fall back
// to an empty dictionary.)
var parsedUrl;
try {parsedUrl = parseurl(_req) || {};}
catch (unusedErr) {parsedUrl = {};}
// If `_req` appears to be a stream (duck-typing), then don't try
// and turn it into a mock stream again.
if (typeof _req === 'object' && _req.read) {
req = _req;
}
else {
if (_req.headers && typeof _req.headers === 'object') {
for (let headerKey of Object.keys(_req.headers)) {
// Strip undefined headers
if (undefined === _req.headers[headerKey]) {
delete _req.headers[headerKey];
continue;
}//•
// Make sure all remaining headers are strings
if (typeof _req.headers[headerKey] !== 'string') {
try {
_req.headers[headerKey] = ''+_req.headers[headerKey];
// FUTURE: This behavior is likely being relied upon by apps, so we can't just change it.
// But in retrospect, it would probably be better to straight-up reject this here if it's not
// a string, since HTTP header values are always supposed to be strings; or at least primitives.
// So maybe reject non-primitives, reject `null`, and then accept primitives, but be smart about
// this, especially in the context of what the client is doing.
} catch (unusedErr) {
delete _req.headers[headerKey];
}
}
}//∞
}//fi
// Create a mock IncomingMessage stream.
req = new MockReq({
method: _req && (_.isString(_req.method) ? _req.method.toUpperCase() : 'GET'),
headers: _req && _req.headers || {},
url: _req && _req.url
});
// Add .get() and .header() methods to match express 3
req.get = req.header = function (name) {
switch (name = name.toLowerCase()) {
case 'referer':
case 'referrer':
return this.headers.referrer || this.headers.referer;
default:
return this.headers[name];
}
};
// Now pump client request body to the mock IncomingMessage stream (req)
// Req stream ends automatically if this is a GET or HEAD or DELETE request
// (since there is no request body in that case) so no need to do it again.
if (req.method !== 'GET' && req.method !== 'HEAD' && req.method !== 'DELETE') {
// Only write the body if there IS a body.
if (req.body) {
req.write(req.body);
}
req.end();
}
}
// Track request start time
req._startTime = new Date();
////////////////////////////////////////////////////////////////////////////////
// Note that other core methods _could_ be added here for use w/ the virtual
// router. But as per convo w/ dougwilson, the same _cannot_ be done for HTTP
// requests coming out of Express. They would either have to (a) rely on modifying
// the HTTP request (IncomingMessage) prototype, or (B) rely on context (i.e. `this`),
// which would require `_.bind()`-ing them to avoid issues when triggered from
// userland code. And re: (B) at that point, the performance impact is effectively
// the same as if they were attached on the fly on a per-request basis.
//
// So we only initially attach `req.*` methods & properties here which are _not_
// already built-in to the mock request, and which are _not_ already taken care of
// by hooks, AND which don't rely on `res` (because it hasn't been built yet).
////////////////////////////////////////////////////////////////////////////////
// Provide defaults for other request state and methods
req = defaultsDeep(req, {
params: [],
query: (_req && _req.query) || require('querystring').parse(parsedUrl.query) || {},
body: (_req && _req.body) || {},
param: function(paramName, defaultValue) {
var key;
var params = {};
for (key in (req.params || {}) ) {
params[key] = params[key] || req.params[key];
}
for (key in (req.query || {}) ) {
params[key] = params[key] || req.query[key];
}
for (key in (req.body || {}) ) {
params[key] = params[key] || req.body[key];
}
// Grab the value of the parameter from the appropriate place
// and return it
if (typeof params[paramName] !== 'undefined') {
return params[paramName];
} else {
return defaultValue;
}
},
wantsJSON: (_req && _req.wantsJSON === false) ? false : true,
method: 'GET',
originalUrl: _req.originalUrl || _req.url,
path: _req.path || parsedUrl.pathname
}, _req||{});
return req;
};