Comparing version 10.0.2 to 10.0.3
@@ -10,20 +10,17 @@ "use strict"; | ||
const request_as_event_emitter_1 = require("./request-as-event-emitter"); | ||
const parseBody = (body, responseType, statusCode) => { | ||
if (responseType === 'json' && is_1.default.string(body)) { | ||
return statusCode === 204 ? '' : JSON.parse(body); | ||
const parseBody = (body, responseType, encoding) => { | ||
if (responseType === 'json') { | ||
return body.length === 0 ? '' : JSON.parse(body.toString()); | ||
} | ||
if (responseType === 'buffer' && is_1.default.string(body)) { | ||
if (responseType === 'buffer') { | ||
return Buffer.from(body); | ||
} | ||
if (responseType === 'text') { | ||
return String(body); | ||
return body.toString(encoding); | ||
} | ||
if (responseType === 'default') { | ||
return body; | ||
} | ||
throw new Error(`Failed to parse body of type '${typeof body}' as '${responseType}'`); | ||
throw new TypeError(`Unknown body type '${responseType}'`); | ||
}; | ||
function asPromise(options) { | ||
const proxy = new EventEmitter(); | ||
let finalResponse; | ||
let body; | ||
// @ts-ignore `.json()`, `.buffer()` and `.text()` are added later | ||
@@ -48,4 +45,5 @@ const promise = new PCancelable((resolve, reject, onCancel) => { | ||
proxy.emit('response', response); | ||
// Download body | ||
try { | ||
response.body = await getStream(response, { encoding: options.encoding }); | ||
body = await getStream.buffer(response, { encoding: 'binary' }); | ||
} | ||
@@ -60,3 +58,21 @@ catch (error) { | ||
} | ||
const isOk = () => { | ||
const { statusCode } = response; | ||
const limitStatusCode = options.followRedirect ? 299 : 399; | ||
return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304; | ||
}; | ||
// Parse body | ||
try { | ||
response.body = parseBody(body, options.responseType, options.encoding); | ||
} | ||
catch (error) { | ||
if (isOk()) { | ||
const parseError = new errors_1.ParseError(error, response, options); | ||
emitError(parseError); | ||
return; | ||
} | ||
// Fallback to `utf8` | ||
response.body = body.toString(); | ||
} | ||
try { | ||
for (const [index, hook] of options.hooks.afterResponse.entries()) { | ||
@@ -72,3 +88,2 @@ // @ts-ignore Promise is not assignable to CancelableRequest | ||
throwHttpErrors: false, | ||
responseType: 'text', | ||
resolveBodyOnly: false | ||
@@ -96,28 +111,12 @@ })); | ||
} | ||
const { statusCode } = response; | ||
finalResponse = { | ||
body: response.body, | ||
statusCode | ||
}; | ||
try { | ||
response.body = parseBody(response.body, options.responseType, response.statusCode); | ||
} | ||
catch (error) { | ||
if (statusCode >= 200 && statusCode < 300) { | ||
const parseError = new errors_1.ParseError(error, response, options); | ||
emitError(parseError); | ||
// Check for HTTP error codes | ||
if (!isOk()) { | ||
const error = new errors_1.HTTPError(response, options); | ||
if (emitter.retry(error)) { | ||
return; | ||
} | ||
} | ||
const limitStatusCode = options.followRedirect ? 299 : 399; | ||
if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) { | ||
const error = new errors_1.HTTPError(response, options); | ||
if (!emitter.retry(error)) { | ||
if (options.throwHttpErrors) { | ||
emitError(error); | ||
return; | ||
} | ||
resolve(options.resolveBodyOnly ? response.body : response); | ||
if (options.throwHttpErrors) { | ||
emitError(error); | ||
return; | ||
} | ||
return; | ||
} | ||
@@ -135,3 +134,3 @@ resolve(options.resolveBodyOnly ? response.body : response); | ||
// eslint-disable-next-line promise/prefer-await-to-then | ||
const newPromise = promise.then(() => parseBody(finalResponse.body, responseType, finalResponse.statusCode)); | ||
const newPromise = promise.then(() => parseBody(body, responseType, options.encoding)); | ||
Object.defineProperties(newPromise, Object.getOwnPropertyDescriptors(promise)); | ||
@@ -141,3 +140,3 @@ return newPromise; | ||
promise.json = () => { | ||
if (is_1.default.undefined(options.headers.accept)) { | ||
if (is_1.default.undefined(body) && is_1.default.undefined(options.headers.accept)) { | ||
options.headers.accept = 'application/json'; | ||
@@ -144,0 +143,0 @@ } |
@@ -69,10 +69,14 @@ "use strict"; | ||
{ | ||
const read = proxy._read.bind(proxy); | ||
const read = proxy._read; | ||
proxy._read = (...args) => { | ||
isFinished = true; | ||
return read(...args); | ||
proxy._read = read; | ||
return read.apply(proxy, args); | ||
}; | ||
} | ||
if (options.encoding) { | ||
proxy.setEncoding(options.encoding); | ||
} | ||
stream.pipeline(response, output, error => { | ||
if (error) { | ||
if (error && error.message !== 'Premature close') { | ||
emitError(new errors_1.ReadError(error, options)); | ||
@@ -79,0 +83,0 @@ } |
@@ -17,5 +17,3 @@ "use strict"; | ||
if (!options.decompress && ['gzip', 'deflate', 'br'].includes((_a = response.headers['content-encoding'], (_a !== null && _a !== void 0 ? _a : '')))) { | ||
options.responseType = 'default'; | ||
// @ts-ignore Internal use. | ||
options.encoding = 'buffer'; | ||
options.responseType = 'buffer'; | ||
} | ||
@@ -22,0 +20,0 @@ emitter.emit('response', newResponse); |
@@ -58,3 +58,3 @@ "use strict"; | ||
useElectronNet: false, | ||
responseType: 'default', | ||
responseType: 'text', | ||
resolveBodyOnly: false, | ||
@@ -61,0 +61,0 @@ maxRedirects: 10, |
@@ -26,2 +26,4 @@ "use strict"; | ||
let currentRequest; | ||
// `request.aborted` is a boolean since v11.0.0: https://github.com/nodejs/node/commit/4b00c4fafaa2ae8c41c1f78823c0feb810ae4723#diff-e3bc37430eb078ccbafe3aa3b570c91a | ||
const isAborted = () => typeof currentRequest.aborted === 'number' || currentRequest.aborted; | ||
const emitError = async (error) => { | ||
@@ -110,3 +112,11 @@ try { | ||
} | ||
await get_response_1.default(typedResponse, options, emitter); | ||
try { | ||
await get_response_1.default(typedResponse, options, emitter); | ||
} | ||
catch (error) { | ||
// Don't throw `Premature close` if the request has been aborted | ||
if (!(isAborted() && error.message === 'Premature close')) { | ||
throw error; | ||
} | ||
} | ||
} | ||
@@ -118,4 +128,8 @@ catch (error) { | ||
const handleRequest = async (request) => { | ||
// `request.aborted` is a boolean since v11.0.0: https://github.com/nodejs/node/commit/4b00c4fafaa2ae8c41c1f78823c0feb810ae4723#diff-e3bc37430eb078ccbafe3aa3b570c91a | ||
const isAborted = () => typeof request.aborted === 'number' || request.aborted; | ||
let piped = false; | ||
let finished = false; | ||
// `request.finished` doesn't indicate whether this has been emitted or not | ||
request.once('finish', () => { | ||
finished = true; | ||
}); | ||
currentRequest = request; | ||
@@ -133,11 +147,15 @@ const onError = (error) => { | ||
}; | ||
const attachErrorHandler = () => { | ||
request.once('error', error => { | ||
// We need to allow `TimedOutTimeoutError` here, because `stream.pipeline(…)` aborts the request automatically. | ||
request.on('error', error => { | ||
if (piped) { | ||
// Check if it's caught by `stream.pipeline(...)` | ||
if (!finished) { | ||
return; | ||
} | ||
// We need to let `TimedOutTimeoutError` through, because `stream.pipeline(…)` aborts the request automatically. | ||
if (isAborted() && !(error instanceof timed_out_1.TimeoutError)) { | ||
return; | ||
} | ||
onError(error); | ||
}); | ||
}; | ||
} | ||
onError(error); | ||
}); | ||
try { | ||
@@ -148,4 +166,4 @@ http_timer_1.default(request); | ||
const uploadStream = progress_1.createProgressStream('uploadProgress', emitter, httpOptions.headers['content-length']); | ||
piped = true; | ||
await pipeline(httpOptions.body, uploadStream, request); | ||
attachErrorHandler(); | ||
request.emit('upload-complete'); | ||
@@ -159,4 +177,2 @@ } | ||
onError(error); | ||
// Handle future errors | ||
attachErrorHandler(); | ||
} | ||
@@ -163,0 +179,0 @@ }; |
@@ -14,4 +14,5 @@ export interface URLOptions { | ||
hash?: string; | ||
path?: string; | ||
} | ||
declare const _default: (options: URLOptions) => URL; | ||
export default _default; |
@@ -24,7 +24,15 @@ "use strict"; | ||
let origin; | ||
if (Reflect.has(options, 'path')) { | ||
throw new TypeError('Parameter `path` is deprecated. Use `pathname` instead.'); | ||
if (options.path) { | ||
if (options.pathname) { | ||
throw new TypeError('Parameters `path` and `pathname` are mutually exclusive.'); | ||
} | ||
if (options.search) { | ||
throw new TypeError('Parameters `path` and `search` are mutually exclusive.'); | ||
} | ||
if (options.searchParams) { | ||
throw new TypeError('Parameters `path` and `searchParams` are mutually exclusive.'); | ||
} | ||
} | ||
if (Reflect.has(options, 'auth')) { | ||
throw new TypeError('Parameter `auth` is deprecated. Use `username`/`password` instead.'); | ||
throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.'); | ||
} | ||
@@ -47,2 +55,15 @@ if (options.search && options.searchParams) { | ||
const url = new URL(origin); | ||
if (options.path) { | ||
const searchIndex = options.path.indexOf('?'); | ||
if (searchIndex === -1) { | ||
options.pathname = options.path; | ||
} | ||
else { | ||
options.pathname = options.path.slice(0, searchIndex); | ||
options.search = options.path.slice(searchIndex + 1); | ||
} | ||
} | ||
if (Reflect.has(options, 'path')) { | ||
delete options.path; | ||
} | ||
for (const key of keys) { | ||
@@ -53,3 +74,3 @@ if (Reflect.has(options, key)) { | ||
} | ||
if (Reflect.has(options, 'searchParams')) { | ||
if (options.searchParams) { | ||
if (typeof options.searchParams !== 'string' && !(options.searchParams instanceof URLSearchParams)) { | ||
@@ -56,0 +77,0 @@ validateSearchParams(options.searchParams); |
@@ -76,2 +76,3 @@ "use strict"; | ||
}); | ||
request.once('abort', cancelTimeouts); | ||
once(request, 'response', (response) => { | ||
@@ -78,0 +79,0 @@ once(response, 'end', cancelTimeouts); |
@@ -19,3 +19,3 @@ /// <reference types="node" /> | ||
export declare type ErrorCode = 'ETIMEDOUT' | 'ECONNRESET' | 'EADDRINUSE' | 'ECONNREFUSED' | 'EPIPE' | 'ENOTFOUND' | 'ENETUNREACH' | 'EAI_AGAIN'; | ||
export declare type ResponseType = 'json' | 'buffer' | 'text' | 'default'; | ||
export declare type ResponseType = 'json' | 'buffer' | 'text'; | ||
export interface Response<BodyType = unknown> extends http.IncomingMessage { | ||
@@ -22,0 +22,0 @@ body: BodyType; |
{ | ||
"name": "got", | ||
"version": "10.0.2", | ||
"version": "10.0.3", | ||
"description": "Human-friendly and powerful HTTP request library for Node.js", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -101,3 +101,3 @@ <div align="center"> | ||
**By default, Got will retry on failure. To disable this option, set [`retry`](#retry) to `0`.** | ||
**By default, Got will retry on failure. To disable this option, set [`options.retry`](#retry) to `0`.** | ||
@@ -126,2 +126,4 @@ #### got(url?, options?) | ||
**Note:** Legacy Url support is disabled. `options.path` is supported only for backwards compatibility. Instead of the `options.auth`, you should use `options.username` and/or `options.password`. | ||
###### prefixUrl | ||
@@ -243,15 +245,10 @@ | ||
Type: `string`\ | ||
Default: `'default'` | ||
Default: `'text'` | ||
**Note:** When using streams, this option is ignored. | ||
Parsing method used to retrieve the body from the response. | ||
The parsing method. Can be `'text'`, `'json'` or `'buffer'`. | ||
- `'default'` - Will give a string unless the body is overwritten in a `afterResponse` hook or if `options.decompress` is set to false - Will give a Buffer if the response is compresssed. | ||
- `'text'` - Will give a string no matter what. | ||
- `'json'` - Will give an object, unless the body is invalid JSON, then it will throw. | ||
- `'buffer'` - Will give a Buffer, ignoring `options.encoding`. It will throw if the body is a custom object. | ||
The promise has also `.text()`, `.json()` and `.buffer()` methods which set this option automatically. | ||
The promise has `.json()` and `.buffer()` and `.text()` methods which set this option automatically. | ||
Example: | ||
@@ -258,0 +255,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
153169
2265
1618
6