1 |
|
2 |
|
3 |
|
4 | import { HttpOperationResponse } from "../httpOperationResponse";
|
5 | import { URLBuilder } from "../url";
|
6 | import { WebResourceLike } from "../webResource";
|
7 | import {
|
8 | BaseRequestPolicy,
|
9 | RequestPolicy,
|
10 | RequestPolicyFactory,
|
11 | RequestPolicyOptionsLike,
|
12 | } from "./requestPolicy";
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | export interface RedirectOptions {
|
18 | |
19 |
|
20 |
|
21 | handleRedirects: boolean;
|
22 |
|
23 | |
24 |
|
25 |
|
26 |
|
27 | maxRetries?: number;
|
28 | }
|
29 |
|
30 | export const DefaultRedirectOptions: RedirectOptions = {
|
31 | handleRedirects: true,
|
32 | maxRetries: 20,
|
33 | };
|
34 |
|
35 | export function redirectPolicy(maximumRetries = 20): RequestPolicyFactory {
|
36 | return {
|
37 | create: (nextPolicy: RequestPolicy, options: RequestPolicyOptionsLike) => {
|
38 | return new RedirectPolicy(nextPolicy, options, maximumRetries);
|
39 | },
|
40 | };
|
41 | }
|
42 |
|
43 | export class RedirectPolicy extends BaseRequestPolicy {
|
44 | constructor(
|
45 | nextPolicy: RequestPolicy,
|
46 | options: RequestPolicyOptionsLike,
|
47 | readonly maxRetries = 20
|
48 | ) {
|
49 | super(nextPolicy, options);
|
50 | }
|
51 |
|
52 | public sendRequest(request: WebResourceLike): Promise<HttpOperationResponse> {
|
53 | return this._nextPolicy
|
54 | .sendRequest(request)
|
55 | .then((response) => handleRedirect(this, response, 0));
|
56 | }
|
57 | }
|
58 |
|
59 | function handleRedirect(
|
60 | policy: RedirectPolicy,
|
61 | response: HttpOperationResponse,
|
62 | currentRetries: number
|
63 | ): Promise<HttpOperationResponse> {
|
64 | const { request, status } = response;
|
65 | const locationHeader = response.headers.get("location");
|
66 | if (
|
67 | locationHeader &&
|
68 | (status === 300 ||
|
69 | (status === 301 && ["GET", "HEAD"].includes(request.method)) ||
|
70 | (status === 302 && ["GET", "POST", "HEAD"].includes(request.method)) ||
|
71 | (status === 303 && "POST" === request.method) ||
|
72 | status === 307) &&
|
73 | ((request.redirectLimit !== undefined && currentRetries < request.redirectLimit) ||
|
74 | (request.redirectLimit === undefined && currentRetries < policy.maxRetries))
|
75 | ) {
|
76 | const builder = URLBuilder.parse(request.url);
|
77 | builder.setPath(locationHeader);
|
78 | request.url = builder.toString();
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | if ((status === 302 || status === 303) && request.method === "POST") {
|
84 | request.method = "GET";
|
85 | delete request.body;
|
86 | }
|
87 |
|
88 | return policy._nextPolicy
|
89 | .sendRequest(request)
|
90 | .then((res) => handleRedirect(policy, res, currentRetries + 1))
|
91 | .then((res) => recordRedirect(res, request.url));
|
92 | }
|
93 |
|
94 | return Promise.resolve(response);
|
95 | }
|
96 |
|
97 | function recordRedirect(response: HttpOperationResponse, redirect: string): HttpOperationResponse {
|
98 |
|
99 |
|
100 | if (!response.redirected) {
|
101 | response.redirected = true;
|
102 | response.url = redirect;
|
103 | }
|
104 | return response;
|
105 | }
|