Skip to content

Commit

Permalink
Migrated XMLHttpRequest to fetch API (#506).
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo committed Jun 12, 2019
1 parent 6c19025 commit 62201c5
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 83 deletions.
7 changes: 6 additions & 1 deletion packages/errors/src.ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ export const NOT_IMPLEMENTED = "NOT_IMPLEMENTED";
// - operation
export const UNSUPPORTED_OPERATION = "UNSUPPORTED_OPERATION";

// Network Error
// Network Error (i.e. Ethereum Network, such as an invalid chain ID)
export const NETWORK_ERROR = "NETWORK_ERROR";

// Some sort of bad response from the server
export const SERVER_ERROR = "SERVER_ERROR";

// Timeout
export const TIMEOUT = "TIMEOUT";

///////////////////
// Operational Errors
Expand Down
4 changes: 2 additions & 2 deletions packages/tests/src.ts/test-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ describe('Test Basic Authentication', function() {
url: string;
user: string;
password: string;
allowInsecure?: boolean;
allowInsecureAuthentication?: boolean;
};

function test(name: string, url: TestCase): void {
Expand Down Expand Up @@ -488,7 +488,7 @@ describe('Test Basic Authentication', function() {
url: 'http://httpbin.org/basic-auth/user/passwd',
user: 'user',
password: 'passwd',
allowInsecure: true
allowInsecureAuthentication: true
};

test('secure url', secure);
Expand Down
8 changes: 0 additions & 8 deletions packages/web/src.ts/browser-xmlhttprequest.ts

This file was deleted.

136 changes: 64 additions & 72 deletions packages/web/src.ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";

import { XMLHttpRequest } from "xmlhttprequest";
import { default as fetch } from "node-fetch";

import { encode as base64Encode } from "@ethersproject/base64";
import * as errors from "@ethersproject/errors";
Expand All @@ -13,7 +13,7 @@ export type ConnectionInfo = {
url: string,
user?: string,
password?: string,
allowInsecure?: boolean,
allowInsecureAuthentication?: boolean,
timeout?: number,
headers?: { [key: string]: string | number }
};
Expand All @@ -40,14 +40,24 @@ export function fetchJson(connection: string | ConnectionInfo, json?: string, pr

let url: string = null;

// @TODO: Allow ConnectionInfo to override some of these values
let options: any = {
method: "GET",
mode: "cors", // no-cors, cors, *same-origin
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, *same-origin, omit
redirect: "follow", // manual, *follow, error
referrer: "client", // no-referrer, *client
};

let timeout = 2 * 60 * 1000;

if (typeof(connection) === "string") {
url = connection;

} else if (typeof(connection) === "object") {
if (connection.url == null) {
errors.throwError("missing URL", errors.MISSING_ARGUMENT, { arg: "url" });
if (connection == null || connection.url == null) {
errors.throwArgumentError("missing URL", "connection.url", connection);
}

url = connection.url;
Expand All @@ -63,7 +73,7 @@ export function fetchJson(connection: string | ConnectionInfo, json?: string, pr
}

if (connection.user != null && connection.password != null) {
if (url.substring(0, 6) !== "https:" && connection.allowInsecure !== true) {
if (url.substring(0, 6) !== "https:" && connection.allowInsecureAuthentication !== true) {
errors.throwError(
"basic authentication requires a secure https url",
errors.INVALID_ARGUMENT,
Expand All @@ -80,18 +90,19 @@ export function fetchJson(connection: string | ConnectionInfo, json?: string, pr
}

return new Promise(function(resolve, reject) {
let request = new XMLHttpRequest();

let timer: any = null;
timer = setTimeout(() => {
if (timer == null) { return; }
timer = null;

reject(new Error("timeout"));
setTimeout(() => {
request.abort();
}, 0);
}, timeout);
if (timeout) {
timer = setTimeout(() => {
if (timer == null) { return; }
timer = null;

reject(errors.makeError("timeout", errors.TIMEOUT, { }));
//setTimeout(() => {
// request.abort();
//}, 0);
}, timeout);
}

let cancelTimeout = () => {
if (timer == null) { return; }
Expand All @@ -100,85 +111,66 @@ export function fetchJson(connection: string | ConnectionInfo, json?: string, pr
}

if (json) {
request.open("POST", url, true);
options.method = "POST";
options.body = json;
headers["content-type"] = { key: "Content-Type", value: "application/json" };
} else {
request.open("GET", url, true);
}

let flatHeaders: { [ key: string ]: string } = { };
Object.keys(headers).forEach((key) => {
let header = headers[key];
request.setRequestHeader(header.key, header.value);
flatHeaders[header.key] = header.value;
});

request.onreadystatechange = function() {
if (request.readyState !== 4) { return; }

if (request.status != 200) {
cancelTimeout();
// @TODO: not any!
let error: any = new Error("invalid response - " + request.status);
error.statusCode = request.status;
if (request.responseText) {
error.responseText = request.responseText;
options.headers = flatHeaders;

return fetch(url, options).then((response) => {
return response.text().then((body) => {
if (!response.ok) {
errors.throwError("bad response", errors.SERVER_ERROR, {
status: response.status,
body: body,
type: response.type,
url: response.url
});
}
reject(error);
return;
}

let result: any = null;
return body;
});

}).then((text) => {
let json: any = null;
try {
result = JSON.parse(request.responseText);
json = JSON.parse(text);
} catch (error) {
cancelTimeout();
// @TODO: not any!
let jsonError: any = new Error("invalid json response");
jsonError.orginialError = error;
jsonError.responseText = request.responseText;
if (json != null) {
jsonError.requestBody = json;
}
jsonError.url = url;
reject(jsonError);
return;
errors.throwError("invalid JSON", errors.SERVER_ERROR, {
body: text,
error: error,
url: url
});
}

if (processFunc) {
try {
result = processFunc(result);
json = processFunc(json);
} catch (error) {
cancelTimeout();
error.url = url;
error.body = json;
error.responseText = request.responseText;
reject(error);
return;
errors.throwError("processing response error", errors.SERVER_ERROR, {
body: json,
error: error
});
}
}

return json;

}, (error) => {
throw error;
}).then((result) => {
cancelTimeout();
resolve(result);
};

request.onerror = function(error) {
}, (error) => {
cancelTimeout();
reject(error);
}

try {
if (json != null) {
request.send(json);
} else {
request.send();
}

} catch (error) {
cancelTimeout();
// @TODO: not any!
let connectionError: any = new Error("connection error");
connectionError.error = error;
reject(connectionError);
}
});
});
}

Expand Down

0 comments on commit 62201c5

Please sign in to comment.