All files / proxy/handlers handleResult.ts

76.74% Statements 33/43
100% Branches 6/6
76.92% Functions 10/13
78.04% Lines 32/41

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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112    2x 2x   2x   2x 19x 19x   19x   19x 19x   52x   48x 48x 3x   48x   19x 14x     19x   19x             5x   5x 5x   5x         5x                     19x   16x 14x 14x                 16x         14x                 14x                                             19x 19x    
import { ResultOptions } from '../model'
import { CloudFrontResultResponse } from 'aws-lambda'
import https from 'https'
import { Region } from '../model'
 
import { updateResponseHeaders, addTrafficMonitoringSearchParamsForVisitorIdRequest } from '../utils'
 
export function handleResult(options: ResultOptions): Promise<CloudFrontResultResponse> {
  return new Promise((resolve) => {
    console.debug('Handling result:', { options })
 
    const data: any[] = []
 
    const url = new URL(getIngressAPIHost(options.region, options.fpIngressBaseHost) + options.suffix)
    decodeURIComponent(options.querystring)
      .split('&')
      .filter((it) => it.includes('='))
      .forEach((it) => {
        const kv = it.split('=')
        if (kv[0] === 'region') {
          kv[1] = options.region
        }
        url.searchParams.append(kv[0], kv[1])
      })
    if (options.method === 'POST') {
      addTrafficMonitoringSearchParamsForVisitorIdRequest(url)
    }
 
    console.debug(`Performing request: ${url.toString()}`)
 
    const request = https.request(
      url,
      {
        method: options.method,
        headers: options.headers,
      },
      (response) => {
        response.on('data', (chunk) => data.push(chunk))
 
        response.on('end', () => {
          const payload = Buffer.concat(data)
 
          console.debug('Response from Ingress API', {
            statusCode: response.statusCode,
            payload: payload.toString('utf-8'),
          })
 
          resolve({
            status: response.statusCode ? response.statusCode.toString() : '500',
            statusDescription: response.statusMessage,
            headers: updateResponseHeaders(response.headers),
            bodyEncoding: 'base64',
            body: payload.toString('base64'),
          })
        })
      }
    )
 
    request.write(Buffer.from(options.body, 'base64'))
 
    request.on('error', (error) => {
      console.error('unable to handle result', { error })
      resolve({
        status: '500',
        statusDescription: 'Bad request',
        headers: {},
        bodyEncoding: 'text',
        body: generateErrorResponse(error),
      })
    })
 
    request.end()
  })
}
 
function generateErrorResponse(err: Error): string {
  const body = {
    v: '2',
    error: {
      code: 'Failed',
      message: `An error occurred with Fingerprint Pro Lambda function. Reason ${err}`,
    },
    requestId: generateRequestId,
    products: {},
  }
  return JSON.stringify(body)
}
 
function generateRequestId(): string {
  const uniqueId = generateRequestUniqueId()
  const now = new Date().getTime()
  return `${now}.aws-${uniqueId}`
}
 
function generateRandomString(length: number): string {
  let result = ''
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length))
  }
  return result
}
 
function generateRequestUniqueId(): string {
  return generateRandomString(2)
}
 
function getIngressAPIHost(region: Region, baseHost: string): string {
  const prefix = region === Region.us ? '' : `${region}.`
  return `https://${prefix}${baseHost}`
}