All files / src/handlers handleApiRequest.ts

96.42% Statements 27/28
87.5% Branches 7/8
100% Functions 6/6
96.15% Lines 25/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81                      156x 156x   156x 73x 73x   73x   83x   83x 83x   83x           156x 156x   17x 8x     9x         139x 139x 139x 12x 12x 12x   127x       156x           83x                       83x                
import { WorkerEnv } from '../env'
import {
  addProxyIntegrationHeaders,
  createErrorResponseForIngress,
  createFallbackErrorResponse,
  createResponseWithMaxAge,
  filterCookies,
} from '../utils'
import { addTrafficMonitoringSearchParamsForIngressRequest } from '../utils/addTrafficMonitoring'
 
export async function handleApiRequest(receivedRequest: Request, env: WorkerEnv, targetURL: URL): Promise<Response> {
  const methodAuthorized = isMethodAuthorized(receivedRequest.method)
  try {
    let fingerprintRequest: Request<unknown, CfProperties<unknown>>
    if (!methodAuthorized) {
      const headers = new Headers(receivedRequest.headers)
      headers.delete('Cookie')
 
      fingerprintRequest = new Request(targetURL, new Request(receivedRequest, { headers }))
    } else {
      addTrafficMonitoringSearchParamsForIngressRequest(targetURL)
 
      const headers = filterCookies(new Headers(receivedRequest.headers), (key) => key === '_iidt')
      addProxyIntegrationHeaders(headers, receivedRequest.url, env)
 
      fingerprintRequest = new Request(
        targetURL,
        new Request(receivedRequest, updateRequestInitForIngress({ headers, body: receivedRequest.body }))
      )
    }
 
    console.log(`Sending ${fingerprintRequest.method} to ${fingerprintRequest.url}...`)
    return await fetch(fingerprintRequest).then((originResponse) => modifyResponseIfNecessary(originResponse))
  } catch (e) {
    if (!methodAuthorized) {
      return createFallbackErrorResponse(e)
    }
 
    return createErrorResponseForIngress(receivedRequest, e)
  }
}
 
export function modifyResponseIfNecessary(originResponse: Response): Response {
  const modifiedResponse = new Response(originResponse.body, originResponse)
  const contentType = modifiedResponse.headers.get('Content-Type')
  if (contentType?.trimStart().startsWith('text/javascript')) {
    const maxMaxAge = 60 * 60
    const maxSMaxAge = 60
    return createResponseWithMaxAge(modifiedResponse, maxMaxAge, maxSMaxAge)
  }
  return modifiedResponse
}
 
export function isMethodAuthorized(method: string) {
  return method === 'POST'
}
 
function updateRequestInitForIngress(
  requestInit: RequestInit<CfProperties<unknown>>
): RequestInit<CfProperties<unknown>> {
  Eif (import.meta.env.MODE !== 'production') {
    // The local worker runtime requires duplex half when making a fetch
    // that is streaming the request body and receiving a response body.
    //
    // This requirement comes from the undici client used by the workers
    // runtime to implement fetch.
    //
    // This is not required by the deployed workers runtime. By wrapping this
    // modification in the meta.env.MODE check, the production build will
    // omit this if branch.
    //
    // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
    return {
      ...requestInit,
      duplex: 'half',
    } as unknown as RequestInit<CfProperties<unknown>>
  }
 
  return requestInit
}