superagent-0.20.0/000077500000000000000000000000001241334056700137535ustar00rootroot00000000000000superagent-0.20.0/.gitignore000066400000000000000000000001511241334056700157400ustar00rootroot00000000000000build lib-cov coverage.html .DS_Store node_modules *.sock test.js components test/node/fixtures/tmp.json superagent-0.20.0/.npmignore000066400000000000000000000000631241334056700157510ustar00rootroot00000000000000support test examples *.sock lib-cov coverage.html superagent-0.20.0/.travis.yml000066400000000000000000000000611241334056700160610ustar00rootroot00000000000000language: node_js node_js: - "0.11" - "0.10" superagent-0.20.0/.zuul.yml000066400000000000000000000003701241334056700155530ustar00rootroot00000000000000ui: mocha-tdd server: ./test/server.js browsers: - name: chrome version: latest platform: 'Mac 10.9' - name: chrome version: latest platform: 'Windows 2008' - name: firefox version: latest - name: ie version: latest superagent-0.20.0/History.md000066400000000000000000000252701241334056700157440ustar00rootroot000000000000000.20.0 / 2014-10-02 ================== * Add toJSON() to request and response instances. (yields) * Prevent HEAD requests from getting parsed. (gjohnson) * Update debug. (TooTallNate) 0.19.1 / 2014-09-24 ================== * Fix basic auth issue when password is falsey value. (gjohnson) 0.19.0 / 2014-09-24 ================== * Add unset() to browser. (shesek) * Prefer XHR over ActiveX. (omeid) * Catch parse errors. (jacwright) * Update qs dependency. (wercker) * Add use() to node. (Financial-Times) * Add response text to errors. (yields) * Don't send empty cookie headers. (undoZen) * Don't parse empty response bodies. (DveMac) * Use hostname when setting cookie host. (prasunsultania) 0.18.2 / 2014-07-12 ================== * Handle parser errors. (kof) * Ensure not to use default parsers when there is a user defined one. (kof) 0.18.1 / 2014-07-05 ================== * Upgrade cookiejar dependency (juanpin) * Support image mime types (nebulade) * Make .agent chainable (kof) * Upgrade debug (TooTallNate) * Fix docs (aheckmann) 0.18.0 / 2014-04-29 =================== * Use "form-data" module for the multipart/form-data implementation. (TooTallNate) * Add basic `field()` and `attach()` functions for HTML5 FormData. (TooTallNate) * Deprecate `part()`. (TooTallNate) * Set default user-agent header. (bevacqua) * Add `unset()` method for removing headers. (bevacqua) * Update cookiejar. (missinglink) * Fix response error formatting. (shesek) 0.17.0 / 2014-03-06 =================== * supply uri malformed error to the callback (yields) * add request event (yields) * allow simple auth (yields) * add request event (yields) * switch to component/reduce (visionmedia) * fix part content-disposition (mscdex) * add browser testing via zuul (defunctzombie) * adds request.use() (johntron) 0.16.0 / 2014-01-07 ================== * remove support for 0.6 (superjoe30) * fix CORS withCredentials (wejendorp) * add "test" script (superjoe30) * add request .accept() method (nickl-) * add xml to mime types mappings (nickl-) * fix parse body error on HEAD requests (gjohnson) * fix documentation typos (matteofigus) * fix content-type + charset (bengourley) * fix null values on query parameters (cristiandouce) 0.15.7 / 2013-10-19 ================== * pin should.js to 1.3.0 due to breaking change in 2.0.x * fix browserify regression 0.15.5 / 2013-10-09 ================== * add browser field to support browserify * fix .field() value number support 0.15.4 / 2013-07-09 ================== * node: add a Request#agent() function to set the http Agent to use 0.15.3 / 2013-07-05 ================== * fix .pipe() unzipping on more recent nodes. Closes #240 * fix passing an empty object to .query() no longer appends "?" * fix formidable error handling * update formidable 0.15.2 / 2013-07-02 ================== * fix: emit 'end' when piping. 0.15.1 / 2013-06-26 ================== * add try/catch around parseLinks 0.15.0 / 2013-06-25 ================== * make `Response#toError()` have a more meaningful `message` 0.14.9 / 2013-06-15 ================== * add debug()s to the node client * add .abort() method to node client 0.14.8 / 2013-06-13 ================== * set .agent = false always * remove X-Requested-With. Closes #189 0.14.7 / 2013-06-06 ================== * fix unzip error handling 0.14.6 / 2013-05-23 ================== * fix HEAD unzip bug 0.14.5 / 2013-05-23 ================== * add flag to ensure the callback is __never__ invoked twice 0.14.4 / 2013-05-22 ================== * add superagent.js build output * update qs * update emitter-component * revert "add browser field to support browserify" see GH-221 0.14.3 / 2013-05-18 ================== * add browser field to support browserify 0.14.2/ 2013-05-07 ================== * add host object check to fix serialization of File/Blobs etc as json 0.14.1 / 2013-04-09 ================== * update qs 0.14.0 / 2013-04-02 ================== * add client-side basic auth * fix retaining of .set() header field case 0.13.0 / 2013-03-13 ================== * add progress events to client * add simple example * add res.headers as alias of res.header for browser client * add res.get(field) to node/client 0.12.4 / 2013-02-11 ================== * fix get content-type even if can't get other headers in firefox. fixes #181 0.12.3 / 2013-02-11 ================== * add quick "progress" event support 0.12.2 / 2013-02-04 ================== * add test to check if response acts as a readable stream * add ReadableStream in the Response prototype. * add test to assert correct redirection when the host changes in the location header. * add default Accept-Encoding. Closes #155 * fix req.pipe() return value of original stream for node parity. Closes #171 * remove the host header when cleaning headers to properly follow the redirection. 0.12.1 / 2013-01-10 ================== * add x-domain error handling 0.12.0 / 2013-01-04 ================== * add header persistence on redirects 0.11.0 / 2013-01-02 ================== * add .error Error object. Closes #156 * add forcing of res.text removal for FF HEAD responses. Closes #162 * add reduce component usage. Closes #90 * move better-assert dep to development deps 0.10.0 / 2012-11-14 ================== * add req.timeout(ms) support for the client 0.9.10 / 2012-11-14 ================== * fix client-side .query(str) support 0.9.9 / 2012-11-14 ================== * add .parse(fn) support * fix socket hangup with dates in querystring. Closes #146 * fix socket hangup "error" event when a callback of arity 2 is provided 0.9.8 / 2012-11-03 ================== * add emission of error from `Request#callback()` * add a better fix for nodes weird socket hang up error * add PUT/POST/PATCH data support to client short-hand functions * add .license property to component.json * change client portion to build using component(1) * fix GET body support [guille] 0.9.7 / 2012-10-19 ================== * fix `.buffer()` `res.text` when no parser matches 0.9.6 / 2012-10-17 ================== * change: use `this` when `window` is undefined * update to new component spec [juliangruber] * fix emission of "data" events for compressed responses without encoding. Closes #125 0.9.5 / 2012-10-01 ================== * add field name to .attach() * add text "parser" * refactor isObject() * remove wtf isFunction() helper 0.9.4 / 2012-09-20 ================== * fix `Buffer` responses [TooTallNate] * fix `res.type` when a "type" param is present [TooTallNate] 0.9.3 / 2012-09-18 ================== * remove __GET__ `.send()` == `.query()` special-case (__API__ change !!!) 0.9.2 / 2012-09-17 ================== * add `.aborted` prop * add `.abort()`. Closes #115 0.9.1 / 2012-09-07 ================== * add `.forbidden` response property * add component.json * change emitter-component to 0.0.5 * fix client-side tests 0.9.0 / 2012-08-28 ================== * add `.timeout(ms)`. Closes #17 0.8.2 / 2012-08-28 ================== * fix pathname relative redirects. Closes #112 0.8.1 / 2012-08-21 ================== * fix redirects when schema is specified 0.8.0 / 2012-08-19 ================== * add `res.buffered` flag * add buffering of text/*, json and forms only by default. Closes #61 * add `.buffer(false)` cancellation * add cookie jar support [hunterloftis] * add agent functionality [hunterloftis] 0.7.0 / 2012-08-03 ================== * allow `query()` to be called after the internal `req` has been created [tootallnate] 0.6.0 / 2012-07-17 ================== * add `res.send('foo=bar')` default of "application/x-www-form-urlencoded" 0.5.1 / 2012-07-16 ================== * add "methods" dep * add `.end()` arity check to node callbacks * fix unzip support due to weird node internals 0.5.0 / 2012-06-16 ================== * Added "Link" response header field parsing, exposing `res.links` 0.4.3 / 2012-06-15 ================== * Added 303, 305 and 307 as redirect status codes [slaskis] * Fixed passing an object as the url 0.4.2 / 2012-06-02 ================== * Added component support * Fixed redirect data 0.4.1 / 2012-04-13 ================== * Added HTTP PATCH support * Fixed: GET / HEAD when following redirects. Closes #86 * Fixed Content-Length detection for multibyte chars 0.4.0 / 2012-03-04 ================== * Added `.head()` method [browser]. Closes #78 * Added `make test-cov` support * Added multipart request support. Closes #11 * Added all methods that node supports. Closes #71 * Added "response" event providing a Response object. Closes #28 * Added `.query(obj)`. Closes #59 * Added `res.type` (browser). Closes #54 * Changed: default `res.body` and `res.files` to {} * Fixed: port existing query-string fix (browser). Closes #57 0.3.0 / 2012-01-24 ================== * Added deflate/gzip support [guillermo] * Added `res.type` (Content-Type void of params) * Added `res.statusCode` to mirror node * Added `res.headers` to mirror node * Changed: parsers take callbacks * Fixed optional schema support. Closes #49 0.2.0 / 2012-01-05 ================== * Added url auth support * Added `.auth(username, password)` * Added basic auth support [node]. Closes #41 * Added `make test-docs` * Added guillermo's EventEmitter. Closes #16 * Removed `Request#data()` for SS, renamed to `send()` * Removed `Request#data()` from client, renamed to `send()` * Fixed array support. [browser] * Fixed array support. Closes #35 [node] * Fixed `EventEmitter#emit()` 0.1.3 / 2011-10-25 ================== * Added error to callback * Bumped node dep for 0.5.x 0.1.2 / 2011-09-24 ================== * Added markdown documentation * Added `request(url[, fn])` support to the client * Added `qs` dependency to package.json * Added options for `Request#pipe()` * Added support for `request(url, callback)` * Added `request(url)` as shortcut for `request.get(url)` * Added `Request#pipe(stream)` * Added inherit from `Stream` * Added multipart support * Added ssl support (node) * Removed Content-Length field from client * Fixed buffering, `setEncoding()` to utf8 [reported by stagas] * Fixed "end" event when piping 0.1.1 / 2011-08-20 ================== * Added `res.redirect` flag (node) * Added redirect support (node) * Added `Request#redirects(n)` (node) * Added `.set(object)` header field support * Fixed `Content-Length` support 0.1.0 / 2011-08-09 ================== * Added support for multiple calls to `.data()` * Added support for `.get(uri, obj)` * Added GET `.data()` querystring support * Added IE{6,7,8} support [alexyoung] 0.0.1 / 2011-08-05 ================== * Initial commit superagent-0.20.0/LICENSE000066400000000000000000000021121241334056700147540ustar00rootroot00000000000000(The MIT License) Copyright (c) 2014 TJ Holowaychuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. superagent-0.20.0/Makefile000066400000000000000000000013201241334056700154070ustar00rootroot00000000000000 TESTS = test/node/*.js REPORTER = dot all: superagent.js test: @NODE_ENV=test NODE_TLS_REJECT_UNAUTHORIZED=0 ./node_modules/.bin/mocha \ --require should \ --reporter $(REPORTER) \ --timeout 5000 \ --growl \ $(TESTS) test-cov: lib-cov SUPERAGENT_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html lib-cov: jscoverage lib lib-cov superagent.js: components @component build \ --standalone superagent \ --out . --name superagent components: component install test-server: @node test/server docs: test-docs test-docs: make test REPORTER=doc \ | cat docs/head.html - docs/tail.html \ > docs/test.html clean: rm -fr superagent.js components .PHONY: test-cov test docs test-docs clean superagent-0.20.0/Readme.md000066400000000000000000000073671241334056700155070ustar00rootroot00000000000000# SuperAgent SuperAgent is a small progressive __client-side__ HTTP request library, and __Node.js__ module with the same API, sporting many high-level HTTP client features. View the [docs](http://visionmedia.github.com/superagent/). ![super agent](http://f.cl.ly/items/3d282n3A0h0Z0K2w0q2a/Screenshot.png) ## Installation node: ``` $ npm install superagent ``` component: ``` $ component install visionmedia/superagent ``` with script tags use ./superagent.js ## Motivation This library spawned from my frustration with jQuery's weak & inconsistent Ajax support. jQuery's API, while having recently added some promise-like support, is largely static, forcing you to build up big objects containing all the header fields and options, not to mention most of the options are awkwardly named "type" instead of "method", etc. Onto examples! The following is what you might typically do for a simple __GET__ with jQuery: ```js $.get('/user/1', function(data, textStatus, xhr){ }); ``` Great, it's ok, but it's kinda lame having 3 arguments just to access something on the `xhr`. Our equivalent would be: ```js request.get('/user/1', function(res){ }); ``` The response object is an instanceof `request.Response`, encapsulating all of this information instead of throwing a bunch of arguments at you. For example, we can check `res.status`, `res.header` for header fields, `res.text`, `res.body` etc. An example of a JSON POST with jQuery typically might use `$.post()`, however once you need to start defining header fields you have to then re-write it using `$.ajax()`... so that might look like: ```js $.ajax({ url: '/api/pet', type: 'POST', data: { name: 'Manny', species: 'cat' }, headers: { 'X-API-Key': 'foobar' } }).success(function(res){ }).error(function(){ }); ``` Not only is it ugly, but it's pretty opinionated. jQuery likes to special-case {4,5}xx- for example, you cannot (easily at least) receive a parsed JSON response for say "400 Bad Request". This same request would look like this: ```js request .post('/api/pet') .send({ name: 'Manny', species: 'cat' }) .set('X-API-Key', 'foobar') .set('Accept', 'application/json') .end(function(error, res){ }); ``` Building on the existing API internally, we also provide something similar to `$.post()` for those times in life where your interactions are very basic: ```js request.post('/api/pet', cat, function(error, res){ }); ``` # Plugins Usage: ```js var nocache = require('no-cache'); var request = require('superagent'); var prefix = require('superagent-prefix')('/static'); prefix(request); // Prefixes *all* requests request .get('/some-url') .use(nocache) // Prevents caching of *only* this request .end(function(res){ // Do something }); ``` Existing plugins: * [superagent-no-cache](https://github.com/johntron/superagent-no-cache) - prevents caching by including Cache-Control header * [superagent-prefix](https://github.com/johntron/superagent-prefix) - prefixes absolute URLs (useful in test environment) Please prefix your plugin component with `superagent-*` For superagent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki). ## Running node tests Install dependencies: $ npm install Run em! $ make test ## Running browser tests Install the test server deps (nodejs / express): $ npm install Start the test server: $ make test-server Visit `localhost:4000/` in the browser. ## Browser build The browser build of superagent is located in ./superagent.js ## Examples: - [agency tests](https://github.com/visionmedia/superagent/blob/master/test/node/agency.js) - [express demo app](https://github.com/hunterloftis/component-test/blob/master/lib/users/test/controller.test.js) ## License MIT superagent-0.20.0/component.json000066400000000000000000000005621241334056700166530ustar00rootroot00000000000000{ "name": "superagent", "repo": "visionmedia/superagent", "description": "awesome http requests", "version": "0.19.0", "keywords": [ "http", "ajax", "request", "agent" ], "scripts": [ "lib/client.js" ], "main": "lib/client.js", "dependencies": { "component/emitter": "*", "component/reduce": "*" }, "license": "MIT" } superagent-0.20.0/examples/000077500000000000000000000000001241334056700155715ustar00rootroot00000000000000superagent-0.20.0/examples/simple-get.js000066400000000000000000000004251241334056700201760ustar00rootroot00000000000000 /** * Module dependencies. */ var request = require('..'); var url = 'https://gist.github.com/visionmedia/9fff5b23c1bf1791c349/raw/3e588e0c4f762f15538cdaf9882df06b3f5b3db6/works.js'; request.get(url, function(err, res){ if (err) throw err; console.log(res.text); }); superagent-0.20.0/lib/000077500000000000000000000000001241334056700145215ustar00rootroot00000000000000superagent-0.20.0/lib/client.js000066400000000000000000000526721241334056700163510ustar00rootroot00000000000000/** * Module dependencies. */ var Emitter = require('emitter'); var reduce = require('reduce'); /** * Root reference for iframes. */ var root = 'undefined' == typeof window ? this : window; /** * Noop. */ function noop(){}; /** * Check if `obj` is a host object, * we don't want to serialize these :) * * TODO: future proof, move to compoent land * * @param {Object} obj * @return {Boolean} * @api private */ function isHost(obj) { var str = {}.toString.call(obj); switch (str) { case '[object File]': case '[object Blob]': case '[object FormData]': return true; default: return false; } } /** * Determine XHR. */ function getXHR() { if (root.XMLHttpRequest && ('file:' != root.location.protocol || !root.ActiveXObject)) { return new XMLHttpRequest; } else { try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} } return false; } /** * Removes leading and trailing whitespace, added to support IE. * * @param {String} s * @return {String} * @api private */ var trim = ''.trim ? function(s) { return s.trim(); } : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; /** * Check if `obj` is an object. * * @param {Object} obj * @return {Boolean} * @api private */ function isObject(obj) { return obj === Object(obj); } /** * Serialize the given `obj`. * * @param {Object} obj * @return {String} * @api private */ function serialize(obj) { if (!isObject(obj)) return obj; var pairs = []; for (var key in obj) { if (null != obj[key]) { pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])); } } return pairs.join('&'); } /** * Expose serialization method. */ request.serializeObject = serialize; /** * Parse the given x-www-form-urlencoded `str`. * * @param {String} str * @return {Object} * @api private */ function parseString(str) { var obj = {}; var pairs = str.split('&'); var parts; var pair; for (var i = 0, len = pairs.length; i < len; ++i) { pair = pairs[i]; parts = pair.split('='); obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } return obj; } /** * Expose parser. */ request.parseString = parseString; /** * Default MIME type map. * * superagent.types.xml = 'application/xml'; * */ request.types = { html: 'text/html', json: 'application/json', xml: 'application/xml', urlencoded: 'application/x-www-form-urlencoded', 'form': 'application/x-www-form-urlencoded', 'form-data': 'application/x-www-form-urlencoded' }; /** * Default serialization map. * * superagent.serialize['application/xml'] = function(obj){ * return 'generated xml here'; * }; * */ request.serialize = { 'application/x-www-form-urlencoded': serialize, 'application/json': JSON.stringify }; /** * Default parsers. * * superagent.parse['application/xml'] = function(str){ * return { object parsed from str }; * }; * */ request.parse = { 'application/x-www-form-urlencoded': parseString, 'application/json': JSON.parse }; /** * Parse the given header `str` into * an object containing the mapped fields. * * @param {String} str * @return {Object} * @api private */ function parseHeader(str) { var lines = str.split(/\r?\n/); var fields = {}; var index; var line; var field; var val; lines.pop(); // trailing CRLF for (var i = 0, len = lines.length; i < len; ++i) { line = lines[i]; index = line.indexOf(':'); field = line.slice(0, index).toLowerCase(); val = trim(line.slice(index + 1)); fields[field] = val; } return fields; } /** * Return the mime type for the given `str`. * * @param {String} str * @return {String} * @api private */ function type(str){ return str.split(/ *; */).shift(); }; /** * Return header field parameters. * * @param {String} str * @return {Object} * @api private */ function params(str){ return reduce(str.split(/ *; */), function(obj, str){ var parts = str.split(/ *= */) , key = parts.shift() , val = parts.shift(); if (key && val) obj[key] = val; return obj; }, {}); }; /** * Initialize a new `Response` with the given `xhr`. * * - set flags (.ok, .error, etc) * - parse header * * Examples: * * Aliasing `superagent` as `request` is nice: * * request = superagent; * * We can use the promise-like API, or pass callbacks: * * request.get('/').end(function(res){}); * request.get('/', function(res){}); * * Sending data can be chained: * * request * .post('/user') * .send({ name: 'tj' }) * .end(function(res){}); * * Or passed to `.send()`: * * request * .post('/user') * .send({ name: 'tj' }, function(res){}); * * Or passed to `.post()`: * * request * .post('/user', { name: 'tj' }) * .end(function(res){}); * * Or further reduced to a single call for simple cases: * * request * .post('/user', { name: 'tj' }, function(res){}); * * @param {XMLHTTPRequest} xhr * @param {Object} options * @api private */ function Response(req, options) { options = options || {}; this.req = req; this.xhr = this.req.xhr; this.text = this.xhr.responseText; this.setStatusProperties(this.xhr.status); this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but // getResponseHeader still works. so we get content-type even if getting // other headers fails. this.header['content-type'] = this.xhr.getResponseHeader('content-type'); this.setHeaderProperties(this.header); this.body = this.req.method != 'HEAD' ? this.parseBody(this.text) : null; } /** * Get case-insensitive `field` value. * * @param {String} field * @return {String} * @api public */ Response.prototype.get = function(field){ return this.header[field.toLowerCase()]; }; /** * Set header related properties: * * - `.type` the content type without params * * A response of "Content-Type: text/plain; charset=utf-8" * will provide you with a `.type` of "text/plain". * * @param {Object} header * @api private */ Response.prototype.setHeaderProperties = function(header){ // content-type var ct = this.header['content-type'] || ''; this.type = type(ct); // params var obj = params(ct); for (var key in obj) this[key] = obj[key]; }; /** * Parse the given body `str`. * * Used for auto-parsing of bodies. Parsers * are defined on the `superagent.parse` object. * * @param {String} str * @return {Mixed} * @api private */ Response.prototype.parseBody = function(str){ var parse = request.parse[this.type]; return parse && str && str.length ? parse(str) : null; }; /** * Set flags such as `.ok` based on `status`. * * For example a 2xx response will give you a `.ok` of __true__ * whereas 5xx will be __false__ and `.error` will be __true__. The * `.clientError` and `.serverError` are also available to be more * specific, and `.statusType` is the class of error ranging from 1..5 * sometimes useful for mapping respond colors etc. * * "sugar" properties are also defined for common cases. Currently providing: * * - .noContent * - .badRequest * - .unauthorized * - .notAcceptable * - .notFound * * @param {Number} status * @api private */ Response.prototype.setStatusProperties = function(status){ var type = status / 100 | 0; // status / class this.status = status; this.statusType = type; // basics this.info = 1 == type; this.ok = 2 == type; this.clientError = 4 == type; this.serverError = 5 == type; this.error = (4 == type || 5 == type) ? this.toError() : false; // sugar this.accepted = 202 == status; this.noContent = 204 == status || 1223 == status; this.badRequest = 400 == status; this.unauthorized = 401 == status; this.notAcceptable = 406 == status; this.notFound = 404 == status; this.forbidden = 403 == status; }; /** * Return an `Error` representative of this response. * * @return {Error} * @api public */ Response.prototype.toError = function(){ var req = this.req; var method = req.method; var url = req.url; var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; var err = new Error(msg); err.status = this.status; err.method = method; err.url = url; return err; }; /** * Expose `Response`. */ request.Response = Response; /** * Initialize a new `Request` with the given `method` and `url`. * * @param {String} method * @param {String} url * @api public */ function Request(method, url) { var self = this; Emitter.call(this); this._query = this._query || []; this.method = method; this.url = url; this.header = {}; this._header = {}; this.on('end', function(){ try { var res = new Response(self); if ('HEAD' == method) res.text = null; self.callback(null, res); } catch(e) { var err = new Error('Parser is unable to parse the response'); err.parse = true; err.original = e; self.callback(err); } }); } /** * Mixin `Emitter`. */ Emitter(Request.prototype); /** * Allow for extension */ Request.prototype.use = function(fn) { fn(this); return this; } /** * Set timeout to `ms`. * * @param {Number} ms * @return {Request} for chaining * @api public */ Request.prototype.timeout = function(ms){ this._timeout = ms; return this; }; /** * Clear previous timeout. * * @return {Request} for chaining * @api public */ Request.prototype.clearTimeout = function(){ this._timeout = 0; clearTimeout(this._timer); return this; }; /** * Abort the request, and clear potential timeout. * * @return {Request} * @api public */ Request.prototype.abort = function(){ if (this.aborted) return; this.aborted = true; this.xhr.abort(); this.clearTimeout(); this.emit('abort'); return this; }; /** * Set header `field` to `val`, or multiple fields with one object. * * Examples: * * req.get('/') * .set('Accept', 'application/json') * .set('X-API-Key', 'foobar') * .end(callback); * * req.get('/') * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) * .end(callback); * * @param {String|Object} field * @param {String} val * @return {Request} for chaining * @api public */ Request.prototype.set = function(field, val){ if (isObject(field)) { for (var key in field) { this.set(key, field[key]); } return this; } this._header[field.toLowerCase()] = val; this.header[field] = val; return this; }; /** * Remove header `field`. * * Example: * * req.get('/') * .unset('User-Agent') * .end(callback); * * @param {String} field * @return {Request} for chaining * @api public */ Request.prototype.unset = function(field){ delete this._header[field.toLowerCase()]; delete this.header[field]; return this; }; /** * Get case-insensitive header `field` value. * * @param {String} field * @return {String} * @api private */ Request.prototype.getHeader = function(field){ return this._header[field.toLowerCase()]; }; /** * Set Content-Type to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.xml = 'application/xml'; * * request.post('/') * .type('xml') * .send(xmlstring) * .end(callback); * * request.post('/') * .type('application/xml') * .send(xmlstring) * .end(callback); * * @param {String} type * @return {Request} for chaining * @api public */ Request.prototype.type = function(type){ this.set('Content-Type', request.types[type] || type); return this; }; /** * Set Accept to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.json = 'application/json'; * * request.get('/agent') * .accept('json') * .end(callback); * * request.get('/agent') * .accept('application/json') * .end(callback); * * @param {String} accept * @return {Request} for chaining * @api public */ Request.prototype.accept = function(type){ this.set('Accept', request.types[type] || type); return this; }; /** * Set Authorization field value with `user` and `pass`. * * @param {String} user * @param {String} pass * @return {Request} for chaining * @api public */ Request.prototype.auth = function(user, pass){ var str = btoa(user + ':' + pass); this.set('Authorization', 'Basic ' + str); return this; }; /** * Add query-string `val`. * * Examples: * * request.get('/shoes') * .query('size=10') * .query({ color: 'blue' }) * * @param {Object|String} val * @return {Request} for chaining * @api public */ Request.prototype.query = function(val){ if ('string' != typeof val) val = serialize(val); if (val) this._query.push(val); return this; }; /** * Write the field `name` and `val` for "multipart/form-data" * request bodies. * * ``` js * request.post('/upload') * .field('foo', 'bar') * .end(callback); * ``` * * @param {String} name * @param {String|Blob|File} val * @return {Request} for chaining * @api public */ Request.prototype.field = function(name, val){ if (!this._formData) this._formData = new FormData(); this._formData.append(name, val); return this; }; /** * Queue the given `file` as an attachment to the specified `field`, * with optional `filename`. * * ``` js * request.post('/upload') * .attach(new Blob(['hey!'], { type: "text/html"})) * .end(callback); * ``` * * @param {String} field * @param {Blob|File} file * @param {String} filename * @return {Request} for chaining * @api public */ Request.prototype.attach = function(field, file, filename){ if (!this._formData) this._formData = new FormData(); this._formData.append(field, file, filename); return this; }; /** * Send `data`, defaulting the `.type()` to "json" when * an object is given. * * Examples: * * // querystring * request.get('/search') * .end(callback) * * // multiple data "writes" * request.get('/search') * .send({ search: 'query' }) * .send({ range: '1..5' }) * .send({ order: 'desc' }) * .end(callback) * * // manual json * request.post('/user') * .type('json') * .send('{"name":"tj"}) * .end(callback) * * // auto json * request.post('/user') * .send({ name: 'tj' }) * .end(callback) * * // manual x-www-form-urlencoded * request.post('/user') * .type('form') * .send('name=tj') * .end(callback) * * // auto x-www-form-urlencoded * request.post('/user') * .type('form') * .send({ name: 'tj' }) * .end(callback) * * // defaults to x-www-form-urlencoded * request.post('/user') * .send('name=tobi') * .send('species=ferret') * .end(callback) * * @param {String|Object} data * @return {Request} for chaining * @api public */ Request.prototype.send = function(data){ var obj = isObject(data); var type = this.getHeader('Content-Type'); // merge if (obj && isObject(this._data)) { for (var key in data) { this._data[key] = data[key]; } } else if ('string' == typeof data) { if (!type) this.type('form'); type = this.getHeader('Content-Type'); if ('application/x-www-form-urlencoded' == type) { this._data = this._data ? this._data + '&' + data : data; } else { this._data = (this._data || '') + data; } } else { this._data = data; } if (!obj) return this; if (!type) this.type('json'); return this; }; /** * Invoke the callback with `err` and `res` * and handle arity check. * * @param {Error} err * @param {Response} res * @api private */ Request.prototype.callback = function(err, res){ var fn = this._callback; if (2 == fn.length) return fn(err, res); if (err) return this.emit('error', err); fn(res); }; /** * Invoke callback with x-domain error. * * @api private */ Request.prototype.crossDomainError = function(){ var err = new Error('Origin is not allowed by Access-Control-Allow-Origin'); err.crossDomain = true; this.callback(err); }; /** * Invoke callback with timeout error. * * @api private */ Request.prototype.timeoutError = function(){ var timeout = this._timeout; var err = new Error('timeout of ' + timeout + 'ms exceeded'); err.timeout = timeout; this.callback(err); }; /** * Enable transmission of cookies with x-domain requests. * * Note that for this to work the origin must not be * using "Access-Control-Allow-Origin" with a wildcard, * and also must set "Access-Control-Allow-Credentials" * to "true". * * @api public */ Request.prototype.withCredentials = function(){ this._withCredentials = true; return this; }; /** * Initiate request, invoking callback `fn(res)` * with an instanceof `Response`. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype.end = function(fn){ var self = this; var xhr = this.xhr = getXHR(); var query = this._query.join('&'); var timeout = this._timeout; var data = this._formData || this._data; // store callback this._callback = fn || noop; // state change xhr.onreadystatechange = function(){ if (4 != xhr.readyState) return; if (0 == xhr.status) { if (self.aborted) return self.timeoutError(); return self.crossDomainError(); } self.emit('end'); }; // progress if (xhr.upload) { xhr.upload.onprogress = function(e){ e.percent = e.loaded / e.total * 100; self.emit('progress', e); }; } // timeout if (timeout && !this._timer) { this._timer = setTimeout(function(){ self.abort(); }, timeout); } // querystring if (query) { query = request.serializeObject(query); this.url += ~this.url.indexOf('?') ? '&' + query : '?' + query; } // initiate request xhr.open(this.method, this.url, true); // CORS if (this._withCredentials) xhr.withCredentials = true; // body if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) { // serialize stuff var serialize = request.serialize[this.getHeader('Content-Type')]; if (serialize) data = serialize(data); } // set header fields for (var field in this.header) { if (null == this.header[field]) continue; xhr.setRequestHeader(field, this.header[field]); } // send stuff this.emit('request', this); xhr.send(data); return this; }; /** * Expose `Request`. */ request.Request = Request; /** * Issue a request: * * Examples: * * request('GET', '/users').end(callback) * request('/users').end(callback) * request('/users', callback) * * @param {String} method * @param {String|Function} url or callback * @return {Request} * @api public */ function request(method, url) { // callback if ('function' == typeof url) { return new Request('GET', method).end(url); } // url first if (1 == arguments.length) { return new Request('GET', method); } return new Request(method, url); } /** * GET `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.get = function(url, data, fn){ var req = request('GET', url); if ('function' == typeof data) fn = data, data = null; if (data) req.query(data); if (fn) req.end(fn); return req; }; /** * HEAD `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.head = function(url, data, fn){ var req = request('HEAD', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * DELETE `url` with optional callback `fn(res)`. * * @param {String} url * @param {Function} fn * @return {Request} * @api public */ request.del = function(url, fn){ var req = request('DELETE', url); if (fn) req.end(fn); return req; }; /** * PATCH `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} data * @param {Function} fn * @return {Request} * @api public */ request.patch = function(url, data, fn){ var req = request('PATCH', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * POST `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} data * @param {Function} fn * @return {Request} * @api public */ request.post = function(url, data, fn){ var req = request('POST', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * PUT `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.put = function(url, data, fn){ var req = request('PUT', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * Expose `request`. */ module.exports = request; superagent-0.20.0/lib/node/000077500000000000000000000000001241334056700154465ustar00rootroot00000000000000superagent-0.20.0/lib/node/agent.js000066400000000000000000000031371241334056700171060ustar00rootroot00000000000000 /** * Module dependencies. */ var CookieJar = require('cookiejar').CookieJar; var CookieAccess = require('cookiejar').CookieAccessInfo; var parse = require('url').parse; var request = require('./index'); var methods = require('methods'); /** * Expose `Agent`. */ module.exports = Agent; /** * Initialize a new `Agent`. * * @api public */ function Agent(options) { if (!(this instanceof Agent)) return new Agent(options); if (options) this._ca = options.ca; this.jar = new CookieJar; } /** * Save the cookies in the given `res` to * the agent's cookie jar for persistence. * * @param {Response} res * @api private */ Agent.prototype.saveCookies = function(res){ var cookies = res.headers['set-cookie']; if (cookies) this.jar.setCookies(cookies); }; /** * Attach cookies when available to the given `req`. * * @param {Request} req * @api private */ Agent.prototype.attachCookies = function(req){ var url = parse(req.url); var access = CookieAccess(url.hostname, url.pathname, 'https:' == url.protocol); var cookies = this.jar.getCookies(access).toValueString(); req.cookies = cookies; }; // generate HTTP verb methods methods.forEach(function(method){ var name = 'delete' == method ? 'del' : method; method = method.toUpperCase(); Agent.prototype[name] = function(url, fn){ var req = request(method, url); req.ca(this._ca); req.on('response', this.saveCookies.bind(this)); req.on('redirect', this.saveCookies.bind(this)); req.on('redirect', this.attachCookies.bind(this, req)); this.attachCookies(req); fn && req.end(fn); return req; }; }); superagent-0.20.0/lib/node/index.js000066400000000000000000000543451241334056700171260ustar00rootroot00000000000000 /** * Module dependencies. */ var debug = require('debug')('superagent'); var formidable = require('formidable'); var FormData = require('form-data'); var Response = require('./response'); var parse = require('url').parse; var format = require('url').format; var methods = require('methods'); var Stream = require('stream'); var utils = require('./utils'); var extend = require('extend'); var Part = require('./part'); var mime = require('mime'); var https = require('https'); var http = require('http'); var fs = require('fs'); var qs = require('qs'); var zlib = require('zlib'); var util = require('util'); var pkg = require('../../package.json'); /** * Expose the request function. */ exports = module.exports = request; /** * Expose the agent function */ exports.agent = require('./agent'); /** * Expose `Part`. */ exports.Part = Part; /** * Noop. */ function noop(){}; /** * Expose `Response`. */ exports.Response = Response; /** * Define "form" mime type. */ mime.define({ 'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data'] }); /** * Protocol map. */ exports.protocols = { 'http:': http, 'https:': https }; /** * Check if `obj` is an object. * * @param {Object} obj * @return {Boolean} * @api private */ function isObject(obj) { return null != obj && 'object' == typeof obj; } /** * Default serialization map. * * superagent.serialize['application/xml'] = function(obj){ * return 'generated xml here'; * }; * */ exports.serialize = { 'application/x-www-form-urlencoded': qs.stringify, 'application/json': JSON.stringify }; /** * Default parsers. * * superagent.parse['application/xml'] = function(res, fn){ * fn(null, result); * }; * */ exports.parse = require('./parsers'); /** * Initialize a new `Request` with the given `method` and `url`. * * @param {String} method * @param {String|Object} url * @api public */ function Request(method, url) { Stream.call(this); var self = this; if ('string' != typeof url) url = format(url); this._agent = false; this._formData = null; this.method = method; this.url = url; this.header = {}; this.writable = true; this._redirects = 0; this.redirects(5); this.cookies = ''; this.qs = {}; this._redirectList = []; this.on('end', this.clearTimeout.bind(this)); this.on('response', function(res){ self.callback(null, res); }); } /** * Inherit from `Stream`. */ util.inherits(Request, Stream); /** * Write the field `name` and `val` for "multipart/form-data" * request bodies. * * ``` js * request.post('http://localhost/upload') * .field('foo', 'bar') * .end(callback); * ``` * * @param {String} name * @param {String|Buffer|fs.ReadStream} val * @return {Request} for chaining * @api public */ Request.prototype.field = function(name, val){ debug('field', name, val); if (!this._formData) this._formData = new FormData(); this._formData.append(name, val); return this; }; /** * Queue the given `file` as an attachment to the specified `field`, * with optional `filename`. * * ``` js * request.post('http://localhost/upload') * .attach(new Buffer('Hello world'), 'hello.html') * .end(callback); * ``` * * A filename may also be used: * * ``` js * request.post('http://localhost/upload') * .attach('files', 'image.jpg') * .end(callback); * ``` * * @param {String} field * @param {String|fs.ReadStream|Buffer} file * @param {String} filename * @return {Request} for chaining * @api public */ Request.prototype.attach = function(field, file, filename){ if (!this._formData) this._formData = new FormData(); if ('string' == typeof file) { filename = file; debug('creating `fs.ReadStream` instance for file: %s', filename); file = fs.createReadStream(filename); } this._formData.append(field, file, filename); return this; }; /** * Set the max redirects to `n`. * * @param {Number} n * @return {Request} for chaining * @api public */ Request.prototype.redirects = function(n){ debug('max redirects %s', n); this._maxRedirects = n; return this; }; /** * Return a new `Part` for this request. * * @return {Part} * @api public * @deprecated pass a readable stream in to `Request#attach()` instead */ Request.prototype.part = util.deprecate(function(){ return new Part(this); }, '`Request#part()` is deprecated. ' + 'Pass a readable stream in to `Request#attach()` instead.'); /** * Gets/sets the `Agent` to use for this HTTP request. The default (if this * function is not called) is to opt out of connection pooling (`agent: false`). * * @param {http.Agent} agent * @return {http.Agent} * @api public */ Request.prototype.agent = function(agent){ if (!arguments.length) return this._agent; this._agent = agent; return this; }; /** * Set header `field` to `val`, or multiple fields with one object. * * Examples: * * req.get('/') * .set('Accept', 'application/json') * .set('X-API-Key', 'foobar') * .end(callback); * * req.get('/') * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) * .end(callback); * * @param {String|Object} field * @param {String} val * @return {Request} for chaining * @api public */ Request.prototype.set = function(field, val){ if (isObject(field)) { for (var key in field) { this.set(key, field[key]); } return this; } debug('set %s "%s"', field, val); this.request().setHeader(field, val); return this; }; /** * Remove header `field`. * * Example: * * req.get('/') * .unset('User-Agent') * .end(callback); * * @param {String} field * @return {Request} for chaining * @api public */ Request.prototype.unset = function(field){ debug('unset %s', field); this.request().removeHeader(field); return this; }; /** * Get request header `field`. * * @param {String} field * @return {String} * @api public */ Request.prototype.get = function(field){ return this.request().getHeader(field); }; /** * Set _Content-Type_ response header passed through `mime.lookup()`. * * Examples: * * request.post('/') * .type('xml') * .send(xmlstring) * .end(callback); * * request.post('/') * .type('json') * .send(jsonstring) * .end(callback); * * request.post('/') * .type('application/json') * .send(jsonstring) * .end(callback); * * @param {String} type * @return {Request} for chaining * @api public */ Request.prototype.type = function(type){ return this.set('Content-Type', ~type.indexOf('/') ? type : mime.lookup(type)); }; /** * Set _Accept_ response header passed through `mime.lookup()`. * * Examples: * * superagent.types.json = 'application/json'; * * request.get('/agent') * .accept('json') * .end(callback); * * request.get('/agent') * .accept('application/json') * .end(callback); * * @param {String} accept * @return {Request} for chaining * @api public */ Request.prototype.accept = function(type){ return this.set('Accept', ~type.indexOf('/') ? type : mime.lookup(type)); }; /** * Add query-string `val`. * * Examples: * * request.get('/shoes') * .query('size=10') * .query({ color: 'blue' }) * * @param {Object|String} val * @return {Request} for chaining * @api public */ Request.prototype.query = function(val){ var obj = {}; if ('string' == typeof val) { var elements = val.split('&'); for (var i = 0; i < elements.length; i++) { var parts = elements[i].split('='); obj[parts[0]] = parts[1]; } return this.query(obj); } extend(this.qs, val); return this; }; /** * Send `data`, defaulting the `.type()` to "json" when * an object is given. * * Examples: * * // manual json * request.post('/user') * .type('json') * .send('{"name":"tj"}') * .end(callback) * * // auto json * request.post('/user') * .send({ name: 'tj' }) * .end(callback) * * // manual x-www-form-urlencoded * request.post('/user') * .type('form') * .send('name=tj') * .end(callback) * * // auto x-www-form-urlencoded * request.post('/user') * .type('form') * .send({ name: 'tj' }) * .end(callback) * * // string defaults to x-www-form-urlencoded * request.post('/user') * .send('name=tj') * .send('foo=bar') * .send('bar=baz') * .end(callback) * * @param {String|Object} data * @return {Request} for chaining * @api public */ Request.prototype.send = function(data){ var obj = isObject(data); var req = this.request(); var type = req.getHeader('Content-Type'); // merge if (obj && isObject(this._data)) { for (var key in data) { this._data[key] = data[key]; } // string } else if ('string' == typeof data) { // default to x-www-form-urlencoded if (!type) this.type('form'); type = req.getHeader('Content-Type'); // concat & if ('application/x-www-form-urlencoded' == type) { this._data = this._data ? this._data + '&' + data : data; } else { this._data = (this._data || '') + data; } } else { this._data = data; } if (!obj) return this; // default to json if (!type) this.type('json'); return this; }; /** * Write raw `data` / `encoding` to the socket. * * @param {Buffer|String} data * @param {String} encoding * @return {Boolean} * @api public */ Request.prototype.write = function(data, encoding){ return this.request().write(data, encoding); }; /** * Pipe the request body to `stream`. * * @param {Stream} stream * @param {Object} options * @return {Stream} * @api public */ Request.prototype.pipe = function(stream, options){ this.piped = true; // HACK... this.buffer(false); this.end().req.on('response', function(res){ if (/^(deflate|gzip)$/.test(res.headers['content-encoding'])) { res.pipe(zlib.createUnzip()).pipe(stream, options); } else { res.pipe(stream, options); } }); return stream; }; /** * Enable / disable buffering. * * @return {Boolean} [val] * @return {Request} for chaining * @api public */ Request.prototype.buffer = function(val){ this._buffer = false === val ? false : true; return this; }; /** * Set timeout to `ms`. * * @param {Number} ms * @return {Request} for chaining * @api public */ Request.prototype.timeout = function(ms){ this._timeout = ms; return this; }; /** * Clear previous timeout. * * @return {Request} for chaining * @api public */ Request.prototype.clearTimeout = function(){ debug('clear timeout %s %s', this.method, this.url); this._timeout = 0; clearTimeout(this._timer); return this; }; /** * Abort and clear timeout. * * @api public */ Request.prototype.abort = function(){ debug('abort %s %s', this.method, this.url); this._aborted = true; this.clearTimeout(); this.req.abort(); this.emit('abort'); }; /** * Define the parser to be used for this response. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype.parse = function(fn){ this._parser = fn; return this; }; /** * Redirect to `url * * @param {IncomingMessage} res * @return {Request} for chaining * @api private */ Request.prototype.redirect = function(res){ var url = res.headers.location; debug('redirect %s -> %s', this.url, url); // location if (!~url.indexOf('://')) { if (0 != url.indexOf('//')) { url = '//' + this.host + url; } url = this.protocol + url; } // ensure the response is being consumed // this is required for Node v0.10+ res.resume(); // strip Content-* related fields // in case of POST etc var header = utils.cleanHeader(this.req._headers); delete this.req; // force GET this.method = 'HEAD' == this.method ? 'HEAD' : 'GET'; // redirect this._data = null; this.url = url; this._redirectList.push(url); this.clearTimeout(); this.emit('redirect', res); this.set(header); this.end(this._callback); return this; }; /** * Set Authorization field value with `user` and `pass`. * * Examples: * * .auth('tobi', 'learnboost') * .auth('tobi:learnboost') * .auth('tobi') * * @param {String} user * @param {String} pass * @return {Request} for chaining * @api public */ Request.prototype.auth = function(user, pass){ if (1 === arguments.length) pass = ''; if (!~user.indexOf(':')) user = user + ':'; var str = new Buffer(user + pass).toString('base64'); return this.set('Authorization', 'Basic ' + str); }; /** * Set the certificate authority option for https request. * * @param {Buffer | Array} cert * @return {Request} for chaining * @api public */ Request.prototype.ca = function(cert){ this._ca = cert; return this; }; /** * Allow for extension */ Request.prototype.use = function(fn) { fn(this); return this; }; /** * Return an http[s] request. * * @return {OutgoingMessage} * @api private */ Request.prototype.request = function(){ if (this.req) return this.req; var self = this; var options = {}; var data = this._data; var url = this.url; // default to http:// if (0 != url.indexOf('http')) url = 'http://' + url; url = parse(url, true); // options options.method = this.method; options.port = url.port; options.path = url.pathname; options.host = url.hostname; options.ca = this._ca; options.agent = this._agent; // initiate request var mod = exports.protocols[url.protocol]; // request var req = this.req = mod.request(options); if ('HEAD' != options.method) req.setHeader('Accept-Encoding', 'gzip, deflate'); this.protocol = url.protocol; this.host = url.host; // expose events req.on('drain', function(){ self.emit('drain'); }); req.on('error', function(err){ // flag abortion here for out timeouts // because node will emit a faux-error "socket hang up" // when request is aborted before a connection is made if (self._aborted) return; self.callback(err); }); // auth if (url.auth) { var auth = url.auth.split(':'); this.auth(auth[0], auth[1]); } // query this.query(url.query); // add cookies if (this.cookies) req.setHeader('Cookie', this.cookies); // set default UA req.setHeader('User-Agent', 'node-superagent/' + pkg.version); return req; }; /** * Invoke the callback with `err` and `res` * and handle arity check. * * @param {Error} err * @param {Response} res * @api private */ Request.prototype.callback = function(err, res){ var fn = this._callback; this.clearTimeout(); if (this.called) return console.warn('double callback!'); this.called = true; if (2 == fn.length) return fn(err, res); if (err) return this.emit('error', err); fn(res); }; /** * Initiate request, invoking callback `fn(err, res)` * with an instanceof `Response`. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype.end = function(fn){ var self = this; var data = this._data; var req = this.request(); var buffer = this._buffer; var method = this.method; var timeout = this._timeout; debug('%s %s', this.method, this.url); // store callback this._callback = fn || noop; // querystring try { var querystring = qs.stringify(this.qs); req.path += querystring.length ? (~req.path.indexOf('?') ? '&' : '?') + querystring : ''; } catch (e) { return this.callback(e); } // timeout if (timeout && !this._timer) { debug('timeout %sms %s %s', timeout, this.method, this.url); this._timer = setTimeout(function(){ var err = new Error('timeout of ' + timeout + 'ms exceeded'); err.timeout = timeout; self.abort(); self.callback(err); }, timeout); } // body if ('HEAD' != method && !req._headerSent) { // serialize stuff if ('string' != typeof data) { var contentType = req.getHeader('Content-Type') // Parse out just the content type from the header (ignore the charset) if (contentType) contentType = contentType.split(';')[0] var serialize = exports.serialize[contentType]; if (serialize) data = serialize(data); } // content-length if (data && !req.getHeader('Content-Length')) { this.set('Content-Length', Buffer.byteLength(data)); } } // response req.on('response', function(res){ debug('%s %s -> %s', self.method, self.url, res.statusCode); var max = self._maxRedirects; var mime = utils.type(res.headers['content-type'] || ''); var len = res.headers['content-length']; var type = mime.split('/'); var subtype = type[1]; var type = type[0]; var multipart = 'multipart' == type; var redirect = isRedirect(res.statusCode); var parser = self._parser; self.res = res; if ('HEAD' == self.method) { var response = new Response(self); self.response = response; response.redirects = self._redirectList; self.emit('response', response); self.emit('end'); return; } if (self.piped) { res.on('end', function(){ self.emit('end'); }); return; } // redirect if (redirect && self._redirects++ != max) { return self.redirect(res); } // zlib support if (/^(deflate|gzip)$/.test(res.headers['content-encoding'])) { utils.unzip(req, res); } // don't buffer multipart if (multipart) buffer = false; // TODO: make all parsers take callbacks if (!parser && multipart) { var form = new formidable.IncomingForm; form.parse(res, function(err, fields, files){ if (err) return self.callback(err); var response = new Response(self); self.response = response; response.body = fields; response.files = files; response.redirects = self._redirectList; self.emit('end'); self.callback(null, response); }); return; } // check for images, one more special treatment if (!parser && isImage(mime)) { exports.parse.image(res, function(err, obj){ if (err) return self.callback(err); var response = new Response(self); self.response = response; response.body = obj; response.redirects = self._redirectList; self.emit('end'); self.callback(null, response); }); return; } // by default only buffer text/*, json // and messed up thing from hell var text = isText(mime); if (null == buffer && text) buffer = true; // parser var parse = 'text' == type ? exports.parse.text : exports.parse[mime]; // buffered response if (buffer) parse = parse || exports.parse.text; // explicit parser if (parser) parse = parser; // parse if (parse) { try { parse(res, function(err, obj){ if (err) self.callback(err); res.body = obj; }); } catch(err) { self.callback(err); return; } } // unbuffered if (!buffer) { debug('unbuffered %s %s', self.method, self.url); self.res = res; var response = new Response(self); self.response = response; response.redirects = self._redirectList; self.emit('response', response); if (multipart) return // allow multipart to handle end event res.on('end', function(){ debug('end %s %s', self.method, self.url); self.emit('end'); }) return; } // end event self.res = res; res.on('end', function(){ debug('end %s %s', self.method, self.url); // TODO: unless buffering emit earlier to stream var response = new Response(self); self.response = response; response.redirects = self._redirectList; self.emit('response', response); self.emit('end'); }); }); this.emit('request', this); // if a FormData instance got created, then we send that as the request body var formData = this._formData; if (formData) { // set headers var headers = formData.getHeaders(); for (var i in headers) { debug('setting FormData header: "%s: %s"', i, headers[i]); req.setHeader(i, headers[i]); } // attempt to get "Content-Length" header formData.getLength(function(err, length) { // TODO: Add chunked encoding when no length (if err) debug('got FormData Content-Length: %s', length); if ('number' == typeof length) { req.setHeader('Content-Length', length); } formData.pipe(req); }); } else { req.end(data); } return this; }; /** * To json. * * @return {Object} * @api public */ Request.prototype.toJSON = function(){ return { method: this.method, url: this.url, data: this._data }; }; /** * Expose `Request`. */ exports.Request = Request; /** * Issue a request: * * Examples: * * request('GET', '/users').end(callback) * request('/users').end(callback) * request('/users', callback) * * @param {String} method * @param {String|Function} url or callback * @return {Request} * @api public */ function request(method, url) { // callback if ('function' == typeof url) { return new Request('GET', method).end(url); } // url first if (1 == arguments.length) { return new Request('GET', method); } return new Request(method, url); } // generate HTTP verb methods methods.forEach(function(method){ var name = 'delete' == method ? 'del' : method; method = method.toUpperCase(); request[name] = function(url, fn){ var req = request(method, url); fn && req.end(fn); return req; }; }); /** * Check if `mime` is text and should be buffered. * * @param {String} mime * @return {Boolean} * @api public */ function isText(mime) { var parts = mime.split('/'); var type = parts[0]; var subtype = parts[1]; return 'text' == type || 'json' == subtype || 'x-www-form-urlencoded' == subtype; } /** * Check if `mime` is image * * @param {String} mime * @return {Boolean} * @api public */ function isImage(mime) { var parts = mime.split('/'); var type = parts[0]; var subtype = parts[1]; return 'image' == type; } /** * Check if we should follow the redirect `code`. * * @param {Number} code * @return {Boolean} * @api private */ function isRedirect(code) { return ~[301, 302, 303, 305, 307].indexOf(code); } superagent-0.20.0/lib/node/parsers/000077500000000000000000000000001241334056700171255ustar00rootroot00000000000000superagent-0.20.0/lib/node/parsers/image.js000066400000000000000000000003451241334056700205470ustar00rootroot00000000000000module.exports = function(res, fn){ var data = []; // Binary data needs binary storage res.on('data', function(chunk){ data.push(chunk); }); res.on('end', function () { fn(null, Buffer.concat(data)); }); };superagent-0.20.0/lib/node/parsers/index.js000066400000000000000000000003001241334056700205630ustar00rootroot00000000000000 exports['application/x-www-form-urlencoded'] = require('./urlencoded'); exports['application/json'] = require('./json'); exports.text = require('./text'); exports.image = require('./image'); superagent-0.20.0/lib/node/parsers/json.js000066400000000000000000000004071241334056700204350ustar00rootroot00000000000000 module.exports = function(res, fn){ res.text = ''; res.setEncoding('utf8'); res.on('data', function(chunk){ res.text += chunk; }); res.on('end', function(){ try { fn(null, JSON.parse(res.text)); } catch (err) { fn(err); } }); };superagent-0.20.0/lib/node/parsers/text.js000066400000000000000000000002411241334056700204440ustar00rootroot00000000000000 module.exports = function(res, fn){ res.text = ''; res.setEncoding('utf8'); res.on('data', function(chunk){ res.text += chunk; }); res.on('end', fn); };superagent-0.20.0/lib/node/parsers/urlencoded.js000066400000000000000000000005001241334056700216020ustar00rootroot00000000000000 /** * Module dependencies. */ var qs = require('qs'); module.exports = function(res, fn){ res.text = ''; res.setEncoding('ascii'); res.on('data', function(chunk){ res.text += chunk; }); res.on('end', function(){ try { fn(null, qs.parse(res.text)); } catch (err) { fn(err); } }); };superagent-0.20.0/lib/node/part.js000066400000000000000000000061111241334056700167510ustar00rootroot00000000000000 /** * Module dependencies. */ var util = require('util'); var mime = require('mime'); var FormData = require('form-data'); var PassThrough = require('readable-stream/passthrough'); /** * Initialize a new `Part` for the given `req`. * * @param {Request} req * @api public * @deprecated pass a readable stream in to `Request#attach()` instead */ var Part = function (req) { PassThrough.call(this); this._req = req; this._attached = false; this._name = null; this._type = null; this._header = null; this._filename = null; this.once('pipe', this._attach.bind(this)); }; Part = util.deprecate(Part, 'The `Part()` constructor is deprecated. ' + 'Pass a readable stream in to `Request#attach()` instead.'); /** * Inherit from `PassThrough`. */ util.inherits(Part, PassThrough); /** * Expose `Part`. */ module.exports = Part; /** * Set header `field` to `val`. * * @param {String} field * @param {String} val * @return {Part} for chaining * @api public */ Part.prototype.set = function(field, val){ //if (!this._header) this._header = {}; //this._header[field] = val; //return this; throw new TypeError('setting custom form-data part headers is unsupported'); }; /** * Set _Content-Type_ response header passed through `mime.lookup()`. * * Examples: * * res.type('html'); * res.type('.html'); * * @param {String} type * @return {Part} for chaining * @api public */ Part.prototype.type = function(type){ var lookup = mime.lookup(type); this._type = lookup; //this.set('Content-Type', lookup); return this; }; /** * Set the "name" portion for the _Content-Disposition_ header field. * * @param {String} name * @return {Part} for chaining * @api public */ Part.prototype.name = function(name){ this._name = name; return this; }; /** * Set _Content-Disposition_ header field to _attachment_ with `filename` * and field `name`. * * @param {String} name * @param {String} filename * @return {Part} for chaining * @api public */ Part.prototype.attachment = function(name, filename){ this.name(name); if (filename) { this.type(filename); this._filename = filename; } return this; }; /** * Calls `FormData#append()` on the Request instance's FormData object. * * Gets called implicitly upon the first `write()` call, or the "pipe" event. * * @api private */ Part.prototype._attach = function(){ if (this._attached) return; this._attached = true; if (!this._name) throw new Error('must call `Part#name()` first!'); // add `this` Stream's readable side as a stream for this Part if (!this._req._formData) this._req._formData = new FormData(); this._req._formData.append(this._name, this, { contentType: this._type, filename: this._filename }); // restore PassThrough's default `write()` function now that we're setup this.write = PassThrough.prototype.write; }; /** * Write `data` with `encoding`. * * @param {Buffer|String} data * @param {String} encoding * @return {Boolean} * @api public */ Part.prototype.write = function(){ this._attach(); return this.write.apply(this, arguments); }; superagent-0.20.0/lib/node/response.js000066400000000000000000000105651241334056700176510ustar00rootroot00000000000000 /** * Module dependencies. */ var util = require('util'); var utils = require('./utils'); var Stream = require('stream'); /** * Expose `Response`. */ module.exports = Response; /** * Initialize a new `Response` with the given `xhr`. * * - set flags (.ok, .error, etc) * - parse header * * @param {Request} req * @param {Object} options * @constructor * @extends {Stream} * @implements {ReadableStream} * @api private */ function Response(req, options) { Stream.call(this); options = options || {}; var res = this.res = req.res; this.request = req; this.req = req.req; this.links = {}; this.text = res.text; this.body = res.body || {}; this.files = res.files || {}; this.buffered = 'string' == typeof this.text; this.header = this.headers = res.headers; this.setStatusProperties(res.statusCode); this.setHeaderProperties(this.header); this.setEncoding = res.setEncoding.bind(res); res.on('data', this.emit.bind(this, 'data')); res.on('end', this.emit.bind(this, 'end')); res.on('close', this.emit.bind(this, 'close')); res.on('error', this.emit.bind(this, 'error')); } /** * Inherit from `Stream`. */ util.inherits(Response, Stream); /** * Get case-insensitive `field` value. * * @param {String} field * @return {String} * @api public */ Response.prototype.get = function(field){ return this.header[field.toLowerCase()]; }; /** * Implements methods of a `ReadableStream` */ Response.prototype.destroy = function(err){ this.res.destroy(err); }; /** * Pause. */ Response.prototype.pause = function(){ this.res.pause(); }; /** * Resume. */ Response.prototype.resume = function(){ this.res.resume(); }; /** * Return an `Error` representative of this response. * * @return {Error} * @api public */ Response.prototype.toError = function(){ var req = this.req; var method = req.method; var path = req.path; var msg = 'cannot ' + method + ' ' + path + ' (' + this.status + ')'; var err = new Error(msg); err.status = this.status; err.text = this.text; err.method = method; err.path = path; return err; }; /** * Set header related properties: * * - `.type` the content type without params * * A response of "Content-Type: text/plain; charset=utf-8" * will provide you with a `.type` of "text/plain". * * @param {Object} header * @api private */ Response.prototype.setHeaderProperties = function(header){ // TODO: moar! // TODO: make this a util // content-type var ct = this.header['content-type'] || ''; // params var params = utils.params(ct); for (var key in params) this[key] = params[key]; this.type = utils.type(ct); // links try { if (header.link) this.links = utils.parseLinks(header.link); } catch (err) { // ignore } }; /** * Parse cookies from the header into an array. */ function parseCookies(header) { return Array.isArray(header) ? header.map(Cookie.parse) : [Cookie.parse(header)]; } /** * Set flags such as `.ok` based on `status`. * * For example a 2xx response will give you a `.ok` of __true__ * whereas 5xx will be __false__ and `.error` will be __true__. The * `.clientError` and `.serverError` are also available to be more * specific, and `.statusType` is the class of error ranging from 1..5 * sometimes useful for mapping respond colors etc. * * "sugar" properties are also defined for common cases. Currently providing: * * - .noContent * - .badRequest * - .unauthorized * - .notAcceptable * - .notFound * * @param {Number} status * @api private */ Response.prototype.setStatusProperties = function(status){ var type = status / 100 | 0; // status / class this.status = this.statusCode = status; this.statusType = type; // basics this.info = 1 == type; this.ok = 2 == type; this.redirect = 3 == type; this.clientError = 4 == type; this.serverError = 5 == type; this.error = (4 == type || 5 == type) ? this.toError() : false; // sugar this.accepted = 202 == status; this.noContent = 204 == status; this.badRequest = 400 == status; this.unauthorized = 401 == status; this.notAcceptable = 406 == status; this.forbidden = 403 == status; this.notFound = 404 == status; }; /** * To json. * * @return {Object} * @api public */ Response.prototype.toJSON = function(){ return { req: this.request.toJSON(), header: this.header, status: this.status, text: this.text }; }; superagent-0.20.0/lib/node/utils.js000066400000000000000000000057011241334056700171470ustar00rootroot00000000000000 /** * Module dependencies. */ var StringDecoder = require('string_decoder').StringDecoder; var Stream = require('stream'); var zlib; /** * Require zlib module for Node 0.6+ */ try { zlib = require('zlib'); } catch (e) { } /** * Generate a UID with the given `len`. * * @param {Number} len * @return {String} * @api private */ exports.uid = function(len){ var buf = ''; var chars = 'abcdefghijklmnopqrstuvwxyz123456789'; var nchars = chars.length; while (len--) buf += chars[Math.random() * nchars | 0]; return buf; }; /** * Return the mime type for the given `str`. * * @param {String} str * @return {String} * @api private */ exports.type = function(str){ return str.split(/ *; */).shift(); }; /** * Return header field parameters. * * @param {String} str * @return {Object} * @api private */ exports.params = function(str){ return str.split(/ *; */).reduce(function(obj, str){ var parts = str.split(/ *= */); var key = parts.shift(); var val = parts.shift(); if (key && val) obj[key] = val; return obj; }, {}); }; /** * Parse Link header fields. * * @param {String} str * @return {Object} * @api private */ exports.parseLinks = function(str){ return str.split(/ *, */).reduce(function(obj, str){ var parts = str.split(/ *; */); var url = parts[0].slice(1, -1); var rel = parts[1].split(/ *= */)[1].slice(1, -1); obj[rel] = url; return obj; }, {}); }; /** * Buffers response data events and re-emits when they're unzipped. * * @param {Request} req * @param {Response} res * @api private */ exports.unzip = function(req, res){ if (!zlib) return; var unzip = zlib.createUnzip(); var stream = new Stream; var decoder; // make node responseOnEnd() happy stream.req = req; unzip.on('error', function(err){ stream.emit('error', err); }); // pipe to unzip res.pipe(unzip); // override `setEncoding` to capture encoding res.setEncoding = function(type){ decoder = new StringDecoder(type); }; // decode upon decompressing with captured encoding unzip.on('data', function(buf){ if (decoder) { var str = decoder.write(buf); if (str.length) stream.emit('data', str); } else { stream.emit('data', buf); } }); unzip.on('end', function(){ stream.emit('end'); }); // override `on` to capture data listeners var _on = res.on; res.on = function(type, fn){ if ('data' == type || 'end' == type) { stream.on(type, fn); } else if ('error' == type) { stream.on(type, fn); _on.call(res, type, fn); } else { _on.call(res, type, fn); } }; }; /** * Strip content related fields from `header`. * * @param {Object} header * @return {Object} header * @api private */ exports.cleanHeader = function(header){ delete header['content-type']; delete header['content-length']; delete header['transfer-encoding']; delete header['cookie']; delete header['host']; return header; }; superagent-0.20.0/package.json000066400000000000000000000023051241334056700162410ustar00rootroot00000000000000{ "name": "superagent", "version": "0.20.0", "description": "elegant & feature rich browser / node HTTP with a fluent API", "scripts": { "test": "make test" }, "keywords": [ "http", "ajax", "request", "agent" ], "author": "TJ Holowaychuk ", "contributors": [ "Hunter Loftis " ], "repository": { "type": "git", "url": "git://github.com/visionmedia/superagent.git" }, "dependencies": { "qs": "1.2.0", "formidable": "1.0.14", "mime": "1.2.11", "component-emitter": "1.1.2", "methods": "1.0.1", "cookiejar": "2.0.1", "debug": "~2.0.0", "reduce-component": "1.0.1", "extend": "~1.2.1", "form-data": "0.1.3", "readable-stream": "1.0.27-1" }, "devDependencies": { "zuul": "~1.6.0", "express": "3.5.0", "better-assert": "~1.0.1", "should": "3.1.3", "mocha": "*" }, "browser": { "./lib/node/index.js": "./lib/client.js", "emitter": "component-emitter", "reduce": "reduce-component" }, "component": { "scripts": { "superagent": "lib/client.js" } }, "main": "./lib/node/index.js", "engines": { "node": ">= 0.8" } } superagent-0.20.0/test/000077500000000000000000000000001241334056700147325ustar00rootroot00000000000000superagent-0.20.0/test/index.html000066400000000000000000000031101241334056700167220ustar00rootroot00000000000000 superagent test suite

    foo

    bar

    baz

      superagent-0.20.0/test/index.js000066400000000000000000000001301241334056700163710ustar00rootroot00000000000000require('./test.utils.js'); require('./test.request.js'); require('./test.xdomain.js'); superagent-0.20.0/test/json2.js000066400000000000000000000420051241334056700163240ustar00rootroot00000000000000/* http://www.JSON.org/json2.js 2011-02-23 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. This file creates a global JSON object containing two methods: stringify and parse. JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array of strings. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the value For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array of strings, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. */ /*jslint evil: true, strict: false, regexp: false */ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, prototype, push, replace, slice, stringify, test, toJSON, toString, valueOf */ // Create a JSON object only if one does not already exist. We create the // methods in a closure to avoid creating global variables. var JSON; if (!JSON) { JSON = {}; } (function () { "use strict"; function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // Is the value an array? if (Object.prototype.toString.apply(value) === '[object Array]') { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { if (typeof rep[i] === 'string') { k = rep[i]; v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // If the JSON object does not yet have a stringify method, give it one. if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }; } // If the JSON object does not yet have a parse method, give it one. if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('JSON.parse'); }; } }()); superagent-0.20.0/test/node/000077500000000000000000000000001241334056700156575ustar00rootroot00000000000000superagent-0.20.0/test/node/agency.js000066400000000000000000000112031241334056700174600ustar00rootroot00000000000000var express = require('express') , app = express() , request = require('../../') , assert = require('assert') , should = require('should'); app.use(express.cookieParser()); app.use(express.session({ secret: 'secret' })); app.post('/signin', function(req, res) { req.session.user = 'hunter@hunterloftis.com'; res.redirect('/dashboard'); }); app.get('/dashboard', function(req, res) { if (req.session.user) return res.send(200, 'dashboard'); res.send(401, 'dashboard'); }); app.all('/signout', function(req, res) { req.session.regenerate(function() { res.send(200, 'signout'); }); }); app.get('/', function(req, res) { if (req.session.user) return res.redirect('/dashboard'); res.send(200, 'home'); }); app.post('/redirect', function(req, res) { res.redirect('/simple'); }); app.get('/simple', function(req, res) { res.send(200, 'simple'); }); app.listen(4000); describe('request', function() { describe('persistent agent', function() { var agent1 = request.agent(); var agent2 = request.agent(); var agent3 = request.agent(); it('should gain a session on POST', function(done) { agent3 .post('http://localhost:4000/signin') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); should.not.exist(res.headers['set-cookie']); res.text.should.include('dashboard'); done(); }); }); it('should start with empty session (set cookies)', function(done) { agent1 .get('http://localhost:4000/dashboard') .end(function(err, res) { should.not.exist(err); res.should.have.status(401); should.exist(res.headers['set-cookie']); done(); }); }); it('should gain a session (cookies already set)', function(done) { agent1 .post('http://localhost:4000/signin') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); should.not.exist(res.headers['set-cookie']); res.text.should.include('dashboard'); done(); }); }); it('should persist cookies across requests', function(done) { agent1 .get('http://localhost:4000/dashboard') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); done(); }); }); it('should not share cookies', function(done) { agent2 .get('http://localhost:4000/dashboard') .end(function(err, res) { should.not.exist(err); res.should.have.status(401); done(); }); }); it('should not lose cookies between agents', function(done) { agent1 .get('http://localhost:4000/dashboard') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); done(); }); }); it('should be able to follow redirects', function(done) { agent1 .get('http://localhost:4000/') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); res.text.should.include('dashboard'); done(); }); }); it('should be able to post redirects', function(done) { agent1 .post('http://localhost:4000/redirect') .send({ foo: 'bar', baz: 'blaaah' }) .end(function(err, res) { should.not.exist(err); res.should.have.status(200); res.text.should.include('simple'); res.redirects.should.eql(['http://localhost:4000/simple']); done(); }); }); it('should be able to limit redirects', function(done) { agent1 .get('http://localhost:4000/') .redirects(0) .end(function(err, res) { should.not.exist(err); res.should.have.status(302); res.redirects.should.eql([]); res.header.location.should.equal('/dashboard'); done(); }); }); it('should be able to create a new session (clear cookie)', function(done) { agent1 .post('http://localhost:4000/signout') .end(function(err, res) { should.not.exist(err); res.should.have.status(200); should.exist(res.headers['set-cookie']); done(); }); }); it('should regenerate with an empty session', function(done) { agent1 .get('http://localhost:4000/dashboard') .end(function(err, res) { should.not.exist(err); res.should.have.status(401); should.not.exist(res.headers['set-cookie']); done(); }); }); }); });superagent-0.20.0/test/node/basic-auth.js000066400000000000000000000023771241334056700202460ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../../') , express = require('express') , assert = require('assert') , app = express(); app.get('/', express.basicAuth('tobi', 'learnboost'), function(req, res){ res.end('you win!'); }); app.get('/again', express.basicAuth('tobi', ''), function(req, res){ res.end('you win again!'); }); app.listen(3010); describe('Basic auth', function(){ describe('when credentials are present in url', function(){ it('should set Authorization', function(done){ request .get('http://tobi:learnboost@localhost:3010') .end(function(res){ res.status.should.equal(200); done(); }); }) }) describe('req.auth(user, pass)', function(){ it('should set Authorization', function(done){ request .get('http://localhost:3010') .auth('tobi', 'learnboost') .end(function(res){ res.status.should.equal(200); done(); }); }) }) describe('req.auth(user + ":" + pass)', function(){ it('should set authorization', function(done){ request .get('http://localhost:3010/again') .auth('tobi') .end(function(res){ res.status.should.eql(200); done(); }); }) }) }) superagent-0.20.0/test/node/basic.js000066400000000000000000000307331241334056700173040ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../..') , express = require('express') , assert = require('better-assert') , app = express() , url = require('url'); app.get('/login', function(req, res){ res.send('
      '); }); app.all('/echo', function(req, res){ res.writeHead(200, req.headers); req.pipe(res); }); app.get('/json', function(req, res){ res.send({ name: 'manny' }); }); app.get('/', function(req, res){ res.redirect('/movies'); }); app.get('/movies', function(req, res){ res.redirect('/movies/all'); }); app.get('/movies/all', function(req, res){ res.redirect('/movies/all/0'); }); app.get('/movies/all/0', function(req, res){ res.send('first movie page'); }); app.get('/links', function(req, res){ res.header('Link', '; rel="next"'); res.end(); }); app.get('/xml', function(req, res){ res.type('xml'); res.send(''); }); app.get('/custom', function(req, res){ res.type('application/x-custom'); res.send('custom stuff'); }); app.get('/error', function(req, res){ res.send(500, 'boom'); }); app.listen(5000); describe('request', function(){ describe('with an object', function(){ it('should format the url', function(done){ request .get(url.parse('http://localhost:5000/login')) .end(function(res){ assert(res.ok); done(); }) }) }) describe('with a callback', function(){ it('should invoke .end()', function(done){ request .get('localhost:5000/login', function(res){ assert(res.status == 200); done(); }) }) }) describe('without a schema', function(){ it('should default to http', function(done){ request .get('localhost:5000/login') .end(function(res){ assert(res.status == 200); done(); }) }) }) describe('.end()', function(){ it('should issue a request', function(done){ request .get('http://localhost:5000/login') .end(function(res){ assert(res.status == 200); done(); }); }) }) describe('req.toJSON()', function(){ it('should describe the request', function(done){ request .post(':5000/echo') .send({ foo: 'baz' }) .end(function(res){ var obj = res.request.toJSON(); assert('POST' == obj.method); assert(':5000/echo' == obj.url); assert('baz' == obj.data.foo); done(); }); }) }) describe('res.toJSON()', function(){ it('should describe the response', function(done){ request .post(':5000/echo') .send({ foo: 'baz' }) .end(function(res){ var obj = res.toJSON(); assert('object' == typeof obj.header); assert('object' == typeof obj.req); assert(200 == obj.status); assert('{"foo":"baz"}' == obj.text); done(); }); }); }) describe('res.error', function(){ it('should should be an Error object', function(done){ request .get(':5000/error') .end(function(res){ res.error.message.should.equal('cannot GET /error (500)'); res.error.status.should.equal(500); done(); }); }) }) describe('res.header', function(){ it('should be an object', function(done){ request .get('http://localhost:5000/login') .end(function(res){ assert('Express' == res.header['x-powered-by']); done(); }); }) }) describe('res.charset', function(){ it('should be set when present', function(done){ request .get('http://localhost:5000/login') .end(function(res){ res.charset.should.equal('utf-8'); done(); }); }) }) describe('res.statusType', function(){ it('should provide the first digit', function(done){ request .get('http://localhost:5000/login') .end(function(res){ assert(200 == res.status); assert(2 == res.statusType); done(); }); }) }) describe('res.type', function(){ it('should provide the mime-type void of params', function(done){ request .get('http://localhost:5000/login') .end(function(res){ res.type.should.equal('text/html'); res.charset.should.equal('utf-8'); done(); }); }) }) describe('res.links', function(){ it('should default to an empty object', function(done){ request .get('http://localhost:5000/login') .end(function(res){ res.links.should.eql({}); done(); }) }) it('should parse the Link header field', function(done){ request .get('http://localhost:5000/links') .end(function(res){ res.links.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2'); done(); }) }) }) describe('req.set(field, val)', function(){ it('should set the header field', function(done){ request .post('http://localhost:5000/echo') .set('X-Foo', 'bar') .set('X-Bar', 'baz') .end(function(res){ assert('bar' == res.header['x-foo']); assert('baz' == res.header['x-bar']); done(); }) }) }) describe('req.set(obj)', function(){ it('should set the header fields', function(done){ request .post('http://localhost:5000/echo') .set({ 'X-Foo': 'bar', 'X-Bar': 'baz' }) .end(function(res){ assert('bar' == res.header['x-foo']); assert('baz' == res.header['x-bar']); done(); }) }) }) describe('req.unset(field)', function(){ it('should remove the header field', function(done){ request .post('http://localhost:5000/echo') .unset('User-Agent') .end(function(res){ assert(void 0 == res.header['user-agent']); done(); }) }) }) describe('req.type(str)', function(){ it('should set the Content-Type', function(done){ request .post('http://localhost:5000/echo') .type('text/x-foo') .end(function(res){ res.header['content-type'].should.equal('text/x-foo'); done(); }); }) it('should map "json"', function(done){ request .post('http://localhost:5000/echo') .type('json') .send('{"a": 1}') .end(function(res){ res.should.be.json; done(); }); }) it('should map "html"', function(done){ request .post('http://localhost:5000/echo') .type('html') .end(function(res){ res.header['content-type'].should.equal('text/html'); done(); }); }) }) describe('req.accept(str)', function(){ it('should set Accept', function(done){ request .get('http://localhost:5000/echo') .accept('text/x-foo') .end(function(res){ res.header['accept'].should.equal('text/x-foo'); done(); }); }) it('should map "json"', function(done){ request .get('http://localhost:5000/echo') .accept('json') .end(function(res){ res.header['accept'].should.equal('application/json'); done(); }); }) it('should map "xml"', function(done){ request .get('http://localhost:5000/echo') .accept('xml') .end(function(res){ res.header['accept'].should.equal('application/xml'); done(); }); }) it('should map "html"', function(done){ request .get('http://localhost:5000/echo') .accept('html') .end(function(res){ res.header['accept'].should.equal('text/html'); done(); }); }) }) describe('req.write(str)', function(){ it('should write the given data', function(done){ var req = request.post('http://localhost:5000/echo'); req.set('Content-Type', 'application/json'); req.write('{"name"').should.be.a.boolean; req.write(':"tobi"}').should.be.a.boolean; req.end(function(res){ res.text.should.equal('{"name":"tobi"}'); done(); }); }) }) describe('req.pipe(stream)', function(){ it('should pipe the response to the given stream', function(done){ var stream = new EventEmitter; stream.buf = ''; stream.writable = true; stream.write = function(chunk){ this.buf += chunk; }; stream.end = function(){ this.buf.should.equal('{"name":"tobi"}'); done(); }; request .post('http://localhost:5000/echo') .send('{"name":"tobi"}') .pipe(stream); }) }) describe('req.send(str)', function(){ it('should write the string', function(done){ request .post('http://localhost:5000/echo') .type('json') .send('{"name":"tobi"}') .end(function(res){ res.text.should.equal('{"name":"tobi"}'); done(); }); }) }) describe('req.send(Object)', function(){ it('should default to json', function(done){ request .post('http://localhost:5000/echo') .send({ name: 'tobi' }) .end(function(res){ res.should.be.json res.text.should.equal('{"name":"tobi"}'); done(); }); }) describe('when called several times', function(){ it('should merge the objects', function(done){ request .post('http://localhost:5000/echo') .send({ name: 'tobi' }) .send({ age: 1 }) .end(function(res){ res.should.be.json res.buffered.should.be.true; res.text.should.equal('{"name":"tobi","age":1}'); done(); }); }) }) }) describe('.end(fn)', function(){ it('should check arity', function(done){ request .post('http://localhost:5000/echo') .send({ name: 'tobi' }) .end(function(err, res){ assert(null == err); res.text.should.equal('{"name":"tobi"}'); done(); }); }) it('should emit request', function(done){ var req = request.post('http://localhost:5000/echo'); req.on('request', function(request){ assert(req == request); done(); }); req.end(); }) }) describe('.buffer()', function(){ it('should enable buffering', function(done){ request .get('http://localhost:5000/custom') .buffer() .end(function(err, res){ assert(null == err); assert('custom stuff' == res.text); assert(res.buffered); done(); }); }) }) describe('.buffer(false)', function(){ it('should disable buffering', function(done){ request .post('http://localhost:5000/echo') .type('application/x-dog') .send('hello this is dog') .buffer(false) .end(function(err, res){ assert(null == err); assert(null == res.text); res.body.should.eql({}); var buf = ''; res.setEncoding('utf8'); res.on('data', function(chunk){ buf += chunk }); res.on('end', function(){ buf.should.equal('hello this is dog'); done(); }); }); }) }) describe('.agent()', function(){ it('should return the defaut agent', function(done){ var req = request.post('http://localhost:5000/echo'); req.agent().should.equal(false); done(); }) }) describe('.agent(undefined)', function(){ it('should set an agent to undefined and ensure it is chainable', function(done){ var req = request.get(); var ret = req.agent(undefined); ret.should.equal(req); assert(req.agent() === undefined); done(); }) }) describe('.agent(new http.Agent())', function(){ it('should set passed agent', function(done){ var http = require('http'); var req = request.get(); var agent = new http.Agent(); var ret = req.agent(agent); ret.should.equal(req); req.agent().should.equal(agent) done(); }) }) describe('with a content type other than application/json or text/*', function(){ it('should disable buffering', function(done){ request .post('http://localhost:5000/echo') .type('application/x-dog') .send('hello this is dog') .end(function(err, res){ assert(null == err); assert(null == res.text); res.body.should.eql({}); var buf = ''; res.setEncoding('utf8'); res.buffered.should.be.false; res.on('data', function(chunk){ buf += chunk }); res.on('end', function(){ buf.should.equal('hello this is dog'); done(); }); }); }) }) }) superagent-0.20.0/test/node/content-type.js000066400000000000000000000015101241334056700206430ustar00rootroot00000000000000var request = require('../../') , express = require('express') , assert = require('better-assert') , app = express(); app.all('/echo', function(req, res){ res.writeHead(200, req.headers); req.pipe(res); }); describe('req.set("Content-Type", contentType)', function(){ it('should work with just the contentType component', function(done){ request .post('http://localhost:3005/echo') .set('Content-Type', 'application/json') .send({ name: 'tobi' }) .end(function(err, res){ assert(!err); done(); }); }); it('should work with the charset component', function(done){ request .post('http://localhost:3005/echo') .set('Content-Type', 'application/json; charset=utf-8') .send({ name: 'tobi' }) .end(function(err, res){ assert(!err); done(); }); }); });superagent-0.20.0/test/node/exports.js000066400000000000000000000011501241334056700177160ustar00rootroot00000000000000 var request = require('../../'); describe('exports', function(){ it('should expose Part', function(){ request.Part.should.be.a.function; }) it('should expose .protocols', function(){ Object.keys(request.protocols) .should.eql(['http:', 'https:']); }) it('should expose .serialize', function(){ Object.keys(request.serialize) .should.eql(['application/x-www-form-urlencoded', 'application/json']); }) it('should expose .parse', function(){ Object.keys(request.parse) .should.eql(['application/x-www-form-urlencoded', 'application/json', 'text', 'image']); }) }) superagent-0.20.0/test/node/fixtures/000077500000000000000000000000001241334056700175305ustar00rootroot00000000000000superagent-0.20.0/test/node/fixtures/cert.pem000066400000000000000000000017211241334056700211710ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICpDCCAYwCCQCP3UEJzq8t6DANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwls b2NhbGhvc3QwHhcNMTMwOTExMTQzNDM1WhcNMTMxMDExMTQzNDM1WjAUMRIwEAYD VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5 PH7dzxu9F9MOPJHvy0osayMgMdUUOj7A1RSjM4dEGUb+Ex99uSC3i2m+ftQ2ahPY jhyowt3sOCFSnMNe1bgd6C1DLLShrir/ZarzB5bKItCiG0oPOOxXHOuDHoA6bpqi ZGJvWeFQBPNzUL4xmF6wCIOt69d0yMMYFqEk8bLZ/Nba6b3uxLr9ZaDU1ZeAsu1g Z1s2ST71Uvn03YEAc19yKaCnTH1BfWMkV2thdWQZ4/d3Dvovb4yUENL4VaQzloyy ptVP4frYZTEo2VD1dSpH6gb13s4wdiaznDJNvqbdZM4A/jSgP9gvmMztuhZdn4SR qwnPoGVIVxtIWijYlGF5AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHRAO+/f6Yvm qMWO/Y0atI5gZ9/FGuw8OPMiNdbLa/0kLOaqlAcxn6GD1jMIjyalIQz22dddDKJ3 7OqVF/uldLypuEIFyoHAqy8IRQYUXsqgIeW2V+6T/fGS0QWoEWUim8D9Cwxs5hnA kLf4KrIihSG5RzjuJ7RbGMF+3UMOGuXQy8m99eI8O/MNjyhk6EnPgU5ghL3wk4pb X+sgh3bUN5NHE1Nmh94l9k1XMyVZniDbnM2DhVf3kkkDHQPZwtNVZL5/jsLZfFLC FqHdPk1xgGFxoQsRgHN78LYp113yxyPfWdKRVmizaZsxm4/FRdt/O+bUVfgg4X6x dgH8W+6R8rI= -----END CERTIFICATE----- superagent-0.20.0/test/node/fixtures/key.pem000066400000000000000000000032131241334056700210220ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAuTx+3c8bvRfTDjyR78tKLGsjIDHVFDo+wNUUozOHRBlG/hMf fbkgt4tpvn7UNmoT2I4cqMLd7DghUpzDXtW4HegtQyy0oa4q/2Wq8weWyiLQohtK DzjsVxzrgx6AOm6aomRib1nhUATzc1C+MZhesAiDrevXdMjDGBahJPGy2fzW2um9 7sS6/WWg1NWXgLLtYGdbNkk+9VL59N2BAHNfcimgp0x9QX1jJFdrYXVkGeP3dw76 L2+MlBDS+FWkM5aMsqbVT+H62GUxKNlQ9XUqR+oG9d7OMHYms5wyTb6m3WTOAP40 oD/YL5jM7boWXZ+EkasJz6BlSFcbSFoo2JRheQIDAQABAoIBAHyPp6g0ayy+5pf+ NwyPIXO5H8e4eta9TBGTt+r+7YjnjouwBE8gvFVwlE0bMEzfDDVlavQ5Bc6g+Bd7 fw04njTOOhGf8F+ApT1U+p2ujsGio7U+sJCH8LWrpttnGUcxtR5abq7+O7r5eVQk CaGEGrg5IYNEwn+vuTFrljUnquNWM17NNJ83Qj8B5JGqvuVaP5iclw2AmfCHe6aG sXEo739T47jY6YBwSvPbtELWMMgciqJ7ZRw9Cr72/MM51jw5AhCC4ZqZswE4fs5T T0zEZpoyDE44ylx9Ics2goAfDdCGhS/C5sgNN/AkkecVQuY53lBt/js6zhSCvgkz wAQxagECgYEA4/5NFP2hMWruO8yt2B5UytCzvfBRXmJcooY927BBHdtYd9lv0sid 7yZR81KfsUCXKhHGrUFPPdFPEkgNwi6QOExT9Apf+VSGVm6XObEEEyqT2enyVsiT xgPVrgDKj6IDc3fXHA4wcsVxaVJ4qIt4rWtG3x6oEdZOC1bsvjkCAqECgYEAz/2d cRGxXCgCzNEjD/QkaW/hEMEnmFnLReOaIKBVkhxzD2FEFhSrem19uZddj3KIPC2E NGU1ZmcyqCaAOJt7DYiOX9zqhc12Cai26S3D8OlavSc4J04AqfJs697Ok7VXxUSA jO4BCVi/UNtzAKmK8tVpsWqv/ETTUJXVUdq1x9kCgYEAuigeuh/paNc9lBgobgk+ BKfpyxGY7q7zokRn56P/VyiNELa6hmoGAonQahOxjmIFy3TeOwLTd88ad/vbOA0a 9szj06RQ/tzUH2iHE7UEdb3TIR/THqcBebIR29SLkEGh/bsBKcgwKNYsJuoO2Neg fkDUikOWyZGpAbtE7IDRsmECgYBxgIFOls0m8V61zttHdX/5WeiEcCPfbAEV3qLZ cyW/Wm8f0YCKXDVH1kBp60RPZ70Yue4PebuualqmkHwgaBi6xe6MOc5xvjHQC5Xl oefvrCisWJ64NEUAeR8fiLNKwAdpy3wrbCZ8p0WgJmGX1u3Qns3S19m53QVEUL/c r3HL4QKBgFpWeWZKwdaJ+4iPxiMdgUVrPNw435QpBVricANLVCl0Z93tIc5vB2CN YwzdQOM+o94+oaswEefWksl4YX0x44lKQMFxhAuYRhj/vezrrCy0bDPinu065azG FZ31YMFD84NUYGgjTh9cpAoMydqtdjzn6Puvh3lGr0YFzgIhoR7t -----END RSA PRIVATE KEY----- superagent-0.20.0/test/node/fixtures/test.png000066400000000000000000000002371241334056700212170ustar00rootroot00000000000000‰PNG  IHDR PXê pHYs  šœtIMEÝ 4¯Ÿ=ÏiTXtCommentCreated with GIMPd.eIDATÓcdø_Ä€01à#UÈã…vƒžîIEND®B`‚superagent-0.20.0/test/node/fixtures/user.html000066400000000000000000000000151241334056700213700ustar00rootroot00000000000000

      name

      superagent-0.20.0/test/node/fixtures/user.json000066400000000000000000000000171241334056700213770ustar00rootroot00000000000000{"name":"tobi"}superagent-0.20.0/test/node/fixtures/user.txt000066400000000000000000000000041241334056700212410ustar00rootroot00000000000000Tobisuperagent-0.20.0/test/node/flags.js000066400000000000000000000061201241334056700173100ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../../') , express = require('express') , assert = require('assert') , app = express(); app.get('/error', function(req, res){ throw new Error('oh noes'); }); app.get('/login', function(req, res){ res.send('
      '); }); app.get('/bad-request', function(req, res){ res.send(400); }); app.get('/unauthorized', function(req, res){ res.send(401); }); app.get('/not-acceptable', function(req, res){ res.send(406); }); app.get('/no-content', function(req, res){ res.send(204); }); app.listen(3004); describe('flags', function(){ describe('with 4xx response', function(){ it('should set res.error and res.clientError', function(done){ request .get('http://localhost:3004/notfound') .end(function(res){ assert(!res.ok, 'response should not be ok'); assert(res.error, 'response should be an error'); assert(res.clientError, 'response should be a client error'); assert(!res.serverError, 'response should not be a server error'); done(); }); }) }) describe('with 5xx response', function(){ it('should set res.error and res.serverError', function(done){ request .get('http://localhost:3004/error') .end(function(res){ assert(!res.ok, 'response should not be ok'); assert(!res.notFound, 'response should not be notFound'); assert(res.error, 'response should be an error'); assert(!res.clientError, 'response should not be a client error'); assert(res.serverError, 'response should be a server error'); done(); }); }) }) describe('with 404 Not Found', function(){ it('should res.notFound', function(done){ request .get('http://localhost:3004/notfound') .end(function(res){ assert(res.notFound, 'response should be .notFound'); done(); }); }) }) describe('with 400 Bad Request', function(){ it('should set req.badRequest', function(done){ request .get('http://localhost:3004/bad-request') .end(function(res){ assert(res.badRequest, 'response should be .badRequest'); done(); }); }) }) describe('with 401 Bad Request', function(){ it('should set res.unauthorized', function(done){ request .get('http://localhost:3004/unauthorized') .end(function(res){ assert(res.unauthorized, 'response should be .unauthorized'); done(); }); }) }) describe('with 406 Not Acceptable', function(){ it('should set res.notAcceptable', function(done){ request .get('http://localhost:3004/not-acceptable') .end(function(res){ assert(res.notAcceptable, 'response should be .notAcceptable'); done(); }); }) }) describe('with 204 No Content', function(){ it('should set res.noContent', function(done){ request .get('http://localhost:3004/no-content') .end(function(res){ assert(res.noContent, 'response should be .noContent'); done(); }); }) }) })superagent-0.20.0/test/node/form.js000066400000000000000000000041741241334056700171660ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , assert = require('better-assert') , app = express(); app.post('/echo', function(req, res){ res.writeHead(200, req.headers); req.pipe(res); }); app.get('/form-data', function(req, res){ res.header('Content-Type', 'application/x-www-form-urlencoded'); res.send('pet[name]=manny'); }); app.listen(3002); describe('req.send(Object) as "form"', function(){ describe('with req.type() set to form', function(){ it('should send x-www-form-urlencoded data', function(done){ request .post('http://localhost:3002/echo') .type('form') .send({ name: 'tobi' }) .end(function(res){ res.header['content-type'].should.equal('application/x-www-form-urlencoded'); res.text.should.equal('name=tobi'); done(); }); }) }) describe('when called several times', function(){ it('should merge the objects', function(done){ request .post('http://localhost:3002/echo') .type('form') .send({ name: { first: 'tobi', last: 'holowaychuk' } }) .send({ age: '1' }) .end(function(res){ res.header['content-type'].should.equal('application/x-www-form-urlencoded'); res.text.should.equal('name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1'); done(); }); }) }) }) describe('req.send(String)', function(){ it('should default to "form"', function(done){ request .post('http://localhost:3002/echo') .send('user[name]=tj') .send('user[email]=tj@vision-media.ca') .end(function(res){ res.header['content-type'].should.equal('application/x-www-form-urlencoded'); res.body.should.eql({ user: { name: 'tj', email: 'tj@vision-media.ca' } }); done(); }) }) }) describe('res.body', function(){ describe('application/x-www-form-urlencoded', function(){ it('should parse the body', function(done){ request .get('http://localhost:3002/form-data') .end(function(res){ res.text.should.equal('pet[name]=manny'); res.body.should.eql({ pet: { name: 'manny' }}); done(); }); }) }) }) superagent-0.20.0/test/node/https.js000066400000000000000000000026221241334056700173610ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../..') , express = require('express') , assert = require('better-assert') , app = express() , url = require('url') , https = require('https') , fs = require('fs') , key = fs.readFileSync(__dirname + '/fixtures/key.pem') , cert = fs.readFileSync(__dirname + '/fixtures/cert.pem') , server; app.get('/', function(req, res){ res.send('Safe and secure!'); }); server = https.createServer({ key: key, cert: cert }, app); server.listen(8443); describe('https', function(){ describe('request', function(){ it('should give a good response', function(done){ request .get('https://localhost:8443/') .ca(cert) .end(function(res){ assert(res.ok); assert('Safe and secure!' === res.text); done(); }); }); }); describe('.agent', function () { it('should be able to make multiple requests without redefining the certificate', function(done){ var agent = request.agent({ca: cert}); agent .get('https://localhost:8443/') .end(function(res){ assert(res.ok); assert('Safe and secure!' === res.text); agent .get(url.parse('https://localhost:8443/')) .end(function(res){ assert(res.ok); assert('Safe and secure!' === res.text); done(); }); }); }); }); }) superagent-0.20.0/test/node/image.js000066400000000000000000000014101241334056700172730ustar00rootroot00000000000000/* jshint indent: 2 */ /* jshint laxcomma: true */ var EventEmitter = require('events').EventEmitter , fs = require('fs') , request = require('../../') , express = require('express') , assert = require('assert') , app = express(); describe('res.body', function(){ 'use strict'; var img = fs.readFileSync(__dirname + '/fixtures/test.png'); app.get('/image', function(req, res){ res.writeHead(200, {'Content-Type': 'image/png' }); res.end(img, 'binary'); }); app.listen(3011); describe('image/png', function(){ it('should parse the body', function(done){ request .get('http://localhost:3011/image') .end(function(res){ (res.body.length - img.length).should.equal(0); done(); }); }); }); }); superagent-0.20.0/test/node/incoming-multipart.js000066400000000000000000000027251241334056700220450ustar00rootroot00000000000000 // var request = require('../../') // , express = require('express') // , assert = require('assert') // , app = express() // , fs = require('fs'); // app.get('/', function(req, res){ // res.set('Content-Type', 'multipart/form-data; boundary=awesome'); // // res.write('\r\n'); TODO: formidable bug // res.write('--awesome\r\n'); // res.write('Content-Disposition: attachment; name="image"; filename="something.png"\r\n'); // res.write('Content-Type: image/png\r\n'); // res.write('\r\n'); // res.write('some data'); // res.write('\r\n--awesome\r\n'); // res.write('Content-Disposition: form-data; name="name"\r\n'); // res.write('Content-Type: text/plain\r\n'); // res.write('\r\n'); // res.write('tobi'); // res.write('\r\n--awesome--'); // setTimeout(function(){ // TODO: lolnode... // res.end(); // }, 1000); // }); // app.listen(3007); // describe('request multipart/form-data', function(){ // describe('req.body', function(){ // it('should be populated with fields', function(done){ // request.get('http://localhost:3007/', function(err, res){ // if (err) return done(err); // res.status.should.equal(200); // res.body.should.eql({ name: 'tobi' }); // res.files.image.name.should.equal('something.png'); // res.files.image.type.should.equal('image/png'); // assert(null == res.text, 'res.text should be empty for multipart'); // done(); // }); // }) // }) // }) superagent-0.20.0/test/node/inflate.js000066400000000000000000000027451241334056700176470ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , zlib /** * Only require zlib for Node 0.6+. */ try { zlib = require('zlib'); } catch (e) { } if (zlib) { var app = express() , subject = 'some long long long long string'; app.listen(3080); app.get('/binary', function(req, res){ zlib.deflate(subject, function (err, buf){ res.set('Content-Encoding', 'gzip'); res.send(buf); }); }); app.get('/', function (req, res, next){ zlib.deflate(subject, function (err, buf){ res.set('Content-Type', 'text/plain'); res.set('Content-Encoding', 'gzip'); res.send(buf); }); }); describe('zlib', function(){ it('should deflate the content', function(done){ request .get('http://localhost:3080') .end(function(res){ res.should.have.status(200); res.text.should.equal(subject); res.headers['content-length'].should.be.below(subject.length); done(); }); }); describe('without encoding set', function(){ it('should emit buffers', function(done){ request .get('http://localhost:3080/binary') .end(function(res){ res.should.have.status(200); res.headers['content-length'].should.be.below(subject.length); res.on('data', function(chunk){ chunk.should.have.length(subject.length); }); res.on('end', done); }); }) }) }); } superagent-0.20.0/test/node/json.js000066400000000000000000000042341241334056700171710ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../../') , express = require('express') , assert = require('assert') , app = express(); app.all('/echo', function(req, res){ res.writeHead(200, req.headers); req.pipe(res); }); app.get('/json', function(req, res){ res.send({ name: 'manny' }); }); app.listen(3005); describe('req.send(Object) as "json"', function(){ it('should default to json', function(done){ request .post('http://localhost:3005/echo') .send({ name: 'tobi' }) .end(function(res){ res.should.be.json res.text.should.equal('{"name":"tobi"}'); done(); }); }) it('should work with arrays', function(done){ request .post('http://localhost:3005/echo') .send([1,2,3]) .end(function(res){ res.should.be.json res.text.should.equal('[1,2,3]'); done(); }); }) it('should work with GET', function(done){ request .get('http://localhost:3005/echo') .send({ tobi: 'ferret' }) .end(function(res){ res.should.be.json res.text.should.equal('{"tobi":"ferret"}'); done(); }); }); describe('when called several times', function(){ it('should merge the objects', function(done){ request .post('http://localhost:3005/echo') .send({ name: 'tobi' }) .send({ age: 1 }) .end(function(res){ res.should.be.json res.text.should.equal('{"name":"tobi","age":1}'); done(); }); }) }) }) describe('res.body', function(){ describe('application/json', function(){ it('should parse the body', function(done){ request .get('http://localhost:3005/json') .end(function(res){ res.text.should.equal('{"name":"manny"}'); res.body.should.eql({ name: 'manny' }); done(); }); }) }) describe('HEAD requests', function(){ it('should not throw a parse error', function(done){ request .head('http://localhost:3005/json') .end(function(err, res){ assert(err === null); assert(res.text === undefined) assert(Object.keys(res.body).length === 0) done(); }); }); }); }) superagent-0.20.0/test/node/multipart.js000066400000000000000000000177631241334056700202540ustar00rootroot00000000000000 // var request = require('../../') // , express = require('express') // , assert = require('assert') // , app = express() // , fs = require('fs'); // function read(file) { // return fs.readFileSync(file, 'utf8'); // } // app.post('/echo', function(req, res){ // res.writeHead(200, req.headers); // req.pipe(res); // }); // app.listen(3005); // function boundary(ct) { // return ct.match(/boundary="(.*)"/)[1]; // } // describe('Request', function(){ // describe('#part()', function(){ // it('should return a new Part', function(){ // var req = request.post('http://localhost:3005'); // req.part().constructor.name.should.equal('Part'); // req.part().constructor.name.should.equal('Part'); // req.part().should.not.equal(req.part()); // }) // }) // it('should default res.files to {}', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.end(function(err, res){ // if (err) return done(err); // res.files.should.eql({}); // res.body.should.eql({}); // done(); // }); // }) // describe('#field(name, value)', function(){ // it('should set a multipart field value', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.field('user[name]', 'tobi'); // req.field('user[age]', '2'); // req.field('user[species]', 'ferret'); // req.end(function(err, res){ // if (err) return done(err); // res.body['user[name]'].should.equal('tobi'); // res.body['user[age]'].should.equal('2'); // res.body['user[species]'].should.equal('ferret'); // done(); // }); // }) // it('should work with file attachments', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.field('name', 'Tobi'); // req.attach('document', 'test/node/fixtures/user.html'); // req.field('species', 'ferret'); // req.end(function(err, res){ // if (err) return done(err); // res.body.name.should.equal('Tobi'); // res.body.species.should.equal('ferret'); // var html = res.files.document; // html.name.should.equal('user.html'); // html.type.should.equal('text/html'); // read(html.path).should.equal('

      name

      '); // done(); // }) // }) // }) // describe('#attach(name, path)', function(){ // it('should attach a file', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.attach('one', 'test/node/fixtures/user.html'); // req.attach('two', 'test/node/fixtures/user.json'); // req.attach('three', 'test/node/fixtures/user.txt'); // req.end(function(err, res){ // if (err) return done(err); // var html = res.files.one; // var json = res.files.two // var text = res.files.three; // html.name.should.equal('user.html'); // html.type.should.equal('text/html'); // read(html.path).should.equal('

      name

      '); // json.name.should.equal('user.json'); // json.type.should.equal('application/json'); // read(json.path).should.equal('{"name":"tobi"}'); // text.name.should.equal('user.txt'); // text.type.should.equal('text/plain'); // read(text.path).should.equal('Tobi'); // done(); // }) // }) // describe('when a file does not exist', function(){ // it('should emit an error', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.attach('name', 'foo'); // req.attach('name2', 'bar'); // req.attach('name3', 'baz'); // req.on('error', function(err){ // err.message.should.include('ENOENT'); // err.path.should.equal('foo'); // done(); // }); // req.end(function(err, res){ // if (err) return done(err); // assert(0, 'end() was called'); // }); // }) // }) // }) // describe('#attach(name, path, filename)', function(){ // it('should use the custom filename', function(done){ // request // .post(':3005/echo') // .attach('document', 'test/node/fixtures/user.html', 'doc.html') // .end(function(err, res){ // if (err) return done(err); // var html = res.files.document; // html.name.should.equal('doc.html'); // html.type.should.equal('text/html'); // read(html.path).should.equal('

      name

      '); // done(); // }) // }) // }) // }) // describe('Part', function(){ // describe('with a single part', function(){ // it('should construct a multipart request', function(done){ // var req = request.post('http://localhost:3005/echo'); // req // .part() // .set('Content-Disposition', 'attachment; name="image"; filename="image.png"') // .set('Content-Type', 'image/png') // .write('some image data'); // req.end(function(err, res){ // if (err) return done(err); // var ct = res.header['content-type']; // ct.should.include('multipart/form-data; boundary='); // res.body.should.eql({}); // res.files.image.name.should.equal('image.png'); // res.files.image.type.should.equal('image/png'); // done(); // }); // }) // }) // describe('with several parts', function(){ // it('should construct a multipart request', function(done){ // var req = request.post('http://localhost:3005/echo'); // req.part() // .set('Content-Type', 'image/png') // .set('Content-Disposition', 'attachment; filename="myimage.png"') // .write('some image data'); // var part = req.part() // .set('Content-Type', 'image/png') // .set('Content-Disposition', 'attachment; filename="another.png"') // part.write('random'); // part.write('thing'); // part.write('here'); // req.part() // .set('Content-Disposition', 'form-data; name="name"') // .set('Content-Type', 'text/plain') // .write('tobi'); // req.end(function(err, res){ // if (err) return done(err); // res.body.name.should.equal('tobi'); // Object.keys(res.files).should.eql(['myimage.png', 'another.png']); // done(); // }); // }) // }) // describe('with a Content-Type specified', function(){ // it('should append the boundary', function(done){ // var req = request // .post('http://localhost:3005/echo') // .type('multipart/form-data'); // req // .part() // .set('Content-Type', 'text/plain') // .set('Content-Disposition', 'form-data; name="name"') // .write('Tobi'); // req.end(function(err, res){ // if (err) return done(err); // res.header['content-type'].should.include('boundary='); // res.body.name.should.equal('Tobi'); // done(); // }); // }) // }) // describe('#name(str)', function(){ // it('should set Content-Disposition to form-data and name param', function(done){ // var req = request // .post('http://localhost:3005/echo'); // req // .part() // .name('user[name]') // .write('Tobi'); // req.end(function(err, res){ // if (err) return done(err); // res.body['user[name]'].should.equal('Tobi'); // done(); // }); // }) // }) // describe('#attachment(name, path)', function(){ // it('should set Content-Disposition and Content-Type', function(done){ // var req = request // .post('http://localhost:3005/echo') // .type('multipart/form-data'); // req // .part() // .attachment('file', 'path/to/my.txt') // .write('Tobi'); // req.end(function(err, res){ // if (err) return done(err); // var file = res.files.file; // file.name.should.equal('my.txt'); // file.type.should.equal('text/plain'); // done(); // }); // }) // }) // }) superagent-0.20.0/test/node/network-error.js000066400000000000000000000013741241334056700210420ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('assert') , net = require('net'); function getFreePort(fn) { var server = net.createServer(); server.listen(0, function(){ var port = server.address().port; server.close(function(){ fn(port); }); }); }; describe('with network error', function(){ before(function(done){ var self = this; // connecting to a free port // will trigger a connection refused getFreePort(function(port){ self.port = port; done(); }); }); it('should error', function(done) { request .get('http://localhost:' + this.port + '/') .end(function(err, res){ assert(err, 'expected an error'); done(); }); }); }); superagent-0.20.0/test/node/not-modified.js000066400000000000000000000016251241334056700205770ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , assert = require('assert') , app = express(); app.get('/', function(req, res){ if (req.header('if-modified-since')) { res.send(304); } else { res.send('' + Date.now()) } }); app.listen(3008); describe('request', function(){ describe('not modified', function(){ var ts; it('should start with 200', function(done){ request .get('http://localhost:3008/') .end(function(res){ res.should.have.status(200) res.text.should.match(/^\d+$/); ts = +res.text; done(); }); }) it('should then be 304', function(done){ request .get('http://localhost:3008/') .set('If-Modified-Since', new Date(ts).toUTCString()) .end(function(res){ res.should.have.status(304) // res.text.should.be.empty done(); }); }) }) })superagent-0.20.0/test/node/parsers.js000066400000000000000000000032761241334056700177040ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('better-assert') , fs = require('fs') , app = express(); app.get('/manny', function(req, res){ res.send('{"name":"manny"}'); }); var img = fs.readFileSync(__dirname + '/fixtures/test.png'); app.get('/image', function(req, res){ res.writeHead(200, {'Content-Type': 'image/png' }); res.end(img, 'binary'); }); app.listen(3033); describe('req.parse(fn)', function(){ it('should take precedence over default parsers', function(done){ request .get('http://localhost:3033/manny') .parse(request.parse['application/json']) .end(function(res){ assert(res.ok); assert('{"name":"manny"}' == res.text); assert('manny' == res.body.name); done(); }); }) it('should be the only parser', function(done){ request .get('http://localhost:3033/image') .parse(function(res, fn) { res.on('data', function() {}); }) .end(function(res){ assert(res.ok); assert(res.text === undefined); res.body.should.eql({}); done(); }); }) it('should emit error if parser throws', function(done){ request .get('http://localhost:3033/manny') .parse(function() { throw new Error('I am broken'); }) .on('error', function(err) { err.message.should.equal('I am broken'); done(); }) .end(); }) it('should emit error if parser returns an error', function(done){ request .get('http://localhost:3033/manny') .parse(function(res, fn) { fn(new Error('I am broken')); }) .on('error', function(err) { err.message.should.equal('I am broken'); done(); }) .end() }) }) superagent-0.20.0/test/node/pipe.js000066400000000000000000000021431241334056700171520ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , app = express() , fs = require('fs'); app.use(express.bodyParser()); app.post('/', function(req, res){ res.send(req.body); }); app.listen(3020); describe('request pipe', function(){ afterEach(removeTmpfile); it('should act as a writable stream', function(done){ var req = request.post('http://localhost:3020'); var stream = fs.createReadStream('test/node/fixtures/user.json'); req.type('json'); req.on('response', function(res){ res.body.should.eql({ name: 'tobi' }); done(); }); stream.pipe(req); }) it('should act as a readable stream', function(done){ var stream = fs.createWriteStream('test/node/fixtures/tmp.json'); var req = request.get('http://localhost:3025'); req.type('json'); req.on('end', function(){ JSON.parse(fs.readFileSync('test/node/fixtures/tmp.json', 'utf8')).should.eql({ name: 'tobi' }); done(); }); req.pipe(stream); }) }); function removeTmpfile(done){ fs.unlink('test/node/fixtures/tmp.json', function(err){ done(); }); } superagent-0.20.0/test/node/query.js000066400000000000000000000070421241334056700173650ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('better-assert') , app = express(); app.get('/', function(req, res){ res.send(req.query); }); app.del('/', function(req, res){ res.send(req.query); }); app.listen(3006); describe('req.query(String)', function(){ it('should work when called once', function(done){ request .del('http://localhost:3006/') .query('name=tobi') .end(function(res){ res.body.should.eql({ name: 'tobi' }); done(); }); }) it('should work with url query-string', function(done){ request .del('http://localhost:3006/?name=tobi') .query('age=2') .end(function(res){ res.body.should.eql({ name: 'tobi', age: '2' }); done(); }); }) it('should work with compound elements', function(done){ request .del('http://localhost:3006/') .query('name=tobi&age=2') .end(function(res){ res.body.should.eql({ name: 'tobi', age: '2' }); done(); }); }) it('should work when called multiple times', function(done){ request .del('http://localhost:3006/') .query('name=tobi') .query('age=2') .end(function(res){ res.body.should.eql({ name: 'tobi', age: '2' }); done(); }); }) it('should work when mixed with objects', function(done){ request .del('http://localhost:3006/') .query('name=tobi') .query({ age: 2 }) .end(function(res){ res.body.should.eql({ name: 'tobi', age: '2' }); done(); }); }) it('should supply uri malformed error to the callback', function(done){ request .get('http://localhost:3006') .query('name=toby') .query('a=\uD800') .query({ b: '\uD800' }) .end(function(err, res){ assert(err instanceof Error); assert('URIError' == err.name); done(); }); }) // it('should leave strange formatting as-is', function(done){ // request // .del('http://localhost:3006/') // .query('a=1&a=2&a=3') // .end(function(res){ // res.body.should.eql({ a: '3' }); // done(); // }); // }) }) describe('req.query(Object)', function(){ it('should construct the query-string', function(done){ request .del('http://localhost:3006/') .query({ name: 'tobi' }) .query({ order: 'asc' }) .query({ limit: ['1', '2'] }) .end(function(res){ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] }); done(); }); }) it('should not error on dates', function(done){ var date = new Date(0); request .del('http://localhost:3006/') .query({ at: date }) .end(function(res){ assert(date.toISOString() == res.body.at); done(); }); }) it('should work after setting header fields', function(done){ request .del('http://localhost:3006/') .set('Foo', 'bar') .set('Bar', 'baz') .query({ name: 'tobi' }) .query({ order: 'asc' }) .query({ limit: ['1', '2'] }) .end(function(res){ res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] }); done(); }); }) it('should append to the original query-string', function(done){ request .del('http://localhost:3006/?name=tobi') .query({ order: 'asc' }) .end(function(res) { res.body.should.eql({ name: 'tobi', order: 'asc' }); done(); }); }); it('should retain the original query-string', function(done){ request .del('http://localhost:3006/?name=tobi') .end(function(res) { res.body.should.eql({ name: 'tobi' }); done(); }); }); }) superagent-0.20.0/test/node/redirects-other-host.js000066400000000000000000000012171241334056700222740ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , assert = require('assert') , app = express() , should = require('should'); app.get('/test', function(req, res){ res.redirect('https://github.com/'); }); app.listen(3210); describe('request', function(){ describe('on redirect', function(){ it('should follow Location even when the host changes', function(done){ var req = request .get('http://localhost:3210/test') .redirects(1) .end(function(res){ req.req._headers.host.should.eql('github.com'); res.status.should.eql(200); done(); }); }) }); });superagent-0.20.0/test/node/redirects.js000066400000000000000000000075331241334056700202110ustar00rootroot00000000000000 var EventEmitter = require('events').EventEmitter , request = require('../../') , express = require('express') , assert = require('assert') , app = express() , should = require('should'); app.get('/', function(req, res){ res.redirect('/movies'); }); app.get('/movies', function(req, res){ res.redirect('/movies/all'); }); app.get('/movies/all', function(req, res){ res.redirect('/movies/all/0'); }); app.get('/movies/all/0', function(req, res){ res.send('first movie page'); }); app.post('/movie', function(req, res){ res.redirect('/movies/all/0'); }); app.get('/tobi', function(req, res){ res.send('tobi'); }); app.get('/relative', function(req, res){ res.set('Location', '/tobi'); res.send(302); }); app.get('/header', function(req, res){ res.redirect('/header/2'); }); app.post('/header', function(req, res){ res.redirect('/header/2'); }); app.get('/header/2', function(req, res){ res.send(req.headers); }); app.listen(3003); describe('request', function(){ describe('on redirect', function(){ it('should follow Location', function(done){ var redirects = []; request .get('http://localhost:3003/') .on('redirect', function(res){ redirects.push(res.headers.location); }) .end(function(res){ var arr = []; arr.push('/movies'); arr.push('/movies/all'); arr.push('/movies/all/0'); redirects.should.eql(arr); res.text.should.equal('first movie page'); done(); }); }) it('should retain header fields', function(done){ request .get('http://localhost:3003/header') .set('X-Foo', 'bar') .end(function(res){ res.body.should.have.property('x-foo', 'bar'); done(); }); }) it('should remove Content-* fields', function(done){ request .post('http://localhost:3003/header') .type('txt') .set('X-Foo', 'bar') .set('X-Bar', 'baz') .send('hey') .end(function(res){ res.body.should.have.property('x-foo', 'bar'); res.body.should.have.property('x-bar', 'baz'); res.body.should.not.have.property('content-type'); res.body.should.not.have.property('content-length'); res.body.should.not.have.property('transfer-encoding'); done(); }); }) describe('when relative', function(){ it('should construct the FQDN', function(done){ var redirects = []; request .get('http://localhost:3003/relative') .on('redirect', function(res){ redirects.push(res.headers.location); }) .end(function(res){ var arr = []; redirects.should.eql(['/tobi']); res.text.should.equal('tobi'); done(); }); }) }) }) describe('req.redirects(n)', function(){ it('should alter the default number of redirects to follow', function(done){ var redirects = []; request .get('http://localhost:3003/') .redirects(2) .on('redirect', function(res){ redirects.push(res.headers.location); }) .end(function(res){ var arr = []; assert(res.redirect, 'res.redirect'); arr.push('/movies'); arr.push('/movies/all'); redirects.should.eql(arr); res.text.should.match(/Moved Temporarily/); done(); }); }) }) describe('on POST', function(){ it('should redirect as GET', function(done){ var redirects = []; request .post('http://localhost:3003/movie') .send({ name: 'Tobi' }) .redirects(2) .on('redirect', function(res){ redirects.push(res.headers.location); }) .end(function(res){ var arr = []; arr.push('/movies/all/0'); redirects.should.eql(arr); res.text.should.equal('first movie page'); done(); }); }) }) }) superagent-0.20.0/test/node/response-readable-stream.js000066400000000000000000000020041241334056700230750ustar00rootroot00000000000000 var request = require('../../') , express = require('express') , app = express() , fs = require('fs'); app.get('/', function(req, res){ fs.createReadStream('test/node/fixtures/user.json').pipe(res); }); app.listen(3025); describe('response', function(){ it('should act as a readable stream', function(done){ var req = request .get('http://localhost:3025') .buffer(false); req.end(function(err,res){ if (err) return done(err); var trackEndEvent = 0; var trackCloseEvent = 0; res.on('end',function(){ trackEndEvent++; trackEndEvent.should.equal(1); trackCloseEvent.should.equal(0); // close should not have been called done(); }); res.on('close',function(){ trackCloseEvent++; }); (function(){ res.pause() }).should.not.throw(); (function(){ res.resume() }).should.not.throw(); (function(){ res.destroy() }).should.not.throw(); }); }); }); superagent-0.20.0/test/node/timeout.js000066400000000000000000000012451241334056700177050ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('assert') , app = express(); app.get('/:ms', function(req, res){ var ms = parseInt(req.params.ms, 10); setTimeout(function(){ res.send('hello'); }, ms); }); app.listen(3009); describe('.timeout(ms)', function(){ describe('when timeout is exceeded', function(done){ it('should error', function(done){ request .get('http://localhost:3009/500') .timeout(150) .end(function(err, res){ assert(err, 'expected an error'); assert('number' == typeof err.timeout, 'expected an error with .timeout'); done(); }); }) }) })superagent-0.20.0/test/node/toError.js000066400000000000000000000012111241334056700176440ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('better-assert') , app = express() , url = require('url'); app.get('/', function(req, res){ res.send(400, 'invalid json'); }); app.listen(8888); describe('res.toError()', function(){ it('should return an Error', function(done){ request .get('http://localhost:8888/') .end(function(res){ var err = res.toError(); assert(err.status == 400); assert(err.method == 'GET'); assert(err.path == '/'); assert(err.message == 'cannot GET / (400)'); assert(err.text == 'invalid json'); done(); }); }) }) superagent-0.20.0/test/node/user-agent.js000066400000000000000000000022131241334056700202650ustar00rootroot00000000000000 var request = require('../..') , express = require('express') , assert = require('better-assert') , app = express() , url = require('url'); app.all('/ua', function(req, res){ res.writeHead(200, req.headers); req.pipe(res); }); app.listen(3345); describe('req.get()', function(){ it('should set a default user-agent', function(done){ request .get('http://localhost:3345/ua') .end(function(err, res){ assert(res.headers); assert(res.headers['user-agent']); assert(/^node-superagent\/\d+\.\d+\.\d+$/.test(res.headers['user-agent'])); done(); }); }); it('should be able to override user-agent', function(done){ request .get('http://localhost:3345/ua') .set('User-Agent', 'foo/bar') .end(function(err, res){ assert(res.headers); assert(res.headers['user-agent'] == 'foo/bar'); done(); }); }); it('should be able to wipe user-agent', function(done){ request .get('http://localhost:3345/ua') .unset('User-Agent') .end(function(err, res){ assert(res.headers); assert(res.headers['user-agent'] == void 0); done(); }); }); }); superagent-0.20.0/test/node/utils.js000066400000000000000000000025661241334056700173660ustar00rootroot00000000000000 var utils = require('../../lib/node/utils') , assert = require('assert'); describe('utils.uid(len)', function(){ it('should generate a unique id', function(){ utils.uid(10).should.have.length(10); utils.uid(30).should.have.length(30); utils.uid(30).should.not.equal(utils.uid(30)); }) }) describe('utils.type(str)', function(){ it('should return the mime type', function(){ utils.type('application/json; charset=utf-8') .should.equal('application/json'); utils.type('application/json') .should.equal('application/json'); }) }) describe('utils.params(str)', function(){ it('should return the field parameters', function(){ var str = 'application/json; charset=utf-8; foo = bar'; var obj = utils.params(str); obj.charset.should.equal('utf-8'); obj.foo.should.equal('bar'); var str = 'application/json'; utils.params(str).should.eql({}); }) }) describe('utils.parseLinks(str)', function(){ it('should parse links', function(){ var str = '; rel="next", ; rel="last"'; var ret = utils.parseLinks(str); ret.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2'); ret.last.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=5'); }) })superagent-0.20.0/test/server.js000066400000000000000000000056641241334056700166110ustar00rootroot00000000000000 /** * Module dependencies. */ var express = require('express'); var app = express(); app.set('json spaces', 0); app.use(function(req, res, next){ if ('/echo' != req.url) return next(); res.set(req.headers); req.pipe(res); }); app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(function(req, res, next){ res.cookie('name', 'tobi'); next(); }); app.use('/xdomain', function(req, res, next){ if (!req.get('Origin')) return next(); res.set('Access-Control-Allow-Origin', req.get('Origin')); res.set('Access-Control-Allow-Credentials', 'true'); res.set('Access-Control-Allow-Methods', 'POST'); res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); if ('OPTIONS' == req.method) return res.send(200); next(); }); app.get('/', function(req, res){ res.redirect('/test/'); }); app.get('/error', function(req, res){ res.status(500).send('fail'); }); app.get('/unauthorized', function(req, res){ res.send(401); }); app.get('/bad-request', function(req, res){ res.send(400); }); app.get('/not-acceptable', function(req, res){ res.send(406); }); app.get('/no-content', function(req, res){ res.send(204); }); app.get('/delay/:ms', function(req, res){ var ms = ~~req.params.ms; setTimeout(function(){ res.send(200); }, ms); }); app.put('/user/:id', function(req, res){ res.send('updated'); }); app.patch('/user/:id', function(req, res){ res.send('updated'); }); app.get('/querystring', function(req, res){ res.send(req.query); }); app.post('/todo/item', function(req, res){ var buf = ''; req.on('data', function(chunk){ buf += chunk; }); req.on('end', function(){ res.send('added "' + buf + '"'); }); }); app.post('/user/:id/pet', function(req, res){ res.send('added pet "' + req.body.pet + '"'); }); app.post('/user', function(req, res){ res.send('created'); }); app.del('/user/:id', function(req, res){ res.send('deleted'); }); app.all('/echo-header/:field', function(req, res){ res.send(req.headers[req.params.field]); }); app.post('/pet', function(req, res){ res.send('added ' + req.body.name + ' the ' + req.body.species); }); app.get('/pets', function(req, res){ res.send(['tobi', 'loki', 'jane']); }); app.get('/text', function(req, res){ res.send("just some text"); }); app.get('/foo', function(req, res){ res .header('Content-Type', 'application/x-www-form-urlencoded') .send('foo=bar'); }); app.post('/auth', function(req, res) { var auth = req.headers.authorization, parts = auth.split(' '), credentials = new Buffer(parts[1], 'base64').toString().split(':'), user = credentials[0], pass = credentials[1]; res.send({ user : user, pass : pass }); }); app.get('/xdomain', function(req, res){ res.send(req.cookies.name); }); app.use(express.static(__dirname + '/../')); var server = app.listen(process.env.ZUUL_PORT, function() { //console.log('Test server listening on port %d', server.address().port); }); superagent-0.20.0/test/test.js000066400000000000000000000042141241334056700162500ustar00rootroot00000000000000 o = $; o(run); var tests = [] , currentTest; function run() { var start = new Date; function next(i) { var test = tests[i] , testStart = new Date; currentTest = test; if (!test) return complete(start); try { if (test.fn.length) { test.fn(function(){ next(++i); }); } else { test.fn(); next(++i); } passed(test, new Date - testStart); } catch (err) { failed(test, err); throw(err); } } next(0); } window.onerror = function(msg){ failed(currentTest, msg); }; function complete(start) { var duration = new Date - start , n = tests.length; $('#test-results') .append('

      ' + n + ' tests completed in ' + duration + 'ms

      '); } function test(label, fn) { tests.push({ label: label , fn: fn }); } function passed(test, duration) { $('#tests').append('
    • ✓ ' + test.label + ' - ' + duration + '
    • '); } function failed(test, err) { $('#tests').append('
    • ✖ ' + test.label + ' - ' + err + '
    • '); } function assert(ok, msg) { if (!ok) throw new Error(msg || 'assertion failed'); } var isArray = Array.isArray ? Array.isArray : function (v) { return Object.prototype.toString.call(v) == '[object Array]'; }; function eql(a, b) { // same object if (a === b) return true; // different types try { // Note: this breaks in IE if (toString.call(a) != toString.call(b)) return false; } catch (e) { } // array if (isArray(a)) { // different length if (a.length != b.length) return false; // different values for (var i = 0, len = a.length; i < len; ++i) { if (!eql(a[i], b[i])) return false; } return true; } // object if (Object == a.constructor) { var alen = 0 , blen = 0; for (var key in a) ++alen; for (var key in b) ++blen; // different lengths if (alen != blen) return false; // different values for (var key in a) { if (!eql(a[key], b[key])) return false; } return true; } return false; } assert.eql = function(a, b, msg){ assert(eql(a, b), msg); }; superagent-0.20.0/test/test.request.js000066400000000000000000000305541241334056700177450ustar00rootroot00000000000000 var assert = require('assert'); var request = require('../'); test('Request inheritance', function(){ assert(request.get('/') instanceof request.Request); }); test('request() simple GET without callback', function(next){ request('GET', 'test/test.request.js').end(); next(); }); test('request() simple GET', function(next){ request('GET', 'test/test.request.js').end(function(res){ assert(res instanceof request.Response, 'respond with Response'); assert(res.ok, 'response should be ok'); assert(res.text, 'res.text'); next(); }); }); test('request() simple HEAD', function(next){ request.head('test/test.request.js').end(function(res){ assert(res instanceof request.Response, 'respond with Response'); assert(res.ok, 'response should be ok'); assert(!res.text, 'res.text'); next(); }); }); test('request() error object', function(next) { request('GET', '/error').end(function(res) { assert(res.error, 'response should be an error'); assert(res.error.message == 'cannot GET /error (500)'); assert(res.error.status == 500); assert(res.error.method == 'GET'); assert(res.error.url == '/error'); next(); }); }); test('request() GET 5xx', function(next){ request('GET', '/error').end(function(res){ assert(!res.ok, 'response should not be ok'); assert(res.error, 'response should be an error'); assert(!res.clientError, 'response should not be a client error'); assert(res.serverError, 'response should be a server error'); next(); }); }); test('request() GET 4xx', function(next){ request('GET', '/notfound').end(function(res){ assert(!res.ok, 'response should not be ok'); assert(res.error, 'response should be an error'); assert(res.clientError, 'response should be a client error'); assert(!res.serverError, 'response should not be a server error'); next(); }); }); test('request() GET 404 Not Found', function(next){ request('GET', '/notfound').end(function(res){ assert(res.notFound, 'response should be .notFound'); next(); }); }); test('request() GET 400 Bad Request', function(next){ request('GET', '/bad-request').end(function(res){ assert(res.badRequest, 'response should be .badRequest'); next(); }); }); test('request() GET 401 Bad Request', function(next){ request('GET', '/unauthorized').end(function(res){ assert(res.unauthorized, 'response should be .unauthorized'); next(); }); }); test('request() GET 406 Not Acceptable', function(next){ request('GET', '/not-acceptable').end(function(res){ assert(res.notAcceptable, 'response should be .notAcceptable'); next(); }); }); test('request() GET 204 No Content', function(next){ request('GET', '/no-content').end(function(res){ assert(res.noContent, 'response should be .noContent'); next(); }); }); test('request() header parsing', function(next){ request('GET', '/notfound').end(function(res){ assert('text/plain' == res.header['content-type']); assert('Express' == res.header['x-powered-by']); next(); }); }); test('request() .status', function(next){ request('GET', '/notfound').end(function(res){ assert(404 == res.status, 'response .status'); assert(4 == res.statusType, 'response .statusType'); next(); }); }); test('get()', function(next){ request.get('/notfound').end(function(res){ assert(404 == res.status, 'response .status'); assert(4 == res.statusType, 'response .statusType'); next(); }); }); test('patch()', function(next){ request.patch('/user/12').end(function(res){ assert('updated' == res.text, 'response text'); next(); }); }); test('put()', function(next){ request.put('/user/12').end(function(res){ assert('updated' == res.text, 'response text'); next(); }); }); test('post()', function(next){ request.post('/user').end(function(res){ assert('created' == res.text, 'response text'); next(); }); }); test('del()', function(next){ request.del('/user/12').end(function(res){ assert('deleted' == res.text, 'response text'); next(); }); }); test('post() data', function(next){ request.post('/todo/item') .type('application/octet-stream') .send('tobi') .end(function(res){ assert('added "tobi"' == res.text, 'response text'); next(); }); }); test('request .type()', function(next){ request .post('/user/12/pet') .type('urlencoded') .send('pet=tobi') .end(function(res){ assert('added pet "tobi"' == res.text, 'response text'); next(); }); }); test('request .type() with alias', function(next){ request .post('/user/12/pet') .type('application/x-www-form-urlencoded') .send('pet=tobi') .end(function(res){ assert('added pet "tobi"' == res.text, 'response text'); next(); }); }); test('request .get() with no data or callback', function(next){ request.get('/echo-header/content-type'); next(); }); test('request .send() with no data only', function(next){ request.post('/user/5/pet').type('urlencoded').send('pet=tobi'); next(); }); test('request .send() with callback only', function(next){ request .get('/echo-header/accept') .set('Accept', 'foo/bar') .end(function(res){ assert('foo/bar' == res.text); next(); }); }); test('request .accept() with json', function(next){ request .get('/echo-header/accept') .accept('json') .end(function(res){ assert('application/json' == res.text); next(); }); }); test('request .accept() with application/json', function(next){ request .get('/echo-header/accept') .accept('application/json') .end(function(res){ assert('application/json' == res.text); next(); }); }); test('request .accept() with xml', function(next){ request .get('/echo-header/accept') .accept('xml') .end(function(res){ assert('application/xml' == res.text); next(); }); }); test('request .accept() with application/xml', function(next){ request .get('/echo-header/accept') .accept('application/xml') .end(function(res){ assert('application/xml' == res.text); next(); }); }); // FIXME: ie6 will POST rather than GET here due to data(), // but I'm not 100% sure why. Newer IEs are OK. test('request .end()', function(next){ request .get('/echo-header/content-type') .set('Content-Type', 'text/plain') .send('wahoo') .end(function(res){ assert('text/plain' == res.text); next(); }); }); test('request .send()', function(next){ request .get('/echo-header/content-type') .set('Content-Type', 'text/plain') .send('wahoo') .end(function(res){ assert('text/plain' == res.text); next(); }); }); test('request .set()', function(next){ request .get('/echo-header/content-type') .set('Content-Type', 'text/plain') .send('wahoo') .end(function(res){ assert('text/plain' == res.text); next(); }); }); test('request .set(object)', function(next){ request .get('/echo-header/content-type') .set({ 'Content-Type': 'text/plain' }) .send('wahoo') .end(function(res){ assert('text/plain' == res.text); next(); }); }); test('POST urlencoded', function(next){ request .post('/pet') .type('urlencoded') .send({ name: 'Manny', species: 'cat' }) .end(function(res){ assert('added Manny the cat' == res.text); next(); }); }); test('POST json', function(next){ request .post('/pet') .type('json') .send({ name: 'Manny', species: 'cat' }) .end(function(res){ assert('added Manny the cat' == res.text); next(); }); }); test('POST json array', function(next){ request .post('/echo') .send([1,2,3]) .end(function(res){ assert('application/json' == res.header['content-type'].split(';')[0]); assert('[1,2,3]' == res.text); next(); }); }); test('POST json default', function(next){ request .post('/pet') .send({ name: 'Manny', species: 'cat' }) .end(function(res){ assert('added Manny the cat' == res.text); next(); }); }); test('POST multiple .send() calls', function(next){ request .post('/pet') .send({ name: 'Manny' }) .send({ species: 'cat' }) .end(function(res){ assert('added Manny the cat' == res.text); next(); }); }); test('POST multiple .send() strings', function(next){ request .post('/echo') .send('user[name]=tj') .send('user[email]=tj@vision-media.ca') .end(function(res){ assert('application/x-www-form-urlencoded' == res.header['content-type'].split(';')[0]); assert(res.text == 'user[name]=tj&user[email]=tj@vision-media.ca') next(); }) }); test('GET .type', function(next){ request .get('/pets') .end(function(res){ assert('application/json' == res.type); next(); }); }); test('GET Content-Type params', function(next){ request .get('/text') .end(function(res){ assert('utf-8' == res.charset); next(); }); }); test('GET json', function(next){ request .get('/pets') .end(function(res){ assert.deepEqual(res.body, ['tobi', 'loki', 'jane']); next(); }); }); test('GET x-www-form-urlencoded', function(next){ request .get('/foo') .end(function(res){ assert.deepEqual(res.body, { foo: 'bar' }); next(); }); }); test('GET shorthand', function(next){ request.get('/foo', function(res){ assert('foo=bar' == res.text); next(); }); }); test('POST shorthand', function(next){ request.post('/user/0/pet', { pet: 'tobi' }, function(res){ assert('added pet "tobi"' == res.text); next(); }); }); test('POST shorthand without callback', function(next){ request.post('/user/0/pet', { pet: 'tobi' }).end(function(res){ assert('added pet "tobi"' == res.text); next(); }); }); test('GET querystring object', function(next){ request .get('/querystring') .query({ search: 'Manny' }) .end(function(res){ assert.deepEqual(res.body, { search: 'Manny' }); next(); }); }); test('GET querystring append original', function(next){ request .get('/querystring?search=Manny') .query({ range: '1..5' }) .end(function(res){ assert.deepEqual(res.body, { search: 'Manny', range: '1..5' }); next(); }); }); test('GET querystring multiple objects', function(next){ request .get('/querystring') .query({ search: 'Manny' }) .query({ range: '1..5' }) .query({ order: 'desc' }) .end(function(res){ assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' }); next(); }); }); test('GET querystring empty objects', function(next){ var req = request .get('/querystring') .query({}) .end(function(res){ assert.deepEqual(req._query, []); assert.deepEqual(res.body, {}); next(); }); }); test('GET querystring with strings', function(next){ request .get('/querystring') .query('search=Manny') .query('range=1..5') .query('order=desc') .end(function(res){ assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' }); next(); }); }); test('GET querystring with strings and objects', function(next){ request .get('/querystring') .query('search=Manny') .query({ order: 'desc', range: '1..5' }) .end(function(res){ assert.deepEqual(res.body, { search: 'Manny', range: '1..5', order: 'desc' }); next(); }); }); test('GET querystring object .get(uri, obj)', function(next){ request .get('/querystring', { search: 'Manny' }) .end(function(res){ assert.deepEqual(res.body, { search: 'Manny' }); next(); }); }); test('GET querystring object .get(uri, obj, fn)', function(next){ request .get('/querystring', { search: 'Manny'}, function(res){ assert.deepEqual(res.body, { search: 'Manny' }); next(); }); }); test('request(method, url)', function(next){ request('GET', '/foo').end(function(res){ assert('bar' == res.body.foo); next(); }); }); test('request(url)', function(next){ request('/foo').end(function(res){ assert('bar' == res.body.foo); next(); }); }); test('request(url, fn)', function(next){ request('/foo', function(res){ assert('bar' == res.body.foo); next(); }); }); test('req.timeout(ms)', function(next){ request .get('/delay/3000') .timeout(1000) .end(function(err, res){ assert(err, 'error missing'); assert(1000 == err.timeout, 'err.timeout missing'); assert('timeout of 1000ms exceeded' == err.message, 'err.message incorrect'); assert(null == res); next(); }) }) test('basic auth', function(next){ request .post('/auth') .auth('foo', 'bar') .end(function(res){ assert('foo' == res.body.user); assert('bar' == res.body.pass); next(); }); }); superagent-0.20.0/test/test.utils.js000066400000000000000000000025051241334056700174100ustar00rootroot00000000000000 var assert = require('assert'); var request = require('../'); function serialize(obj, res) { var val = request.serializeObject(obj); assert(val == res , JSON.stringify(obj) + ' to "' + res + '" serialization failed. got: ' + '"' + val + '"'); } function parse(str, obj) { var val = request.parseString(str); assert.deepEqual(val , obj , '"' + str + '" to ' + JSON.stringify(obj) + ' parse failed. got: ' + JSON.stringify(val)); } test('test .serializeObject() basics', function(){ serialize('test', 'test'); serialize('foo=bar', 'foo=bar'); serialize({ foo: 'bar' }, 'foo=bar'); serialize({ foo: null }, ''); serialize({ foo: 'null' }, 'foo=null'); serialize({ foo: undefined }, ''); serialize({ foo: 'undefined' }, 'foo=undefined'); serialize({ name: 'tj', age: 24 }, 'name=tj&age=24'); }); test('test .serializeObject() encoding', function(){ serialize({ name: '&tj&' }, 'name=%26tj%26'); serialize({ '&name&': 'tj' }, '%26name%26=tj'); }); test('test .parseString()', function(){ parse('name=tj', { name: 'tj' }); parse('name=Manny&species=cat', { name: 'Manny', species: 'cat' }); parse('redirect=/&ok', { redirect: '/', ok: 'undefined' }); }); test('test .parseString() decoding', function(){ parse('%26name=tj', { '&name': 'tj' }); parse('name=tj%26', { name: 'tj&' }); }); superagent-0.20.0/test/test.xdomain.js000066400000000000000000000014761241334056700177150ustar00rootroot00000000000000 var assert = require('assert'); var request = require('../'); test('req.withCredentials()', function(next){ // we force port 80 here when we don't know // this works on localhost testing cause we have a port // but on regular localtunnel.me we don't // and since localtunnel.me loads over https, port 80 // becomes cross domain var port = window.location.port || 80; var hostname = window.location.hostname; request .get('http://' + hostname + ':' + port + '/xdomain') .withCredentials() .end(function(res){ assert(200 == res.status); assert('tobi' == res.text); next(); }) }) test('x-domain failure', function(next){ request .get('http://google.com') .end(function(err, res){ assert(err, 'error missing'); assert(err.crossDomain, 'not .crossDomain'); next(); }); });