Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch - dont override server returned error code and message #108

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { createAsyncMiddleware, JsonRpcMiddleware } from 'json-rpc-engine';
import { EthereumRpcError, ethErrors } from 'eth-rpc-errors';
import {
createAsyncMiddleware,
JsonRpcMiddleware,
JsonRpcRequest,
} from 'json-rpc-engine';
import {
EthereumRpcError,
ethErrors,
getMessageFromCode,
} from 'eth-rpc-errors';
import { Payload, Block } from './utils/cache';

/* eslint-disable node/global-require,@typescript-eslint/no-require-imports */
Expand Down Expand Up @@ -68,7 +76,7 @@ export function createFetchMiddleware({
`FetchMiddleware - failed to parse response body: "${rawBody}"`,
);
}
const result: Block = parseResponse(fetchRes, fetchBody);
const result: Block = parseResponse(fetchRes, fetchBody, req);
// set result and exit retry loop
res.result = result;
return;
Expand Down Expand Up @@ -106,7 +114,11 @@ function checkForHttpErrors(fetchRes: Response): void {
}
}

function parseResponse(fetchRes: Response, body: Record<string, Block>): Block {
function parseResponse(
fetchRes: Response,
body: Record<string, Block>,
req: JsonRpcRequest<unknown>,
): Block {
// check for error code
if (fetchRes.status !== 200) {
throw ethErrors.rpc.internal({
Expand All @@ -117,9 +129,21 @@ function parseResponse(fetchRes: Response, body: Record<string, Block>): Block {

// check for rpc error
if (body.error) {
throw ethErrors.rpc.internal({
data: body.error,
});
console.log('body.error', body.error)
const reqData = { req, res: body };
if (body.error.code) {
throw new EthereumRpcError(
Number(body.error.code),
String(body.error.message) ||
getMessageFromCode(Number(body.error.code)),
reqData,
);
} else {
throw new Error('yabdab')
throw ethErrors.rpc.internal({
data: reqData,
});
}
}
// return successful result
return body.result;
Expand Down
194 changes: 135 additions & 59 deletions test/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,65 +81,141 @@ test('fetch - auth in url', (t) => {
t.end();
});

test('fetch - server test', (t) => {
const rpcUrl = 'http://localhost:3000/abc/xyz';

const req = {
method: 'eth_getBalance',
params: ['0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 'latest'],
};
serverTest('fetch - server test', {
createReq() {
return {
id: 1,
json_rpc: '2.0',
method: 'eth_getBalance',
params: ['0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 'latest'],
};
},
handleReq() {
return {
id: 1,
result: 42,
};
},
afternFn(t, res) {
t.deepEquals(res, {
id: 1,
result: 42,
});
},
});

const rpcRes = { id: 1 };
let server;
let serverSideRequest;
let serverSidePayload;

series([createServer, makeRequest, closeServer], (err) => {
t.ifError(err, 'should not error');
// validate request
t.equals(serverSideRequest.headers.accept, 'application/json');
t.equals(serverSideRequest.headers['content-type'], 'application/json');
t.equals(serverSideRequest.method, 'POST');
// eslint-disable-next-line node/no-deprecated-api
t.equals(serverSideRequest.url, url.parse(rpcUrl).path);
t.deepEquals(serverSidePayload, req);
// validate response
t.deepEquals(rpcRes, { id: 1, result: 42 });
t.end();
});
serverTest('fetch - server with error code', {
createReq() {
return {
id: 1,
json_rpc: '2.0',
method: 'eth_getBalance',
params: ['0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 'latest'],
};
},
handleReq() {
return {
id: 1,
error: {
code: 42,
message: 'dank',
},
};
},
afternFn(t, res) {
t.deepEquals(res, {
id: 1,
error: {
code: 42,
message: 'dank',
},
});
},
});

function requestHandler(request, response) {
request.pipe(
concat((rawRequestBody) => {
const payload = JSON.parse(rawRequestBody.toString());
// save request details
serverSideRequest = request;
serverSidePayload = payload;
// send response
const responseBody = JSON.stringify({
id: 1,
result: 42,
});
response.end(responseBody);
}),
);
}

function createServer(cb) {
server = http.createServer(requestHandler);
server.listen(3000, cb);
}

function closeServer(cb) {
server.close(cb);
}

function makeRequest(cb) {
const middleware = createFetchMiddleware({ rpcUrl });
middleware(req, rpcRes, failTest, cb);
}

function failTest() {
t.fail('something broke');
}
serverTest('fetch - server with NO error code', {
createReq() {
return {
id: 1,
json_rpc: '2.0',
method: 'eth_getBalance',
params: ['0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 'latest'],
};
},
handleReq() {
return {
id: 1,
error: {
message: 'big baloo',
},
};
},
afternFn(t, res) {
t.deepEquals(res, {
id: 1,
error: {
message: 'big baloo',
},
});
},
});

function serverTest(label, { createReq, handleReq, afternFn }) {
test(label, (t) => {
const rpcUrl = 'http://localhost:3000/abc/xyz';
const clientReq = createReq();
const clientRes = { id: clientReq.id };

let server;
let serverSideNetworkRequest;
let serverSideReq;
let serverSideRes;

series([createServer, makeRequest, closeServer], (err) => {
t.ifError(err, 'should not error');
// validate request
t.equals(serverSideNetworkRequest.headers.accept, 'application/json');
t.equals(
serverSideNetworkRequest.headers['content-type'],
'application/json',
);
t.equals(serverSideNetworkRequest.method, 'POST');
// eslint-disable-next-line node/no-deprecated-api
t.equals(serverSideNetworkRequest.url, url.parse(rpcUrl).path);
afternFn(t, serverSideRes);
t.end();
});

function requestHandler(request, response) {
request.pipe(
concat((rawRequestBody) => {
// save request details
serverSideNetworkRequest = request;
serverSideReq = JSON.parse(rawRequestBody.toString());
serverSideRes = handleReq(serverSideReq);
// send response
const responseBody = JSON.stringify(serverSideRes);
response.end(responseBody);
}),
);
}

function createServer(cb) {
server = http.createServer(requestHandler);
server.listen(3000, cb);
}

function closeServer(cb) {
server.close(cb);
}

function makeRequest(cb) {
const middleware = createFetchMiddleware({ rpcUrl });
middleware(clientReq, clientRes, failTest, (err) => cb());
}

function failTest() {
t.fail('something broke');
}
});
}