├── index.js ├── test ├── manual │ ├── fixture.json │ ├── cors.html │ └── basic.html ├── specs │ ├── basicAuth.spec.js │ ├── utils │ │ ├── trim.spec.js │ │ ├── extend.spec.js │ │ ├── merge.spec.js │ │ ├── forEach.spec.js │ │ └── isX.spec.js │ ├── helpers │ │ ├── bind.spec.js │ │ ├── isURLSameOrigin.spec.js │ │ ├── spread.spec.js │ │ ├── btoa.spec.js │ │ ├── normalizeHeaderName.spec.js │ │ ├── isAbsoluteURL.spec.js │ │ ├── combineURLs.spec.js │ │ ├── cookies.spec.js │ │ ├── parseHeaders.spec.js │ │ └── buildURL.spec.js │ ├── cancel │ │ ├── isCancel.spec.js │ │ ├── Cancel.spec.js │ │ └── CancelToken.spec.js │ ├── adapter.spec.js │ ├── basicAuthWithPolyfill.spec.js │ ├── core │ │ ├── createError.spec.js │ │ ├── enhanceError.spec.js │ │ ├── transformData.spec.js │ │ └── settle.spec.js │ ├── promise.spec.js │ ├── options.spec.js │ ├── transform.spec.js │ ├── headers.spec.js │ ├── api.spec.js │ ├── xsrf.spec.js │ ├── instance.spec.js │ ├── __helpers.js │ ├── cancel.spec.js │ ├── progress.spec.js │ ├── defaults.spec.js │ ├── interceptors.spec.js │ └── requests.spec.js └── typescript │ └── axios.ts ├── lib ├── cancel │ ├── isCancel.js │ ├── Cancel.js │ └── CancelToken.js ├── helpers │ ├── bind.js │ ├── README.md │ ├── normalizeHeaderName.js │ ├── combineURLs.js │ ├── isAbsoluteURL.js │ ├── spread.js │ ├── deprecatedMethod.js │ ├── btoa.js │ ├── cookies.js │ ├── parseHeaders.js │ ├── buildURL.js │ └── isURLSameOrigin.js ├── core │ ├── README.md │ ├── transformData.js │ ├── enhanceError.js │ ├── createError.js │ ├── settle.js │ ├── InterceptorManager.js │ ├── Axios.js │ └── dispatchRequest.js ├── adapters │ ├── README.md │ ├── xhr.js │ └── http.js ├── axios.js ├── defaults.js └── utils.js ├── .gitignore ├── examples ├── README.md ├── upload │ ├── server.js │ └── index.html ├── post │ ├── server.js │ └── index.html ├── get │ ├── server.js │ └── index.html ├── amd │ └── index.html ├── all │ └── index.html ├── transform-response │ └── index.html └── server.js ├── .npmignore ├── sandbox ├── client.js ├── server.js └── client.html ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── .travis.yml ├── bower.json ├── webpack.config.js ├── LICENSE ├── ECOSYSTEM.md ├── CONTRIBUTING.md ├── package.json ├── Gruntfile.js ├── COLLABORATOR_GUIDE.md ├── COOKBOOK.md ├── CODE_OF_CONDUCT.md ├── index.d.ts ├── UPGRADE_GUIDE.md ├── karma.conf.js └── .eslintrc /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/axios'); -------------------------------------------------------------------------------- /test/manual/fixture.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "axios" 3 | } -------------------------------------------------------------------------------- /test/specs/basicAuth.spec.js: -------------------------------------------------------------------------------- 1 | describe('basicAuth without btoa polyfill', function () { 2 | setupBasicAuthTest(); 3 | }); 4 | -------------------------------------------------------------------------------- /lib/cancel/isCancel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function isCancel(value) { 4 | return !!(value && value.__CANCEL__); 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | .tscache 4 | .DS_Store 5 | node_modules/ 6 | typings/ 7 | coverage/ 8 | test/typescript/axios.js* 9 | sauce_connect.log 10 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # axios examples 2 | 3 | To run the examples: 4 | 5 | 1. `git clone https://github.com/axios/axios.git` 6 | 2. `cd axios` 7 | 3. `npm install` 8 | 4. `grunt build` 9 | 5. `npm run examples` 10 | 6. [http://localhost:3000](http://localhost:3000) 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/.* 2 | *.iml 3 | coverage/ 4 | examples/ 5 | node_modules/ 6 | typings/ 7 | sandbox/ 8 | test/ 9 | bower.json 10 | CODE_OF_CONDUCT.md 11 | COLLABORATOR_GUIDE.md 12 | CONTRIBUTING.md 13 | COOKBOOK.md 14 | ECOSYSTEM.md 15 | Gruntfile.js 16 | karma.conf.js 17 | webpack.*.js 18 | sauce_connect.log 19 | -------------------------------------------------------------------------------- /examples/upload/server.js: -------------------------------------------------------------------------------- 1 | module.exports = function (req, res) { 2 | var data = ''; 3 | 4 | req.on('data', function (chunk) { 5 | data += chunk; 6 | }); 7 | 8 | req.on('end', function () { 9 | console.log('File uploaded'); 10 | res.writeHead(200); 11 | res.end(); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /lib/helpers/bind.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function bind(fn, thisArg) { 4 | return function wrap() { 5 | var args = new Array(arguments.length); 6 | for (var i = 0; i < args.length; i++) { 7 | args[i] = arguments[i]; 8 | } 9 | return fn.apply(thisArg, args); 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /test/specs/utils/trim.spec.js: -------------------------------------------------------------------------------- 1 | var trim = require('../../../lib/utils').trim; 2 | 3 | describe('utils::trim', function () { 4 | it('should trim spaces', function () { 5 | expect(trim(' foo ')).toEqual('foo'); 6 | }); 7 | 8 | it('should trim tabs', function () { 9 | expect(trim('\tfoo\t')).toEqual('foo'); 10 | }); 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /test/specs/helpers/bind.spec.js: -------------------------------------------------------------------------------- 1 | var bind = require('../../../lib/helpers/bind'); 2 | 3 | describe('bind', function () { 4 | it('should bind an object to a function', function () { 5 | var o = { val: 123 }; 6 | var f = bind(function (num) { 7 | return this.val * num; 8 | }, o); 9 | 10 | expect(f(2)).toEqual(246); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /lib/core/README.md: -------------------------------------------------------------------------------- 1 | # axios // core 2 | 3 | The modules found in `core/` should be modules that are specific to the domain logic of axios. These modules would most likely not make sense to be consumed outside of the axios module, as their logic is too specific. Some examples of core modules are: 4 | 5 | - Dispatching requests 6 | - Managing interceptors 7 | - Handling config 8 | -------------------------------------------------------------------------------- /lib/helpers/README.md: -------------------------------------------------------------------------------- 1 | # axios // helpers 2 | 3 | The modules found in `helpers/` should be generic modules that are _not_ specific to the domain logic of axios. These modules could theoretically be published to npm on their own and consumed by other modules or apps. Some examples of generic modules are things like: 4 | 5 | - Browser polyfills 6 | - Managing cookies 7 | - Parsing HTTP headers 8 | -------------------------------------------------------------------------------- /examples/post/server.js: -------------------------------------------------------------------------------- 1 | module.exports = function (req, res) { 2 | var data = ''; 3 | 4 | req.on('data', function (chunk) { 5 | data += chunk; 6 | }); 7 | 8 | req.on('end', function () { 9 | console.log('POST data received'); 10 | res.writeHead(200, { 11 | 'Content-Type': 'text/json' 12 | }); 13 | res.write(JSON.stringify(data)); 14 | res.end(); 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /lib/helpers/normalizeHeaderName.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('../utils'); 4 | 5 | module.exports = function normalizeHeaderName(headers, normalizedName) { 6 | utils.forEach(headers, function processHeader(value, name) { 7 | if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) { 8 | headers[normalizedName] = value; 9 | delete headers[name]; 10 | } 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /test/specs/cancel/isCancel.spec.js: -------------------------------------------------------------------------------- 1 | var isCancel = require('../../../lib/cancel/isCancel'); 2 | var Cancel = require('../../../lib/cancel/Cancel'); 3 | 4 | describe('isCancel', function() { 5 | it('returns true if value is a Cancel', function() { 6 | expect(isCancel(new Cancel())).toBe(true); 7 | }); 8 | 9 | it('returns false if value is not a Cancel', function() { 10 | expect(isCancel({ foo: 'bar' })).toBe(false); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/specs/adapter.spec.js: -------------------------------------------------------------------------------- 1 | var axios = require('../../index'); 2 | 3 | describe('adapter', function () { 4 | it('should support custom adapter', function (done) { 5 | var called = false; 6 | 7 | axios('/foo', { 8 | adapter: function (config) { 9 | called = true; 10 | } 11 | }); 12 | 13 | setTimeout(function () { 14 | expect(called).toBe(true); 15 | done(); 16 | }, 100); 17 | }); 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /lib/helpers/combineURLs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Creates a new URL by combining the specified URLs 5 | * 6 | * @param {string} baseURL The base URL 7 | * @param {string} relativeURL The relative URL 8 | * @returns {string} The combined URL 9 | */ 10 | module.exports = function combineURLs(baseURL, relativeURL) { 11 | return relativeURL 12 | ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') 13 | : baseURL; 14 | }; 15 | -------------------------------------------------------------------------------- /test/specs/helpers/isURLSameOrigin.spec.js: -------------------------------------------------------------------------------- 1 | var isURLSameOrigin = require('../../../lib/helpers/isURLSameOrigin'); 2 | 3 | describe('helpers::isURLSameOrigin', function () { 4 | it('should detect same origin', function () { 5 | expect(isURLSameOrigin(window.location.href)).toEqual(true); 6 | }); 7 | 8 | it('should detect different origin', function () { 9 | expect(isURLSameOrigin('https://github.com/axios/axios')).toEqual(false); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /lib/cancel/Cancel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * A `Cancel` is an object that is thrown when an operation is canceled. 5 | * 6 | * @class 7 | * @param {string=} message The message. 8 | */ 9 | function Cancel(message) { 10 | this.message = message; 11 | } 12 | 13 | Cancel.prototype.toString = function toString() { 14 | return 'Cancel' + (this.message ? ': ' + this.message : ''); 15 | }; 16 | 17 | Cancel.prototype.__CANCEL__ = true; 18 | 19 | module.exports = Cancel; 20 | -------------------------------------------------------------------------------- /sandbox/client.js: -------------------------------------------------------------------------------- 1 | var axios = require('../index'); 2 | 3 | var URL = 'http://127.0.0.1:3000/api'; 4 | var BODY = { 5 | foo: 'bar', 6 | baz: 1234 7 | }; 8 | 9 | function handleSuccess(data) { console.log(data); } 10 | function handleFailure(data) { console.log('error', data); } 11 | 12 | // GET 13 | axios.get(URL, { params: BODY }) 14 | .then(handleSuccess) 15 | .catch(handleFailure); 16 | 17 | // POST 18 | axios.post(URL, BODY) 19 | .then(handleSuccess) 20 | .catch(handleFailure); -------------------------------------------------------------------------------- /test/specs/basicAuthWithPolyfill.spec.js: -------------------------------------------------------------------------------- 1 | var window_btoa; 2 | 3 | describe('basicAuth with btoa polyfill', function () { 4 | beforeAll(function() { 5 | window_btoa = window.btoa; 6 | window.btoa = undefined; 7 | }); 8 | 9 | afterAll(function() { 10 | window.btoa = window_btoa; 11 | window_btoa = undefined; 12 | }); 13 | 14 | it('should not have native window.btoa', function () { 15 | expect(window.btoa).toEqual(undefined); 16 | }); 17 | 18 | setupBasicAuthTest(); 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /test/specs/helpers/spread.spec.js: -------------------------------------------------------------------------------- 1 | var spread = require('../../../lib/helpers/spread'); 2 | 3 | describe('helpers::spread', function () { 4 | it('should spread array to arguments', function () { 5 | var value = 0; 6 | spread(function (a, b) { 7 | value = a * b; 8 | })([5, 10]); 9 | 10 | expect(value).toEqual(50); 11 | }); 12 | 13 | it('should return callback result', function () { 14 | var value = spread(function (a, b) { 15 | return a * b; 16 | })([5, 10]); 17 | 18 | expect(value).toEqual(50); 19 | }); 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /test/manual/cors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | An alert should be shown with {"status":"ok"} 9 | 10 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/specs/cancel/Cancel.spec.js: -------------------------------------------------------------------------------- 1 | var Cancel = require('../../../lib/cancel/Cancel'); 2 | 3 | describe('Cancel', function() { 4 | describe('toString', function() { 5 | it('returns correct result when message is not specified', function() { 6 | var cancel = new Cancel(); 7 | expect(cancel.toString()).toBe('Cancel'); 8 | }); 9 | 10 | it('returns correct result when message is specified', function() { 11 | var cancel = new Cancel('Operation has been canceled.'); 12 | expect(cancel.toString()).toBe('Cancel: Operation has been canceled.'); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/manual/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | An alert should be shown with the {"name":"axios"} 9 | 10 | 11 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/helpers/isAbsoluteURL.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Determines whether the specified URL is absolute 5 | * 6 | * @param {string} url The URL to test 7 | * @returns {boolean} True if the specified URL is absolute, otherwise false 8 | */ 9 | module.exports = function isAbsoluteURL(url) { 10 | // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). 11 | // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed 12 | // by any combination of letters, digits, plus, period, or hyphen. 13 | return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); 14 | }; 15 | -------------------------------------------------------------------------------- /lib/core/transformData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | /** 6 | * Transform the data for a request or a response 7 | * 8 | * @param {Object|String} data The data to be transformed 9 | * @param {Array} headers The headers for the request or response 10 | * @param {Array|Function} fns A single function or Array of functions 11 | * @returns {*} The resulting transformed data 12 | */ 13 | module.exports = function transformData(data, headers, fns) { 14 | /*eslint no-param-reassign:0*/ 15 | utils.forEach(fns, function transform(fn) { 16 | data = fn(data, headers); 17 | }); 18 | 19 | return data; 20 | }; 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Instructions 4 | 5 | Please read and follow this instructions before creating and submitting a pull request: 6 | 7 | - Create an issue explaining the feature. It could save you some effort in case we don't consider it should be included in axios. 8 | - If you're fixing a bug, try to commit the failing test/s and the code fixing it in different commits. 9 | - Ensure you're following our [contributing guide](https://github.com/axios/axios/blob/master/CONTRIBUTING.md). 10 | 11 | *^^^ Delete the instructions before submitting the pull request ^^^* 12 | 13 | Describe your pull request here. 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | email: 5 | on_failure: change 6 | on_success: never 7 | before_script: 8 | - "export DISPLAY=:99.0" 9 | - "sh -e /etc/init.d/xvfb start" 10 | - sleep 3 # give xvfb some time to start 11 | after_success: 12 | - npm run coveralls 13 | env: 14 | global: 15 | - secure: LlXIBEaBLgJznkHWfTV6aftkGoBjH2vik4ZQhKq4k5pvoPLD+n5n28+0bjwlzDIHUdHb+n2YXtyM2PGvGzuqwltV+UY1gu0uG2RNR+5CBsp0pOr0FfGXK6YMXn0BYER6tGYIhaG7ElHBEO0SLcQeQV/xN/m3leyawbKEMBUGizU= 16 | - secure: XbXYzVddHJSVdbJRd/YtsdNu6Wlgx3pXvpuBpg9qBc3TytAF4LzhJNI8u1p4D1Gn8wANlxv1GNgEgkecxbzlTPST+mUrd6KlPLa1+Cmffgajr4oQjsh9ILKMe5Haqx8FOVrPK/leB1mi52liNLlkuo3/BK2r/tC2kMji+2zbses= 17 | -------------------------------------------------------------------------------- /lib/core/enhanceError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Update an Error with the specified config, error code, and response. 5 | * 6 | * @param {Error} error The error to update. 7 | * @param {Object} config The config. 8 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 9 | * @param {Object} [request] The request. 10 | * @param {Object} [response] The response. 11 | * @returns {Error} The error. 12 | */ 13 | module.exports = function enhanceError(error, config, code, request, response) { 14 | error.config = config; 15 | if (code) { 16 | error.code = code; 17 | } 18 | error.request = request; 19 | error.response = response; 20 | return error; 21 | }; 22 | -------------------------------------------------------------------------------- /lib/helpers/spread.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Syntactic sugar for invoking a function and expanding an array for arguments. 5 | * 6 | * Common use case would be to use `Function.prototype.apply`. 7 | * 8 | * ```js 9 | * function f(x, y, z) {} 10 | * var args = [1, 2, 3]; 11 | * f.apply(null, args); 12 | * ``` 13 | * 14 | * With `spread` this example can be re-written. 15 | * 16 | * ```js 17 | * spread(function(x, y, z) {})([1, 2, 3]); 18 | * ``` 19 | * 20 | * @param {Function} callback 21 | * @returns {Function} 22 | */ 23 | module.exports = function spread(callback) { 24 | return function wrap(arr) { 25 | return callback.apply(null, arr); 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/core/createError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var enhanceError = require('./enhanceError'); 4 | 5 | /** 6 | * Create an Error with the specified message, config, error code, request and response. 7 | * 8 | * @param {string} message The error message. 9 | * @param {Object} config The config. 10 | * @param {string} [code] The error code (for example, 'ECONNABORTED'). 11 | * @param {Object} [request] The request. 12 | * @param {Object} [response] The response. 13 | * @returns {Error} The created error. 14 | */ 15 | module.exports = function createError(message, config, code, request, response) { 16 | var error = new Error(message); 17 | return enhanceError(error, config, code, request, response); 18 | }; 19 | -------------------------------------------------------------------------------- /test/specs/core/createError.spec.js: -------------------------------------------------------------------------------- 1 | var createError = require('../../../lib/core/createError'); 2 | 3 | describe('core::createError', function() { 4 | it('should create an Error with message, config, code, request and response', function() { 5 | var request = { path: '/foo' }; 6 | var response = { status: 200, data: { foo: 'bar' } }; 7 | var error = createError('Boom!', { foo: 'bar' }, 'ESOMETHING', request, response); 8 | expect(error instanceof Error).toBe(true); 9 | expect(error.message).toBe('Boom!'); 10 | expect(error.config).toEqual({ foo: 'bar' }); 11 | expect(error.code).toBe('ESOMETHING'); 12 | expect(error.request).toBe(request); 13 | expect(error.response).toBe(response); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/specs/helpers/btoa.spec.js: -------------------------------------------------------------------------------- 1 | var __btoa = require('../../../lib/helpers/btoa'); 2 | 3 | describe('btoa polyfill', function () { 4 | it('should behave the same as native window.btoa', function () { 5 | // btoa doesn't exist in IE8/9 6 | if (isOldIE && typeof Int8Array === 'undefined') { 7 | return; 8 | } 9 | 10 | var data = 'Hello, world'; 11 | expect(__btoa(data)).toEqual(window.btoa(data)); 12 | }); 13 | 14 | it('should throw an error if char is out of range 0xFF', function () { 15 | var err; 16 | var data = 'I ♡ Unicode!'; 17 | 18 | try { 19 | __btoa(data); 20 | } catch (e) { 21 | err = e; 22 | } 23 | 24 | validateInvalidCharacterError(err); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /examples/get/server.js: -------------------------------------------------------------------------------- 1 | var people = [ 2 | { 3 | "name": "Matt Zabriskie", 4 | "github": "mzabriskie", 5 | "twitter": "mzabriskie", 6 | "avatar": "199035" 7 | }, 8 | { 9 | "name": "Ryan Florence", 10 | "github": "rpflorence", 11 | "twitter": "ryanflorence", 12 | "avatar": "100200" 13 | }, 14 | { 15 | "name": "Kent C. Dodds", 16 | "github": "kentcdodds", 17 | "twitter": "kentcdodds", 18 | "avatar": "1500684" 19 | }, 20 | { 21 | "name": "Chris Esplin", 22 | "github": "deltaepsilon", 23 | "twitter": "chrisesplin", 24 | "avatar": "878947" 25 | } 26 | ]; 27 | 28 | module.exports = function (req, res) { 29 | res.writeHead(200, { 30 | 'Content-Type': 'text/json' 31 | }); 32 | res.write(JSON.stringify(people)); 33 | res.end(); 34 | }; 35 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@contentful/axios", 3 | "main": "./dist/axios.js", 4 | "version": "0.18.0", 5 | "homepage": "https://github.com/axios/axios", 6 | "authors": [ 7 | "Matt Zabriskie" 8 | ], 9 | "description": "Promise based HTTP client for the browser and node.js", 10 | "moduleType": [ 11 | "amd", 12 | "globals" 13 | ], 14 | "keywords": [ 15 | "xhr", 16 | "http", 17 | "ajax", 18 | "promise", 19 | "node" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "*.iml", 25 | "examples", 26 | "lib", 27 | "node_modules", 28 | "sandbox", 29 | "test", 30 | "CONTRIBUTING.md", 31 | "COOKBOOK.md", 32 | "Gruntfile.js", 33 | "index.js", 34 | "karma.conf.js", 35 | "package.json", 36 | "webpack.*.js" 37 | ] 38 | } -------------------------------------------------------------------------------- /test/specs/helpers/normalizeHeaderName.spec.js: -------------------------------------------------------------------------------- 1 | var normalizeHeaderName = require('../../../lib/helpers/normalizeHeaderName'); 2 | 3 | describe('helpers::normalizeHeaderName', function () { 4 | it('should normalize matching header name', function () { 5 | var headers = { 6 | 'conTenT-Type': 'foo/bar', 7 | }; 8 | normalizeHeaderName(headers, 'Content-Type'); 9 | expect(headers['Content-Type']).toBe('foo/bar'); 10 | expect(headers['conTenT-Type']).toBeUndefined(); 11 | }); 12 | 13 | it('should not change non-matching header name', function () { 14 | var headers = { 15 | 'content-type': 'foo/bar', 16 | }; 17 | normalizeHeaderName(headers, 'Content-Length'); 18 | expect(headers['content-type']).toBe('foo/bar'); 19 | expect(headers['Content-Length']).toBeUndefined(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/specs/core/enhanceError.spec.js: -------------------------------------------------------------------------------- 1 | var enhanceError = require('../../../lib/core/enhanceError'); 2 | 3 | describe('core::enhanceError', function() { 4 | it('should add config, config, request and response to error', function() { 5 | var error = new Error('Boom!'); 6 | var request = { path: '/foo' }; 7 | var response = { status: 200, data: { foo: 'bar' } }; 8 | 9 | enhanceError(error, { foo: 'bar' }, 'ESOMETHING', request, response); 10 | expect(error.config).toEqual({ foo: 'bar' }); 11 | expect(error.code).toBe('ESOMETHING'); 12 | expect(error.request).toBe(request); 13 | expect(error.response).toBe(response); 14 | }); 15 | 16 | it('should return error', function() { 17 | var error = new Error('Boom!'); 18 | expect(enhanceError(error, { foo: 'bar' }, 'ESOMETHING')).toBe(error); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/specs/core/transformData.spec.js: -------------------------------------------------------------------------------- 1 | var transformData = require('../../../lib/core/transformData'); 2 | 3 | describe('core::transformData', function () { 4 | it('should support a single transformer', function () { 5 | var data; 6 | data = transformData(data, null, function (data) { 7 | data = 'foo'; 8 | return data; 9 | }); 10 | 11 | expect(data).toEqual('foo'); 12 | }); 13 | 14 | it('should support an array of transformers', function () { 15 | var data = ''; 16 | data = transformData(data, null, [function (data) { 17 | data += 'f'; 18 | return data; 19 | }, function (data) { 20 | data += 'o'; 21 | return data; 22 | }, function (data) { 23 | data += 'o'; 24 | return data; 25 | }]); 26 | 27 | expect(data).toEqual('foo'); 28 | }); 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /lib/helpers/deprecatedMethod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*eslint no-console:0*/ 4 | 5 | /** 6 | * Supply a warning to the developer that a method they are using 7 | * has been deprecated. 8 | * 9 | * @param {string} method The name of the deprecated method 10 | * @param {string} [instead] The alternate method to use if applicable 11 | * @param {string} [docs] The documentation URL to get further details 12 | */ 13 | module.exports = function deprecatedMethod(method, instead, docs) { 14 | try { 15 | console.warn( 16 | 'DEPRECATED method `' + method + '`.' + 17 | (instead ? ' Use `' + instead + '` instead.' : '') + 18 | ' This method will be removed in a future release.'); 19 | 20 | if (docs) { 21 | console.warn('For more information about usage see ' + docs); 22 | } 23 | } catch (e) { /* Ignore */ } 24 | }; 25 | -------------------------------------------------------------------------------- /lib/core/settle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var createError = require('./createError'); 4 | 5 | /** 6 | * Resolve or reject a Promise based on response status. 7 | * 8 | * @param {Function} resolve A function that resolves the promise. 9 | * @param {Function} reject A function that rejects the promise. 10 | * @param {object} response The response. 11 | */ 12 | module.exports = function settle(resolve, reject, response) { 13 | var validateStatus = response.config.validateStatus; 14 | // Note: status is not exposed by XDomainRequest 15 | if (!response.status || !validateStatus || validateStatus(response.status)) { 16 | resolve(response); 17 | } else { 18 | reject(createError( 19 | 'Request failed with status code ' + response.status, 20 | response.config, 21 | null, 22 | response.request, 23 | response 24 | )); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /test/specs/utils/extend.spec.js: -------------------------------------------------------------------------------- 1 | var extend = require('../../../lib/utils').extend; 2 | 3 | describe('utils::extend', function () { 4 | it('should be mutable', function () { 5 | var a = {}; 6 | var b = {foo: 123}; 7 | 8 | extend(a, b); 9 | 10 | expect(a.foo).toEqual(b.foo); 11 | }); 12 | 13 | it('should extend properties', function () { 14 | var a = {foo: 123, bar: 456}; 15 | var b = {bar: 789}; 16 | 17 | a = extend(a, b); 18 | 19 | expect(a.foo).toEqual(123); 20 | expect(a.bar).toEqual(789); 21 | }); 22 | 23 | it('should bind to thisArg', function () { 24 | var a = {}; 25 | var b = {getFoo: function getFoo() { return this.foo; }}; 26 | var thisArg = { foo: 'barbaz' }; 27 | 28 | extend(a, b, thisArg); 29 | 30 | expect(typeof a.getFoo).toEqual('function'); 31 | expect(a.getFoo()).toEqual(thisArg.foo); 32 | }); 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /test/specs/helpers/isAbsoluteURL.spec.js: -------------------------------------------------------------------------------- 1 | var isAbsoluteURL = require('../../../lib/helpers/isAbsoluteURL'); 2 | 3 | describe('helpers::isAbsoluteURL', function () { 4 | it('should return true if URL begins with valid scheme name', function () { 5 | expect(isAbsoluteURL('https://api.github.com/users')).toBe(true); 6 | expect(isAbsoluteURL('custom-scheme-v1.0://example.com/')).toBe(true); 7 | expect(isAbsoluteURL('HTTP://example.com/')).toBe(true); 8 | }); 9 | 10 | it('should return false if URL begins with invalid scheme name', function () { 11 | expect(isAbsoluteURL('123://example.com/')).toBe(false); 12 | expect(isAbsoluteURL('!valid://example.com/')).toBe(false); 13 | }); 14 | 15 | it('should return true if URL is protocol-relative', function () { 16 | expect(isAbsoluteURL('//example.com/')).toBe(true); 17 | }); 18 | 19 | it('should return false if URL is relative', function () { 20 | expect(isAbsoluteURL('/foo')).toBe(false); 21 | expect(isAbsoluteURL('foo')).toBe(false); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var config = {}; 3 | 4 | function generateConfig(name) { 5 | var uglify = name.indexOf('min') > -1; 6 | var config = { 7 | entry: './index.js', 8 | output: { 9 | path: 'dist/', 10 | filename: name + '.js', 11 | sourceMapFilename: name + '.map', 12 | library: 'axios', 13 | libraryTarget: 'umd' 14 | }, 15 | node: { 16 | process: false 17 | }, 18 | devtool: 'source-map' 19 | }; 20 | 21 | config.plugins = [ 22 | new webpack.DefinePlugin({ 23 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 24 | }) 25 | ]; 26 | 27 | if (uglify) { 28 | config.plugins.push( 29 | new webpack.optimize.UglifyJsPlugin({ 30 | compressor: { 31 | warnings: false 32 | } 33 | }) 34 | ); 35 | } 36 | 37 | return config; 38 | } 39 | 40 | ['axios', 'axios.min'].forEach(function (key) { 41 | config[key] = generateConfig(key); 42 | }); 43 | 44 | module.exports = config; 45 | -------------------------------------------------------------------------------- /test/specs/helpers/combineURLs.spec.js: -------------------------------------------------------------------------------- 1 | var combineURLs = require('../../../lib/helpers/combineURLs'); 2 | 3 | describe('helpers::combineURLs', function () { 4 | it('should combine URLs', function () { 5 | expect(combineURLs('https://api.github.com', '/users')).toBe('https://api.github.com/users'); 6 | }); 7 | 8 | it('should remove duplicate slashes', function () { 9 | expect(combineURLs('https://api.github.com/', '/users')).toBe('https://api.github.com/users'); 10 | }); 11 | 12 | it('should insert missing slash', function () { 13 | expect(combineURLs('https://api.github.com', 'users')).toBe('https://api.github.com/users'); 14 | }); 15 | 16 | it('should not insert slash when relative url missing/empty', function () { 17 | expect(combineURLs('https://api.github.com/users', '')).toBe('https://api.github.com/users'); 18 | }); 19 | 20 | it('should allow a single slash for relative url', function () { 21 | expect(combineURLs('https://api.github.com/users', '/')).toBe('https://api.github.com/users/'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /lib/adapters/README.md: -------------------------------------------------------------------------------- 1 | # axios // adapters 2 | 3 | The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received. 4 | 5 | ## Example 6 | 7 | ```js 8 | var settle = require('./../core/settle'); 9 | 10 | module.exports = function myAdapter(config) { 11 | // At this point: 12 | // - config has been merged with defaults 13 | // - request transformers have already run 14 | // - request interceptors have already run 15 | 16 | // Make the request using config provided 17 | // Upon response settle the Promise 18 | 19 | return new Promise(function(resolve, reject) { 20 | 21 | var response = { 22 | data: responseData, 23 | status: request.status, 24 | statusText: request.statusText, 25 | headers: responseHeaders, 26 | config: config, 27 | request: request 28 | }; 29 | 30 | settle(resolve, reject, response); 31 | 32 | // From here: 33 | // - response transformers will run 34 | // - response interceptors will run 35 | }); 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /test/specs/utils/merge.spec.js: -------------------------------------------------------------------------------- 1 | var merge = require('../../../lib/utils').merge; 2 | 3 | describe('utils::merge', function () { 4 | it('should be immutable', function () { 5 | var a = {}; 6 | var b = {foo: 123}; 7 | var c = {bar: 456}; 8 | 9 | merge(a, b, c); 10 | 11 | expect(typeof a.foo).toEqual('undefined'); 12 | expect(typeof a.bar).toEqual('undefined'); 13 | expect(typeof b.bar).toEqual('undefined'); 14 | expect(typeof c.foo).toEqual('undefined'); 15 | }); 16 | 17 | it('should merge properties', function () { 18 | var a = {foo: 123}; 19 | var b = {bar: 456}; 20 | var c = {foo: 789}; 21 | var d = merge(a, b, c); 22 | 23 | expect(d.foo).toEqual(789); 24 | expect(d.bar).toEqual(456); 25 | }); 26 | 27 | it('should merge recursively', function () { 28 | var a = {foo: {bar: 123}}; 29 | var b = {foo: {baz: 456}, bar: {qux: 789}}; 30 | 31 | expect(merge(a, b)).toEqual({ 32 | foo: { 33 | bar: 123, 34 | baz: 456 35 | }, 36 | bar: { 37 | qux: 789 38 | } 39 | }); 40 | }); 41 | }); 42 | 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Matt Zabriskie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/helpers/btoa.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // btoa polyfill for IE<10 courtesy https://github.com/davidchambers/Base64.js 4 | 5 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 6 | 7 | function E() { 8 | this.message = 'String contains an invalid character'; 9 | } 10 | E.prototype = new Error; 11 | E.prototype.code = 5; 12 | E.prototype.name = 'InvalidCharacterError'; 13 | 14 | function btoa(input) { 15 | var str = String(input); 16 | var output = ''; 17 | for ( 18 | // initialize result and counter 19 | var block, charCode, idx = 0, map = chars; 20 | // if the next str index does not exist: 21 | // change the mapping table to "=" 22 | // check if d has no fractional digits 23 | str.charAt(idx | 0) || (map = '=', idx % 1); 24 | // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 25 | output += map.charAt(63 & block >> 8 - idx % 1 * 8) 26 | ) { 27 | charCode = str.charCodeAt(idx += 3 / 4); 28 | if (charCode > 0xFF) { 29 | throw new E(); 30 | } 31 | block = block << 8 | charCode; 32 | } 33 | return output; 34 | } 35 | 36 | module.exports = btoa; 37 | -------------------------------------------------------------------------------- /examples/amd/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AMD 5 | 6 | 7 | 8 |

AMD

9 | 10 |
11 |

User

12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/specs/helpers/cookies.spec.js: -------------------------------------------------------------------------------- 1 | var cookies = require('../../../lib/helpers/cookies'); 2 | 3 | describe('helpers::cookies', function () { 4 | afterEach(function () { 5 | // Remove all the cookies 6 | var expires = Date.now() - (60 * 60 * 24 * 7); 7 | document.cookie.split(';').map(function (cookie) { 8 | return cookie.split('=')[0]; 9 | }).forEach(function (name) { 10 | document.cookie = name + '=; expires=' + new Date(expires).toGMTString(); 11 | }); 12 | }); 13 | 14 | it('should write cookies', function () { 15 | cookies.write('foo', 'baz'); 16 | expect(document.cookie).toEqual('foo=baz'); 17 | }); 18 | 19 | it('should read cookies', function () { 20 | cookies.write('foo', 'abc'); 21 | cookies.write('bar', 'def'); 22 | expect(cookies.read('foo')).toEqual('abc'); 23 | expect(cookies.read('bar')).toEqual('def'); 24 | }); 25 | 26 | it('should remove cookies', function () { 27 | cookies.write('foo', 'bar'); 28 | cookies.remove('foo'); 29 | expect(cookies.read('foo')).toEqual(null); 30 | }); 31 | 32 | it('should uri encode values', function () { 33 | cookies.write('foo', 'bar baz%'); 34 | expect(document.cookie).toEqual('foo=bar%20baz%25'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /examples/post/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios - post example 5 | 6 | 7 | 8 |

axios.post

9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/specs/utils/forEach.spec.js: -------------------------------------------------------------------------------- 1 | var forEach = require('../../../lib/utils').forEach; 2 | 3 | describe('utils::forEach', function () { 4 | it('should loop over an array', function () { 5 | var sum = 0; 6 | 7 | forEach([1, 2, 3, 4, 5], function (val) { 8 | sum += val; 9 | }); 10 | 11 | expect(sum).toEqual(15); 12 | }); 13 | 14 | it('should loop over object keys', function () { 15 | var keys = ''; 16 | var vals = 0; 17 | var obj = { 18 | b: 1, 19 | a: 2, 20 | r: 3 21 | }; 22 | 23 | forEach(obj, function (v, k) { 24 | keys += k; 25 | vals += v; 26 | }); 27 | 28 | expect(keys).toEqual('bar'); 29 | expect(vals).toEqual(6); 30 | }); 31 | 32 | it('should handle undefined gracefully', function () { 33 | var count = 0; 34 | 35 | forEach(undefined, function () { 36 | count++; 37 | }); 38 | 39 | expect(count).toEqual(0); 40 | }); 41 | 42 | it('should make an array out of non-array argument', function () { 43 | var count = 0; 44 | 45 | forEach(function () {}, function () { 46 | count++; 47 | }); 48 | 49 | expect(count).toEqual(1); 50 | }); 51 | 52 | it('should handle non object prototype gracefully', function () { 53 | var count = 0; 54 | var data = Object.create(null); 55 | data.foo = 'bar' 56 | 57 | forEach(data, function () { 58 | count++; 59 | }); 60 | 61 | expect(count).toEqual(1); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /examples/get/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios - get example 5 | 6 | 7 | 8 |

axios.get

9 | 10 | 11 | 12 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /lib/core/InterceptorManager.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | function InterceptorManager() { 6 | this.handlers = []; 7 | } 8 | 9 | /** 10 | * Add a new interceptor to the stack 11 | * 12 | * @param {Function} fulfilled The function to handle `then` for a `Promise` 13 | * @param {Function} rejected The function to handle `reject` for a `Promise` 14 | * 15 | * @return {Number} An ID used to remove interceptor later 16 | */ 17 | InterceptorManager.prototype.use = function use(fulfilled, rejected) { 18 | this.handlers.push({ 19 | fulfilled: fulfilled, 20 | rejected: rejected 21 | }); 22 | return this.handlers.length - 1; 23 | }; 24 | 25 | /** 26 | * Remove an interceptor from the stack 27 | * 28 | * @param {Number} id The ID that was returned by `use` 29 | */ 30 | InterceptorManager.prototype.eject = function eject(id) { 31 | if (this.handlers[id]) { 32 | this.handlers[id] = null; 33 | } 34 | }; 35 | 36 | /** 37 | * Iterate over all the registered interceptors 38 | * 39 | * This method is particularly useful for skipping over any 40 | * interceptors that may have become `null` calling `eject`. 41 | * 42 | * @param {Function} fn The function to call for each interceptor 43 | */ 44 | InterceptorManager.prototype.forEach = function forEach(fn) { 45 | utils.forEach(this.handlers, function forEachHandler(h) { 46 | if (h !== null) { 47 | fn(h); 48 | } 49 | }); 50 | }; 51 | 52 | module.exports = InterceptorManager; 53 | -------------------------------------------------------------------------------- /lib/cancel/CancelToken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Cancel = require('./Cancel'); 4 | 5 | /** 6 | * A `CancelToken` is an object that can be used to request cancellation of an operation. 7 | * 8 | * @class 9 | * @param {Function} executor The executor function. 10 | */ 11 | function CancelToken(executor) { 12 | if (typeof executor !== 'function') { 13 | throw new TypeError('executor must be a function.'); 14 | } 15 | 16 | var resolvePromise; 17 | this.promise = new Promise(function promiseExecutor(resolve) { 18 | resolvePromise = resolve; 19 | }); 20 | 21 | var token = this; 22 | executor(function cancel(message) { 23 | if (token.reason) { 24 | // Cancellation has already been requested 25 | return; 26 | } 27 | 28 | token.reason = new Cancel(message); 29 | resolvePromise(token.reason); 30 | }); 31 | } 32 | 33 | /** 34 | * Throws a `Cancel` if cancellation has been requested. 35 | */ 36 | CancelToken.prototype.throwIfRequested = function throwIfRequested() { 37 | if (this.reason) { 38 | throw this.reason; 39 | } 40 | }; 41 | 42 | /** 43 | * Returns an object that contains a new `CancelToken` and a function that, when called, 44 | * cancels the `CancelToken`. 45 | */ 46 | CancelToken.source = function source() { 47 | var cancel; 48 | var token = new CancelToken(function executor(c) { 49 | cancel = c; 50 | }); 51 | return { 52 | token: token, 53 | cancel: cancel 54 | }; 55 | }; 56 | 57 | module.exports = CancelToken; 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Instructions 4 | 5 | Please read and follow this instructions before submitting an issue: 6 | 7 | - Read all our documentation, specially the [README](https://github.com/axios/axios/blob/master/README.md). It may contain information that helps you solve your issue. 8 | - Ensure your issue isn't already [reported](https://github.com/axios/axios/issues?utf8=%E2%9C%93&q=is%3Aissue). 9 | - If you aren't sure that the issue is caused by axios or you just need for help, please use [Stack Overflow](https://stackoverflow.com/questions/tagged/axios) or [our chat](https://gitter.im/mzabriskie/axios). 10 | - If you're reporting a bug, ensure it isn't already fixed in the latest axios version. 11 | - If you need a new feature there's a chance it's already implemented in a [library](https://github.com/axios/axios/blob/master/ECOSYSTEM.md) or you can implement it using [interceptors](https://github.com/axios/axios#interceptors). 12 | 13 | *^^^ Delete the instructions before submitting the issue ^^^* 14 | 15 | #### Summary 16 | 17 | Describe your issue here, including as much detail as necessary. 18 | 19 | If you're reporting a bug, include the relevant code and stack traces to debug it (removing any private information). 20 | 21 | If you're requesting a feature, include some context and examples of code using it. 22 | 23 | #### Context 24 | 25 | - axios version: *e.g.: v0.16.0* 26 | - Environment: *e.g.: node v6.9.4, chrome 54, windows 7* 27 | -------------------------------------------------------------------------------- /examples/all/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios - all example 5 | 6 | 7 | 8 |

axios.all

9 | 10 |
11 |

User

12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 |

Orgs

20 | 21 |
22 | 23 | 24 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/axios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var bind = require('./helpers/bind'); 5 | var Axios = require('./core/Axios'); 6 | var defaults = require('./defaults'); 7 | 8 | /** 9 | * Create an instance of Axios 10 | * 11 | * @param {Object} defaultConfig The default config for the instance 12 | * @return {Axios} A new instance of Axios 13 | */ 14 | function createInstance(defaultConfig) { 15 | var context = new Axios(defaultConfig); 16 | var instance = bind(Axios.prototype.request, context); 17 | 18 | // Copy axios.prototype to instance 19 | utils.extend(instance, Axios.prototype, context); 20 | 21 | // Copy context to instance 22 | utils.extend(instance, context); 23 | 24 | return instance; 25 | } 26 | 27 | // Create the default instance to be exported 28 | var axios = createInstance(defaults); 29 | 30 | // Expose Axios class to allow class inheritance 31 | axios.Axios = Axios; 32 | 33 | // Factory for creating new instances 34 | axios.create = function create(instanceConfig) { 35 | return createInstance(utils.merge(defaults, instanceConfig)); 36 | }; 37 | 38 | // Expose Cancel & CancelToken 39 | axios.Cancel = require('./cancel/Cancel'); 40 | axios.CancelToken = require('./cancel/CancelToken'); 41 | axios.isCancel = require('./cancel/isCancel'); 42 | 43 | // Expose all/spread 44 | axios.all = function all(promises) { 45 | return Promise.all(promises); 46 | }; 47 | axios.spread = require('./helpers/spread'); 48 | 49 | module.exports = axios; 50 | 51 | // Allow use of default import syntax in TypeScript 52 | module.exports.default = axios; 53 | -------------------------------------------------------------------------------- /lib/helpers/cookies.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | module.exports = ( 6 | utils.isStandardBrowserEnv() ? 7 | 8 | // Standard browser envs support document.cookie 9 | (function standardBrowserEnv() { 10 | return { 11 | write: function write(name, value, expires, path, domain, secure) { 12 | var cookie = []; 13 | cookie.push(name + '=' + encodeURIComponent(value)); 14 | 15 | if (utils.isNumber(expires)) { 16 | cookie.push('expires=' + new Date(expires).toGMTString()); 17 | } 18 | 19 | if (utils.isString(path)) { 20 | cookie.push('path=' + path); 21 | } 22 | 23 | if (utils.isString(domain)) { 24 | cookie.push('domain=' + domain); 25 | } 26 | 27 | if (secure === true) { 28 | cookie.push('secure'); 29 | } 30 | 31 | document.cookie = cookie.join('; '); 32 | }, 33 | 34 | read: function read(name) { 35 | var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)')); 36 | return (match ? decodeURIComponent(match[3]) : null); 37 | }, 38 | 39 | remove: function remove(name) { 40 | this.write(name, '', Date.now() - 86400000); 41 | } 42 | }; 43 | })() : 44 | 45 | // Non standard browser env (web workers, react-native) lack needed support. 46 | (function nonStandardBrowserEnv() { 47 | return { 48 | write: function write() {}, 49 | read: function read() { return null; }, 50 | remove: function remove() {} 51 | }; 52 | })() 53 | ); 54 | -------------------------------------------------------------------------------- /test/specs/helpers/parseHeaders.spec.js: -------------------------------------------------------------------------------- 1 | var parseHeaders = require('../../../lib/helpers/parseHeaders'); 2 | 3 | describe('helpers::parseHeaders', function () { 4 | it('should parse headers', function () { 5 | var date = new Date(); 6 | var parsed = parseHeaders( 7 | 'Date: ' + date.toISOString() + '\n' + 8 | 'Content-Type: application/json\n' + 9 | 'Connection: keep-alive\n' + 10 | 'Transfer-Encoding: chunked' 11 | ); 12 | 13 | expect(parsed['date']).toEqual(date.toISOString()); 14 | expect(parsed['content-type']).toEqual('application/json'); 15 | expect(parsed['connection']).toEqual('keep-alive'); 16 | expect(parsed['transfer-encoding']).toEqual('chunked'); 17 | }); 18 | 19 | it('should use array for set-cookie', function() { 20 | var parsedZero = parseHeaders(''); 21 | var parsedSingle = parseHeaders( 22 | 'Set-Cookie: key=val;' 23 | ); 24 | var parsedMulti = parseHeaders( 25 | 'Set-Cookie: key=val;\n' + 26 | 'Set-Cookie: key2=val2;\n' 27 | ); 28 | 29 | expect(parsedZero['set-cookie']).toBeUndefined(); 30 | expect(parsedSingle['set-cookie']).toEqual(['key=val;']); 31 | expect(parsedMulti['set-cookie']).toEqual(['key=val;', 'key2=val2;']); 32 | }); 33 | 34 | it('should handle duplicates', function() { 35 | var parsed = parseHeaders( 36 | 'Age: age-a\n' + // age is in ignore duplicates blacklist 37 | 'Age: age-b\n' + 38 | 'Foo: foo-a\n' + 39 | 'Foo: foo-b\n' 40 | ); 41 | 42 | expect(parsed['age']).toEqual('age-a'); 43 | expect(parsed['foo']).toEqual('foo-a, foo-b'); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /lib/helpers/parseHeaders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | // Headers whose duplicates are ignored by node 6 | // c.f. https://nodejs.org/api/http.html#http_message_headers 7 | var ignoreDuplicateOf = [ 8 | 'age', 'authorization', 'content-length', 'content-type', 'etag', 9 | 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', 10 | 'last-modified', 'location', 'max-forwards', 'proxy-authorization', 11 | 'referer', 'retry-after', 'user-agent' 12 | ]; 13 | 14 | /** 15 | * Parse headers into an object 16 | * 17 | * ``` 18 | * Date: Wed, 27 Aug 2014 08:58:49 GMT 19 | * Content-Type: application/json 20 | * Connection: keep-alive 21 | * Transfer-Encoding: chunked 22 | * ``` 23 | * 24 | * @param {String} headers Headers needing to be parsed 25 | * @returns {Object} Headers parsed into an object 26 | */ 27 | module.exports = function parseHeaders(headers) { 28 | var parsed = {}; 29 | var key; 30 | var val; 31 | var i; 32 | 33 | if (!headers) { return parsed; } 34 | 35 | utils.forEach(headers.split('\n'), function parser(line) { 36 | i = line.indexOf(':'); 37 | key = utils.trim(line.substr(0, i)).toLowerCase(); 38 | val = utils.trim(line.substr(i + 1)); 39 | 40 | if (key) { 41 | if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) { 42 | return; 43 | } 44 | if (key === 'set-cookie') { 45 | parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]); 46 | } else { 47 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; 48 | } 49 | } 50 | }); 51 | 52 | return parsed; 53 | }; 54 | -------------------------------------------------------------------------------- /ECOSYSTEM.md: -------------------------------------------------------------------------------- 1 | # Ecosystem 2 | 3 | This is a list of axios related libraries and resources. If you have a suggestion on what to add, please don't hesitate to submit a PR. 4 | 5 | ## Libraries 6 | 7 | * [moxios](https://github.com/axios/moxios) - Mock axios requests for testing 8 | * [axios-response-logger](https://github.com/srph/axios-response-logger) - Axios interceptor which logs responses 9 | * [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) — Axios adapter that allows to easily mock requests 10 | * [redux-axios-middleware](https://github.com/svrcekmichal/redux-axios-middleware) - Redux middleware for fetching data with axios HTTP client 11 | * [axios-vcr](https://github.com/nettofarah/axios-vcr) - 📼 Record and Replay Axios requests 12 | * [@3846masa/axios-cookiejar-support](https://github.com/3846masa/axios-cookiejar-support) - Add tough-cookie support to axios 13 | * [axios-debug-log](https://github.com/Gerhut/axios-debug-log) - Axios interceptor of logging requests & responses by debug. 14 | * [axios-method-override](https://github.com/jacobbuck/axios-method-override) - Axios http request method override plugin 15 | * [mocha-axios](https://github.com/jdrydn/mocha-axios) - Streamlined integration testing with Mocha & Axios 16 | * [axiosist](https://github.com/Gerhut/axiosist) - Axios based supertest: convert node.js request handler to axios adapter, used for node.js server unit test. 17 | * [axios-cache-plugin](https://github.com/jin5354/axios-cache-plugin) - Help you cache GET request when using axios. 18 | * [axios-extensions](https://github.com/kuitos/axios-extensions) - A collection of axios extensions, including throttle and cache GET request plugin. 19 | -------------------------------------------------------------------------------- /examples/transform-response/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios - transform response example 5 | 6 | 7 | 8 |

transformResponse

9 | 10 |
11 | 12 |
13 |
14 | Created:
15 | Updated: 16 |
17 |
18 | 19 | 20 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/upload/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios - file upload example 5 | 6 | 7 | 8 |

file upload

9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/helpers/buildURL.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | function encode(val) { 6 | return encodeURIComponent(val). 7 | replace(/%40/gi, '@'). 8 | replace(/%3A/gi, ':'). 9 | replace(/%24/g, '$'). 10 | replace(/%2C/gi, ','). 11 | replace(/%20/g, '+'). 12 | replace(/%5B/gi, '['). 13 | replace(/%5D/gi, ']'); 14 | } 15 | 16 | /** 17 | * Build a URL by appending params to the end 18 | * 19 | * @param {string} url The base of the url (e.g., http://www.google.com) 20 | * @param {object} [params] The params to be appended 21 | * @returns {string} The formatted url 22 | */ 23 | module.exports = function buildURL(url, params, paramsSerializer) { 24 | /*eslint no-param-reassign:0*/ 25 | if (!params) { 26 | return url; 27 | } 28 | 29 | var serializedParams; 30 | if (paramsSerializer) { 31 | serializedParams = paramsSerializer(params); 32 | } else if (utils.isURLSearchParams(params)) { 33 | serializedParams = params.toString(); 34 | } else { 35 | var parts = []; 36 | 37 | utils.forEach(params, function serialize(val, key) { 38 | if (val === null || typeof val === 'undefined') { 39 | return; 40 | } 41 | 42 | if (utils.isArray(val)) { 43 | key = key + '[]'; 44 | } 45 | 46 | if (!utils.isArray(val)) { 47 | val = [val]; 48 | } 49 | 50 | utils.forEach(val, function parseValue(v) { 51 | if (utils.isDate(v)) { 52 | v = v.toISOString(); 53 | } else if (utils.isObject(v)) { 54 | v = JSON.stringify(v); 55 | } 56 | parts.push(encode(key) + '=' + encode(v)); 57 | }); 58 | }); 59 | 60 | serializedParams = parts.join('&'); 61 | } 62 | 63 | if (serializedParams) { 64 | url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; 65 | } 66 | 67 | return url; 68 | }; 69 | -------------------------------------------------------------------------------- /test/specs/promise.spec.js: -------------------------------------------------------------------------------- 1 | describe('promise', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | }); 9 | 10 | it('should provide succinct object to then', function (done) { 11 | var response; 12 | 13 | axios('/foo').then(function (r) { 14 | response = r; 15 | }); 16 | 17 | getAjaxRequest().then(function (request) { 18 | request.respondWith({ 19 | status: 200, 20 | responseText: '{"hello":"world"}' 21 | }); 22 | 23 | setTimeout(function () { 24 | expect(typeof response).toEqual('object'); 25 | expect(response.data.hello).toEqual('world'); 26 | expect(response.status).toEqual(200); 27 | expect(response.headers['content-type']).toEqual('application/json'); 28 | expect(response.config.url).toEqual('/foo'); 29 | done(); 30 | }, 100); 31 | }); 32 | }); 33 | 34 | it('should support all', function (done) { 35 | var fulfilled = false; 36 | 37 | axios.all([true, 123]).then(function () { 38 | fulfilled = true; 39 | }); 40 | 41 | setTimeout(function () { 42 | expect(fulfilled).toEqual(true); 43 | done(); 44 | }, 100); 45 | }); 46 | 47 | it('should support spread', function (done) { 48 | var sum = 0; 49 | var fulfilled = false; 50 | var result; 51 | 52 | axios 53 | .all([123, 456]) 54 | .then(axios.spread(function (a, b) { 55 | sum = a + b; 56 | fulfilled = true; 57 | return 'hello world'; 58 | })) 59 | .then(function (res) { 60 | result = res; 61 | }); 62 | 63 | setTimeout(function () { 64 | expect(fulfilled).toEqual(true); 65 | expect(sum).toEqual(123 + 456); 66 | expect(result).toEqual('hello world'); 67 | done(); 68 | }, 100); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /sandbox/server.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var url = require('url'); 3 | var path = require('path'); 4 | var http = require('http'); 5 | var server; 6 | 7 | function pipeFileToResponse(res, file, type) { 8 | if (type) { 9 | res.writeHead(200, { 10 | 'Content-Type': type 11 | }); 12 | } 13 | 14 | fs.createReadStream(path.join(__dirname, file)).pipe(res); 15 | } 16 | 17 | server = http.createServer(function (req, res) { 18 | req.setEncoding('utf8'); 19 | 20 | var parsed = url.parse(req.url, true); 21 | var pathname = parsed.pathname; 22 | 23 | console.log('[' + new Date() + ']', req.method, pathname); 24 | 25 | if (pathname === '/') { 26 | pathname = '/index.html'; 27 | } 28 | 29 | if (pathname === '/index.html') { 30 | pipeFileToResponse(res, './client.html'); 31 | } else if (pathname === '/axios.js') { 32 | pipeFileToResponse(res, '../dist/axios.js', 'text/javascript'); 33 | } else if (pathname === '/axios.map') { 34 | pipeFileToResponse(res, '../dist/axios.map', 'text/javascript'); 35 | } else if (pathname === '/api') { 36 | var status; 37 | var result; 38 | var data = ''; 39 | 40 | req.on('data', function (chunk) { 41 | data += chunk; 42 | }); 43 | 44 | req.on('end', function () { 45 | try { 46 | status = 200; 47 | result = { 48 | url: req.url, 49 | data: data ? JSON.parse(data) : undefined, 50 | method: req.method, 51 | headers: req.headers 52 | }; 53 | } catch (e) { 54 | console.error('Error:', e.message); 55 | status = 400; 56 | result = { 57 | error: e.message 58 | }; 59 | } 60 | 61 | res.writeHead(status, { 62 | 'Content-Type': 'application/json' 63 | }); 64 | res.end(JSON.stringify(result)); 65 | }); 66 | } else { 67 | res.writeHead(404); 68 | res.end('

404 Not Found

'); 69 | } 70 | }); 71 | 72 | server.listen(3000); -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We are open to, and grateful for, any contributions made by the community. By contributing to axios, you agree to abide by the [code of conduct](https://github.com/axios/axios/blob/master/CODE_OF_CONDUCT.md). 4 | 5 | ### Code Style 6 | 7 | Please follow the [node style guide](https://github.com/felixge/node-style-guide). 8 | 9 | ### Commit Messages 10 | 11 | Commit messages should be verb based, using the following pattern: 12 | 13 | - `Fixing ...` 14 | - `Adding ...` 15 | - `Updating ...` 16 | - `Removing ...` 17 | 18 | ### Testing 19 | 20 | Please update the tests to reflect your code changes. Pull requests will not be accepted if they are failing on [Travis CI](https://travis-ci.org/mzabriskie/axios). 21 | 22 | ### Documentation 23 | 24 | Please update the docs accordingly so that there are no discrepencies between the API and the documentation. 25 | 26 | ### Developing 27 | 28 | - `grunt test` run the jasmine and nodeunit tests 29 | - `grunt build` run webpack and bundle the source 30 | - `grunt version` prepare the code for release 31 | - `grunt watch:test` watch for changes and run `test` 32 | - `grunt watch:build` watch for changes and run `build` 33 | 34 | Please don't include changes to `dist/` in your pull request. This should only be updated when releasing a new version. 35 | 36 | ### Releasing 37 | 38 | Releasing a new version is mostly automated. For now the [CHANGELOG](https://github.com/axios/axios/blob/master/CHANGELOG.md) requires being updated manually. Once this has been done run the commands below. Versions should follow [semantic versioning](http://semver.org/). 39 | 40 | - `npm version -m "Releasing %s"` 41 | - `npm publish` 42 | 43 | ### Running Examples 44 | 45 | Examples are included in part to allow manual testing. 46 | 47 | Running example 48 | 49 | ```bash 50 | $ npm run examples 51 | # Open 127.0.0.1:3000 52 | ``` 53 | 54 | Running sandbox in browser 55 | 56 | ```bash 57 | $ npm start 58 | # Open 127.0.0.1:3000 59 | ``` 60 | 61 | Running sandbox in terminal 62 | 63 | ```bash 64 | $ npm start 65 | $ node ./sandbox/client 66 | ``` 67 | -------------------------------------------------------------------------------- /test/specs/helpers/buildURL.spec.js: -------------------------------------------------------------------------------- 1 | var buildURL = require('../../../lib/helpers/buildURL'); 2 | var URLSearchParams = require('url-search-params'); 3 | 4 | describe('helpers::buildURL', function () { 5 | it('should support null params', function () { 6 | expect(buildURL('/foo')).toEqual('/foo'); 7 | }); 8 | 9 | it('should support params', function () { 10 | expect(buildURL('/foo', { 11 | foo: 'bar' 12 | })).toEqual('/foo?foo=bar'); 13 | }); 14 | 15 | it('should support object params', function () { 16 | expect(buildURL('/foo', { 17 | foo: { 18 | bar: 'baz' 19 | } 20 | })).toEqual('/foo?foo=' + encodeURI('{"bar":"baz"}')); 21 | }); 22 | 23 | it('should support date params', function () { 24 | var date = new Date(); 25 | 26 | expect(buildURL('/foo', { 27 | date: date 28 | })).toEqual('/foo?date=' + date.toISOString()); 29 | }); 30 | 31 | it('should support array params', function () { 32 | expect(buildURL('/foo', { 33 | foo: ['bar', 'baz'] 34 | })).toEqual('/foo?foo[]=bar&foo[]=baz'); 35 | }); 36 | 37 | it('should support special char params', function () { 38 | expect(buildURL('/foo', { 39 | foo: '@:$, ' 40 | })).toEqual('/foo?foo=@:$,+'); 41 | }); 42 | 43 | it('should support existing params', function () { 44 | expect(buildURL('/foo?foo=bar', { 45 | bar: 'baz' 46 | })).toEqual('/foo?foo=bar&bar=baz'); 47 | }); 48 | 49 | it('should support "length" parameter', function () { 50 | expect(buildURL('/foo', { 51 | query: 'bar', 52 | start: 0, 53 | length: 5 54 | })).toEqual('/foo?query=bar&start=0&length=5'); 55 | }); 56 | 57 | it('should use serializer if provided', function () { 58 | serializer = sinon.stub(); 59 | params = {foo: 'bar'}; 60 | serializer.returns('foo=bar'); 61 | expect(buildURL('/foo', params, serializer)).toEqual('/foo?foo=bar'); 62 | expect(serializer.calledOnce).toBe(true); 63 | expect(serializer.calledWith(params)).toBe(true); 64 | }); 65 | 66 | it('should support URLSearchParams', function () { 67 | expect(buildURL('/foo', new URLSearchParams('bar=baz'))).toEqual('/foo?bar=baz'); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/specs/options.spec.js: -------------------------------------------------------------------------------- 1 | describe('options', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | }); 9 | 10 | it('should default method to get', function (done) { 11 | axios('/foo'); 12 | 13 | getAjaxRequest().then(function (request) { 14 | expect(request.method).toBe('GET'); 15 | done(); 16 | }); 17 | }); 18 | 19 | it('should accept headers', function (done) { 20 | axios('/foo', { 21 | headers: { 22 | 'X-Requested-With': 'XMLHttpRequest' 23 | } 24 | }); 25 | 26 | getAjaxRequest().then(function (request) { 27 | expect(request.requestHeaders['X-Requested-With']).toEqual('XMLHttpRequest'); 28 | done(); 29 | }); 30 | }); 31 | 32 | it('should accept params', function (done) { 33 | axios('/foo', { 34 | params: { 35 | foo: 123, 36 | bar: 456 37 | } 38 | }); 39 | 40 | getAjaxRequest().then(function (request) { 41 | expect(request.url).toBe('/foo?foo=123&bar=456'); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should allow overriding default headers', function (done) { 47 | axios('/foo', { 48 | headers: { 49 | 'Accept': 'foo/bar' 50 | } 51 | }); 52 | 53 | getAjaxRequest().then(function (request) { 54 | expect(request.requestHeaders['Accept']).toEqual('foo/bar'); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('should accept base URL', function (done) { 60 | var instance = axios.create({ 61 | baseURL: 'http://test.com/' 62 | }); 63 | 64 | instance.get('/foo'); 65 | 66 | getAjaxRequest().then(function (request) { 67 | expect(request.url).toBe('http://test.com/foo'); 68 | done(); 69 | }); 70 | }); 71 | 72 | it('should ignore base URL if request URL is absolute', function (done) { 73 | var instance = axios.create({ 74 | baseURL: 'http://someurl.com/' 75 | }); 76 | 77 | instance.get('http://someotherurl.com/'); 78 | 79 | getAjaxRequest().then(function (request) { 80 | expect(request.url).toBe('http://someotherurl.com/'); 81 | done(); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /test/specs/transform.spec.js: -------------------------------------------------------------------------------- 1 | describe('transform', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | }); 9 | 10 | it('should transform JSON to string', function (done) { 11 | var data = { 12 | foo: 'bar' 13 | }; 14 | 15 | axios.post('/foo', data); 16 | 17 | getAjaxRequest().then(function (request) { 18 | expect(request.params).toEqual('{"foo":"bar"}'); 19 | done(); 20 | }); 21 | }); 22 | 23 | it('should transform string to JSON', function (done) { 24 | var response; 25 | 26 | axios('/foo').then(function (data) { 27 | response = data; 28 | }); 29 | 30 | getAjaxRequest().then(function (request) { 31 | request.respondWith({ 32 | status: 200, 33 | responseText: '{"foo": "bar"}' 34 | }); 35 | 36 | setTimeout(function () { 37 | expect(typeof response.data).toEqual('object'); 38 | expect(response.data.foo).toEqual('bar'); 39 | done(); 40 | }, 100); 41 | }); 42 | }); 43 | 44 | it('should override default transform', function (done) { 45 | var data = { 46 | foo: 'bar' 47 | }; 48 | 49 | axios.post('/foo', data, { 50 | transformRequest: function (data) { 51 | return data; 52 | } 53 | }); 54 | 55 | getAjaxRequest().then(function (request) { 56 | expect(typeof request.params).toEqual('object'); 57 | done(); 58 | }); 59 | }); 60 | 61 | it('should allow an Array of transformers', function (done) { 62 | var data = { 63 | foo: 'bar' 64 | }; 65 | 66 | axios.post('/foo', data, { 67 | transformRequest: axios.defaults.transformRequest.concat( 68 | function (data) { 69 | return data.replace('bar', 'baz'); 70 | } 71 | ) 72 | }); 73 | 74 | getAjaxRequest().then(function (request) { 75 | expect(request.params).toEqual('{"foo":"baz"}'); 76 | done(); 77 | }); 78 | }); 79 | 80 | it('should allowing mutating headers', function (done) { 81 | var token = Math.floor(Math.random() * Math.pow(2, 64)).toString(36); 82 | 83 | axios('/foo', { 84 | transformRequest: function (data, headers) { 85 | headers['X-Authorization'] = token; 86 | } 87 | }); 88 | 89 | getAjaxRequest().then(function (request) { 90 | expect(request.requestHeaders['X-Authorization']).toEqual(token); 91 | done(); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /lib/core/Axios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var defaults = require('./../defaults'); 4 | var utils = require('./../utils'); 5 | var InterceptorManager = require('./InterceptorManager'); 6 | var dispatchRequest = require('./dispatchRequest'); 7 | 8 | /** 9 | * Create a new instance of Axios 10 | * 11 | * @param {Object} instanceConfig The default config for the instance 12 | */ 13 | function Axios(instanceConfig) { 14 | this.defaults = instanceConfig; 15 | this.interceptors = { 16 | request: new InterceptorManager(), 17 | response: new InterceptorManager() 18 | }; 19 | } 20 | 21 | /** 22 | * Dispatch a request 23 | * 24 | * @param {Object} config The config specific for this request (merged with this.defaults) 25 | */ 26 | Axios.prototype.request = function request(config) { 27 | /*eslint no-param-reassign:0*/ 28 | // Allow for axios('example/url'[, config]) a la fetch API 29 | if (typeof config === 'string') { 30 | config = utils.merge({ 31 | url: arguments[0] 32 | }, arguments[1]); 33 | } 34 | 35 | config = utils.merge(defaults, this.defaults, { method: 'get' }, config); 36 | config.method = config.method.toLowerCase(); 37 | 38 | // Hook up interceptors middleware 39 | var chain = [dispatchRequest, undefined]; 40 | var promise = Promise.resolve(config); 41 | 42 | this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { 43 | chain.unshift(interceptor.fulfilled, interceptor.rejected); 44 | }); 45 | 46 | this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { 47 | chain.push(interceptor.fulfilled, interceptor.rejected); 48 | }); 49 | 50 | while (chain.length) { 51 | promise = promise.then(chain.shift(), chain.shift()); 52 | } 53 | 54 | return promise; 55 | }; 56 | 57 | // Provide aliases for supported request methods 58 | utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { 59 | /*eslint func-names:0*/ 60 | Axios.prototype[method] = function(url, config) { 61 | return this.request(utils.merge(config || {}, { 62 | method: method, 63 | url: url 64 | })); 65 | }; 66 | }); 67 | 68 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 69 | /*eslint func-names:0*/ 70 | Axios.prototype[method] = function(url, data, config) { 71 | return this.request(utils.merge(config || {}, { 72 | method: method, 73 | url: url, 74 | data: data 75 | })); 76 | }; 77 | }); 78 | 79 | module.exports = Axios; 80 | -------------------------------------------------------------------------------- /lib/helpers/isURLSameOrigin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | 5 | module.exports = ( 6 | utils.isStandardBrowserEnv() ? 7 | 8 | // Standard browser envs have full support of the APIs needed to test 9 | // whether the request URL is of the same origin as current location. 10 | (function standardBrowserEnv() { 11 | var msie = /(msie|trident)/i.test(navigator.userAgent); 12 | var urlParsingNode = document.createElement('a'); 13 | var originURL; 14 | 15 | /** 16 | * Parse a URL to discover it's components 17 | * 18 | * @param {String} url The URL to be parsed 19 | * @returns {Object} 20 | */ 21 | function resolveURL(url) { 22 | var href = url; 23 | 24 | if (msie) { 25 | // IE needs attribute set twice to normalize properties 26 | urlParsingNode.setAttribute('href', href); 27 | href = urlParsingNode.href; 28 | } 29 | 30 | urlParsingNode.setAttribute('href', href); 31 | 32 | // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils 33 | return { 34 | href: urlParsingNode.href, 35 | protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', 36 | host: urlParsingNode.host, 37 | search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', 38 | hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', 39 | hostname: urlParsingNode.hostname, 40 | port: urlParsingNode.port, 41 | pathname: (urlParsingNode.pathname.charAt(0) === '/') ? 42 | urlParsingNode.pathname : 43 | '/' + urlParsingNode.pathname 44 | }; 45 | } 46 | 47 | originURL = resolveURL(window.location.href); 48 | 49 | /** 50 | * Determine if a URL shares the same origin as the current location 51 | * 52 | * @param {String} requestURL The URL to test 53 | * @returns {boolean} True if URL shares the same origin, otherwise false 54 | */ 55 | return function isURLSameOrigin(requestURL) { 56 | var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL; 57 | return (parsed.protocol === originURL.protocol && 58 | parsed.host === originURL.host); 59 | }; 60 | })() : 61 | 62 | // Non standard browser envs (web workers, react-native) lack needed support. 63 | (function nonStandardBrowserEnv() { 64 | return function isURLSameOrigin() { 65 | return true; 66 | }; 67 | })() 68 | ); 69 | -------------------------------------------------------------------------------- /lib/core/dispatchRequest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | var transformData = require('./transformData'); 5 | var isCancel = require('../cancel/isCancel'); 6 | var defaults = require('../defaults'); 7 | var isAbsoluteURL = require('./../helpers/isAbsoluteURL'); 8 | var combineURLs = require('./../helpers/combineURLs'); 9 | 10 | /** 11 | * Throws a `Cancel` if cancellation has been requested. 12 | */ 13 | function throwIfCancellationRequested(config) { 14 | if (config.cancelToken) { 15 | config.cancelToken.throwIfRequested(); 16 | } 17 | } 18 | 19 | /** 20 | * Dispatch a request to the server using the configured adapter. 21 | * 22 | * @param {object} config The config that is to be used for the request 23 | * @returns {Promise} The Promise to be fulfilled 24 | */ 25 | module.exports = function dispatchRequest(config) { 26 | throwIfCancellationRequested(config); 27 | 28 | // Support baseURL config 29 | if (config.baseURL && !isAbsoluteURL(config.url)) { 30 | config.url = combineURLs(config.baseURL, config.url); 31 | } 32 | 33 | // Ensure headers exist 34 | config.headers = config.headers || {}; 35 | 36 | // Transform request data 37 | config.data = transformData( 38 | config.data, 39 | config.headers, 40 | config.transformRequest 41 | ); 42 | 43 | // Flatten headers 44 | config.headers = utils.merge( 45 | config.headers.common || {}, 46 | config.headers[config.method] || {}, 47 | config.headers || {} 48 | ); 49 | 50 | utils.forEach( 51 | ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], 52 | function cleanHeaderConfig(method) { 53 | delete config.headers[method]; 54 | } 55 | ); 56 | 57 | var adapter = config.adapter || defaults.adapter; 58 | 59 | return adapter(config).then(function onAdapterResolution(response) { 60 | throwIfCancellationRequested(config); 61 | 62 | // Transform response data 63 | response.data = transformData( 64 | response.data, 65 | response.headers, 66 | config.transformResponse 67 | ); 68 | 69 | return response; 70 | }, function onAdapterRejection(reason) { 71 | if (!isCancel(reason)) { 72 | throwIfCancellationRequested(config); 73 | 74 | // Transform response data 75 | if (reason && reason.response) { 76 | reason.response.data = transformData( 77 | reason.response.data, 78 | reason.response.headers, 79 | config.transformResponse 80 | ); 81 | } 82 | } 83 | 84 | return Promise.reject(reason); 85 | }); 86 | }; 87 | -------------------------------------------------------------------------------- /test/specs/headers.spec.js: -------------------------------------------------------------------------------- 1 | function testHeaderValue(headers, key, val) { 2 | var found = false; 3 | 4 | for (var k in headers) { 5 | if (k.toLowerCase() === key.toLowerCase()) { 6 | found = true; 7 | expect(headers[k]).toEqual(val); 8 | break; 9 | } 10 | } 11 | 12 | if (!found) { 13 | if (typeof val === 'undefined') { 14 | expect(headers.hasOwnProperty(key)).toEqual(false); 15 | } else { 16 | throw new Error(key + ' was not found in headers'); 17 | } 18 | } 19 | } 20 | 21 | describe('headers', function () { 22 | beforeEach(function () { 23 | jasmine.Ajax.install(); 24 | }); 25 | 26 | afterEach(function () { 27 | jasmine.Ajax.uninstall(); 28 | }); 29 | 30 | it('should default common headers', function (done) { 31 | var headers = axios.defaults.headers.common; 32 | 33 | axios('/foo'); 34 | 35 | getAjaxRequest().then(function (request) { 36 | for (var key in headers) { 37 | if (headers.hasOwnProperty(key)) { 38 | expect(request.requestHeaders[key]).toEqual(headers[key]); 39 | } 40 | } 41 | done(); 42 | }); 43 | }); 44 | 45 | it('should add extra headers for post', function (done) { 46 | var headers = axios.defaults.headers.common; 47 | 48 | axios.post('/foo', 'fizz=buzz'); 49 | 50 | getAjaxRequest().then(function (request) { 51 | for (var key in headers) { 52 | if (headers.hasOwnProperty(key)) { 53 | expect(request.requestHeaders[key]).toEqual(headers[key]); 54 | } 55 | } 56 | done(); 57 | }); 58 | }); 59 | 60 | it('should use application/json when posting an object', function (done) { 61 | axios.post('/foo/bar', { 62 | firstName: 'foo', 63 | lastName: 'bar' 64 | }); 65 | 66 | getAjaxRequest().then(function (request) { 67 | testHeaderValue(request.requestHeaders, 'Content-Type', 'application/json;charset=utf-8'); 68 | done(); 69 | }); 70 | }); 71 | 72 | it('should remove content-type if data is empty', function (done) { 73 | axios.post('/foo'); 74 | 75 | getAjaxRequest().then(function (request) { 76 | testHeaderValue(request.requestHeaders, 'Content-Type', undefined); 77 | done(); 78 | }); 79 | }); 80 | 81 | it('should preserve content-type if data is false', function (done) { 82 | axios.post('/foo', false); 83 | 84 | getAjaxRequest().then(function (request) { 85 | testHeaderValue(request.requestHeaders, 'Content-Type', 'application/x-www-form-urlencoded'); 86 | done(); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/specs/api.spec.js: -------------------------------------------------------------------------------- 1 | describe('static api', function () { 2 | it('should have request method helpers', function () { 3 | expect(typeof axios.request).toEqual('function'); 4 | expect(typeof axios.get).toEqual('function'); 5 | expect(typeof axios.head).toEqual('function'); 6 | expect(typeof axios.options).toEqual('function'); 7 | expect(typeof axios.delete).toEqual('function'); 8 | expect(typeof axios.post).toEqual('function'); 9 | expect(typeof axios.put).toEqual('function'); 10 | expect(typeof axios.patch).toEqual('function'); 11 | }); 12 | 13 | it('should have promise method helpers', function () { 14 | var promise = axios(); 15 | 16 | expect(typeof promise.then).toEqual('function'); 17 | expect(typeof promise.catch).toEqual('function'); 18 | }); 19 | 20 | it('should have defaults', function () { 21 | expect(typeof axios.defaults).toEqual('object'); 22 | expect(typeof axios.defaults.headers).toEqual('object'); 23 | }); 24 | 25 | it('should have interceptors', function () { 26 | expect(typeof axios.interceptors.request).toEqual('object'); 27 | expect(typeof axios.interceptors.response).toEqual('object'); 28 | }); 29 | 30 | it('should have all/spread helpers', function () { 31 | expect(typeof axios.all).toEqual('function'); 32 | expect(typeof axios.spread).toEqual('function'); 33 | }); 34 | 35 | it('should have factory method', function () { 36 | expect(typeof axios.create).toEqual('function'); 37 | }); 38 | 39 | it('should have Cancel, CancelToken, and isCancel properties', function () { 40 | expect(typeof axios.Cancel).toEqual('function'); 41 | expect(typeof axios.CancelToken).toEqual('function'); 42 | expect(typeof axios.isCancel).toEqual('function'); 43 | }); 44 | }); 45 | 46 | describe('instance api', function () { 47 | var instance = axios.create(); 48 | 49 | it('should have request methods', function () { 50 | expect(typeof instance.request).toEqual('function'); 51 | expect(typeof instance.get).toEqual('function'); 52 | expect(typeof instance.options).toEqual('function'); 53 | expect(typeof instance.head).toEqual('function'); 54 | expect(typeof instance.delete).toEqual('function'); 55 | expect(typeof instance.post).toEqual('function'); 56 | expect(typeof instance.put).toEqual('function'); 57 | expect(typeof instance.patch).toEqual('function'); 58 | }); 59 | 60 | it('should have interceptors', function () { 61 | expect(typeof instance.interceptors.request).toEqual('object'); 62 | expect(typeof instance.interceptors.response).toEqual('object'); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/specs/core/settle.spec.js: -------------------------------------------------------------------------------- 1 | var settle = require('../../../lib/core/settle'); 2 | 3 | describe('core::settle', function() { 4 | var resolve; 5 | var reject; 6 | 7 | beforeEach(function() { 8 | resolve = jasmine.createSpy('resolve'); 9 | reject = jasmine.createSpy('reject'); 10 | }); 11 | 12 | it('should resolve promise if status is not set', function() { 13 | var response = { 14 | config: { 15 | validateStatus: function() { 16 | return true; 17 | } 18 | } 19 | }; 20 | settle(resolve, reject, response); 21 | expect(resolve).toHaveBeenCalledWith(response); 22 | expect(reject).not.toHaveBeenCalled(); 23 | }); 24 | 25 | it('should resolve promise if validateStatus is not set', function() { 26 | var response = { 27 | status: 500, 28 | config: { 29 | } 30 | }; 31 | settle(resolve, reject, response); 32 | expect(resolve).toHaveBeenCalledWith(response); 33 | expect(reject).not.toHaveBeenCalled(); 34 | }); 35 | 36 | it('should resolve promise if validateStatus returns true', function() { 37 | var response = { 38 | status: 500, 39 | config: { 40 | validateStatus: function() { 41 | return true; 42 | } 43 | } 44 | }; 45 | settle(resolve, reject, response); 46 | expect(resolve).toHaveBeenCalledWith(response); 47 | expect(reject).not.toHaveBeenCalled(); 48 | }); 49 | 50 | it('should reject promise if validateStatus returns false', function() { 51 | var req = { 52 | path: '/foo' 53 | }; 54 | var response = { 55 | status: 500, 56 | config: { 57 | validateStatus: function() { 58 | return false; 59 | } 60 | }, 61 | request: req 62 | }; 63 | settle(resolve, reject, response); 64 | expect(resolve).not.toHaveBeenCalled(); 65 | expect(reject).toHaveBeenCalled(); 66 | var reason = reject.calls.first().args[0]; 67 | expect(reason instanceof Error).toBe(true); 68 | expect(reason.message).toBe('Request failed with status code 500'); 69 | expect(reason.config).toBe(response.config); 70 | expect(reason.request).toBe(req); 71 | expect(reason.response).toBe(response); 72 | }); 73 | 74 | it('should pass status to validateStatus', function() { 75 | var validateStatus = jasmine.createSpy('validateStatus'); 76 | var response = { 77 | status: 500, 78 | config: { 79 | validateStatus: validateStatus 80 | } 81 | }; 82 | settle(resolve, reject, response); 83 | expect(validateStatus).toHaveBeenCalledWith(500); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/specs/xsrf.spec.js: -------------------------------------------------------------------------------- 1 | var cookies = require('../../lib/helpers/cookies'); 2 | 3 | describe('xsrf', function () { 4 | beforeEach(function () { 5 | jasmine.Ajax.install(); 6 | }); 7 | 8 | afterEach(function () { 9 | document.cookie = axios.defaults.xsrfCookieName + '=;expires=' + new Date(Date.now() - 86400000).toGMTString(); 10 | jasmine.Ajax.uninstall(); 11 | }); 12 | 13 | it('should not set xsrf header if cookie is null', function (done) { 14 | axios('/foo'); 15 | 16 | getAjaxRequest().then(function (request) { 17 | expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(undefined); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('should set xsrf header if cookie is set', function (done) { 23 | document.cookie = axios.defaults.xsrfCookieName + '=12345'; 24 | 25 | axios('/foo'); 26 | 27 | getAjaxRequest().then(function (request) { 28 | expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual('12345'); 29 | done(); 30 | }); 31 | }); 32 | 33 | it('should not set xsrf header if xsrfCookieName is null', function (done) { 34 | document.cookie = axios.defaults.xsrfCookieName + '=12345'; 35 | 36 | axios('/foo', { 37 | xsrfCookieName: null 38 | }); 39 | 40 | getAjaxRequest().then(function (request) { 41 | expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(undefined); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should not read cookies at all if xsrfCookieName is null', function (done) { 47 | spyOn(cookies, "read"); 48 | 49 | axios('/foo', { 50 | xsrfCookieName: null 51 | }); 52 | 53 | getAjaxRequest().then(function (request) { 54 | expect(cookies.read).not.toHaveBeenCalled(); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('should not set xsrf header for cross origin', function (done) { 60 | document.cookie = axios.defaults.xsrfCookieName + '=12345'; 61 | 62 | axios('http://example.com/'); 63 | 64 | getAjaxRequest().then(function (request) { 65 | expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual(undefined); 66 | done(); 67 | }); 68 | }); 69 | 70 | it('should set xsrf header for cross origin when using withCredentials', function (done) { 71 | document.cookie = axios.defaults.xsrfCookieName + '=12345'; 72 | 73 | axios('http://example.com/', { 74 | withCredentials: true 75 | }); 76 | 77 | getAjaxRequest().then(function (request) { 78 | expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toEqual('12345'); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /lib/defaults.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./utils'); 4 | var normalizeHeaderName = require('./helpers/normalizeHeaderName'); 5 | 6 | var DEFAULT_CONTENT_TYPE = { 7 | 'Content-Type': 'application/x-www-form-urlencoded' 8 | }; 9 | 10 | function setContentTypeIfUnset(headers, value) { 11 | if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { 12 | headers['Content-Type'] = value; 13 | } 14 | } 15 | 16 | function getDefaultAdapter() { 17 | var adapter; 18 | if (typeof XMLHttpRequest !== 'undefined') { 19 | // For browsers use XHR adapter 20 | adapter = require('./adapters/xhr'); 21 | } else if (typeof process !== 'undefined') { 22 | // For node use HTTP adapter 23 | adapter = require('./adapters/http'); 24 | } 25 | return adapter; 26 | } 27 | 28 | var defaults = { 29 | adapter: getDefaultAdapter(), 30 | 31 | transformRequest: [function transformRequest(data, headers) { 32 | normalizeHeaderName(headers, 'Content-Type'); 33 | if (utils.isFormData(data) || 34 | utils.isArrayBuffer(data) || 35 | utils.isBuffer(data) || 36 | utils.isStream(data) || 37 | utils.isFile(data) || 38 | utils.isBlob(data) 39 | ) { 40 | return data; 41 | } 42 | if (utils.isArrayBufferView(data)) { 43 | return data.buffer; 44 | } 45 | if (utils.isURLSearchParams(data)) { 46 | setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); 47 | return data.toString(); 48 | } 49 | if (utils.isObject(data)) { 50 | setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); 51 | return JSON.stringify(data); 52 | } 53 | return data; 54 | }], 55 | 56 | transformResponse: [function transformResponse(data) { 57 | /*eslint no-param-reassign:0*/ 58 | if (typeof data === 'string') { 59 | try { 60 | data = JSON.parse(data); 61 | } catch (e) { /* Ignore */ } 62 | } 63 | return data; 64 | }], 65 | 66 | timeout: 0, 67 | 68 | xsrfCookieName: 'XSRF-TOKEN', 69 | xsrfHeaderName: 'X-XSRF-TOKEN', 70 | 71 | maxContentLength: -1, 72 | 73 | validateStatus: function validateStatus(status) { 74 | return status >= 200 && status < 300; 75 | } 76 | }; 77 | 78 | defaults.headers = { 79 | common: { 80 | 'Accept': 'application/json, text/plain, */*' 81 | } 82 | }; 83 | 84 | utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { 85 | defaults.headers[method] = {}; 86 | }); 87 | 88 | utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { 89 | defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); 90 | }); 91 | 92 | module.exports = defaults; 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@contentful/axios", 3 | "version": "0.18.0", 4 | "description": "Promise based HTTP client for the browser and node.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "grunt test && bundlesize", 8 | "start": "node ./sandbox/server.js", 9 | "build": "NODE_ENV=production grunt build", 10 | "preversion": "npm test", 11 | "version": "npm run build && grunt version && git add -A dist && git add CHANGELOG.md bower.json package.json", 12 | "postversion": "git push && git push --tags", 13 | "examples": "node ./examples/server.js", 14 | "coveralls": "cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/contentful/axios.git" 19 | }, 20 | "keywords": [ 21 | "xhr", 22 | "http", 23 | "ajax", 24 | "promise", 25 | "node" 26 | ], 27 | "author": "Matt Zabriskie", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/axios/axios/issues" 31 | }, 32 | "homepage": "https://github.com/axios/axios", 33 | "devDependencies": { 34 | "bundlesize": "^0.5.7", 35 | "coveralls": "^2.11.9", 36 | "es6-promise": "^4.0.5", 37 | "grunt": "^1.0.1", 38 | "grunt-banner": "^0.6.0", 39 | "grunt-cli": "^1.2.0", 40 | "grunt-contrib-clean": "^1.0.0", 41 | "grunt-contrib-nodeunit": "^1.0.0", 42 | "grunt-contrib-watch": "^1.0.0", 43 | "grunt-eslint": "^19.0.0", 44 | "grunt-karma": "^2.0.0", 45 | "grunt-ts": "^6.0.0-beta.3", 46 | "grunt-webpack": "^1.0.18", 47 | "istanbul-instrumenter-loader": "^1.0.0", 48 | "jasmine-core": "^2.4.1", 49 | "karma": "^1.3.0", 50 | "karma-chrome-launcher": "^2.0.0", 51 | "karma-coverage": "^1.0.0", 52 | "karma-firefox-launcher": "^1.0.0", 53 | "karma-jasmine": "^1.0.2", 54 | "karma-jasmine-ajax": "^0.1.13", 55 | "karma-opera-launcher": "^1.0.0", 56 | "karma-phantomjs-launcher": "^1.0.0", 57 | "karma-safari-launcher": "^1.0.0", 58 | "karma-sauce-launcher": "^1.1.0", 59 | "karma-sinon": "^1.0.5", 60 | "karma-sourcemap-loader": "^0.3.7", 61 | "karma-webpack": "^1.7.0", 62 | "load-grunt-tasks": "^3.5.2", 63 | "minimist": "^1.2.0", 64 | "phantomjs-prebuilt": "^2.1.7", 65 | "sinon": "^1.17.4", 66 | "webpack": "^1.13.1", 67 | "webpack-dev-server": "^1.14.1", 68 | "url-search-params": "^0.6.1", 69 | "typescript": "^2.0.3" 70 | }, 71 | "browser": { 72 | "./lib/adapters/http.js": "./lib/adapters/xhr.js" 73 | }, 74 | "typings": "./index.d.ts", 75 | "dependencies": { 76 | "follow-redirects": "^1.2.5", 77 | "is-buffer": "^1.1.5" 78 | }, 79 | "bundlesize": [ 80 | { 81 | "path": "./dist/axios.min.js", 82 | "threshold": "5kB" 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | require('load-grunt-tasks')(grunt); 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | meta: { 7 | banner: '/* <%= pkg.name %> v<%= pkg.version %> | (c) <%= grunt.template.today("yyyy") %> by Matt Zabriskie */\n' 8 | }, 9 | 10 | clean: { 11 | dist: 'dist/**' 12 | }, 13 | 14 | ts: { 15 | test: { 16 | options: { 17 | lib: [ 18 | 'es5', 19 | 'es2015.promise', 20 | 'dom' 21 | ] 22 | }, 23 | src: ['typings/index.d.ts', 'test/typescript/*.ts'] 24 | } 25 | }, 26 | 27 | package2bower: { 28 | all: { 29 | fields: [ 30 | 'name', 31 | 'description', 32 | 'version', 33 | 'homepage', 34 | 'license', 35 | 'keywords' 36 | ] 37 | } 38 | }, 39 | 40 | usebanner: { 41 | all: { 42 | options: { 43 | banner: '<%= meta.banner %>', 44 | linebreak: false 45 | }, 46 | files: { 47 | src: ['dist/*.js'] 48 | } 49 | } 50 | }, 51 | 52 | eslint: { 53 | target: ['lib/**/*.js'] 54 | }, 55 | 56 | karma: { 57 | options: { 58 | configFile: 'karma.conf.js' 59 | }, 60 | single: { 61 | singleRun: true 62 | }, 63 | continuous: { 64 | singleRun: false 65 | } 66 | }, 67 | 68 | nodeunit: { 69 | all: ['test/unit/**/*.js'] 70 | }, 71 | 72 | watch: { 73 | build: { 74 | files: ['lib/**/*.js'], 75 | tasks: ['build'] 76 | }, 77 | test: { 78 | files: ['lib/**/*.js', 'test/**/*.js', '!test/typescript/axios.js', '!test/typescript/out.js'], 79 | tasks: ['test'] 80 | } 81 | }, 82 | 83 | webpack: require('./webpack.config.js') 84 | }); 85 | 86 | grunt.registerMultiTask('package2bower', 'Sync package.json to bower.json', function () { 87 | var npm = grunt.file.readJSON('package.json'); 88 | var bower = grunt.file.readJSON('bower.json'); 89 | var fields = this.data.fields || []; 90 | 91 | for (var i=0, l=fields.length; i -1) { 23 | continue; 24 | } 25 | expect(typeof instance[prop]).toBe(typeof axios[prop]); 26 | } 27 | }); 28 | 29 | it('should make an http request without verb helper', function (done) { 30 | var instance = axios.create(); 31 | 32 | instance('/foo'); 33 | 34 | getAjaxRequest().then(function (request) { 35 | expect(request.url).toBe('/foo'); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should make an http request', function (done) { 41 | var instance = axios.create(); 42 | 43 | instance.get('/foo'); 44 | 45 | getAjaxRequest().then(function (request) { 46 | expect(request.url).toBe('/foo'); 47 | done(); 48 | }); 49 | }); 50 | 51 | it('should use instance options', function (done) { 52 | var instance = axios.create({ timeout: 1000 }); 53 | 54 | instance.get('/foo'); 55 | 56 | getAjaxRequest().then(function (request) { 57 | expect(request.timeout).toBe(1000); 58 | done(); 59 | }); 60 | }); 61 | 62 | it('should have defaults.headers', function () { 63 | var instance = axios.create({ 64 | baseURL: 'https://api.example.com' 65 | }); 66 | 67 | expect(typeof instance.defaults.headers, 'object'); 68 | expect(typeof instance.defaults.headers.common, 'object'); 69 | }); 70 | 71 | it('should have interceptors on the instance', function (done) { 72 | axios.interceptors.request.use(function (config) { 73 | config.foo = true; 74 | return config; 75 | }); 76 | 77 | var instance = axios.create(); 78 | instance.interceptors.request.use(function (config) { 79 | config.bar = true; 80 | return config; 81 | }); 82 | 83 | var response; 84 | instance.get('/foo').then(function (res) { 85 | response = res; 86 | }); 87 | 88 | getAjaxRequest().then(function (request) { 89 | request.respondWith({ 90 | status: 200 91 | }); 92 | 93 | setTimeout(function () { 94 | expect(response.config.foo).toEqual(undefined); 95 | expect(response.config.bar).toEqual(true); 96 | done(); 97 | }, 100); 98 | }); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/specs/__helpers.js: -------------------------------------------------------------------------------- 1 | // Polyfill ES6 Promise 2 | require('es6-promise').polyfill(); 3 | 4 | // Polyfill URLSearchParams 5 | URLSearchParams = require('url-search-params'); 6 | 7 | // Import axios 8 | axios = require('../../index'); 9 | 10 | // Jasmine config 11 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; 12 | jasmine.getEnv().defaultTimeoutInterval = 20000; 13 | 14 | // Is this an old version of IE that lacks standard objects like DataView, ArrayBuffer, FormData, etc. 15 | isOldIE = /MSIE (8|9)\.0/.test(navigator.userAgent); 16 | 17 | // Get Ajax request using an increasing timeout to retry 18 | getAjaxRequest = (function () { 19 | var attempts = 0; 20 | var MAX_ATTEMPTS = 5; 21 | var ATTEMPT_DELAY_FACTOR = 5; 22 | 23 | function getAjaxRequest() { 24 | return new Promise(function (resolve, reject) { 25 | attempts = 0; 26 | attemptGettingAjaxRequest(resolve, reject); 27 | }); 28 | } 29 | 30 | function attemptGettingAjaxRequest(resolve, reject) { 31 | var delay = attempts * attempts * ATTEMPT_DELAY_FACTOR; 32 | 33 | if (attempts++ > MAX_ATTEMPTS) { 34 | reject(new Error('No request was found')); 35 | return; 36 | } 37 | 38 | setTimeout(function () { 39 | var request = jasmine.Ajax.requests.mostRecent(); 40 | if (request) { 41 | resolve(request); 42 | } else { 43 | attemptGettingAjaxRequest(resolve, reject); 44 | } 45 | }, delay); 46 | } 47 | 48 | return getAjaxRequest; 49 | })(); 50 | 51 | // Validate an invalid character error 52 | validateInvalidCharacterError = function validateInvalidCharacterError(error) { 53 | expect(/character/i.test(error.message)).toEqual(true); 54 | }; 55 | 56 | // Setup basic auth tests 57 | setupBasicAuthTest = function setupBasicAuthTest() { 58 | beforeEach(function () { 59 | jasmine.Ajax.install(); 60 | }); 61 | 62 | afterEach(function () { 63 | jasmine.Ajax.uninstall(); 64 | }); 65 | 66 | it('should accept HTTP Basic auth with username/password', function (done) { 67 | axios('/foo', { 68 | auth: { 69 | username: 'Aladdin', 70 | password: 'open sesame' 71 | } 72 | }); 73 | 74 | setTimeout(function () { 75 | var request = jasmine.Ajax.requests.mostRecent(); 76 | 77 | expect(request.requestHeaders['Authorization']).toEqual('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='); 78 | done(); 79 | }, 100); 80 | }); 81 | 82 | it('should fail to encode HTTP Basic auth credentials with non-Latin1 characters', function (done) { 83 | axios('/foo', { 84 | auth: { 85 | username: 'Aladßç£☃din', 86 | password: 'open sesame' 87 | } 88 | }).then(function(response) { 89 | done(new Error('Should not succeed to make a HTTP Basic auth request with non-latin1 chars in credentials.')); 90 | }).catch(function(error) { 91 | validateInvalidCharacterError(error); 92 | done(); 93 | }); 94 | }); 95 | }; 96 | -------------------------------------------------------------------------------- /test/specs/utils/isX.spec.js: -------------------------------------------------------------------------------- 1 | var utils = require('../../../lib/utils'); 2 | var Stream = require('stream'); 3 | 4 | describe('utils::isX', function () { 5 | it('should validate Array', function () { 6 | expect(utils.isArray([])).toEqual(true); 7 | expect(utils.isArray({length: 5})).toEqual(false); 8 | }); 9 | 10 | it('should validate ArrayBuffer', function () { 11 | // ArrayBuffer doesn't exist in IE8/9 12 | if (isOldIE && typeof ArrayBuffer === 'undefined') { 13 | return; 14 | } 15 | 16 | expect(utils.isArrayBuffer(new ArrayBuffer(2))).toEqual(true); 17 | expect(utils.isArrayBuffer({})).toEqual(false); 18 | }); 19 | 20 | it('should validate ArrayBufferView', function () { 21 | // ArrayBuffer doesn't exist in IE8/9 22 | if (isOldIE && typeof ArrayBuffer === 'undefined') { 23 | return; 24 | } 25 | 26 | expect(utils.isArrayBufferView(new DataView(new ArrayBuffer(2)))).toEqual(true); 27 | }); 28 | 29 | it('should validate FormData', function () { 30 | // FormData doesn't exist in IE8/9 31 | if (isOldIE && typeof FormData === 'undefined') { 32 | return; 33 | } 34 | 35 | expect(utils.isFormData(new FormData())).toEqual(true); 36 | }); 37 | 38 | it('should validate Blob', function () { 39 | // Blob doesn't exist in IE8/9 40 | if (isOldIE && typeof Blob === 'undefined') { 41 | return; 42 | } 43 | 44 | expect(utils.isBlob(new Blob())).toEqual(true); 45 | }); 46 | 47 | it('should validate String', function () { 48 | expect(utils.isString('')).toEqual(true); 49 | expect(utils.isString({toString: function () { return ''; }})).toEqual(false); 50 | }); 51 | 52 | it('should validate Number', function () { 53 | expect(utils.isNumber(123)).toEqual(true); 54 | expect(utils.isNumber('123')).toEqual(false); 55 | }); 56 | 57 | it('should validate Undefined', function () { 58 | expect(utils.isUndefined()).toEqual(true); 59 | expect(utils.isUndefined(null)).toEqual(false); 60 | }); 61 | 62 | it('should validate Object', function () { 63 | expect(utils.isObject({})).toEqual(true); 64 | expect(utils.isObject(null)).toEqual(false); 65 | }); 66 | 67 | it('should validate Date', function () { 68 | expect(utils.isDate(new Date())).toEqual(true); 69 | expect(utils.isDate(Date.now())).toEqual(false); 70 | }); 71 | 72 | it('should validate Function', function () { 73 | expect(utils.isFunction(function () {})).toEqual(true); 74 | expect(utils.isFunction('function')).toEqual(false); 75 | }); 76 | 77 | it('should validate Stream', function () { 78 | expect(utils.isStream(new Stream.Readable())).toEqual(true); 79 | expect(utils.isStream({ foo: 'bar' })).toEqual(false); 80 | }); 81 | 82 | it('should validate URLSearchParams', function () { 83 | expect(utils.isURLSearchParams(new URLSearchParams())).toEqual(true); 84 | expect(utils.isURLSearchParams('foo=1&bar=2')).toEqual(false); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /COLLABORATOR_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Collaborator Guide 2 | 3 | As a collaborator you will be involved with axios with some administrative responsibilities. This guide will help you understand your role and the responsibilities that come with being a collaborator. 4 | 5 | 1. __Adhere to and help enforce the Code of Conduct.__ It is expected that you have read the [code of conduct](https://github.com/axios/axios/blob/master/CODE_OF_CONDUCT.md) and that you agree to live by it. This community should be friendly and welcoming. 6 | 7 | 1. __Triage issues.__ As a collaborator you may help sort through the issues that are reported. Issues vary from bugs, regressions, feature requests, questions, etc. Apply the appropriate label(s) and respond as needed. If it is a legitimate request please address it, otherwise feel free to close the issue and include a comment with a suggestion on where to find support. If an issue has been inactive for more than a week (i.e, the owner of the issue hasn’t responded to you), close the issue with a note indicating stales issues are closed; it can always be reopened if needed. In the case of issues that require a code change encourage the owner to submit a PR. For less complex code changes, add a very simple and detailed checklist, apply the “first-timers-only” label, and encourage a newcomer to open source to get involved. 8 | 9 | 1. __Answer questions.__ It is not expected that you provide answers to questions that aren’t relevant, nor do you need to mentor people on how to use JavaScript, etc. If the question is not directly about the module, please close the issue. If the question stems from poor documentation, please update the docs and consider adding a code example. In any event try to be helpful and remember that there’s no such thing as a stupid question. 10 | 11 | 1. __Assist with PRs.__ By encouraging contributors to supply a PR for their own issue this is ideally where most of your attention will be focused. Keep a few things in mind as you review PRs. 12 | - When fixing a bug: does the PR adequately solve the problem without introducing any regressions? 13 | - When implementing a feature: does the feature fit within the scope of axios? 14 | - When removing functionality: is it properly deprecated with a warning? 15 | - When introducing functionality: is the API predictable? 16 | - Does the new code work for all supported platforms/browsers? 17 | - Do the tests and linting pass CI? 18 | - Are there tests to validate the changes that have been made? 19 | 20 | 1. __Fix bugs and implement features.__ When things need to be fixed or implemented and a PR can’t wait, you may do things yourself. You should still submit a PR yourself and get it checked off by at least one other contributor. Keep the points from number 4 in consideration as you push your code. 21 | 22 | Thank you again for your help as a collaborator and in making axios community great! If you have any questions, or need any assistance please feel free to contact another collaborator or the owner. 23 | 24 | -------------------------------------------------------------------------------- /test/specs/cancel.spec.js: -------------------------------------------------------------------------------- 1 | var Cancel = axios.Cancel; 2 | var CancelToken = axios.CancelToken; 3 | 4 | describe('cancel', function() { 5 | beforeEach(function() { 6 | jasmine.Ajax.install(); 7 | }); 8 | 9 | afterEach(function() { 10 | jasmine.Ajax.uninstall(); 11 | }); 12 | 13 | describe('when called before sending request', function() { 14 | it('rejects Promise with a Cancel object', function (done) { 15 | var source = CancelToken.source(); 16 | source.cancel('Operation has been canceled.'); 17 | axios.get('/foo', { 18 | cancelToken: source.token 19 | }).catch(function (thrown) { 20 | expect(thrown).toEqual(jasmine.any(Cancel)); 21 | expect(thrown.message).toBe('Operation has been canceled.'); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | 27 | describe('when called after request has been sent', function() { 28 | it('rejects Promise with a Cancel object', function (done) { 29 | var source = CancelToken.source(); 30 | axios.get('/foo/bar', { 31 | cancelToken: source.token 32 | }).catch(function (thrown) { 33 | expect(thrown).toEqual(jasmine.any(Cancel)); 34 | expect(thrown.message).toBe('Operation has been canceled.'); 35 | done(); 36 | }); 37 | 38 | getAjaxRequest().then(function (request) { 39 | // call cancel() when the request has been sent, but a response has not been received 40 | source.cancel('Operation has been canceled.'); 41 | request.respondWith({ 42 | status: 200, 43 | responseText: 'OK' 44 | }); 45 | }); 46 | }); 47 | 48 | it('calls abort on request object', function (done) { 49 | var source = CancelToken.source(); 50 | var request; 51 | axios.get('/foo/bar', { 52 | cancelToken: source.token 53 | }).catch(function() { 54 | // jasmine-ajax sets statusText to 'abort' when request.abort() is called 55 | expect(request.statusText).toBe('abort'); 56 | done(); 57 | }); 58 | 59 | getAjaxRequest().then(function (req) { 60 | // call cancel() when the request has been sent, but a response has not been received 61 | source.cancel(); 62 | request = req; 63 | }); 64 | }); 65 | }); 66 | 67 | describe('when called after response has been received', function() { 68 | // https://github.com/axios/axios/issues/482 69 | it('does not cause unhandled rejection', function (done) { 70 | var source = CancelToken.source(); 71 | axios.get('/foo', { 72 | cancelToken: source.token 73 | }).then(function () { 74 | window.addEventListener('unhandledrejection', function () { 75 | done.fail('Unhandled rejection.'); 76 | }); 77 | source.cancel(); 78 | setTimeout(done, 100); 79 | }); 80 | 81 | getAjaxRequest().then(function (request) { 82 | request.respondWith({ 83 | status: 200, 84 | responseText: 'OK' 85 | }); 86 | }); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /COOKBOOK.md: -------------------------------------------------------------------------------- 1 | # Cookbook 2 | 3 | In an effort to keep axios as light weight as possible, and to avoid a rats nest of code for supporting every possible integration, it is often necessary to say no to feature requests. This doesn't mean that those use cases aren't legitimate, but rather that they are easily supported by augmenting axios with other libraries. 4 | 5 | The following are the recipes for some of the commonly requested features. 6 | 7 | ### Promise.prototype.done 8 | 9 | ```bash 10 | $ npm install axios promise --save 11 | ``` 12 | 13 | ```js 14 | var axios = require('axios'); 15 | require('promise/polyfill-done'); 16 | 17 | axios 18 | .get('http://www.example.com/user') 19 | .then(function (response) { 20 | console.log(response.data); 21 | return response; 22 | }) 23 | .done(); 24 | ``` 25 | 26 | ### Promise.prototype.finally 27 | 28 | ```bash 29 | $ npm install axios promise.prototype.finally --save 30 | ``` 31 | 32 | ```js 33 | var axios = require('axios'); 34 | require('promise.prototype.finally').shim(); 35 | 36 | axios 37 | .get('http://www.example.com/user') 38 | .then(function (response) { 39 | console.log(response.data); 40 | return response; 41 | }) 42 | .finally(function () { 43 | console.log('this will always be called'); 44 | }); 45 | ``` 46 | 47 | ### Inflate/Deflate 48 | 49 | ```bash 50 | $ npm install axios pako --save 51 | ``` 52 | 53 | ```js 54 | // client.js 55 | var axios = require('axios'); 56 | var pako = require('pako'); 57 | 58 | var user = { 59 | firstName: 'Fred', 60 | lastName: 'Flintstone' 61 | }; 62 | 63 | var data = pako.deflate(JSON.stringify(user), { to: 'string' }); 64 | 65 | axios 66 | .post('http://127.0.0.1:3333/user', data) 67 | .then(function (response) { 68 | response.data = JSON.parse(pako.inflate(response.data, { to: 'string' })); 69 | console.log(response.data); 70 | return response; 71 | }); 72 | ``` 73 | 74 | ```js 75 | // server.js 76 | var pako = require('pako'); 77 | var http = require('http'); 78 | var url = require('url'); 79 | var server; 80 | 81 | server = http.createServer(function (req, res) { 82 | req.setEncoding('utf8'); 83 | 84 | var parsed = url.parse(req.url, true); 85 | var pathname = parsed.pathname; 86 | 87 | if (pathname === '/user') { 88 | var data = ''; 89 | req.on('data', function (chunk) { 90 | data += chunk; 91 | }); 92 | 93 | req.on('end', function () { 94 | var user = JSON.parse(pako.inflate(data, { to: 'string' })); 95 | console.log(user); 96 | 97 | res.writeHead(200, { 98 | 'Content-Type': 'application/json' 99 | }); 100 | res.end(pako.deflate(JSON.stringify({result: 'success'}), { to: 'string' })); 101 | }); 102 | } else { 103 | res.writeHead(404); 104 | res.end(pako.deflate(JSON.stringify({result: 'error'}), { to: 'string' })); 105 | } 106 | }); 107 | 108 | server.listen(3333); 109 | ``` 110 | 111 | ### JSONP 112 | 113 | ```bash 114 | $ npm install jsonp --save 115 | ``` 116 | 117 | ```js 118 | var jsonp = require('jsonp'); 119 | 120 | jsonp('http://www.example.com/foo', null, function (err, data) { 121 | if (err) { 122 | console.error(err.message); 123 | } else { 124 | console.log(data); 125 | } 126 | }); 127 | ``` 128 | -------------------------------------------------------------------------------- /test/specs/cancel/CancelToken.spec.js: -------------------------------------------------------------------------------- 1 | var CancelToken = require('../../../lib/cancel/CancelToken'); 2 | var Cancel = require('../../../lib/cancel/Cancel'); 3 | 4 | describe('CancelToken', function() { 5 | describe('constructor', function() { 6 | it('throws when executor is not specified', function() { 7 | expect(function() { 8 | new CancelToken(); 9 | }).toThrowError(TypeError, 'executor must be a function.'); 10 | }); 11 | 12 | it('throws when executor is not a function', function() { 13 | expect(function() { 14 | new CancelToken(123); 15 | }).toThrowError(TypeError, 'executor must be a function.'); 16 | }); 17 | }); 18 | 19 | describe('reason', function() { 20 | it('returns a Cancel if cancellation has been requested', function() { 21 | var cancel; 22 | var token = new CancelToken(function(c) { 23 | cancel = c; 24 | }); 25 | cancel('Operation has been canceled.'); 26 | expect(token.reason).toEqual(jasmine.any(Cancel)); 27 | expect(token.reason.message).toBe('Operation has been canceled.'); 28 | }); 29 | 30 | it('returns undefined if cancellation has not been requested', function() { 31 | var token = new CancelToken(function() {}); 32 | expect(token.reason).toBeUndefined(); 33 | }); 34 | }); 35 | 36 | describe('promise', function() { 37 | it('returns a Promise that resolves when cancellation is requested', function(done) { 38 | var cancel; 39 | var token = new CancelToken(function(c) { 40 | cancel = c; 41 | }); 42 | token.promise.then(function onFulfilled(value) { 43 | expect(value).toEqual(jasmine.any(Cancel)); 44 | expect(value.message).toBe('Operation has been canceled.'); 45 | done(); 46 | }); 47 | cancel('Operation has been canceled.'); 48 | }); 49 | }); 50 | 51 | describe('throwIfRequested', function() { 52 | it('throws if cancellation has been requested', function() { 53 | // Note: we cannot use expect.toThrowError here as Cancel does not inherit from Error 54 | var cancel; 55 | var token = new CancelToken(function(c) { 56 | cancel = c; 57 | }); 58 | cancel('Operation has been canceled.'); 59 | try { 60 | token.throwIfRequested(); 61 | fail('Expected throwIfRequested to throw.'); 62 | } catch (thrown) { 63 | if (!(thrown instanceof Cancel)) { 64 | fail('Expected throwIfRequested to throw a Cancel, but it threw ' + thrown + '.'); 65 | } 66 | expect(thrown.message).toBe('Operation has been canceled.'); 67 | } 68 | }); 69 | 70 | it('does not throw if cancellation has not been requested', function() { 71 | var token = new CancelToken(function() {}); 72 | token.throwIfRequested(); 73 | }); 74 | }); 75 | 76 | describe('source', function() { 77 | it('returns an object containing token and cancel function', function() { 78 | var source = CancelToken.source(); 79 | expect(source.token).toEqual(jasmine.any(CancelToken)); 80 | expect(source.cancel).toEqual(jasmine.any(Function)); 81 | expect(source.token.reason).toBeUndefined(); 82 | source.cancel('Operation has been canceled.'); 83 | expect(source.token.reason).toEqual(jasmine.any(Cancel)); 84 | expect(source.token.reason.message).toBe('Operation has been canceled.'); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at mzabriskie@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /test/specs/progress.spec.js: -------------------------------------------------------------------------------- 1 | describe('progress events', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | }); 9 | 10 | it('should add a download progress handler', function (done) { 11 | var progressSpy = jasmine.createSpy('progress'); 12 | 13 | axios('/foo', { onDownloadProgress: progressSpy } ); 14 | 15 | getAjaxRequest().then(function (request) { 16 | request.respondWith({ 17 | status: 200, 18 | responseText: '{"foo": "bar"}' 19 | }); 20 | expect(progressSpy).toHaveBeenCalled(); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should add a upload progress handler', function (done) { 26 | var progressSpy = jasmine.createSpy('progress'); 27 | 28 | axios('/foo', { onUploadProgress: progressSpy } ); 29 | 30 | getAjaxRequest().then(function (request) { 31 | // Jasmine AJAX doesn't trigger upload events. Waiting for upstream fix 32 | // expect(progressSpy).toHaveBeenCalled(); 33 | done(); 34 | }); 35 | }); 36 | 37 | it('should add both upload and download progress handlers', function (done) { 38 | var downloadProgressSpy = jasmine.createSpy('downloadProgress'); 39 | var uploadProgressSpy = jasmine.createSpy('uploadProgress'); 40 | 41 | axios('/foo', { onDownloadProgress: downloadProgressSpy, onUploadProgress: uploadProgressSpy }); 42 | 43 | getAjaxRequest().then(function (request) { 44 | // expect(uploadProgressSpy).toHaveBeenCalled(); 45 | expect(downloadProgressSpy).not.toHaveBeenCalled(); 46 | request.respondWith({ 47 | status: 200, 48 | responseText: '{"foo": "bar"}' 49 | }); 50 | expect(downloadProgressSpy).toHaveBeenCalled(); 51 | done(); 52 | }); 53 | }); 54 | 55 | it('should add a download progress handler from instance config', function (done) { 56 | var progressSpy = jasmine.createSpy('progress'); 57 | 58 | var instance = axios.create({ 59 | onDownloadProgress: progressSpy, 60 | }); 61 | 62 | instance.get('/foo'); 63 | 64 | getAjaxRequest().then(function (request) { 65 | request.respondWith({ 66 | status: 200, 67 | responseText: '{"foo": "bar"}' 68 | }); 69 | expect(progressSpy).toHaveBeenCalled(); 70 | done(); 71 | }); 72 | }); 73 | 74 | it('should add a upload progress handler from instance config', function (done) { 75 | var progressSpy = jasmine.createSpy('progress'); 76 | 77 | var instance = axios.create({ 78 | onUploadProgress: progressSpy, 79 | }); 80 | 81 | instance.get('/foo'); 82 | 83 | getAjaxRequest().then(function (request) { 84 | // expect(progressSpy).toHaveBeenCalled(); 85 | done(); 86 | }); 87 | }); 88 | 89 | it('should add upload and download progress handlers from instance config', function (done) { 90 | var downloadProgressSpy = jasmine.createSpy('downloadProgress'); 91 | var uploadProgressSpy = jasmine.createSpy('uploadProgress'); 92 | 93 | var instance = axios.create({ 94 | onDownloadProgress: downloadProgressSpy, 95 | onUploadProgress: uploadProgressSpy, 96 | }); 97 | 98 | instance.get('/foo'); 99 | 100 | getAjaxRequest().then(function (request) { 101 | // expect(uploadProgressSpy).toHaveBeenCalled(); 102 | expect(downloadProgressSpy).not.toHaveBeenCalled(); 103 | request.respondWith({ 104 | status: 200, 105 | responseText: '{"foo": "bar"}' 106 | }); 107 | expect(downloadProgressSpy).toHaveBeenCalled(); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var url = require('url'); 3 | var path = require('path'); 4 | var http = require('http'); 5 | var argv = require('minimist')(process.argv.slice(2)); 6 | var server; 7 | var dirs; 8 | 9 | function listDirs(root) { 10 | var files = fs.readdirSync(root); 11 | var dirs = []; 12 | 13 | for (var i=0, l=files.length; i' + url + ''; 30 | }); 31 | 32 | return ( 33 | '' + 34 | '' + 35 | '' + 36 | 'axios examples' + 37 | '' + 44 | '' + 45 | '
    ' + 46 | links.join('') + 47 | '
' 48 | ); 49 | } 50 | 51 | function sendResponse(res, statusCode, body) { 52 | res.writeHead(statusCode); 53 | res.write(body); 54 | res.end(); 55 | } 56 | 57 | function send200(res, body) { 58 | sendResponse(res, 200, body || '

OK

'); 59 | } 60 | 61 | function send404(res, body) { 62 | sendResponse(res, 404, body || '

Not Found

'); 63 | } 64 | 65 | function pipeFileToResponse(res, file, type) { 66 | if (type) { 67 | res.writeHead(200, { 68 | 'Content-Type': type 69 | }); 70 | } 71 | fs.createReadStream(path.join(__dirname, file)).pipe(res); 72 | } 73 | 74 | 75 | dirs = listDirs(__dirname); 76 | 77 | server = http.createServer(function (req, res) { 78 | var url = req.url; 79 | 80 | // Process axios itself 81 | if (/axios\.min\.js$/.test(url)) { 82 | pipeFileToResponse(res, '../dist/axios.min.js', 'text/javascript'); 83 | return; 84 | } 85 | if (/axios\.min\.map$/.test(url)) { 86 | pipeFileToResponse(res, '../dist/axios.min.map', 'text/javascript'); 87 | return; 88 | } 89 | if (/axios\.amd\.min\.js$/.test(url)) { 90 | pipeFileToResponse(res, '../dist/axios.amd.min.js', 'text/javascript'); 91 | return; 92 | } 93 | if (/axios\.amd\.min\.map$/.test(url)) { 94 | pipeFileToResponse(res, '../dist/axios.amd.min.map', 'text/javascript'); 95 | return; 96 | } 97 | 98 | // Process / 99 | if (url === '/' || url === '/index.html') { 100 | send200(res, getIndexTemplate()); 101 | return; 102 | } 103 | 104 | // Format request */ -> */index.html 105 | if (/\/$/.test(url)) { 106 | url += 'index.html'; 107 | } 108 | 109 | // Format request /get -> /get/index.html 110 | var parts = url.split('/'); 111 | if (dirs.indexOf(parts[parts.length - 1]) > -1) { 112 | url += '/index.html'; 113 | } 114 | 115 | // Process index.html request 116 | if (/index\.html$/.test(url)) { 117 | if (fs.existsSync(path.join(__dirname, url))) { 118 | pipeFileToResponse(res, url, 'text/html'); 119 | } else { 120 | send404(res); 121 | } 122 | } 123 | 124 | // Process server request 125 | else if (new RegExp('(' + dirs.join('|') + ')\/server').test(url)) { 126 | if (fs.existsSync(path.join(__dirname, url + '.js'))) { 127 | require(path.join(__dirname, url + '.js'))(req, res); 128 | } else { 129 | send404(res); 130 | } 131 | } 132 | else { 133 | send404(res); 134 | } 135 | }); 136 | 137 | server.listen(argv.p || 3000); 138 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export interface AxiosTransformer { 2 | (data: any, headers?: any): any; 3 | } 4 | 5 | export interface AxiosAdapter { 6 | (config: AxiosRequestConfig): AxiosPromise; 7 | } 8 | 9 | export interface AxiosBasicCredentials { 10 | username: string; 11 | password: string; 12 | } 13 | 14 | export interface AxiosProxyConfig { 15 | host: string; 16 | port: number; 17 | auth?: { 18 | username: string; 19 | password:string; 20 | }; 21 | protocol?: string; 22 | } 23 | 24 | export interface AxiosRequestConfig { 25 | url?: string; 26 | method?: string; 27 | baseURL?: string; 28 | transformRequest?: AxiosTransformer | AxiosTransformer[]; 29 | transformResponse?: AxiosTransformer | AxiosTransformer[]; 30 | headers?: any; 31 | params?: any; 32 | paramsSerializer?: (params: any) => string; 33 | data?: any; 34 | timeout?: number; 35 | withCredentials?: boolean; 36 | adapter?: AxiosAdapter; 37 | auth?: AxiosBasicCredentials; 38 | responseType?: string; 39 | xsrfCookieName?: string; 40 | xsrfHeaderName?: string; 41 | onUploadProgress?: (progressEvent: any) => void; 42 | onDownloadProgress?: (progressEvent: any) => void; 43 | maxContentLength?: number; 44 | validateStatus?: (status: number) => boolean; 45 | maxRedirects?: number; 46 | httpAgent?: any; 47 | httpsAgent?: any; 48 | proxy?: AxiosProxyConfig | false; 49 | cancelToken?: CancelToken; 50 | } 51 | 52 | export interface AxiosResponse { 53 | data: T; 54 | status: number; 55 | statusText: string; 56 | headers: any; 57 | config: AxiosRequestConfig; 58 | request?: any; 59 | } 60 | 61 | export interface AxiosError extends Error { 62 | config: AxiosRequestConfig; 63 | code?: string; 64 | request?: any; 65 | response?: AxiosResponse; 66 | } 67 | 68 | export interface AxiosPromise extends Promise> { 69 | } 70 | 71 | export interface CancelStatic { 72 | new (message?: string): Cancel; 73 | } 74 | 75 | export interface Cancel { 76 | message: string; 77 | } 78 | 79 | export interface Canceler { 80 | (message?: string): void; 81 | } 82 | 83 | export interface CancelTokenStatic { 84 | new (executor: (cancel: Canceler) => void): CancelToken; 85 | source(): CancelTokenSource; 86 | } 87 | 88 | export interface CancelToken { 89 | promise: Promise; 90 | reason?: Cancel; 91 | throwIfRequested(): void; 92 | } 93 | 94 | export interface CancelTokenSource { 95 | token: CancelToken; 96 | cancel: Canceler; 97 | } 98 | 99 | export interface AxiosInterceptorManager { 100 | use(onFulfilled?: (value: V) => V | Promise, onRejected?: (error: any) => any): number; 101 | eject(id: number): void; 102 | } 103 | 104 | export interface AxiosInstance { 105 | defaults: AxiosRequestConfig; 106 | interceptors: { 107 | request: AxiosInterceptorManager; 108 | response: AxiosInterceptorManager; 109 | }; 110 | request(config: AxiosRequestConfig): AxiosPromise; 111 | get(url: string, config?: AxiosRequestConfig): AxiosPromise; 112 | delete(url: string, config?: AxiosRequestConfig): AxiosPromise; 113 | head(url: string, config?: AxiosRequestConfig): AxiosPromise; 114 | post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; 115 | put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; 116 | patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise; 117 | } 118 | 119 | export interface AxiosStatic extends AxiosInstance { 120 | (config: AxiosRequestConfig): AxiosPromise; 121 | (url: string, config?: AxiosRequestConfig): AxiosPromise; 122 | create(config?: AxiosRequestConfig): AxiosInstance; 123 | Cancel: CancelStatic; 124 | CancelToken: CancelTokenStatic; 125 | isCancel(value: any): boolean; 126 | all(values: (T | Promise)[]): Promise; 127 | spread(callback: (...args: T[]) => R): (array: T[]) => R; 128 | } 129 | 130 | declare const Axios: AxiosStatic; 131 | 132 | export default Axios; 133 | -------------------------------------------------------------------------------- /test/specs/defaults.spec.js: -------------------------------------------------------------------------------- 1 | var defaults = require('../../lib/defaults'); 2 | var utils = require('../../lib/utils'); 3 | 4 | describe('defaults', function () { 5 | var XSRF_COOKIE_NAME = 'CUSTOM-XSRF-TOKEN'; 6 | 7 | beforeEach(function () { 8 | jasmine.Ajax.install(); 9 | }); 10 | 11 | afterEach(function () { 12 | jasmine.Ajax.uninstall(); 13 | delete axios.defaults.baseURL; 14 | delete axios.defaults.headers.get['X-CUSTOM-HEADER']; 15 | delete axios.defaults.headers.post['X-CUSTOM-HEADER']; 16 | document.cookie = XSRF_COOKIE_NAME + '=;expires=' + new Date(Date.now() - 86400000).toGMTString(); 17 | }); 18 | 19 | it('should transform request json', function () { 20 | expect(defaults.transformRequest[0]({foo: 'bar'})).toEqual('{"foo":"bar"}'); 21 | }); 22 | 23 | it('should do nothing to request string', function () { 24 | expect(defaults.transformRequest[0]('foo=bar')).toEqual('foo=bar'); 25 | }); 26 | 27 | it('should transform response json', function () { 28 | var data = defaults.transformResponse[0]('{"foo":"bar"}'); 29 | 30 | expect(typeof data).toEqual('object'); 31 | expect(data.foo).toEqual('bar'); 32 | }); 33 | 34 | it('should do nothing to response string', function () { 35 | expect(defaults.transformResponse[0]('foo=bar')).toEqual('foo=bar'); 36 | }); 37 | 38 | it('should use global defaults config', function (done) { 39 | axios('/foo'); 40 | 41 | getAjaxRequest().then(function (request) { 42 | expect(request.url).toBe('/foo'); 43 | done(); 44 | }); 45 | }); 46 | 47 | it('should use modified defaults config', function (done) { 48 | axios.defaults.baseURL = 'http://example.com/'; 49 | 50 | axios('/foo'); 51 | 52 | getAjaxRequest().then(function (request) { 53 | expect(request.url).toBe('http://example.com/foo'); 54 | done(); 55 | }); 56 | }); 57 | 58 | it('should use request config', function (done) { 59 | axios('/foo', { 60 | baseURL: 'http://www.example.com' 61 | }); 62 | 63 | getAjaxRequest().then(function (request) { 64 | expect(request.url).toBe('http://www.example.com/foo'); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('should use default config for custom instance', function (done) { 70 | var instance = axios.create({ 71 | xsrfCookieName: XSRF_COOKIE_NAME, 72 | xsrfHeaderName: 'X-CUSTOM-XSRF-TOKEN' 73 | }); 74 | document.cookie = instance.defaults.xsrfCookieName + '=foobarbaz'; 75 | 76 | instance.get('/foo'); 77 | 78 | getAjaxRequest().then(function (request) { 79 | expect(request.requestHeaders[instance.defaults.xsrfHeaderName]).toEqual('foobarbaz'); 80 | done(); 81 | }); 82 | }); 83 | 84 | it('should use GET headers', function (done) { 85 | axios.defaults.headers.get['X-CUSTOM-HEADER'] = 'foo'; 86 | axios.get('/foo'); 87 | 88 | getAjaxRequest().then(function (request) { 89 | expect(request.requestHeaders['X-CUSTOM-HEADER']).toBe('foo'); 90 | done(); 91 | }); 92 | }); 93 | 94 | it('should use POST headers', function (done) { 95 | axios.defaults.headers.post['X-CUSTOM-HEADER'] = 'foo'; 96 | axios.post('/foo', {}); 97 | 98 | getAjaxRequest().then(function (request) { 99 | expect(request.requestHeaders['X-CUSTOM-HEADER']).toBe('foo'); 100 | done(); 101 | }); 102 | }); 103 | 104 | it('should use header config', function (done) { 105 | var instance = axios.create({ 106 | headers: { 107 | common: { 108 | 'X-COMMON-HEADER': 'commonHeaderValue' 109 | }, 110 | get: { 111 | 'X-GET-HEADER': 'getHeaderValue' 112 | }, 113 | post: { 114 | 'X-POST-HEADER': 'postHeaderValue' 115 | } 116 | } 117 | }); 118 | 119 | instance.get('/foo', { 120 | headers: { 121 | 'X-FOO-HEADER': 'fooHeaderValue', 122 | 'X-BAR-HEADER': 'barHeaderValue' 123 | } 124 | }); 125 | 126 | getAjaxRequest().then(function (request) { 127 | expect(request.requestHeaders).toEqual( 128 | utils.merge(defaults.headers.common, defaults.headers.get, { 129 | 'X-COMMON-HEADER': 'commonHeaderValue', 130 | 'X-GET-HEADER': 'getHeaderValue', 131 | 'X-FOO-HEADER': 'fooHeaderValue', 132 | 'X-BAR-HEADER': 'barHeaderValue' 133 | }) 134 | ); 135 | done(); 136 | }); 137 | }); 138 | 139 | it('should be used by custom instance if set before instance created', function (done) { 140 | axios.defaults.baseURL = 'http://example.org/'; 141 | var instance = axios.create(); 142 | 143 | instance.get('/foo'); 144 | 145 | getAjaxRequest().then(function (request) { 146 | expect(request.url).toBe('http://example.org/foo'); 147 | done(); 148 | }); 149 | }); 150 | 151 | it('should be used by custom instance if set after instance created', function (done) { 152 | var instance = axios.create(); 153 | axios.defaults.baseURL = 'http://example.org/'; 154 | 155 | instance.get('/foo'); 156 | 157 | getAjaxRequest().then(function (request) { 158 | expect(request.url).toBe('http://example.org/foo'); 159 | done(); 160 | }); 161 | }); 162 | }); 163 | -------------------------------------------------------------------------------- /UPGRADE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ### 0.15.x -> 0.16.0 4 | 5 | #### `Promise` Type Declarations 6 | 7 | The `Promise` type declarations have been removed from the axios typings in favor of the built-in type declarations. If you use axios in a TypeScript project that targets `ES5`, please make sure to include the `es2015.promise` lib. Please see [this post](https://blog.mariusschulz.com/2016/11/25/typescript-2-0-built-in-type-declarations) for details. 8 | 9 | ### 0.13.x -> 0.14.0 10 | 11 | #### TypeScript Definitions 12 | 13 | The axios TypeScript definitions have been updated to match the axios API and use the ES2015 module syntax. 14 | 15 | Please use the following `import` statement to import axios in TypeScript: 16 | 17 | ```typescript 18 | import axios from 'axios'; 19 | 20 | axios.get('/foo') 21 | .then(response => console.log(response)) 22 | .catch(error => console.log(error)); 23 | ``` 24 | 25 | #### `agent` Config Option 26 | 27 | The `agent` config option has been replaced with two new options: `httpAgent` and `httpsAgent`. Please use them instead. 28 | 29 | ```js 30 | { 31 | // Define a custom agent for HTTP 32 | httpAgent: new http.Agent({ keepAlive: true }), 33 | // Define a custom agent for HTTPS 34 | httpsAgent: new https.Agent({ keepAlive: true }) 35 | } 36 | ``` 37 | 38 | #### `progress` Config Option 39 | 40 | The `progress` config option has been replaced with the `onUploadProgress` and `onDownloadProgress` options. 41 | 42 | ```js 43 | { 44 | // Define a handler for upload progress events 45 | onUploadProgress: function (progressEvent) { 46 | // ... 47 | }, 48 | 49 | // Define a handler for download progress events 50 | onDownloadProgress: function (progressEvent) { 51 | // ... 52 | } 53 | } 54 | ``` 55 | 56 | ### 0.12.x -> 0.13.0 57 | 58 | The `0.13.0` release contains several changes to custom adapters and error handling. 59 | 60 | #### Error Handling 61 | 62 | Previous to this release an error could either be a server response with bad status code or an actual `Error`. With this release Promise will always reject with an `Error`. In the case that a response was received, the `Error` will also include the response. 63 | 64 | ```js 65 | axios.get('/user/12345') 66 | .catch((error) => { 67 | console.log(error.message); 68 | console.log(error.code); // Not always specified 69 | console.log(error.config); // The config that was used to make the request 70 | console.log(error.response); // Only available if response was received from the server 71 | }); 72 | ``` 73 | 74 | #### Request Adapters 75 | 76 | This release changes a few things about how request adapters work. Please take note if you are using your own custom adapter. 77 | 78 | 1. Response transformer is now called outside of adapter. 79 | 2. Request adapter returns a `Promise`. 80 | 81 | This means that you no longer need to invoke `transformData` on response data. You will also no longer receive `resolve` and `reject` as arguments in your adapter. 82 | 83 | Previous code: 84 | 85 | ```js 86 | function myAdapter(resolve, reject, config) { 87 | var response = { 88 | data: transformData( 89 | responseData, 90 | responseHeaders, 91 | config.transformResponse 92 | ), 93 | status: request.status, 94 | statusText: request.statusText, 95 | headers: responseHeaders 96 | }; 97 | settle(resolve, reject, response); 98 | } 99 | ``` 100 | 101 | New code: 102 | 103 | ```js 104 | function myAdapter(config) { 105 | return new Promise(function (resolve, reject) { 106 | var response = { 107 | data: responseData, 108 | status: request.status, 109 | statusText: request.statusText, 110 | headers: responseHeaders 111 | }; 112 | settle(resolve, reject, response); 113 | }); 114 | } 115 | ``` 116 | 117 | See the related commits for more details: 118 | - [Response transformers](https://github.com/axios/axios/commit/10eb23865101f9347570552c04e9d6211376e25e) 119 | - [Request adapter Promise](https://github.com/axios/axios/commit/157efd5615890301824e3121cc6c9d2f9b21f94a) 120 | 121 | ### 0.5.x -> 0.6.0 122 | 123 | The `0.6.0` release contains mostly bug fixes, but there are a couple things to be aware of when upgrading. 124 | 125 | #### ES6 Promise Polyfill 126 | 127 | Up until the `0.6.0` release ES6 `Promise` was being polyfilled using [es6-promise](https://github.com/jakearchibald/es6-promise). With this release, the polyfill has been removed, and you will need to supply it yourself if your environment needs it. 128 | 129 | ```js 130 | require('es6-promise').polyfill(); 131 | var axios = require('axios'); 132 | ``` 133 | 134 | This will polyfill the global environment, and only needs to be done once. 135 | 136 | #### `axios.success`/`axios.error` 137 | 138 | The `success`, and `error` aliases were deprectated in [0.4.0](https://github.com/axios/axios/blob/master/CHANGELOG.md#040-oct-03-2014). As of this release they have been removed entirely. Instead please use `axios.then`, and `axios.catch` respectively. 139 | 140 | ```js 141 | axios.get('some/url') 142 | .then(function (res) { 143 | /* ... */ 144 | }) 145 | .catch(function (err) { 146 | /* ... */ 147 | }); 148 | ``` 149 | 150 | #### UMD 151 | 152 | Previous versions of axios shipped with an AMD, CommonJS, and Global build. This has all been rolled into a single UMD build. 153 | 154 | ```js 155 | // AMD 156 | require(['bower_components/axios/dist/axios'], function (axios) { 157 | /* ... */ 158 | }); 159 | 160 | // CommonJS 161 | var axios = require('axios/dist/axios'); 162 | ``` 163 | -------------------------------------------------------------------------------- /sandbox/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | axios 5 | 6 | 13 | 14 | 15 |

axios

16 | 17 |
18 |

Input

19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 34 |
35 |
36 | 37 | 38 |
39 | 43 |
44 | 45 | 46 |
47 | 48 |
49 |
50 | 51 |
52 |

Request

53 |
No Data
54 |
55 | 56 |
57 |

Response

58 |
No Data
59 |
60 | 61 | 62 | 172 | 173 | -------------------------------------------------------------------------------- /lib/adapters/xhr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | var settle = require('./../core/settle'); 5 | var buildURL = require('./../helpers/buildURL'); 6 | var parseHeaders = require('./../helpers/parseHeaders'); 7 | var isURLSameOrigin = require('./../helpers/isURLSameOrigin'); 8 | var createError = require('../core/createError'); 9 | var btoa = (typeof window !== 'undefined' && window.btoa && window.btoa.bind(window)) || require('./../helpers/btoa'); 10 | 11 | module.exports = function xhrAdapter(config) { 12 | return new Promise(function dispatchXhrRequest(resolve, reject) { 13 | var requestData = config.data; 14 | var requestHeaders = config.headers; 15 | 16 | if (utils.isFormData(requestData)) { 17 | delete requestHeaders['Content-Type']; // Let the browser set it 18 | } 19 | 20 | var request = new XMLHttpRequest(); 21 | var loadEvent = 'onreadystatechange'; 22 | var xDomain = false; 23 | 24 | // For IE 8/9 CORS support 25 | // Only supports POST and GET calls and doesn't returns the response headers. 26 | // DON'T do this for testing b/c XMLHttpRequest is mocked, not XDomainRequest. 27 | if (process.env.NODE_ENV !== 'test' && 28 | typeof window !== 'undefined' && 29 | window.XDomainRequest && !('withCredentials' in request) && 30 | !isURLSameOrigin(config.url)) { 31 | request = new window.XDomainRequest(); 32 | loadEvent = 'onload'; 33 | xDomain = true; 34 | request.onprogress = function handleProgress() {}; 35 | request.ontimeout = function handleTimeout() {}; 36 | } 37 | 38 | // HTTP basic authentication 39 | if (config.auth) { 40 | var username = config.auth.username || ''; 41 | var password = config.auth.password || ''; 42 | requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); 43 | } 44 | 45 | request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); 46 | 47 | // Set the request timeout in MS 48 | request.timeout = config.timeout; 49 | 50 | // Listen for ready state 51 | request[loadEvent] = function handleLoad() { 52 | if (!request || (request.readyState !== 4 && !xDomain)) { 53 | return; 54 | } 55 | 56 | // The request errored out and we didn't get a response, this will be 57 | // handled by onerror instead 58 | // With one exception: request that using file: protocol, most browsers 59 | // will return status as 0 even though it's a successful request 60 | if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) { 61 | return; 62 | } 63 | 64 | // Prepare the response 65 | var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; 66 | var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; 67 | var response = { 68 | data: responseData, 69 | // IE sends 1223 instead of 204 (https://github.com/axios/axios/issues/201) 70 | status: request.status === 1223 ? 204 : request.status, 71 | statusText: request.status === 1223 ? 'No Content' : request.statusText, 72 | headers: responseHeaders, 73 | config: config, 74 | request: request 75 | }; 76 | 77 | settle(resolve, reject, response); 78 | 79 | // Clean up request 80 | request = null; 81 | }; 82 | 83 | // Handle low level network errors 84 | request.onerror = function handleError() { 85 | // Real errors are hidden from us by the browser 86 | // onerror should only fire if it's a network error 87 | reject(createError('Network Error', config, null, request)); 88 | 89 | // Clean up request 90 | request = null; 91 | }; 92 | 93 | // Handle timeout 94 | request.ontimeout = function handleTimeout() { 95 | reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', 96 | request)); 97 | 98 | // Clean up request 99 | request = null; 100 | }; 101 | 102 | // Add xsrf header 103 | // This is only done if running in a standard browser environment. 104 | // Specifically not if we're in a web worker, or react-native. 105 | if (utils.isStandardBrowserEnv()) { 106 | var cookies = require('./../helpers/cookies'); 107 | 108 | // Add xsrf header 109 | var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ? 110 | cookies.read(config.xsrfCookieName) : 111 | undefined; 112 | 113 | if (xsrfValue) { 114 | requestHeaders[config.xsrfHeaderName] = xsrfValue; 115 | } 116 | } 117 | 118 | // Add headers to the request 119 | if ('setRequestHeader' in request) { 120 | utils.forEach(requestHeaders, function setRequestHeader(val, key) { 121 | if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { 122 | // Remove Content-Type if data is undefined 123 | delete requestHeaders[key]; 124 | } else { 125 | // Otherwise add header to the request 126 | request.setRequestHeader(key, val); 127 | } 128 | }); 129 | } 130 | 131 | // Add withCredentials to request if needed 132 | if (config.withCredentials) { 133 | request.withCredentials = true; 134 | } 135 | 136 | // Add responseType to request if needed 137 | if (config.responseType) { 138 | try { 139 | request.responseType = config.responseType; 140 | } catch (e) { 141 | // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2. 142 | // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function. 143 | if (config.responseType !== 'json') { 144 | throw e; 145 | } 146 | } 147 | } 148 | 149 | // Handle progress if needed 150 | if (typeof config.onDownloadProgress === 'function') { 151 | request.addEventListener('progress', config.onDownloadProgress); 152 | } 153 | 154 | // Not all browsers support upload events 155 | if (typeof config.onUploadProgress === 'function' && request.upload) { 156 | request.upload.addEventListener('progress', config.onUploadProgress); 157 | } 158 | 159 | if (config.cancelToken) { 160 | // Handle cancellation 161 | config.cancelToken.promise.then(function onCanceled(cancel) { 162 | if (!request) { 163 | return; 164 | } 165 | 166 | request.abort(); 167 | reject(cancel); 168 | // Clean up request 169 | request = null; 170 | }); 171 | } 172 | 173 | if (requestData === undefined) { 174 | requestData = null; 175 | } 176 | 177 | // Send the request 178 | request.send(requestData); 179 | }); 180 | }; 181 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Fri Aug 15 2014 23:11:13 GMT-0500 (CDT) 3 | 4 | var webpack = require('webpack'); 5 | 6 | function createCustomLauncher(browser, version, platform) { 7 | return { 8 | base: 'SauceLabs', 9 | browserName: browser, 10 | version: version, 11 | platform: platform 12 | }; 13 | } 14 | 15 | module.exports = function(config) { 16 | var customLaunchers = {}; 17 | var browsers = []; 18 | 19 | if (process.env.SAUCE_USERNAME || process.env.SAUCE_ACCESS_KEY) { 20 | customLaunchers = {}; 21 | 22 | var runAll = true; 23 | var options = [ 24 | 'SAUCE_CHROME', 25 | 'SAUCE_FIREFOX', 26 | 'SAUCE_SAFARI', 27 | 'SAUCE_OPERA', 28 | 'SAUCE_IE', 29 | 'SAUCE_EDGE', 30 | 'SAUCE_IOS', 31 | 'SAUCE_ANDROID' 32 | ]; 33 | 34 | options.forEach(function (opt) { 35 | if (process.env[opt]) { 36 | runAll = false; 37 | } 38 | }); 39 | 40 | // Chrome 41 | if (runAll || process.env.SAUCE_CHROME) { 42 | customLaunchers.SL_Chrome = createCustomLauncher('chrome'); 43 | // customLaunchers.SL_ChromeDev = createCustomLauncher('chrome', 'dev'); 44 | // customLaunchers.SL_ChromeBeta = createCustomLauncher('chrome', 'beta'); 45 | } 46 | 47 | // Firefox 48 | if (runAll || process.env.SAUCE_FIREFOX) { 49 | customLaunchers.SL_Firefox = createCustomLauncher('firefox'); 50 | // customLaunchers.SL_FirefoxDev = createCustomLauncher('firefox', 'dev'); 51 | // customLaunchers.SL_FirefoxBeta = createCustomLauncher('firefox', 'beta'); 52 | } 53 | 54 | // Safari 55 | if (runAll || process.env.SAUCE_SAFARI) { 56 | // customLaunchers.SL_Safari7 = createCustomLauncher('safari', 7); 57 | // customLaunchers.SL_Safari8 = createCustomLauncher('safari', 8); 58 | 59 | customLaunchers.SL_Safari9 = createCustomLauncher('safari', 9); 60 | } 61 | 62 | // Opera 63 | if (runAll || process.env.SAUCE_OPERA) { 64 | // TODO The available versions of Opera are too old and lack basic APIs 65 | // customLaunchers.SL_Opera11 = createCustomLauncher('opera', 11, 'Windows XP'); 66 | // customLaunchers.SL_Opera12 = createCustomLauncher('opera', 12, 'Windows 7'); 67 | } 68 | 69 | // IE 70 | if (runAll || process.env.SAUCE_IE) { 71 | // customLaunchers.SL_IE8 = createCustomLauncher('internet explorer', 8, 'Windows 7'); 72 | customLaunchers.SL_IE9 = createCustomLauncher('internet explorer', 9, 'Windows 2008'); 73 | customLaunchers.SL_IE10 = createCustomLauncher('internet explorer', 10, 'Windows 2012'); 74 | customLaunchers.SL_IE11 = createCustomLauncher('internet explorer', 11, 'Windows 8.1'); 75 | } 76 | 77 | // Edge 78 | if (runAll || process.env.SAUCE_EDGE) { 79 | customLaunchers.SL_Edge = createCustomLauncher('microsoftedge', null, 'Windows 10'); 80 | } 81 | 82 | // IOS 83 | if (runAll || process.env.SAUCE_IOS) { 84 | // TODO IOS7 capture always timesout 85 | // customLaunchers.SL_IOS7 = createCustomLauncher('iphone', '7.1', 'OS X 10.10'); 86 | // TODO Mobile browsers are causing failures, possibly from too many concurrent VMs 87 | // customLaunchers.SL_IOS8 = createCustomLauncher('iphone', '8.4', 'OS X 10.10'); 88 | // customLaunchers.SL_IOS9 = createCustomLauncher('iphone', '9.2', 'OS X 10.10'); 89 | } 90 | 91 | // Android 92 | if (runAll || process.env.SAUCE_ANDROID) { 93 | // TODO Mobile browsers are causing failures, possibly from too many concurrent VMs 94 | // customLaunchers.SL_Android4 = createCustomLauncher('android', '4.4', 'Linux'); 95 | // customLaunchers.SL_Android5 = createCustomLauncher('android', '5.1', 'Linux'); 96 | } 97 | 98 | browsers = Object.keys(customLaunchers); 99 | } else if (process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false') { 100 | console.log( 101 | 'Cannot run on Sauce Labs as encrypted environment variables are not available to PRs. ' + 102 | 'Running on Travis.' 103 | ); 104 | browsers = ['Firefox']; 105 | } else { 106 | console.log('Running locally since SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are not set.'); 107 | browsers = ['Firefox', 'Chrome', 'Safari', 'Opera']; 108 | } 109 | 110 | config.set({ 111 | // base path that will be used to resolve all patterns (eg. files, exclude) 112 | basePath: '', 113 | 114 | 115 | // frameworks to use 116 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 117 | frameworks: ['jasmine-ajax', 'jasmine', 'sinon'], 118 | 119 | 120 | // list of files / patterns to load in the browser 121 | files: [ 122 | 'test/specs/__helpers.js', 123 | 'test/specs/**/*.spec.js', 124 | ], 125 | 126 | 127 | // list of files to exclude 128 | exclude: [ 129 | 130 | ], 131 | 132 | 133 | // preprocess matching files before serving them to the browser 134 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 135 | preprocessors: { 136 | 'test/specs/__helpers.js': ['webpack', 'sourcemap'], 137 | 'test/specs/**/*.spec.js': ['webpack', 'sourcemap'] 138 | }, 139 | 140 | 141 | // test results reporter to use 142 | // possible values: 'dots', 'progress' 143 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 144 | reporters: ['dots', 'coverage', 'saucelabs'], 145 | 146 | 147 | // web server port 148 | port: 9876, 149 | 150 | 151 | // Increase timeouts to prevent the issue with disconnected tests (https://goo.gl/nstA69) 152 | captureTimeout: 4 * 60 * 1000, 153 | browserDisconnectTimeout: 10000, 154 | browserDisconnectTolerance: 1, 155 | browserNoActivityTimeout: 4 * 60 * 1000, 156 | 157 | 158 | // enable / disable colors in the output (reporters and logs) 159 | colors: true, 160 | 161 | 162 | // level of logging 163 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 164 | logLevel: config.LOG_INFO, 165 | 166 | 167 | // enable / disable watching file and executing tests whenever any file changes 168 | autoWatch: false, 169 | 170 | 171 | // start these browsers 172 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 173 | browsers: browsers, 174 | 175 | 176 | // Continuous Integration mode 177 | // if true, Karma captures browsers, runs the tests and exits 178 | singleRun: false, 179 | 180 | // Webpack config 181 | webpack: { 182 | cache: true, 183 | devtool: 'inline-source-map', 184 | module: { 185 | postLoaders: [ 186 | { 187 | test: /\.js$/, 188 | exclude: /(node_modules|test)/, 189 | loader: 'istanbul-instrumenter' 190 | } 191 | ] 192 | }, 193 | externals: [ 194 | { 195 | './adapters/http': 'var undefined' 196 | } 197 | ], 198 | plugins: [ 199 | new webpack.DefinePlugin({ 200 | 'process.env.NODE_ENV': JSON.stringify('test') 201 | }) 202 | ] 203 | }, 204 | 205 | webpackServer: { 206 | stats: { 207 | colors: true 208 | } 209 | }, 210 | 211 | 212 | // Coverage reporting 213 | coverageReporter: { 214 | type: 'lcov', 215 | dir: 'coverage/', 216 | subdir: '.' 217 | }, 218 | 219 | 220 | // SauceLabs config 221 | sauceLabs: { 222 | recordScreenshots: false, 223 | connectOptions: { 224 | // port: 5757, 225 | logfile: 'sauce_connect.log' 226 | }, 227 | public: 'public' 228 | }, 229 | 230 | customLaunchers: customLaunchers 231 | }); 232 | }; 233 | -------------------------------------------------------------------------------- /test/specs/interceptors.spec.js: -------------------------------------------------------------------------------- 1 | describe('interceptors', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | axios.interceptors.request.handlers = []; 9 | axios.interceptors.response.handlers = []; 10 | }); 11 | 12 | it('should add a request interceptor', function (done) { 13 | axios.interceptors.request.use(function (config) { 14 | config.headers.test = 'added by interceptor'; 15 | return config; 16 | }); 17 | 18 | axios('/foo'); 19 | 20 | getAjaxRequest().then(function (request) { 21 | request.respondWith({ 22 | status: 200, 23 | responseText: 'OK' 24 | }); 25 | 26 | expect(request.requestHeaders.test).toBe('added by interceptor'); 27 | done(); 28 | }); 29 | }); 30 | 31 | it('should add a request interceptor that returns a new config object', function (done) { 32 | axios.interceptors.request.use(function () { 33 | return { 34 | url: '/bar', 35 | method: 'post' 36 | }; 37 | }); 38 | 39 | axios('/foo'); 40 | 41 | getAjaxRequest().then(function (request) { 42 | expect(request.method).toBe('POST'); 43 | expect(request.url).toBe('/bar'); 44 | done(); 45 | }); 46 | }); 47 | 48 | it('should add a request interceptor that returns a promise', function (done) { 49 | axios.interceptors.request.use(function (config) { 50 | return new Promise(function (resolve) { 51 | // do something async 52 | setTimeout(function () { 53 | config.headers.async = 'promise'; 54 | resolve(config); 55 | }, 100); 56 | }); 57 | }); 58 | 59 | axios('/foo'); 60 | 61 | getAjaxRequest().then(function (request) { 62 | expect(request.requestHeaders.async).toBe('promise'); 63 | done(); 64 | }); 65 | }); 66 | 67 | it('should add multiple request interceptors', function (done) { 68 | axios.interceptors.request.use(function (config) { 69 | config.headers.test1 = '1'; 70 | return config; 71 | }); 72 | axios.interceptors.request.use(function (config) { 73 | config.headers.test2 = '2'; 74 | return config; 75 | }); 76 | axios.interceptors.request.use(function (config) { 77 | config.headers.test3 = '3'; 78 | return config; 79 | }); 80 | 81 | axios('/foo'); 82 | 83 | getAjaxRequest().then(function (request) { 84 | expect(request.requestHeaders.test1).toBe('1'); 85 | expect(request.requestHeaders.test2).toBe('2'); 86 | expect(request.requestHeaders.test3).toBe('3'); 87 | done(); 88 | }); 89 | }); 90 | 91 | it('should add a response interceptor', function (done) { 92 | var response; 93 | 94 | axios.interceptors.response.use(function (data) { 95 | data.data = data.data + ' - modified by interceptor'; 96 | return data; 97 | }); 98 | 99 | axios('/foo').then(function (data) { 100 | response = data; 101 | }); 102 | 103 | getAjaxRequest().then(function (request) { 104 | request.respondWith({ 105 | status: 200, 106 | responseText: 'OK' 107 | }); 108 | 109 | setTimeout(function () { 110 | expect(response.data).toBe('OK - modified by interceptor'); 111 | done(); 112 | }, 100); 113 | }); 114 | }); 115 | 116 | it('should add a response interceptor that returns a new data object', function (done) { 117 | var response; 118 | 119 | axios.interceptors.response.use(function () { 120 | return { 121 | data: 'stuff' 122 | }; 123 | }); 124 | 125 | axios('/foo').then(function (data) { 126 | response = data; 127 | }); 128 | 129 | getAjaxRequest().then(function (request) { 130 | request.respondWith({ 131 | status: 200, 132 | responseText: 'OK' 133 | }); 134 | 135 | setTimeout(function () { 136 | expect(response.data).toBe('stuff'); 137 | done(); 138 | }, 100); 139 | }); 140 | }); 141 | 142 | it('should add a response interceptor that returns a promise', function (done) { 143 | var response; 144 | 145 | axios.interceptors.response.use(function (data) { 146 | return new Promise(function (resolve) { 147 | // do something async 148 | setTimeout(function () { 149 | data.data = 'you have been promised!'; 150 | resolve(data); 151 | }, 10); 152 | }); 153 | }); 154 | 155 | axios('/foo').then(function (data) { 156 | response = data; 157 | }); 158 | 159 | getAjaxRequest().then(function (request) { 160 | request.respondWith({ 161 | status: 200, 162 | responseText: 'OK' 163 | }); 164 | 165 | setTimeout(function () { 166 | expect(response.data).toBe('you have been promised!'); 167 | done(); 168 | }, 100); 169 | }); 170 | }); 171 | 172 | it('should add multiple response interceptors', function (done) { 173 | var response; 174 | 175 | axios.interceptors.response.use(function (data) { 176 | data.data = data.data + '1'; 177 | return data; 178 | }); 179 | axios.interceptors.response.use(function (data) { 180 | data.data = data.data + '2'; 181 | return data; 182 | }); 183 | axios.interceptors.response.use(function (data) { 184 | data.data = data.data + '3'; 185 | return data; 186 | }); 187 | 188 | axios('/foo').then(function (data) { 189 | response = data; 190 | }); 191 | 192 | getAjaxRequest().then(function (request) { 193 | request.respondWith({ 194 | status: 200, 195 | responseText: 'OK' 196 | }); 197 | 198 | setTimeout(function () { 199 | expect(response.data).toBe('OK123'); 200 | done(); 201 | }, 100); 202 | }); 203 | }); 204 | 205 | it('should allow removing interceptors', function (done) { 206 | var response, intercept; 207 | 208 | axios.interceptors.response.use(function (data) { 209 | data.data = data.data + '1'; 210 | return data; 211 | }); 212 | intercept = axios.interceptors.response.use(function (data) { 213 | data.data = data.data + '2'; 214 | return data; 215 | }); 216 | axios.interceptors.response.use(function (data) { 217 | data.data = data.data + '3'; 218 | return data; 219 | }); 220 | 221 | axios.interceptors.response.eject(intercept); 222 | 223 | axios('/foo').then(function (data) { 224 | response = data; 225 | }); 226 | 227 | getAjaxRequest().then(function (request) { 228 | request.respondWith({ 229 | status: 200, 230 | responseText: 'OK' 231 | }); 232 | 233 | setTimeout(function () { 234 | expect(response.data).toBe('OK13'); 235 | done(); 236 | }, 100); 237 | }); 238 | }); 239 | 240 | it('should execute interceptors before transformers', function (done) { 241 | axios.interceptors.request.use(function (config) { 242 | config.data.baz = 'qux'; 243 | return config; 244 | }); 245 | 246 | axios.post('/foo', { 247 | foo: 'bar' 248 | }); 249 | 250 | getAjaxRequest().then(function (request) { 251 | expect(request.params).toEqual('{"foo":"bar","baz":"qux"}'); 252 | done(); 253 | }); 254 | }); 255 | 256 | it('should modify base URL in request interceptor', function (done) { 257 | var instance = axios.create({ 258 | baseURL: 'http://test.com/' 259 | }); 260 | 261 | instance.interceptors.request.use(function (config) { 262 | config.baseURL = 'http://rebase.com/'; 263 | return config; 264 | }); 265 | 266 | instance.get('/foo'); 267 | 268 | getAjaxRequest().then(function (request) { 269 | expect(request.url).toBe('http://rebase.com/foo'); 270 | done(); 271 | }); 272 | }); 273 | }); 274 | -------------------------------------------------------------------------------- /test/typescript/axios.ts: -------------------------------------------------------------------------------- 1 | import axios, { 2 | AxiosRequestConfig, 3 | AxiosResponse, 4 | AxiosError, 5 | AxiosInstance, 6 | AxiosAdapter, 7 | Cancel, 8 | CancelToken, 9 | CancelTokenSource, 10 | Canceler 11 | } from '../../'; 12 | 13 | const config: AxiosRequestConfig = { 14 | url: '/user', 15 | method: 'get', 16 | baseURL: 'https://api.example.com/', 17 | transformRequest: (data: any) => '{"foo":"bar"}', 18 | transformResponse: [ 19 | (data: any) => ({ baz: 'qux' }) 20 | ], 21 | headers: { 'X-FOO': 'bar' }, 22 | params: { id: 12345 }, 23 | paramsSerializer: (params: any) => 'id=12345', 24 | data: { foo: 'bar' }, 25 | timeout: 10000, 26 | withCredentials: true, 27 | auth: { 28 | username: 'janedoe', 29 | password: 's00pers3cret' 30 | }, 31 | responseType: 'json', 32 | xsrfCookieName: 'XSRF-TOKEN', 33 | xsrfHeaderName: 'X-XSRF-TOKEN', 34 | onUploadProgress: (progressEvent: any) => {}, 35 | onDownloadProgress: (progressEvent: any) => {}, 36 | maxContentLength: 2000, 37 | validateStatus: (status: number) => status >= 200 && status < 300, 38 | maxRedirects: 5, 39 | proxy: { 40 | host: '127.0.0.1', 41 | port: 9000 42 | }, 43 | cancelToken: new axios.CancelToken((cancel: Canceler) => {}) 44 | }; 45 | 46 | const handleResponse = (response: AxiosResponse) => { 47 | console.log(response.data); 48 | console.log(response.status); 49 | console.log(response.statusText); 50 | console.log(response.headers); 51 | console.log(response.config); 52 | }; 53 | 54 | const handleError = (error: AxiosError) => { 55 | if (error.response) { 56 | console.log(error.response.data); 57 | console.log(error.response.status); 58 | console.log(error.response.headers); 59 | } else { 60 | console.log(error.message); 61 | } 62 | }; 63 | 64 | axios(config) 65 | .then(handleResponse) 66 | .catch(handleError); 67 | 68 | axios.get('/user?id=12345') 69 | .then(handleResponse) 70 | .catch(handleError); 71 | 72 | axios.get('/user', { params: { id: 12345 } }) 73 | .then(handleResponse) 74 | .catch(handleError); 75 | 76 | axios.head('/user') 77 | .then(handleResponse) 78 | .catch(handleError); 79 | 80 | axios.delete('/user') 81 | .then(handleResponse) 82 | .catch(handleError); 83 | 84 | axios.post('/user', { foo: 'bar' }) 85 | .then(handleResponse) 86 | .catch(handleError); 87 | 88 | axios.post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) 89 | .then(handleResponse) 90 | .catch(handleError); 91 | 92 | axios.put('/user', { foo: 'bar' }) 93 | .then(handleResponse) 94 | .catch(handleError); 95 | 96 | axios.patch('/user', { foo: 'bar' }) 97 | .then(handleResponse) 98 | .catch(handleError); 99 | 100 | // Typed methods 101 | interface User { 102 | id: number; 103 | name: string; 104 | } 105 | 106 | const handleUserResponse = (response: AxiosResponse) => { 107 | console.log(response.data.id); 108 | console.log(response.data.name); 109 | console.log(response.status); 110 | console.log(response.statusText); 111 | console.log(response.headers); 112 | console.log(response.config); 113 | }; 114 | 115 | axios.get('/user?id=12345') 116 | .then(handleUserResponse) 117 | .catch(handleError); 118 | 119 | axios.get('/user', { params: { id: 12345 } }) 120 | .then(handleUserResponse) 121 | .catch(handleError); 122 | 123 | axios.post('/user', { foo: 'bar' }) 124 | .then(handleUserResponse) 125 | .catch(handleError); 126 | 127 | axios.post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) 128 | .then(handleUserResponse) 129 | .catch(handleError); 130 | 131 | axios.put('/user', { foo: 'bar' }) 132 | .then(handleUserResponse) 133 | .catch(handleError); 134 | 135 | axios.patch('/user', { foo: 'bar' }) 136 | .then(handleUserResponse) 137 | .catch(handleError); 138 | 139 | // Instances 140 | 141 | const instance1: AxiosInstance = axios.create(); 142 | const instance2: AxiosInstance = axios.create(config); 143 | 144 | instance1.request(config) 145 | .then(handleResponse) 146 | .catch(handleError); 147 | 148 | instance1.get('/user?id=12345') 149 | .then(handleResponse) 150 | .catch(handleError); 151 | 152 | instance1.get('/user', { params: { id: 12345 } }) 153 | .then(handleResponse) 154 | .catch(handleError); 155 | 156 | instance1.post('/user', { foo: 'bar' }) 157 | .then(handleResponse) 158 | .catch(handleError); 159 | 160 | instance1.post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) 161 | .then(handleResponse) 162 | .catch(handleError); 163 | 164 | // Defaults 165 | 166 | axios.defaults.baseURL = 'https://api.example.com/'; 167 | axios.defaults.headers.common['Authorization'] = 'token'; 168 | axios.defaults.headers.post['X-FOO'] = 'bar'; 169 | axios.defaults.timeout = 2500; 170 | 171 | instance1.defaults.baseURL = 'https://api.example.com/'; 172 | instance1.defaults.headers.common['Authorization'] = 'token'; 173 | instance1.defaults.headers.post['X-FOO'] = 'bar'; 174 | instance1.defaults.timeout = 2500; 175 | 176 | // Interceptors 177 | 178 | const requestInterceptorId: number = axios.interceptors.request.use( 179 | (config: AxiosRequestConfig) => config, 180 | (error: any) => Promise.reject(error) 181 | ); 182 | 183 | axios.interceptors.request.eject(requestInterceptorId); 184 | 185 | axios.interceptors.request.use( 186 | (config: AxiosRequestConfig) => Promise.resolve(config), 187 | (error: any) => Promise.reject(error) 188 | ); 189 | 190 | axios.interceptors.request.use((config: AxiosRequestConfig) => config); 191 | axios.interceptors.request.use((config: AxiosRequestConfig) => Promise.resolve(config)); 192 | 193 | const responseInterceptorId: number = axios.interceptors.response.use( 194 | (response: AxiosResponse) => response, 195 | (error: any) => Promise.reject(error) 196 | ); 197 | 198 | axios.interceptors.response.eject(responseInterceptorId); 199 | 200 | axios.interceptors.response.use( 201 | (response: AxiosResponse) => Promise.resolve(response), 202 | (error: any) => Promise.reject(error) 203 | ); 204 | 205 | axios.interceptors.response.use((response: AxiosResponse) => response); 206 | axios.interceptors.response.use((response: AxiosResponse) => Promise.resolve(response)); 207 | 208 | // Adapters 209 | 210 | const adapter: AxiosAdapter = (config: AxiosRequestConfig) => { 211 | const response: AxiosResponse = { 212 | data: { foo: 'bar' }, 213 | status: 200, 214 | statusText: 'OK', 215 | headers: { 'X-FOO': 'bar' }, 216 | config 217 | }; 218 | return Promise.resolve(response); 219 | }; 220 | 221 | axios.defaults.adapter = adapter; 222 | 223 | // axios.all 224 | 225 | const promises = [ 226 | Promise.resolve(1), 227 | Promise.resolve(2) 228 | ]; 229 | 230 | const promise: Promise = axios.all(promises); 231 | 232 | // axios.spread 233 | 234 | const fn1 = (a: number, b: number, c: number) => `${a}-${b}-${c}`; 235 | const fn2: (arr: number[]) => string = axios.spread(fn1); 236 | 237 | // Promises 238 | 239 | axios.get('/user') 240 | .then((response: AxiosResponse) => 'foo') 241 | .then((value: string) => {}); 242 | 243 | axios.get('/user') 244 | .then((response: AxiosResponse) => Promise.resolve('foo')) 245 | .then((value: string) => {}); 246 | 247 | axios.get('/user') 248 | .then((response: AxiosResponse) => 'foo', (error: any) => 'bar') 249 | .then((value: string) => {}); 250 | 251 | axios.get('/user') 252 | .then((response: AxiosResponse) => 'foo', (error: any) => 123) 253 | .then((value: string | number) => {}); 254 | 255 | axios.get('/user') 256 | .catch((error: any) => 'foo') 257 | .then((value: string) => {}); 258 | 259 | axios.get('/user') 260 | .catch((error: any) => Promise.resolve('foo')) 261 | .then((value: string) => {}); 262 | 263 | // Cancellation 264 | 265 | const source: CancelTokenSource = axios.CancelToken.source(); 266 | 267 | axios.get('/user', { 268 | cancelToken: source.token 269 | }).catch((thrown: AxiosError | Cancel) => { 270 | if (axios.isCancel(thrown)) { 271 | const cancel: Cancel = thrown; 272 | console.log(cancel.message); 273 | } 274 | }); 275 | 276 | source.cancel('Operation has been canceled.'); 277 | -------------------------------------------------------------------------------- /lib/adapters/http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('./../utils'); 4 | var settle = require('./../core/settle'); 5 | var buildURL = require('./../helpers/buildURL'); 6 | var http = require('http'); 7 | var https = require('https'); 8 | var httpFollow = require('follow-redirects').http; 9 | var httpsFollow = require('follow-redirects').https; 10 | var url = require('url'); 11 | var zlib = require('zlib'); 12 | var pkg = require('./../../package.json'); 13 | var createError = require('../core/createError'); 14 | var enhanceError = require('../core/enhanceError'); 15 | 16 | var isHttps = /https:?/; 17 | 18 | /*eslint consistent-return:0*/ 19 | module.exports = function httpAdapter(config) { 20 | return new Promise(function dispatchHttpRequest(resolve, reject) { 21 | var data = config.data; 22 | var headers = config.headers; 23 | var timer; 24 | 25 | // Set User-Agent (required by some servers) 26 | // Only set header if it hasn't been set in config 27 | // See https://github.com/axios/axios/issues/69 28 | if (!headers['User-Agent'] && !headers['user-agent']) { 29 | headers['User-Agent'] = 'axios/' + pkg.version; 30 | } 31 | 32 | if (data && !utils.isStream(data)) { 33 | if (Buffer.isBuffer(data)) { 34 | // Nothing to do... 35 | } else if (utils.isArrayBuffer(data)) { 36 | data = new Buffer(new Uint8Array(data)); 37 | } else if (utils.isString(data)) { 38 | data = new Buffer(data, 'utf-8'); 39 | } else { 40 | return reject(createError( 41 | 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', 42 | config 43 | )); 44 | } 45 | 46 | // Add Content-Length header if data exists 47 | headers['Content-Length'] = data.length; 48 | } 49 | 50 | // HTTP basic authentication 51 | var auth = undefined; 52 | if (config.auth) { 53 | var username = config.auth.username || ''; 54 | var password = config.auth.password || ''; 55 | auth = username + ':' + password; 56 | } 57 | 58 | // Parse url 59 | var parsed = url.parse(config.url); 60 | var protocol = parsed.protocol || 'http:'; 61 | 62 | if (!auth && parsed.auth) { 63 | var urlAuth = parsed.auth.split(':'); 64 | var urlUsername = urlAuth[0] || ''; 65 | var urlPassword = urlAuth[1] || ''; 66 | auth = urlUsername + ':' + urlPassword; 67 | } 68 | 69 | if (auth) { 70 | delete headers.Authorization; 71 | } 72 | 73 | var isHttpsRequest = isHttps.test(protocol); 74 | var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent; 75 | 76 | var options = { 77 | hostname: parsed.hostname, 78 | port: parsed.port, 79 | path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''), 80 | method: config.method, 81 | headers: headers, 82 | agent: agent, 83 | auth: auth 84 | }; 85 | 86 | var proxy = config.proxy; 87 | if (!proxy && proxy !== false) { 88 | var proxyEnv = protocol.slice(0, -1) + '_proxy'; 89 | var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()]; 90 | if (proxyUrl) { 91 | var parsedProxyUrl = url.parse(proxyUrl); 92 | proxy = { 93 | host: parsedProxyUrl.hostname, 94 | port: parsedProxyUrl.port 95 | }; 96 | 97 | if (parsedProxyUrl.auth) { 98 | var proxyUrlAuth = parsedProxyUrl.auth.split(':'); 99 | proxy.auth = { 100 | username: proxyUrlAuth[0], 101 | password: proxyUrlAuth[1] 102 | }; 103 | } 104 | } 105 | } 106 | 107 | if (proxy) { 108 | options.hostname = proxy.host; 109 | options.host = proxy.host; 110 | options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : ''); 111 | options.port = proxy.port; 112 | options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path; 113 | 114 | // Basic proxy authorization 115 | if (proxy.auth) { 116 | var base64 = new Buffer(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64'); 117 | options.headers['Proxy-Authorization'] = 'Basic ' + base64; 118 | } 119 | } 120 | 121 | var transport; 122 | var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true); 123 | if (config.transport) { 124 | transport = config.transport; 125 | } else if (config.maxRedirects === 0) { 126 | transport = isHttpsProxy ? https : http; 127 | } else { 128 | if (config.maxRedirects) { 129 | options.maxRedirects = config.maxRedirects; 130 | } 131 | transport = isHttpsProxy ? httpsFollow : httpFollow; 132 | } 133 | 134 | // Create the request 135 | var req = transport.request(options, function handleResponse(res) { 136 | if (req.aborted) return; 137 | 138 | // Response has been received so kill timer that handles request timeout 139 | clearTimeout(timer); 140 | timer = null; 141 | 142 | // uncompress the response body transparently if required 143 | var stream = res; 144 | switch (res.headers['content-encoding']) { 145 | /*eslint default-case:0*/ 146 | case 'gzip': 147 | case 'compress': 148 | case 'deflate': 149 | // add the unzipper to the body stream processing pipeline 150 | stream = stream.pipe(zlib.createUnzip()); 151 | 152 | // remove the content-encoding in order to not confuse downstream operations 153 | delete res.headers['content-encoding']; 154 | break; 155 | } 156 | 157 | // return the last request in case of redirects 158 | var lastRequest = res.req || req; 159 | 160 | var response = { 161 | status: res.statusCode, 162 | statusText: res.statusMessage, 163 | headers: res.headers, 164 | config: config, 165 | request: lastRequest 166 | }; 167 | 168 | if (config.responseType === 'stream') { 169 | response.data = stream; 170 | settle(resolve, reject, response); 171 | } else { 172 | var responseBuffer = []; 173 | stream.on('data', function handleStreamData(chunk) { 174 | responseBuffer.push(chunk); 175 | 176 | // make sure the content length is not over the maxContentLength if specified 177 | if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) { 178 | reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', 179 | config, null, lastRequest)); 180 | } 181 | }); 182 | 183 | stream.on('error', function handleStreamError(err) { 184 | if (req.aborted) return; 185 | reject(enhanceError(err, config, null, lastRequest)); 186 | }); 187 | 188 | stream.on('end', function handleStreamEnd() { 189 | var responseData = Buffer.concat(responseBuffer); 190 | if (config.responseType !== 'arraybuffer') { 191 | responseData = responseData.toString('utf8'); 192 | } 193 | 194 | response.data = responseData; 195 | settle(resolve, reject, response); 196 | }); 197 | } 198 | }); 199 | 200 | // Handle errors 201 | req.on('error', function handleRequestError(err) { 202 | if (req.aborted) return; 203 | reject(enhanceError(err, config, null, req)); 204 | }); 205 | 206 | // Handle request timeout 207 | if (config.timeout && !timer) { 208 | timer = setTimeout(function handleRequestTimeout() { 209 | req.abort(); 210 | reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req)); 211 | }, config.timeout); 212 | } 213 | 214 | if (config.cancelToken) { 215 | // Handle cancellation 216 | config.cancelToken.promise.then(function onCanceled(cancel) { 217 | if (req.aborted) return; 218 | 219 | req.abort(); 220 | reject(cancel); 221 | }); 222 | } 223 | 224 | // Send the request 225 | if (utils.isStream(data)) { 226 | data.pipe(req); 227 | } else { 228 | req.end(data); 229 | } 230 | }); 231 | }; 232 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "console": true, 4 | "module": true, 5 | "require": true 6 | }, 7 | "env": { 8 | "browser": true, 9 | "node": true 10 | }, 11 | "rules": { 12 | /** 13 | * Strict mode 14 | */ 15 | "strict": [2, "global"], // http://eslint.org/docs/rules/strict 16 | 17 | /** 18 | * Variables 19 | */ 20 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 21 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 22 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars 23 | "vars": "local", 24 | "args": "after-used" 25 | }], 26 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 27 | 28 | /** 29 | * Possible errors 30 | */ 31 | "comma-dangle": [2, "never"], // http://eslint.org/docs/rules/comma-dangle 32 | "no-cond-assign": [2, "except-parens"], // http://eslint.org/docs/rules/no-cond-assign 33 | "no-console": 1, // http://eslint.org/docs/rules/no-console 34 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 35 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 36 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 37 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 38 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 39 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 40 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 41 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 42 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 43 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 44 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 45 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 46 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 47 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 48 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 49 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 50 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 51 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 52 | 53 | /** 54 | * Best practices 55 | */ 56 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 57 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 58 | "default-case": 2, // http://eslint.org/docs/rules/default-case 59 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 60 | "allowKeywords": true 61 | }], 62 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 63 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 64 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 65 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 66 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 67 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 68 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 69 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 70 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 71 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 72 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 73 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 74 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 75 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 76 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 77 | "no-new": 2, // http://eslint.org/docs/rules/no-new 78 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 79 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 80 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 81 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 82 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 83 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 84 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 85 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 86 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 87 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 88 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 89 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 90 | "no-with": 2, // http://eslint.org/docs/rules/no-with 91 | "radix": 2, // http://eslint.org/docs/rules/radix 92 | "vars-on-top": 0, // http://eslint.org/docs/rules/vars-on-top 93 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 94 | "yoda": 2, // http://eslint.org/docs/rules/yoda 95 | 96 | /** 97 | * Style 98 | */ 99 | "indent": [2, 2], // http://eslint.org/docs/rules/indent 100 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 101 | "1tbs", { 102 | "allowSingleLine": true 103 | }], 104 | "quotes": [ 105 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 106 | ], 107 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 108 | "properties": "never" 109 | }], 110 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 111 | "before": false, 112 | "after": true 113 | }], 114 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 115 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 116 | "func-names": 1, // http://eslint.org/docs/rules/func-names 117 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 118 | "beforeColon": false, 119 | "afterColon": true 120 | }], 121 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 122 | "newIsCap": true 123 | }], 124 | "no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines 125 | "max": 2 126 | }], 127 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 128 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 129 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 130 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 131 | "no-extra-parens": [2, "functions"], // http://eslint.org/docs/rules/no-extra-parens 132 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 133 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 134 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks 135 | "semi": [2, "always"], // http://eslint.org/docs/rules/semi 136 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 137 | "before": false, 138 | "after": true 139 | }], 140 | "keyword-spacing": 2, // http://eslint.org/docs/rules/keyword-spacing 141 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 142 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 143 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 144 | "spaced-comment": [2, "always", {// http://eslint.org/docs/rules/spaced-comment 145 | "markers": ["global", "eslint"] 146 | }] 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var bind = require('./helpers/bind'); 4 | var isBuffer = require('is-buffer'); 5 | 6 | /*global toString:true*/ 7 | 8 | // utils is a library of generic helper functions non-specific to axios 9 | 10 | var toString = Object.prototype.toString; 11 | 12 | /** 13 | * Determine if a value is an Array 14 | * 15 | * @param {Object} val The value to test 16 | * @returns {boolean} True if value is an Array, otherwise false 17 | */ 18 | function isArray(val) { 19 | return toString.call(val) === '[object Array]'; 20 | } 21 | 22 | /** 23 | * Determine if a value is an ArrayBuffer 24 | * 25 | * @param {Object} val The value to test 26 | * @returns {boolean} True if value is an ArrayBuffer, otherwise false 27 | */ 28 | function isArrayBuffer(val) { 29 | return toString.call(val) === '[object ArrayBuffer]'; 30 | } 31 | 32 | /** 33 | * Determine if a value is a FormData 34 | * 35 | * @param {Object} val The value to test 36 | * @returns {boolean} True if value is an FormData, otherwise false 37 | */ 38 | function isFormData(val) { 39 | return (typeof FormData !== 'undefined') && (val instanceof FormData); 40 | } 41 | 42 | /** 43 | * Determine if a value is a view on an ArrayBuffer 44 | * 45 | * @param {Object} val The value to test 46 | * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false 47 | */ 48 | function isArrayBufferView(val) { 49 | var result; 50 | if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { 51 | result = ArrayBuffer.isView(val); 52 | } else { 53 | result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); 54 | } 55 | return result; 56 | } 57 | 58 | /** 59 | * Determine if a value is a String 60 | * 61 | * @param {Object} val The value to test 62 | * @returns {boolean} True if value is a String, otherwise false 63 | */ 64 | function isString(val) { 65 | return typeof val === 'string'; 66 | } 67 | 68 | /** 69 | * Determine if a value is a Number 70 | * 71 | * @param {Object} val The value to test 72 | * @returns {boolean} True if value is a Number, otherwise false 73 | */ 74 | function isNumber(val) { 75 | return typeof val === 'number'; 76 | } 77 | 78 | /** 79 | * Determine if a value is undefined 80 | * 81 | * @param {Object} val The value to test 82 | * @returns {boolean} True if the value is undefined, otherwise false 83 | */ 84 | function isUndefined(val) { 85 | return typeof val === 'undefined'; 86 | } 87 | 88 | /** 89 | * Determine if a value is an Object 90 | * 91 | * @param {Object} val The value to test 92 | * @returns {boolean} True if value is an Object, otherwise false 93 | */ 94 | function isObject(val) { 95 | return val !== null && typeof val === 'object'; 96 | } 97 | 98 | /** 99 | * Determine if a value is a Date 100 | * 101 | * @param {Object} val The value to test 102 | * @returns {boolean} True if value is a Date, otherwise false 103 | */ 104 | function isDate(val) { 105 | return toString.call(val) === '[object Date]'; 106 | } 107 | 108 | /** 109 | * Determine if a value is a File 110 | * 111 | * @param {Object} val The value to test 112 | * @returns {boolean} True if value is a File, otherwise false 113 | */ 114 | function isFile(val) { 115 | return toString.call(val) === '[object File]'; 116 | } 117 | 118 | /** 119 | * Determine if a value is a Blob 120 | * 121 | * @param {Object} val The value to test 122 | * @returns {boolean} True if value is a Blob, otherwise false 123 | */ 124 | function isBlob(val) { 125 | return toString.call(val) === '[object Blob]'; 126 | } 127 | 128 | /** 129 | * Determine if a value is a Function 130 | * 131 | * @param {Object} val The value to test 132 | * @returns {boolean} True if value is a Function, otherwise false 133 | */ 134 | function isFunction(val) { 135 | return toString.call(val) === '[object Function]'; 136 | } 137 | 138 | /** 139 | * Determine if a value is a Stream 140 | * 141 | * @param {Object} val The value to test 142 | * @returns {boolean} True if value is a Stream, otherwise false 143 | */ 144 | function isStream(val) { 145 | return isObject(val) && isFunction(val.pipe); 146 | } 147 | 148 | /** 149 | * Determine if a value is a URLSearchParams object 150 | * 151 | * @param {Object} val The value to test 152 | * @returns {boolean} True if value is a URLSearchParams object, otherwise false 153 | */ 154 | function isURLSearchParams(val) { 155 | return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; 156 | } 157 | 158 | /** 159 | * Trim excess whitespace off the beginning and end of a string 160 | * 161 | * @param {String} str The String to trim 162 | * @returns {String} The String freed of excess whitespace 163 | */ 164 | function trim(str) { 165 | return str.replace(/^\s*/, '').replace(/\s*$/, ''); 166 | } 167 | 168 | /** 169 | * Determine if we're running in a standard browser environment 170 | * 171 | * This allows axios to run in a web worker, and react-native. 172 | * Both environments support XMLHttpRequest, but not fully standard globals. 173 | * 174 | * web workers: 175 | * typeof window -> undefined 176 | * typeof document -> undefined 177 | * 178 | * react-native: 179 | * navigator.product -> 'ReactNative' 180 | */ 181 | function isStandardBrowserEnv() { 182 | if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') { 183 | return false; 184 | } 185 | return ( 186 | typeof window !== 'undefined' && 187 | typeof document !== 'undefined' 188 | ); 189 | } 190 | 191 | /** 192 | * Iterate over an Array or an Object invoking a function for each item. 193 | * 194 | * If `obj` is an Array callback will be called passing 195 | * the value, index, and complete array for each item. 196 | * 197 | * If 'obj' is an Object callback will be called passing 198 | * the value, key, and complete object for each property. 199 | * 200 | * @param {Object|Array} obj The object to iterate 201 | * @param {Function} fn The callback to invoke for each item 202 | */ 203 | function forEach(obj, fn) { 204 | // Don't bother if no value provided 205 | if (obj === null || typeof obj === 'undefined') { 206 | return; 207 | } 208 | 209 | // Force an array if not already something iterable 210 | if (typeof obj !== 'object') { 211 | /*eslint no-param-reassign:0*/ 212 | obj = [obj]; 213 | } 214 | 215 | if (isArray(obj)) { 216 | // Iterate over array values 217 | for (var i = 0, l = obj.length; i < l; i++) { 218 | fn.call(null, obj[i], i, obj); 219 | } 220 | } else { 221 | // Iterate over object keys 222 | for (var key in obj) { 223 | if (Object.prototype.hasOwnProperty.call(obj, key)) { 224 | fn.call(null, obj[key], key, obj); 225 | } 226 | } 227 | } 228 | } 229 | 230 | /** 231 | * Accepts varargs expecting each argument to be an object, then 232 | * immutably merges the properties of each object and returns result. 233 | * 234 | * When multiple objects contain the same key the later object in 235 | * the arguments list will take precedence. 236 | * 237 | * Example: 238 | * 239 | * ```js 240 | * var result = merge({foo: 123}, {foo: 456}); 241 | * console.log(result.foo); // outputs 456 242 | * ``` 243 | * 244 | * @param {Object} obj1 Object to merge 245 | * @returns {Object} Result of all merge properties 246 | */ 247 | function merge(/* obj1, obj2, obj3, ... */) { 248 | var result = {}; 249 | function assignValue(val, key) { 250 | if (typeof result[key] === 'object' && typeof val === 'object') { 251 | result[key] = merge(result[key], val); 252 | } else { 253 | result[key] = val; 254 | } 255 | } 256 | 257 | for (var i = 0, l = arguments.length; i < l; i++) { 258 | forEach(arguments[i], assignValue); 259 | } 260 | return result; 261 | } 262 | 263 | /** 264 | * Extends object a by mutably adding to it the properties of object b. 265 | * 266 | * @param {Object} a The object to be extended 267 | * @param {Object} b The object to copy properties from 268 | * @param {Object} thisArg The object to bind function to 269 | * @return {Object} The resulting value of object a 270 | */ 271 | function extend(a, b, thisArg) { 272 | forEach(b, function assignValue(val, key) { 273 | if (thisArg && typeof val === 'function') { 274 | a[key] = bind(val, thisArg); 275 | } else { 276 | a[key] = val; 277 | } 278 | }); 279 | return a; 280 | } 281 | 282 | module.exports = { 283 | isArray: isArray, 284 | isArrayBuffer: isArrayBuffer, 285 | isBuffer: isBuffer, 286 | isFormData: isFormData, 287 | isArrayBufferView: isArrayBufferView, 288 | isString: isString, 289 | isNumber: isNumber, 290 | isObject: isObject, 291 | isUndefined: isUndefined, 292 | isDate: isDate, 293 | isFile: isFile, 294 | isBlob: isBlob, 295 | isFunction: isFunction, 296 | isStream: isStream, 297 | isURLSearchParams: isURLSearchParams, 298 | isStandardBrowserEnv: isStandardBrowserEnv, 299 | forEach: forEach, 300 | merge: merge, 301 | extend: extend, 302 | trim: trim 303 | }; 304 | -------------------------------------------------------------------------------- /test/specs/requests.spec.js: -------------------------------------------------------------------------------- 1 | describe('requests', function () { 2 | beforeEach(function () { 3 | jasmine.Ajax.install(); 4 | }); 5 | 6 | afterEach(function () { 7 | jasmine.Ajax.uninstall(); 8 | }); 9 | 10 | it('should treat single string arg as url', function (done) { 11 | axios('/foo'); 12 | 13 | getAjaxRequest().then(function (request) { 14 | expect(request.url).toBe('/foo'); 15 | expect(request.method).toBe('GET'); 16 | done(); 17 | }); 18 | }); 19 | 20 | it('should treat method value as lowercase string', function (done) { 21 | axios({ 22 | url: '/foo', 23 | method: 'POST' 24 | }).then(function (response) { 25 | expect(response.config.method).toBe('post'); 26 | done(); 27 | }); 28 | 29 | getAjaxRequest().then(function (request) { 30 | request.respondWith({ 31 | status: 200 32 | }); 33 | }); 34 | }); 35 | 36 | it('should allow string arg as url, and config arg', function (done) { 37 | axios.post('/foo'); 38 | 39 | getAjaxRequest().then(function (request) { 40 | expect(request.url).toBe('/foo'); 41 | expect(request.method).toBe('POST'); 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should make an http request', function (done) { 47 | axios('/foo'); 48 | 49 | getAjaxRequest().then(function (request) { 50 | expect(request.url).toBe('/foo'); 51 | done(); 52 | }); 53 | }); 54 | 55 | it('should reject on network errors', function (done) { 56 | // disable jasmine.Ajax since we're hitting a non-existant server anyway 57 | jasmine.Ajax.uninstall(); 58 | 59 | var resolveSpy = jasmine.createSpy('resolve'); 60 | var rejectSpy = jasmine.createSpy('reject'); 61 | 62 | var finish = function () { 63 | expect(resolveSpy).not.toHaveBeenCalled(); 64 | expect(rejectSpy).toHaveBeenCalled(); 65 | var reason = rejectSpy.calls.first().args[0]; 66 | expect(reason instanceof Error).toBe(true); 67 | expect(reason.config.method).toBe('get'); 68 | expect(reason.config.url).toBe('http://thisisnotaserver/foo'); 69 | expect(reason.request).toEqual(jasmine.any(XMLHttpRequest)); 70 | 71 | // re-enable jasmine.Ajax 72 | jasmine.Ajax.install(); 73 | 74 | done(); 75 | }; 76 | 77 | axios('http://thisisnotaserver/foo') 78 | .then(resolveSpy, rejectSpy) 79 | .then(finish, finish); 80 | }); 81 | 82 | it('should reject when validateStatus returns false', function (done) { 83 | var resolveSpy = jasmine.createSpy('resolve'); 84 | var rejectSpy = jasmine.createSpy('reject'); 85 | 86 | axios('/foo', { 87 | validateStatus: function (status) { 88 | return status !== 500; 89 | } 90 | }).then(resolveSpy) 91 | .catch(rejectSpy) 92 | .then(function () { 93 | expect(resolveSpy).not.toHaveBeenCalled(); 94 | expect(rejectSpy).toHaveBeenCalled(); 95 | var reason = rejectSpy.calls.first().args[0]; 96 | expect(reason instanceof Error).toBe(true); 97 | expect(reason.message).toBe('Request failed with status code 500'); 98 | expect(reason.config.method).toBe('get'); 99 | expect(reason.config.url).toBe('/foo'); 100 | expect(reason.response.status).toBe(500); 101 | 102 | done(); 103 | }); 104 | 105 | getAjaxRequest().then(function (request) { 106 | request.respondWith({ 107 | status: 500 108 | }); 109 | }); 110 | }); 111 | 112 | it('should resolve when validateStatus returns true', function (done) { 113 | var resolveSpy = jasmine.createSpy('resolve'); 114 | var rejectSpy = jasmine.createSpy('reject'); 115 | 116 | axios('/foo', { 117 | validateStatus: function (status) { 118 | return status === 500; 119 | } 120 | }).then(resolveSpy) 121 | .catch(rejectSpy) 122 | .then(function () { 123 | expect(resolveSpy).toHaveBeenCalled(); 124 | expect(rejectSpy).not.toHaveBeenCalled(); 125 | done(); 126 | }); 127 | 128 | getAjaxRequest().then(function (request) { 129 | request.respondWith({ 130 | status: 500 131 | }); 132 | }); 133 | }); 134 | 135 | // https://github.com/axios/axios/issues/378 136 | it('should return JSON when rejecting', function (done) { 137 | var response; 138 | 139 | axios('/api/account/signup', { 140 | username: null, 141 | password: null 142 | }, { 143 | method: 'post', 144 | headers: { 145 | 'Accept': 'application/json' 146 | } 147 | }) 148 | .catch(function (error) { 149 | response = error.response; 150 | }); 151 | 152 | getAjaxRequest().then(function (request) { 153 | request.respondWith({ 154 | status: 400, 155 | statusText: 'Bad Request', 156 | responseText: '{"error": "BAD USERNAME", "code": 1}' 157 | }); 158 | 159 | setTimeout(function () { 160 | expect(typeof response.data).toEqual('object'); 161 | expect(response.data.error).toEqual('BAD USERNAME'); 162 | expect(response.data.code).toEqual(1); 163 | done(); 164 | }, 100); 165 | }); 166 | }); 167 | 168 | it('should make cross domian http request', function (done) { 169 | var response; 170 | 171 | axios.post('www.someurl.com/foo').then(function(res){ 172 | response = res; 173 | }); 174 | 175 | getAjaxRequest().then(function (request) { 176 | request.respondWith({ 177 | status: 200, 178 | statusText: 'OK', 179 | responseText: '{"foo": "bar"}', 180 | headers: { 181 | 'Content-Type': 'application/json' 182 | } 183 | }); 184 | 185 | setTimeout(function () { 186 | expect(response.data.foo).toEqual('bar'); 187 | expect(response.status).toEqual(200); 188 | expect(response.statusText).toEqual('OK'); 189 | expect(response.headers['content-type']).toEqual('application/json'); 190 | done(); 191 | }, 100); 192 | }); 193 | }); 194 | 195 | 196 | it('should supply correct response', function (done) { 197 | var response; 198 | 199 | axios.post('/foo').then(function (res) { 200 | response = res; 201 | }); 202 | 203 | getAjaxRequest().then(function (request) { 204 | request.respondWith({ 205 | status: 200, 206 | statusText: 'OK', 207 | responseText: '{"foo": "bar"}', 208 | headers: { 209 | 'Content-Type': 'application/json' 210 | } 211 | }); 212 | 213 | setTimeout(function () { 214 | expect(response.data.foo).toEqual('bar'); 215 | expect(response.status).toEqual(200); 216 | expect(response.statusText).toEqual('OK'); 217 | expect(response.headers['content-type']).toEqual('application/json'); 218 | done(); 219 | }, 100); 220 | }); 221 | }); 222 | 223 | // https://github.com/axios/axios/issues/201 224 | it('should fix IE no content error', function (done) { 225 | var response; 226 | 227 | axios('/foo').then(function (res) { 228 | response = res 229 | }); 230 | 231 | getAjaxRequest().then(function (request) { 232 | request.respondWith({ 233 | status: 1223, 234 | statusText: 'Unknown' 235 | }); 236 | 237 | setTimeout(function () { 238 | expect(response.status).toEqual(204); 239 | expect(response.statusText).toEqual('No Content'); 240 | done(); 241 | }, 100); 242 | }); 243 | }); 244 | 245 | it('should allow overriding Content-Type header case-insensitive', function (done) { 246 | var response; 247 | var contentType = 'application/vnd.myapp.type+json'; 248 | 249 | axios.post('/foo', { prop: 'value' }, { 250 | headers: { 251 | 'content-type': contentType 252 | } 253 | }).then(function (res) { 254 | response = res; 255 | }); 256 | 257 | getAjaxRequest().then(function (request) { 258 | expect(request.requestHeaders['Content-Type']).toEqual(contentType); 259 | done(); 260 | }); 261 | }); 262 | 263 | it('should support binary data as array buffer', function (done) { 264 | // Int8Array doesn't exist in IE8/9 265 | if (isOldIE && typeof Int8Array === 'undefined') { 266 | done(); 267 | return; 268 | } 269 | 270 | var input = new Int8Array(2); 271 | input[0] = 1; 272 | input[1] = 2; 273 | 274 | axios.post('/foo', input.buffer); 275 | 276 | getAjaxRequest().then(function (request) { 277 | var output = new Int8Array(request.params); 278 | expect(output.length).toEqual(2); 279 | expect(output[0]).toEqual(1); 280 | expect(output[1]).toEqual(2); 281 | done(); 282 | }); 283 | }); 284 | 285 | it('should support binary data as array buffer view', function (done) { 286 | // Int8Array doesn't exist in IE8/9 287 | if (isOldIE && typeof Int8Array === 'undefined') { 288 | done(); 289 | return; 290 | } 291 | 292 | var input = new Int8Array(2); 293 | input[0] = 1; 294 | input[1] = 2; 295 | 296 | axios.post('/foo', input); 297 | 298 | getAjaxRequest().then(function (request) { 299 | var output = new Int8Array(request.params); 300 | expect(output.length).toEqual(2); 301 | expect(output[0]).toEqual(1); 302 | expect(output[1]).toEqual(2); 303 | done(); 304 | }); 305 | }); 306 | 307 | it('should support array buffer response', function (done) { 308 | // ArrayBuffer doesn't exist in IE8/9 309 | if (isOldIE && typeof ArrayBuffer === 'undefined') { 310 | done(); 311 | return; 312 | } 313 | 314 | var response; 315 | 316 | function str2ab(str) { 317 | var buff = new ArrayBuffer(str.length * 2); 318 | var view = new Uint16Array(buff); 319 | for ( var i=0, l=str.length; i