From 1ee5a40d5016e286a7492c8cbd7b08d5c92b1844 Mon Sep 17 00:00:00 2001 From: Kamil Pajdzik <41709775+kpajdzik@users.noreply.github.com> Date: Fri, 25 Jan 2019 09:22:30 -0800 Subject: [PATCH] Add HTTP(S) over HTTP(S) proxy support (#322) * Add proxy support using tunnel package # Conflicts: # lib/axiosHttpClient.ts # lib/policies/proxyPolicy.ts # lib/serviceClient.ts # package.json * Fix incorrect merge * Add tests * Remove commented code * Add tunnel to rollup configuration * Fix test title casing * Remove only * Add axios client tests * Mock buffer * Remove rewire * Fix default HTTP client tests * Add some proxy tests * Add support for HTTPS proxy * Address PR comments --- lib/axiosHttpClient.ts | 70 ++++++++++++++----- lib/policies/proxyPolicy.ts | 40 ++++++++++- lib/serviceClient.ts | 7 +- package.json | 2 + rollup.config.ts | 11 +-- test/axiosHttpClientTests.node.ts | 111 ++++++++++++++++++++++++++++++ test/defaultHttpClientTests.ts | 27 +++++--- test/mockHttp.ts | 18 +++-- test/policies/proxyPolicyTests.ts | 96 ++++++++++++++++++++++++-- test/serializationTests.ts | 2 +- test/serviceClientTests.ts | 4 +- webpack.testconfig.ts | 13 ++-- 12 files changed, 343 insertions(+), 58 deletions(-) create mode 100644 test/axiosHttpClientTests.node.ts diff --git a/lib/axiosHttpClient.ts b/lib/axiosHttpClient.ts index aa71c144..11e72169 100644 --- a/lib/axiosHttpClient.ts +++ b/lib/axiosHttpClient.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosProxyConfig } from "axios"; +import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; import { Transform, Readable } from "stream"; import FormData from "form-data"; import * as tough from "tough-cookie"; @@ -10,9 +10,11 @@ import { HttpHeaders } from "./httpHeaders"; import { HttpOperationResponse } from "./httpOperationResponse"; import { RestError } from "./restError"; import { WebResource, HttpRequestBody } from "./webResource"; +import * as tunnel from "tunnel"; import { ProxySettings } from "./serviceClient"; - -export const axiosClient = axios.create(); +import http from "http"; +import https from "https"; +import { URLBuilder } from "./url"; /** * A HttpClient implementation that uses axios to send HTTP requests. @@ -130,9 +132,19 @@ export class AxiosHttpClient implements HttpClient { responseType: httpRequest.streamResponseBody ? "stream" : "text", cancelToken, timeout: httpRequest.timeout, - proxy: convertToAxiosProxyConfig(httpRequest.proxySettings) + proxy: false }; - res = await axiosClient(config); + + if (httpRequest.proxySettings) { + const agent = createProxyAgent(httpRequest.url, httpRequest.proxySettings, httpRequest.headers); + if (agent.isHttps) { + config.httpsAgent = agent.agent; + } else { + config.httpAgent = agent.agent; + } + } + + res = await axios.request(config); } catch (err) { if (err instanceof axios.Cancel) { throw new RestError(err.message, RestError.REQUEST_SEND_ERROR, undefined, httpRequest); @@ -198,25 +210,45 @@ export class AxiosHttpClient implements HttpClient { } } -function convertToAxiosProxyConfig(proxySettings: ProxySettings | undefined): AxiosProxyConfig | undefined { - if (!proxySettings) { - return undefined; +function isReadableStream(body: any): body is Readable { + return typeof body.pipe === "function"; +} + +declare type ProxyAgent = { isHttps: boolean; agent: http.Agent | https.Agent }; +export function createProxyAgent(requestUrl: string, proxySettings: ProxySettings, headers?: HttpHeaders): ProxyAgent { + const tunnelOptions: tunnel.HttpsOverHttpsOptions = { + proxy: { + host: proxySettings.host, + port: proxySettings.port, + headers: (headers && headers.rawHeaders()) || {} + } + }; + + if ((proxySettings.username && proxySettings.password)) { + tunnelOptions.proxy!.proxyAuth = `${proxySettings.username}:${proxySettings.password}`; } - const axiosAuthConfig = (proxySettings.username && proxySettings.password) ? { - username: proxySettings.username, - password: proxySettings.password - } : undefined; + const requestScheme = URLBuilder.parse(requestUrl).getScheme() || ""; + const isRequestHttps = requestScheme.toLowerCase() === "https"; + const proxyScheme = URLBuilder.parse(proxySettings.host).getScheme() || ""; + const isProxyHttps = proxyScheme.toLowerCase() === "https"; - const axiosProxyConfig: AxiosProxyConfig = { - host: proxySettings.host, - port: proxySettings.port, - auth: axiosAuthConfig + const proxyAgent = { + isHttps: isRequestHttps, + agent: createTunnel(isRequestHttps, isProxyHttps, tunnelOptions) }; - return axiosProxyConfig; + return proxyAgent; } -function isReadableStream(body: any): body is Readable { - return typeof body.pipe === "function"; +export function createTunnel(isRequestHttps: boolean, isProxyHttps: boolean, tunnelOptions: tunnel.HttpsOverHttpsOptions): http.Agent | https.Agent { + if (isRequestHttps && isProxyHttps) { + return tunnel.httpsOverHttps(tunnelOptions); + } else if (isRequestHttps && !isProxyHttps) { + return tunnel.httpsOverHttp(tunnelOptions); + } else if (!isRequestHttps && isProxyHttps) { + return tunnel.httpOverHttps(tunnelOptions); + } else { + return tunnel.httpOverHttp(tunnelOptions); + } } diff --git a/lib/policies/proxyPolicy.ts b/lib/policies/proxyPolicy.ts index 48340194..58f58612 100644 --- a/lib/policies/proxyPolicy.ts +++ b/lib/policies/proxyPolicy.ts @@ -5,11 +5,47 @@ import { BaseRequestPolicy, RequestPolicy, RequestPolicyFactory, RequestPolicyOp import { HttpOperationResponse } from "../httpOperationResponse"; import { ProxySettings } from "../serviceClient"; import { WebResource } from "../webResource"; +import { Constants } from "../util/constants"; +import { URLBuilder } from "../url"; -export function proxyPolicy(proxySettings: ProxySettings): RequestPolicyFactory { +function loadEnvironmentProxyValue(): string | undefined { + if (!process) { + return undefined; + } + + if (process.env[Constants.HTTPS_PROXY]) { + return process.env[Constants.HTTPS_PROXY]; + } else if (process.env[Constants.HTTPS_PROXY.toLowerCase()]) { + return process.env[Constants.HTTPS_PROXY.toLowerCase()]; + } else if (process.env[Constants.HTTP_PROXY]) { + return process.env[Constants.HTTP_PROXY]; + } else if (process.env[Constants.HTTP_PROXY.toLowerCase()]) { + return process.env[Constants.HTTP_PROXY.toLowerCase()]; + } + + return undefined; +} + +export function getDefaultProxySettings(proxyUrl?: string): ProxySettings | undefined { + if (!proxyUrl) { + proxyUrl = loadEnvironmentProxyValue(); + if (!proxyUrl) { + return undefined; + } + } + + const parsedUrl = URLBuilder.parse(proxyUrl); + return { + host: parsedUrl.getScheme() + "://" + parsedUrl.getHost(), + port: Number.parseInt(parsedUrl.getPort() || "80") + }; +} + + +export function proxyPolicy(proxySettings?: ProxySettings): RequestPolicyFactory { return { create: (nextPolicy: RequestPolicy, options: RequestPolicyOptions) => { - return new ProxyPolicy(nextPolicy, options, proxySettings); + return new ProxyPolicy(nextPolicy, options, proxySettings!); } }; } diff --git a/lib/serviceClient.ts b/lib/serviceClient.ts index f4c51ee5..eafc9b81 100644 --- a/lib/serviceClient.ts +++ b/lib/serviceClient.ts @@ -26,8 +26,8 @@ import { stringifyXML } from "./util/xml"; import { RequestOptionsBase, RequestPrepareOptions, WebResource } from "./webResource"; import { OperationResponse } from "./operationResponse"; import { ServiceCallback } from "./util/utils"; +import { proxyPolicy, getDefaultProxySettings } from "./policies/proxyPolicy"; import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy"; -import { proxyPolicy } from "./policies/proxyPolicy"; /** @@ -410,8 +410,9 @@ function createDefaultRequestPolicyFactories(credentials: ServiceClientCredentia factories.push(deserializationPolicy(options.deserializationContentTypes)); - if (options.proxySettings) { - factories.push(proxyPolicy(options.proxySettings)); + const proxySettings = options.proxySettings || getDefaultProxySettings(); + if (proxySettings) { + factories.push(proxyPolicy(proxySettings)); } return factories; diff --git a/package.json b/package.json index 2b8bea26..ee3d7fbd 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "form-data": "^2.3.2", "tough-cookie": "^2.4.3", "tslib": "^1.9.2", + "tunnel": "0.0.6", "uuid": "^3.2.1", "xml2js": "^0.4.19" }, @@ -67,6 +68,7 @@ "@types/semver": "^5.5.0", "@types/sinon": "^5.0.6", "@types/tough-cookie": "^2.3.3", + "@types/tunnel": "0.0.0", "@types/uuid": "^3.4.4", "@types/webpack": "^4.4.13", "@types/webpack-dev-middleware": "^2.0.2", diff --git a/rollup.config.ts b/rollup.config.ts index 6cbc893c..8c82327c 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -24,13 +24,14 @@ input: "./es/lib/msRest.js", external: [ "axios", - "xml2js", - "tough-cookie", - "uuid/v4", - "tslib", "form-data", + "os", "stream", - "os" + "tough-cookie", + "tslib", + "tunnel", + "uuid/v4", + "xml2js", ], output: { file: "./dist/msRest.node.js", diff --git a/test/axiosHttpClientTests.node.ts b/test/axiosHttpClientTests.node.ts new file mode 100644 index 00000000..f16af6ff --- /dev/null +++ b/test/axiosHttpClientTests.node.ts @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +import "chai/register-should"; +import { should } from "chai"; +import tunnel from "tunnel"; +import https from "https"; + +import { HttpHeaders } from "../lib/msRest"; +import { createTunnel, createProxyAgent } from "../lib/axiosHttpClient"; + +describe("AxiosHttpClient", () => { + describe("createProxyAgent", () => { + type HttpsAgent = https.Agent & { + defaultPort: number | undefined, + options: { + proxy: tunnel.ProxyOptions + }, + proxyOptions: tunnel.ProxyOptions + }; + + [ + { proxy: "http", request: "ftp", port: undefined, isProxyHttps: false }, + { proxy: "http", request: "http", port: undefined, isProxyHttps: false }, + { proxy: "hTtp", request: "https", port: 443, isProxyHttps: true }, + { proxy: "HTTPS", request: "http", port: undefined, isProxyHttps: false }, + { proxy: "https", request: "hTTps", port: 443, isProxyHttps: true } + ].forEach(testCase => { + it(`should return ${testCase.isProxyHttps ? "HTTPS" : "HTTP"} proxy for ${testCase.proxy.toUpperCase()} proxy server and ${testCase.request.toUpperCase()} request`, function (done) { + const proxySettings = { + host: `${testCase.proxy}://proxy.microsoft.com`, + port: 8080 + }; + const requestUrl = `${testCase.request}://example.com`; + + const proxyAgent = createProxyAgent(requestUrl, proxySettings); + + proxyAgent.isHttps.should.equal(testCase.isProxyHttps); + const agent = proxyAgent.agent as HttpsAgent; + should().equal(agent.defaultPort, testCase.port); + agent.options.proxy.host!.should.equal(proxySettings.host); + agent.options.proxy.port!.should.equal(proxySettings.port); + done(); + }); + }); + + it("should copy headers correctly", function (done) { + const proxySettings = { + host: "http://proxy.microsoft.com", + port: 8080 + }; + const headers = new HttpHeaders({ + "User-Agent": "Node.js" + }); + + const proxyAgent = createProxyAgent("http://example.com", proxySettings, headers); + + const agent = proxyAgent.agent as HttpsAgent; + agent.proxyOptions.headers.should.contain({ "user-agent": "Node.js" }); + done(); + }); + }); + + describe("createTunnel", () => { + const defaultProxySettings = { + host: "http://proxy.microsoft.com", + port: 8080 + }; + + type HttpsAgent = https.Agent & { + defaultPort: number | undefined, + options: { + proxy: tunnel.ProxyOptions + } + }; + + [true, false].forEach(value => { + it(`returns HTTP agent for HTTP request and HTTP${value ? "S" : ""} proxy`, function () { + const tunnelConfig: tunnel.HttpsOverHttpsOptions = { + proxy: { + host: defaultProxySettings.host, + port: defaultProxySettings.port, + headers: {} + } + }; + + const tunnel = createTunnel(false, value, tunnelConfig) as HttpsAgent; + tunnel.options.proxy.host!.should.equal(defaultProxySettings.host); + tunnel.options.proxy.port!.should.equal(defaultProxySettings.port); + should().not.exist(tunnel.defaultPort); + }); + }); + + [true, false].forEach(value => { + it(`returns HTTPS agent for HTTPS request and HTTP${value ? "S" : ""} proxy`, function () { + const tunnelConfig: tunnel.HttpsOverHttpsOptions = { + proxy: { + host: defaultProxySettings.host, + port: defaultProxySettings.port, + headers: {} + } + }; + + const tunnel = createTunnel(true, value, tunnelConfig) as HttpsAgent; + tunnel.options.proxy.host!.should.equal(defaultProxySettings.host); + tunnel.options.proxy.port!.should.equal(defaultProxySettings.port); + tunnel.defaultPort!.should.equal(443); + }); + }); + }); +}); diff --git a/test/defaultHttpClientTests.ts b/test/defaultHttpClientTests.ts index afe42f71..0981dd83 100644 --- a/test/defaultHttpClientTests.ts +++ b/test/defaultHttpClientTests.ts @@ -4,15 +4,16 @@ import { assert, AssertionError } from "chai"; import "chai/register-should"; import { createReadStream } from "fs"; +import axios from "axios"; import { DefaultHttpClient } from "../lib/defaultHttpClient"; import { RestError } from "../lib/restError"; import { isNode } from "../lib/util/utils"; import { WebResource, HttpRequestBody, TransferProgressEvent } from "../lib/webResource"; -import { getHttpMock } from "./mockHttp"; +import { getHttpMock, HttpMockFacade } from "./mockHttp"; +import { TestFunction } from "mocha"; -const nodeIt = isNode ? it : it.skip; -const httpMock = getHttpMock(); +const nodeIt = (isNode ? it : it.skip) as TestFunction; function getAbortController(): AbortController { let controller: AbortController; @@ -30,8 +31,13 @@ describe("defaultHttpClient", function () { return new Promise(resolve => setTimeout(resolve, ms)); } - beforeEach(() => httpMock.setup()); + let httpMock: HttpMockFacade; + beforeEach(() => { + httpMock = getHttpMock(axios); + httpMock.setup(); + }); afterEach(() => httpMock.teardown()); + after(() => httpMock.teardown()); it("should return a response instead of throwing for awaited 404", async function () { const resourceUrl = "/nonexistent/"; @@ -138,7 +144,7 @@ describe("defaultHttpClient", function () { it("for simple bodies", async function () { httpMock.post("/fileupload", async (_url, _method, body) => { - return { status: 201, body: body, headers: { "Content-Length": "200" } }; + return { status: 251, body: body, headers: { "Content-Length": "200" } }; }); const upload: Notified = { notified: false }; @@ -152,6 +158,7 @@ describe("defaultHttpClient", function () { const client = new DefaultHttpClient(); const response = await client.sendRequest(request); response.should.exist; + response.status.should.equal(251); upload.notified.should.be.true; download.notified.should.be.true; }); @@ -167,7 +174,7 @@ describe("defaultHttpClient", function () { const size = isNode ? payload.toString().length : undefined; httpMock.post("/fileupload", async (_url, _method, _body) => { - return { status: 201, body: payload, headers: { "Content-Type": "text/javascript", "Content-length": size } }; + return { status: 250, body: payload, headers: { "Content-Type": "text/javascript", "Content-length": size } }; }); const upload: Notified = { notified: false }; @@ -179,6 +186,7 @@ describe("defaultHttpClient", function () { const client = new DefaultHttpClient(); const response = await client.sendRequest(request); + response.status.should.equal(250); if (response.blobBody) { await response.blobBody; } else if ((typeof response.readableStreamBody === "function")) { @@ -209,9 +217,9 @@ describe("defaultHttpClient", function () { }); it("should give a graceful error for nonexistent hosts", async function () { - const requestUrl = "http://foo.notawebsite/"; - httpMock.passThrough(requestUrl); - const request = new WebResource(requestUrl); + const requestUrl = "http://fake.domain"; + httpMock.passThrough(); + const request = new WebResource(requestUrl, "GET"); const client = new DefaultHttpClient(); try { await client.sendRequest(request); @@ -312,5 +320,6 @@ describe("defaultHttpClient", function () { assert.strictEqual( responseBody && responseBody.replace(/\s/g, ""), expectedResponseBody.replace(/\s/g, "")); + httpMock.teardown(); }); }); diff --git a/test/mockHttp.ts b/test/mockHttp.ts index 22da5cc9..eb59b0ac 100644 --- a/test/mockHttp.ts +++ b/test/mockHttp.ts @@ -4,7 +4,7 @@ import xhrMock, { proxy } from "xhr-mock"; import MockAdapter from "axios-mock-adapter"; import { isNode, HttpMethods } from "../lib/msRest"; -import { AxiosRequestConfig } from "axios"; +import { AxiosRequestConfig, AxiosInstance } from "axios"; export type UrlFilter = string | RegExp; @@ -18,7 +18,7 @@ export type MockResponseFunction = (url?: string, method?: string, body?: any, h export type MockResponse = MockResponseData | MockResponseFunction; -interface HttpMockFacade { +export interface HttpMockFacade { setup(): void; teardown(): void; passThrough(url?: UrlFilter): void; @@ -29,22 +29,26 @@ interface HttpMockFacade { put(url: UrlFilter, response: MockResponse): void; } -export function getHttpMock(): HttpMockFacade { - return (isNode ? new NodeHttpMock() : new BrowserHttpMock()); +export function getHttpMock(axiosInstance?: AxiosInstance): HttpMockFacade { + return (isNode ? new NodeHttpMock(axiosInstance) : new BrowserHttpMock()); } class NodeHttpMock implements HttpMockFacade { private _mockAdapter: MockAdapter; - constructor() { - const axiosClient = require("../lib/axiosHttpClient").axiosClient; - this._mockAdapter = new MockAdapter(axiosClient); + constructor(axiosInstance?: AxiosInstance) { + if (!axiosInstance) { + throw new Error("Axios instance cannot be undefined"); + } + this._mockAdapter = new MockAdapter(axiosInstance); } setup(): void { + this._mockAdapter.reset(); } teardown(): void { + this._mockAdapter.restore(); } mockHttpMethod(method: HttpMethods, url: UrlFilter, response: MockResponse): void { diff --git a/test/policies/proxyPolicyTests.ts b/test/policies/proxyPolicyTests.ts index c3f25897..2386afc8 100644 --- a/test/policies/proxyPolicyTests.ts +++ b/test/policies/proxyPolicyTests.ts @@ -2,15 +2,15 @@ // Licensed under the MIT License. See License.txt in the project root for license information. import "chai/register-should"; +import { should } from "chai"; import { ProxySettings } from "../../lib/serviceClient"; import { RequestPolicyOptions } from "../../lib/policies/requestPolicy"; import { WebResource } from "../../lib/webResource"; import { HttpHeaders } from "../../lib/httpHeaders"; -import { proxyPolicy, ProxyPolicy } from "../../lib/policies/proxyPolicy"; +import { proxyPolicy, ProxyPolicy, getDefaultProxySettings } from "../../lib/policies/proxyPolicy"; +import { Constants } from "../../lib/msRest"; - - -describe("ProxyPolicy", function() { +describe("ProxyPolicy", function () { const proxySettings: ProxySettings = { host: "https://example.com", port: 3030, @@ -36,6 +36,7 @@ describe("ProxyPolicy", function() { done(); }); + it("sets correct proxy settings through constructor", function (done) { const policy = new ProxyPolicy(emptyRequestPolicy, emptyPolicyOptions, proxySettings); policy.proxySettings.should.be.deep.equal(proxySettings); @@ -61,4 +62,91 @@ describe("ProxyPolicy", function() { request.proxySettings!.should.be.deep.equal(requestSpecificProxySettings); }); + +}); + +describe("getDefaultProxySettings", () => { + const proxyUrl = "https://proxy.microsoft.com"; + const defaultPort = 80; + + it("should return settings with passed address", () => { + const proxySettings: ProxySettings = getDefaultProxySettings(proxyUrl)!; + proxySettings.host.should.equal(proxyUrl); + }); + + it("should return settings with default port", () => { + const proxySettings: ProxySettings = getDefaultProxySettings(proxyUrl)!; + proxySettings.port.should.equal(defaultPort); + }); + + it("should return settings with passed port", () => { + const port = 3030; + const proxyUrl = "prot://proxy.microsoft.com"; + const proxyUrlWithPort = `${proxyUrl}:${port}`; + const proxySettings: ProxySettings = getDefaultProxySettings(proxyUrlWithPort)!; + proxySettings.host.should.equal(proxyUrl); + proxySettings.port.should.equal(port); + }); + + describe("with loadEnvironmentProxyValue", () => { + beforeEach(() => { + delete process.env[Constants.HTTP_PROXY]; + delete process.env[Constants.HTTPS_PROXY]; + delete process.env[Constants.HTTP_PROXY.toLowerCase()]; + delete process.env[Constants.HTTPS_PROXY.toLowerCase()]; + }); + + it("should return undefined when no proxy passed and environment variable is not set", () => { + const proxySettings: ProxySettings | undefined = getDefaultProxySettings(); + should().not.exist(proxySettings); + }); + + it("should load settings from environment variables when no proxyUrl passed", () => { + const proxyUrl = "http://proxy.azure.com"; + process.env[Constants.HTTP_PROXY] = proxyUrl; + const proxySettings: ProxySettings = getDefaultProxySettings()!; + + proxySettings.host.should.equal(proxyUrl); + proxySettings.port.should.equal(defaultPort); + }); + + describe("should prefer HTTPS proxy over HTTP proxy", () => { + [ + { name: "lower case", func: (envVar: string) => envVar.toLowerCase() }, + { name: "upper case", func: (envVar: string) => envVar.toUpperCase() } + ].forEach(testCase => { + it(`with ${testCase.name}`, () => { + const httpProxy = "http://proxy.microsoft.com"; + const httpsProxy = "https://proxy.azure.com"; + process.env[testCase.func(Constants.HTTP_PROXY)] = httpProxy; + process.env[testCase.func(Constants.HTTPS_PROXY)] = httpsProxy; + + const proxySettings: ProxySettings = getDefaultProxySettings()!; + proxySettings.host.should.equal(httpsProxy); + proxySettings.port.should.equal(defaultPort); + }); + }); + + it("should prefer HTTPS proxy over HTTP proxy", () => { + const httpProxy = "http://proxy.microsoft.com"; + const httpsProxy = "https://proxy.azure.com"; + process.env[Constants.HTTP_PROXY] = httpProxy; + process.env[Constants.HTTPS_PROXY] = httpsProxy; + + const proxySettings: ProxySettings = getDefaultProxySettings()!; + proxySettings.host.should.equal(httpsProxy); + proxySettings.port.should.equal(defaultPort); + }); + }); + + ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"].forEach(envVariableName => { + it(`should should load setting from "${envVariableName}" environmental variable`, () => { + process.env[envVariableName] = proxyUrl; + const proxySettings: ProxySettings = getDefaultProxySettings()!; + + proxySettings.host.should.equal(proxyUrl); + proxySettings.port.should.equal(defaultPort); + }); + }); + }); }); diff --git a/test/serializationTests.ts b/test/serializationTests.ts index 83371514..bcbb5704 100644 --- a/test/serializationTests.ts +++ b/test/serializationTests.ts @@ -12,7 +12,7 @@ const Serializer = new msRest.Serializer({}); const valid_uuid = "ceaafd1e-f936-429f-bbfc-82ee75dddc33"; function stringToByteArray(str: string): Uint8Array { - if (typeof Buffer === "function") { + if (typeof Buffer === "function" && msRest.isNode) { return Buffer.from(str, "utf-8"); } else { return new TextEncoder().encode(str); diff --git a/test/serviceClientTests.ts b/test/serviceClientTests.ts index ef629c3e..b9a81cbb 100644 --- a/test/serviceClientTests.ts +++ b/test/serviceClientTests.ts @@ -101,7 +101,7 @@ describe("ServiceClient", function () { assert.strictEqual(JSON.stringify(response), "{}"); }); - it("Should serialize collection:multi query parameters", async function () { + it("should serialize collection:multi query parameters", async function () { const expected = "?q=1&q=2&q=3"; let request: WebResource; @@ -766,7 +766,7 @@ describe("ServiceClient", function () { }); function stringToByteArray(str: string): Uint8Array { - if (typeof Buffer === "function") { + if (typeof Buffer === "function" && isNode) { return Buffer.from(str, "utf-8"); } else { return new TextEncoder().encode(str); diff --git a/webpack.testconfig.ts b/webpack.testconfig.ts index 970ca05c..fc6d368a 100644 --- a/webpack.testconfig.ts +++ b/webpack.testconfig.ts @@ -3,7 +3,7 @@ import * as glob from "glob"; import * as path from "path"; const config: webpack.Configuration = { - entry: glob.sync(path.join(__dirname, "test/**/*.ts")), + entry: glob.sync(path.join(__dirname, "test/**/*[^node\.].ts")), mode: "development", devtool: "source-map", stats: { @@ -46,16 +46,17 @@ const config: webpack.Configuration = { extensions: [".tsx", ".ts", ".js"] }, node: { + Buffer: "mock", + dns: false, fs: "empty", net: "empty", path: "empty", - dns: false, - tls: false, + process: "mock", + stream: "empty", + tls: "empty", tty: false, + tunnel: "empty", v8: false, - Buffer: false, - process: false, - stream: "empty" } };