-
-
Notifications
You must be signed in to change notification settings - Fork 935
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split
index.js
into separate files (#500)
- Loading branch information
1 parent
77bc901
commit b54b680
Showing
10 changed files
with
726 additions
and
691 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}; |
Oops, something went wrong.