Skip to content

Commit

Permalink
Changing adapter signature to receive config and return promises
Browse files Browse the repository at this point in the history
  • Loading branch information
rubennorte committed Jul 9, 2016
1 parent 8f3a430 commit 157efd5
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 343 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -247,8 +247,8 @@ These are the available config options for making requests. Only the `url` is re
withCredentials: false, // default

// `adapter` allows custom handling of requests which makes testing easier.
// Call `resolve` or `reject` and supply a valid response (see [response docs](#response-api)).
adapter: function (resolve, reject, config) {
// Return a promise and supply a valid response (see [response docs](#response-api)).
adapter: function (config) {
/* ... */
},

Expand Down
31 changes: 17 additions & 14 deletions lib/adapters/README.md
@@ -1,34 +1,37 @@
# axios // adapters

The modules under `adapters/` are modules that handle dispatching a request and settling a `Promise` once a response is received.
The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received.

## Example

```js
var settle = require('./../core/settle');

module.exports myAdapter(resolve, reject, config) {
module.exports myAdapter(config) {
// At this point:
// - config has been merged with defaults
// - request transformers have already run
// - request interceptors have already run

// Make the request using config provided
// Upon response settle the Promise

return new Promise(function(resolve, reject) {

var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};

settle(resolve, reject, response);
settle(resolve, reject, response);

// From here:
// - response transformers will run
// - response interceptors will run
// From here:
// - response transformers will run
// - response interceptors will run
});
}
```
286 changes: 144 additions & 142 deletions lib/adapters/http.js
Expand Up @@ -15,158 +15,160 @@ var createError = require('../core/createError');
var enhanceError = require('../core/enhanceError');

/*eslint consistent-return:0*/
module.exports = function httpAdapter(resolve, reject, config) {
var data = config.data;
var headers = config.headers;
var timer;
var aborted = false;

// Set User-Agent (required by some servers)
// Only set header if it hasn't been set in config
// See https://github.com/mzabriskie/axios/issues/69
if (!headers['User-Agent'] && !headers['user-agent']) {
headers['User-Agent'] = 'axios/' + pkg.version;
}

if (data && !utils.isStream(data)) {
if (utils.isArrayBuffer(data)) {
data = new Buffer(new Uint8Array(data));
} else if (utils.isString(data)) {
data = new Buffer(data, 'utf-8');
} else {
return reject(createError(
'Data after transformation must be a string, an ArrayBuffer, or a Stream',
config
));
module.exports = function httpAdapter(config) {
return new Promise(function dispatchHttpRequest(resolve, reject) {
var data = config.data;
var headers = config.headers;
var timer;
var aborted = false;

// Set User-Agent (required by some servers)
// Only set header if it hasn't been set in config
// See https://github.com/mzabriskie/axios/issues/69
if (!headers['User-Agent'] && !headers['user-agent']) {
headers['User-Agent'] = 'axios/' + pkg.version;
}

// Add Content-Length header if data exists
headers['Content-Length'] = data.length;
}

// HTTP basic authentication
var auth = undefined;
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password || '';
auth = username + ':' + password;
}

// Parse url
var parsed = url.parse(config.url);
if (!auth && parsed.auth) {
var urlAuth = parsed.auth.split(':');
var urlUsername = urlAuth[0] || '';
var urlPassword = urlAuth[1] || '';
auth = urlUsername + ':' + urlPassword;
}
var options = {
hostname: parsed.hostname,
port: parsed.port,
path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
method: config.method,
headers: headers,
agent: config.agent,
auth: auth
};

if (config.proxy) {
options.host = config.proxy.host;
options.port = config.proxy.port;
options.path = parsed.protocol + '//' + parsed.hostname + options.path;
}

var transport;
if (config.maxRedirects === 0) {
transport = parsed.protocol === 'https:' ? https : http;
} else {
if (config.maxRedirects) {
options.maxRedirects = config.maxRedirects;
if (data && !utils.isStream(data)) {
if (utils.isArrayBuffer(data)) {
data = new Buffer(new Uint8Array(data));
} else if (utils.isString(data)) {
data = new Buffer(data, 'utf-8');
} else {
return reject(createError(
'Data after transformation must be a string, an ArrayBuffer, or a Stream',
config
));
}

// Add Content-Length header if data exists
headers['Content-Length'] = data.length;
}
transport = parsed.protocol === 'https:' ? httpsFollow : httpFollow;
}

// Create the request
var req = transport.request(options, function handleResponse(res) {
if (aborted) return;

// Response has been received so kill timer that handles request timeout
clearTimeout(timer);
timer = null;

// uncompress the response body transparently if required
var stream = res;
switch (res.headers['content-encoding']) {
/*eslint default-case:0*/
case 'gzip':
case 'compress':
case 'deflate':
// add the unzipper to the body stream processing pipeline
stream = stream.pipe(zlib.createUnzip());

// remove the content-encoding in order to not confuse downstream operations
delete res.headers['content-encoding'];
break;

// HTTP basic authentication
var auth = undefined;
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password || '';
auth = username + ':' + password;
}

var response = {
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
config: config,
request: req
// Parse url
var parsed = url.parse(config.url);
if (!auth && parsed.auth) {
var urlAuth = parsed.auth.split(':');
var urlUsername = urlAuth[0] || '';
var urlPassword = urlAuth[1] || '';
auth = urlUsername + ':' + urlPassword;
}
var options = {
hostname: parsed.hostname,
port: parsed.port,
path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
method: config.method,
headers: headers,
agent: config.agent,
auth: auth
};

if (config.responseType === 'stream') {
response.data = stream;
settle(resolve, reject, response);
if (config.proxy) {
options.host = config.proxy.host;
options.port = config.proxy.port;
options.path = parsed.protocol + '//' + parsed.hostname + options.path;
}

var transport;
if (config.maxRedirects === 0) {
transport = parsed.protocol === 'https:' ? https : http;
} else {
var responseBuffer = [];
stream.on('data', function handleStreamData(chunk) {
responseBuffer.push(chunk);

// make sure the content length is not over the maxContentLength if specified
if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', config));
}
});

stream.on('error', function handleStreamError(err) {
if (aborted) return;
reject(enhanceError(err, config));
});

stream.on('end', function handleStreamEnd() {
var responseData = Buffer.concat(responseBuffer);
if (config.responseType !== 'arraybuffer') {
responseData = responseData.toString('utf8');
}

response.data = responseData;
if (config.maxRedirects) {
options.maxRedirects = config.maxRedirects;
}
transport = parsed.protocol === 'https:' ? httpsFollow : httpFollow;
}

// Create the request
var req = transport.request(options, function handleResponse(res) {
if (aborted) return;

// Response has been received so kill timer that handles request timeout
clearTimeout(timer);
timer = null;

// uncompress the response body transparently if required
var stream = res;
switch (res.headers['content-encoding']) {
/*eslint default-case:0*/
case 'gzip':
case 'compress':
case 'deflate':
// add the unzipper to the body stream processing pipeline
stream = stream.pipe(zlib.createUnzip());

// remove the content-encoding in order to not confuse downstream operations
delete res.headers['content-encoding'];
break;
}

var response = {
status: res.statusCode,
statusText: res.statusMessage,
headers: res.headers,
config: config,
request: req
};

if (config.responseType === 'stream') {
response.data = stream;
settle(resolve, reject, response);
});
} else {
var responseBuffer = [];
stream.on('data', function handleStreamData(chunk) {
responseBuffer.push(chunk);

// make sure the content length is not over the maxContentLength if specified
if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', config));
}
});

stream.on('error', function handleStreamError(err) {
if (aborted) return;
reject(enhanceError(err, config));
});

stream.on('end', function handleStreamEnd() {
var responseData = Buffer.concat(responseBuffer);
if (config.responseType !== 'arraybuffer') {
responseData = responseData.toString('utf8');
}

response.data = responseData;
settle(resolve, reject, response);
});
}
});

// Handle errors
req.on('error', function handleRequestError(err) {
if (aborted) return;
reject(enhanceError(err, config));
});

// Handle request timeout
if (config.timeout && !timer) {
timer = setTimeout(function handleRequestTimeout() {
req.abort();
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED'));
aborted = true;
}, config.timeout);
}
});

// Handle errors
req.on('error', function handleRequestError(err) {
if (aborted) return;
reject(enhanceError(err, config));
// Send the request
if (utils.isStream(data)) {
data.pipe(req);
} else {
req.end(data);
}
});

// Handle request timeout
if (config.timeout && !timer) {
timer = setTimeout(function handleRequestTimeout() {
req.abort();
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED'));
aborted = true;
}, config.timeout);
}

// Send the request
if (utils.isStream(data)) {
data.pipe(req);
} else {
req.end(data);
}
};

0 comments on commit 157efd5

Please sign in to comment.