parse-proxy-response.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. function debug(...args) {
  4. utils.logger.log('[https-proxy-agent:parse-proxy-response]', ...args);
  5. }
  6. function parseProxyResponse(socket) {
  7. return new Promise((resolve, reject) => {
  8. // we need to buffer any HTTP traffic that happens with the proxy before we get
  9. // the CONNECT response, so that if the response is anything other than an "200"
  10. // response code, then we can re-play the "data" events on the socket once the
  11. // HTTP parser is hooked up...
  12. let buffersLength = 0;
  13. const buffers = [];
  14. function read() {
  15. const b = socket.read();
  16. if (b) ondata(b);
  17. else socket.once('readable', read);
  18. }
  19. function cleanup() {
  20. socket.removeListener('end', onend);
  21. socket.removeListener('error', onerror);
  22. socket.removeListener('readable', read);
  23. }
  24. function onend() {
  25. cleanup();
  26. debug('onend');
  27. reject(new Error('Proxy connection ended before receiving CONNECT response'));
  28. }
  29. function onerror(err) {
  30. cleanup();
  31. debug('onerror %o', err);
  32. reject(err);
  33. }
  34. function ondata(b) {
  35. buffers.push(b);
  36. buffersLength += b.length;
  37. const buffered = Buffer.concat(buffers, buffersLength);
  38. const endOfHeaders = buffered.indexOf('\r\n\r\n');
  39. if (endOfHeaders === -1) {
  40. // keep buffering
  41. debug('have not received end of HTTP headers yet...');
  42. read();
  43. return;
  44. }
  45. const headerParts = buffered.slice(0, endOfHeaders).toString('ascii').split('\r\n');
  46. const firstLine = headerParts.shift();
  47. if (!firstLine) {
  48. socket.destroy();
  49. return reject(new Error('No header received from proxy CONNECT response'));
  50. }
  51. const firstLineParts = firstLine.split(' ');
  52. const statusCode = +firstLineParts[1];
  53. const statusText = firstLineParts.slice(2).join(' ');
  54. const headers = {};
  55. for (const header of headerParts) {
  56. if (!header) continue;
  57. const firstColon = header.indexOf(':');
  58. if (firstColon === -1) {
  59. socket.destroy();
  60. return reject(new Error(`Invalid header from proxy CONNECT response: "${header}"`));
  61. }
  62. const key = header.slice(0, firstColon).toLowerCase();
  63. const value = header.slice(firstColon + 1).trimStart();
  64. const current = headers[key];
  65. if (typeof current === 'string') {
  66. headers[key] = [current, value];
  67. } else if (Array.isArray(current)) {
  68. current.push(value);
  69. } else {
  70. headers[key] = value;
  71. }
  72. }
  73. debug('got proxy server response: %o %o', firstLine, headers);
  74. cleanup();
  75. resolve({
  76. connect: {
  77. statusCode,
  78. statusText,
  79. headers,
  80. },
  81. buffered,
  82. });
  83. }
  84. socket.on('error', onerror);
  85. socket.on('end', onend);
  86. read();
  87. });
  88. }
  89. exports.parseProxyResponse = parseProxyResponse;
  90. //# sourceMappingURL=parse-proxy-response.js.map