-
Notifications
You must be signed in to change notification settings - Fork 539
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
feat: preparsed Headers #3582
Comments
HeadersList should not be exposed. |
WIP #3408 |
Alternatively, make a wrapper over fetch/request, but this does not solve the problem with the fact that Buffer[] -> Headers are parsed for each request throughout undici and all interceptors in order to already work with them. I've been doing a little bit here, it may be appropriate to make an implementation of the interceptors, where everything will already be given in the right form It is simply impossible to write interceptors on the current implementation of undici, since there may be completely different data coming to opts If there was something like this in undici that was compatible with fetch/request, it would be very convenient. import undici, { Dispatcher } from 'undici';
type RequestInterceptor = (
request: Dispatcher.DispatchOptions,
next: (
request?: Dispatcher.DispatchOptions
) => Promise<Dispatcher.ResponseData>
) => Promise<Dispatcher.ResponseData>;
function LoggerInterceptor(prefix: string): RequestInterceptor {
return async (request, next) => {
console.log(`[${prefix}] on request:`, request.method);
const resp = await next();
console.log(`[${prefix}] on response:`, resp.statusCode);
return resp;
};
}
function DecompressInterceptor(): RequestInterceptor {
return async (request, next) => {
const resp = await next();
const { headers } = resp;
if (resp.body && headers && headers['content-encoding']) {
// remove headers
delete headers['content-encoding'];
delete headers['content-length'];
resp.body = decompress(resp.body);
}
return resp;
};
} import { Request, fetch } from 'undici';
type RequestInterceptor = (
request: Request,
next: () => Promise<Response>
) => Promise<Response>;
function LoggerInterceptor(): RequestInterceptor {
return async (request, next) => {
console.log('Request:', request);
const resp = await next();
console.log('Response:', resp);
return resp;
};
}
function DecompressInterceptor(): RequestInterceptor {
return async (request, next) => {
console.log('Request:', request);
const resp = await next();
if (resp.body && resp.headers.has('content-encoding')) {
const encodings = resp.headers
.get('content-encoding')!
.split(',')
.map(v => v.trim())
.reverse();
for (const encoding of encodings) {
// @ts-expect-error:
resp.body = resp.body.pipeThrough(new DecompressionStream(encoding));
}
}
return resp;
};
} |
import { scheduler } from 'node:timers/promises';
import undici, { Dispatcher, getGlobalDispatcher } from 'undici';
type RequestInterceptor = (
request: Dispatcher.DispatchOptions,
next: () => Promise<Dispatcher.ResponseData>
) => Promise<Dispatcher.ResponseData>;
function composeInterceptors(interceptors: RequestInterceptor[] = []) {
// Logic for applying interceptors
return async (
request: Dispatcher.DispatchOptions,
next: () => Promise<Dispatcher.ResponseData>
) => {
let index = -1;
const runner = () => {
index += 1;
if (index < interceptors.length) {
// Call the current interceptor and pass the runner as `next`
return interceptors[index](request, runner);
} else {
// No more interceptors, call the original fetch
return next();
}
};
return runner();
};
}
function LoggerInterceptor(prefix: string): RequestInterceptor {
return async (request, next) => {
console.log(`[${prefix}] on request:`, request.method);
const resp = await next();
console.log(
`[${prefix}] on response:`,
resp.statusCode,
`body:`,
!!resp.body
);
return resp;
};
}
function AsyncInterceptor(): RequestInterceptor {
return async (request, next) => {
console.log('wait 1sec');
await scheduler.wait(1000);
console.log('wait response');
const resp = await next();
console.log('wait 1sec');
await scheduler.wait(1000);
console.log('modify response to null');
// @ts-expect-error:
resp.body = null;
console.log('retun resp');
return resp;
};
}
type RequestOptions = Partial<Dispatcher.RequestOptions> & {
dispatcher?: Dispatcher;
} & Partial<Pick<Dispatcher.RequestOptions, 'method' | 'path' | 'origin'>>;
function requestWithInterceptors(interceptors: RequestInterceptor[] = []) {
const intercept = composeInterceptors([...interceptors]);
return async (
url: string | URL,
options: RequestOptions = {}
): Promise<Dispatcher.ResponseData> => {
var waiter = Promise.withResolvers<Dispatcher.ResponseData>();
var composePromise: Promise<Dispatcher.ResponseData>;
options.dispatcher ??= getGlobalDispatcher();
options.dispatcher = options.dispatcher.compose(
dispatch => (opts, handler) => {
composePromise = intercept(opts, () => waiter.promise);
return dispatch(opts, handler);
}
);
undici.request(url, options).then(
resp => waiter.resolve(resp),
reason => waiter.reject(reason)
);
// @ts-expect-error:
return composePromise;
};
}
const request = requestWithInterceptors([
LoggerInterceptor('1'),
AsyncInterceptor(),
LoggerInterceptor('2'),
]);
const response = await request('https://api.ipify.org/');
console.log(response); |
Let's rewrite the manipulation of request/response headers already?
In undici, [string, string][] for headers are passed almost everywhere, and almost everywhere it is checked and reduced to the same type
Maybe it's worth writing a Headers List and using it everywhere inside the library?
In client-1 client-2, you can immediately set the headersList: new Headers List() and then use this structure everywhere
This structure can be used for all Compose Interceptors and for Handlers
In the current version, only fetch is converted from any type to Record<string, string>, and request just passes this raw data on
My motivation is that I need to make sure that the interceptors and handlers always have the data structure we need and then only transfer it, and not parse Buffer[] in each interceptor or handler, make a modification and throw it further.
You can make a separate interceptor that would lead from any type of headers to the Headers List, and would do the same with onHeaders. But do we even need raw headers in the form of Buffer[]?
The text was updated successfully, but these errors were encountered: