Skip to content

Commit

Permalink
chore: Cherry pick PR2504 (#2541)
Browse files Browse the repository at this point in the history
feat: Add Koa Server Request Timeout (#2504)

* Add Koa Server Request Timeout



* fix: Added test and documentation to the server request timeout setting.



fix: Added doc updated.



fix: Bumped timeout to 60 seconds.



fix: removed only.



fix: Added empty line.



Update packages/server/src/index.ts




fix: Applied feedback



fix: Added back the chai.



fix: Restored the chai exclude



fix: Removed the async in the sendJsonRpcRequestWithDelay



fix: Reduced the timeout to allow tests to finish.



fix: Moved timeout test to rateLimiter.



fix: Removed comma after LIMIT_DURATION



fix: Bumped up the timeout delay to ensure it works in CI.



fix: Testing in CI.  Removed the .only



fix: Adjusted the test delay.



fix: Added error details.



fix: Added more debug info.



fix: Added delay for linux.  Debugging in CI.



fix: Debugging in CI.  Setting test timeout



fix: Debugging in CI.  Nested promise.



fix: Including delay in testfile.  Debugging CI.



fix: Debugging in CIfix: Debugging in CI.  Updated package-log.


fix: Extracted setting logic to util function to be used in server startup and test framework



fix:  Bumped up the timeout for tests on local node.



* fix: Moved into its own CI job.



* fix: Cleanup



* fix: Clean up.



---------

Signed-off-by: Nana Essilfie-Conduah <nana@swirldslabs.com>
Signed-off-by: ebadiere <ebadiere@gmail.com>
Signed-off-by: Logan Nguyen <logan.nguyen@swirldslabs.com>
Co-authored-by: Nana Essilfie-Conduah <56320167+Nana-EC@users.noreply.github.com>
Co-authored-by: ebadiere <ebadiere@gmail.com>
  • Loading branch information
3 people authored May 28, 2024
1 parent d0a8ceb commit 859fc4a
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/acceptance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ jobs:
with:
testfilter: cache-service

server-config:
name: Server Config
uses: ./.github/workflows/acceptance-workflow.yml
with:
testfilter: serverconfig

publish_results:
name: Publish Results
if: ${{ !cancelled() }}
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Unless you need to set a non-default value, it is recommended to only populate o
| `RATE_LIMIT_DISABLED` | "false" | Flag to disable IP based rate limiting. |
| `REQUEST_ID_IS_OPTIONAL` | "" | Flag to set it the JSON RPC request id field in the body should be optional. Note, this breaks the API spec and is not advised and is provided for test purposes only where some wallets may be non compliant |
| `SERVER_PORT` | "7546" | The RPC server port number to listen for requests on. Currently a static value defaulting to 7546. See [#955](https://github.com/hashgraph/hedera-json-rpc-relay/issues/955) |
| `SERVER_REQUEST_TIMEOUT_MS`| "60000" | The time of inactivity allowed before a timeout is triggered and the socket is closed. See [NodeJs Server Timeout](https://nodejs.org/api/http.html#serversettimeoutmsecs-callback) |

## Relay

Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"acceptancetest:precompile-calls": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@precompile-calls' --exit",
"acceptancetest:cache-service": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@cache-service' --exit",
"acceptancetest:rpc_api_schema_conformity": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@api-conformity' --exit",
"acceptancetest:serverconfig": "ts-mocha packages/server/tests/acceptance/index.spec.ts -g '@server-config' --exit",
"build": "npx lerna run build",
"build-and-test": "npx lerna run build && npx lerna run test",
"build:docker": "docker build . -t ${npm_package_name}",
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
*/

import app from './server';
import { setServerTimeout } from './koaJsonRpc/lib/utils'; // Import the 'setServerTimeout' function from the correct location

async function main() {
await app.listen({ port: process.env.SERVER_PORT || 7546 });
const server = await app.listen({ port: process.env.SERVER_PORT || 7546 });

// set request timeout to ensure sockets are closed after specified time of inactivity
setServerTimeout(server);
}

main();
26 changes: 26 additions & 0 deletions packages/server/src/koaJsonRpc/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*-
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import type { Server } from 'http';

export function setServerTimeout(server: Server): void {
const requestTimeoutMs = parseInt(process.env.SERVER_REQUEST_TIMEOUT_MS ?? '60000');
server.setTimeout(requestTimeoutMs);
}
2 changes: 2 additions & 0 deletions packages/server/tests/acceptance/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import constants from '@hashgraph/json-rpc-relay/dist/lib/constants';
// Utils and types
import { Utils } from '../helpers/utils';
import { AliasAccount } from '../types/AliasAccount';
import { setServerTimeout } from '../../src/koaJsonRpc/lib/utils';

chai.use(chaiAsPromised);
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
Expand Down Expand Up @@ -185,6 +186,7 @@ describe('RPC Server Acceptance Tests', function () {

logger.info(`Start relay on port ${constants.RELAY_PORT}`);
relayServer = app.listen({ port: constants.RELAY_PORT });
setServerTimeout(relayServer);

if (process.env.TEST_WS_SERVER === 'true') {
logger.info(`Start ws-server on port ${constants.WEB_SOCKET_PORT}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/server/tests/acceptance/rateLimiter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2023 Hedera Hashgraph, LLC
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
41 changes: 41 additions & 0 deletions packages/server/tests/acceptance/serverConfig.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*-
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { expect } from 'chai';
import { Utils } from '../helpers/utils';

describe('@server-config Server Configuration Options Coverage', function () {
describe('Koa Server Timeout', () => {
it('should timeout a request after the specified time', async () => {
const requestTimeoutMs: number = parseInt(process.env.SERVER_REQUEST_TIMEOUT_MS || '3000');
const host = 'localhost';
const port = parseInt(process.env.SERVER_PORT || '7546');
const method = 'eth_blockNumber';
const params: any[] = [];

try {
await Utils.sendJsonRpcRequestWithDelay(host, port, method, params, requestTimeoutMs + 1000);
throw new Error('Request did not timeout as expected'); // Force the test to fail if the request does not time out
} catch (err) {
expect(err.code).to.equal('ECONNRESET');
expect(err.message).to.equal('socket hang up');
}
});
});
});
60 changes: 60 additions & 0 deletions packages/server/tests/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import RelayCall from '../../tests/helpers/constants';
import { AccountId, KeyList, PrivateKey } from '@hashgraph/sdk';
import { AliasAccount } from '../types/AliasAccount';
import ServicesClient from '../clients/servicesClient';
import http from 'http';

export class Utils {
/**
Expand Down Expand Up @@ -308,4 +309,63 @@ export class Utils {
}
return accounts;
}

static sendJsonRpcRequestWithDelay(
host: string,
port: number,
method: string,
params: any[],
delayMs: number,
): Promise<any> {
const requestData = JSON.stringify({
jsonrpc: '2.0',
method: method,
params: params,
id: 1,
});

const options = {
hostname: host,
port: port,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(requestData),
},
timeout: delayMs,
};

return new Promise((resolve, reject) => {
// setup the request
const req = http.request(options, (res) => {
let data = '';

res.on('data', (chunk) => {
data += chunk;
});

res.on('end', () => {
resolve(JSON.parse(data));
});
});

// handle request errors for testing purposes
req.on('timeout', () => {
req.destroy();
reject(new Error(`Request timed out after ${delayMs}ms`));
});

req.on('error', (err) => {
reject(err);
});

// Introduce a delay with inactivity, before sending the request
setTimeout(async () => {
req.write(requestData);
req.end();
await new Promise((r) => setTimeout(r, delayMs + 1000));
}, delayMs);
});
}
}
1 change: 1 addition & 0 deletions packages/server/tests/localAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ TEST_GAS_PRICE_DEVIATION=0.2
WS_NEW_HEADS_ENABLED=false
INITIAL_BALANCE='5000000000'
LIMIT_DURATION=90000
SERVER_REQUEST_TIMEOUT_MS=60000
1 change: 1 addition & 0 deletions packages/server/tests/previewnetAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ FILTER_API_ENABLED=true
DEBUG_API_ENABLED=true
TEST_GAS_PRICE_DEVIATION=0.2
WS_NEW_HEADS_ENABLED=true
SERVER_REQUEST_TIMEOUT_MS=60000

1 change: 1 addition & 0 deletions packages/server/tests/testnetAcceptance.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ SUBSCRIPTIONS_ENABLED=true
FILTER_API_ENABLED=true
DEBUG_API_ENABLED=true
TEST_GAS_PRICE_DEVIATION=0.2
SERVER_REQUEST_TIMEOUT_MS=60000

0 comments on commit 859fc4a

Please sign in to comment.