{"version":3,"file":"http-transport-utils.js","sourceRoot":"","sources":["../../../src/transport/http-transport-utils.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type * as http from 'http';\nimport type * as https from 'https';\nimport * as zlib from 'zlib';\nimport { Readable } from 'stream';\nimport type { ExportResponse } from '../export-response';\nimport {\n  isExportHTTPErrorRetryable,\n  parseRetryAfterToMills,\n} from '../is-export-retryable';\nimport { OTLPExporterError } from '../types';\nimport { VERSION } from '../version';\n\nconst DEFAULT_USER_AGENT = `OTel-OTLP-Exporter-JavaScript/${VERSION}`;\n\n/**\n * Maximum response body size (4 MB) that the HTTP transport will read.\n * If the server sends more data the connection is destroyed and the export\n * is treated as a non-retryable error regardless of status code.\n */\nexport const MAX_RESPONSE_BODY_SIZE = 4 * 1024 * 1024;\n\n/**\n * Sends data using http\n * @param request\n * @param url\n * @param headers\n * @param compression\n * @param userAgent\n * @param agent\n * @param data\n * @param timeoutMillis\n */\nexport function sendWithHttp(\n  request: typeof https.request | typeof http.request,\n  url: string,\n  headers: Record<string, string>,\n  compression: 'gzip' | 'none',\n  userAgent: string | undefined,\n  agent: http.Agent | https.Agent,\n  data: Uint8Array,\n  timeoutMillis: number\n): Promise<ExportResponse> {\n  return new Promise<ExportResponse>(resolve => {\n    const parsedUrl = new URL(url);\n\n    if (userAgent) {\n      headers['User-Agent'] = `${userAgent} ${DEFAULT_USER_AGENT}`;\n    } else {\n      headers['User-Agent'] = DEFAULT_USER_AGENT;\n    }\n\n    const options: http.RequestOptions | https.RequestOptions = {\n      hostname: parsedUrl.hostname,\n      port: parsedUrl.port,\n      path: parsedUrl.pathname,\n      method: 'POST',\n      headers,\n      agent,\n    };\n\n    const req = request(options, (res: http.IncomingMessage) => {\n      const responseData: Buffer[] = [];\n      let responseSize = 0;\n      res.on('data', (chunk: Buffer) => {\n        responseSize += chunk.length;\n        if (responseSize > MAX_RESPONSE_BODY_SIZE) {\n          const sizeError = new Error(\n            `OTLP export response body exceeded size limit of ${MAX_RESPONSE_BODY_SIZE} bytes`\n          );\n          // Oversized responses are treated as non-retryable errors\n          // regardless of status code.\n          // Resolve before destroying: res.destroy() tears down the socket which\n          // triggers ECONNRESET on req.on('error'), so, resolving first makes that\n          // a no-op. res.on('error') does not fire because destroy() is called\n          // without an error argument.\n          resolve({ status: 'failure', error: sizeError });\n          res.destroy();\n          return;\n        }\n        responseData.push(chunk);\n      });\n\n      res.on('end', () => {\n        if (res.statusCode && res.statusCode <= 299) {\n          resolve({\n            status: 'success',\n            data: Buffer.concat(responseData),\n          });\n        } else if (\n          res.statusCode &&\n          isExportHTTPErrorRetryable(res.statusCode)\n        ) {\n          resolve({\n            status: 'retryable',\n            retryInMillis: parseRetryAfterToMills(res.headers['retry-after']),\n          });\n        } else {\n          const error = new OTLPExporterError(\n            res.statusMessage,\n            res.statusCode,\n            Buffer.concat(responseData).toString()\n          );\n          resolve({\n            status: 'failure',\n            error,\n          });\n        }\n      });\n\n      res.on('error', (error: Error) => {\n        // Note: 'end' may still be emitted after 'error' on the same response object, since we're resolving a promise,\n        // the first call to resolve() will determine the final state.\n        if (res.statusCode && res.statusCode <= 299) {\n          // If the response is successful but an error occurs while reading the response,\n          // we consider it a success since the data has been sent successfully.\n          resolve({\n            status: 'success',\n          });\n        } else if (\n          res.statusCode &&\n          isExportHTTPErrorRetryable(res.statusCode)\n        ) {\n          resolve({\n            status: 'retryable',\n            error: error,\n            retryInMillis: parseRetryAfterToMills(res.headers['retry-after']),\n          });\n        } else {\n          resolve({\n            status: 'failure',\n            error,\n          });\n        }\n      });\n    });\n\n    req.setTimeout(timeoutMillis, () => {\n      req.destroy();\n      resolve({\n        status: 'retryable',\n        error: new Error('Request timed out'),\n      });\n    });\n\n    req.on('error', (error: Error) => {\n      if (isHttpTransportNetworkErrorRetryable(error)) {\n        resolve({\n          status: 'retryable',\n          error,\n        });\n      } else {\n        resolve({\n          status: 'failure',\n          error,\n        });\n      }\n    });\n\n    compressAndSend(req, compression, data, (error: Error) => {\n      resolve({\n        status: 'failure',\n        error,\n      });\n    });\n  });\n}\n\nexport function compressAndSend(\n  req: http.ClientRequest,\n  compression: 'gzip' | 'none',\n  data: Uint8Array,\n  onError: (error: Error) => void\n) {\n  let dataStream = readableFromUint8Array(data);\n\n  if (compression === 'gzip') {\n    req.setHeader('Content-Encoding', 'gzip');\n    dataStream = dataStream\n      .on('error', onError)\n      .pipe(zlib.createGzip())\n      .on('error', onError);\n  }\n\n  dataStream.pipe(req).on('error', onError);\n}\n\nfunction readableFromUint8Array(buff: string | Uint8Array): Readable {\n  const readable = new Readable();\n  readable.push(buff);\n  readable.push(null);\n\n  return readable;\n}\n\nfunction isHttpTransportNetworkErrorRetryable(error: Error): boolean {\n  const RETRYABLE_NETWORK_ERROR_CODES = new Set([\n    'ECONNRESET',\n    'ECONNREFUSED',\n    'EPIPE',\n    'ETIMEDOUT',\n    'EAI_AGAIN',\n    'ENOTFOUND',\n    'ENETUNREACH',\n    'EHOSTUNREACH',\n  ]);\n\n  if ('code' in error && typeof error.code === 'string') {\n    return RETRYABLE_NETWORK_ERROR_CODES.has(error.code);\n  }\n\n  return false;\n}\n"]}