|
5 | 5 | * a request API compatible with window.fetch
|
6 | 6 | */
|
7 | 7 |
|
8 |
| -import Body, { writeToStream } from './body'; |
| 8 | +import Body, { writeToStream, getTotalBytes } from './body'; |
9 | 9 | import Response from './response';
|
10 | 10 | import Headers, { createHeadersLenient } from './headers';
|
11 | 11 | import Request, { getNodeRequestOptions } from './request';
|
@@ -67,49 +67,67 @@ export default function fetch(url, opts) {
|
67 | 67 | req.on('response', res => {
|
68 | 68 | clearTimeout(reqTimeout);
|
69 | 69 |
|
70 |
| - // handle redirect |
71 |
| - if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') { |
72 |
| - if (request.redirect === 'error') { |
73 |
| - reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); |
74 |
| - return; |
75 |
| - } |
76 |
| - |
77 |
| - if (request.counter >= request.follow) { |
78 |
| - reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); |
79 |
| - return; |
80 |
| - } |
81 |
| - |
82 |
| - if (!res.headers.location) { |
83 |
| - reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect')); |
84 |
| - return; |
85 |
| - } |
| 70 | + const headers = createHeadersLenient(res.headers); |
86 | 71 |
|
87 |
| - // Create a new Request object. |
88 |
| - const requestOpts = { |
89 |
| - headers: new Headers(request.headers), |
90 |
| - follow: request.follow, |
91 |
| - counter: request.counter + 1, |
92 |
| - agent: request.agent, |
93 |
| - compress: request.compress, |
94 |
| - method: request.method |
95 |
| - }; |
96 |
| - |
97 |
| - // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect |
98 |
| - if (res.statusCode === 303 |
99 |
| - || ((res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST')) |
100 |
| - { |
101 |
| - requestOpts.method = 'GET'; |
102 |
| - requestOpts.headers.delete('content-length'); |
| 72 | + // HTTP fetch step 5 |
| 73 | + if (fetch.isRedirect(res.statusCode)) { |
| 74 | + // HTTP fetch step 5.2 |
| 75 | + const location = headers.get('Location'); |
| 76 | + |
| 77 | + // HTTP fetch step 5.3 |
| 78 | + const locationURL = location === null ? null : resolve_url(request.url, location); |
| 79 | + |
| 80 | + // HTTP fetch step 5.5 |
| 81 | + switch (request.redirect) { |
| 82 | + case 'error': |
| 83 | + reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect')); |
| 84 | + return; |
| 85 | + case 'manual': |
| 86 | + // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. |
| 87 | + if (locationURL !== null) { |
| 88 | + headers.set('Location', locationURL); |
| 89 | + } |
| 90 | + break; |
| 91 | + case 'follow': |
| 92 | + // HTTP-redirect fetch step 2 |
| 93 | + if (locationURL === null) { |
| 94 | + break; |
| 95 | + } |
| 96 | + |
| 97 | + // HTTP-redirect fetch step 5 |
| 98 | + if (request.counter >= request.follow) { |
| 99 | + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); |
| 100 | + return; |
| 101 | + } |
| 102 | + |
| 103 | + // HTTP-redirect fetch step 6 (counter increment) |
| 104 | + // Create a new Request object. |
| 105 | + const requestOpts = { |
| 106 | + headers: new Headers(request.headers), |
| 107 | + follow: request.follow, |
| 108 | + counter: request.counter + 1, |
| 109 | + agent: request.agent, |
| 110 | + compress: request.compress, |
| 111 | + method: request.method, |
| 112 | + body: request.body |
| 113 | + }; |
| 114 | + |
| 115 | + // HTTP-redirect fetch step 9 |
| 116 | + if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { |
| 117 | + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')) |
| 118 | + } |
| 119 | + |
| 120 | + // HTTP-redirect fetch step 11 |
| 121 | + if (res.statusCode === 303 || ((res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST')) { |
| 122 | + requestOpts.method = 'GET'; |
| 123 | + requestOpts.body = undefined; |
| 124 | + requestOpts.headers.delete('content-length'); |
| 125 | + } |
| 126 | + |
| 127 | + // HTTP-redirect fetch step 15 |
| 128 | + resolve(fetch(new Request(locationURL, requestOpts))); |
| 129 | + return; |
103 | 130 | }
|
104 |
| - |
105 |
| - resolve(fetch(new Request(resolve_url(request.url, res.headers.location), requestOpts))); |
106 |
| - return; |
107 |
| - } |
108 |
| - |
109 |
| - const headers = createHeadersLenient(res.headers); |
110 |
| - // normalize location header for manual redirect mode |
111 |
| - if (request.redirect === 'manual' && headers.has('Location')) { |
112 |
| - headers.set('Location', resolve_url(request.url, headers.get('Location'))); |
113 | 131 | }
|
114 | 132 |
|
115 | 133 | // prepare response
|
|
0 commit comments