/** * ------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. * See License in the project root for license information. * ------------------------------------------------------------------------------------------- */ /** * @module RedirectHandler */ import { HttpMethod } from "@microsoft/kiota-abstractions"; import { trace } from "@opentelemetry/api"; import { getObservabilityOptionsFromRequest } from "../observabilityOptions.js"; import { RedirectHandlerOptionKey, RedirectHandlerOptions } from "./options/redirectHandlerOptions.js"; /** * * Class * Middleware * Class representing RedirectHandler */ export class RedirectHandler { /** * * * To create an instance of RedirectHandler * @param [options] - The redirect handler options instance * @returns An instance of RedirectHandler */ constructor(options = new RedirectHandlerOptions()) { this.options = options; if (!options) { throw new Error("The options parameter is required."); } } /** * * To check whether the response has the redirect status code or not * @param response - The response object * @returns A boolean representing whether the response contains the redirect status code or not */ isRedirect(response) { return RedirectHandler.REDIRECT_STATUS_CODES.has(response.status); } /** * * To check whether the response has location header or not * @param response - The response object * @returns A boolean representing the whether the response has location header or not */ hasLocationHeader(response) { return response.headers.has(RedirectHandler.LOCATION_HEADER); } /** * * To get the redirect url from location header in response object * @param response - The response object * @returns A redirect url from location header */ getLocationHeader(response) { return response.headers.get(RedirectHandler.LOCATION_HEADER); } /** * * To check whether the given url is a relative url or not * @param url - The url string value * @returns A boolean representing whether the given url is a relative url or not */ isRelativeURL(url) { return !url.includes("://"); } /** * * To check whether the authorization header in the request should be dropped for consequent redirected requests * @param requestUrl - The request url value * @param redirectUrl - The redirect url value * @returns A boolean representing whether the authorization header in the request should be dropped for consequent redirected requests */ shouldDropAuthorizationHeader(requestUrl, redirectUrl) { const schemeHostRegex = /^[A-Za-z].+?:\/\/.+?(?=\/|$)/; const requestMatches = schemeHostRegex.exec(requestUrl); let requestAuthority; let redirectAuthority; if (requestMatches !== null) { requestAuthority = requestMatches[0]; } const redirectMatches = schemeHostRegex.exec(redirectUrl); if (redirectMatches !== null) { redirectAuthority = redirectMatches[0]; } return typeof requestAuthority !== "undefined" && typeof redirectAuthority !== "undefined" && requestAuthority !== redirectAuthority; } /** * To execute the next middleware and to handle in case of redirect response returned by the server * @param url - The url string value * @param fetchRequestInit - The Fetch RequestInit object * @param redirectCount - The redirect count value * @param currentOptions - The redirect handler options instance * @param requestOptions - The request options * @param tracerName - The name to use for the tracer * @returns A promise that resolves to nothing */ async executeWithRedirect(url, fetchRequestInit, redirectCount, currentOptions, requestOptions, tracerName) { var _a; const response = await ((_a = this.next) === null || _a === void 0 ? void 0 : _a.execute(url, fetchRequestInit, requestOptions)); if (!response) { throw new Error("Response is undefined"); } if (redirectCount < currentOptions.maxRedirects && this.isRedirect(response) && this.hasLocationHeader(response) && currentOptions.shouldRedirect(response)) { ++redirectCount; if (response.status === RedirectHandler.STATUS_CODE_SEE_OTHER) { fetchRequestInit.method = HttpMethod.GET; delete fetchRequestInit.body; } else { const redirectUrl = this.getLocationHeader(response); if (redirectUrl) { if (fetchRequestInit.headers && !this.isRelativeURL(redirectUrl) && this.shouldDropAuthorizationHeader(url, redirectUrl)) { delete fetchRequestInit.headers[RedirectHandler.AUTHORIZATION_HEADER]; } url = redirectUrl; } } if (tracerName) { return trace.getTracer(tracerName).startActiveSpan(`redirectHandler - redirect ${redirectCount}`, (span) => { try { span.setAttribute("com.microsoft.kiota.handler.redirect.count", redirectCount); span.setAttribute("http.response.status_code", response.status); return this.executeWithRedirect(url, fetchRequestInit, redirectCount, currentOptions, requestOptions); } finally { span.end(); } }); } return await this.executeWithRedirect(url, fetchRequestInit, redirectCount, currentOptions, requestOptions); } else { return response; } } /** * Executes the request and returns a promise resolving the response. * @param url - The url for the request * @param requestInit - The Fetch RequestInit object. * @param requestOptions - The request options. * @returns A Promise that resolves to the response. */ execute(url, requestInit, requestOptions) { const redirectCount = 0; let currentOptions = this.options; if (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions[RedirectHandlerOptionKey]) { currentOptions = requestOptions[RedirectHandlerOptionKey]; } requestInit.redirect = RedirectHandler.MANUAL_REDIRECT; const obsOptions = getObservabilityOptionsFromRequest(requestOptions); if (obsOptions) { return trace.getTracer(obsOptions.getTracerInstrumentationName()).startActiveSpan("redirectHandler - execute", (span) => { try { span.setAttribute("com.microsoft.kiota.handler.redirect.enable", true); return this.executeWithRedirect(url, requestInit, redirectCount, currentOptions, requestOptions, obsOptions.getTracerInstrumentationName()); } finally { span.end(); } }); } return this.executeWithRedirect(url, requestInit, redirectCount, currentOptions, requestOptions); } } /** * A member holding the array of redirect status codes */ RedirectHandler.REDIRECT_STATUS_CODES = new Set([ 301, // Moved Permanently 302, // Found 303, // See Other 307, // Temporary Permanently 308, // Moved Permanently ]); /** * A member holding SeeOther status code */ RedirectHandler.STATUS_CODE_SEE_OTHER = 303; /** * A member holding the name of the location header */ RedirectHandler.LOCATION_HEADER = "Location"; /** * A member representing the authorization header name */ RedirectHandler.AUTHORIZATION_HEADER = "Authorization"; /** * A member holding the manual redirect value */ RedirectHandler.MANUAL_REDIRECT = "manual"; //# sourceMappingURL=redirectHandler.js.map