Skip to content

Commit

Permalink
Split index.js into separate files (#500)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandon93s authored and sindresorhus committed Jul 6, 2018
1 parent 77bc901 commit b54b680
Show file tree
Hide file tree
Showing 10 changed files with 726 additions and 691 deletions.
96 changes: 96 additions & 0 deletions source/as-promise.js
@@ -0,0 +1,96 @@
'use strict';
const EventEmitter = require('events');
const getStream = require('get-stream');
const is = require('@sindresorhus/is');
const PCancelable = require('p-cancelable');
const pTimeout = require('p-timeout');
const requestAsEventEmitter = require('./request-as-event-emitter');
const {HTTPError, ParseError, ReadError, RequestError} = require('./errors');

module.exports = options => {
const timeoutFn = requestPromise => options.gotTimeout && options.gotTimeout.request ?
pTimeout(requestPromise, options.gotTimeout.request, new RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, options)) :
requestPromise;

const proxy = new EventEmitter();

const cancelable = new PCancelable((resolve, reject, onCancel) => {
const emitter = requestAsEventEmitter(options);
let cancelOnRequest = false;

onCancel(() => {
cancelOnRequest = true;
});

emitter.on('request', req => {
if (cancelOnRequest) {
req.abort();
}

onCancel(() => {
req.abort();
});

if (is.nodeStream(options.body)) {
options.body.pipe(req);
options.body = undefined;
return;
}

req.end(options.body);
});

emitter.on('response', async response => {
const stream = is.null(options.encoding) ? getStream.buffer(response) : getStream(response, options);

let data;
try {
data = await stream;
} catch (error) {
reject(new ReadError(error, options));
return;
}

const {statusCode} = response;
const limitStatusCode = options.followRedirect ? 299 : 399;

response.body = data;

if (options.json && response.body) {
try {
response.body = JSON.parse(response.body);
} catch (error) {
if (statusCode >= 200 && statusCode < 300) {
const parseError = new ParseError(error, statusCode, options, data);
Object.defineProperty(parseError, 'response', {value: response});
reject(parseError);
}
}
}

if (options.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
const error = new HTTPError(statusCode, response.statusMessage, response.headers, options);
Object.defineProperty(error, 'response', {value: response});
reject(error);
}

resolve(response);
});

emitter.once('error', reject);
emitter.on('redirect', proxy.emit.bind(proxy, 'redirect'));
emitter.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
emitter.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));
});

const promise = timeoutFn(cancelable);

promise.cancel = cancelable.cancel.bind(cancelable);

promise.on = (name, fn) => {
proxy.on(name, fn);
return promise;
};

return promise;
};
80 changes: 80 additions & 0 deletions source/as-stream.js
@@ -0,0 +1,80 @@
'use strict';
const {PassThrough} = require('stream');
const duplexer3 = require('duplexer3');
const is = require('@sindresorhus/is');
const requestAsEventEmitter = require('./request-as-event-emitter');
const {HTTPError, ReadError, RequestError} = require('./errors');

module.exports = options => {
options.stream = true;

const input = new PassThrough();
const output = new PassThrough();
const proxy = duplexer3(input, output);
let timeout;

if (options.gotTimeout && options.gotTimeout.request) {
timeout = setTimeout(() => {
proxy.emit('error', new RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, options));
}, options.gotTimeout.request);
}

if (options.json) {
throw new Error('Got can not be used as a stream when the `json` option is used');
}

if (options.body) {
proxy.write = () => {
throw new Error('Got\'s stream is not writable when the `body` option is used');
};
}

const emitter = requestAsEventEmitter(options);

emitter.on('request', req => {
proxy.emit('request', req);

if (is.nodeStream(options.body)) {
options.body.pipe(req);
return;
}

if (options.body) {
req.end(options.body);
return;
}

if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') {
input.pipe(req);
return;
}

req.end();
});

emitter.on('response', response => {
clearTimeout(timeout);

const {statusCode} = response;

response.on('error', error => {
proxy.emit('error', new ReadError(error, options));
});

response.pipe(output);

if (options.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
proxy.emit('error', new HTTPError(statusCode, response.statusMessage, response.headers, options), null, response);
return;
}

proxy.emit('response', response);
});

emitter.on('error', proxy.emit.bind(proxy, 'error'));
emitter.on('redirect', proxy.emit.bind(proxy, 'redirect'));
emitter.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
emitter.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));

return proxy;
};
66 changes: 66 additions & 0 deletions source/create.js
@@ -0,0 +1,66 @@
'use strict';
const extend = require('extend');
const is = require('@sindresorhus/is');
const asStream = require('./as-stream');
const asPromise = require('./as-promise');
const errors = require('./errors');
const normalizeArguments = require('./normalize-arguments');

const assignOptions = (defaults, options = {}) => {
const opts = extend(true, {}, defaults, options);

if (Reflect.has(options, 'headers')) {
for (const [key, value] of Object.entries(options.headers)) {
if (is.nullOrUndefined(value)) {
delete opts.headers[key];
continue;
}
}
}

return opts;
};

const create = (defaults = {}) => {
function got(url, options) {
try {
options = assignOptions(defaults, options);
const normalizedArgs = normalizeArguments(url, options);

if (normalizedArgs.stream) {
return asStream(normalizedArgs);
}

return asPromise(normalizedArgs);
} catch (error) {
return Promise.reject(error);
}
}

got.create = (options = {}) => create(assignOptions(defaults, options));

got.stream = (url, options) => {
options = assignOptions(defaults, options);
return asStream(normalizeArguments(url, options));
};

const methods = [
'get',
'post',
'put',
'patch',
'head',
'delete'
];

for (const method of methods) {
got[method] = (url, options) => got(url, {...options, method});
got.stream[method] = (url, options) => got.stream(url, {...options, method});
}

Object.assign(got, errors);

return got;
};

module.exports = create;
36 changes: 36 additions & 0 deletions source/get-body-size.js
@@ -0,0 +1,36 @@
'use strict';
const fs = require('fs');
const util = require('util');
const is = require('@sindresorhus/is');
const isFormData = require('./is-form-data');

module.exports = async options => {
const {body} = options;

if (options.headers['content-length']) {
return Number(options.headers['content-length']);
}

if (!body && !options.stream) {
return 0;
}

if (is.string(body)) {
return Buffer.byteLength(body);
}

if (isFormData(body)) {
return util.promisify(body.getLength.bind(body))();
}

if (body instanceof fs.ReadStream) {
const {size} = await util.promisify(fs.stat)(body.path);
return size;
}

if (is.nodeStream(body) && is.buffer(body._buffer)) {
return body._buffer.length;
}

return null;
};
60 changes: 60 additions & 0 deletions source/get-response.js
@@ -0,0 +1,60 @@
'use strict';
const {Transform} = require('stream');
const decompressResponse = require('decompress-response');
const is = require('@sindresorhus/is');
const mimicResponse = require('mimic-response');

module.exports = (response, options, emitter, redirects) => {
const downloadBodySize = Number(response.headers['content-length']) || null;
let downloaded = 0;

const progressStream = new Transform({
transform(chunk, encoding, callback) {
downloaded += chunk.length;

const percent = downloadBodySize ? downloaded / downloadBodySize : 0;

// Let flush() be responsible for emitting the last event
if (percent < 1) {
emitter.emit('downloadProgress', {
percent,
transferred: downloaded,
total: downloadBodySize
});
}

callback(null, chunk);
},

flush(callback) {
emitter.emit('downloadProgress', {
percent: 1,
transferred: downloaded,
total: downloadBodySize
});

callback();
}
});

mimicResponse(response, progressStream);
progressStream.redirectUrls = redirects;

const newResponse = options.decompress === true &&
is.function(decompressResponse) &&
options.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream;

if (!options.decompress && ['gzip', 'deflate'].includes(response.headers['content-encoding'])) {
options.encoding = null;
}

emitter.emit('response', newResponse);

emitter.emit('downloadProgress', {
percent: 0,
transferred: 0,
total: downloadBodySize
});

response.pipe(progressStream);
};

0 comments on commit b54b680

Please sign in to comment.