parse-proxy-response.js 2.9 KB

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