Skip to content

Commit 64d275b

Browse files
committed
Remove Undici Dependency
1 parent 70bcdf4 commit 64d275b

File tree

9 files changed

+51
-72
lines changed

9 files changed

+51
-72
lines changed

package-lock.json

Lines changed: 1 addition & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@
4343
"dependencies": {
4444
"@azure/functions-extensions-base": "0.2.0",
4545
"cookie": "^0.7.0",
46-
"long": "^4.0.0",
47-
"undici": "^5.29.0"
46+
"long": "^4.0.0"
4847
},
4948
"devDependencies": {
5049
"@types/chai": "^4.2.22",

src/http/HttpRequest.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Blob } from 'buffer';
88
import { IncomingMessage } from 'http';
99
import * as stream from 'stream';
1010
import { ReadableStream } from 'stream/web';
11-
import { FormData, Headers, HeadersInit, Request as uRequest } from 'undici';
1211
import { URLSearchParams } from 'url';
1312
import { fromNullableMapping } from '../converters/fromRpcNullable';
1413
import { fromRpcTypedData } from '../converters/fromRpcTypedData';
@@ -17,22 +16,22 @@ import { isDefined, nonNullProp } from '../utils/nonNull';
1716
import { extractHttpUserFromHeaders } from './extractHttpUserFromHeaders';
1817

1918
interface InternalHttpRequestInit extends RpcHttpData {
20-
undiciRequest?: uRequest;
19+
nativeRequest?: Request;
2120
}
2221

2322
export class HttpRequest implements types.HttpRequest {
2423
readonly query: URLSearchParams;
2524
readonly params: HttpRequestParams;
2625

2726
#cachedUser?: HttpRequestUser | null;
28-
#uReq: uRequest;
27+
#nativeReq: Request;
2928
#init: InternalHttpRequestInit;
3029

3130
constructor(init: InternalHttpRequestInit) {
3231
this.#init = init;
3332

34-
let uReq = init.undiciRequest;
35-
if (!uReq) {
33+
let nativeReq = init.nativeRequest;
34+
if (!nativeReq) {
3635
const url = nonNullProp(init, 'url');
3736

3837
let body: Buffer | string | undefined;
@@ -42,33 +41,33 @@ export class HttpRequest implements types.HttpRequest {
4241
body = init.body.string;
4342
}
4443

45-
uReq = new uRequest(url, {
44+
nativeReq = new Request(url, {
4645
body,
4746
method: nonNullProp(init, 'method'),
4847
headers: fromNullableMapping(init.nullableHeaders, init.headers),
4948
});
5049
}
51-
this.#uReq = uReq;
50+
this.#nativeReq = nativeReq;
5251

5352
if (init.nullableQuery || init.query) {
5453
this.query = new URLSearchParams(fromNullableMapping(init.nullableQuery, init.query));
5554
} else {
56-
this.query = new URL(this.#uReq.url).searchParams;
55+
this.query = new URL(this.#nativeReq.url).searchParams;
5756
}
5857

5958
this.params = fromNullableMapping(init.nullableParams, init.params);
6059
}
6160

6261
get url(): string {
63-
return this.#uReq.url;
62+
return this.#nativeReq.url;
6463
}
6564

6665
get method(): string {
67-
return this.#uReq.method;
66+
return this.#nativeReq.method;
6867
}
6968

7069
get headers(): Headers {
71-
return this.#uReq.headers;
70+
return this.#nativeReq.headers;
7271
}
7372

7473
get user(): HttpRequestUser | null {
@@ -80,36 +79,36 @@ export class HttpRequest implements types.HttpRequest {
8079
}
8180

8281
get body(): ReadableStream<any> | null {
83-
return this.#uReq.body;
82+
return this.#nativeReq.body as ReadableStream<any> | null;
8483
}
8584

8685
get bodyUsed(): boolean {
87-
return this.#uReq.bodyUsed;
86+
return this.#nativeReq.bodyUsed;
8887
}
8988

9089
async arrayBuffer(): Promise<ArrayBuffer> {
91-
return this.#uReq.arrayBuffer();
90+
return this.#nativeReq.arrayBuffer();
9291
}
9392

9493
async blob(): Promise<Blob> {
95-
return this.#uReq.blob();
94+
return this.#nativeReq.blob() as Promise<Blob>;
9695
}
9796

9897
async formData(): Promise<FormData> {
99-
return this.#uReq.formData();
98+
return this.#nativeReq.formData();
10099
}
101100

102101
async json(): Promise<unknown> {
103-
return this.#uReq.json();
102+
return this.#nativeReq.json();
104103
}
105104

106105
async text(): Promise<string> {
107-
return this.#uReq.text();
106+
return this.#nativeReq.text();
108107
}
109108

110109
clone(): HttpRequest {
111110
const newInit = structuredClone(this.#init);
112-
newInit.undiciRequest = this.#uReq.clone();
111+
newInit.nativeRequest = this.#nativeReq.clone();
113112
return new HttpRequest(newInit);
114113
}
115114
}
@@ -144,8 +143,10 @@ export function createStreamRequest(
144143
headers = <HeadersInit>headersData;
145144
}
146145

147-
const uReq = new uRequest(url, {
148-
body,
146+
const nativeReq = new Request(url, {
147+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
148+
body: body as any,
149+
// @ts-expect-error duplex is needed for streaming but not in all TypeScript versions
149150
duplex: 'half',
150151
method: nonNullProp(proxyReq, 'method'),
151152
headers,
@@ -159,7 +160,7 @@ export function createStreamRequest(
159160
}
160161

161162
return new HttpRequest({
162-
undiciRequest: uReq,
163+
nativeRequest: nativeReq,
163164
params,
164165
});
165166
}

src/http/HttpResponse.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,37 @@ import * as types from '@azure/functions';
55
import { HttpResponseInit } from '@azure/functions';
66
import { Blob } from 'buffer';
77
import { ReadableStream } from 'stream/web';
8-
import { FormData, Headers, Response as uResponse, ResponseInit as uResponseInit } from 'undici';
98
import { isDefined } from '../utils/nonNull';
109

1110
interface InternalHttpResponseInit extends HttpResponseInit {
12-
undiciResponse?: uResponse;
11+
nativeResponse?: Response;
1312
}
1413

1514
export class HttpResponse implements types.HttpResponse {
1615
readonly cookies: types.Cookie[];
1716
readonly enableContentNegotiation: boolean;
1817

19-
#uRes: uResponse;
18+
#nativeRes: Response;
2019
#init: InternalHttpResponseInit;
2120

2221
constructor(init?: InternalHttpResponseInit) {
2322
init ??= {};
2423
this.#init = init;
2524

26-
if (init.undiciResponse) {
27-
this.#uRes = init.undiciResponse;
25+
if (init.nativeResponse) {
26+
this.#nativeRes = init.nativeResponse;
2827
} else {
29-
const uResInit: uResponseInit = { status: init.status, headers: init.headers };
28+
const resInit: ResponseInit = { status: init.status, headers: init.headers };
3029
if (isDefined(init.jsonBody)) {
31-
this.#uRes = uResponse.json(init.jsonBody, uResInit);
30+
// Response.json is not available in all versions, so we create it manually
31+
const jsonBody = JSON.stringify(init.jsonBody);
32+
const jsonHeaders = new Headers(resInit.headers);
33+
if (!jsonHeaders.has('content-type')) {
34+
jsonHeaders.set('content-type', 'application/json');
35+
}
36+
this.#nativeRes = new Response(jsonBody, { ...resInit, headers: jsonHeaders });
3237
} else {
33-
this.#uRes = new uResponse(init.body, uResInit);
38+
this.#nativeRes = new Response(init.body, resInit);
3439
}
3540
}
3641

@@ -39,44 +44,44 @@ export class HttpResponse implements types.HttpResponse {
3944
}
4045

4146
get status(): number {
42-
return this.#uRes.status;
47+
return this.#nativeRes.status;
4348
}
4449

4550
get headers(): Headers {
46-
return this.#uRes.headers;
51+
return this.#nativeRes.headers;
4752
}
4853

4954
get body(): ReadableStream<any> | null {
50-
return this.#uRes.body;
55+
return this.#nativeRes.body as ReadableStream<any> | null;
5156
}
5257

5358
get bodyUsed(): boolean {
54-
return this.#uRes.bodyUsed;
59+
return this.#nativeRes.bodyUsed;
5560
}
5661

5762
async arrayBuffer(): Promise<ArrayBuffer> {
58-
return this.#uRes.arrayBuffer();
63+
return this.#nativeRes.arrayBuffer();
5964
}
6065

6166
async blob(): Promise<Blob> {
62-
return this.#uRes.blob();
67+
return this.#nativeRes.blob() as Promise<Blob>;
6368
}
6469

6570
async formData(): Promise<FormData> {
66-
return this.#uRes.formData();
71+
return this.#nativeRes.formData();
6772
}
6873

6974
async json(): Promise<unknown> {
70-
return this.#uRes.json();
75+
return this.#nativeRes.json();
7176
}
7277

7378
async text(): Promise<string> {
74-
return this.#uRes.text();
79+
return this.#nativeRes.text();
7580
}
7681

7782
clone(): HttpResponse {
7883
const newInit = structuredClone(this.#init);
79-
newInit.undiciResponse = this.#uRes.clone();
84+
newInit.nativeResponse = this.#nativeRes.clone();
8085
return new HttpResponse(newInit);
8186
}
8287
}

src/http/extractHttpUserFromHeaders.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License.
33

44
import { HttpRequestUser } from '@azure/functions';
5-
import { Headers } from 'undici';
65
import { nonNullValue } from '../utils/nonNull';
76

87
/* grandfathered in. Should fix when possible */

test/converters/toRpcHttp.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'mocha';
55
import * as chai from 'chai';
66
import { expect } from 'chai';
77
import * as chaiAsPromised from 'chai-as-promised';
8-
import { Headers } from 'undici';
98
import { toRpcHttp } from '../../src/converters/toRpcHttp';
109
import { HttpResponse } from '../../src/http/HttpResponse';
1110

test/http/HttpRequest.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'mocha';
55
import * as chai from 'chai';
66
import { expect } from 'chai';
77
import * as chaiAsPromised from 'chai-as-promised';
8-
import { File } from 'undici';
98
import { HttpRequest } from '../../src/http/HttpRequest';
109

1110
chai.use(chaiAsPromised);
@@ -94,7 +93,7 @@ world
9493

9594
const parsedForm = await req.formData();
9695
expect(parsedForm.has('myfile')).to.equal(true);
97-
const file = <File>parsedForm.get('myfile');
96+
const file = parsedForm.get('myfile') as File;
9897
expect(file.name).to.equal('test.txt');
9998
expect(file.type).to.equal('text/plain');
10099
expect(await file.text()).to.equal(`hello\r\nworld`);
@@ -135,7 +134,8 @@ value2
135134
const contentTypes = ['application/octet-stream', 'application/json', 'text/plain', 'invalid'];
136135
for (const contentType of contentTypes) {
137136
const req = createFormRequest('', contentType);
138-
await expect(req.formData()).to.eventually.be.rejectedWith(/Could not parse content as FormData/i);
137+
// Native fetch API has different error message than undici
138+
await expect(req.formData()).to.eventually.be.rejectedWith(/Content-Type.*not.*one of|Could not parse content as FormData/i);
139139
}
140140
});
141141
});

test/http/extractHttpUserFromHeaders.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import 'mocha';
55
import { HttpRequestUser } from '@azure/functions';
66
import { expect } from 'chai';
7-
import { Headers } from 'undici';
87
import { extractHttpUserFromHeaders } from '../../src/http/extractHttpUserFromHeaders';
98

109
describe('Extract Http User Claims Principal from Headers', () => {

types/http.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import { Blob } from 'buffer';
55
import { ReadableStream } from 'stream/web';
6-
import { BodyInit, FormData, Headers, HeadersInit } from 'undici';
76
import { URLSearchParams } from 'url';
87
import { FunctionOptions, FunctionOutput, FunctionResult, FunctionTrigger } from './index';
98
import { InvocationContext } from './InvocationContext';

0 commit comments

Comments
 (0)