Skip to content

Commit 7316bcb

Browse files
committed
fix(ts-sdk): use shared https.Agent for connection pooling
- Add module-level sharedHttpsAgent with keepAlive enabled - Prevents creating new https.Agent on every HTTP request - Enables TLS session reuse and connection pooling - Follows Python SDK pattern (_SHARED_SSL_CONTEXT) Fixes PR alibaba#492 reviewer feedback about performance degradation under load.
1 parent ea7f386 commit 7316bcb

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

rock/ts-sdk/src/utils/http.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
*/
44

55
import axios from 'axios';
6-
import { HttpUtils, HttpResponse } from './http.js';
6+
import https from 'https';
7+
import { HttpUtils, HttpResponse, sharedHttpsAgent } from './http.js';
78

89
// Mock axios
910
jest.mock('axios');
@@ -286,4 +287,55 @@ describe('HttpUtils case conversion', () => {
286287
expect(entries[0]?.[0]).toBe('file');
287288
});
288289
});
290+
});
291+
292+
describe('HttpUtils shared https.Agent', () => {
293+
test('exports a shared https.Agent for connection pooling', () => {
294+
// Following Python SDK pattern: _SHARED_SSL_CONTEXT
295+
// The agent should be created once at module level
296+
expect(sharedHttpsAgent).toBeDefined();
297+
expect(sharedHttpsAgent).toBeInstanceOf(https.Agent);
298+
});
299+
300+
test('shared agent has keepAlive enabled for connection reuse', () => {
301+
// keepAlive enables TCP keep-alive sockets for better performance
302+
// Access through options property as per Node.js Agent implementation
303+
expect((sharedHttpsAgent as unknown as { keepAlive: boolean }).keepAlive).toBe(true);
304+
});
305+
306+
test('shared agent has rejectUnauthorized enabled for security', () => {
307+
expect((sharedHttpsAgent.options as { rejectUnauthorized?: boolean }).rejectUnauthorized).toBe(true);
308+
});
309+
310+
test('multiple requests use the same agent instance', async () => {
311+
const mockResponse = {
312+
data: { status: 'Success', result: { sandbox_id: '123' } },
313+
headers: {},
314+
};
315+
const mockPost = jest.fn().mockResolvedValue(mockResponse);
316+
const mockGet = jest.fn().mockResolvedValue(mockResponse);
317+
318+
const capturedAgents: https.Agent[] = [];
319+
mockedAxios.create = jest.fn().mockImplementation((config) => {
320+
if (config?.httpsAgent) {
321+
capturedAgents.push(config.httpsAgent);
322+
}
323+
return { post: mockPost, get: mockGet };
324+
});
325+
326+
// Make multiple requests
327+
await HttpUtils.post('http://test/api1', {}, {});
328+
await HttpUtils.post('http://test/api2', {}, {});
329+
await HttpUtils.get('http://test/api3', {});
330+
331+
// All requests should use the same agent instance (same reference)
332+
expect(capturedAgents.length).toBe(3);
333+
expect(capturedAgents[0]).toBe(sharedHttpsAgent);
334+
expect(capturedAgents[1]).toBe(sharedHttpsAgent);
335+
expect(capturedAgents[2]).toBe(sharedHttpsAgent);
336+
337+
// All captured agents should be the exact same object reference
338+
expect(capturedAgents[0]).toBe(capturedAgents[1]);
339+
expect(capturedAgents[1]).toBe(capturedAgents[2]);
340+
});
289341
});

rock/ts-sdk/src/utils/http.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ import https from 'https';
77
import { PID_PREFIX, PID_SUFFIX } from '../common/constants.js';
88
import { objectToCamel, objectToSnake } from './case.js';
99

10+
/**
11+
* Shared HTTPS agent for connection pooling and TLS session reuse.
12+
* Following Python SDK pattern: _SHARED_SSL_CONTEXT
13+
*
14+
* This prevents performance degradation under load by:
15+
* - Enabling TLS session reuse
16+
* - Enabling connection pooling via keepAlive
17+
*/
18+
export const sharedHttpsAgent = new https.Agent({
19+
rejectUnauthorized: true,
20+
keepAlive: true,
21+
});
22+
1023
/**
1124
* HTTP client configuration
1225
*/
@@ -43,9 +56,7 @@ export class HttpUtils {
4356
'Content-Type': 'application/json',
4457
...config?.headers,
4558
},
46-
httpsAgent: new https.Agent({
47-
rejectUnauthorized: true,
48-
}),
59+
httpsAgent: sharedHttpsAgent,
4960
});
5061
}
5162

0 commit comments

Comments
 (0)