Merge pull request #94 from RikkiGibson/Axios

Axios and cancellation
This commit is contained in:
Rikki Gibson 2018-05-21 16:29:37 -07:00 коммит произвёл GitHub
Родитель cd33133617 fb7e7f608b
Коммит 28ac7ad626
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 716 добавлений и 255 удалений

Просмотреть файл

@ -3,17 +3,28 @@
import * as FormData from "form-data";
import * as xml2js from "isomorphic-xml2js";
import axios, { AxiosResponse, AxiosError, AxiosRequestConfig } from "axios";
import { HttpClient } from "./httpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { WebResource } from "./webResource";
import { RestError } from "./restError";
import { HttpHeaders } from "./httpHeaders";
import { isNode } from "./util/utils";
import * as tough from "isomorphic-tough-cookie";
const axiosClient = axios.create();
if (isNode) {
// Workaround for https://github.com/axios/axios/issues/1158
axiosClient.interceptors.request.use(config => ({ ...config, method: config.method && config.method.toUpperCase() }));
}
/**
* A HttpClient implementation that uses fetch to send HTTP requests.
* A HttpClient implementation that uses axios to send HTTP requests.
*/
export class FetchHttpClient implements HttpClient {
export class AxiosHttpClient implements HttpClient {
private readonly cookieJar = isNode ? new tough.CookieJar() : undefined;
public async sendRequest(httpRequest: WebResource): Promise<HttpOperationResponse> {
if (!httpRequest) {
return Promise.reject(new Error("options (WebResource) cannot be null or undefined and must be of type object."));
@ -59,43 +70,94 @@ export class FetchHttpClient implements HttpClient {
}
}
// allow cross-origin cookies in browser
(httpRequest as any).credentials = "include";
if (this.cookieJar) {
const cookieString = await new Promise<string>((resolve, reject) => {
this.cookieJar!.getCookieString(httpRequest.url, (err, cookie) => {
if (err) {
reject(err);
} else {
resolve(cookie);
}
});
});
let res: Response;
try {
res = await myFetch(httpRequest.url, httpRequest);
} catch (err) {
return Promise.reject(err);
httpRequest.headers["Cookie"] = cookieString;
}
const abortSignal = httpRequest.abortSignal;
if (abortSignal && abortSignal.aborted) {
throw new RestError("The request was aborted", "REQUEST_ABORTED_ERROR", undefined, httpRequest);
}
const headers = new HttpHeaders();
res.headers.forEach((value: string, name: string) => {
headers.set(name, value);
const cancelToken = abortSignal && new axios.CancelToken(canceler => {
abortSignal.addEventListener("abort", () => canceler());
});
let res: AxiosResponse;
try {
const config: AxiosRequestConfig = {
method: httpRequest.method,
url: httpRequest.url,
headers: httpRequest.headers,
// Workaround for https://github.com/axios/axios/issues/755
// tslint:disable-next-line:no-null-keyword
data: httpRequest.body === undefined ? null : httpRequest.body,
transformResponse: undefined,
validateStatus: () => true,
withCredentials: true,
// Workaround for https://github.com/axios/axios/issues/1362
maxContentLength: 1024 * 1024 * 1024 * 10,
responseType: httpRequest.rawResponse ? (isNode ? "stream" : "blob") : "text",
cancelToken
};
res = await axiosClient(config);
} catch (err) {
if (err instanceof axios.Cancel) {
throw new RestError(err.message, "REQUEST_ABORTED_ERROR", undefined, httpRequest);
} else {
const axiosErr = err as AxiosError;
throw new RestError(axiosErr.message, "REQUEST_SEND_ERROR", undefined, httpRequest);
}
}
const headers = new HttpHeaders(res.headers);
const operationResponse: HttpOperationResponse = {
request: httpRequest,
status: res.status,
headers,
readableStreamBody: isNode ? res.body as any : undefined,
blobBody: isNode ? undefined : () => res.blob()
readableStreamBody: httpRequest.rawResponse && isNode ? res.data as any : undefined,
blobBody: !httpRequest.rawResponse || isNode ? undefined : () => res.data
};
if (this.cookieJar) {
const setCookieHeader = operationResponse.headers.get("Set-Cookie");
if (setCookieHeader != undefined) {
await new Promise((resolve, reject) => {
this.cookieJar!.setCookie(setCookieHeader, httpRequest.url, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
}
if (!httpRequest.rawResponse) {
try {
operationResponse.bodyAsText = await res.text();
operationResponse.bodyAsText = res.data;
} catch (err) {
const msg = `Error "${err}" occured while converting the raw response body into string.`;
const errCode = err.code || "RAWTEXT_CONVERSION_ERROR";
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, res.body);
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, res.data);
return Promise.reject(e);
}
try {
if (operationResponse.bodyAsText) {
const contentType = res.headers.get("Content-Type")!;
const contentType = operationResponse.headers.get("Content-Type");
if (contentType === "application/xml" || contentType === "text/xml") {
const xmlParser = new xml2js.Parser(XML2JS_PARSER_OPTS);
const parseString = new Promise(function (resolve: (result: any) => void, reject: (err: any) => void) {
@ -109,7 +171,7 @@ export class FetchHttpClient implements HttpClient {
});
operationResponse.parsedBody = await parseString;
} else {
} else if (contentType === "application/json" || contentType === "text/json" || !contentType) {
operationResponse.parsedBody = JSON.parse(operationResponse.bodyAsText);
}
}
@ -124,26 +186,6 @@ export class FetchHttpClient implements HttpClient {
}
}
/**
* Provides the fetch() method based on the environment.
* @returns {fetch} fetch - The fetch() method available in the environment to make requests
*/
export function getFetch(): Function {
// using window.Fetch in Edge gives a TypeMismatchError
// (https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8546263/).
// Hence we will be using the fetch-ponyfill for Edge.
if (typeof window !== "undefined" && window.fetch && window.navigator &&
window.navigator.userAgent && window.navigator.userAgent.indexOf("Edge/") === -1) {
return window.fetch.bind(window);
}
return require("fetch-ponyfill")({ useCookie: true }).fetch;
}
/**
* A constant that provides the fetch() method based on the environment.
*/
export const myFetch = getFetch();
const XML2JS_PARSER_OPTS: xml2js.OptionsV2 = {
explicitArray: false,
explicitCharkey: false,

Просмотреть файл

@ -8,4 +8,4 @@ import { RequestPolicy } from "./policies/requestPolicy";
* @returns A Promise that resolves to the HttpResponse from the targeted server.
*/
export interface HttpClient extends RequestPolicy {
}
}

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
export { WebResource, RequestPrepareOptions, HttpMethods, ParameterValue, RequestOptionsBase } from "./webResource";
export { FetchHttpClient } from "./fetchHttpClient";
export { AxiosHttpClient } from "./axiosHttpClient";
export { HttpClient } from "./httpClient";
export { HttpHeaders } from "./httpHeaders";
export { HttpOperationResponse } from "./httpOperationResponse";

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
import { ServiceClientCredentials } from "./credentials/serviceClientCredentials";
import { FetchHttpClient } from "./fetchHttpClient";
import { AxiosHttpClient } from "./axiosHttpClient";
import { HttpClient } from "./httpClient";
import { HttpOperationResponse } from "./httpOperationResponse";
import { HttpPipelineLogger } from "./httpPipelineLogger";
@ -110,7 +110,7 @@ export class ServiceClient {
// do nothing
}
this._httpClient = options.httpClient || new FetchHttpClient();
this._httpClient = options.httpClient || new AxiosHttpClient();
this._requestPolicyOptions = new RequestPolicyOptions(options.httpPipelineLogger);
this._requestPolicyCreators = options.requestPolicyCreators || createDefaultRequestPolicyCreators(credentials, options, this.userAgentInfo.value);

Просмотреть файл

@ -24,7 +24,9 @@ export class WebResource {
query?: { [key: string]: any; };
operationSpec?: OperationSpec;
constructor(url?: string, method?: HttpMethods, body?: any, query?: { [key: string]: any; }, headers: { [key: string]: any; } = {}, rawResponse = false) {
abortSignal?: AbortSignal;
constructor(url?: string, method?: HttpMethods, body?: any, query?: { [key: string]: any; }, headers: { [key: string]: any; } = {}, rawResponse = false, abortSignal?: AbortSignal) {
this.rawResponse = rawResponse;
this.url = url || "";
this.method = method || "GET";
@ -32,6 +34,7 @@ export class WebResource {
this.body = body;
this.query = query;
this.formData = undefined;
this.abortSignal = abortSignal;
}
/**
@ -224,6 +227,8 @@ export class WebResource {
}
}
this.abortSignal = options.abortSignal;
return this;
}
}
@ -288,6 +293,7 @@ export interface RequestPrepareOptions {
deserializationMapper?: object;
disableJsonStringifyOnBody?: boolean;
bodyIsStream?: boolean;
abortSignal?: AbortSignal;
}
/**

335
package-lock.json сгенерированный
Просмотреть файл

@ -143,14 +143,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.17.tgz",
"integrity": "sha512-K/pOQpXevFVZYFWI+Oi6yDzVv4j665eEW3w5pXa/GOfWi7kwzHiSkX1kMEDwoAe0LcHFIOIezgOQfXfUXd392Q=="
},
"@types/node-fetch": {
"version": "1.6.9",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-1.6.9.tgz",
"integrity": "sha512-n2r6WLoY7+uuPT7pnEtKJCmPUGyJ+cbyBR8Avnu4+m1nzz7DwBVuyIvvlBzCZ/nrpC7rIgb3D6pNavL7rFEa9g==",
"requires": {
"@types/node": "*"
}
},
"@types/rimraf": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-0.0.28.tgz",
@ -178,6 +170,11 @@
"integrity": "sha512-42zEJkBpNfMEAvWR5WlwtTH22oDzcMjFsL9gDGExwF8X8WvAiw7Vwop7hPw03QT8TKfec83LwbHj6SvpqM4ELQ==",
"dev": true
},
"@types/tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz",
"integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ=="
},
"@types/uglify-js": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.0.2.tgz",
@ -252,17 +249,6 @@
"@webassemblyjs/wast-parser": "1.4.3",
"debug": "^3.1.0",
"webassemblyjs": "1.4.3"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"@webassemblyjs/floating-point-hex-parser": {
@ -278,17 +264,6 @@
"dev": true,
"requires": {
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"@webassemblyjs/helper-code-frame": {
@ -323,17 +298,6 @@
"@webassemblyjs/helper-wasm-bytecode": "1.4.3",
"@webassemblyjs/wasm-gen": "1.4.3",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"@webassemblyjs/leb128": {
@ -369,17 +333,6 @@
"@webassemblyjs/wasm-parser": "1.4.3",
"@webassemblyjs/wast-printer": "1.4.3",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"@webassemblyjs/wasm-gen": {
@ -404,17 +357,6 @@
"@webassemblyjs/wasm-gen": "1.4.3",
"@webassemblyjs/wasm-parser": "1.4.3",
"debug": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"@webassemblyjs/wasm-parser": {
@ -455,6 +397,12 @@
"long": "^3.2.0"
}
},
"abortcontroller-polyfill": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.1.9.tgz",
"integrity": "sha512-omvG7zOHIs3BphdH62Kh3xy8nlftAsTyp7PDa9EmC3Jz9pa6sZFYk7UhNgu9Y4sIBhj6jF0RgeFZYvPnsP5sBw==",
"dev": true
},
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
@ -689,6 +637,22 @@
"integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=",
"dev": true
},
"axios": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
"requires": {
"follow-redirects": "^1.3.0",
"is-buffer": "^1.1.5"
},
"dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
}
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -748,6 +712,15 @@
"source-map": "^0.5.7"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@ -1560,6 +1533,17 @@
"globals": "^9.18.0",
"invariant": "^2.2.2",
"lodash": "^4.17.4"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"babel-types": {
@ -1695,11 +1679,14 @@
"type-is": "~1.6.15"
},
"dependencies": {
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
"dev": true
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
@ -1960,9 +1947,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30000842",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000842.tgz",
"integrity": "sha512-juspQHLwQRgptEM03HN66SpM/ggZUB+m49NAgJIaIS11aXVNeRB57sEY1X6tEzeK2THGvYWKZZu1wIbh+W7YTA==",
"version": "1.0.30000843",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000843.tgz",
"integrity": "sha512-1ntiW826MhRBmM0CeI7w1cQr16gxwOoM8doJWh3BFalPZoKWdZXs27Bc04xth/3NR1/wNXn9cpP4F92lVenCvg==",
"dev": true
},
"chalk": {
@ -2517,10 +2504,9 @@
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
@ -2786,14 +2772,6 @@
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
"dev": true
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
@ -2883,11 +2861,6 @@
"next-tick": "1"
}
},
"es6-denodeify": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz",
"integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8="
},
"es6-iterator": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
@ -3050,6 +3023,15 @@
"to-regex": "^3.0.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@ -3172,6 +3154,17 @@
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"extend-shallow": {
@ -3297,34 +3290,6 @@
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
},
"fetch-cookie": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.0.tgz",
"integrity": "sha512-Mm5pGlT3agW6t71xVM7vMZPIvI7T4FaTuFW4jari6dVzYHFDb3WZZsGpN22r/o3XMdkM0E7sPd1EGeyVbH2Tgg==",
"requires": {
"es6-denodeify": "^0.1.1",
"tough-cookie": "^2.3.1"
}
},
"fetch-ponyfill": {
"version": "github:amarzavery/fetch-ponyfill#136e6f8192bdb2aa0b7983f0b3b4361c357be9db",
"from": "github:amarzavery/fetch-ponyfill#master",
"requires": {
"fetch-cookie": "~0.6.0",
"node-fetch": "~1.7.1"
},
"dependencies": {
"fetch-cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.6.0.tgz",
"integrity": "sha1-T+xOQIzAAH9sBOVTYYr0s97jf2k=",
"requires": {
"es6-denodeify": "^0.1.1",
"tough-cookie": "^2.3.1"
}
}
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@ -3376,6 +3341,17 @@
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"find-cache-dir": {
@ -3423,6 +3399,14 @@
"readable-stream": "^2.0.4"
}
},
"follow-redirects": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz",
"integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==",
"requires": {
"debug": "^3.1.0"
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -4506,12 +4490,10 @@
"dev": true
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
"dev": true
},
"ieee754": {
"version": "1.1.11",
@ -5001,6 +4983,15 @@
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
"dev": true
},
"isomorphic-tough-cookie": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-tough-cookie/-/isomorphic-tough-cookie-0.0.1.tgz",
"integrity": "sha512-9vV3Nl9PpBdd/LHIjBdiMYwWoAU3j+v+WvYJNkVnMrAgFBthgkIwQvwRl+M1J8R1AE51N/Dyv3nT38zzi7FSaw==",
"requires": {
"@types/tough-cookie": "^2.3.3",
"tough-cookie": "^2.3.4"
}
},
"isomorphic-xml2js": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/isomorphic-xml2js/-/isomorphic-xml2js-0.0.3.tgz",
@ -5246,6 +5237,17 @@
"dev": true,
"requires": {
"debug": "^2.6.8"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"listr": {
@ -5966,17 +5968,6 @@
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "4.4.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"mocha-chrome": {
@ -6000,17 +5991,6 @@
"loglevel": "^1.4.1",
"meow": "^4.0.0",
"nanobus": "^4.2.0"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"move-concurrently": {
@ -6030,8 +6010,7 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"multimatch": {
"version": "2.1.0",
@ -6143,15 +6122,6 @@
"integrity": "sha1-VfuN62mQcHB/tn+RpGDwRIKUx30=",
"dev": true
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"node-libs-browser": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",
@ -7002,12 +6972,6 @@
"statuses": ">= 1.3.1 < 2"
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
"dev": true
},
"setprototypeof": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
@ -7355,11 +7319,6 @@
"ret": "~0.1.10"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -7406,6 +7365,17 @@
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"serialize-javascript": {
@ -7615,6 +7585,15 @@
"use": "^3.1.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
@ -8284,9 +8263,9 @@
}
},
"tsutils": {
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.0.tgz",
"integrity": "sha512-JcyX25oM9pFcb3zh60OqG1St8p/uSqC5Bgipdo3ieacB/Ao4dPhm7hAtKT9NrEu23CyYrrgJPV3CqYfo+/+T4w==",
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz",
"integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
@ -9150,15 +9129,6 @@
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@ -9212,15 +9182,6 @@
"lodash": "^4.14.0"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",

Просмотреть файл

@ -33,24 +33,25 @@
"@types/form-data": "^2.2.1",
"@types/is-stream": "^1.1.0",
"@types/node": "^9.4.6",
"@types/node-fetch": "^1.6.7",
"@types/uuid": "^3.4.3",
"fetch-cookie": "^0.7.0",
"fetch-ponyfill": "amarzavery/fetch-ponyfill#master",
"axios": "^0.18.0",
"form-data": "^2.3.2",
"is-buffer": "^2.0.0",
"is-stream": "^1.1.0",
"isomorphic-tough-cookie": "^0.0.1",
"isomorphic-xml2js": "^0.0.3",
"url-parse": "^1.2.0",
"uuid": "^3.2.1",
"isomorphic-xml2js": "^0.0.3"
"uuid": "^3.2.1"
},
"devDependencies": {
"@types/glob": "^5.0.35",
"@types/mocha": "^5.2.0",
"@types/should": "^8.1.30",
"@types/tough-cookie": "^2.3.3",
"@types/url-parse": "^1.1.0",
"@types/webpack": "^4.1.3",
"@types/webpack-dev-middleware": "^2.0.1",
"abortcontroller-polyfill": "^1.1.9",
"express": "^4.16.3",
"glob": "^7.1.2",
"mocha": "^5.1.1",
@ -94,4 +95,4 @@
"preview": "node ./.scripts/preview.js",
"latest": "node ./.scripts/latest.js"
}
}
}

Просмотреть файл

@ -6,23 +6,16 @@ import * as path from "path";
let serverProcess: childProcess.ChildProcess;
// TODO: figure out why this hangs on Windows
const flag = false;
if (flag) {
before(function(done) {
serverProcess = childProcess.spawn(path.join(__dirname, "../../node_modules/.bin/ts-node"), ["testserver", "--no-webpack"], { shell: true });
const dataListener = function(data: Buffer) {
console.log(data.toString("utf-8"));
serverProcess.stdout.removeListener("data", dataListener);
done();
};
before(function(done) {
serverProcess = childProcess.spawn(path.join(__dirname, "../../node_modules/.bin/ts-node"), ["testserver", "--no-webpack"], { shell: true });
const dataListener = function() { done(); serverProcess.stdout.removeListener("data", dataListener); };
serverProcess.stdout.on("data", dataListener);
serverProcess.stderr.on("data", dataListener);
serverProcess.on("error", function(err) { done(err); });
});
serverProcess.stdout.on("data", dataListener);
serverProcess.stderr.on("data", dataListener);
serverProcess.on("error", function(err) { done(err); });
});
after(function() {
serverProcess.kill();
});
}
after(function() {
serverProcess.stdout.destroy();
serverProcess.stderr.destroy();
serverProcess.kill();
});

Просмотреть файл

@ -0,0 +1,50 @@
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 50px;
background-color: #fff;
border-radius: 1em;
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
body {
background-color: #fff;
}
div {
width: auto;
margin: 0 auto;
border-radius: 0;
padding: 1em;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is established to be used for illustrative examples in documents. You may use this
domain in examples without prior coordination or asking for permission.</p>
<p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,246 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='content-type' value='text/html;charset=utf8'>
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
<title>httpbin(1): HTTP Client Testing Service</title>
<style type='text/css' media='all'>
/* style: man */
body#manpage {margin:0}
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
.mp h2 {margin:10px 0 0 0}
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
.mp h3 {margin:0 0 0 4ex}
.mp dt {margin:0;clear:left}
.mp dt.flush {float:left;width:8ex}
.mp dd {margin:0 0 0 9ex}
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
.mp pre {margin-bottom:20px}
.mp pre+h2,.mp pre+h3 {margin-top:22px}
.mp h2+pre,.mp h3+pre {margin-top:5px}
.mp img {display:block;margin:auto}
.mp h1.man-title {display:none}
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
.mp h2 {font-size:16px;line-height:1.25}
.mp h1 {font-size:20px;line-height:2}
.mp {text-align:justify;background:#fff}
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
.mp u {text-decoration:underline}
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
.mp b.man-ref {font-weight:normal;color:#434241}
.mp pre {padding:0 4ex}
.mp pre code {font-weight:normal;color:#434241}
.mp h2+pre,h3+pre {padding-left:0}
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
ol.man-decor {width:100%}
ol.man-decor li.tl {text-align:left}
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
ol.man-decor li.tr {text-align:right;float:right}
</style>
<style type='text/css' media='all'>
/* style: 80c */
.mp {max-width:86ex}
ul {list-style: None; margin-left: 1em!important}
.man-navigation {left:101ex}
</style>
</head>
<body id='manpage'>
<a href="http://github.com/kennethreitz/httpbin"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<div class='mp'>
<h1>httpbin(1): HTTP Request &amp; Response Service</h1>
<p>Freely hosted in <a href="http://httpbin.org">HTTP</a>, <a href="https://httpbin.org">HTTPS</a>, &amp; <a href="http://eu.httpbin.org/">EU</a> flavors by <a href="http://kennethreitz.org/bitcoin">Kenneth Reitz</a> &amp; <a href="https://www.runscope.com/">Runscope</a>.</p>
<h2 id="BONUSPOINTS">BONUSPOINTS</h2>
<ul>
<li><a href="https://now.httpbin.org/" data-bare-link="true"><code>now.httpbin.org</code></a> The current time, in a variety of formats.</li>
</ul>
<h2 id="ENDPOINTS">ENDPOINTS</h2>
<ul>
<li><a href="/" data-bare-link="true"><code>/</code></a> This page.</li>
<li><a href="/ip" data-bare-link="true"><code>/ip</code></a> Returns Origin IP.</li>
<li><a href="/uuid" data-bare-link="true"><code>/uuid</code></a> Returns UUID4.</li>
<li><a href="/user-agent" data-bare-link="true"><code>/user-agent</code></a> Returns user-agent.</li>
<li><a href="/headers" data-bare-link="true"><code>/headers</code></a> Returns header dict.</li>
<li><a href="/get" data-bare-link="true"><code>/get</code></a> Returns GET data.</li>
<li><code>/post</code> Returns POST data.</li>
<li><code>/patch</code> Returns PATCH data.</li>
<li><code>/put</code> Returns PUT data.</li>
<li><code>/delete</code> Returns DELETE data</li>
<li><a href="/anything" data-bare-link="true"><code>/anything</code></a> Returns request data, including method used.</li>
<li><code>/anything/:anything</code> Returns request data, including the URL.</li>
<li><a href="/base64/aGVsbG8gd29ybGQNCg%3D%3D"><code>/base64/:value</code></a> Decodes base64url-encoded string.</li>
<li><a href="/encoding/utf8"><code>/encoding/utf8</code></a> Returns page containing UTF-8 data.</li>
<li><a href="/gzip" data-bare-link="true"><code>/gzip</code></a> Returns gzip-encoded data.</li>
<li><a href="/deflate" data-bare-link="true"><code>/deflate</code></a> Returns deflate-encoded data.</li>
<li><a href="/brotli" data-bare-link="true"><code>/brotli</code></a> Returns brotli-encoded data.</li>
<li><a href="/status/418"><code>/status/:code</code></a> Returns given HTTP Status code.</li>
<li><a href="/response-headers?Content-Type=text%2Fplain%3B+charset%3DUTF-8&amp;Server=httpbin"><code>/response-headers?key=val</code></a> Returns given response headers.</li>
<li><a href="/redirect/6"><code>/redirect/:n</code></a> 302 Redirects <em>n</em> times.</li>
<li><a href="/redirect-to?url=http%3A%2F%2Fexample.com%2F"><code>/redirect-to?url=foo</code></a> 302 Redirects to the <em>foo</em> URL.</li>
<li><a href="/redirect-to?url=http%3A%2F%2Fexample.com%2F&amp;status_code=307"><code>/redirect-to?url=foo&status_code=307</code></a> 307 Redirects to the <em>foo</em> URL.</li>
<li><a href="/relative-redirect/6"><code>/relative-redirect/:n</code></a> 302 Relative redirects <em>n</em> times.</li>
<li><a href="/absolute-redirect/6"><code>/absolute-redirect/:n</code></a> 302 Absolute redirects <em>n</em> times.</li>
<li><a href="/cookies" data-bare-link="true"><code>/cookies</code></a> Returns cookie data.</li>
<li><a href="/cookies/set?k2=v2&amp;k1=v1"><code>/cookies/set?name=value</code></a> Sets one or more simple cookies.</li>
<li><a href="/cookies/delete?k2=&amp;k1="><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>
<li><a href="/basic-auth/user/passwd"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>
<li><a href="/hidden-basic-auth/user/passwd"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5/never"><code>/digest-auth/:qop/:user/:passwd/:algorithm</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/digest-auth/auth/user/passwd/MD5/never"><code>/digest-auth/:qop/:user/:passwd</code></a> Challenges HTTP Digest Auth.</li>
<li><a href="/stream/20"><code>/stream/:n</code></a> Streams <em>min(n, 100)</em> lines.</li>
<li><a href="/delay/3"><code>/delay/:n</code></a> Delays responding for <em>min(n, 10)</em> seconds.</li>
<li><a href="/drip?duration=5&amp;numbytes=5&amp;code=200"><code>/drip?numbytes=n&amp;duration=s&amp;delay=s&amp;code=code</code></a> Drips data over a duration after an optional initial delay, then (optionally) returns with the given status code.</li>
<li><a href="/range/1024"><code>/range/1024?duration=s&amp;chunk_size=code</code></a> Streams <em>n</em> bytes, and allows specifying a <em>Range</em> header to select a subset of the data. Accepts a <em>chunk_size</em> and request <em>duration</em> parameter.</li>
<li><a href="/html" data-bare-link="true"><code>/html</code></a> Renders an HTML Page.</li>
<li><a href="/robots.txt" data-bare-link="true"><code>/robots.txt</code></a> Returns some robots.txt rules.</li>
<li><a href="/deny" data-bare-link="true"><code>/deny</code></a> Denied by robots.txt file.</li>
<li><a href="/cache" data-bare-link="true"><code>/cache</code></a> Returns 200 unless an If-Modified-Since or If-None-Match header is provided, when it returns a 304.</li>
<li><a href="/etag/etag"><code>/etag/:etag</code></a> Assumes the resource has the given etag and responds to If-None-Match header with a 200 or 304 and If-Match with a 200 or 412 as appropriate.</li>
<li><a href="/cache/60"><code>/cache/:n</code></a> Sets a Cache-Control header for <em>n</em> seconds.</li>
<li><a href="/bytes/1024"><code>/bytes/:n</code></a> Generates <em>n</em> random bytes of binary data, accepts optional <em>seed</em> integer parameter.</li>
<li><a href="/stream-bytes/1024"><code>/stream-bytes/:n</code></a> Streams <em>n</em> random bytes of binary data in chunked encoding, accepts optional <em>seed</em> and <em>chunk_size</em> integer parameters.</li>
<li><a href="/links/10"><code>/links/:n</code></a> Returns page containing <em>n</em> HTML links.</li>
<li><a href="/image"><code>/image</code></a> Returns page containing an image based on sent Accept header.</li>
<li><a href="/image/png"><code>/image/png</code></a> Returns a PNG image.</li>
<li><a href="/image/jpeg"><code>/image/jpeg</code></a> Returns a JPEG image.</li>
<li><a href="/image/webp"><code>/image/webp</code></a> Returns a WEBP image.</li>
<li><a href="/image/svg"><code>/image/svg</code></a> Returns a SVG image.</li>
<li><a href="/forms/post" data-bare-link="true"><code>/forms/post</code></a> HTML form that submits to <em>/post</em></li>
<li><a href="/xml" data-bare-link="true"><code>/xml</code></a> Returns some XML</li>
</ul>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<p>Testing an HTTP Library can become difficult sometimes. <a href="http://requestb.in">RequestBin</a> is fantastic for testing POST requests, but doesn't let you control the response. This exists to cover all kinds of HTTP scenarios. Additional endpoints are being considered.</p>
<p>All endpoint responses are JSON-encoded.</p>
<h2 id="EXAMPLES">EXAMPLES</h2>
<h3 id="-curl-http-httpbin-org-ip">$ curl http://httpbin.org/ip</h3>
<pre><code>{"origin": "24.127.96.129"}
</code></pre>
<h3 id="-curl-http-httpbin-org-user-agent">$ curl http://httpbin.org/user-agent</h3>
<pre><code>{"user-agent": "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"}
</code></pre>
<h3 id="-curl-http-httpbin-org-get">$ curl http://httpbin.org/get</h3>
<pre><code>{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "",
"Content-Type": "",
"Host": "httpbin.org",
"User-Agent": "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"
},
"origin": "24.127.96.129",
"url": "http://httpbin.org/get"
}
</code></pre>
<h3 id="-curl-I-http-httpbin-org-status-418">$ curl -I http://httpbin.org/status/418</h3>
<pre><code>HTTP/1.1 418 I'M A TEAPOT
Server: nginx/0.7.67
Date: Mon, 13 Jun 2011 04:25:38 GMT
Connection: close
x-more-info: http://tools.ietf.org/html/rfc2324
Content-Length: 135
</code></pre>
<h3 id="-curl-https-httpbin-org-get-show_env-1">$ curl https://httpbin.org/get?show_env=1</h3>
<pre><code>{
"headers": {
"Content-Length": "",
"Accept-Language": "en-US,en;q=0.8",
"Accept-Encoding": "gzip,deflate,sdch",
"X-Forwarded-Port": "443",
"X-Forwarded-For": "109.60.101.240",
"Host": "httpbin.org",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11",
"X-Request-Start": "1350053933441",
"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.3",
"Connection": "keep-alive",
"X-Forwarded-Proto": "https",
"Cookie": "_gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1; _gauges_unique_hour=1",
"Content-Type": ""
},
"args": {
"show_env": "1"
},
"origin": "109.60.101.240",
"url": "http://httpbin.org/get?show_env=1"
}
</code></pre>
<h2 id="Installing-and-running-from-PyPI">Installing and running from PyPI</h2>
<p>You can install httpbin as a library from PyPI and run it as a WSGI app. For example, using Gunicorn:</p>
<pre><code class="bash">$ pip install httpbin
$ gunicorn httpbin:app
</code></pre>
<h2 id="AUTHOR">AUTHOR</h2>
<p>A <a href="http://kennethreitz.com/">Kenneth Reitz</a> project.</p>
<p>BTC: <a href="https://www.kennethreitz.org/bitcoin"><code>1Me2iXTJ91FYZhrGvaGaRDCBtnZ4KdxCug</code></a></p>
<h2 id="SEE-ALSO">SEE ALSO</h2>
<p><a href="https://www.hurl.it">Hurl.it</a> - Make HTTP requests.</p>
<p><a href="http://requestb.in">RequestBin</a> - Inspect HTTP requests.</p>
<p><a href="http://python-requests.org" data-bare-link="true">http://python-requests.org</a></p>
</div>
<script type="text/javascript">
(function() {
window._pa = window._pa || {};
_pa.productId = "httpbin";
var pa = document.createElement('script'); pa.type = 'text/javascript'; pa.async = true;
pa.src = ('https:' == document.location.protocol ? 'https:' : 'http:') + "//tag.perfectaudience.com/serve/5226171f87bc6890da0000a0.js";
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(pa, s);
})();
</script>
<script type="text/javascript">
var _gauges = _gauges || [];
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id', '58cb2e71c88d9043ac01d000');
t.setAttribute('data-track-path', 'https://track.gaug.es/track.gif');
t.src = 'https://d36ee2fcip1434.cloudfront.net/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, s);
})();
</script>
</body>
</html>

1
test/resources/test.txt Normal file
Просмотреть файл

@ -0,0 +1 @@
The quick brown fox jumps over the lazy dog

Просмотреть файл

@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
import * as assert from "assert";
import * as should from "should";
import { AxiosHttpClient } from "../../lib/axiosHttpClient";
import { baseURL } from "../testUtils";
import { WebResource } from "../../lib/webResource";
import { isNode } from "../../lib/util/utils";
describe("axiosHttpClient", () => {
it("should send HTTP requests", async () => {
const request = new WebResource(`${baseURL}/example-index.html`, "GET");
const httpClient = new AxiosHttpClient();
const response = await httpClient.sendRequest(request);
assert.deepStrictEqual(response.request, request);
assert.strictEqual(response.status, 200);
assert(response.headers);
// content-length varies based on OS line endings
assert.strictEqual(response.headers.get("content-length"), response.bodyAsText!.length.toString());
assert.strictEqual(response.headers.get("content-type")!.split(";")[0], "text/html");
const responseBody: string | null | undefined = response.bodyAsText;
const expectedResponseBody =
`<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
div {
width: 600px;
margin: 5em auto;
padding: 50px;
background-color: #fff;
border-radius: 1em;
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
body {
background-color: #fff;
}
div {
width: auto;
margin: 0 auto;
border-radius: 0;
padding: 1em;
}
}
</style>
</head>
<body>
<div>
<h1>Example Domain</h1>
<p>This domain is established to be used for illustrative examples in documents. You may use this
domain in examples without prior coordination or asking for permission.</p>
<p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
`;
assert.strictEqual(
responseBody && responseBody.replace(/\r\n/g, "\n"),
expectedResponseBody.replace(/\r\n/g, "\n"));
});
it("should return a response instead of throwing for awaited 404", async () => {
const request = new WebResource(`${baseURL}/nonexistent`, "GET");
const httpClient = new AxiosHttpClient();
const response = await httpClient.sendRequest(request);
assert(response);
});
it("should allow canceling requests", async function() {
// ensure that a large upload is actually cancelled
this.timeout(2000);
let controller: AbortController;
if (isNode) {
const AbortControllerPonyfill = require("abortcontroller-polyfill/dist/cjs-ponyfill").AbortController;
controller = new AbortControllerPonyfill();
} else {
controller = new AbortController();
}
const request = new WebResource(`${baseURL}/fileupload`, "POST", new Uint8Array(1024*1024*100), undefined, undefined, true, controller.signal);
const client = new AxiosHttpClient();
const promise = client.sendRequest(request);
await new Promise((resolve) => {
setTimeout(() => {
controller.abort();
resolve();
});
});
try {
await promise;
assert.fail('');
} catch (err) {
should(err).not.be.instanceof(assert.AssertionError);
}
});
it("should allow canceling multiple requests with one token", async function () {
// ensure that a large upload is actually cancelled
this.timeout(4000);
let controller: AbortController;
if (isNode) {
const AbortControllerPonyfill = require("abortcontroller-polyfill/dist/cjs-ponyfill").AbortController;
controller = new AbortControllerPonyfill();
} else {
controller = new AbortController();
}
const buf = new Uint8Array(1024*1024*100);
const requests = [
new WebResource(`${baseURL}/fileupload`, "POST", buf, undefined, undefined, true, controller.signal),
new WebResource(`${baseURL}/fileupload`, "POST", buf, undefined, undefined, true, controller.signal)
];
const client = new AxiosHttpClient();
const promises = requests.map(r => client.sendRequest(r));
await new Promise((resolve) => {
setTimeout(() => {
controller.abort();
resolve();
});
});
// Ensure each promise is individually rejected
for (const promise of promises) {
try {
await promise;
assert.fail('');
} catch (err) {
should(err).not.be.instanceof(assert.AssertionError);
}
}
});
});

10
test/testUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// parseInt just gives NaN (falsy) for undefined/null
const port = parseInt(process.env.PORT!) || 3001;
/**
* Base URL for the ms-rest-js testserver.
*/
export const baseURL = `http://localhost:${port}`;