Skip to content

Commit 253e222

Browse files
committed
moved tests to acceptance batch
Signed-off-by: Simeon Nakov <[email protected]>
1 parent 50cf050 commit 253e222

File tree

2 files changed

+211
-216
lines changed

2 files changed

+211
-216
lines changed

packages/server/tests/acceptance/rateLimiter.spec.ts

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services';
34
import { IPRateLimiterService } from '@hashgraph/json-rpc-relay/src/lib/services';
45
import { RedisRateLimitStore } from '@hashgraph/json-rpc-relay/src/lib/services/rateLimiterService/RedisRateLimitStore';
56
import { RequestDetails } from '@hashgraph/json-rpc-relay/src/lib/types/RequestDetails';
7+
import Axios, { AxiosInstance, AxiosResponse } from 'axios';
68
import { expect } from 'chai';
9+
import { Server } from 'http';
10+
import Koa from 'koa';
711
import pino from 'pino';
812
import { Registry } from 'prom-client';
913
import { createClient, RedisClientType } from 'redis';
1014

15+
import { ConfigServiceTestHelper } from '../../../config-service/tests/configServiceTestHelper';
16+
import { overrideEnvsInMochaDescribe } from '../../../relay/tests/helpers';
17+
import RelayCalls from '../../tests/helpers/constants';
18+
1119
describe('@ratelimiter Shared Rate Limiting Acceptance Tests', function () {
1220
this.timeout(30 * 1000); // 30 seconds
1321

@@ -281,4 +289,207 @@ describe('@ratelimiter Shared Rate Limiting Acceptance Tests', function () {
281289
expect(nextRequest).to.be.true;
282290
});
283291
});
292+
293+
describe('X-Forwarded-For Header Integration Tests', function () {
294+
// Test with rate limiting enabled and a low limit to make testing easier
295+
overrideEnvsInMochaDescribe({
296+
RATE_LIMIT_DISABLED: false,
297+
TIER_2_RATE_LIMIT: 3, // Low limit for easy testing
298+
});
299+
300+
let testServer: Server;
301+
let testClient: AxiosInstance;
302+
let app: Koa<Koa.DefaultState, Koa.DefaultContext>;
303+
304+
// Simple static test IPs - each test uses different IP ranges to avoid conflicts
305+
const TEST_IP_A = '192.168.1.100';
306+
const TEST_IP_B = '192.168.2.100';
307+
const TEST_IP_C = '192.168.3.100';
308+
const TEST_IP_D = '192.168.4.100';
309+
const TEST_IP_E = '192.168.5.100';
310+
const TEST_METHOD = RelayCalls.ETH_ENDPOINTS.ETH_CHAIN_ID;
311+
312+
before(function () {
313+
ConfigServiceTestHelper.appendEnvsFromPath(__dirname + '/../integration/test.env');
314+
app = require('../../src/server').default;
315+
testServer = app.listen(ConfigService.get('E2E_SERVER_PORT'));
316+
testClient = createTestClient();
317+
});
318+
319+
after(function () {
320+
testServer.close((err) => {
321+
if (err) {
322+
console.error(err);
323+
}
324+
});
325+
});
326+
327+
this.timeout(10000);
328+
329+
function createTestClient(port = ConfigService.get('E2E_SERVER_PORT')) {
330+
return Axios.create({
331+
baseURL: 'http://localhost:' + port,
332+
responseType: 'json' as const,
333+
headers: {
334+
'Content-Type': 'application/json',
335+
},
336+
method: 'POST',
337+
timeout: 5 * 1000,
338+
});
339+
}
340+
341+
function createRequestWithIP(id: string, ip: string) {
342+
return {
343+
id: id,
344+
jsonrpc: '2.0',
345+
method: TEST_METHOD,
346+
params: [null],
347+
};
348+
}
349+
350+
async function makeRequestWithForwardedIP(ip: string, id: string = '1') {
351+
return testClient.post('/', createRequestWithIP(id, ip), {
352+
headers: {
353+
'X-Forwarded-For': ip,
354+
},
355+
});
356+
}
357+
358+
async function makeRequestWithoutForwardedIP(id: string = '1') {
359+
return testClient.post('/', createRequestWithIP(id, ''));
360+
}
361+
362+
it('should use X-Forwarded-For header IP for rate limiting when app.proxy is true', async function () {
363+
// Make requests up to the rate limit for IP_A using X-Forwarded-For header
364+
const responses: AxiosResponse[] = [];
365+
366+
// Make requests within the limit (TIER_2_RATE_LIMIT = 3)
367+
for (let i = 1; i <= 3; i++) {
368+
const response = await makeRequestWithForwardedIP(TEST_IP_A, i.toString());
369+
responses.push(response);
370+
371+
expect(response.status).to.eq(200);
372+
expect(response.data.result).to.be.equal(ConfigService.get('CHAIN_ID'));
373+
}
374+
375+
// The next request should be rate limited for IP_A
376+
try {
377+
await makeRequestWithForwardedIP(TEST_IP_A, '4');
378+
expect.fail('Expected rate limit to be exceeded');
379+
} catch (error: any) {
380+
expect(error.response.status).to.eq(429);
381+
expect(error.response.data.error.code).to.eq(-32605); // IP Rate Limit Exceeded
382+
expect(error.response.data.error.message).to.include('IP Rate limit exceeded');
383+
}
384+
});
385+
386+
it('should treat different X-Forwarded-For IPs independently', async function () {
387+
// First, exhaust the rate limit for TEST_IP_B
388+
for (let i = 1; i <= 3; i++) {
389+
await makeRequestWithForwardedIP(TEST_IP_B, `b${i}`);
390+
}
391+
392+
// Verify TEST_IP_B is rate limited
393+
try {
394+
await makeRequestWithForwardedIP(TEST_IP_B, 'b4');
395+
expect.fail('Expected rate limit to be exceeded for TEST_IP_B');
396+
} catch (error: any) {
397+
expect(error.response.status).to.eq(429);
398+
expect(error.response.data.error.code).to.eq(-32605);
399+
}
400+
401+
// Now make requests with TEST_IP_C - should not be rate limited
402+
for (let i = 1; i <= 3; i++) {
403+
const response = await makeRequestWithForwardedIP(TEST_IP_C, `c${i}`);
404+
expect(response.status).to.eq(200);
405+
expect(response.data.result).to.be.equal(ConfigService.get('CHAIN_ID'));
406+
}
407+
408+
// TEST_IP_C should also get rate limited after hitting its own limit
409+
try {
410+
await makeRequestWithForwardedIP(TEST_IP_C, 'c4');
411+
expect.fail('Expected rate limit to be exceeded for TEST_IP_C');
412+
} catch (error: any) {
413+
expect(error.response.status).to.eq(429);
414+
expect(error.response.data.error.code).to.eq(-32605);
415+
}
416+
});
417+
418+
it('should use actual client IP when X-Forwarded-For header is not present', async function () {
419+
// Make requests without X-Forwarded-For header
420+
// These should use the actual client IP and have their own rate limit
421+
for (let i = 1; i <= 3; i++) {
422+
const response = await makeRequestWithoutForwardedIP(i.toString());
423+
expect(response.status).to.eq(200);
424+
expect(response.data.result).to.be.equal(ConfigService.get('CHAIN_ID'));
425+
}
426+
427+
// The next request should be rate limited for the actual client IP
428+
try {
429+
await makeRequestWithoutForwardedIP('4');
430+
expect.fail('Expected rate limit to be exceeded for actual client IP');
431+
} catch (error: any) {
432+
expect(error.response.status).to.eq(429);
433+
expect(error.response.data.error.code).to.eq(-32605);
434+
}
435+
});
436+
437+
it('should handle multiple IPs in X-Forwarded-For header (use first IP)', async function () {
438+
// X-Forwarded-For can contain multiple IPs: "client, proxy1, proxy2"
439+
// Koa should use the first IP (leftmost) as the client IP
440+
const multipleIPs = `${TEST_IP_D}, 10.0.0.1, 10.0.0.2`;
441+
442+
// Make requests with multiple IPs in the header
443+
for (let i = 1; i <= 3; i++) {
444+
const response = await testClient.post('/', createRequestWithIP(i.toString(), TEST_IP_D), {
445+
headers: {
446+
'X-Forwarded-For': multipleIPs,
447+
},
448+
});
449+
450+
expect(response.status).to.eq(200);
451+
expect(response.data.result).to.be.equal(ConfigService.get('CHAIN_ID'));
452+
}
453+
454+
// Should be rate limited based on the first IP (TEST_IP_D)
455+
try {
456+
await testClient.post('/', createRequestWithIP('4', TEST_IP_D), {
457+
headers: {
458+
'X-Forwarded-For': multipleIPs,
459+
},
460+
});
461+
expect.fail('Expected rate limit to be exceeded for first IP in X-Forwarded-For');
462+
} catch (error: any) {
463+
expect(error.response.status).to.eq(429);
464+
expect(error.response.data.error.code).to.eq(-32605);
465+
}
466+
});
467+
468+
it('should properly handle X-Forwarded-For header with different request patterns', async function () {
469+
// Make requests with X-Forwarded-For header
470+
for (let i = 1; i <= 3; i++) {
471+
const response = await testClient.post('/', createRequestWithIP(i.toString(), TEST_IP_E), {
472+
headers: {
473+
'X-Forwarded-For': TEST_IP_E,
474+
},
475+
});
476+
477+
expect(response.status).to.eq(200);
478+
expect(response.data.result).to.be.equal(ConfigService.get('CHAIN_ID'));
479+
}
480+
481+
// Next request should be rate limited
482+
try {
483+
await testClient.post('/', createRequestWithIP('4', TEST_IP_E), {
484+
headers: {
485+
'X-Forwarded-For': TEST_IP_E,
486+
},
487+
});
488+
expect.fail('Expected rate limit to be exceeded');
489+
} catch (error: any) {
490+
expect(error.response.status).to.eq(429);
491+
expect(error.response.data.error.code).to.eq(-32605);
492+
}
493+
});
494+
});
284495
});

0 commit comments

Comments
 (0)