typed-rest-client
Advanced tools
Comparing version 1.5.0 to 1.7.0
@@ -0,0 +0,0 @@ export { BasicCredentialHandler } from "./handlers/basiccreds"; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import ifm = require('../Interfaces'); |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import ifm = require('../Interfaces'); |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -10,5 +10,5 @@ /// <reference types="node" /> | ||
handleAuthentication(httpClient: ifm.IHttpClient, requestInfo: ifm.IRequestInfo, objs: any): Promise<ifm.IHttpClientResponse>; | ||
private handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback); | ||
private sendType1Message(httpClient, requestInfo, objs, finalCallback); | ||
private sendType3Message(httpClient, requestInfo, objs, res, callback); | ||
private handleAuthenticationPrivate; | ||
private sendType1Message; | ||
private sendType3Message; | ||
} |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import ifm = require('../Interfaces'); |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -27,2 +27,3 @@ /// <reference types="node" /> | ||
Gone = 410, | ||
TooManyRequests = 429, | ||
InternalServerError = 500, | ||
@@ -32,3 +33,3 @@ NotImplemented = 501, | ||
ServiceUnavailable = 503, | ||
GatewayTimeout = 504, | ||
GatewayTimeout = 504 | ||
} | ||
@@ -47,3 +48,3 @@ export declare class HttpClientResponse implements ifm.IHttpClientResponse { | ||
export declare class HttpClient implements ifm.IHttpClient { | ||
userAgent: string; | ||
userAgent: string | null | undefined; | ||
handlers: ifm.IRequestHandler[]; | ||
@@ -56,2 +57,3 @@ requestOptions: ifm.IRequestOptions; | ||
private _allowRedirects; | ||
private _allowRedirectDowngrade; | ||
private _maxRedirects; | ||
@@ -68,3 +70,3 @@ private _allowRetries; | ||
private _key; | ||
constructor(userAgent: string, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions); | ||
constructor(userAgent: string | null | undefined, handlers?: ifm.IRequestHandler[], requestOptions?: ifm.IRequestOptions); | ||
options(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; | ||
@@ -101,9 +103,9 @@ get(requestUrl: string, additionalHeaders?: ifm.IHeaders): Promise<ifm.IHttpClientResponse>; | ||
requestRawWithCallback(info: ifm.IRequestInfo, data: string | NodeJS.ReadableStream, onResult: (err: any, res: ifm.IHttpClientResponse) => void): void; | ||
private _prepareRequest(method, requestUrl, headers); | ||
private _isPresigned(requestUrl); | ||
private _mergeHeaders(headers); | ||
private _getAgent(requestUrl); | ||
private _getProxy(requestUrl); | ||
private _isBypassProxy(requestUrl); | ||
private _performExponentialBackoff(retryNumber); | ||
private _prepareRequest; | ||
private _isPresigned; | ||
private _mergeHeaders; | ||
private _getAgent; | ||
private _getProxy; | ||
private _isMatchInBypassProxyList; | ||
private _performExponentialBackoff; | ||
} |
@@ -16,2 +16,3 @@ "use strict"; | ||
const https = require("https"); | ||
const util = require("./Util"); | ||
let fs; | ||
@@ -42,2 +43,3 @@ let tunnel; | ||
HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; | ||
HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; | ||
HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; | ||
@@ -60,9 +62,22 @@ HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; | ||
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { | ||
let output = ''; | ||
this.message.on('data', (chunk) => { | ||
output += chunk; | ||
let buffer = Buffer.alloc(0); | ||
const encodingCharset = util.obtainContentCharset(this); | ||
// Extract Encoding from header: 'content-encoding' | ||
// Match `gzip`, `gzip, deflate` variations of GZIP encoding | ||
const contentEncoding = this.message.headers['content-encoding'] || ''; | ||
const isGzippedEncoded = new RegExp('(gzip$)|(gzip, *deflate)').test(contentEncoding); | ||
this.message.on('data', function (data) { | ||
const chunk = (typeof data === 'string') ? Buffer.from(data, encodingCharset) : data; | ||
buffer = Buffer.concat([buffer, chunk]); | ||
}).on('end', function () { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (isGzippedEncoded) { // Process GZipped Response Body HERE | ||
const gunzippedBody = yield util.decompressGzippedContent(buffer, encodingCharset); | ||
resolve(gunzippedBody); | ||
} | ||
resolve(buffer.toString(encodingCharset)); | ||
}); | ||
}).on('error', function (err) { | ||
reject(err); | ||
}); | ||
this.message.on('end', () => { | ||
resolve(output); | ||
}); | ||
})); | ||
@@ -81,2 +96,3 @@ } | ||
EnvironmentVariables["HTTPS_PROXY"] = "HTTPS_PROXY"; | ||
EnvironmentVariables["NO_PROXY"] = "NO_PROXY"; | ||
})(EnvironmentVariables || (EnvironmentVariables = {})); | ||
@@ -87,2 +103,3 @@ class HttpClient { | ||
this._allowRedirects = true; | ||
this._allowRedirectDowngrade = false; | ||
this._maxRedirects = 50; | ||
@@ -95,2 +112,9 @@ this._allowRetries = false; | ||
this.handlers = handlers || []; | ||
let no_proxy = process.env[EnvironmentVariables.NO_PROXY]; | ||
if (no_proxy) { | ||
this._httpProxyBypassHosts = []; | ||
no_proxy.split(',').forEach(bypass => { | ||
this._httpProxyBypassHosts.push(new RegExp(bypass, 'i')); | ||
}); | ||
} | ||
this.requestOptions = requestOptions; | ||
@@ -113,3 +137,3 @@ if (requestOptions) { | ||
fs = require('fs'); | ||
// cache the cert content into memory, so we don't have to read it from disk every time | ||
// cache the cert content into memory, so we don't have to read it from disk every time | ||
if (this._certConfig.caFile && fs.existsSync(this._certConfig.caFile)) { | ||
@@ -128,2 +152,5 @@ this._ca = fs.readFileSync(this._certConfig.caFile, 'utf8'); | ||
} | ||
if (requestOptions.allowRedirectDowngrade != null) { | ||
this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; | ||
} | ||
if (requestOptions.maxRedirects != null) { | ||
@@ -177,3 +204,4 @@ this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); | ||
} | ||
let info = this._prepareRequest(verb, requestUrl, headers); | ||
let parsedUrl = url.parse(requestUrl); | ||
let info = this._prepareRequest(verb, parsedUrl, headers); | ||
// Only perform retries on reads since writes may not be idempotent. | ||
@@ -212,2 +240,6 @@ let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1; | ||
} | ||
let parsedRedirectUrl = url.parse(redirectUrl); | ||
if (parsedUrl.protocol == 'https:' && parsedUrl.protocol != parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) { | ||
throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true."); | ||
} | ||
// we need to finish reading the response before reassigning response | ||
@@ -217,3 +249,3 @@ // which will leak the open socket. | ||
// let's make the request with the new redirectUrl | ||
info = this._prepareRequest(verb, redirectUrl, headers); | ||
info = this._prepareRequest(verb, parsedRedirectUrl, headers); | ||
response = yield this.requestRaw(info, data); | ||
@@ -313,3 +345,3 @@ redirectsRemaining--; | ||
const info = {}; | ||
info.parsedUrl = url.parse(requestUrl); | ||
info.parsedUrl = requestUrl; | ||
const usingSsl = info.parsedUrl.protocol === 'https:'; | ||
@@ -324,6 +356,8 @@ info.httpModule = usingSsl ? https : http; | ||
info.options.headers = this._mergeHeaders(headers); | ||
info.options.headers["user-agent"] = this.userAgent; | ||
info.options.agent = this._getAgent(requestUrl); | ||
if (this.userAgent != null) { | ||
info.options.headers["user-agent"] = this.userAgent; | ||
} | ||
info.options.agent = this._getAgent(info.parsedUrl); | ||
// gives handlers an opportunity to participate | ||
if (this.handlers && !this._isPresigned(requestUrl)) { | ||
if (this.handlers && !this._isPresigned(url.format(requestUrl))) { | ||
this.handlers.forEach((handler) => { | ||
@@ -353,6 +387,6 @@ handler.prepareRequest(info.options); | ||
} | ||
_getAgent(requestUrl) { | ||
_getAgent(parsedUrl) { | ||
let agent; | ||
let proxy = this._getProxy(requestUrl); | ||
let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isBypassProxy(requestUrl); | ||
let proxy = this._getProxy(parsedUrl); | ||
let useProxy = proxy.proxyUrl && proxy.proxyUrl.hostname && !this._isMatchInBypassProxyList(parsedUrl); | ||
if (this._keepAlive && useProxy) { | ||
@@ -368,3 +402,2 @@ agent = this._proxyAgent; | ||
} | ||
let parsedUrl = url.parse(requestUrl); | ||
const usingSsl = parsedUrl.protocol === 'https:'; | ||
@@ -421,4 +454,3 @@ let maxSockets = 100; | ||
} | ||
_getProxy(requestUrl) { | ||
const parsedUrl = url.parse(requestUrl); | ||
_getProxy(parsedUrl) { | ||
let usingSsl = parsedUrl.protocol === 'https:'; | ||
@@ -453,3 +485,3 @@ let proxyConfig = this._httpProxy; | ||
} | ||
_isBypassProxy(requestUrl) { | ||
_isMatchInBypassProxyList(parsedUrl) { | ||
if (!this._httpProxyBypassHosts) { | ||
@@ -460,3 +492,3 @@ return false; | ||
this._httpProxyBypassHosts.forEach(bypassHost => { | ||
if (bypassHost.test(requestUrl)) { | ||
if (bypassHost.test(parsedUrl.href)) { | ||
bypass = true; | ||
@@ -463,0 +495,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); |
@@ -44,2 +44,3 @@ /// <reference types="node" /> | ||
allowRedirects?: boolean; | ||
allowRedirectDowngrade?: boolean; | ||
maxRedirects?: number; | ||
@@ -64,1 +65,12 @@ maxSockets?: number; | ||
} | ||
export interface IRequestQueryParams { | ||
options?: { | ||
separator?: string; | ||
arrayFormat?: string; | ||
shouldAllowDots?: boolean; | ||
shouldOnlyEncodeValues?: boolean; | ||
}; | ||
params: { | ||
[name: string]: string | number | (string | number)[]; | ||
}; | ||
} |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ var crypto = require('crypto'); |
@@ -0,0 +0,0 @@ // This software (ntlm.js) was copied from a file of the same name at https://github.com/SamDecrock/node-http-ntlm/blob/master/ntlm.js. |
{ | ||
"name": "typed-rest-client", | ||
"version": "1.5.0", | ||
"version": "1.7.0", | ||
"description": "Node Rest and Http Clients for use with TypeScript", | ||
@@ -37,5 +37,6 @@ "main": "./RestClient.js", | ||
"nock": "9.6.1", | ||
"qs": "^6.9.1", | ||
"react-scripts": "1.1.5", | ||
"semver": "4.3.3", | ||
"shelljs": "0.7.6", | ||
"semver": "4.3.3", | ||
"typescript": "3.1.5" | ||
@@ -42,0 +43,0 @@ }, |
@@ -0,3 +1,7 @@ | ||
<a href="https://github.com/microsoft/typed-rest-client"><img alt="GitHub Actions status" src="https://github.com/microsoft/typed-rest-client/workflows/all-tests/badge.svg"></a> | ||
[![Build Status](https://dev.azure.com/ms/typed-rest-client/_apis/build/status/Microsoft.typed-rest-client?branchName=master)](https://dev.azure.com/ms/typed-rest-client/_build/latest?definitionId=42&branchName=master) | ||
# Typed REST and HTTP Client with TypeScript Typings | ||
@@ -101,1 +105,7 @@ | ||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. | ||
## Security Issues | ||
Do you think there might be a security issue? | ||
Have you been phished or identified a security vulnerability? | ||
Please don't report it here - let us know by sending an email to secure@microsoft.com. |
@@ -14,2 +14,3 @@ /// <reference types="node" /> | ||
deserializeDates?: boolean; | ||
queryParameters?: ifm.IRequestQueryParams; | ||
} | ||
@@ -75,5 +76,5 @@ export declare class RestClient { | ||
uploadStream<T>(verb: string, requestUrl: string, stream: NodeJS.ReadableStream, options?: IRequestOptions): Promise<IRestResponse<T>>; | ||
private _headersFromOptions(options, contentType?); | ||
private static dateTimeDeserializer(key, value); | ||
private _processResponse<T>(res, options); | ||
private _headersFromOptions; | ||
private static dateTimeDeserializer; | ||
private _processResponse; | ||
} |
@@ -51,3 +51,3 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
let url = util.getUrl(resource, this._baseUrl); | ||
let url = util.getUrl(resource, this._baseUrl, (options || {}).queryParameters); | ||
let res = yield this.client.get(url, this._headersFromOptions(options)); | ||
@@ -54,0 +54,0 @@ return this._processResponse(res, options); |
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
import { IRequestQueryParams, IHttpClientResponse } from './Interfaces'; | ||
/** | ||
@@ -5,4 +7,23 @@ * creates an url from a request url and optional base url (http://server:8080) | ||
* @param {string} baseUrl - an optional baseUrl (http://server:8080) | ||
* @param {IRequestOptions} options - an optional options object, could include QueryParameters e.g. | ||
* @return {string} - resultant url | ||
*/ | ||
export declare function getUrl(resource: string, baseUrl?: string): string; | ||
export declare function getUrl(resource: string, baseUrl?: string, queryParams?: IRequestQueryParams): string; | ||
/** | ||
* Decompress/Decode gzip encoded JSON | ||
* Using Node.js built-in zlib module | ||
* | ||
* @param {Buffer} buffer | ||
* @param {string} charset? - optional; defaults to 'utf-8' | ||
* @return {Promise<string>} | ||
*/ | ||
export declare function decompressGzippedContent(buffer: Buffer, charset?: string): Promise<string>; | ||
/** | ||
* Obtain Response's Content Charset. | ||
* Through inspecting `content-type` response header. | ||
* It Returns 'utf-8' if NO charset specified/matched. | ||
* | ||
* @param {IHttpClientResponse} response | ||
* @return {string} - Content Encoding Charset; Default=utf-8 | ||
*/ | ||
export declare function obtainContentCharset(response: IHttpClientResponse): string; |
91
Util.js
"use strict"; | ||
// Copyright (c) Microsoft. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const qs = require("qs"); | ||
const url = require("url"); | ||
const path = require("path"); | ||
const zlib = require("zlib"); | ||
/** | ||
@@ -11,11 +21,13 @@ * creates an url from a request url and optional base url (http://server:8080) | ||
* @param {string} baseUrl - an optional baseUrl (http://server:8080) | ||
* @param {IRequestOptions} options - an optional options object, could include QueryParameters e.g. | ||
* @return {string} - resultant url | ||
*/ | ||
function getUrl(resource, baseUrl) { | ||
function getUrl(resource, baseUrl, queryParams) { | ||
const pathApi = path.posix || path; | ||
let requestUrl = ''; | ||
if (!baseUrl) { | ||
return resource; | ||
requestUrl = resource; | ||
} | ||
else if (!resource) { | ||
return baseUrl; | ||
requestUrl = baseUrl; | ||
} | ||
@@ -33,5 +45,76 @@ else { | ||
} | ||
return url.format(resultantUrl); | ||
requestUrl = url.format(resultantUrl); | ||
} | ||
return queryParams ? | ||
getUrlWithParsedQueryParams(requestUrl, queryParams) : | ||
requestUrl; | ||
} | ||
exports.getUrl = getUrl; | ||
/** | ||
* | ||
* @param {string} requestUrl | ||
* @param {IRequestQueryParams} queryParams | ||
* @return {string} - Request's URL with Query Parameters appended/parsed. | ||
*/ | ||
function getUrlWithParsedQueryParams(requestUrl, queryParams) { | ||
const url = requestUrl.replace(/\?$/g, ''); // Clean any extra end-of-string "?" character | ||
const parsedQueryParams = qs.stringify(queryParams.params, buildParamsStringifyOptions(queryParams)); | ||
return `${url}${parsedQueryParams}`; | ||
} | ||
/** | ||
* Build options for QueryParams Stringifying. | ||
* | ||
* @param {IRequestQueryParams} queryParams | ||
* @return {object} | ||
*/ | ||
function buildParamsStringifyOptions(queryParams) { | ||
let options = { | ||
addQueryPrefix: true, | ||
delimiter: (queryParams.options || {}).separator || '&', | ||
allowDots: (queryParams.options || {}).shouldAllowDots || false, | ||
arrayFormat: (queryParams.options || {}).arrayFormat || 'repeat', | ||
encodeValuesOnly: (queryParams.options || {}).shouldOnlyEncodeValues || true | ||
}; | ||
return options; | ||
} | ||
/** | ||
* Decompress/Decode gzip encoded JSON | ||
* Using Node.js built-in zlib module | ||
* | ||
* @param {Buffer} buffer | ||
* @param {string} charset? - optional; defaults to 'utf-8' | ||
* @return {Promise<string>} | ||
*/ | ||
function decompressGzippedContent(buffer, charset) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { | ||
zlib.gunzip(buffer, function (error, buffer) { | ||
if (error) { | ||
reject(error); | ||
} | ||
resolve(buffer.toString(charset || 'utf-8')); | ||
}); | ||
})); | ||
}); | ||
} | ||
exports.decompressGzippedContent = decompressGzippedContent; | ||
/** | ||
* Obtain Response's Content Charset. | ||
* Through inspecting `content-type` response header. | ||
* It Returns 'utf-8' if NO charset specified/matched. | ||
* | ||
* @param {IHttpClientResponse} response | ||
* @return {string} - Content Encoding Charset; Default=utf-8 | ||
*/ | ||
function obtainContentCharset(response) { | ||
// Find the charset, if specified. | ||
// Search for the `charset=CHARSET` string, not including `;,\r\n` | ||
// Example: content-type: 'application/json;charset=utf-8' | ||
// |__ matches would be ['charset=utf-8', 'utf-8', index: 18, input: 'application/json; charset=utf-8'] | ||
// |_____ matches[1] would have the charset :tada: , in our example it's utf-8 | ||
// However, if the matches Array was empty or no charset found, 'utf-8' would be returned by default. | ||
const contentType = response.message.headers['content-type'] || ''; | ||
const matches = contentType.match(/charset=([^;,\r\n]+)/i); | ||
return (matches && matches[1]) ? matches[1] : 'utf-8'; | ||
} | ||
exports.obtainContentCharset = obtainContentCharset; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
158254
1690
111
10
4
5