pax_global_header00006660000000000000000000000064122004731120014502gustar00rootroot0000000000000052 comment=b422510ba16315c3e0e1293a17f3a8fa7a653a77 request-2.26.1/000077500000000000000000000000001220047311200132625ustar00rootroot00000000000000request-2.26.1/.gitignore000066400000000000000000000000151220047311200152460ustar00rootroot00000000000000node_modules request-2.26.1/LICENSE000066400000000000000000000216641220047311200143000ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONSrequest-2.26.1/README.md000066400000000000000000000301201220047311200145350ustar00rootroot00000000000000# Request -- Simplified HTTP client [![NPM](https://nodei.co/npm/request.png)](https://nodei.co/npm/request/) ## Super simple to use 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'); request('http://www.google.com', function (error, response, body) { if (!error && response.statusCode == 200) { console.log(body) // Print the google web page. } }) ``` ## Streaming You can stream any response to a file stream. ```javascript 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.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. ```javascript request.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png')) ``` Now let's get fancy. ```javascript http.createServer(function (req, resp) { if (req.url === '/doodle.png') { if (req.method === 'PUT') { req.pipe(request.put('http://mysite.com/doodle.png')) } else if (req.method === 'GET' || req.method === 'HEAD') { request.get('http://mysite.com/doodle.png').pipe(resp) } } }) ``` You can also pipe() from a http.ServerRequest instance and to a http.ServerResponse instance. The HTTP method and headers will be sent as well as the entity-body data. Which means that, if you don't really care about security, you can do: ```javascript http.createServer(function (req, resp) { if (req.url === '/doodle.png') { var x = request('http://mysite.com/doodle.png') req.pipe(x) x.pipe(resp) } }) ``` And since pipe() returns the destination stream in node 0.5.x you can do one line proxying :) ```javascript req.pipe(request('http://mysite.com/doodle.png')).pipe(resp) ``` Also, none of this new functionality conflicts with requests previous features, it just expands them. ```javascript var r = request.defaults({'proxy':'http://localproxy.com'}) http.createServer(function (req, resp) { if (req.url === '/doodle.png') { r.get('http://google.com/doodle.png').pipe(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')) ``` ## HTTP Authentication ```javascript request.get('http://some.server.com/').auth('username', 'password', false); // or request.get('http://some.server.com/', { 'auth': { 'user': 'username', 'pass': 'password', 'sendImmediately': false } }); ``` If passed as an option, `auth` should be a hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). The method form takes parameters `auth(username, password, sendImmediately)`. `sendImmediately` defaults to true, which will cause a basic authentication header to be sent. If `sendImmediately` is `false`, then `request` will retry with a proper authentication header after receiving a 401 response from the server (which must contain a `WWW-Authenticate` header indicating the required authentication method). Digest authentication is supported, but it only works with `sendImmediately` set to `false` (otherwise `request` will send basic authentication on the initial request, which will probably cause the request to fail). ## OAuth Signing ```javascript // Twitter OAuth var qs = require('querystring') , oauth = { callback: 'http://mysite.com/callback/' , consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET } , url = 'https://api.twitter.com/oauth/request_token' ; request.post({url:url, oauth:oauth}, function (e, r, body) { // Ideally, you would take the body in the response // and construct a URL that a user clicks on (like a sign in button). // The verifier is only available in the response after a user has // verified with twitter that they are authorizing your app. var access_token = qs.parse(body) , oauth = { consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET , token: access_token.oauth_token , verifier: access_token.oauth_verifier } , url = 'https://api.twitter.com/oauth/access_token' ; request.post({url:url, oauth:oauth}, function (e, r, body) { var perm_token = qs.parse(body) , oauth = { consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET , token: perm_token.oauth_token , token_secret: perm_token.oauth_token_secret } , url = 'https://api.twitter.com/1/users/show.json?' , params = { screen_name: perm_token.screen_name , user_id: perm_token.user_id } ; url += qs.stringify(params) request.get({url:url, oauth:oauth, json:true}, function (e, r, user) { console.log(user) }) }) }) ``` ### request(options, callback) 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 PATCH, POST and PUT requests. Must be buffer or string. * `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. * `auth` - A hash containing values `user` || `username`, `password` || `pass`, and `sendImmediately` (optional). See documentation above. * `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. * `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 * `proxy` - An HTTP proxy to be used. Support proxy Auth with Basic Auth the same way it's supported with the `url` parameter by embedding the auth info in the uri. * `oauth` - Options for OAuth HMAC-SHA1 signing, see documentation above. * `hawk` - Options for [Hawk signing](https://github.com/hueniverse/hawk). The `credentials` key must contain the necessary signing info, [see hawk docs for details](https://github.com/hueniverse/hawk#usage-example). * `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 `true` if you 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) * `httpSignature` - Options for the [HTTP Signature Scheme](https://github.com/joyent/node-http-signature/blob/master/http_signing.md) using [Joyent's library](https://github.com/joyent/node-http-signature). The `keyId` and `key` properties must be specified. See the docs for other options. * `localAddress` - Local interface to bind for network connections. 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 is an http.ClientResponse object. The third is the response body String or Buffer. ## Convenience methods There are also shorthand methods for different HTTP METHODs and some other conveniences. ### request.defaults(options) This method returns a wrapper around the normal request API that defaults to whatever options you pass in to it. ### request.put Same as request() but defaults to `method: "PUT"`. ```javascript request.put(url) ``` ### request.patch Same as request() but defaults to `method: "PATCH"`. ```javascript request.patch(url) ``` ### request.post Same as request() but defaults to `method: "POST"`. ```javascript request.post(url) ``` ### request.head Same as request() but defaults to `method: "HEAD"`. ```javascript request.head(url) ``` ### request.del Same as request() but defaults to `method: "DELETE"`. ```javascript request.del(url) ``` ### request.get Alias to normal request method for uniformity. ```javascript request.get(url) ``` ### request.cookie Function that creates a new cookie. ```javascript request.cookie('cookie_string_here') ``` ### request.jar Function that creates a new cookie jar. ```javascript request.jar() ``` ## Examples: ```javascript var request = require('request') , rand = Math.floor(Math.random()*100000000).toString() ; request( { method: 'PUT' , 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' }}}) } , { body: 'I am an attachment' } ] } , function (error, response, body) { if(response.statusCode == 201){ console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand) } else { console.log('error: '+ response.statusCode) console.log(body) } } ) ``` Cookies are disabled by default (else, they would be used in subsequent requests). To enable cookies set jar to true (either in defaults or in the options sent). ```javascript var request = request.defaults({jar: true}) request('http://www.google.com', function () { request('http://images.google.com') }) ``` If you to use a custom cookie jar (instead of letting request use its own global cookie jar) you do so by setting the jar default or by specifying it as an option: ```javascript var j = request.jar() var request = request.defaults({jar:j}) request('http://www.google.com', function () { request('http://images.google.com') }) ``` OR ```javascript var j = request.jar() var cookie = request.cookie('your_cookie_here') j.add(cookie) request({url: 'http://www.google.com', jar: j}, function () { request('http://images.google.com') }) ``` request-2.26.1/index.js000077500000000000000000001231531220047311200147370ustar00rootroot00000000000000// Copyright 2010-2012 Mikeal Rogers // // 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. var http = require('http') , https = false , tls = false , url = require('url') , util = require('util') , stream = require('stream') , qs = require('qs') , querystring = require('querystring') , crypto = require('crypto') , oauth = require('oauth-sign') , hawk = require('hawk') , aws = require('aws-sign') , httpSignature = require('http-signature') , uuid = require('node-uuid') , mime = require('mime') , tunnel = require('tunnel-agent') , safeStringify = require('json-stringify-safe') , ForeverAgent = require('forever-agent') , FormData = require('form-data') , Cookie = require('cookie-jar') , CookieJar = Cookie.Jar , cookieJar = new CookieJar ; try { https = require('https') } catch (e) {} try { tls = require('tls') } catch (e) {} var debug if (/\brequest\b/.test(process.env.NODE_DEBUG)) { debug = function() { console.error('REQUEST %s', util.format.apply(util, arguments)) } } else { debug = function() {} } function toBase64 (str) { return (new Buffer(str || "", "ascii")).toString("base64") } function md5 (str) { return crypto.createHash('md5').update(str).digest('hex') } // Hacky fix for pre-0.4.4 https if (https && !https.Agent) { https.Agent = function (options) { http.Agent.call(this, options) } util.inherits(https.Agent, http.Agent) https.Agent.prototype._getConnection = function (host, port, cb) { var s = tls.connect(port, host, this.options, function () { // do other checks here? if (cb) cb() }) return s } } function isReadStream (rs) { if (rs.readable && rs.path && rs.mode) { return true } } function copy (obj) { var o = {} Object.keys(obj).forEach(function (i) { o[i] = obj[i] }) return o } var isUrl = /^https?:/i var globalPool = {} function Request (options) { stream.Stream.call(this) this.readable = true this.writable = true if (typeof options === 'string') { options = {uri:options} } var reserved = Object.keys(Request.prototype) for (var i in options) { if (reserved.indexOf(i) === -1) { this[i] = options[i] } else { if (typeof options[i] === 'function') { delete options[i] } } } if (options.method) { this.explicitMethod = true } this.init(options) } util.inherits(Request, stream.Stream) Request.prototype.init = function (options) { // init() contains all the code to setup the request object. // the actual outgoing request is not started until start() is called // this function is called from both the constructor and on redirect. var self = this if (!options) options = {} if (!self.method) self.method = options.method || 'GET' self.localAddress = options.localAddress debug(options) if (!self.pool && self.pool !== false) self.pool = globalPool self.dests = self.dests || [] self.__isRequestRequest = true // Protect against double callback if (!self._callback && self.callback) { self._callback = self.callback self.callback = function () { if (self._callbackCalled) return // Print a warning maybe? self._callbackCalled = true self._callback.apply(self, arguments) } self.on('error', self.callback.bind()) self.on('complete', self.callback.bind(self, null)) } if (self.url && !self.uri) { // People use this property instead all the time so why not just support it. self.uri = self.url delete self.url } if (!self.uri) { // this will throw if unhandled but is handleable when in a redirect return self.emit('error', new Error("options.uri is a required argument")) } else { if (typeof self.uri == "string") self.uri = url.parse(self.uri) } if (self.strictSSL === false) { self.rejectUnauthorized = false } if (self.proxy) { if (typeof self.proxy == 'string') self.proxy = url.parse(self.proxy) // do the HTTP CONNECT dance using koichik/node-tunnel if (http.globalAgent && self.uri.protocol === "https:") { var tunnelFn = self.proxy.protocol === "http:" ? tunnel.httpsOverHttp : tunnel.httpsOverHttps var tunnelOptions = { proxy: { host: self.proxy.hostname , port: +self.proxy.port , proxyAuth: self.proxy.auth , headers: { Host: self.uri.hostname + ':' + (self.uri.port || self.uri.protocol === 'https:' ? 443 : 80) }} , rejectUnauthorized: self.rejectUnauthorized , ca: this.ca } self.agent = tunnelFn(tunnelOptions) self.tunnel = true } } if (!self.uri.pathname) {self.uri.pathname = '/'} if (!self.uri.host) { // Invalid URI: it may generate lot of bad errors, like "TypeError: Cannot call method 'indexOf' of undefined" in CookieJar // Detect and reject it as soon as possible var faultyUri = url.format(self.uri) var message = 'Invalid URI "' + faultyUri + '"' if (Object.keys(options).length === 0) { // No option ? This can be the sign of a redirect // As this is a case where the user cannot do anything (he didn't call request directly with this URL) // he should be warned that it can be caused by a redirection (can save some hair) message += '. This can be caused by a crappy redirection.' } self.emit('error', new Error(message)) return // This error was fatal } self._redirectsFollowed = self._redirectsFollowed || 0 self.maxRedirects = (self.maxRedirects !== undefined) ? self.maxRedirects : 10 self.followRedirect = (self.followRedirect !== undefined) ? self.followRedirect : true self.followAllRedirects = (self.followAllRedirects !== undefined) ? self.followAllRedirects : false if (self.followRedirect || self.followAllRedirects) self.redirects = self.redirects || [] self.headers = self.headers ? copy(self.headers) : {} self.setHost = false if (!self.hasHeader('host')) { self.setHeader('host', self.uri.hostname) if (self.uri.port) { if ( !(self.uri.port === 80 && self.uri.protocol === 'http:') && !(self.uri.port === 443 && self.uri.protocol === 'https:') ) self.setHeader('host', self.getHeader('host') + (':'+self.uri.port) ) } self.setHost = true } self.jar(self._jar || options.jar) if (!self.uri.port) { if (self.uri.protocol == 'http:') {self.uri.port = 80} else if (self.uri.protocol == 'https:') {self.uri.port = 443} } if (self.proxy && !self.tunnel) { self.port = self.proxy.port self.host = self.proxy.hostname } else { self.port = self.uri.port self.host = self.uri.hostname } self.clientErrorHandler = function (error) { if (self._aborted) return if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' && self.agent.addRequestNoreuse) { self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) } self.start() self.req.end() return } if (self.timeout && self.timeoutTimer) { clearTimeout(self.timeoutTimer) self.timeoutTimer = null } self.emit('error', error) } self._parserErrorHandler = function (error) { if (this.res) { if (this.res.request) { this.res.request.emit('error', error) } else { this.res.emit('error', error) } } else { this._httpMessage.emit('error', error) } } if (options.form) { self.form(options.form) } if (options.qs) self.qs(options.qs) if (self.uri.path) { self.path = self.uri.path } else { self.path = self.uri.pathname + (self.uri.search || "") } if (self.path.length === 0) self.path = '/' // Auth must happen last in case signing is dependent on other headers if (options.oauth) { self.oauth(options.oauth) } if (options.aws) { self.aws(options.aws) } if (options.hawk) { self.hawk(options.hawk) } if (options.httpSignature) { self.httpSignature(options.httpSignature) } if (options.auth) { self.auth( (options.auth.user==="") ? options.auth.user : (options.auth.user || options.auth.username ), options.auth.pass || options.auth.password, options.auth.sendImmediately) } if (self.uri.auth && !self.hasHeader('authorization')) { var authPieces = self.uri.auth.split(':').map(function(item){ return querystring.unescape(item) }) self.auth(authPieces[0], authPieces.slice(1).join(':'), true) } if (self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization') && !self.tunnel) { self.setHeader('proxy-authorization', "Basic " + toBase64(self.proxy.auth.split(':').map(function(item){ return querystring.unescape(item)}).join(':'))) } if (self.proxy && !self.tunnel) self.path = (self.uri.protocol + '//' + self.uri.host + self.path) if (options.json) { self.json(options.json) } else if (options.multipart) { self.boundary = uuid() self.multipart(options.multipart) } if (self.body) { var length = 0 if (!Buffer.isBuffer(self.body)) { if (Array.isArray(self.body)) { for (var i = 0; i < self.body.length; i++) { length += self.body[i].length } } else { self.body = new Buffer(self.body) length = self.body.length } } else { length = self.body.length } if (length) { if (!self.hasHeader('content-length')) self.setHeader('content-length', length) } else { throw new Error('Argument error, options.body.') } } var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol , defaultModules = {'http:':http, 'https:':https} , httpModules = self.httpModules || {} ; self.httpModule = httpModules[protocol] || defaultModules[protocol] if (!self.httpModule) return this.emit('error', new Error("Invalid protocol")) if (options.ca) self.ca = options.ca if (!self.agent) { if (options.agentOptions) self.agentOptions = options.agentOptions if (options.agentClass) { self.agentClass = options.agentClass } else if (options.forever) { self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL } else { self.agentClass = self.httpModule.Agent } } if (self.pool === false) { self.agent = false } else { self.agent = self.agent || self.getAgent() if (self.maxSockets) { // Don't use our pooling if node has the refactored client self.agent.maxSockets = self.maxSockets } if (self.pool.maxSockets) { // Don't use our pooling if node has the refactored client self.agent.maxSockets = self.pool.maxSockets } } self.on('pipe', function (src) { if (self.ntick && self._started) throw new Error("You cannot pipe to this stream after the outbound request has started.") self.src = src if (isReadStream(src)) { if (!self.hasHeader('content-type')) self.setHeader('content-type', mime.lookup(src.path)) } else { if (src.headers) { for (var i in src.headers) { if (!self.hasHeader(i)) { self.setHeader(i, src.headers[i]) } } } if (self._json && !self.hasHeader('content-type')) self.setHeader('content-type', 'application/json') if (src.method && !self.explicitMethod) { self.method = src.method } } // self.on('pipe', function () { // console.error("You have already piped to this stream. Pipeing twice is likely to break the request.") // }) }) process.nextTick(function () { if (self._aborted) return if (self._form) { self.setHeaders(self._form.getHeaders()) self._form.pipe(self) } if (self.body) { if (Array.isArray(self.body)) { self.body.forEach(function (part) { self.write(part) }) } else { self.write(self.body) } self.end() } else if (self.requestBodyStream) { console.warn("options.requestBodyStream is deprecated, please pass the request object to stream.pipe.") self.requestBodyStream.pipe(self) } else if (!self.src) { if (self.method !== 'GET' && typeof self.method !== 'undefined') { self.setHeader('content-length', 0) } self.end() } self.ntick = true }) } // Must call this when following a redirect from https to http or vice versa // Attempts to keep everything as identical as possible, but update the // httpModule, Tunneling agent, and/or Forever Agent in use. Request.prototype._updateProtocol = function () { var self = this var protocol = self.uri.protocol if (protocol === 'https:') { // previously was doing http, now doing https // if it's https, then we might need to tunnel now. if (self.proxy) { self.tunnel = true var tunnelFn = self.proxy.protocol === 'http:' ? tunnel.httpsOverHttp : tunnel.httpsOverHttps var tunnelOptions = { proxy: { host: self.proxy.hostname , port: +self.proxy.port , proxyAuth: self.proxy.auth } , rejectUnauthorized: self.rejectUnauthorized , ca: self.ca } self.agent = tunnelFn(tunnelOptions) return } self.httpModule = https switch (self.agentClass) { case ForeverAgent: self.agentClass = ForeverAgent.SSL break case http.Agent: self.agentClass = https.Agent break default: // nothing we can do. Just hope for the best. return } // if there's an agent, we need to get a new one. if (self.agent) self.agent = self.getAgent() } else { // previously was doing https, now doing http // stop any tunneling. if (self.tunnel) self.tunnel = false self.httpModule = http switch (self.agentClass) { case ForeverAgent.SSL: self.agentClass = ForeverAgent break case https.Agent: self.agentClass = http.Agent break default: // nothing we can do. just hope for the best return } // if there's an agent, then get a new one. if (self.agent) { self.agent = null self.agent = self.getAgent() } } } Request.prototype.getAgent = function () { var Agent = this.agentClass var options = {} if (this.agentOptions) { for (var i in this.agentOptions) { options[i] = this.agentOptions[i] } } if (this.ca) options.ca = this.ca if (typeof this.rejectUnauthorized !== 'undefined') options.rejectUnauthorized = this.rejectUnauthorized if (this.cert && this.key) { options.key = this.key options.cert = this.cert } var poolKey = '' // different types of agents are in different pools if (Agent !== this.httpModule.Agent) { poolKey += Agent.name } if (!this.httpModule.globalAgent) { // node 0.4.x options.host = this.host options.port = this.port if (poolKey) poolKey += ':' poolKey += this.host + ':' + this.port } // ca option is only relevant if proxy or destination are https var proxy = this.proxy if (typeof proxy === 'string') proxy = url.parse(proxy) var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:' if (isHttps) { if (options.ca) { if (poolKey) poolKey += ':' poolKey += options.ca } if (typeof options.rejectUnauthorized !== 'undefined') { if (poolKey) poolKey += ':' poolKey += options.rejectUnauthorized } if (options.cert) poolKey += options.cert.toString('ascii') + options.key.toString('ascii') if (options.ciphers) { if (poolKey) poolKey += ':' poolKey += options.ciphers } if (options.secureOptions) { if (poolKey) poolKey += ':' poolKey += options.secureOptions } } if (this.pool === globalPool && !poolKey && Object.keys(options).length === 0 && this.httpModule.globalAgent) { // not doing anything special. Use the globalAgent return this.httpModule.globalAgent } // we're using a stored agent. Make sure it's protocol-specific poolKey = this.uri.protocol + poolKey // already generated an agent for this setting if (this.pool[poolKey]) return this.pool[poolKey] return this.pool[poolKey] = new Agent(options) } Request.prototype.start = function () { // start() is called once we are ready to send the outgoing HTTP request. // this is usually called on the first write(), end() or on nextTick() var self = this if (self._aborted) return self._started = true self.method = self.method || 'GET' self.href = self.uri.href if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) { self.setHeader('content-length', self.src.stat.size) } if (self._aws) { self.aws(self._aws, true) } // We have a method named auth, which is completely different from the http.request // auth option. If we don't remove it, we're gonna have a bad time. var reqOptions = copy(self) delete reqOptions.auth debug('make request', self.uri.href) self.req = self.httpModule.request(reqOptions, self.onResponse.bind(self)) if (self.timeout && !self.timeoutTimer) { self.timeoutTimer = setTimeout(function () { self.req.abort() var e = new Error("ETIMEDOUT") e.code = "ETIMEDOUT" self.emit("error", e) }, self.timeout) // Set additional timeout on socket - in case if remote // server freeze after sending headers if (self.req.setTimeout) { // only works on node 0.6+ self.req.setTimeout(self.timeout, function () { if (self.req) { self.req.abort() var e = new Error("ESOCKETTIMEDOUT") e.code = "ESOCKETTIMEDOUT" self.emit("error", e) } }) } } self.req.on('error', self.clientErrorHandler) self.req.on('drain', function() { self.emit('drain') }) self.on('end', function() { if ( self.req.connection ) self.req.connection.removeListener('error', self._parserErrorHandler) }) self.emit('request', self.req) } Request.prototype.onResponse = function (response) { var self = this debug('onResponse', self.uri.href, response.statusCode, response.headers) response.on('end', function() { debug('response end', self.uri.href, response.statusCode, response.headers) }); if (response.connection.listeners('error').indexOf(self._parserErrorHandler) === -1) { response.connection.once('error', self._parserErrorHandler) } if (self._aborted) { debug('aborted', self.uri.href) response.resume() return } if (self._paused) response.pause() else response.resume() self.response = response response.request = self response.toJSON = toJSON // XXX This is different on 0.10, because SSL is strict by default if (self.httpModule === https && self.strictSSL && !response.client.authorized) { debug('strict ssl error', self.uri.href) var sslErr = response.client.authorizationError self.emit('error', new Error('SSL Error: '+ sslErr)) return } if (self.setHost && self.hasHeader('host')) delete self.headers[self.hasHeader('host')] if (self.timeout && self.timeoutTimer) { clearTimeout(self.timeoutTimer) self.timeoutTimer = null } var addCookie = function (cookie) { if (self._jar){ if(self._jar.add){ self._jar.add(new Cookie(cookie)) } else cookieJar.add(new Cookie(cookie)) } } if (hasHeader('set-cookie', response.headers) && (!self._disableCookies)) { var headerName = hasHeader('set-cookie', response.headers) if (Array.isArray(response.headers[headerName])) response.headers[headerName].forEach(addCookie) else addCookie(response.headers[headerName]) } var redirectTo = null if (response.statusCode >= 300 && response.statusCode < 400 && hasHeader('location', response.headers)) { var location = response.headers[hasHeader('location', response.headers)] debug('redirect', location) if (self.followAllRedirects) { redirectTo = location } else if (self.followRedirect) { switch (self.method) { case 'PATCH': case 'PUT': case 'POST': case 'DELETE': // Do not follow redirects break default: redirectTo = location break } } } else if (response.statusCode == 401 && self._hasAuth && !self._sentAuth) { var authHeader = response.headers[hasHeader('www-authenticate', response.headers)] var authVerb = authHeader && authHeader.split(' ')[0] debug('reauth', authVerb) switch (authVerb) { case 'Basic': self.auth(self._user, self._pass, true) redirectTo = self.uri break case 'Digest': // TODO: More complete implementation of RFC 2617. For reference: // http://tools.ietf.org/html/rfc2617#section-3 // https://github.com/bagder/curl/blob/master/lib/http_digest.c var matches = authHeader.match(/([a-z0-9_-]+)="([^"]+)"/gi) var challenge = {} for (var i = 0; i < matches.length; i++) { var eqPos = matches[i].indexOf('=') var key = matches[i].substring(0, eqPos) var quotedValue = matches[i].substring(eqPos + 1) challenge[key] = quotedValue.substring(1, quotedValue.length - 1) } var ha1 = md5(self._user + ':' + challenge.realm + ':' + self._pass) var ha2 = md5(self.method + ':' + self.uri.path) var digestResponse = md5(ha1 + ':' + challenge.nonce + ':1::auth:' + ha2) var authValues = { username: self._user, realm: challenge.realm, nonce: challenge.nonce, uri: self.uri.path, qop: challenge.qop, response: digestResponse, nc: 1, cnonce: '' } authHeader = [] for (var k in authValues) { authHeader.push(k + '="' + authValues[k] + '"') } authHeader = 'Digest ' + authHeader.join(', ') self.setHeader('authorization', authHeader) self._sentAuth = true redirectTo = self.uri break } } if (redirectTo) { debug('redirect to', redirectTo) // ignore any potential response body. it cannot possibly be useful // to us at this point. if (self._paused) response.resume() if (self._redirectsFollowed >= self.maxRedirects) { self.emit('error', new Error("Exceeded maxRedirects. Probably stuck in a redirect loop "+self.uri.href)) return } self._redirectsFollowed += 1 if (!isUrl.test(redirectTo)) { redirectTo = url.resolve(self.uri.href, redirectTo) } var uriPrev = self.uri self.uri = url.parse(redirectTo) // handle the case where we change protocol from https to http or vice versa if (self.uri.protocol !== uriPrev.protocol) { self._updateProtocol() } self.redirects.push( { statusCode : response.statusCode , redirectUri: redirectTo } ) if (self.followAllRedirects && response.statusCode != 401) self.method = 'GET' // self.method = 'GET' // Force all redirects to use GET || commented out fixes #215 delete self.src delete self.req delete self.agent delete self._started if (response.statusCode != 401) { // Remove parameters from the previous response, unless this is the second request // for a server that requires digest authentication. delete self.body delete self._form if (self.headers) { if (self.hasHeader('host')) delete self.headers[self.hasHeader('host')] if (self.hasHeader('content-type')) delete self.headers[self.hasHeader('content-type')] if (self.hasHeader('content-length')) delete self.headers[self.hasHeader('content-length')] } } self.emit('redirect'); self.init() return // Ignore the rest of the response } else { self._redirectsFollowed = self._redirectsFollowed || 0 // Be a good stream and emit end when the response is finished. // Hack to emit end on close because of a core bug that never fires end response.on('close', function () { if (!self._ended) self.response.emit('end') }) if (self.encoding) { if (self.dests.length !== 0) { console.error("Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.") } else { response.setEncoding(self.encoding) } } self.emit('response', response) self.dests.forEach(function (dest) { self.pipeDest(dest) }) response.on("data", function (chunk) { self._destdata = true self.emit("data", chunk) }) response.on("end", function (chunk) { self._ended = true self.emit("end", chunk) }) response.on("close", function () {self.emit("close")}) if (self.callback) { var buffer = [] var bodyLen = 0 self.on("data", function (chunk) { buffer.push(chunk) bodyLen += chunk.length }) self.on("end", function () { debug('end event', self.uri.href) if (self._aborted) { debug('aborted', self.uri.href) return } if (buffer.length && Buffer.isBuffer(buffer[0])) { debug('has body', self.uri.href, bodyLen) var body = new Buffer(bodyLen) var i = 0 buffer.forEach(function (chunk) { chunk.copy(body, i, 0, chunk.length) i += chunk.length }) if (self.encoding === null) { response.body = body } else { response.body = body.toString(self.encoding) } } else if (buffer.length) { // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation. // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse(). if (self.encoding === 'utf8' && buffer[0].length > 0 && buffer[0][0] === "\uFEFF") { buffer[0] = buffer[0].substring(1) } response.body = buffer.join('') } if (self._json) { try { response.body = JSON.parse(response.body) } catch (e) {} } debug('emitting complete', self.uri.href) if(response.body == undefined && !self._json) { response.body = ""; } self.emit('complete', response, response.body) }) } //if no callback else{ self.on("end", function () { if (self._aborted) { debug('aborted', self.uri.href) return } self.emit('complete', response); }); } } debug('finish init function', self.uri.href) } Request.prototype.abort = function () { this._aborted = true if (this.req) { this.req.abort() } else if (this.response) { this.response.abort() } this.emit("abort") } Request.prototype.pipeDest = function (dest) { var response = this.response // Called after the response is received if (dest.headers && !dest.headersSent) { if (hasHeader('content-type', response.headers)) { var ctname = hasHeader('content-type', response.headers) if (dest.setHeader) dest.setHeader(ctname, response.headers[ctname]) else dest.headers[ctname] = response.headers[ctname] } if (hasHeader('content-length', response.headers)) { var clname = hasHeader('content-length', response.headers) if (dest.setHeader) dest.setHeader(clname, response.headers[clname]) else dest.headers[clname] = response.headers[clname] } } if (dest.setHeader && !dest.headersSent) { for (var i in response.headers) { dest.setHeader(i, response.headers[i]) } dest.statusCode = response.statusCode } if (this.pipefilter) this.pipefilter(response, dest) } // Composable API Request.prototype.setHeader = function (name, value, clobber) { if (clobber === undefined) clobber = true if (clobber || !this.hasHeader(name)) this.headers[name] = value else this.headers[this.hasHeader(name)] += ',' + value return this } Request.prototype.setHeaders = function (headers) { for (var i in headers) {this.setHeader(i, headers[i])} return this } Request.prototype.hasHeader = function (header, headers) { var headers = Object.keys(headers || this.headers) , lheaders = headers.map(function (h) {return h.toLowerCase()}) ; header = header.toLowerCase() for (var i=0;i", "repository": { "type": "git", "url": "http://github.com/mikeal/request.git" }, "bugs": { "url": "http://github.com/mikeal/request/issues" }, "engines": [ "node >= 0.8.0" ], "main": "index.js", "dependencies": { "qs": "~0.6.0", "json-stringify-safe": "~5.0.0", "forever-agent": "~0.5.0", "tunnel-agent": "~0.3.0", "http-signature": "~0.10.0", "hawk": "~1.0.0", "aws-sign": "~0.3.0", "oauth-sign": "~0.3.0", "cookie-jar": "~0.3.0", "node-uuid": "~1.4.0", "mime": "~1.2.9", "form-data": "~0.1.0" }, "scripts": { "test": "node tests/run.js" } } request-2.26.1/tests/000077500000000000000000000000001220047311200144245ustar00rootroot00000000000000request-2.26.1/tests/googledoodle.jpg000066400000000000000000001131561220047311200176000ustar00rootroot00000000000000CC   @! 1"A2Q #Baq$3Rbr%C F!1AQaq"2B#Rbr$3scCS„ ?DMh&;eۮmI!L<|.W_4<15e0,B WUn 0ۗ@tE]1R'DM4DDM4DDM4DDM4DDM4DDM4DDM4DDM4DDMi ̈́+Sbɗ\J~ QֻLPˤG[FGlQN B>M kk{C)8m.hkzd}F̚onAʶMR`&ZVZx!(ELQQ~o^L]G -#-Nȥb2&O2 ,!ziNRV{tz3V`9v1ц\A S$KȨȬITUU% MD  "kl1쑍7F wAy52&"h&"h&"h(,FN1C\1%K YصsWaǑF0 -p/=@EH*n}c~W/GrMieY݊AoxGcl2M2y6DADM~( RȘ@?@IEN51I)x;euL$^)]7^T^p<7vq]%GscjcyG#} uPM4E3uh42DMG5US 5E]C`{Z 'F(^=IP&(2q-8,cV 5EUH Nx5Wo֎ϣ0Ho Iv:4pr~aNj$QUD GIEDLrB)`y+X[ņ 幎cK$r7L$lv b5ԳN2A|Beޚ"h&"h&"h&"h&"h&"h&Odj󈶷e^qK Cz "1A@/H:-ٮi%1XK6q[y( #-cٵ[Mb 1ut{:ƕb6"Ɑ[lnI|}j7uci69vx֑/b\ln,X$MzlU&)&JW L(=RqƬT:Q${vVY<危䧖߳r9N&"h&"h&?7n+mczFV6\Twz ]_JSYsn)5MC*DUtpڭP;o]C(|X͒,W>]<9cZ!gYɸh'2dY~7ewꡏCPq̤ɏVy'$P9!Qe%+R9Y9"3tRqkHzu8x~A)#\8u kUҚ@OSRj.MÇ|솳ВYM\a2>mAj'&xIx´eE9 3H^ɠP~u˚av|٤mq! YG[)CMH3;7F>l(<%?⹑Z\ȿq0@95+/ZRQvqd2VK%3@64x4zUGYRI`9rǀWE@ǝJt$ / L#Y4-ipkCZN kFNNq|2["kCFy2r|2O%4DDMAia쿟Nձ5tw`%\]VTwˋv2dU2,z 8 HAPCJ'!MR@:'X|sshDM4DDM4DDM4DDMiݖIm%Y)hXjtw͎0A2-6"@H:֝v4kced44w jfvp{@kqBn8h%mX$`vhIW3^"t4$U)N%/P<:K,Ѵ;yq4`g^g8ճ0Ysiު!DT(Џq8?\i?v.*ZjR>ȕ'ȹ9pSVkE5›Y3qI%/>H`x8s3{dKXb_:9a@ fLDD~᫋ns28Ʒ+'$.0Qã&bQls5EYkOq7DDb.XKԪN"uM9=G.llkx[zTsgQq~ѨF^9YMfh{b=z1YHt9u."[~B$rHTIنTe3du۸f?nYtS"DɪB(ALP&"h&"h/BRR65qQ1m?pF,H9v"-۷Dc)Cy4TI<5ĀI9317$.w-nw16ĪձYd˖ A\RE@89**hrZO}h v]GeռR|U4l{Ei9?uuuL*]U1rSu<: TVkydc|,3;/Y`yDRXPU)((D jQԱ6;oR9X&[t4VacIijV,oA&m@N^&mU7HSr]d;=_I q;h'*qNWtY֞,BĬtT`!)Q웧۬!ƮW*QT3|X&7 {0Ȟ@1wRiEdMALr1Px(jiII+^;YR'aƹ.ؓ0;L6rZGD}dTJeXX3L9 w >aL7Y5]E׎bɌ11Ǒ3y1+6DZM8i]Σ36LucbߑBG:$]D"Tb 2R=.g,Hf|٣#l18rsƈ"h&"h&zC &^^:f%Bf7)@E'O&F7,K.E%{N=sz9vHj)4#2U5E߷nO&0L`|C)Nv7#ӭ Ҷ2Yq+E8/]i$FBd&"G0HPMJd$1LGQI5-1p'%oϓr-c[^O:RQSURiwH*RQyl Tao&WSg=zds^{9!Kor:sJUǧ<䫰#*&'8]੐# WSнUI;!ltQ7oy@]F-* dAj+G ew\'-i;T0$5odppVcSGTAV,^(?׽&RM2Y;I w^Oʆ7^{gwV  >bDh[S7I©̓_vuZ}s sр~ JY>Q )x԰ve}3I$wh+׻C(1c+1߽@+&uT-5? EnwYmoYT6S ˍ7*]UbgqԶ>VG2 w j^^J'jpȂL?`Gw#8^ֆ"ukn%cFs!\yZ$FUY2*ʽKnq i|)HvC]ŏKҚK%qGW Vܥ^r  @5 q "j*)L1tb҅("SGVe}uھ*zFt O@2O@z' @ ŽT Hq#3Cj4Fhkk~clqќyZAYb"p2l<>{` Cέ[Si,'iXK:ӆ+]ʸQ4D!5TC#IIڵw1yš6;/|6&5nAڊyefs'J3aoC|;|?dɴ9Spc6j]?~wVzЊ€(tDD^l+R,bbآwdݷ`tÓnɎ`(~𩩦JEN= kGszQ>k[6|sc.QP*0RSTt![e,Sx"n4,/s?O6@9(Ogc=5%DG戙 H{dpP$ֱ#U(N'v([.Dr2FGU@4 cLGLAN3BB"U2ftWl %P㏒ l*ZJh+)*%,cAE}Th*9':7-qrRr gDw_;4J-nQ}V}%k[uENRdh4{x\7>gjyZ24xZ2lDn LQ)ۨQ)n]p>ƨK0OQ/=Ro64lZ^`̎Z1pR~rTBlX(3 C(%1G]Ks5wCO5t/պV1c pe'RDDUX0 $NK)N7Y8נd H#luE@GZی~ӡu5N^PCs<`ZxE%!P~ʀ``R8(xW1-.E׫,1iF&19򰢏Д. y.ێ2[`q5v[VJi#bHƠŜI'F`C1uGhm/q3U_*skm #k1 *qhՙEǴBh!4n0h1j$nةTS)Rlh&"h&"94cQ? 5t29(b6us,sx5T#gع3[7*: `XJ- UI'\}ۇi9zӴ~gW)ɒJm^0蟁No"$ @=~dn s1SWXIyc2py7xP"W.tT!J&,冩K72d-!jLEbɔĊh&Q5\!.@J*[@%uqY/A D4p8Vǘ+1ǎ}uD:jL쵏4㿡1m" VL VКYT 5! x ۭ1f}p;׉Bt͠G}}Ual{$;XdDX1՛zQ0_NZnE. ĬZq$"㏐[) !JB8)JR?@(0Zf {:iq~LaUe"^Wr ,̆r IvL\(QS)ڽb(*[3DM4DB|])FRڕDL5CT6^[u jQM-Oc 1gI1=y2.;, zJ -.U3jU6jl6P2LP09PPLq k_Us#Bxch ĕ1P**#navBjh$Hb&(9UT1T1(r5~U@GeO=e]II?cZs7XrsA0!= )bR bϩ$W7Jp杰G#䡌-L[1R><֢m}&XU# k#hkʎ'nC ;\ϘC)?П#X][G) b1&eQ7E=H7 StqիtKME^YG ͔dd~Zz2/9Sgk S^:]vD$ ,b$d+ysP^޷.jJبowXCECg`f,c?䡅Գg-7n]vNZlkrtʪȬ9DJbV=1F6 EAy2-Qˋeu^=*.Uj{-4ҫ Qe!zosTjJM/jQ}~<&'k6جvP 7m9ۓL+ђEX+2 S{',Ywb@詮Cz2볙O)%d]M2$B$ie2!P(p5 Q4DpMQvEo^Ϊs&ȅN:_by0u;M;<ݐHW1ϐqcFxuck4E ?3l/&+>DJZ6KsoW)c=!@ MI ?~ P\ qof1+uDulih"Ҧƴd8_#X6yi"h}H,?믡Evm-~pɮ 79EOBcīnm)3"iqW&̓hR1'9jXE9_^Fо8*@$88%āTSTf3ayp1x9YkknaYvUe ="B wZaDn94cB|C, <G OM; sx49@jIj׵Ϥ{:N|)(A/*Itܢd"@*UPQ@CWf"t49ikAi#spFs@@9 ?jFG%tDDMQA".1˔Xs@R1Y yn>]l{F@,lCZ9R䃷%YmobR}]M2-&[H*ED#MS701?պkY+h4-ŧ>;;9b>΍%!rrn&8U=ǶET7ACa|#ݭJr*t%?Zy G ZS;-1yf.B_=hDՙ#HӲ@y8DG8REQn'O >̌s wOx|0J܋(=7_65 "nΘ tšpOưj *+3P8w3GǒpY?o~&v`Q Nծ&q8˖ {@q T {Vޟ/E ߹% -sGxGD*䵼;tˮ [LOm\02fZ9F8n^ jI5Hdn0(.vq\399sqH("nh>JHD[u4^; 1Ï{MߝSV]GWE03F;-?qEug7X܄-yv-g 1RZhe XOa*31\3\ ?p?phT\А_f< ȯR=d5><@\`ևVǧ-h/˹x\b-3Ɏ :tȨpU TQECeI;] G"r̀@zOAEjCxu7C^G뛱ĊSp{g^>fPu6|t_YJFmM's$ꤡ"S(OCʹ4H:x5eL֨<#B,%o%jH;G ̋|t A!Ck6h;L\wiY.vCq SMg3,Sz \7kuyhXD}Y܃QuNiQ"E"E)DGu}h:Jآ |D给vGF2pMfT4URG8{TfPYtJPR ҫlj=@&F Keћ.7R>Ĵ.kCwHbwݢyH6I2VڨᆢX@y}]:h.Jh{p+\ucdvrzQȫMHUA%7o*MEL:OCOCL/l~~ihA'ezhYܟ\FlR*6(;A0RbɄ~ƵΌG7=\9* f~9[l@9F*'r$H:hT"h>GbRg\"gN d8ʹB瓆'8' m1h&.a$K }U$9E9J$|w{!:[>jK "㍲;8\?``p95yk<,*C*b⻍3 (^JŕEy&B<6_A\"Y,EtdN!mOeOY-lc.trdsrؐQHE*eWee΅o*wn!.U7twX<`:T;q1~ g(>"/4&-s%t,6_tޠ}A#ED7Ṏc=1N\OZٯO٬&RMQ>G">[Ïi} GALɷERxATbVX:&G>Mɍ֥8cWzʉ߇& gG*y⭈m7$SIW nXAC@ ɴvalNGڔ]\>@()f٫fH&՛d6DD[E4HH b(Xء @zE-ncg5[8O*۪h3V0NjzF8LE:L!DY.,u?8]MQ*3mRNArHIVv(I,rgqeļLȻe-IlgS@uMUGI]9 E6ɸM:1VEdC[+J- B{u0_ݡU 6 c l-w͑8lϴ* XS.dhZKn1'1̽{pykC\珤gq<~DuUgM:EER c#π.i,1 @ "?@VGlerh2 QeX~B@~8񯤟GKAviE?[QS, *}_wJ6UnMS\7*W啂B¹S0da ENq֯gYQjm4f@ yxNcgqښN( d7΃<=F$̲ɷV*V+"~x2P=ϝdD7|NYk=t1|H\-\w,BdII&K#8ՠ(%ITPG ̻.{ LXdb{kr浅#ߥq 'bN1{+|Ed$(EY2*b/ bk1z|xߡ5NukCk L2?qibiDG1qL/"h.gӵ}L-9ZCcVYpYiXaQe"1D~rk/u򿈲F3  }I^Q$yG[jn82UD/clbM VFAGܣ1kCs:]&j%e k#ssxOp%bAzA*øۓnscS~L2>!ˁ霠0A8u" X\%XNRFM_?C5IYT:|x4E8a9U6ݭ"O! %D^o=́ӛ!So>!KO?tEB)njK1i&Cpo&"^ΈJf7"Qڋw%xc < $Nx7:"dqUf(QؽT WM6 /ob:~QWrXw}9o;>>(:I qF59Sn DlE*b" g"|  "@VtEf])@8"DDMF獔F:ŘA̧ZVIHnWM~FtZN ő~WЈEY>nQY5F7y{lr.I2jخ2Y[+-Sr8fIX'R(W"9Ȥ7]P@n@cly( 1i"EPxz{hY< WDUvd*a1„A(x?"m_ YS);2 )/{J/V4G8`)DD8 Tk{fIX A)U?2j1nyNHn2O I% ~cS"d8>8!^5n ݍDu/)H6IHR#??5S@C Xͷn)kO"϶h}=ٖwSW1`ש.ҲP]dIbM5i!:zN!dF"SkmA iQgq#.^"݁I ;yG%ee%䔔9zڢ""mq|uΑ'rNw$QN4׈3!웂u8<>~ nםR&{We- J T|Ok S'Qjs[4oaԨ,u'Nťb)Y@F G*?S bcNc U/YG;{9> sAـ!cE^~KUM[U֏{Vz޾ɫϜ'Ak$ dS* (Κ ~ԚhUJ3L-kZwlx;?dbJ59~>J=5jڲX1,ys%!a_G;X*ͳ9r \j$#V2T=:#Rk=;m}^[d$x!"79MqxxlW<>>hJf+QWBMbɉt'<맚C`~#x.2}e|NZEqFc"(BYnO`e!3˟.B`7.?(1*)1Cq[_ AcZZISU4=E)UV o׮ ,k}u=P'N<0{8"߲c"OKɌ[U 8k+[#=A UNr¢nNʊ9p]QQw Qe }SXL;!-rT!6y5&9cTLEWANGg>}"Ø~˽ xsaoNű,T1;6 Lyh&źrGq1x =ahx[N ehOg7g6&ZF qFȒDrYFVF-N5tJkvimb$SS1 >%@?t Њzk8DDMC?n;ۯGBg a<@]*] 4e.VpUDL-7pLa1JSlW9j0.0NiÇA CK&=ZC ~lmpv֒6 Tꞷ郮TU,Xdkͻtج٧>JJdEmqlLV/Bg7m Gv6:ibla>2Z!kCq 'spw |9LVbD9RNXYJLjB5BK`" Br]sƙs[h*zwi$tsz9Ή\q p[kUT_s4M;P"ey r^x}CtP\zY+sφF>x#* ZgΉb F6W)f ar,l6 ⥡º(6Se1Z[A#.pl%9fCZ}n0r ?!+9L~*Zd,slr_fnR1Le1L#ԱWH.vݼ-x#AaWeԘ9q $V9 %UN"asD<m]lr}d7?6I>wi?\kr D9\b>`kdov{MuR1IEwHˍ-vF`_` OLfS}#(ARQU)Ja \gɋ%X-Y(sc*8& !,FN6!űD9Y䋴4H8GTIÀk¦VT;y hs"` idb{ oi"*Qq>|JpEkfPM-Kv{t䈀sʂSʪ^&Rl13/c2Es(v\m3]i- ;9cGPʚrNO*U%#(Ԍj1(1p<Ag *{6.ݺJi kMS[lsUT<" `ـdA!7Šm=wwMAHSv>{>iI%yu )! w{u##"k͋^E=8<DM4EWJ?}'\GHd(xpbdS苑)7(, B42( uLT{b܆iF9OCUf I$C矣Ho="\WwH\qIP<񃱖)b?:P/P:t8}C=!'UmF`0t[@8/ξ챔q  %\TUL~խ뉍cr0ɷרPq$ط b¦< Sv_#mFyѷ.P+Md*]dJU0 -9u Gд4yiy πrV48u˙fn8hs ~ ><E/pGhW'9/sD>%(ad:3֨z%8h2v>ԙF6K'UՑ7:)INawm!`H_?@g][VȚ3#-hTZެnQGk:DC`e\@J^ SƷ>5ֽ-eCr9ͭ,LF$Þ˂iJT(C\lչ)2,g3q-n/Njc]ՉcNz)~p+!ty-u]R9y$KH>YT5.Qd>o.1.J⣒+jvfEDQ#CƿMD919=7~;m٧;ZNaciZBZ &QFaHUS4!0!Ls)HR"#WHsjB4vfsIsSE:a0N[Rm,-.bg`*GZbw`W<SnuNŚ+%~sX}byr7;c{l\HLLO,Stݪ[jhY-tr4a KGKqelT6X}4;\B] ? sݝP'ݦ *Y :e4৑86M{[ȞXOqpr;lr*ߤQr2IrT @/ۀ0%t=•:H n6XG,"${ B QGJ)=rMD/Z J&܆޿G\)ۗ1Å%F{k?Y-XU2d5@A5D@r WWm#@vw6=iRSYdt1x]LB@@@@ !;(/4D%%;KۮaŻ\rS*dONް [mT ݝ&JEH$xeK"=X22mͣ D\inCI ;YsYjeIBO.+@ϩtA| xF#¦,c?1mku5)H톆Nb/uX D1BMjEUQ4APTJ 29$leP1}{cNN7uWϐWwwDiDP8h}>٪MaX̩|U Aj#&U=CguջZ MixI8H'8 s{Ӂ 'ޞ!Gs,DS@*mة)D>LlD>#0]dG[cq&,L'ù>Nv]?>0B'f:A4]#:NQģ}rU{SBIaUfx,43sl&Mip)l%rӤkPV\%54r0L{s0s}&l&P*MexS))u~͏!o&3G6@> 't *HcFBbGWHBgCY4vU}k3 ;2 Gxr7[ICIB@|vt(~)@5oPe^^z#"sn@IܩH1Q AnG8C+?ۮ=-ձY{(kؤ/ F@VXSHBZcCVN=O#hە har 4xQ(sYL̓V{5db1pYҺt Cܯhm[t|4&`yԒTDM4DNx7{~:AN+]OXwp(PHẠ>C\eۦT>ಖ L\֟T$((tI8q#8uq/?\M?ǧ5"صIU)E:TW1>xCP|eec㐳CN:IOq9$%Xu88 :< ﯠݍFh;tew'^F*4N] B"05m az*w7/9qSR)>xwF'kMvv}aX@(/'Oz{Sd5߮ES$(w&lQr"svȊe>?̽/&pz(ps9L>?(yH"?$Q8=F:2YzJwBq.ș@~M馧~%5~iE8kw EVmZW$T'ؠWµ:Z̻{}0J:d#c3'HS&0kJt(i2na˔Z~~+O9hu]P߁s_%g4 [t Xή]*/;j""|p":-r7${;o'༦>X9W FVRD#\~}Y9R)?(WP־p? (aIKFz-ݶ;adgE}$!iD@Tn |k wzpi(]z8jc"!0%!WG~FL@?_.7np?VˎC&?wpbrBS&&%"܇AO'/#naI ;c5lW4LL$BHN"rFQ?1 ¤s\3K1rvcBj=$FdeiY2"GFn۝07iA7I`ٶNUn)vl4d~p7rmQuamO?#?m5}Y2mC/;ZER|jB3!>a*2`7M{+[ &=TTkTFFA{M*M2d45j"He)P&"h(żt]X5R /4&tlQ]k(pmѭECKҌ\-TqCv/$z|ZzAs(S1R]FGUsdhbrSuΗ:WFpGµ[$pܻdY* m@^2V8([*()+U@<V 2.жc }N:qxxNfrAɪ*"-_Gmւ݀u$L%7h)i . 4#/\c`wo8qVK1:I6Ev(MHQg^8PJR!ϜIv~/m_SK CݠrCÍcu/[S MAu&*)L(H*17H2(4h;VD/v? OG!"h[lSȒZJMfZ@ҟueEr2")'uAq[lϬWEOJnd!z\zzEUԽdroDQZBA"=(CNE?84nyl4J^8iNxhkp_ɒ9q7nfb+ubAXAQuM^U ت$s._.Pxww\d4@4zx(-ʣF iUfP2Ik:zdLrw.u;GNssdtBzv/i-/p9zW-|ҵUH%)b ~k`h{?f̏X@ ~ +[F`.^)E]-\$oMT!n)) <AA斸eqi|³nqll#RQPPʝ)&*(!{J&q\vwSet=ė4 ,uo;x^I?ڡB.n~ٓrCr"~!unv*YRFT'p ??p6}>ƸEedg&'^<S0?aqc;ź..^; cD#*roӬ r~%lݤF{ >hұQ;iZa| yyؽVCtyT(^@kIP\8G!cM<%LZR&_;PLJNd]"xGSnGһs}h#q7?6KݥA>F!5rBnrOv@In]AϵkiQo]GdU8eIۏw#p n_)?OƹJUJ/ +"iq@DuU8:J~{u^1Fm;nT:DHqMSb4>1AbDQjc4Z]cĵNa?Q<\h.sfr-21!K-i@LwU"$"+I5Mf`cږUhUс4C#<&7+ ip, vÚf]]&)3e91LC(8 Byoc9l:,i'/?j`Ulg*>վq^:Zyi,=BNw1/.xnJ53M8"DL/A#Sئwu8dY#A9(yY\0sȃj&1a:gx"0Ihzav=iԙ=k]{asGU.1`JDEi_noyֶB&"h&"+חyz1T)]_5Q=Q&m2"hQ職2iIDS֚Ã%ʝ\y;;V:O({h~~\GVJfx>KZv=U}<͕p+fӯr1鶎x&šnuNN*UpȓH [79{]́r?UP;y+T{Tes/SBM*++fd.<2B PTt iWc^+v\!iذ~X@s8 9bkF:5x ~{hDDJb9u^MYS0mLqϚ ĕ&6- 4ٵyCt,Q2.#qnR0e򢚯W; g4X*|7>rT%sɬ םf#TkYj]7 yK3dkػ2Bؤ;&0ܬD R֮uQji'ޑ7䣰8)> x'r<;|LES$7ip$U%r..!#țH ٷE2I͈DM4XmT|$AM-g;E`Jgo0t ebJ/%dӑ3BMV8EP ig=q!vo ɼlD4{8nFL&AdJBإ }. JX)cXƍZZkS9@0 LS))ysE]nܺ*V2-l=̚"zAQ9쎩+쏨Xr@{,wi>jA[#g[M^;lڅ=UCJ4m= !±˵\ AD0 W`]c|c-VԄ #24ȢC7J%K"B ВqR~4U} ]/gQ n10% %DЂl9䪑(kaDiWozZ|ZB)R)JP)JRR)@<|H"#n(&TU6r$tw Un ~HrCjhs\N.a\~9Ew,~D>BʝEbt(2`mcr.9k"NJ NL'Ӈ\\tuk:ES-qp9;xXhj%bccAnݢ6sliԸXKɒVT{f? K5gqiA znpHW*qq=AI\KN9((Q05/j)e4NGJpp4xiE"8uYUܪe(&9D>G#/y%=|~?QYzĜ ##J<~Pձ I c\|`0)h,Eq['2lڂU,Y&L[D.!( jJbڹw3u5]i3lC"㳞D"t W7<.RLꝲv T|:f"J:B.sHcr=>e> >9l,D1n7=̹n+ ,x>0FtHMY&NE_8Ӈ nܺGS K-`Õ\vla7Tgۤn"LE@,ͥIzbGH?ΰvI4Gl55TeТX\k-2*.rU"Hӯ#Yt0p/&|5 ?ITԝOtV727݈nLmaG 2EE 9 ~)(Y55Oq9,gk[̮%4M`KL(#Rǃ<a,VUҰ{O#>Yܟc_N|?qI%k7ldxme-ު tVnr㏶aGdHsѮs${b<-@oVv:YUYZ[RUC( )]q=Bnhrq2B@fRS5͈9^zI> "<.EgqT+n͵u]lq̥5Lfk)1EJS5DΎm5L[ѵMic&C lw Tk vQm XCG@Ĵ'Cvqmhѹ?*IQ"sD0 -E4r h.yjxYy`hE_U8)%DNƦ"s `D/p>;(|r1N]}E;F,@s˛@_S7;eLo<EJ6Z并 HRe| S LNP0yIA(x0|VʉA4G~#yeo !͙9Ĉ*ؾ\=#ud! [*|E$p*5=Mvӗ{*֌KLxd5pVۅV\Ύz~Ȯqdpc%Rщ38q)JDRE4,.7ZnuiƻB`gqvy+SlUўRd*Kc+"h&"hj۵nvnU/2͓y/(Fo6T6tS?aRI%c②Ѹ`ȃAkZᖕGmko;q]ˉhĭni7"R G:V_Nj)-}]+ ~eN|MG!tyu9sK6T9O>@85' ,xp[P@Iɸ8r>@@~8p:UAm{VmJ&dH2}'?Q>5b <3R9 'o2.<ƻnW *kTEX?^N$Dx*A#ܯamR. }_<w~C[2&zgu+ 1q ] QogC 89}bxLR@ P㤀 Q Hq˱Em9}͒<=fiALz9SuJQԎW`^rk;3Lg fyE#tbGYj6hDe?;볎mݟY~IꉱMoQ2C\wqPS [ DMid0iH;unIWkRRQHQe[$ "&P4E򶽇0d6.'DR&uGD[DM4DDMEGjbLqv8L4FDqRQ9UfERN?kTN|XnPB.Q7=?{0PRj6AۤZG4}Jȸx6Ȋs)T)*U@}1F|ŻysU fpṯM%TP2-|T@Pq֯X+-1 ϒgHʇZAO R?P,^~bLHR1ّ"ig܀sT: YNA0c:uzS۩$b}4〞gMq_"*D)}/01cAMLS *H"U?WRƹI+rrp1ן ½ zNq/b<+NܯޱIL9Y2n2FbQji#|кNHF~ 8oV1N2hv1u>`ѳ@ Bk$DDUM5T 9Gn߰wYS#=fqX4z `f= Uf^?Q:MXUIM:H9r, =j1V#ZJMঢ]VAvqo&rLQH$ ^kO[$UT3E+Ns8Gr>yødys,U,B1Ь589HqĂ'>8ו=M͒Fi7|8p\N|0Dc㝔œjFI 2 drH(=2FOn9yB~mHF1]:{UM,bG_Z]F%au n*^ qJ&E AA@@ov2]W9Zܼ [I%)"^H)m¿rW: -?z>nӔߠO}Vwsg>;g;8=xc|{,WL"h(ḬrAf[쐓}Q 4LjJmf9QYc ^Göhbz Vv6oG*&6e˻lI,"ci`Yh8pq0'@0v}f>IYݹa>%o-^}yJsnwuN;w&b 3[EHu!FT&L`5-FZ'oA <$uPO@Y"Q1{ +jݥ|uW{ #9~,ԷTv,66k<(-Q/O<* aZJ&8k `xܼp pT)ܳ͞82/}Bs)T=BueHj9`s^\R-[I8dN!MDGkC؄0"\V82-3Syjg>ʻI sc8qPu ]r+jb2UOr;ږB?iF: Gk+DDMV^2LR K2/pu:oѵhfێ@㚭} Ed2^f, Nҧ-G5ڷ -=6@sff!o`L}&K_6*s4ER#`E%DDM4DDPkusu+&O&1DEw,pwLYڧsT8ETS2tg$"0Pv6*XacWc1T8UWnܣ9"fM^ЀGȨ1=>;d5oiŠ>0 ,YxZ,a#֘ՅKLI) NǺh܀09)I8E90%)jh!&9K(4tLc?5&-Th(`=ҍm̶Ƚnh"pr$*²f+PT:UxȴD|NԺͥi{G=o:\rYxL RATlʘ׋H,NH@T7'k^zR {VHcq#s ţsQ®Flٸ]F1:*ˈW=Ec1T @L ^8ְ|clÐ`6QoHePTKO9sGa3>5åvI,Kv<'?Ƽ( (i0kaьl*C-•/7K ]& XUL:~;|<5Hݑˏ7@(3.n2z6;UL9شO%bc!o$~i(tV쩙UYE$:gRWZI) Gps@0IvRvC atE.)+j;P=Kl!*);t੔YݧKXl.p>Je1u"h&~lx6ݖ%^7C+3'mMeGp!)~;u bSvnnOqFzdܫ#cC̪ľ۸y7n&F>`,x7)bv!N`qOq!xR]N<=c8,fSh 5^MOj `/ŭahHxIlQ6rmqôJq*n ϋ^ ek/mv {,;4G GQɀḡK u(A7gbJm f[%@sOL,/+'_nI-М ic\@W75;l9T0~^7H)^.곷]rL+ΊA d\ b %__{iwIl*}x% 7Iab)N;|:puL#ձ\(%S(&"h&"^"-TTVk41ܤJ&%[Dߘ)Nbt5K E>kgd3P̀^s\N2w#ʿi-'0>z#TVۨۀ|ގiJU؈uvRJnYHyj9K!_o5TTw;c{hKel2tp$7]S+ũTu(  u"%YfMuI0Uub9lIJXƀ]NHPڪ1Hc'aY64DDM4DȰC4ןz"J8$4f"Sk+nrP67m/n ㌲wXsZ۳Z' Ej28A6I;%ZnnEe  Wd ̬HDM4DDM4DDM4DDM4DDM4DDMz-#Z;z{ϤEzݢ~-$@^M2P! &8gbȩ"jż]6I,rTL^ IZ##`68;su?UWcK:LɁ.K+gˬAu`̸1r1JL!i(mԶqO6;wHw79rO*ݯ=7'BęUr "A䘓U~8b'9P?Gsirl;#?р3c{->#v-ugn!eFaM2 >$t &Q9-wӺ:rs\<_NcT̯ sX^C:EلM"ɘP!&`a/wGN!~>`ԝG{( ' _TҰ7g~J(*/:~ P]Q>=n9zvs݁^ dzVVrFf揧w’IqBgjlKP5$ |!G3]KmnRm oVf&^BYD$ݐ!@@LnF|W[Ù[ ONpp-oW j5嚦I%b9Y9VC <=6nbJd"Y%7J=z7]}#o*<$`8DQ+[0p6Ы'f?F֌lEzéM,)&7)rO ZZRkݑSDg%Ӌb&cI)B*7DE$@@4E艢&"h&"h&"h&"8 ~艢&"h&"h&"h&"h&"h&"h,zR V5kE~I1I4sIXDh% =&㨣^SLNH |:ЯhD M9⨫)JDܥ:C.\iM0 kkeZvN)f?7]exLO^C<9(57S)(n5t܁g(FaXd݈Kj6Ud,9m|3tĢZTI-b͵b^|5/a2\5 1xG g9IPʲ/6Y51>(k/N/l**SF"b(o" G!ͭ}gi wsُҊ LP5H䊄|4k8(8*,"ݣd! R)dp4`55y>Q4EMG?EӍ]=Rלݳ2o{Xc^d1ޞY$,X F=QtR+CS= 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; } exports.createGetResponse = function (text, contentType) { var l = function (req, resp) { contentType = contentType || 'text/plain' resp.writeHead(200, {'content-type':contentType}) resp.write(text) resp.end() } return l; } exports.createChunkResponse = function (chunks, contentType) { var l = function (req, resp) { contentType = contentType || 'text/plain' resp.writeHead(200, {'content-type':contentType}) chunks.forEach(function (chunk) { resp.write(chunk) }) resp.end() } return l; } request-2.26.1/tests/squid.conf000066400000000000000000000047271220047311200164320ustar00rootroot00000000000000# # Recommended minimum configuration: # acl manager proto cache_object acl localhost src 127.0.0.1/32 ::1 acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1 # Example rule allowing access from your local networks. # Adapt to list your (internal) IP networks from where browsing # should be allowed acl localnet src 10.0.0.0/8 # RFC1918 possible internal network acl localnet src 172.16.0.0/12 # RFC1918 possible internal network acl localnet src 192.168.0.0/16 # RFC1918 possible internal network acl localnet src fc00::/7 # RFC 4193 local private network range acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines acl SSL_ports port 443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl Safe_ports port 70 # gopher acl Safe_ports port 210 # wais acl Safe_ports port 1025-65535 # unregistered ports acl Safe_ports port 280 # http-mgmt acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT # # Recommended minimum Access Permission configuration: # # Only allow cachemgr access from localhost http_access allow manager localhost http_access deny manager # Deny requests to certain unsafe ports http_access deny !Safe_ports # Deny CONNECT to other than secure SSL ports #http_access deny CONNECT !SSL_ports # We strongly recommend the following be uncommented to protect innocent # web applications running on the proxy server who think the only # one who can access services on "localhost" is a local user #http_access deny to_localhost # # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS # # Example rule allowing access from your local networks. # Adapt localnet in the ACL section to list your (internal) IP networks # from where browsing should be allowed http_access allow localnet http_access allow localhost # And finally deny all other access to this proxy http_access deny all # Squid normally listens to port 3128 http_port 3128 # We recommend you to use at least the following line. hierarchy_stoplist cgi-bin ? # Uncomment and adjust the following to add a disk cache directory. #cache_dir ufs /usr/local/var/cache 100 16 256 # Leave coredumps in the first cache dir coredump_dir /usr/local/var/cache # Add any of your own refresh_pattern entries above these. refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern . 0 20% 4320 request-2.26.1/tests/ssl/000077500000000000000000000000001220047311200152255ustar00rootroot00000000000000request-2.26.1/tests/ssl/ca/000077500000000000000000000000001220047311200156105ustar00rootroot00000000000000request-2.26.1/tests/ssl/ca/ca.cnf000066400000000000000000000011041220047311200166570ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = request Certificate Authority CN = requestCA emailAddress = mikeal@mikealrogers.com [ req_attributes ] challengePassword = password challenge request-2.26.1/tests/ssl/ca/ca.crl000066400000000000000000000000001220047311200166630ustar00rootroot00000000000000request-2.26.1/tests/ssl/ca/ca.crt000066400000000000000000000017611220047311200167120ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICvTCCAiYCCQDn+P/MSbDsWjANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xMjAzMDEyMjUwNTZaFw0yMjAyMjcyMjUwNTZaMIGiMQswCQYD VQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT B3JlcXVlc3QxJjAkBgNVBAsTHXJlcXVlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 MRIwEAYDVQQDEwlyZXF1ZXN0Q0ExJjAkBgkqhkiG9w0BCQEWF21pa2VhbEBtaWtl YWxyb2dlcnMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7t9pQUAK4 5XJYTI6NrF0n3G2HZsfN+rPYSVzzL8SuVyb1tHXos+vbPm3NKI4E8X1yVAXU8CjJ 5SqXnp4DAypAhaseho81cbhk7LXUhFz78OvAa+OD+xTAEAnNQ8tGUr4VGyplEjfD xsBVuqV2j8GPNTftr+drOCFlqfAgMrBn4wIDAQABMA0GCSqGSIb3DQEBBQUAA4GB ADVdTlVAL45R+PACNS7Gs4o81CwSclukBu4FJbxrkd4xGQmurgfRrYYKjtqiopQm D7ysRamS3HMN9/VKq2T7r3z1PMHPAy7zM4uoXbbaTKwlnX4j/8pGPn8Ca3qHXYlo 88L/OOPc6Di7i7qckS3HFbXQCTiULtxWmy97oEuTwrAj -----END CERTIFICATE----- request-2.26.1/tests/ssl/ca/ca.csr000066400000000000000000000014111220047311200167010ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICBjCCAW8CAQAwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UE BxMHT2FrbGFuZDEQMA4GA1UEChMHcmVxdWVzdDEmMCQGA1UECxMdcmVxdWVzdCBD ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEjAQBgNVBAMTCXJlcXVlc3RDQTEmMCQGCSqG SIb3DQEJARYXbWlrZWFsQG1pa2VhbHJvZ2Vycy5jb20wgZ8wDQYJKoZIhvcNAQEB BQADgY0AMIGJAoGBALu32lBQArjlclhMjo2sXSfcbYdmx836s9hJXPMvxK5XJvW0 deiz69s+bc0ojgTxfXJUBdTwKMnlKpeengMDKkCFqx6GjzVxuGTstdSEXPvw68Br 44P7FMAQCc1Dy0ZSvhUbKmUSN8PGwFW6pXaPwY81N+2v52s4IWWp8CAysGfjAgMB AAGgIzAhBgkqhkiG9w0BCQcxFBMScGFzc3dvcmQgY2hhbGxlbmdlMA0GCSqGSIb3 DQEBBQUAA4GBAGJO7grHeVHXetjHEK8urIxdnvfB2qeZeObz4GPKIkqUurjr0rfj bA3EK1kDMR5aeQWR8RunixdM16Q6Ry0lEdLVWkdSwRN9dmirIHT9cypqnD/FYOia SdezZ0lUzXgmJIwRYRwB1KSMMocIf52ll/xC2bEGg7/ZAEuAyAgcZV3X -----END CERTIFICATE REQUEST----- request-2.26.1/tests/ssl/ca/ca.key000066400000000000000000000017031220047311200167060ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,C8B5887048377F02 nyD5ZH0Wup2uWsDvurq5mKDaDrf8lvNn9w0SH/ZkVnfR1/bkwqrFriqJWvZNUG+q nS0iBYczsWLJnbub9a1zLOTENWUKVD5uqbC3aGHhnoUTNSa27DONgP8gHOn6JgR+ GAKo01HCSTiVT4LjkwN337QKHnMP2fTzg+IoC/CigvMcq09hRLwU1/guq0GJKGwH gTxYNuYmQC4Tjh8vdS4liF+Ve/P3qPR2CehZrIOkDT8PHJBGQJRo4xGUIB7Tpk38 VCk+UZ0JCS2coY8VkY/9tqFJp/ZnnQQVmaNbdRqg7ECKL+bXnNo7yjzmazPZmPe3 /ShbE0+CTt7LrjCaQAxWbeDzqfo1lQfgN1LulTm8MCXpQaJpv7v1VhIhQ7afjMYb 4thW/ypHPiYS2YJCAkAVlua9Oxzzh1qJoh8Df19iHtpd79Q77X/qf+1JvITlMu0U gi7yEatmQcmYNws1mtTC1q2DXrO90c+NZ0LK/Alse6NRL/xiUdjug2iHeTf/idOR Gg/5dSZbnnlj1E5zjSMDkzg6EHAFmHV4jYGSAFLEQgp4V3ZhMVoWZrvvSHgKV/Qh FqrAK4INr1G2+/QTd09AIRzfy3/j6yD4A9iNaOsEf9Ua7Qh6RcALRCAZTWR5QtEf dX+iSNJ4E85qXs0PqwkMDkoaxIJ+tmIRJY7y8oeylV8cfGAi8Soubt/i3SlR8IHC uDMas/2OnwafK3N7ODeE1i7r7wkzQkSHaEz0TrF8XRnP25jAICCSLiMdAAjKfxVb EvzsFSuAy3Jt6bU3hSLY9o4YVYKE+68ITMv9yNjvTsEiW+T+IbN34w== -----END RSA PRIVATE KEY----- request-2.26.1/tests/ssl/ca/ca.srl000066400000000000000000000000211220047311200167060ustar00rootroot00000000000000ADF62016AA40C9C3 request-2.26.1/tests/ssl/ca/server.cnf000066400000000000000000000010431220047311200176040ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = testing CN = testing.request.mikealrogers.com emailAddress = mikeal@mikealrogers.com [ req_attributes ] challengePassword = password challenge request-2.26.1/tests/ssl/ca/server.crt000066400000000000000000000016301220047311200176300ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICejCCAeMCCQCt9iAWqkDJwzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xMjAzMDEyMjUwNTZaFw0yMjAyMjcyMjUwNTZaMIGjMQswCQYD VQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT B3JlcXVlc3QxEDAOBgNVBAsTB3Rlc3RpbmcxKTAnBgNVBAMTIHRlc3RpbmcucmVx dWVzdC5taWtlYWxyb2dlcnMuY29tMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlr ZWFscm9nZXJzLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDgVl0jMumvOpmM 20W5v9yhGgZj8hPhEQF/N7yCBVBn/rWGYm70IHC8T/pR5c0LkWc5gdnCJEvKWQjh DBKxZD8FAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEABShRkNgFbgs4vUWW9R9deNJj 7HJoiTmvkmoOC7QzcYkjdgHbOxsSq3rBnwxsVjY9PAtPwBn0GRspOeG7KzKRgySB kb22LyrCFKbEOfKO/+CJc80ioK9zEPVjGsFMyAB+ftYRqM+s/4cQlTg/m89l01wC yapjN3RxZbInGhWR+jA= -----END CERTIFICATE----- request-2.26.1/tests/ssl/ca/server.csr000066400000000000000000000011271220047311200176300ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIBgjCCASwCAQAwgaMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UE BxMHT2FrbGFuZDEQMA4GA1UEChMHcmVxdWVzdDEQMA4GA1UECxMHdGVzdGluZzEp MCcGA1UEAxMgdGVzdGluZy5yZXF1ZXN0Lm1pa2VhbHJvZ2Vycy5jb20xJjAkBgkq hkiG9w0BCQEWF21pa2VhbEBtaWtlYWxyb2dlcnMuY29tMFwwDQYJKoZIhvcNAQEB BQADSwAwSAJBAOBWXSMy6a86mYzbRbm/3KEaBmPyE+ERAX83vIIFUGf+tYZibvQg cLxP+lHlzQuRZzmB2cIkS8pZCOEMErFkPwUCAwEAAaAjMCEGCSqGSIb3DQEJBzEU ExJwYXNzd29yZCBjaGFsbGVuZ2UwDQYJKoZIhvcNAQEFBQADQQBD3E5WekQzCEJw 7yOcqvtPYIxGaX8gRKkYfLPoj3pm3GF5SGqtJKhylKfi89szHXgktnQgzff9FN+A HidVJ/3u -----END CERTIFICATE REQUEST----- request-2.26.1/tests/ssl/ca/server.js000066400000000000000000000014531220047311200174570ustar00rootroot00000000000000var fs = require("fs") var https = require("https") var options = { key: fs.readFileSync("./server.key") , cert: fs.readFileSync("./server.crt") } var server = https.createServer(options, function (req, res) { res.writeHead(200) res.end() server.close() }) server.listen(1337) var ca = fs.readFileSync("./ca.crt") var agent = new https.Agent({ host: "localhost", port: 1337, ca: ca }) https.request({ host: "localhost" , method: "HEAD" , port: 1337 , headers: { host: "testing.request.mikealrogers.com" } , agent: agent , ca: [ ca ] , path: "/" }, function (res) { if (res.client.authorized) { console.log("node test: OK") } else { throw new Error(res.client.authorizationError) } }).end() request-2.26.1/tests/ssl/ca/server.key000066400000000000000000000007611220047311200176340ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAOBWXSMy6a86mYzbRbm/3KEaBmPyE+ERAX83vIIFUGf+tYZibvQg cLxP+lHlzQuRZzmB2cIkS8pZCOEMErFkPwUCAwEAAQJAK+r8ZM2sze8s7FRo/ApB iRBtO9fCaIdJwbwJnXKo4RKwZDt1l2mm+fzZ+/QaQNjY1oTROkIIXmnwRvZWfYlW gQIhAPKYsG+YSBN9o8Sdp1DMyZ/rUifKX3OE6q9tINkgajDVAiEA7Ltqh01+cnt0 JEnud/8HHcuehUBLMofeg0G+gCnSbXECIQCqDvkXsWNNLnS/3lgsnvH0Baz4sbeJ rjIpuVEeg8eM5QIgbu0+9JmOV6ybdmmiMV4yAncoF35R/iKGVHDZCAsQzDECIQDZ 0jGz22tlo5YMcYSqrdD3U4sds1pwiAaWFRbCunoUJw== -----END RSA PRIVATE KEY----- request-2.26.1/tests/ssl/npm-ca.crt000066400000000000000000000016501220047311200171140ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIChzCCAfACCQDauvz/KHp8ejANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMQwwCgYDVQQKEwNucG0x IjAgBgNVBAsTGW5wbSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxDjAMBgNVBAMTBW5w bUNBMRcwFQYJKoZIhvcNAQkBFghpQGl6cy5tZTAeFw0xMTA5MDUwMTQ3MTdaFw0y MTA5MDIwMTQ3MTdaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNV BAcTB09ha2xhbmQxDDAKBgNVBAoTA25wbTEiMCAGA1UECxMZbnBtIENlcnRpZmlj YXRlIEF1dGhvcml0eTEOMAwGA1UEAxMFbnBtQ0ExFzAVBgkqhkiG9w0BCQEWCGlA aXpzLm1lMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLI4tIqPpRW+ACw9GE OgBlJZwK5f8nnKCLK629Pv5yJpQKs3DENExAyOgDcyaF0HD0zk8zTp+ZsLaNdKOz Gn2U181KGprGKAXP6DU6ByOJDWmTlY6+Ad1laYT0m64fERSpHw/hjD3D+iX4aMOl y0HdbT5m1ZGh6SJz3ZqxavhHLQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAC4ySDbC l7W1WpLmtLGEQ/yuMLUf6Jy/vr+CRp4h+UzL+IQpCv8FfxsYE7dhf/bmWTEupBkv yNL18lipt2jSvR3v6oAHAReotvdjqhxddpe5Holns6EQd1/xEZ7sB1YhQKJtvUrl ZNufy1Jf1r0ldEGeA+0ISck7s+xSh9rQD2Op -----END CERTIFICATE----- request-2.26.1/tests/ssl/test.crt000066400000000000000000000015171220047311200167220ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICQzCCAawCCQCO/XWtRFck1jANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJU SDEQMA4GA1UECBMHQmFuZ2tvazEOMAwGA1UEBxMFU2lsb20xGzAZBgNVBAoTElRo ZSBSZXF1ZXN0IE1vZHVsZTEYMBYGA1UEAxMPcmVxdWVzdC5leGFtcGxlMB4XDTEx MTIwMzAyMjkyM1oXDTIxMTEzMDAyMjkyM1owZjELMAkGA1UEBhMCVEgxEDAOBgNV BAgTB0Jhbmdrb2sxDjAMBgNVBAcTBVNpbG9tMRswGQYDVQQKExJUaGUgUmVxdWVz dCBNb2R1bGUxGDAWBgNVBAMTD3JlcXVlc3QuZXhhbXBsZTCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAwmctddZqlA48+NXs0yOy92DijcQV1jf87zMiYAIlNUto wghVbTWgJU5r0pdKrD16AptnWJTzKanhItEX8XCCPgsNkq1afgTtJP7rNkwu3xcj eIMkhJg/ay4ZnkbnhYdsii5VTU5prix6AqWRAhbkBgoA+iVyHyof8wvZyKBoFTMC AwEAATANBgkqhkiG9w0BAQUFAAOBgQB6BybMJbpeiABgihDfEVBcAjDoQ8gUMgwV l4NulugfKTDmArqnR9aPd4ET5jX5dkMP4bwCHYsvrcYDeWEQy7x5WWuylOdKhua4 L4cEi2uDCjqEErIG3cc1MCOk6Cl6Ld6tkIzQSf953qfdEACRytOeUqLNQcrXrqeE c7U8F6MWLQ== -----END CERTIFICATE----- request-2.26.1/tests/ssl/test.key000066400000000000000000000015731220047311200167240ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDCZy111mqUDjz41ezTI7L3YOKNxBXWN/zvMyJgAiU1S2jCCFVt NaAlTmvSl0qsPXoCm2dYlPMpqeEi0RfxcII+Cw2SrVp+BO0k/us2TC7fFyN4gySE mD9rLhmeRueFh2yKLlVNTmmuLHoCpZECFuQGCgD6JXIfKh/zC9nIoGgVMwIDAQAB AoGBALXFwfUf8vHTSmGlrdZS2AGFPvEtuvldyoxi9K5u8xmdFCvxnOcLsF2RsTHt Mu5QYWhUpNJoG+IGLTPf7RJdj/kNtEs7xXqWy4jR36kt5z5MJzqiK+QIgiO9UFWZ fjUb6oeDnTIJA9YFBdYi97MDuL89iU/UK3LkJN3hd4rciSbpAkEA+MCkowF5kSFb rkOTBYBXZfiAG78itDXN6DXmqb9XYY+YBh3BiQM28oxCeQYyFy6pk/nstnd4TXk6 V/ryA2g5NwJBAMgRKTY9KvxJWbESeMEFe2iBIV0c26/72Amgi7ZKUCLukLfD4tLF +WSZdmTbbqI1079YtwaiOVfiLm45Q/3B0eUCQAaQ/0eWSGE+Yi8tdXoVszjr4GXb G81qBi91DMu6U1It+jNfIba+MPsiHLcZJMVb4/oWBNukN7bD1nhwFWdlnu0CQQCf Is9WHkdvz2RxbZDxb8verz/7kXXJQJhx5+rZf7jIYFxqX3yvTNv3wf2jcctJaWlZ fVZwB193YSivcgt778xlAkEAprYUz3jczjF5r2hrgbizPzPDR94tM5BTO3ki2v3w kbf+j2g7FNAx6kZiVN8XwfLc8xEeUGiPKwtq3ddPDFh17w== -----END RSA PRIVATE KEY----- request-2.26.1/tests/test-agentOptions.js000066400000000000000000000014441220047311200204140ustar00rootroot00000000000000var request = require('../index') , http = require('http') , server = require('./server') , assert = require('assert') ; var s = http.createServer(function (req, resp) { resp.statusCode = 200 resp.end('') }).listen(6767, function () { // requests without agentOptions should use global agent var r = request('http://localhost:6767', function (e, resp, body) { assert.deepEqual(r.agent, http.globalAgent); assert.equal(Object.keys(r.pool).length, 0); // requests with agentOptions should apply agentOptions to new agent in pool var r2 = request('http://localhost:6767', { agentOptions: { foo: 'bar' } }, function (e, resp, body) { assert.deepEqual(r2.agent.options, { foo: 'bar' }); assert.equal(Object.keys(r2.pool).length, 1); s.close() }); }) }) request-2.26.1/tests/test-basic-auth.js000066400000000000000000000077341220047311200177720ustar00rootroot00000000000000var assert = require('assert') , http = require('http') , request = require('../index') ; var numBasicRequests = 0; var basicServer = http.createServer(function (req, res) { console.error('Basic auth server: ', req.method, req.url); numBasicRequests++; var ok; if (req.headers.authorization) { if (req.headers.authorization == 'Basic ' + new Buffer('test:testing2').toString('base64')) { ok = true; } else if ( req.headers.authorization == 'Basic ' + new Buffer(':apassword').toString('base64')) { ok = true; } else if ( req.headers.authorization == 'Basic ' + new Buffer('justauser').toString('base64')) { ok = true; } else { // Bad auth header, don't send back WWW-Authenticate header ok = false; } } else { // No auth header, send back WWW-Authenticate header ok = false; res.setHeader('www-authenticate', 'Basic realm="Private"'); } if (req.url == '/post/') { var expectedContent = 'data_key=data_value'; req.on('data', function(data) { assert.equal(data, expectedContent); console.log('received request data: ' + data); }); assert.equal(req.method, 'POST'); assert.equal(req.headers['content-length'], '' + expectedContent.length); assert.equal(req.headers['content-type'], 'application/x-www-form-urlencoded; charset=utf-8'); } if (ok) { console.log('request ok'); res.end('ok'); } else { console.log('status=401'); res.statusCode = 401; res.end('401'); } }); basicServer.listen(6767); var tests = [ function(next) { request({ 'method': 'GET', 'uri': 'http://localhost:6767/test/', 'auth': { 'user': 'test', 'pass': 'testing2', 'sendImmediately': false } }, function(error, res, body) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 2); next(); }); }, function(next) { // If we don't set sendImmediately = false, request will send basic auth request({ 'method': 'GET', 'uri': 'http://localhost:6767/test2/', 'auth': { 'user': 'test', 'pass': 'testing2' } }, function(error, res, body) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 3); next(); }); }, function(next) { request({ 'method': 'GET', 'uri': 'http://test:testing2@localhost:6767/test2/' }, function(error, res, body) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 4); next(); }); }, function(next) { request({ 'method': 'POST', 'form': { 'data_key': 'data_value' }, 'uri': 'http://localhost:6767/post/', 'auth': { 'user': 'test', 'pass': 'testing2', 'sendImmediately': false } }, function(error, res, body) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 6); next(); }); }, function(next) { assert.doesNotThrow( function() { request({ 'method': 'GET', 'uri': 'http://localhost:6767/allow_empty_user/', 'auth': { 'user': '', 'pass': 'apassword', 'sendImmediately': false } }, function(error, res, body ) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 8); next(); }); }) }, function(next) { assert.doesNotThrow( function() { request({ 'method': 'GET', 'uri': 'http://localhost:6767/allow_undefined_password/', 'auth': { 'user': 'justauser', 'pass': undefined, 'sendImmediately': false } }, function(error, res, body ) { assert.equal(res.statusCode, 200); assert.equal(numBasicRequests, 10); next(); }); }) } ]; function runTest(i) { if (i < tests.length) { tests[i](function() { runTest(i + 1); }); } else { console.log('All tests passed'); basicServer.close(); } } runTest(0); request-2.26.1/tests/test-body.js000066400000000000000000000061271220047311200167020ustar00rootroot00000000000000var server = require('./server') , events = require('events') , stream = require('stream') , assert = require('assert') , request = require('../index') ; var s = server.createServer(); var tests = { testGet : { resp : server.createGetResponse("TESTING!") , expectBody: "TESTING!" } , testGetChunkBreak : { resp : server.createChunkResponse( [ new Buffer([239]) , new Buffer([163]) , new Buffer([191]) , new Buffer([206]) , new Buffer([169]) , new Buffer([226]) , new Buffer([152]) , new Buffer([131]) ]) , expectBody: "Ω☃" } , 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" } , testGetUTF8: { resp: server.createGetResponse(new Buffer([0xEF, 0xBB, 0xBF, 226, 152, 131])) , encoding: "utf8" , expectBody: "☃" } , testGetJSON : { resp : server.createGetResponse('{"test":true}', 'application/json') , json : true , expectBody: {"test":true} } , testPutString : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : "PUTTINGDATA" } , testPutBuffer : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : new Buffer("PUTTINGDATA") } , testPutJSON : { resp : server.createPostValidator(JSON.stringify({foo: 'bar'})) , method: "PUT" , json: {foo: 'bar'} } , testPutMultipart : { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ) , method: "PUT" , multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'} , {'body': 'Oh hi.'} ] } , testPutMultipartPreambleCRLF : { resp: server.createPostValidator( '\r\n--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ) , method: "PUT" , preambleCRLF: true , multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'} , {'body': 'Oh hi.'} ] } } s.listen(s.port, function () { var counter = 0 for (i in tests) { (function () { var test = tests[i] s.on('/'+i, test.resp) test.uri = s.url + '/' + i request(test, function (err, resp, body) { if (err) throw err if (test.expectBody) { assert.deepEqual(test.expectBody, body) } counter = counter - 1; if (counter === 0) { console.log(Object.keys(tests).length+" tests passed.") s.close() } }) counter++ })() } }) request-2.26.1/tests/test-defaults.js000066400000000000000000000075421220047311200175560ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') ; var s = server.createServer(); s.listen(s.port, function () { var counter = 0; s.on('/get', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.method, 'GET') resp.writeHead(200, {'Content-Type': 'text/plain'}); resp.end('TESTING!'); }); // test get(string, function) request.defaults({headers:{foo:"bar"}})(s.url + '/get', function (e, r, b){ if (e) throw e; assert.deepEqual("TESTING!", b); counter += 1; }); s.on('/post', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.headers['content-type'], null); assert.equal(req.method, 'POST') resp.writeHead(200, {'Content-Type': 'application/json'}); resp.end(JSON.stringify({foo:'bar'})); }); // test post(string, object, function) request.defaults({headers:{foo:"bar"}}).post(s.url + '/post', {json: true}, function (e, r, b){ if (e) throw e; assert.deepEqual('bar', b.foo); counter += 1; }); s.on('/patch', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.headers['content-type'], null); assert.equal(req.method, 'PATCH') resp.writeHead(200, {'Content-Type': 'application/json'}); resp.end(JSON.stringify({foo:'bar'})); }); // test post(string, object, function) request.defaults({headers:{foo:"bar"}}).patch(s.url + '/patch', {json: true}, function (e, r, b){ if (e) throw e; assert.deepEqual('bar', b.foo); counter += 1; }); s.on('/post-body', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.headers['content-type'], 'application/json'); assert.equal(req.method, 'POST') resp.writeHead(200, {'Content-Type': 'application/json'}); resp.end(JSON.stringify({foo:'bar'})); }); // test post(string, object, function) with body request.defaults({headers:{foo:"bar"}}).post(s.url + '/post-body', {json: true, body:{bar:"baz"}}, function (e, r, b){ if (e) throw e; assert.deepEqual('bar', b.foo); counter += 1; }); s.on('/del', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.method, 'DELETE') resp.writeHead(200, {'Content-Type': 'application/json'}); resp.end(JSON.stringify({foo:'bar'})); }); // test .del(string, function) request.defaults({headers:{foo:"bar"}, json:true}).del(s.url + '/del', function (e, r, b){ if (e) throw e; assert.deepEqual('bar', b.foo); counter += 1; }); s.on('/head', function (req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.method, 'HEAD') resp.writeHead(200, {'Content-Type': 'text/plain'}); resp.end(); }); // test head.(object, function) request.defaults({headers:{foo:"bar"}}).head({uri: s.url + '/head'}, function (e, r, b){ if (e) throw e; counter += 1; }); s.on('/get_custom', function(req, resp) { assert.equal(req.headers.foo, 'bar'); assert.equal(req.headers.x, 'y'); resp.writeHead(200, {'Content-Type': 'text/plain'}); resp.end(); }); // test custom request handler function var defaultRequest = request.defaults({ headers:{foo:"bar"} , body: 'TESTING!' }, function(uri, options, callback) { var params = request.initParams(uri, options, callback); options = params.options; options.headers.x = 'y'; return request(params.uri, params.options, params.callback); }); var msg = 'defaults test failed. head request should throw earlier'; assert.throws(function() { defaultRequest.head(s.url + '/get_custom', function(e, r, b) { throw new Error(msg); }); counter+=1; }, msg); defaultRequest.get(s.url + '/get_custom', function(e, r, b) { if(e) throw e; counter += 1; console.log(counter.toString() + " tests passed."); s.close(); }); }) request-2.26.1/tests/test-digest-auth.js000066400000000000000000000035161220047311200201620ustar00rootroot00000000000000var assert = require('assert') , http = require('http') , request = require('../index') ; // Test digest auth // Using header values captured from interaction with Apache var numDigestRequests = 0; var digestServer = http.createServer(function (req, res) { console.error('Digest auth server: ', req.method, req.url); numDigestRequests++; var ok; if (req.headers.authorization) { if (req.headers.authorization == 'Digest username="test", realm="Private", nonce="WpcHS2/TBAA=dffcc0dbd5f96d49a5477166649b7c0ae3866a93", uri="/test/", qop="auth", response="54753ce37c10cb20b09b769f0bed730e", nc="1", cnonce=""') { ok = true; } else { // Bad auth header, don't send back WWW-Authenticate header ok = false; } } else { // No auth header, send back WWW-Authenticate header ok = false; res.setHeader('www-authenticate', 'Digest realm="Private", nonce="WpcHS2/TBAA=dffcc0dbd5f96d49a5477166649b7c0ae3866a93", algorithm=MD5, qop="auth"'); } if (ok) { console.log('request ok'); res.end('ok'); } else { console.log('status=401'); res.statusCode = 401; res.end('401'); } }); digestServer.listen(6767); request({ 'method': 'GET', 'uri': 'http://localhost:6767/test/', 'auth': { 'user': 'test', 'pass': 'testing', 'sendImmediately': false } }, function(error, response, body) { assert.equal(response.statusCode, 200); assert.equal(numDigestRequests, 2); // If we don't set sendImmediately = false, request will send basic auth request({ 'method': 'GET', 'uri': 'http://localhost:6767/test/', 'auth': { 'user': 'test', 'pass': 'testing' } }, function(error, response, body) { assert.equal(response.statusCode, 401); assert.equal(numDigestRequests, 3); console.log('All tests passed'); digestServer.close(); }); }); request-2.26.1/tests/test-emptyBody.js000066400000000000000000000010451220047311200177130ustar00rootroot00000000000000var request = require('../index') , http = require('http') , assert = require('assert') ; var s = http.createServer(function (req, resp) { resp.statusCode = 200 resp.end('') }).listen(8080, function () { var r = request('http://localhost:8080', function (e, resp, body) { assert.equal(resp.statusCode, 200) assert.equal(body, "") var r2 = request({ url: 'http://localhost:8080', json: {} }, function (e, resp, body) { assert.equal(resp.statusCode, 200) assert.equal(body, undefined) s.close() }); }) }) request-2.26.1/tests/test-errors.js000066400000000000000000000015121220047311200172520ustar00rootroot00000000000000var server = require('./server') , events = require('events') , assert = require('assert') , request = require('../index') ; var local = 'http://localhost:8888/asdf' try { request({uri:local, body:{}}) assert.fail("Should have throw") } catch(e) { assert.equal(e.message, 'Argument error, options.body.') } try { request({uri:local, multipart: 'foo'}) assert.fail("Should have throw") } catch(e) { assert.equal(e.message, 'Argument error, options.multipart.') } try { request({uri:local, multipart: [{}]}) assert.fail("Should have throw") } catch(e) { 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.") request-2.26.1/tests/test-follow-all-303.js000066400000000000000000000014271220047311200203160ustar00rootroot00000000000000var request = require('../index'); var http = require('http'); var requests = 0; var assert = require('assert'); var server = http.createServer(function (req, res) { console.error(req.method, req.url); requests ++; if (req.method === 'POST') { console.error('send 303'); res.setHeader('location', req.url); res.statusCode = 303; res.end('try again, i guess\n'); } else { console.error('send 200') res.end('ok: ' + requests); } }); server.listen(6767); request.post({ url: 'http://localhost:6767/foo', followAllRedirects: true, form: { foo: 'bar' } }, function (er, req, body) { if (er) throw er; assert.equal(body, 'ok: 2'); assert.equal(requests, 2); console.error('ok - ' + process.version); server.close(); }); request-2.26.1/tests/test-follow-all.js000066400000000000000000000015541220047311200200140ustar00rootroot00000000000000var request = require('../index'); var http = require('http'); var requests = 0; var assert = require('assert'); var server = http.createServer(function (req, res) { requests ++; // redirect everything 3 times, no matter what. var c = req.headers.cookie; if (!c) c = 0; else c = +c.split('=')[1] || 0; if (c > 3) { res.end('ok: '+requests); return; } res.setHeader('set-cookie', 'c=' + (c + 1)); res.setHeader('location', req.url); res.statusCode = 302; res.end('try again, i guess\n'); }); server.listen(6767); request.post({ url: 'http://localhost:6767/foo', followAllRedirects: true, jar: true, form: { foo: 'bar' } }, function (er, req, body) { if (er) throw er; assert.equal(body, 'ok: 5'); assert.equal(requests, 5); console.error('ok - ' + process.version); server.close(); }); request-2.26.1/tests/test-form.js000066400000000000000000000043521220047311200167060ustar00rootroot00000000000000var assert = require('assert') var http = require('http'); var path = require('path'); var mime = require('mime'); var request = require('../index'); var fs = require('fs'); var remoteFile = 'http://nodejs.org/images/logo.png'; var FIELDS = [ {name: 'my_field', value: 'my_value'}, {name: 'my_buffer', value: new Buffer([1, 2, 3])}, {name: 'my_file', value: fs.createReadStream(__dirname + '/unicycle.jpg')}, {name: 'remote_file', value: request(remoteFile) } ]; var server = http.createServer(function(req, res) { // temp workaround var data = ''; req.setEncoding('utf8'); req.on('data', function(d) { data += d; }); req.on('end', function() { // check for the fields' traces // 1st field : my_field var field = FIELDS.shift(); assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 ); assert.ok( data.indexOf(field.value) != -1 ); // 2nd field : my_buffer var field = FIELDS.shift(); assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 ); assert.ok( data.indexOf(field.value) != -1 ); // 3rd field : my_file var field = FIELDS.shift(); assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 ); assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 ); // check for unicycle.jpg traces assert.ok( data.indexOf('2005:06:21 01:44:12') != -1 ); assert.ok( data.indexOf('Content-Type: '+mime.lookup(field.value.path) ) != -1 ); // 4th field : remote_file var field = FIELDS.shift(); assert.ok( data.indexOf('form-data; name="'+field.name+'"') != -1 ); assert.ok( data.indexOf('; filename="'+path.basename(field.value.path)+'"') != -1 ); // check for http://nodejs.org/images/logo.png traces assert.ok( data.indexOf('ImageReady') != -1 ); assert.ok( data.indexOf('Content-Type: '+mime.lookup(remoteFile) ) != -1 ); res.writeHead(200); res.end('done'); }); }); server.listen(8080, function() { var req = request.post('http://localhost:8080/upload', function () { server.close(); }) var form = req.form() FIELDS.forEach(function(field) { form.append(field.name, field.value); }); }); process.on('exit', function() { assert.strictEqual(FIELDS.length, 0); }); request-2.26.1/tests/test-hawk.js000077500000000000000000000017651220047311200167050ustar00rootroot00000000000000var createServer = require('http').createServer , request = require('../index') , hawk = require('hawk') , assert = require('assert') ; var server = createServer(function (req, resp) { var getCred = function (id, callback) { assert.equal(id, 'dh37fgj492je') var credentials = { key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn' , algorithm: 'sha256' , user: 'Steve' } return callback(null, credentials) } hawk.server.authenticate(req, getCred, {}, function (err, credentials, attributes) { resp.writeHead(!err ? 200 : 401, { 'Content-Type': 'text/plain' }) resp.end(!err ? 'Hello ' + credentials.user : 'Shoosh!') }) }) server.listen(8080, function () { var creds = {key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', algorithm: 'sha256', id:'dh37fgj492je'} request('http://localhost:8080', {hawk:{credentials:creds}}, function (e, r, b) { assert.equal(200, r.statusCode) assert.equal(b, 'Hello Steve') server.close() }) })request-2.26.1/tests/test-headers.js000066400000000000000000000030741220047311200173560ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') , Cookie = require('cookie-jar') , Jar = Cookie.Jar , s = server.createServer() s.listen(s.port, function () { var serverUri = 'http://localhost:' + s.port , numTests = 0 , numOutstandingTests = 0 function createTest(requestObj, serverAssertFn) { var testNumber = numTests; numTests += 1; numOutstandingTests += 1; s.on('/' + testNumber, function (req, res) { serverAssertFn(req, res); res.writeHead(200); res.end(); }); requestObj.url = serverUri + '/' + testNumber request(requestObj, function (err, res, body) { assert.ok(!err) assert.equal(res.statusCode, 200) numOutstandingTests -= 1 if (numOutstandingTests === 0) { console.log(numTests + ' tests passed.') s.close() } }) } // Issue #125: headers.cookie shouldn't be replaced when a cookie jar isn't specified createTest({headers: {cookie: 'foo=bar'}}, function (req, res) { assert.ok(req.headers.cookie) assert.equal(req.headers.cookie, 'foo=bar') }) // Issue #125: headers.cookie + cookie jar var jar = new Jar() jar.add(new Cookie('quux=baz')); createTest({jar: jar, headers: {cookie: 'foo=bar'}}, function (req, res) { assert.ok(req.headers.cookie) assert.equal(req.headers.cookie, 'foo=bar; quux=baz') }) // There should be no cookie header when neither headers.cookie nor a cookie jar is specified createTest({}, function (req, res) { assert.ok(!req.headers.cookie) }) }) request-2.26.1/tests/test-http-signature.js000077500000000000000000000103311220047311200207160ustar00rootroot00000000000000var createServer = require('http').createServer , request = require('../index') , httpSignature = require('http-signature') , assert = require('assert') ; var privateKeyPEMs = {} privateKeyPEMs['key-1'] = '-----BEGIN RSA PRIVATE KEY-----\n' + 'MIIEpAIBAAKCAQEAzWSJl+Z9Bqv00FVL5N3+JCUoqmQPjIlya1BbeqQroNQ5yG1i\n' + 'VbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9diDMnrzijAnYlTLOl84CK2vOxkj5b6\n' + '8zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcuVi2lT9VjygFs1ILG4RyeX1BXUumu\n' + 'Y8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u114wS7OQPigu6G99dpn/iPHa3zBm8\n' + '7baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADPTdnO/SO+kvXy7fqd8atSn+HlQcx6\n' + 'tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft2QIDAQABAoIBAG1exe3/LEBrPLfb\n' + 'U8iRdY0lxFvHYIhDgIwohC3wUdMYb5SMurpNdEZn+7Sh/fkUVgp/GKJViu1mvh52\n' + 'bKd2r52DwG9NQBQjVgkqY/auRYSglIPpr8PpYNSZlcneunCDGeqEY9hMmXc5Ssqs\n' + 'PQYoEKKPN+IlDTg6PguDgAfLR4IUvt9KXVvmB/SSgV9tSeTy35LECt1Lq3ozbUgu\n' + '30HZI3U6/7H+X22Pxxf8vzBtzkg5rRCLgv+OeNPo16xMnqbutt4TeqEkxRv5rtOo\n' + '/A1i9khBeki0OJAFJsE82qnaSZodaRsxic59VnN8sWBwEKAt87tEu5A3K3j4XSDU\n' + '/avZxAECgYEA+pS3DvpiQLtHlaO3nAH6MxHRrREOARXWRDe5nUQuUNpS1xq9wte6\n' + 'DkFtba0UCvDLic08xvReTCbo9kH0y6zEy3zMpZuJlKbcWCkZf4S5miYPI0RTZtF8\n' + 'yps6hWqzYFSiO9hMYws9k4OJLxX0x3sLK7iNZ32ujcSrkPBSiBr0gxkCgYEA0dWl\n' + '637K41AJ/zy0FP0syq+r4eIkfqv+/t6y2aQVUBvxJYrj9ci6XHBqoxpDV8lufVYj\n' + 'fUAfeI9/MZaWvQJRbnYLre0I6PJfLuCBIL5eflO77BGso165AF7QJZ+fwtgKv3zv\n' + 'ZX75eudCSS/cFo0po9hlbcLMT4B82zEkgT8E2MECgYEAnz+3/wrdOmpLGiyL2dff\n' + '3GjsqmJ2VfY8z+niSrI0BSpbD11tT9Ct67VlCBjA7hsOH6uRfpd6/kaUMzzDiFVq\n' + 'VDAiFvV8QD6zNkwYalQ9aFvbrvwTTPrBpjl0vamMCiJ/YC0cjq1sGr2zh3sar1Ph\n' + 'S43kP+s97dcZeelhaiJHVrECgYEAsx61q/loJ/LDFeYzs1cLTVn4V7I7hQY9fkOM\n' + 'WM0AhInVqD6PqdfXfeFYpjJdGisQ7l0BnoGGW9vir+nkcyPvb2PFRIr6+B8tsU5j\n' + '7BeVgjDoUfQkcrEBK5fEBtnj/ud9BUkY8oMZZBjVNLRuI7IMwZiPvMp0rcj4zAN/\n' + 'LfUlpgECgYArBvFcBxSkNAzR3Rtteud1YDboSKluRM37Ey5plrn4BS0DD0jm++aD\n' + '0pG2Hsik000hibw92lCkzvvBVAqF8BuAcnPlAeYfsOaa97PGEjSKEN5bJVWZ9/om\n' + '9FV1axotRN2XWlwrhixZLEaagkREXhgQc540FS5O8IaI2Vpa80Atzg==\n' + '-----END RSA PRIVATE KEY-----' var publicKeyPEMs = {} publicKeyPEMs['key-1'] = '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzWSJl+Z9Bqv00FVL5N3+\n' + 'JCUoqmQPjIlya1BbeqQroNQ5yG1iVbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9di\n' + 'DMnrzijAnYlTLOl84CK2vOxkj5b68zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcu\n' + 'Vi2lT9VjygFs1ILG4RyeX1BXUumuY8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u1\n' + '14wS7OQPigu6G99dpn/iPHa3zBm87baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADP\n' + 'TdnO/SO+kvXy7fqd8atSn+HlQcx6tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft\n' + '2QIDAQAB\n' + '-----END PUBLIC KEY-----' publicKeyPEMs['key-2'] = '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqp04VVr9OThli9b35Omz\n' + 'VqSfWbsoQuRrgyWsrNRn3XkFmbWw4FzZwQ42OgGMzQ84Ta4d9zGKKQyFriTiPjPf\n' + 'xhhrsaJnDuybcpVhcr7UNKjSZ0S59tU3hpRiEz6hO+Nc/OSSLkvalG0VKrxOln7J\n' + 'LK/h3rNS/l6wDZ5S/KqsI6CYtV2ZLpn3ahLrizvEYNY038Qcm38qMWx+VJAvZ4di\n' + 'qqmW7RLIsLT59SWmpXdhFKnkYYGhxrk1Mwl22dBTJNY5SbriU5G3gWgzYkm8pgHr\n' + '6CtrXch9ciJAcDJehPrKXNvNDOdUh8EW3fekNJerF1lWcwQg44/12v8sDPyfbaKB\n' + 'dQIDAQAB\n' + '-----END PUBLIC KEY-----' var server = createServer(function (req, res) { var parsed = httpSignature.parseRequest(req) var publicKeyPEM = publicKeyPEMs[parsed.keyId] var verified = httpSignature.verifySignature(parsed, publicKeyPEM) res.writeHead(verified ? 200 : 400) res.end() }) server.listen(8080, function () { function correctKeyTest(callback) { var options = { httpSignature: { keyId: 'key-1', key: privateKeyPEMs['key-1'] } } request('http://localhost:8080', options, function (e, r, b) { assert.equal(200, r.statusCode) callback() }) } function incorrectKeyTest(callback) { var options = { httpSignature: { keyId: 'key-2', key: privateKeyPEMs['key-1'] } } request('http://localhost:8080', options, function (e, r, b) { assert.equal(400, r.statusCode) callback() }) } var tests = [correctKeyTest, incorrectKeyTest] var todo = tests.length; for(var i = 0; i < tests.length; ++i) { tests[i](function() { if(!--todo) { server.close() } }) } }) request-2.26.1/tests/test-httpModule.js000066400000000000000000000050111220047311200200610ustar00rootroot00000000000000var http = require('http') , https = require('https') , server = require('./server') , assert = require('assert') , request = require('../index') var faux_requests_made = {'http':0, 'https':0} function wrap_request(name, module) { // Just like the http or https module, but note when a request is made. var wrapped = {} Object.keys(module).forEach(function(key) { var value = module[key]; if(key != 'request') wrapped[key] = value; else wrapped[key] = function(options, callback) { faux_requests_made[name] += 1 return value.apply(this, arguments) } }) return wrapped; } var faux_http = wrap_request('http', http) , faux_https = wrap_request('https', https) , plain_server = server.createServer() , https_server = server.createSSLServer() plain_server.listen(plain_server.port, function() { plain_server.on('/plain', function (req, res) { res.writeHead(200) res.end('plain') }) plain_server.on('/to_https', function (req, res) { res.writeHead(301, {'location':'https://localhost:'+https_server.port + '/https'}) res.end() }) https_server.listen(https_server.port, function() { https_server.on('/https', function (req, res) { res.writeHead(200) res.end('https') }) https_server.on('/to_plain', function (req, res) { res.writeHead(302, {'location':'http://localhost:'+plain_server.port + '/plain'}) res.end() }) run_tests() run_tests({}) run_tests({'http:':faux_http}) run_tests({'https:':faux_https}) run_tests({'http:':faux_http, 'https:':faux_https}) }) }) function run_tests(httpModules) { var to_https = 'http://localhost:'+plain_server.port+'/to_https' var to_plain = 'https://localhost:'+https_server.port+'/to_plain' request(to_https, {'httpModules':httpModules, strictSSL:false}, function (er, res, body) { if (er) throw er assert.equal(body, 'https', 'Received HTTPS server body') done() }) request(to_plain, {'httpModules':httpModules, strictSSL:false}, function (er, res, body) { if (er) throw er assert.equal(body, 'plain', 'Received HTTPS server body') done() }) } var passed = 0; function done() { passed += 1 var expected = 10 if(passed == expected) { plain_server.close() https_server.close() assert.equal(faux_requests_made.http, 4, 'Wrapped http module called appropriately') assert.equal(faux_requests_made.https, 4, 'Wrapped https module called appropriately') console.log((expected+2) + ' tests passed.') } } request-2.26.1/tests/test-https-strict.js000066400000000000000000000050631220047311200204130ustar00rootroot00000000000000// a test where we validate the siguature of the keys // otherwise exactly the same as the ssl test var server = require('./server') , assert = require('assert') , request = require('../index') , fs = require('fs') , path = require('path') , opts = { key: path.resolve(__dirname, 'ssl/ca/server.key') , cert: path.resolve(__dirname, 'ssl/ca/server.crt') } , s = server.createSSLServer(null, opts) , caFile = path.resolve(__dirname, 'ssl/ca/ca.crt') , ca = fs.readFileSync(caFile) var tests = { testGet : { resp : server.createGetResponse("TESTING!") , expectBody: "TESTING!" } , testGetChunkBreak : { resp : server.createChunkResponse( [ new Buffer([239]) , new Buffer([163]) , new Buffer([191]) , new Buffer([206]) , new Buffer([169]) , new Buffer([226]) , new Buffer([152]) , new Buffer([131]) ]) , expectBody: "Ω☃" } , testGetJSON : { resp : server.createGetResponse('{"test":true}', 'application/json') , json : true , expectBody: {"test":true} } , testPutString : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : "PUTTINGDATA" } , testPutBuffer : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : new Buffer("PUTTINGDATA") } , testPutJSON : { resp : server.createPostValidator(JSON.stringify({foo: 'bar'})) , method: "PUT" , json: {foo: 'bar'} } , testPutMultipart : { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ) , method: "PUT" , multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'} , {'body': 'Oh hi.'} ] } } s.listen(s.port, function () { var counter = 0 for (i in tests) { (function () { var test = tests[i] s.on('/'+i, test.resp) test.uri = s.url + '/' + i test.strictSSL = true test.ca = ca test.headers = { host: 'testing.request.mikealrogers.com' } request(test, function (err, resp, body) { if (err) throw err if (test.expectBody) { assert.deepEqual(test.expectBody, body) } counter = counter - 1; if (counter === 0) { console.log(Object.keys(tests).length+" tests passed.") s.close() } }) counter++ })() } }) request-2.26.1/tests/test-https.js000066400000000000000000000041611220047311200171030ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') var s = server.createSSLServer(); var tests = { testGet : { resp : server.createGetResponse("TESTING!") , expectBody: "TESTING!" } , testGetChunkBreak : { resp : server.createChunkResponse( [ new Buffer([239]) , new Buffer([163]) , new Buffer([191]) , new Buffer([206]) , new Buffer([169]) , new Buffer([226]) , new Buffer([152]) , new Buffer([131]) ]) , expectBody: "Ω☃" } , testGetJSON : { resp : server.createGetResponse('{"test":true}', 'application/json') , json : true , expectBody: {"test":true} } , testPutString : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : "PUTTINGDATA" } , testPutBuffer : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : new Buffer("PUTTINGDATA") } , testPutJSON : { resp : server.createPostValidator(JSON.stringify({foo: 'bar'})) , method: "PUT" , json: {foo: 'bar'} } , testPutMultipart : { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ) , method: "PUT" , multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'} , {'body': 'Oh hi.'} ] } } s.listen(s.port, function () { var counter = 0 for (i in tests) { (function () { var test = tests[i] s.on('/'+i, test.resp) test.uri = s.url + '/' + i test.rejectUnauthorized = false request(test, function (err, resp, body) { if (err) throw err if (test.expectBody) { assert.deepEqual(test.expectBody, body) } counter = counter - 1; if (counter === 0) { console.log(Object.keys(tests).length+" tests passed.") s.close() } }) counter++ })() } }) request-2.26.1/tests/test-isUrl.js000066400000000000000000000012251220047311200170350ustar00rootroot00000000000000var assert = require('assert') , request = require('../index') , http = require('http') ; var s = http.createServer(function(req, res) { res.statusCode = 200; res.end(''); }).listen(6767, function () { // Test lowercase request('http://localhost:6767', function (err, resp, body) { // just need to get here without throwing an error assert.equal(true, true); }) // Test uppercase request('HTTP://localhost:6767', function (err, resp, body) { assert.equal(true, true); }) // Test mixedcase request('HtTp://localhost:6767', function (err, resp, body) { assert.equal(true, true); // clean up s.close(); }) })request-2.26.1/tests/test-localAddress.js000066400000000000000000000006171220047311200203430ustar00rootroot00000000000000var request = require('../index') , assert = require('assert') ; request.get({ uri: 'http://www.google.com', localAddress: '1.2.3.4' // some invalid address }, function(err, res) { assert(!res) // asserting that no response received }) request.get({ uri: 'http://www.google.com', localAddress: '127.0.0.1' }, function(err, res) { assert(!res) // asserting that no response received }) request-2.26.1/tests/test-oauth.js000066400000000000000000000102201220047311200170520ustar00rootroot00000000000000var hmacsign = require('oauth-sign').hmacsign , assert = require('assert') , qs = require('querystring') , request = require('../index') ; function getsignature (r) { var sign 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) } // Tests from Twitter documentation https://dev.twitter.com/docs/auth/oauth var reqsign = hmacsign('POST', 'https://api.twitter.com/oauth/request_token', { oauth_callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11' , oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g' , oauth_nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk' , oauth_signature_method: 'HMAC-SHA1' , oauth_timestamp: '1272323042' , oauth_version: '1.0' }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98") console.log(reqsign) console.log('8wUi7m5HFQy76nowoCThusfgB+Q=') assert.equal(reqsign, '8wUi7m5HFQy76nowoCThusfgB+Q=') var accsign = hmacsign('POST', 'https://api.twitter.com/oauth/access_token', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g' , oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8' , oauth_signature_method: 'HMAC-SHA1' , oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc' , oauth_timestamp: '1272323047' , oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY' , oauth_version: '1.0' }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98", "x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA") console.log(accsign) console.log('PUw/dHA4fnlJYM6RhXk5IU/0fCc=') assert.equal(accsign, 'PUw/dHA4fnlJYM6RhXk5IU/0fCc=') var upsign = hmacsign('POST', 'http://api.twitter.com/1/statuses/update.json', { oauth_consumer_key: "GDdmIQH6jhtmLUypg82g" , oauth_nonce: "oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y" , oauth_signature_method: "HMAC-SHA1" , oauth_token: "819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw" , oauth_timestamp: "1272325550" , oauth_version: "1.0" , status: 'setting up my twitter 私のさえずりを設定する' }, "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98", "J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA") console.log(upsign) console.log('yOahq5m0YjDDjfjxHaXEsW9D+X0=') assert.equal(upsign, 'yOahq5m0YjDDjfjxHaXEsW9D+X0=') 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' , consumer_key: 'GDdmIQH6jhtmLUypg82g' , nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk' , timestamp: '1272323042' , version: '1.0' , consumer_secret: "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98" } }) setTimeout(function () { console.log(getsignature(rsign)) assert.equal(reqsign, getsignature(rsign)) }) var raccsign = request.post( { url: 'https://api.twitter.com/oauth/access_token' , oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g' , nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8' , signature_method: 'HMAC-SHA1' , token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc' , timestamp: '1272323047' , verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY' , version: '1.0' , consumer_secret: "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98" , token_secret: "x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA" } }) setTimeout(function () { console.log(getsignature(raccsign)) assert.equal(accsign, getsignature(raccsign)) }, 1) var rupsign = request.post( { url: 'http://api.twitter.com/1/statuses/update.json' , oauth: { consumer_key: "GDdmIQH6jhtmLUypg82g" , nonce: "oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y" , signature_method: "HMAC-SHA1" , token: "819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw" , timestamp: "1272325550" , version: "1.0" , consumer_secret: "MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98" , token_secret: "J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA" } , form: {status: 'setting up my twitter 私のさえずりを設定する'} }) setTimeout(function () { console.log(getsignature(rupsign)) assert.equal(upsign, getsignature(rupsign)) }, 1) request-2.26.1/tests/test-onelineproxy.js000066400000000000000000000022111220047311200204660ustar00rootroot00000000000000var http = require('http') , assert = require('assert') , request = require('../index') ; var server = http.createServer(function (req, resp) { resp.statusCode = 200 if (req.url === '/get') { assert.equal(req.method, 'GET') resp.write('content') resp.end() return } if (req.url === '/put') { var x = '' assert.equal(req.method, 'PUT') req.on('data', function (chunk) { x += chunk }) req.on('end', function () { assert.equal(x, 'content') resp.write('success') resp.end() }) return } if (req.url === '/proxy') { assert.equal(req.method, 'PUT') return req.pipe(request('http://localhost:8080/put')).pipe(resp) } if (req.url === '/test') { return request('http://localhost:8080/get').pipe(request.put('http://localhost:8080/proxy')).pipe(resp) } throw new Error('Unknown url', req.url) }).listen(8080, function () { request('http://localhost:8080/test', function (e, resp, body) { if (e) throw e if (resp.statusCode !== 200) throw new Error('statusCode not 200 ' + resp.statusCode) assert.equal(body, 'success') server.close() }) }) request-2.26.1/tests/test-params.js000066400000000000000000000046451220047311200172330ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') ; var s = server.createServer(); var tests = { testGet : { resp : server.createGetResponse("TESTING!") , expectBody: "TESTING!" } , testGetChunkBreak : { resp : server.createChunkResponse( [ new Buffer([239]) , new Buffer([163]) , new Buffer([191]) , new Buffer([206]) , new Buffer([169]) , new Buffer([226]) , new Buffer([152]) , new Buffer([131]) ]) , expectBody: "Ω☃" } , testGetBuffer : { resp : server.createGetResponse(new Buffer("TESTING!")) , encoding: null , expectBody: new Buffer("TESTING!") } , testGetJSON : { resp : server.createGetResponse('{"test":true}', 'application/json') , json : true , expectBody: {"test":true} } , testPutString : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : "PUTTINGDATA" } , testPutBuffer : { resp : server.createPostValidator("PUTTINGDATA") , method : "PUT" , body : new Buffer("PUTTINGDATA") } , testPutJSON : { resp : server.createPostValidator(JSON.stringify({foo: 'bar'})) , method: "PUT" , json: {foo: 'bar'} } , testPutMultipart : { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ) , method: "PUT" , multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'} , {'body': 'Oh hi.'} ] } } s.listen(s.port, function () { var counter = 0 for (i in tests) { (function () { var test = tests[i] s.on('/'+i, test.resp) //test.uri = s.url + '/' + i request(s.url + '/' + i, test, function (err, resp, body) { if (err) throw err if (test.expectBody) { assert.deepEqual(test.expectBody, body) } counter = counter - 1; if (counter === 0) { assert.notEqual(typeof test.callback, 'function') console.log(1 + Object.keys(tests).length+" tests passed.") s.close() } }) counter++ })() } }) request-2.26.1/tests/test-piped-redirect.js000066400000000000000000000015441220047311200206430ustar00rootroot00000000000000var http = require('http') , assert = require('assert') , request = require('../index') ; var portOne = 8968 , portTwo = 8969 ; // server one var s1 = http.createServer(function (req, resp) { if (req.url == '/original') { resp.writeHeader(302, {'location': '/redirected'}) resp.end() } else if (req.url == '/redirected') { resp.writeHeader(200, {'content-type': 'text/plain'}) resp.write('OK') resp.end() } }).listen(portOne); // server two var s2 = http.createServer(function (req, resp) { var x = request('http://localhost:'+portOne+'/original') req.pipe(x) x.pipe(resp) }).listen(portTwo, function () { var r = request('http://localhost:'+portTwo+'/original', function (err, res, body) { assert.equal(body, 'OK') s1.close() s2.close() }); // it hangs, so wait a second :) r.timeout = 1000; }) request-2.26.1/tests/test-pipes.js000066400000000000000000000131141220047311200170570ustar00rootroot00000000000000var server = require('./server') , events = require('events') , stream = require('stream') , assert = require('assert') , fs = require('fs') , request = require('../index') , path = require('path') , util = require('util') ; var s = server.createServer(3453); function ValidationStream(str) { this.str = str this.buf = '' this.on('data', function (data) { this.buf += data }) this.on('end', function () { assert.equal(this.str, this.buf) }) this.writable = true } util.inherits(ValidationStream, stream.Stream) ValidationStream.prototype.write = function (chunk) { this.emit('data', chunk) } ValidationStream.prototype.end = function (chunk) { if (chunk) emit('data', chunk) this.emit('end') } s.listen(s.port, function () { counter = 0; var check = function () { counter = counter - 1 if (counter === 0) { console.log('All tests passed.') setTimeout(function () { process.exit(); }, 500) } } // Test pipeing to a request object s.once('/push', server.createPostValidator("mydata")); var mydata = new stream.Stream(); mydata.readable = true counter++ var r1 = request.put({url:'http://localhost:3453/push'}, function () { check(); }) mydata.pipe(r1) 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")); var mypulldata = new stream.Stream(); mypulldata.writable = true counter++ request({url:'http://localhost:3453/pull'}).pipe(mypulldata) var d = ''; mypulldata.write = function (chunk) { d += chunk; } mypulldata.end = function () { assert.equal(d, 'mypulldata'); check(); }; s.on('/cat', function (req, resp) { if (req.method === "GET") { resp.writeHead(200, {'content-type':'text/plain-test', 'content-length':4}); resp.end('asdf') } else if (req.method === "PUT") { assert.equal(req.headers['content-type'], 'text/plain-test'); assert.equal(req.headers['content-length'], 4) var validate = ''; req.on('data', function (chunk) {validate += chunk}) req.on('end', function () { resp.writeHead(201); resp.end(); assert.equal(validate, 'asdf'); check(); }) } }) s.on('/pushjs', function (req, resp) { if (req.method === "PUT") { assert.equal(req.headers['content-type'], 'application/javascript'); check(); } }) s.on('/catresp', function (req, resp) { request.get('http://localhost:3453/cat').pipe(resp) }) s.on('/doodle', function (req, resp) { if (req.headers['x-oneline-proxy']) { resp.setHeader('x-oneline-proxy', 'yup') } resp.writeHead('200', {'content-type':'image/jpeg'}) fs.createReadStream(path.join(__dirname, 'googledoodle.jpg')).pipe(resp) }) s.on('/onelineproxy', function (req, resp) { var x = request('http://localhost:3453/doodle') req.pipe(x) x.pipe(resp) }) counter++ fs.createReadStream(__filename).pipe(request.put('http://localhost:3453/pushjs')) counter++ request.get('http://localhost:3453/cat').pipe(request.put('http://localhost:3453/cat')) counter++ request.get('http://localhost:3453/catresp', function (e, resp, body) { assert.equal(resp.headers['content-type'], 'text/plain-test'); assert.equal(resp.headers['content-length'], 4) check(); }) var doodleWrite = fs.createWriteStream(path.join(__dirname, 'test.jpg')) counter++ request.get('http://localhost:3453/doodle').pipe(doodleWrite) doodleWrite.on('close', function () { assert.deepEqual(fs.readFileSync(path.join(__dirname, 'googledoodle.jpg')), fs.readFileSync(path.join(__dirname, 'test.jpg'))) check() }) process.on('exit', function () { fs.unlinkSync(path.join(__dirname, 'test.jpg')) }) counter++ request.get({uri:'http://localhost:3453/onelineproxy', headers:{'x-oneline-proxy':'nope'}}, function (err, resp, body) { assert.equal(resp.headers['x-oneline-proxy'], 'yup') check() }) s.on('/afterresponse', function (req, resp) { resp.write('d') resp.end() }) counter++ var afterresp = request.post('http://localhost:3453/afterresponse').on('response', function () { var v = new ValidationStream('d') 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 }) }) request-2.26.1/tests/test-pool.js000066400000000000000000000007001220047311200167050ustar00rootroot00000000000000var request = require('../index') , http = require('http') , assert = require('assert') ; var s = http.createServer(function (req, resp) { resp.statusCode = 200; resp.end('asdf'); }).listen(8080, function () { request({'url': 'http://localhost:8080', 'pool': false}, function (e, resp) { var agent = resp.request.agent; assert.strictEqual(typeof agent, 'boolean'); assert.strictEqual(agent, false); s.close(); }); });request-2.26.1/tests/test-protocol-changing-redirect.js000066400000000000000000000025061220047311200231560ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') var s = server.createServer() var ss = server.createSSLServer() var sUrl = 'http://localhost:' + s.port var ssUrl = 'https://localhost:' + ss.port s.listen(s.port, bouncy(s, ssUrl)) ss.listen(ss.port, bouncy(ss, sUrl)) var hits = {} var expect = {} var pending = 0 function bouncy (s, server) { return function () { var redirs = { a: 'b' , b: 'c' , c: 'd' , d: 'e' , e: 'f' , f: 'g' , g: 'h' , h: 'end' } var perm = true Object.keys(redirs).forEach(function (p) { var t = redirs[p] // switch type each time var type = perm ? 301 : 302 perm = !perm s.on('/' + p, function (req, res) { res.writeHead(type, { location: server + '/' + t }) res.end() }) }) s.on('/end', function (req, res) { var h = req.headers['x-test-key'] hits[h] = true pending -- if (pending === 0) done() }) }} for (var i = 0; i < 5; i ++) { pending ++ var val = 'test_' + i expect[val] = true request({ url: (i % 2 ? sUrl : ssUrl) + '/a' , headers: { 'x-test-key': val } , rejectUnauthorized: false }) } function done () { assert.deepEqual(hits, expect) process.exit(0) } request-2.26.1/tests/test-proxy.js000066400000000000000000000016261220047311200171250ustar00rootroot00000000000000var server = require('./server') , events = require('events') , stream = require('stream') , assert = require('assert') , fs = require('fs') , request = require('../index') , path = require('path') , util = require('util') ; var port = 6768 , called = false , proxiedHost = 'google.com' ; var s = server.createServer(port) s.listen(port, function () { s.on('http://google.com/', function (req, res) { called = true assert.equal(req.headers.host, proxiedHost) res.writeHeader(200) res.end() }) request ({ url: 'http://'+proxiedHost, proxy: 'http://localhost:'+port /* //should behave as if these arguments where passed: url: 'http://localhost:'+port, headers: {host: proxiedHost} //*/ }, function (err, res, body) { s.close() }) }) process.on('exit', function () { assert.ok(called, 'the request must be made to the proxy server') }) request-2.26.1/tests/test-qs.js000066400000000000000000000022731220047311200163660ustar00rootroot00000000000000var request = request = require('../index') , assert = require('assert') ; // Test adding a querystring var req1 = request.get({ uri: 'http://www.google.com', qs: { q : 'search' }}) setTimeout(function() { assert.equal('/?q=search', req1.path) }, 1) // Test replacing a querystring value var req2 = request.get({ uri: 'http://www.google.com?q=abc', qs: { q : 'search' }}) setTimeout(function() { assert.equal('/?q=search', req2.path) }, 1) // Test appending a querystring value to the ones present in the uri var req3 = request.get({ uri: 'http://www.google.com?x=y', qs: { q : 'search' }}) setTimeout(function() { assert.equal('/?x=y&q=search', req3.path) }, 1) // Test leaving a querystring alone var req4 = request.get({ uri: 'http://www.google.com?x=y'}) setTimeout(function() { assert.equal('/?x=y', req4.path) }, 1) // Test giving empty qs property var req5 = request.get({ uri: 'http://www.google.com', qs: {}}) setTimeout(function(){ assert.equal('/', req5.path) }, 1) // Test modifying the qs after creating the request var req6 = request.get({ uri: 'http://www.google.com', qs: {}}); req6.qs({ q: "test" }); process.nextTick(function() { assert.equal('/?q=test', req6.path); }); request-2.26.1/tests/test-redirect.js000066400000000000000000000133261220047311200175450ustar00rootroot00000000000000var server = require('./server') , assert = require('assert') , request = require('../index') , Cookie = require('cookie-jar') , Jar = Cookie.Jar ; var s = server.createServer() s.listen(s.port, function () { var server = 'http://localhost:' + s.port; var hits = {} var passed = 0; bouncer(301, 'temp') bouncer(302, 'perm') bouncer(302, 'nope') function bouncer(code, label) { var landing = label+'_landing'; s.on('/'+label, function (req, res) { hits[label] = true; res.writeHead(code, { 'location':server + '/'+landing, 'set-cookie': 'ham=eggs' }) res.end() }) s.on('/'+landing, function (req, res) { if (req.method !== 'GET') { // We should only accept GET redirects console.error("Got a non-GET request to the redirect destination URL"); res.writeHead(400); res.end(); return; } // Make sure the cookie doesn't get included twice, see #139: // Make sure cookies are set properly after redirect assert.equal(req.headers.cookie, 'foo=bar; quux=baz; ham=eggs'); hits[landing] = true; res.writeHead(200) res.end(landing) }) } // Permanent bounce var jar = new Jar() jar.add(new Cookie('quux=baz')) request({uri: server+'/perm', jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 200) throw new Error('Status is not 200: '+res.statusCode) assert.ok(hits.perm, 'Original request is to /perm') assert.ok(hits.perm_landing, 'Forward to permanent landing URL') assert.equal(body, 'perm_landing', 'Got permanent landing content') passed += 1 done() }) // Temporary bounce request({uri: server+'/temp', jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 200) throw new Error('Status is not 200: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(hits.temp_landing, 'Forward to temporary landing URL') assert.equal(body, 'temp_landing', 'Got temporary landing content') passed += 1 done() }) // Prevent bouncing. request({uri:server+'/nope', jar: jar, headers: {cookie: 'foo=bar'}, followRedirect:false}, function (er, res, body) { if (er) throw er if (res.statusCode !== 302) throw new Error('Status is not 302: '+res.statusCode) assert.ok(hits.nope, 'Original request to /nope') assert.ok(!hits.nope_landing, 'No chasing the redirect') assert.equal(res.statusCode, 302, 'Response is the bounce itself') passed += 1 done() }) // Should not follow post redirects by default request.post(server+'/temp', { jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 301) throw new Error('Status is not 301: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(!hits.temp_landing, 'No chasing the redirect when post') assert.equal(res.statusCode, 301, 'Response is the bounce itself') passed += 1 done() }) // Should follow post redirects when followAllRedirects true request.post({uri:server+'/temp', followAllRedirects:true, jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 200) throw new Error('Status is not 200: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(hits.temp_landing, 'Forward to temporary landing URL') assert.equal(body, 'temp_landing', 'Got temporary landing content') passed += 1 done() }) request.post({uri:server+'/temp', followAllRedirects:false, jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 301) throw new Error('Status is not 301: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(!hits.temp_landing, 'No chasing the redirect') assert.equal(res.statusCode, 301, 'Response is the bounce itself') passed += 1 done() }) // Should not follow delete redirects by default request.del(server+'/temp', { jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode < 301) throw new Error('Status is not a redirect.') assert.ok(hits.temp, 'Original request is to /temp') assert.ok(!hits.temp_landing, 'No chasing the redirect when delete') assert.equal(res.statusCode, 301, 'Response is the bounce itself') passed += 1 done() }) // Should not follow delete redirects even if followRedirect is set to true request.del(server+'/temp', { followRedirect: true, jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 301) throw new Error('Status is not 301: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(!hits.temp_landing, 'No chasing the redirect when delete') assert.equal(res.statusCode, 301, 'Response is the bounce itself') passed += 1 done() }) // Should follow delete redirects when followAllRedirects true request.del(server+'/temp', {followAllRedirects:true, jar: jar, headers: {cookie: 'foo=bar'}}, function (er, res, body) { if (er) throw er if (res.statusCode !== 200) throw new Error('Status is not 200: '+res.statusCode) assert.ok(hits.temp, 'Original request is to /temp') assert.ok(hits.temp_landing, 'Forward to temporary landing URL') assert.equal(body, 'temp_landing', 'Got temporary landing content') passed += 1 done() }) var reqs_done = 0; function done() { reqs_done += 1; if(reqs_done == 9) { console.log(passed + ' tests passed.') s.close() } } }) request-2.26.1/tests/test-s3.js000066400000000000000000000005241220047311200162650ustar00rootroot00000000000000var request = require('../index') var r = request.get('https://log.curlybracecast.com.s3.amazonaws.com/', { aws: { key: 'AKIAI6KIQRRVMGK3WK5Q' , secret: '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' , bucket: 'log.curlybracecast.com' } }, function (e, resp, body) { console.log(r.headers) console.log(body) } )request-2.26.1/tests/test-timeout.js000066400000000000000000000036111220047311200174260ustar00rootroot00000000000000var server = require('./server') , events = require('events') , stream = require('stream') , assert = require('assert') , request = require('../index') ; var s = server.createServer(); var expectedBody = "waited"; var remainingTests = 5; s.listen(s.port, function () { // Request that waits for 200ms s.on('/timeout', function (req, resp) { setTimeout(function(){ resp.writeHead(200, {'content-type':'text/plain'}) resp.write(expectedBody) resp.end() }, 200); }); // Scenario that should timeout var shouldTimeout = { url: s.url + "/timeout", timeout:100 } request(shouldTimeout, function (err, resp, body) { assert.equal(err.code, "ETIMEDOUT"); checkDone(); }) // Scenario that shouldn't timeout var shouldntTimeout = { url: s.url + "/timeout", timeout:300 } request(shouldntTimeout, function (err, resp, body) { assert.equal(err, null); assert.equal(expectedBody, body) checkDone(); }) // Scenario with no timeout set, so shouldn't timeout var noTimeout = { url: s.url + "/timeout" } request(noTimeout, function (err, resp, body) { assert.equal(err); assert.equal(expectedBody, body) checkDone(); }) // Scenario with a negative timeout value, should be treated a zero or the minimum delay var negativeTimeout = { url: s.url + "/timeout", timeout:-1000 } request(negativeTimeout, function (err, resp, body) { assert.equal(err.code, "ETIMEDOUT"); checkDone(); }) // Scenario with a float timeout value, should be rounded by setTimeout anyway var floatTimeout = { url: s.url + "/timeout", timeout: 100.76 } request(floatTimeout, function (err, resp, body) { assert.equal(err.code, "ETIMEDOUT"); checkDone(); }) function checkDone() { if(--remainingTests == 0) { s.close(); console.log("All tests passed."); } } }) request-2.26.1/tests/test-toJSON.js000066400000000000000000000005711220047311200170560ustar00rootroot00000000000000var request = require('../index') , http = require('http') , assert = require('assert') ; var s = http.createServer(function (req, resp) { resp.statusCode = 200 resp.end('asdf') }).listen(8080, function () { var r = request('http://localhost:8080', function (e, resp) { assert.equal(JSON.parse(JSON.stringify(r)).response.statusCode, 200) s.close() }) })request-2.26.1/tests/test-tunnel.js000066400000000000000000000040501220047311200172430ustar00rootroot00000000000000// test that we can tunnel a https request over an http proxy // keeping all the CA and whatnot intact. // // Note: this requires that squid is installed. // If the proxy fails to start, we'll just log a warning and assume success. var server = require('./server') , assert = require('assert') , request = require('../index') , fs = require('fs') , path = require('path') , caFile = path.resolve(__dirname, 'ssl/npm-ca.crt') , ca = fs.readFileSync(caFile) , child_process = require('child_process') , sqConf = path.resolve(__dirname, 'squid.conf') , sqArgs = ['-f', sqConf, '-N', '-d', '5'] , proxy = 'http://localhost:3128' , hadError = null var squid = child_process.spawn('squid', sqArgs); var ready = false squid.stderr.on('data', function (c) { console.error('SQUIDERR ' + c.toString().trim().split('\n') .join('\nSQUIDERR ')) ready = c.toString().match(/ready to serve requests|Accepting HTTP Socket connections/i) }) squid.stdout.on('data', function (c) { console.error('SQUIDOUT ' + c.toString().trim().split('\n') .join('\nSQUIDOUT ')) }) squid.on('error', function (c) { console.error('squid: error '+c) if (c && !ready) { notInstalled() return } }) squid.on('exit', function (c) { console.error('squid: exit '+c) if (c && !ready) { notInstalled() return } if (c) { hadError = hadError || new Error('Squid exited with '+c) } if (hadError) throw hadError }) setTimeout(function F () { if (!ready) return setTimeout(F, 100) request({ uri: 'https://registry.npmjs.org/' , proxy: 'http://localhost:3128' , strictSSL: true , ca: ca , json: true }, function (er, body) { hadError = er console.log(er || typeof body) if (!er) console.log("ok") squid.kill('SIGKILL') }) }, 100) function notInstalled() { console.error('squid must be installed to run this test.') console.error('skipping this test. please install squid and run again if you need to test tunneling.') c = null hadError = null process.exit(0) } request-2.26.1/tests/unicycle.jpg000066400000000000000000000465361220047311200167570ustar00rootroot00000000000000JFIFHH(ICC_PROFILEappl scnrRGB XYZ acspAPPLappl-appl rXYZgXYZbXYZ0wtptDchadX,rTRCgTRCbTRCdesc=cprtAdscmXYZ tK>XYZ Zs&XYZ (W3XYZ Rsf32 B&lcurv3descCamera RGB ProfileCamera RGB Profilemluc enUS$esES,LdaDK4deDE,fiFI(frFU<itIT,rnlNL$noNO xptBR(JsvSE*jaJPkoKR2zhTW2zhCNKameran RGB-profiiliRGB-profil fr Kamera000 RGB 000000exOMvj_ RGB r_icϏPerfil RGB para CmaraRGB-kameraprofilRGB-Profil fr Kamerasvg: RGB cϏeNRGB-beskrivelse til KameraRGB-profiel CameratT| RGB \ |Perfil RGB de CmeraProfilo RGB FotocameraCamera RGB ProfileProfil RVB de l appareil-phototextCopyright 2003 Apple Computer Inc., all rights reserved.ExifMM* z(12iCASIO COMPUTER CO.,LTD QV-R41 HH1.00 2005:06:21 01:44:12&"0221.BV ^f  n0100v'  2005:06:21 01:44:122005:06:21 01:44:12wC     C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4J;k2;qggyzPnaoTzU1He8aqZx\ sH.֦:Ÿr)iA ڝRN<+cE|~*W&UGK8 Oaq%[[Q\MΙrq>H= \t@Zgj>VfT}@6m#RҮIE{ q4D2+'ƪ}-|c=empG>߅~-W TMSX~= Լ-;Ơ>{ L['eSЍЭd`sz,x1nGFWF)# _ĭK¹cxn~e3Z3#] ֮:sR* `QךOϳ[NF<k|CIERw(`OC]GϋVJ]SsX|0YJ9C",UAQRVhᱰs׿^-f=>Tܴ.>s\أ@ڐ%d!cSө\^ x׽d&r^`xV?v6]lc'xέT9P͌6xY<%YX1TU85mw_%te<}4= Xn>XkM{v7 sm5kɵV_$&H2ϵt 5Ñm+u&H$1N o4{%k n,8Vr:phc<%doZdrx|>˞P}-#9 e@+T:u퟈4X0$]D)Ł\'~c6 ??o8+"8ngѡ3YIgrڥFo@$zrZCĤ (u78ҶbojjuؠlmyesHutخOO3]x<LNjfUk*{+Qԉzkî~"iSэnGd-W+/%VƷqs$mSaz/"ikQULCk2 =܁ξy?-tx%༚w8'eh؎Y KxM,-ȟqcױOU]7Z[ VݝC 9$W4t-'ϕ 'vr@<`׍*nNZTD-ޣld@d]]#&S td$eH^SPY a̅fܤc+dɢ\dmsxVJ#)Ez6|2(ez7T ?9cK!WϫǧZG֦)sYys9qIj4F_ #|[۟NXb/i a`u(|2Njy7зpzmH80Z֗7-WSzh"Iyn!|CȯEPVCw eΏOQCsvψVmm>TRGsծThv"-Ju5o=tRyC꧷ӥz?ÏNy\@0pN/p~t j^Z3s(?)T1mV(I$yqc 9;x\DTU#>|ygN;db7ұ- Ǫ,+Ef H^-)ӵ+8 i6zMEea4; eJ m~|C9E'}O|/YVmӢ'nW n?g/xnhx^oaeI( lqW3׵Cykc4;@^q|TZ=Ob%tޭ B"tEi2nc ~"k$7OȇBZ2n<x ax6~_[<ߥ׏)9:_׿-%ZRךv:.hR# )-"I֍ky.v[>VJ)$D}3#Vs俏>!5]ZXIbϖ=$xZz<6:3,eBȅK>R_"e,hLs!s޽ÒNInq 꽖5mNo34+a PS'tO|o6a sœ+ҽ cOEYx*K-`6k)#^kukTΔő; Oְ[|-upTVΏi{j֗Aw m rbR$r:r[-d}0|#Ծ>5[4JH, `y;^٫Ǟ4oᘖyM!\A?/x_V<+ Fӡq(O|jN*TNVkxE}*i1Q"kC5͞,[Gw!l$y+JDm bmG$t+{ T)qhđH6yjU'$r|UHsׇ:Jǚךͤp[[Z_n%[2an~^ska(R^[X2/;]/., ЪjTNm  [*+˵ k6ޑt0ܪsBT3קT>flCtytuدdeky#;Xҥ]RQ]2yxZ44"mBnϜ4@pO}9KOxMa{-2lHdĕsNr c_٧x襯b?^'SQ>"uX5F,d(x[[>PkH *ez;;0oD9+ J:x?5fG㧁9: I^W%|nO¡_2=Bp)O{5?w6%T:lA R Z#-{V\o2Ism?"hY?#R0}MG+&Qmtm?j̴b~7،yFc?wҴIǡWBz|ՕeOT~h.oNJ>$6Ye5E#lnَԂ=|ӼIk}sF`a` a+FH2:J*c;wX3nZznxNo,-W"4AQE ϠS^ Kwg ˹m,ԳUgzZ.mwdld-/q܊쯾g s j*pH#gxY)|ጣr..#1 eqBq;rvq'ԏǚK񯌬t>/j̑ȋiurȥ* _ 藰E 6/&I30|X&LMK[{>2މ9)xwfĤ h3mz@+2 sWK'+t2n bP+|Y=GP.!!r}[>WȠH;H :okقlX ?CW-< ,TϽ}[q1lMO‰A+azxG)|LG+ H1=3ׁ!Q~$Ixa!qo$|F?\G_? ?|LOoG&x_L_/-$F0d8?;!D>ث!';u <ȶ&J?_ <5({Cƫ*{q??@hZu>gc#5 }58fRc y4۟r4K| ɒ9+tUWZ%q!x*OvŪNJ5q= /ua3 i~Qm$de }*R@{U[h=<9cm-Hd$U'gqL$ž6a'}J* ¯ qISgEa ~{Vމkeqszम34ͻ{ܓ~]$|e/y֧/ ǧ2+5l/'9o|e[Kn,toY{u>2 kddy6耪+l/^ҿh C/h0iv! }r1$.}0~ҵ3sLڗlZDRebL2:c= fTG=ji~cXto$.ّ_'i\V煮>8N%܎]Ā3Th_7Unm\n/.M>lW/~jwa'[ +tZG- ZG lH$Wº<:Z<&(b y#͟ \.Zu)$poz?6Y~'-.FKYˍ)P9 _7~(i{~'!ѼIM-V) mrݫ3VJmgpݟֱ\ &T<8ledYVdzqvƩ;iO yCͳG"ܴD#WϪjZMbU\KpӹiWtp8=+ښ]Ou ;g<"ĉ֔^)sM8u:)B[ |Z?rB r8֨rP}:zV5DL  F;匑R_a4}?ɣGS8nLTb&Q6cig>ߟYv>3,B78ZǨ6Q+CjmawSzv4i:$q^{Ū<+fWhl>yo%UI0@=xаMˣ.vt>[K(͟<($yѥٱa}kb,Y|X&Kr7sW "Fk;FHU(#uNꖅZb. Rk̯r۩.I(~xš}m!4@:漷ƛtK4Okfoa0/XHgƽ6fM!qu{V|ijqy: 85ۈqG5:?`.ol[꺝%؈"7^'Oعͣ^Gd,\o_$O=Rbm;KkaȀyakY[ړziЮ4DUaVPqGNkJ :oZ+WѯE?Jy! k}Q2|4$SDPݸҫì^iumjDv!Qw >^:)I3fYK 8w:MsR ?Ig&nwB5 =k>:~d%Ƥۏ8*@0s+dU gR{g]SN[mF) w[J`XMK?=?g"~QMB _Rh:n E3^z !bP"27{ҾP7s> Z`y( c5i?~:/ux~Ѧ~DžE2}nFCʫn_\𳪽_GSzC=hnL6VPr1XPx7I5,4Ůyeg vݻ{q-|bŏ*PY*J9G)}?w gr>vnj~^"KK#|ij)E;)ag ;7Z>.iv&wqD'ˌ1+̓]SM[IWҢSJg <E [l6`Ue3ǵs|}2 hA;d\m`qWGv^5ھ/`nMEb6qY}_kF(,vlp@kkZf,Y9ݹOsYW:]F,HN;H8 E{t6Puf{_Kg[nɠPydž}xo ޿&xc5K;iZ 36 3хM29ibpJ>74}B{<{l(Ưx+[fF\gnt8?VnNwuԊ4K%gh;lĞy$fƳӁY؁@@׫9ګ"*gs-O#!>⻿xv\F Pcd*ٓIŨ۷@A+_cӖ2wm\fƤUGPn)G'mkֺߊ5B7@mоv-կGK u@ݹoA2nT1\m_,%$ͳ|zחQsٹdфcSwC3674+KS{Jxw_©OZv} ԙ疩4Y4//EsOB0 ڹvkXK} 7nY~R_Q/_V΃%&j mܬ)<8qlfp۔b${"[7LIr)<P #בF2sV=Җ]]M_gLdybsGJijj7rZɴ& *Xq2VUN1,pFx9YuԺg9㸕[ ;=zW9J:m+=Zv(jTw44Eir[M$=_ kϭ^ۨK60PNG>fzk~kܮWogw>$i%M݌Q@fܸ6Yt=\3Q%yJ-G[kWasjkm+ȯdP?{vھoym]˅bj6|52mM0vCՎ;q^}~{ i bku!EoЌ!*OԮ$]b-g{g|Y޷wPmxv;{ZM kKZvuϥbx GR8 "8a2pk223ھ^j o/Aܒ.AU6173YpۿWY"nz5VK!f囖WGR՞ νNXW>qs_lKHZ98>%m[Դ+{U'-frLyU⾐)"RR8#:l5Euښ0CGehYg^ b¾ ӛ.l#/[`w/#r+ԗ~V}Ԓ;;k 4m-lrX%9*x!{՚Li\cy5v}Gk |*|cou{._·Hm,F`*& ʉG^e 9Oe/q6< 1>?u ƺ9SK4+|qU{R|?p\*"ZE!{ߵtFsS_Ye-#NP|ɲ*˱KJ$zhc+y~#NIBQI{m4nu5cH#l-ʑ(;Jm?Z<5j#!MOA%%K"g+q :+Ɓ-;S1\ ӧ.5g\6# PѤh98!+zM;>O[ N?GkDû`#E~ixQ~(}$Iovƀ|aԚ ;C=ИY"rd%'3$#bOP__3Qc}I֧V>ዻK b=8=2 d ^o[Ȃ8!Ms&[ea<盖-`jgs>dO9g}kjW=FL<Ui5M]ݴ=.$ZvwR{mlax{e:\fҤExrnjq_GO7v3έͯ+o#RɄ)nIoq.c'MgyK[Hӓ_wK' 52b;mG(CJ[;Hb J0xX-q0UcV6rL򿃾 .<[k3\[k%FW5LT4?b刖")GOr\ww&8"xJK-6Vr+` `w9?gMX\OmiqtĴsl]G8ǽt>3x'/ص G)ϺD} tӓo/絗Qͼ, ' j|DʼZ5%mV%/G2]Ќuw]sgs33 " %c ذn.~PէXg–sݐe1\~F|7:z&]DhYK K(K+*כ :I?XVcR>T)>fڽoE}z/cq[]2Vkxs2xEҵkVּζ[<YA&/AҮ>C])NK4#qctnc5ͦEr<-^ q!3} fuƔi>Xϳ|󺕥UJڏnk?a}{=mI3BUF@ppA'َJ֣V5l6ӑnXgWֽՆ二J ͵#ǻnXF̎(릿_AMgz[լzf@_FX)y8 4Zohx;~ۑc[h1)bnܪ@\s%;V Dl1<ܬG͌ϭ};[xb]G>K NU#]\_-X}{?Zxi6ge$,vHf吖F>5]Yq<2 R;_W_K+llIt8j9î^S&)5%]`״gUL,$dLӟʪxe -,8g޾l]v{- xiRO㡯IU['Q-q͚Ƭ**9<}6,, >[[?'3sb0*wVzWk^ +ĄQhVX^ 7 f̹\1N]lUfqtv^3}+"F鉕=X=`qELR֪9±XZ6vq@u6]隸${EٍڀׯJ_7%ŏtGzqyMmf0ˏxJvK"K;fK KуBz1S0=; P9\Z'miw<leυ~-5\ǧ&A<>#39O5QkEvp2GZ)aq}g]KKb5TӤLwOp$LU%NV+^ќ 6׳ [4*rwzv4gi7o+iں)rnch@'y40U}~}kroo9t [Ygv@0hI`N5\#QS[8Ճ2pS|VhTN3WwO!~45+ }vt}BǷz Wֱ\I5q:;=|i*aSGn^TIu[[̇KosXZXy$; ̬:OBj 8qҼtxmv?  %z`i]y3> eQ-V6tTkΚp+Ru#zsmȇ$)ԱK8guFGӊ4k%\۴( N ([HkdIp{utԞ |)TqM+~'ul < 66Zb)1/֠}$'tiqțpۑnUS~)nŨB =Zqc+#=:(tH m9?HmCy8O&<Yyt&xq' |?59'k?)U5gĨxW83_Q O޼)]y/oSkZ(%OaC{/2i+Ws>N1_xbz>k~3"09r5ZKW_N^FI46.%r;_sKfhMcԦap=\_ h^,wV2n}s޾ү!ִK'QIG,6S] @۵%y{WK1u0 C_Jm-֠zx:όn$i- FTG!A~xnu)bB>{#Mg᷋|e5޵;{5Ҡ ARz?SS,g{q1#B,7Smߐf1T+%Ӣ>/95rk¢[jՆȮ.m_'?Eo}M-٤2*nvcS^7Oђ=.L`t^f]J.e90`[[?0okwG=1*pߕmj:hܷkBOUeC~+8=HG3m]0(?luZiy3FO'|ys+{KMFħMEvp@{W5wr-) ~f>13_hj_@&[X:O(Kpm3)wMbUU;T[ZRI ]>Mg_H$e#wS׆Zu[;m#T ႗_UyᾒZ Fa2yM[x:oBѡ60Lν%wMz=S_qɌ˽fɢGO&kgwmh^*ljQYCjQ[:hm zZ+|C:-9ui蠞V9>ʓtz439N3nIYM|<3VjzMUk7Oi xHԺucUL>!Qv&{-m)&ɐ?Xj1 ׅ@0V &s6`~_@w⹏_Z⾑ <0*ַ#8I'٧ ʬ8kתkdG^쭶wUDe<-jH b>9V_6z_X}ľ  }j@ ,eE Nsx<+sL$^t2XKʽ~PznC?{$^߳ǁ:,v0<[2qp1I|H/Kڬ bhWn3͚=Շuilt/oBڄ2pr_hPRZ3}:=zױjrP}7A$NG|+u6f3$L.z֥\4_H49!Ip H-ƿٶ7P ڇͺꚘ#OU~ڟTiϞ>uxwAc=C #)m yqМr鋨&WzmιeCvX2D~5PSOkpOp.nLt9{|