finally works

This commit is contained in:
Craig Condon
2012-11-30 20:11:55 -06:00
parent 8b03984ca8
commit ad5532eaad
5173 changed files with 1558686 additions and 27414 deletions
+34 -9
View File
@@ -16,7 +16,7 @@ Or from source:
## Super simple to use
Request is designed to be the simplest way possible to make http calls. It support HTTPS and follows redirects by default.
Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default.
```javascript
var request = require('request');
@@ -38,7 +38,7 @@ request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))
You can also stream a file to a PUT or POST request. This method will also check the file extension against a mapping of file extensions to content-types, in this case `application/json`, and use the proper content-type in the PUT request if one is not already provided in the headers.
```javascript
fs.readStream('file.json').pipe(request.put('http://mysite.com/obj.json'))
fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))
```
Request can also pipe to itself. When doing so the content-type and content-length will be preserved in the PUT headers.
@@ -90,9 +90,31 @@ http.createServer(function (req, resp) {
}
})
```
You can still use intermediate proxies, the requests will still follow HTTP forwards, etc.
## Forms
`request` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. For `multipart/related` refer to the `multipart` API.
Url encoded forms are simple
```javascript
request.post('http://service.com/upload', {form:{key:'value'}})
// or
request.post('http://service.com/upload').form({key:'value'})
```
For `multipart/form-data` we use the [form-data](https://github.com/felixge/node-form-data) library by [@felixge](https://github.com/felixge). You don't need to worry about piping the form object or setting the headers, `request` will handle that for you.
```javascript
var r = request.post('http://service.com/upload')
var form = r.form()
form.append('my_field', 'my_value')
form.append('my_buffer', new Buffer([1, 2, 3]))
form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png'))
form.append('remote_file', request('http://google.com/doodle.png'))
```
## OAuth Signing
```javascript
@@ -146,15 +168,17 @@ request.post({url:url, oauth:oauth}, function (e, r, body) {
The first argument can be either a url or an options object. The only required option is uri, all others are optional.
* `uri` || `url` - fully qualified uri or a parsed url object from url.parse()
* `qs` - object containing querystring values to be appended to the uri
* `method` - http method, defaults to GET
* `headers` - http headers, defaults to {}
* `body` - entity body for POST and PUT requests. Must be buffer or string.
* `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header.
* `form` - when passed an object this will set `body` but to a querystring representation of value and adds `Content-type: application/x-www-form-urlencoded; charset=utf-8` header. When passed no option a FormData instance is returned that will be piped to request.
* `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json` header. Additionally, parses the response body as json.
* `multipart` - (experimental) array of objects which contains their own headers and `body` attribute. Sends `multipart/related` request. See example below.
* `followRedirect` - follow HTTP 3xx responses as redirects. defaults to true.
* `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects. defaults to false.
* `maxRedirects` - the maximum number of redirects to follow, defaults to 10.
* `onResponse` - If true the callback will be fired on the "response" event instead of "end". If a function it will be called on "response" and not effect the regular semantics of the main callback on "end".
* `encoding` - Encoding to be used on response.setEncoding when buffering the response data.
* `encoding` - Encoding to be used on `setEncoding` of response data. If set to `null`, the body is returned as a Buffer.
* `pool` - A hash object containing the agents for these requests. If omitted this request will use the global pool which is set to node's default maxSockets.
* `pool.maxSockets` - Integer containing the maximum amount of sockets in the pool.
* `timeout` - Integer containing the number of milliseconds to wait for a request to respond before aborting the request
@@ -162,9 +186,10 @@ The first argument can be either a url or an options object. The only required o
* `oauth` - Options for OAuth HMAC-SHA1 signing, see documentation above.
* `strictSSL` - Set to `true` to require that SSL certificates be valid. Note: to use your own certificate authority, you need to specify an agent that was created with that ca as an option.
* `jar` - Set to `false` if you don't want cookies to be remembered for future use or define your custom cookie jar (see examples section)
* `aws` - object containing aws signing information, should have the properties `key` and `secret` as well as `bucket` unless you're specifying your bucket as part of the path, or you are making a request that doesn't use a bucket (i.e. GET Services)
The callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body buffer.
The callback argument gets 3 arguments. The first is an error when applicable (usually from the http.Client option not the http.ClientRequest object). The second in an http.ClientResponse object. The third is the response body String or Buffer.
## Convenience methods
@@ -237,7 +262,7 @@ request.jar()
;
request(
{ method: 'PUT'
, uri: 'http://mikeal.couchone.com/testjs/' + rand
, uri: 'http://mikeal.iriscouch.com/testjs/' + rand
, multipart:
[ { 'content-type': 'application/json'
, body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}})
@@ -247,7 +272,7 @@ request.jar()
}
, function (error, response, body) {
if(response.statusCode == 201){
console.log('document saved as: http://mikeal.couchone.com/testjs/'+ rand)
console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand)
} else {
console.log('error: '+ response.statusCode)
console.log(body)
+848 -316
View File
File diff suppressed because it is too large Load Diff
-146
View File
@@ -1,146 +0,0 @@
// from http://github.com/felixge/node-paperboy
exports.types = {
"aiff":"audio/x-aiff",
"arj":"application/x-arj-compressed",
"asf":"video/x-ms-asf",
"asx":"video/x-ms-asx",
"au":"audio/ulaw",
"avi":"video/x-msvideo",
"bcpio":"application/x-bcpio",
"ccad":"application/clariscad",
"cod":"application/vnd.rim.cod",
"com":"application/x-msdos-program",
"cpio":"application/x-cpio",
"cpt":"application/mac-compactpro",
"csh":"application/x-csh",
"css":"text/css",
"deb":"application/x-debian-package",
"dl":"video/dl",
"doc":"application/msword",
"drw":"application/drafting",
"dvi":"application/x-dvi",
"dwg":"application/acad",
"dxf":"application/dxf",
"dxr":"application/x-director",
"etx":"text/x-setext",
"ez":"application/andrew-inset",
"fli":"video/x-fli",
"flv":"video/x-flv",
"gif":"image/gif",
"gl":"video/gl",
"gtar":"application/x-gtar",
"gz":"application/x-gzip",
"hdf":"application/x-hdf",
"hqx":"application/mac-binhex40",
"html":"text/html",
"ice":"x-conference/x-cooltalk",
"ico":"image/x-icon",
"ief":"image/ief",
"igs":"model/iges",
"ips":"application/x-ipscript",
"ipx":"application/x-ipix",
"jad":"text/vnd.sun.j2me.app-descriptor",
"jar":"application/java-archive",
"jpeg":"image/jpeg",
"jpg":"image/jpeg",
"js":"text/javascript",
"json":"application/json",
"latex":"application/x-latex",
"lsp":"application/x-lisp",
"lzh":"application/octet-stream",
"m":"text/plain",
"m3u":"audio/x-mpegurl",
"man":"application/x-troff-man",
"me":"application/x-troff-me",
"midi":"audio/midi",
"mif":"application/x-mif",
"mime":"www/mime",
"movie":"video/x-sgi-movie",
"mustache":"text/plain",
"mp4":"video/mp4",
"mpg":"video/mpeg",
"mpga":"audio/mpeg",
"ms":"application/x-troff-ms",
"nc":"application/x-netcdf",
"oda":"application/oda",
"ogm":"application/ogg",
"pbm":"image/x-portable-bitmap",
"pdf":"application/pdf",
"pgm":"image/x-portable-graymap",
"pgn":"application/x-chess-pgn",
"pgp":"application/pgp",
"pm":"application/x-perl",
"png":"image/png",
"pnm":"image/x-portable-anymap",
"ppm":"image/x-portable-pixmap",
"ppz":"application/vnd.ms-powerpoint",
"pre":"application/x-freelance",
"prt":"application/pro_eng",
"ps":"application/postscript",
"qt":"video/quicktime",
"ra":"audio/x-realaudio",
"rar":"application/x-rar-compressed",
"ras":"image/x-cmu-raster",
"rgb":"image/x-rgb",
"rm":"audio/x-pn-realaudio",
"rpm":"audio/x-pn-realaudio-plugin",
"rtf":"text/rtf",
"rtx":"text/richtext",
"scm":"application/x-lotusscreencam",
"set":"application/set",
"sgml":"text/sgml",
"sh":"application/x-sh",
"shar":"application/x-shar",
"silo":"model/mesh",
"sit":"application/x-stuffit",
"skt":"application/x-koan",
"smil":"application/smil",
"snd":"audio/basic",
"sol":"application/solids",
"spl":"application/x-futuresplash",
"src":"application/x-wais-source",
"stl":"application/SLA",
"stp":"application/STEP",
"sv4cpio":"application/x-sv4cpio",
"sv4crc":"application/x-sv4crc",
"svg":"image/svg+xml",
"swf":"application/x-shockwave-flash",
"tar":"application/x-tar",
"tcl":"application/x-tcl",
"tex":"application/x-tex",
"texinfo":"application/x-texinfo",
"tgz":"application/x-tar-gz",
"tiff":"image/tiff",
"tr":"application/x-troff",
"tsi":"audio/TSP-audio",
"tsp":"application/dsptype",
"tsv":"text/tab-separated-values",
"unv":"application/i-deas",
"ustar":"application/x-ustar",
"vcd":"application/x-cdlink",
"vda":"application/vda",
"vivo":"video/vnd.vivo",
"vrm":"x-world/x-vrml",
"wav":"audio/x-wav",
"wax":"audio/x-ms-wax",
"wma":"audio/x-ms-wma",
"wmv":"video/x-ms-wmv",
"wmx":"video/x-ms-wmx",
"wrl":"model/vrml",
"wvx":"video/x-ms-wvx",
"xbm":"image/x-xbitmap",
"xlw":"application/vnd.ms-excel",
"xml":"text/xml",
"xpm":"image/x-xpixmap",
"xwd":"image/x-xwindowdump",
"xyz":"chemical/x-pdb",
"zip":"application/zip",
};
exports.lookup = function(ext, defaultType) {
defaultType = defaultType || 'application/octet-stream';
return (ext in exports.types)
? exports.types[ext]
: defaultType;
};
+32 -12
View File
@@ -6,18 +6,38 @@ function sha1 (key, body) {
return crypto.createHmac('sha1', key).update(body).digest('base64')
}
function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret, body) {
// adapted from https://dev.twitter.com/docs/auth/oauth
var base =
httpMethod + "&" +
encodeURIComponent( base_uri ) + "&" +
Object.keys(params).sort().map(function (i) {
// big WTF here with the escape + encoding but it's what twitter wants
return encodeURIComponent(qs.escape(i)) + "%3D" + encodeURIComponent(qs.escape(params[i]))
}).join("%26")
var key = consumer_secret + '&'
if (token_secret) key += token_secret
function rfc3986 (str) {
return encodeURIComponent(str)
.replace(/!/g,'%21')
.replace(/\*/g,'%2A')
.replace(/\(/g,'%28')
.replace(/\)/g,'%29')
.replace(/'/g,'%27')
;
}
function hmacsign (httpMethod, base_uri, params, consumer_secret, token_secret) {
// adapted from https://dev.twitter.com/docs/auth/oauth and
// https://dev.twitter.com/docs/auth/creating-signature
var querystring = Object.keys(params).sort().map(function(key){
// big WTF here with the escape + encoding but it's what twitter wants
return escape(rfc3986(key)) + "%3D" + escape(rfc3986(params[key]))
}).join('%26')
var base = [
httpMethod ? httpMethod.toUpperCase() : 'GET',
rfc3986(base_uri),
querystring
].join('&')
var key = [
consumer_secret,
token_secret || ''
].map(rfc3986).join('&')
return sha1(key, base)
}
exports.hmacsign = hmacsign
exports.hmacsign = hmacsign
exports.rfc3986 = rfc3986
+40 -14
View File
File diff suppressed because one or more lines are too long
-6
View File
@@ -1,6 +0,0 @@
FAILS=0
for i in tests/test-*.js; do
echo $i
node $i || let FAILS++
done
exit $FAILS
+40 -7
View File
@@ -1,4 +1,7 @@
var http = require('http')
var fs = require('fs')
, http = require('http')
, path = require('path')
, https = require('https')
, events = require('events')
, stream = require('stream')
, assert = require('assert')
@@ -14,6 +17,28 @@ exports.createServer = function (port) {
return s;
}
exports.createSSLServer = function(port, opts) {
port = port || 16767
var options = { 'key' : path.join(__dirname, 'ssl', 'test.key')
, 'cert': path.join(__dirname, 'ssl', 'test.crt')
}
if (opts) {
for (var i in opts) options[i] = opts[i]
}
for (var i in options) {
options[i] = fs.readFileSync(options[i])
}
var s = https.createServer(options, function (req, resp) {
s.emit(req.url, req, resp);
})
s.port = port
s.url = 'https://localhost:'+port
return s;
}
exports.createPostStream = function (text) {
var postStream = new stream.Stream();
postStream.writeable = true;
@@ -21,16 +46,24 @@ exports.createPostStream = function (text) {
setTimeout(function () {postStream.emit('data', new Buffer(text)); postStream.emit('end')}, 0);
return postStream;
}
exports.createPostValidator = function (text) {
exports.createPostValidator = function (text, reqContentType) {
var l = function (req, resp) {
var r = '';
req.on('data', function (chunk) {r += chunk})
req.on('end', function () {
if (r !== text) console.log(r, text);
assert.equal(r, text)
resp.writeHead(200, {'content-type':'text/plain'})
resp.write('OK')
resp.end()
if (req.headers['content-type'] && req.headers['content-type'].indexOf('boundary=') >= 0) {
var boundary = req.headers['content-type'].split('boundary=')[1];
text = text.replace(/__BOUNDARY__/g, boundary);
}
if (r !== text) console.log(r, text);
assert.equal(r, text)
if (reqContentType) {
assert.ok(req.headers['content-type'])
assert.ok(~req.headers['content-type'].indexOf(reqContentType))
}
resp.writeHead(200, {'content-type':'text/plain'})
resp.write('OK')
resp.end()
})
}
return l;
+34 -7
View File
@@ -25,11 +25,21 @@ var tests =
])
, expectBody: "Ω☃"
}
, testGetJSON :
{ resp : server.createGetResponse('{"test":true}', 'application/json')
, json : true
, expectBody: {"test":true}
, testGetBuffer :
{ resp : server.createGetResponse(new Buffer("TESTING!"))
, encoding: null
, expectBody: new Buffer("TESTING!")
}
, testGetEncoding :
{ resp : server.createGetResponse(new Buffer('efa3bfcea9e29883', 'hex'))
, encoding: 'hex'
, expectBody: "efa3bfcea9e29883"
}
, testGetJSON :
{ resp : server.createGetResponse('{"test":true}', 'application/json')
, json : true
, expectBody: {"test":true}
}
, testPutString :
{ resp : server.createPostValidator("PUTTINGDATA")
, method : "PUT"
@@ -47,13 +57,13 @@ var tests =
}
, testPutMultipart :
{ resp: server.createPostValidator(
'--frontier\r\n' +
'--__BOUNDARY__\r\n' +
'content-type: text/html\r\n' +
'\r\n' +
'<html><body>Oh hi.</body></html>' +
'\r\n--frontier\r\n\r\n' +
'\r\n--__BOUNDARY__\r\n\r\n' +
'Oh hi.' +
'\r\n--frontier--'
'\r\n--__BOUNDARY__--'
)
, method: "PUT"
, multipart:
@@ -61,6 +71,23 @@ var tests =
, {'body': 'Oh hi.'}
]
}
, testPutMultipartPreambleCRLF :
{ resp: server.createPostValidator(
'\r\n--__BOUNDARY__\r\n' +
'content-type: text/html\r\n' +
'\r\n' +
'<html><body>Oh hi.</body></html>' +
'\r\n--__BOUNDARY__\r\n\r\n' +
'Oh hi.' +
'\r\n--__BOUNDARY__--'
)
, method: "PUT"
, preambleCRLF: true
, multipart:
[ {'content-type': 'text/html', 'body': '<html><body>Oh hi.</body></html>'}
, {'body': 'Oh hi.'}
]
}
}
s.listen(s.port, function () {
+3 -3
View File
@@ -1,7 +1,7 @@
var Cookie = require('../vendor/cookie')
, assert = require('assert');
var str = 'sid=s543qactge.wKE61E01Bs%2BKhzmxrwrnug; path=/; httpOnly; expires=Sat, 04 Dec 2010 23:27:28 GMT';
var str = 'Sid="s543qactge.wKE61E01Bs%2BKhzmxrwrnug="; Path=/; httpOnly; Expires=Sat, 04 Dec 2010 23:27:28 GMT';
var cookie = new Cookie(str);
// test .toString()
@@ -14,10 +14,10 @@ assert.equal(cookie.path, '/');
assert.equal(cookie.httpOnly, true);
// test .name
assert.equal(cookie.name, 'sid');
assert.equal(cookie.name, 'Sid');
// test .value
assert.equal(cookie.value, 's543qactge.wKE61E01Bs%2BKhzmxrwrnug');
assert.equal(cookie.value, '"s543qactge.wKE61E01Bs%2BKhzmxrwrnug="');
// test .expires
assert.equal(cookie.expires instanceof Date, true);
+7
View File
@@ -27,4 +27,11 @@ try {
assert.equal(e.message, 'Body attribute missing in multipart.')
}
try {
request(local, {multipart: [{}]})
assert.fail("Should have throw")
} catch(e) {
assert.equal(e.message, 'Body attribute missing in multipart.')
}
console.log("All tests passed.")
+18 -10
View File
@@ -6,7 +6,7 @@ var hmacsign = require('../oauth').hmacsign
function getsignature (r) {
var sign
r.headers.authorization.slice('OAuth '.length).replace(/,\ /g, ',').split(',').forEach(function (v) {
r.headers.Authorization.slice('OAuth '.length).replace(/,\ /g, ',').split(',').forEach(function (v) {
if (v.slice(0, 'oauth_signature="'.length) === 'oauth_signature="') sign = v.slice('oauth_signature="'.length, -1)
})
return decodeURIComponent(sign)
@@ -56,7 +56,7 @@ console.log('yOahq5m0YjDDjfjxHaXEsW9D+X0=')
assert.equal(upsign, 'yOahq5m0YjDDjfjxHaXEsW9D+X0=')
var r = request.post(
var rsign = request.post(
{ url: 'https://api.twitter.com/oauth/request_token'
, oauth:
{ callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11'
@@ -68,10 +68,12 @@ var r = request.post(
}
})
console.log(getsignature(r))
assert.equal(reqsign, getsignature(r))
setTimeout(function () {
console.log(getsignature(rsign))
assert.equal(reqsign, getsignature(rsign))
})
var r = request.post(
var raccsign = request.post(
{ url: 'https://api.twitter.com/oauth/access_token'
, oauth:
{ consumer_key: 'GDdmIQH6jhtmLUypg82g'
@@ -86,10 +88,12 @@ var r = request.post(
}
})
console.log(getsignature(r))
assert.equal(accsign, getsignature(r))
setTimeout(function () {
console.log(getsignature(raccsign))
assert.equal(accsign, getsignature(raccsign))
}, 1)
var r = request.post(
var rupsign = request.post(
{ url: 'http://api.twitter.com/1/statuses/update.json'
, oauth:
{ consumer_key: "GDdmIQH6jhtmLUypg82g"
@@ -103,7 +107,11 @@ var r = request.post(
}
, form: {status: 'setting up my twitter 私のさえずりを設定する'}
})
setTimeout(function () {
console.log(getsignature(rupsign))
assert.equal(upsign, getsignature(rupsign))
}, 1)
console.log(getsignature(r))
assert.equal(upsign, getsignature(r))
+50 -1
View File
@@ -58,6 +58,20 @@ s.listen(s.port, function () {
mydata.emit('data', 'mydata');
mydata.emit('end');
// Test pipeing to a request object with a json body
s.once('/push-json', server.createPostValidator("{\"foo\":\"bar\"}", "application/json"));
var mybodydata = new stream.Stream();
mybodydata.readable = true
counter++
var r2 = request.put({url:'http://localhost:3453/push-json',json:true}, function () {
check();
})
mybodydata.pipe(r2)
mybodydata.emit('data', JSON.stringify({foo:"bar"}));
mybodydata.emit('end');
// Test pipeing from a request object.
s.once('/pull', server.createGetResponse("mypulldata"));
@@ -99,7 +113,7 @@ s.listen(s.port, function () {
})
s.on('/pushjs', function (req, resp) {
if (req.method === "PUT") {
assert.equal(req.headers['content-type'], 'text/javascript');
assert.equal(req.headers['content-type'], 'application/javascript');
check();
}
})
@@ -163,5 +177,40 @@ s.listen(s.port, function () {
afterresp.pipe(v)
v.on('end', check)
})
s.on('/forward1', function (req, resp) {
resp.writeHead(302, {location:'/forward2'})
resp.end()
})
s.on('/forward2', function (req, resp) {
resp.writeHead('200', {'content-type':'image/png'})
resp.write('d')
resp.end()
})
counter++
var validateForward = new ValidationStream('d')
validateForward.on('end', check)
request.get('http://localhost:3453/forward1').pipe(validateForward)
// Test pipe options
s.once('/opts', server.createGetResponse('opts response'));
var optsStream = new stream.Stream();
optsStream.writable = true
var optsData = '';
optsStream.write = function (buf) {
optsData += buf;
if (optsData === 'opts response') {
setTimeout(check, 10);
}
}
optsStream.end = function () {
assert.fail('end called')
};
counter++
request({url:'http://localhost:3453/opts'}).pipe(optsStream, { end : false })
})
+6 -6
View File
@@ -1,19 +1,19 @@
module.exports = function () {
var s = [], itoh = '0123456789ABCDEF';
var s = [], itoh = '0123456789ABCDEF'
// Make array of random hex digits. The UUID only has 32 digits in it, but we
// allocate an extra items to make room for the '-'s we'll be inserting.
for (var i = 0; i <36; i++) s[i] = Math.floor(Math.random()*0x10);
for (var i = 0; i <36; i++) s[i] = Math.floor(Math.random()*0x10)
// Conform to RFC-4122, section 4.4
s[14] = 4; // Set 4 high bits of time_high field to version
s[19] = (s[19] & 0x3) | 0x8; // Specify 2 high bits of clock sequence
s[19] = (s[19] & 0x3) | 0x8 // Specify 2 high bits of clock sequence
// Convert to hex chars
for (var i = 0; i <36; i++) s[i] = itoh[s[i]];
for (var i = 0; i <36; i++) s[i] = itoh[s[i]]
// Insert '-'s
s[8] = s[13] = s[18] = s[23] = '-';
s[8] = s[13] = s[18] = s[23] = '-'
return s.join('');
return s.join('')
}
+19 -11
View File
@@ -21,18 +21,26 @@ var url = require('url');
var Cookie = exports = module.exports = function Cookie(str, req) {
this.str = str;
// First key is the name
this.name = str.substr(0, str.indexOf('='));
// Map the key/val pairs
str.split(/ *; */).reduce(function(obj, pair){
pair = pair.split(/ *= */);
obj[pair[0]] = pair[1] || true;
return obj;
}, this);
var p = pair.indexOf('=');
var key = p > 0 ? pair.substring(0, p).trim() : pair.trim();
var lowerCasedKey = key.toLowerCase();
var value = p > 0 ? pair.substring(p + 1).trim() : true;
// Assign value
this.value = this[this.name];
if (!obj.name) {
// First key is the name
obj.name = key;
obj.value = value;
}
else if (lowerCasedKey === 'httponly') {
obj.httpOnly = value;
}
else {
obj[lowerCasedKey] = value;
}
return obj;
}, this);
// Expires
this.expires = this.expires
@@ -41,8 +49,8 @@ var Cookie = exports = module.exports = function Cookie(str, req) {
// Default or trim path
this.path = this.path
? this.path.trim()
: url.parse(req.url).pathname;
? this.path.trim(): req
? url.parse(req.url).pathname: '/';
};
/**