-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: pick dns cache commits to v4 #5841
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use strict'; | ||
|
|
||
| module.exports = (app) => { | ||
| app.get('/', async (ctx) => { | ||
| ctx.body = 'ok'; | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| 'use strict'; | ||
|
|
||
| const os = require('os'); | ||
| const path = require('path'); | ||
|
|
||
| let rpcIdCounter = 0; | ||
|
|
||
| // Use unique log directory per vitest worker to avoid Windows file locking issues | ||
| const workerId = process.env.VITEST_WORKER_ID || '0'; | ||
| const tempBase = path.join(os.tmpdir(), `egg-httpclient-test-${workerId}`); | ||
|
|
||
| exports.httpclient = { | ||
| interceptors: [ | ||
| // Tracer interceptor: injects trace headers into every request | ||
| (dispatch) => { | ||
| return (opts, handler) => { | ||
| opts.headers = opts.headers || {}; | ||
| opts.headers['x-trace-id'] = 'trace-123'; | ||
| rpcIdCounter++; | ||
| opts.headers['x-rpc-id'] = `rpc-${rpcIdCounter}`; | ||
|
Comment on lines
+6
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a module-level variable |
||
| return dispatch(opts, handler); | ||
| }; | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| exports.logger = { | ||
| dir: path.join(tempBase, 'logs', 'httpclient-interceptor'), | ||
| }; | ||
|
|
||
| exports.rundir = path.join(tempBase, 'run'); | ||
|
|
||
| exports.keys = 'test key'; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "name": "httpclient-interceptor-app" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import { strict as assert } from 'node:assert'; | ||
| import http from 'node:http'; | ||
|
|
||
| import { describe, it, beforeAll, afterAll } from 'vitest'; | ||
|
|
||
| import { createApp, type MockApplication, startNewLocalServer } from '../../utils.js'; | ||
|
|
||
| describe('test/lib/core/httpclient_interceptor.test.ts', () => { | ||
| describe('with interceptors configured', () => { | ||
| let app: MockApplication; | ||
| let url: string; | ||
| let serverInfo: { url: string; server: http.Server }; | ||
|
|
||
| beforeAll(async () => { | ||
| app = createApp('apps/httpclient-interceptor'); | ||
| await app.ready(); | ||
| serverInfo = await startNewLocalServer(); | ||
| url = serverInfo.url; | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| if (serverInfo?.server?.listening) { | ||
| serverInfo.server.close(); | ||
| } | ||
| await app.close(); | ||
| }); | ||
|
|
||
| it('should inject trace headers via interceptor', async () => { | ||
| const res = await app.curl(url + '/get_headers', { dataType: 'json' }); | ||
| assert.equal(res.status, 200); | ||
| assert.equal(res.data.headers['x-trace-id'], 'trace-123'); | ||
| // rpcId should be present | ||
| assert(res.data.headers['x-rpc-id']); | ||
| assert(res.data.headers['x-rpc-id'].startsWith('rpc-')); | ||
| }); | ||
|
|
||
| it('should increment rpcId on each request', async () => { | ||
| const res1 = await app.curl(url + '/get_headers', { dataType: 'json' }); | ||
| const rpcId1 = parseInt(res1.data.headers['x-rpc-id'].replace('rpc-', ''), 10); | ||
|
|
||
| const res2 = await app.curl(url + '/get_headers', { dataType: 'json' }); | ||
| const rpcId2 = parseInt(res2.data.headers['x-rpc-id'].replace('rpc-', ''), 10); | ||
|
|
||
| assert(rpcId2 > rpcId1, `Expected ${rpcId2} > ${rpcId1}`); | ||
| }); | ||
|
|
||
| it('should work with httpClient.request directly', async () => { | ||
| const res = await app.httpClient.request(url + '/get_headers', { dataType: 'json' }); | ||
| assert.equal(res.status, 200); | ||
| assert.equal(res.data.headers['x-trace-id'], 'trace-123'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('without interceptors configured', () => { | ||
| let app: MockApplication; | ||
| let url: string; | ||
| let serverInfo: { url: string; server: http.Server }; | ||
|
|
||
| beforeAll(async () => { | ||
| app = createApp('apps/dnscache_httpclient'); | ||
| await app.ready(); | ||
| serverInfo = await startNewLocalServer(); | ||
| url = serverInfo.url; | ||
| }); | ||
|
|
||
| afterAll(async () => { | ||
| if (serverInfo?.server?.listening) { | ||
| serverInfo.server.close(); | ||
| } | ||
| await app.close(); | ||
| }); | ||
|
|
||
| it('should work normally without interceptors', async () => { | ||
| const res = await app.curl(url + '/get_headers', { dataType: 'json' }); | ||
| assert.equal(res.status, 200); | ||
| // No trace headers injected | ||
| assert.equal(res.data.headers['x-trace-id'], undefined); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
urllib npm package Dispatcher.DispatcherComposeInterceptor type💡 Result:
No such type "Dispatcher.DispatcherComposeInterceptor" exists in the urllib npm package or its underlying undici dependency. Urllib (v4.9.0, latest as of 2025-12-13) is a high-level HTTP client built on undici that exports undici's Dispatcher types like Agent, ProxyAgent, MockAgent, but does not define custom types like DispatcherComposeInterceptor. Undici (v7.1.1 dependency) has Dispatcher.compose method which accepts interceptors of type DispatcherComposeInterceptor (in newer versions) or DispatchInterceptor (older deprecated versions). This type is: type DispatcherComposeInterceptor = (dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch']; It takes the base dispatch function and returns a new dispatch-like function for chaining interceptors (e.g., retry, redirect). Example usage from undici docs: const { Agent, interceptors } = require('undici'); const agent = new Agent.compose( interceptors.retry, interceptors.redirect ); Urllib usage example with custom dispatcher: import { ProxyAgent, request } from 'urllib'; const proxyAgent = new ProxyAgent('http://proxy:8080'); const response = await request('https://example.com', { dispatcher: proxyAgent }); To use compose interceptors in urllib, pass a composed undici Dispatcher via options.dispatcher.
Citations:
🏁 Script executed:
cat -n packages/egg/src/lib/types.ts | head -100Repository: eggjs/egg
Length of output: 4100
🏁 Script executed:
rg "Dispatcher" packages/egg/src/lib/types.ts -B 5 -A 5Repository: eggjs/egg
Length of output: 1194
🏁 Script executed:
rg "import.*Dispatcher" packages/egg/src/lib/ -t tsRepository: eggjs/egg
Length of output: 124
🏁 Script executed:
Repository: eggjs/egg
Length of output: 1058
🏁 Script executed:
rg "DispatcherComposeInterceptor" packages/ -t tsRepository: eggjs/egg
Length of output: 142
🏁 Script executed:
Repository: eggjs/egg
Length of output: 88
🏁 Script executed:
Repository: eggjs/egg
Length of output: 514
🏁 Script executed:
Repository: eggjs/egg
Length of output: 5462
🏁 Script executed:
Repository: eggjs/egg
Length of output: 859
🏁 Script executed:
Repository: eggjs/egg
Length of output: 57
Type reference
Dispatcher.DispatcherComposeInterceptordoes not exist in urllib.The type
Dispatcher.DispatcherComposeInterceptoris not exported from urllib v4.8.2 or its underlying undici dependency. The actual type from undici isDispatcherComposeInterceptor(without theDispatchernamespace), defined as(dispatch: Dispatcher['dispatch']) => Dispatcher['dispatch']. This code will fail TypeScript compilation. The type annotation should be corrected to reference the proper type from undici, or importDispatcherComposeInterceptordirectly rather than accessing it as a namespace member.🤖 Prompt for AI Agents