parse-proxy-response.js 3.8 KB

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