import { createLogger , ErrorWithCause , first , pickIgnoreCase , pickNonNullish , pickValueIgnoreCase , removeTrailingSlashes } from '@sap-cloud-sdk/util' ; import { Destination , DestinationFetchOptions } from '@sap-cloud-sdk/connectivity' ; import { HttpRequestConfig } from './http-client-types' ; import { executeHttpRequest } from '.' ; const logger = createLogger ( { package : 'http-client' , messageContext : 'csrf-token-header' } ) ; param destination param requestConfig returns internal export async function buildCsrfHeaders < T extends HttpRequestConfig > ( destination , requestConfig ) { const csrfHeaders = await makeCsrfRequest ( destination , requestConfig ) ; validateCsrfTokenResponse ( csrfHeaders ) ; return pickNonNullish ( { ... pickIgnoreCase ( csrfHeaders , 'x-csrf-token' ) , cookie : buildCookieHeaderValue ( pickValueIgnoreCase ( csrfHeaders , 'set-cookie' ) ) } ) ; } param headers returns internal export function buildCsrfFetchHeaders ( headers ) { const fetchHeader = ! pickValueIgnoreCase ( headers , 'x-csrf-token' ) && { 'x-csrf-token' : 'Fetch' } ; const contentLengthHeaderKey = first ( Object . keys ( pickIgnoreCase ( headers , 'content-length' ) ) ) || 'content-length' ; return { ... fetchHeader , ... headers , [ contentLengthHeaderKey ] : 0 } ; } function makeCsrfRequest < T extends HttpRequestConfig > ( destination , requestConfig ) { const axiosConfig = { method : 'head' , ... requestConfig , headers : buildCsrfFetchHeaders ( requestConfig . headers ) , url : requestConfig . url } ; const requestConfigWithTrailingSlash = appendSlash ( axiosConfig ) ; return executeHttpRequest ( destination , requestConfigWithTrailingSlash ) . then ( response => response . headers ) . catch ( error1 => { const headers1 = getResponseHeadersFromError ( error1 ) ; if ( hasCsrfToken ( headers1 ) ) { return headers1 ; } logger . warn ( new ErrorWithCause ( `First attempt to fetch CSRF token failed with the URL: ${ requestConfigWithTrailingSlash . url }. Retrying without trailing slash.` , error1 ) ) ; const requestConfigWithOutTrailingSlash = removeSlash ( axiosConfig ) ; return executeHttpRequest ( destination , requestConfigWithOutTrailingSlash ) . then ( response => response . headers ) . catch ( error2 => { const headers2 = getResponseHeadersFromError ( error2 ) ; if ( hasCsrfToken ( headers2 ) ) { return headers2 ; } logger . warn ( new ErrorWithCause ( `Second attempt to fetch CSRF token failed with the URL: ${ requestConfigWithOutTrailingSlash . url }. No CSRF token fetched.` , error2 ) ) ; return { } ; } ) ; } ) ; } function hasCsrfToken ( headers ) { return ! ! headers [ 'x-csrf-token' ] ; } function getResponseHeadersFromError ( error ) { return error . response ?. headers || { } ; } function appendSlash ( requestConfig ) { if ( ! requestConfig . url ! . endsWith ( '/' ) ) { requestConfig . url = `${ requestConfig . url }/` ; } return requestConfig ; } function removeSlash ( requestConfig ) { if ( requestConfig . url ! . endsWith ( '/' ) ) { requestConfig . url = removeTrailingSlashes ( requestConfig . url ! ) ; } return requestConfig ; } function validateCsrfTokenResponse ( responseHeaders ) { if ( ! responseHeaders [ 'x-csrf-token' ] ) { logger . warn ( 'Destination did not return a CSRF token. This may cause a failure when sending the OData request.' ) ; } if ( ! responseHeaders [ 'set-cookie' ] ) { logger . warn ( 'CSRF header response does not include cookies.' ) ; } return responseHeaders ; } function buildCookieHeaderValue ( cookies ? ) { if ( cookies && cookies . length ) { return cookies . map ( ( cookie ) => cookie . split ( ';' ) [ 0 ] ) . join ( ';' ) ; } }