1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | import {format as formatUrl} from 'node:url';
|
10 | import {deprecate} from 'node:util';
|
11 | import Headers from './headers.js';
|
12 | import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
13 | import {isAbortSignal} from './utils/is.js';
|
14 | import {getSearch} from './utils/get-search.js';
|
15 | import {
|
16 | validateReferrerPolicy, determineRequestsReferrer, DEFAULT_REFERRER_POLICY
|
17 | } from './utils/referrer.js';
|
18 |
|
19 | const INTERNALS = Symbol('Request internals');
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | const isRequest = object => {
|
28 | return (
|
29 | typeof object === 'object' &&
|
30 | typeof object[INTERNALS] === 'object'
|
31 | );
|
32 | };
|
33 |
|
34 | const doBadDataWarn = deprecate(() => {},
|
35 | '.data is not a valid RequestInit property, use .body instead',
|
36 | 'https://github.com/node-fetch/node-fetch/issues/1000 (request)');
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 | export default class Request extends Body {
|
48 | constructor(input, init = {}) {
|
49 | let parsedURL;
|
50 |
|
51 |
|
52 | if (isRequest(input)) {
|
53 | parsedURL = new URL(input.url);
|
54 | } else {
|
55 | parsedURL = new URL(input);
|
56 | input = {};
|
57 | }
|
58 |
|
59 | if (parsedURL.username !== '' || parsedURL.password !== '') {
|
60 | throw new TypeError(`${parsedURL} is an url with embedded credentials.`);
|
61 | }
|
62 |
|
63 | let method = init.method || input.method || 'GET';
|
64 | if (/^(delete|get|head|options|post|put)$/i.test(method)) {
|
65 | method = method.toUpperCase();
|
66 | }
|
67 |
|
68 | if (!isRequest(init) && 'data' in init) {
|
69 | doBadDataWarn();
|
70 | }
|
71 |
|
72 |
|
73 | if ((init.body != null || (isRequest(input) && input.body !== null)) &&
|
74 | (method === 'GET' || method === 'HEAD')) {
|
75 | throw new TypeError('Request with GET/HEAD method cannot have body');
|
76 | }
|
77 |
|
78 | const inputBody = init.body ?
|
79 | init.body :
|
80 | (isRequest(input) && input.body !== null ?
|
81 | clone(input) :
|
82 | null);
|
83 |
|
84 | super(inputBody, {
|
85 | size: init.size || input.size || 0
|
86 | });
|
87 |
|
88 | const headers = new Headers(init.headers || input.headers || {});
|
89 |
|
90 | if (inputBody !== null && !headers.has('Content-Type')) {
|
91 | const contentType = extractContentType(inputBody, this);
|
92 | if (contentType) {
|
93 | headers.set('Content-Type', contentType);
|
94 | }
|
95 | }
|
96 |
|
97 | let signal = isRequest(input) ?
|
98 | input.signal :
|
99 | null;
|
100 | if ('signal' in init) {
|
101 | signal = init.signal;
|
102 | }
|
103 |
|
104 |
|
105 | if (signal != null && !isAbortSignal(signal)) {
|
106 | throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget');
|
107 | }
|
108 |
|
109 |
|
110 |
|
111 | let referrer = init.referrer == null ? input.referrer : init.referrer;
|
112 | if (referrer === '') {
|
113 |
|
114 | referrer = 'no-referrer';
|
115 | } else if (referrer) {
|
116 |
|
117 | const parsedReferrer = new URL(referrer);
|
118 |
|
119 | referrer = /^about:(\/\/)?client$/.test(parsedReferrer) ? 'client' : parsedReferrer;
|
120 | } else {
|
121 | referrer = undefined;
|
122 | }
|
123 |
|
124 | this[INTERNALS] = {
|
125 | method,
|
126 | redirect: init.redirect || input.redirect || 'follow',
|
127 | headers,
|
128 | parsedURL,
|
129 | signal,
|
130 | referrer
|
131 | };
|
132 |
|
133 |
|
134 | this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow;
|
135 | this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress;
|
136 | this.counter = init.counter || input.counter || 0;
|
137 | this.agent = init.agent || input.agent;
|
138 | this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
|
139 | this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false;
|
140 |
|
141 |
|
142 |
|
143 | this.referrerPolicy = init.referrerPolicy || input.referrerPolicy || '';
|
144 | }
|
145 |
|
146 |
|
147 | get method() {
|
148 | return this[INTERNALS].method;
|
149 | }
|
150 |
|
151 |
|
152 | get url() {
|
153 | return formatUrl(this[INTERNALS].parsedURL);
|
154 | }
|
155 |
|
156 |
|
157 | get headers() {
|
158 | return this[INTERNALS].headers;
|
159 | }
|
160 |
|
161 | get redirect() {
|
162 | return this[INTERNALS].redirect;
|
163 | }
|
164 |
|
165 |
|
166 | get signal() {
|
167 | return this[INTERNALS].signal;
|
168 | }
|
169 |
|
170 |
|
171 | get referrer() {
|
172 | if (this[INTERNALS].referrer === 'no-referrer') {
|
173 | return '';
|
174 | }
|
175 |
|
176 | if (this[INTERNALS].referrer === 'client') {
|
177 | return 'about:client';
|
178 | }
|
179 |
|
180 | if (this[INTERNALS].referrer) {
|
181 | return this[INTERNALS].referrer.toString();
|
182 | }
|
183 |
|
184 | return undefined;
|
185 | }
|
186 |
|
187 | get referrerPolicy() {
|
188 | return this[INTERNALS].referrerPolicy;
|
189 | }
|
190 |
|
191 | set referrerPolicy(referrerPolicy) {
|
192 | this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
193 | }
|
194 |
|
195 | |
196 |
|
197 |
|
198 |
|
199 |
|
200 | clone() {
|
201 | return new Request(this);
|
202 | }
|
203 |
|
204 | get [Symbol.toStringTag]() {
|
205 | return 'Request';
|
206 | }
|
207 | }
|
208 |
|
209 | Object.defineProperties(Request.prototype, {
|
210 | method: {enumerable: true},
|
211 | url: {enumerable: true},
|
212 | headers: {enumerable: true},
|
213 | redirect: {enumerable: true},
|
214 | clone: {enumerable: true},
|
215 | signal: {enumerable: true},
|
216 | referrer: {enumerable: true},
|
217 | referrerPolicy: {enumerable: true}
|
218 | });
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | export const getNodeRequestOptions = request => {
|
227 | const {parsedURL} = request[INTERNALS];
|
228 | const headers = new Headers(request[INTERNALS].headers);
|
229 |
|
230 |
|
231 | if (!headers.has('Accept')) {
|
232 | headers.set('Accept', '*/*');
|
233 | }
|
234 |
|
235 |
|
236 | let contentLengthValue = null;
|
237 | if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
238 | contentLengthValue = '0';
|
239 | }
|
240 |
|
241 | if (request.body !== null) {
|
242 | const totalBytes = getTotalBytes(request);
|
243 |
|
244 | if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) {
|
245 | contentLengthValue = String(totalBytes);
|
246 | }
|
247 | }
|
248 |
|
249 | if (contentLengthValue) {
|
250 | headers.set('Content-Length', contentLengthValue);
|
251 | }
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | if (request.referrerPolicy === '') {
|
257 | request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
258 | }
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | if (request.referrer && request.referrer !== 'no-referrer') {
|
264 | request[INTERNALS].referrer = determineRequestsReferrer(request);
|
265 | } else {
|
266 | request[INTERNALS].referrer = 'no-referrer';
|
267 | }
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | if (request[INTERNALS].referrer instanceof URL) {
|
273 | headers.set('Referer', request.referrer);
|
274 | }
|
275 |
|
276 |
|
277 | if (!headers.has('User-Agent')) {
|
278 | headers.set('User-Agent', 'node-fetch');
|
279 | }
|
280 |
|
281 |
|
282 | if (request.compress && !headers.has('Accept-Encoding')) {
|
283 | headers.set('Accept-Encoding', 'gzip, deflate, br');
|
284 | }
|
285 |
|
286 | let {agent} = request;
|
287 | if (typeof agent === 'function') {
|
288 | agent = agent(parsedURL);
|
289 | }
|
290 |
|
291 | if (!headers.has('Connection') && !agent) {
|
292 | headers.set('Connection', 'close');
|
293 | }
|
294 |
|
295 |
|
296 |
|
297 |
|
298 | const search = getSearch(parsedURL);
|
299 |
|
300 |
|
301 |
|
302 | const options = {
|
303 |
|
304 | path: parsedURL.pathname + search,
|
305 |
|
306 | method: request.method,
|
307 | headers: headers[Symbol.for('nodejs.util.inspect.custom')](),
|
308 | insecureHTTPParser: request.insecureHTTPParser,
|
309 | agent
|
310 | };
|
311 |
|
312 | return {
|
313 |
|
314 | parsedURL,
|
315 | options
|
316 | };
|
317 | };
|