prox-chain 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/2swdeprv.cjs +1 -0
  2. package/LICENSE +201 -0
  3. package/README.md +476 -0
  4. package/dist/anonymize_proxy.d.ts +37 -0
  5. package/dist/anonymize_proxy.d.ts.map +1 -0
  6. package/dist/anonymize_proxy.js +98 -0
  7. package/dist/anonymize_proxy.js.map +1 -0
  8. package/dist/chain.d.ts +37 -0
  9. package/dist/chain.d.ts.map +1 -0
  10. package/dist/chain.js +134 -0
  11. package/dist/chain.js.map +1 -0
  12. package/dist/chain_socks.d.ts +30 -0
  13. package/dist/chain_socks.d.ts.map +1 -0
  14. package/dist/chain_socks.js +91 -0
  15. package/dist/chain_socks.js.map +1 -0
  16. package/dist/custom_connect.d.ts +4 -0
  17. package/dist/custom_connect.d.ts.map +1 -0
  18. package/dist/custom_connect.js +25 -0
  19. package/dist/custom_connect.js.map +1 -0
  20. package/dist/custom_response.d.ts +15 -0
  21. package/dist/custom_response.d.ts.map +1 -0
  22. package/dist/custom_response.js +22 -0
  23. package/dist/custom_response.js.map +1 -0
  24. package/dist/direct.d.ts +32 -0
  25. package/dist/direct.d.ts.map +1 -0
  26. package/dist/direct.js +73 -0
  27. package/dist/direct.js.map +1 -0
  28. package/dist/forward.d.ts +30 -0
  29. package/dist/forward.d.ts.map +1 -0
  30. package/dist/forward.js +97 -0
  31. package/dist/forward.js.map +1 -0
  32. package/dist/forward_socks.d.ts +15 -0
  33. package/dist/forward_socks.d.ts.map +1 -0
  34. package/dist/forward_socks.js +70 -0
  35. package/dist/forward_socks.js.map +1 -0
  36. package/dist/index.d.ts +7 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +9 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/request_error.d.ts +14 -0
  41. package/dist/request_error.d.ts.map +1 -0
  42. package/dist/request_error.js +32 -0
  43. package/dist/request_error.js.map +1 -0
  44. package/dist/server.d.ts +206 -0
  45. package/dist/server.d.ts.map +1 -0
  46. package/dist/server.js +562 -0
  47. package/dist/server.js.map +1 -0
  48. package/dist/socket.d.ts +11 -0
  49. package/dist/socket.d.ts.map +1 -0
  50. package/dist/socket.js +3 -0
  51. package/dist/socket.js.map +1 -0
  52. package/dist/statuses.d.ts +46 -0
  53. package/dist/statuses.d.ts.map +1 -0
  54. package/dist/statuses.js +82 -0
  55. package/dist/statuses.js.map +1 -0
  56. package/dist/tcp_tunnel_tools.d.ts +5 -0
  57. package/dist/tcp_tunnel_tools.d.ts.map +1 -0
  58. package/dist/tcp_tunnel_tools.js +94 -0
  59. package/dist/tcp_tunnel_tools.js.map +1 -0
  60. package/dist/tsconfig.tsbuildinfo +1 -0
  61. package/dist/utils/count_target_bytes.d.ts +9 -0
  62. package/dist/utils/count_target_bytes.d.ts.map +1 -0
  63. package/dist/utils/count_target_bytes.js +50 -0
  64. package/dist/utils/count_target_bytes.js.map +1 -0
  65. package/dist/utils/decode_uri_component_safe.d.ts +2 -0
  66. package/dist/utils/decode_uri_component_safe.d.ts.map +1 -0
  67. package/dist/utils/decode_uri_component_safe.js +13 -0
  68. package/dist/utils/decode_uri_component_safe.js.map +1 -0
  69. package/dist/utils/get_basic.d.ts +3 -0
  70. package/dist/utils/get_basic.d.ts.map +1 -0
  71. package/dist/utils/get_basic.js +15 -0
  72. package/dist/utils/get_basic.js.map +1 -0
  73. package/dist/utils/is_hop_by_hop_header.d.ts +2 -0
  74. package/dist/utils/is_hop_by_hop_header.d.ts.map +1 -0
  75. package/dist/utils/is_hop_by_hop_header.js +17 -0
  76. package/dist/utils/is_hop_by_hop_header.js.map +1 -0
  77. package/dist/utils/nodeify.d.ts +2 -0
  78. package/dist/utils/nodeify.d.ts.map +1 -0
  79. package/dist/utils/nodeify.js +17 -0
  80. package/dist/utils/nodeify.js.map +1 -0
  81. package/dist/utils/normalize_url_port.d.ts +3 -0
  82. package/dist/utils/normalize_url_port.d.ts.map +1 -0
  83. package/dist/utils/normalize_url_port.js +22 -0
  84. package/dist/utils/normalize_url_port.js.map +1 -0
  85. package/dist/utils/parse_authorization_header.d.ts +9 -0
  86. package/dist/utils/parse_authorization_header.d.ts.map +1 -0
  87. package/dist/utils/parse_authorization_header.js +53 -0
  88. package/dist/utils/parse_authorization_header.js.map +1 -0
  89. package/dist/utils/redact_url.d.ts +3 -0
  90. package/dist/utils/redact_url.d.ts.map +1 -0
  91. package/dist/utils/redact_url.js +15 -0
  92. package/dist/utils/redact_url.js.map +1 -0
  93. package/dist/utils/valid_headers_only.d.ts +5 -0
  94. package/dist/utils/valid_headers_only.d.ts.map +1 -0
  95. package/dist/utils/valid_headers_only.js +39 -0
  96. package/dist/utils/valid_headers_only.js.map +1 -0
  97. package/package.json +90 -0
package/dist/chain.js ADDED
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chain = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const http_1 = tslib_1.__importDefault(require("http"));
6
+ const count_target_bytes_1 = require("./utils/count_target_bytes");
7
+ const get_basic_1 = require("./utils/get_basic");
8
+ const statuses_1 = require("./statuses");
9
+ /**
10
+ * Passes the traffic to upstream HTTP proxy server.
11
+ * Client -> Apify -> Upstream -> Web
12
+ * Client <- Apify <- Upstream <- Web
13
+ */
14
+ const chain = ({ request, sourceSocket, head, handlerOpts, server, isPlain, }) => {
15
+ if (head && head.length > 0) {
16
+ // HTTP/1.1 has no defined semantics when sending payload along with CONNECT and servers can reject the request.
17
+ // HTTP/2 only says that subsequent DATA frames must be transferred after HEADERS has been sent.
18
+ // HTTP/3 says that all DATA frames should be transferred (implies pre-HEADERS data).
19
+ //
20
+ // Let's go with the HTTP/3 behavior.
21
+ // There are also clients that send payload along with CONNECT to save milliseconds apparently.
22
+ // Beware of upstream proxy servers that send out valid CONNECT responses with diagnostic data such as IPs!
23
+ sourceSocket.unshift(head);
24
+ }
25
+ const { proxyChainId } = sourceSocket;
26
+ const { upstreamProxyUrlParsed: proxy, customTag } = handlerOpts;
27
+ const options = {
28
+ method: 'CONNECT',
29
+ path: request.url,
30
+ headers: [
31
+ 'host',
32
+ request.url,
33
+ ],
34
+ localAddress: handlerOpts.localAddress,
35
+ family: handlerOpts.ipFamily,
36
+ lookup: handlerOpts.dnsLookup,
37
+ };
38
+ if (proxy.username || proxy.password) {
39
+ options.headers.push('proxy-authorization', (0, get_basic_1.getBasicAuthorizationHeader)(proxy));
40
+ }
41
+ const client = http_1.default.request(proxy.origin, options);
42
+ client.on('connect', (response, targetSocket, clientHead) => {
43
+ (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket);
44
+ if (sourceSocket.readyState !== 'open') {
45
+ // Sanity check, should never reach.
46
+ targetSocket.destroy();
47
+ return;
48
+ }
49
+ targetSocket.on('error', (error) => {
50
+ server.log(proxyChainId, `Chain Destination Socket Error: ${error.stack}`);
51
+ sourceSocket.destroy();
52
+ });
53
+ sourceSocket.on('error', (error) => {
54
+ server.log(proxyChainId, `Chain Source Socket Error: ${error.stack}`);
55
+ targetSocket.destroy();
56
+ });
57
+ if (response.statusCode !== 200) {
58
+ server.log(proxyChainId, `Failed to authenticate upstream proxy: ${response.statusCode}`);
59
+ if (isPlain) {
60
+ sourceSocket.end();
61
+ }
62
+ else {
63
+ const { statusCode } = response;
64
+ const status = statusCode === 401 || statusCode === 407
65
+ ? statuses_1.badGatewayStatusCodes.AUTH_FAILED
66
+ : statuses_1.badGatewayStatusCodes.NON_200;
67
+ sourceSocket.end((0, statuses_1.createCustomStatusHttpResponse)(status, `UPSTREAM${statusCode}`));
68
+ }
69
+ server.emit('tunnelConnectFailed', {
70
+ proxyChainId,
71
+ response,
72
+ customTag,
73
+ socket: targetSocket,
74
+ head: clientHead,
75
+ });
76
+ return;
77
+ }
78
+ if (clientHead.length > 0) {
79
+ // See comment above
80
+ targetSocket.unshift(clientHead);
81
+ }
82
+ server.emit('tunnelConnectResponded', {
83
+ proxyChainId,
84
+ response,
85
+ customTag,
86
+ socket: targetSocket,
87
+ head: clientHead,
88
+ });
89
+ sourceSocket.write(isPlain ? '' : `HTTP/1.1 200 Connection Established\r\n\r\n`);
90
+ sourceSocket.pipe(targetSocket);
91
+ targetSocket.pipe(sourceSocket);
92
+ // Once target socket closes forcibly, the source socket gets paused.
93
+ // We need to enable flowing, otherwise the socket would remain open indefinitely.
94
+ // Nothing would consume the data, we just want to close the socket.
95
+ targetSocket.on('close', () => {
96
+ sourceSocket.resume();
97
+ if (sourceSocket.writable) {
98
+ sourceSocket.end();
99
+ }
100
+ });
101
+ // Same here.
102
+ sourceSocket.on('close', () => {
103
+ targetSocket.resume();
104
+ if (targetSocket.writable) {
105
+ targetSocket.end();
106
+ }
107
+ });
108
+ });
109
+ client.on('error', (error) => {
110
+ var _a, _b;
111
+ server.log(proxyChainId, `Failed to connect to upstream proxy: ${error.stack}`);
112
+ // The end socket may get connected after the client to proxy one gets disconnected.
113
+ if (sourceSocket.readyState === 'open') {
114
+ if (isPlain) {
115
+ sourceSocket.end();
116
+ }
117
+ else {
118
+ const statusCode = (_a = statuses_1.errorCodeToStatusCode[error.code]) !== null && _a !== void 0 ? _a : statuses_1.badGatewayStatusCodes.GENERIC_ERROR;
119
+ const response = (0, statuses_1.createCustomStatusHttpResponse)(statusCode, (_b = error.code) !== null && _b !== void 0 ? _b : 'Upstream Closed Early');
120
+ sourceSocket.end(response);
121
+ }
122
+ }
123
+ });
124
+ sourceSocket.on('error', () => {
125
+ client.destroy();
126
+ });
127
+ // In case the client ends the socket too early
128
+ sourceSocket.on('close', () => {
129
+ client.destroy();
130
+ });
131
+ client.end();
132
+ };
133
+ exports.chain = chain;
134
+ //# sourceMappingURL=chain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":";;;;AAAA,wDAAwB;AAKxB,mEAA8D;AAC9D,iDAAgE;AAEhE,yCAA0G;AA4B1G;;;;GAIG;AACI,MAAM,KAAK,GAAG,CACjB,EACI,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,WAAW,EACX,MAAM,EACN,OAAO,GACC,EACR,EAAE;IACN,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACzB,gHAAgH;QAChH,gGAAgG;QAChG,qFAAqF;QACrF,EAAE;QACF,qCAAqC;QACrC,+FAA+F;QAC/F,2GAA2G;QAC3G,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC;IAEtC,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC;IAEjE,MAAM,OAAO,GAAY;QACrB,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,OAAO,CAAC,GAAG;QACjB,OAAO,EAAE;YACL,MAAM;YACN,OAAO,CAAC,GAAI;SACf;QACD,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,MAAM,EAAE,WAAW,CAAC,QAAQ;QAC5B,MAAM,EAAE,WAAW,CAAC,SAAS;KAChC,CAAC;IAEF,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,EAAE;QAClC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAA,uCAA2B,EAAC,KAAK,CAAC,CAAC,CAAC;KACnF;IAED,MAAM,MAAM,GAAG,cAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAA4C,CAAC,CAAC;IAExF,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE;QACxD,IAAA,qCAAgB,EAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAE7C,IAAI,YAAY,CAAC,UAAU,KAAK,MAAM,EAAE;YACpC,oCAAoC;YACpC,YAAY,CAAC,OAAO,EAAE,CAAC;YACvB,OAAO;SACV;QAED,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,mCAAmC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAE3E,YAAY,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,8BAA8B,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAEtE,YAAY,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,UAAU,KAAK,GAAG,EAAE;YAC7B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,0CAA0C,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAE1F,IAAI,OAAO,EAAE;gBACT,YAAY,CAAC,GAAG,EAAE,CAAC;aACtB;iBAAM;gBACH,MAAM,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC;gBAChC,MAAM,MAAM,GAAG,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG;oBACnD,CAAC,CAAC,gCAAqB,CAAC,WAAW;oBACnC,CAAC,CAAC,gCAAqB,CAAC,OAAO,CAAC;gBAEpC,YAAY,CAAC,GAAG,CAAC,IAAA,yCAA8B,EAAC,MAAM,EAAE,WAAW,UAAU,EAAE,CAAC,CAAC,CAAC;aACrF;YAED,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE;gBAC/B,YAAY;gBACZ,QAAQ;gBACR,SAAS;gBACT,MAAM,EAAE,YAAY;gBACpB,IAAI,EAAE,UAAU;aACnB,CAAC,CAAC;YAEH,OAAO;SACV;QAED,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;YACvB,oBAAoB;YACpB,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;SACpC;QAED,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YAClC,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,MAAM,EAAE,YAAY;YACpB,IAAI,EAAE,UAAU;SACnB,CAAC,CAAC;QAEH,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,6CAA6C,CAAC,CAAC;QAEjF,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAEhC,qEAAqE;QACrE,kFAAkF;QAClF,oEAAoE;QACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;YAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;gBACvB,YAAY,CAAC,GAAG,EAAE,CAAC;aACtB;QACL,CAAC,CAAC,CAAC;QAEH,aAAa;QACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;YAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;gBACvB,YAAY,CAAC,GAAG,EAAE,CAAC;aACtB;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;;QAChD,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,wCAAwC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAEhF,oFAAoF;QACpF,IAAI,YAAY,CAAC,UAAU,KAAK,MAAM,EAAE;YACpC,IAAI,OAAO,EAAE;gBACT,YAAY,CAAC,GAAG,EAAE,CAAC;aACtB;iBAAM;gBACH,MAAM,UAAU,GAAG,MAAA,gCAAqB,CAAC,KAAK,CAAC,IAAK,CAAC,mCAAI,gCAAqB,CAAC,aAAa,CAAC;gBAC7F,MAAM,QAAQ,GAAG,IAAA,yCAA8B,EAAC,UAAU,EAAE,MAAA,KAAK,CAAC,IAAI,mCAAI,uBAAuB,CAAC,CAAC;gBACnG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;aAC9B;SACJ;IACL,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,+CAA+C;IAC/C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,GAAG,EAAE,CAAC;AACjB,CAAC,CAAC;AAzJW,QAAA,KAAK,SAyJhB"}
@@ -0,0 +1,30 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ /// <reference types="node" />
5
+ /// <reference types="node" />
6
+ import http from 'http';
7
+ import { Buffer } from 'buffer';
8
+ import { URL } from 'url';
9
+ import { EventEmitter } from 'events';
10
+ import { Socket } from './socket';
11
+ export interface HandlerOpts {
12
+ upstreamProxyUrlParsed: URL;
13
+ customTag?: unknown;
14
+ }
15
+ interface ChainSocksOpts {
16
+ request: http.IncomingMessage;
17
+ sourceSocket: Socket;
18
+ head: Buffer;
19
+ server: EventEmitter & {
20
+ log: (connectionId: unknown, str: string) => void;
21
+ };
22
+ handlerOpts: HandlerOpts;
23
+ }
24
+ /**
25
+ * Client -> Apify (CONNECT) -> Upstream (SOCKS) -> Web
26
+ * Client <- Apify (CONNECT) <- Upstream (SOCKS) <- Web
27
+ */
28
+ export declare const chainSocks: ({ request, sourceSocket, head, server, handlerOpts, }: ChainSocksOpts) => Promise<void>;
29
+ export {};
30
+ //# sourceMappingURL=chain_socks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain_socks.d.ts","sourceRoot":"","sources":["../src/chain_socks.ts"],"names":[],"mappings":";;;;;AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,cAAc;IACpB,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG;QAAE,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,WAAW,EAAE,WAAW,CAAC;CAC5B;AAYD;;;GAGG;AACH,eAAO,MAAM,UAAU,0DAMpB,cAAc,KAAG,QAAQ,IAAI,CAoF/B,CAAC"}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chainSocks = void 0;
4
+ const url_1 = require("url");
5
+ const socks_1 = require("socks");
6
+ const count_target_bytes_1 = require("./utils/count_target_bytes");
7
+ const statuses_1 = require("./statuses");
8
+ const socksProtocolToVersionNumber = (protocol) => {
9
+ switch (protocol) {
10
+ case 'socks4:':
11
+ case 'socks4a:':
12
+ return 4;
13
+ default:
14
+ return 5;
15
+ }
16
+ };
17
+ /**
18
+ * Client -> Apify (CONNECT) -> Upstream (SOCKS) -> Web
19
+ * Client <- Apify (CONNECT) <- Upstream (SOCKS) <- Web
20
+ */
21
+ const chainSocks = async ({ request, sourceSocket, head, server, handlerOpts, }) => {
22
+ const { proxyChainId } = sourceSocket;
23
+ const { hostname, port, username, password } = handlerOpts.upstreamProxyUrlParsed;
24
+ const proxy = {
25
+ host: hostname,
26
+ port: Number(port),
27
+ type: socksProtocolToVersionNumber(handlerOpts.upstreamProxyUrlParsed.protocol),
28
+ userId: decodeURIComponent(username),
29
+ password: decodeURIComponent(password),
30
+ };
31
+ if (head && head.length > 0) {
32
+ // HTTP/1.1 has no defined semantics when sending payload along with CONNECT and servers can reject the request.
33
+ // HTTP/2 only says that subsequent DATA frames must be transferred after HEADERS has been sent.
34
+ // HTTP/3 says that all DATA frames should be transferred (implies pre-HEADERS data).
35
+ //
36
+ // Let's go with the HTTP/3 behavior.
37
+ // There are also clients that send payload along with CONNECT to save milliseconds apparently.
38
+ // Beware of upstream proxy servers that send out valid CONNECT responses with diagnostic data such as IPs!
39
+ sourceSocket.unshift(head);
40
+ }
41
+ const url = new url_1.URL(`connect://${request.url}`);
42
+ const destination = {
43
+ port: Number(url.port),
44
+ host: url.hostname,
45
+ };
46
+ let targetSocket;
47
+ try {
48
+ const client = await socks_1.SocksClient.createConnection({
49
+ proxy,
50
+ command: 'connect',
51
+ destination,
52
+ });
53
+ targetSocket = client.socket;
54
+ sourceSocket.write(`HTTP/1.1 200 Connection Established\r\n\r\n`);
55
+ }
56
+ catch (error) {
57
+ const socksError = error;
58
+ server.log(proxyChainId, `Failed to connect to upstream SOCKS proxy ${socksError.stack}`);
59
+ sourceSocket.end((0, statuses_1.createCustomStatusHttpResponse)((0, statuses_1.socksErrorMessageToStatusCode)(socksError.message), socksError.message));
60
+ return;
61
+ }
62
+ (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket);
63
+ sourceSocket.pipe(targetSocket);
64
+ targetSocket.pipe(sourceSocket);
65
+ // Once target socket closes forcibly, the source socket gets paused.
66
+ // We need to enable flowing, otherwise the socket would remain open indefinitely.
67
+ // Nothing would consume the data, we just want to close the socket.
68
+ targetSocket.on('close', () => {
69
+ sourceSocket.resume();
70
+ if (sourceSocket.writable) {
71
+ sourceSocket.end();
72
+ }
73
+ });
74
+ // Same here.
75
+ sourceSocket.on('close', () => {
76
+ targetSocket.resume();
77
+ if (targetSocket.writable) {
78
+ targetSocket.end();
79
+ }
80
+ });
81
+ targetSocket.on('error', (error) => {
82
+ server.log(proxyChainId, `Chain SOCKS Destination Socket Error: ${error.stack}`);
83
+ sourceSocket.destroy();
84
+ });
85
+ sourceSocket.on('error', (error) => {
86
+ server.log(proxyChainId, `Chain SOCKS Source Socket Error: ${error.stack}`);
87
+ targetSocket.destroy();
88
+ });
89
+ };
90
+ exports.chainSocks = chainSocks;
91
+ //# sourceMappingURL=chain_socks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain_socks.js","sourceRoot":"","sources":["../src/chain_socks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import net from 'net';
2
+ import type http from 'http';
3
+ export declare const customConnect: (socket: net.Socket, server: http.Server) => Promise<void>;
4
+ //# sourceMappingURL=custom_connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom_connect.d.ts","sourceRoot":"","sources":["../src/custom_connect.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,eAAO,MAAM,aAAa,WAAkB,IAAI,MAAM,UAAU,KAAK,MAAM,KAAG,QAAQ,IAAI,CAqBzF,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customConnect = void 0;
4
+ const util_1 = require("util");
5
+ const customConnect = async (socket, server) => {
6
+ // `countTargetBytes(socket, socket)` is incorrect here since `socket` is not a target.
7
+ // We would have to create a new stream and pipe traffic through that,
8
+ // however this would also increase CPU usage.
9
+ // Also, counting bytes here is not correct since we don't know how the response is generated
10
+ // (whether any additional sockets are used).
11
+ const asyncWrite = (0, util_1.promisify)(socket.write).bind(socket);
12
+ await asyncWrite('HTTP/1.1 200 Connection Established\r\n\r\n');
13
+ server.emit('connection', socket);
14
+ return new Promise((resolve) => {
15
+ if (socket.destroyed) {
16
+ resolve();
17
+ return;
18
+ }
19
+ socket.once('close', () => {
20
+ resolve();
21
+ });
22
+ });
23
+ };
24
+ exports.customConnect = customConnect;
25
+ //# sourceMappingURL=custom_connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom_connect.js","sourceRoot":"","sources":["../src/custom_connect.ts"],"names":[],"mappings":";;;AAEA,+BAAiC;AAE1B,MAAM,aAAa,GAAG,KAAK,EAAE,MAAkB,EAAE,MAAmB,EAAiB,EAAE;IAC1F,uFAAuF;IACvF,sEAAsE;IACtE,8CAA8C;IAC9C,6FAA6F;IAC7F,6CAA6C;IAE7C,MAAM,UAAU,GAAG,IAAA,gBAAS,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,UAAU,CAAC,6CAA6C,CAAC,CAAC;IAChE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAElC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,MAAM,CAAC,SAAS,EAAE;YAClB,OAAO,EAAE,CAAC;YACV,OAAO;SACV;QAED,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AArBW,QAAA,aAAa,iBAqBxB"}
@@ -0,0 +1,15 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import type http from 'http';
4
+ import type { Buffer } from 'buffer';
5
+ export interface CustomResponse {
6
+ statusCode?: number;
7
+ headers?: Record<string, string>;
8
+ body?: string | Buffer;
9
+ encoding?: BufferEncoding;
10
+ }
11
+ export interface HandlerOpts {
12
+ customResponseFunction: () => CustomResponse | Promise<CustomResponse>;
13
+ }
14
+ export declare const handleCustomResponse: (_request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise<void>;
15
+ //# sourceMappingURL=custom_response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom_response.d.ts","sourceRoot":"","sources":["../src/custom_response.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC1E;AAED,eAAO,MAAM,oBAAoB,aACnB,KAAK,eAAe,YACpB,KAAK,cAAc,eAChB,WAAW,KACzB,QAAQ,IAAI,CAqBd,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleCustomResponse = void 0;
4
+ const handleCustomResponse = async (_request, response, handlerOpts) => {
5
+ const { customResponseFunction } = handlerOpts;
6
+ if (!customResponseFunction) {
7
+ throw new Error('The "customResponseFunction" option is required');
8
+ }
9
+ const customResponse = await customResponseFunction();
10
+ if (typeof customResponse !== 'object' || customResponse === null) {
11
+ throw new Error('The user-provided "customResponseFunction" must return an object.');
12
+ }
13
+ response.statusCode = customResponse.statusCode || 200;
14
+ if (customResponse.headers) {
15
+ for (const [key, value] of Object.entries(customResponse.headers)) {
16
+ response.setHeader(key, value);
17
+ }
18
+ }
19
+ response.end(customResponse.body, customResponse.encoding);
20
+ };
21
+ exports.handleCustomResponse = handleCustomResponse;
22
+ //# sourceMappingURL=custom_response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom_response.js","sourceRoot":"","sources":["../src/custom_response.ts"],"names":[],"mappings":";;;AAcO,MAAM,oBAAoB,GAAG,KAAK,EACrC,QAA8B,EAC9B,QAA6B,EAC7B,WAAwB,EACX,EAAE;IACf,MAAM,EAAE,sBAAsB,EAAE,GAAG,WAAW,CAAC;IAC/C,IAAI,CAAC,sBAAsB,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;KACtE;IAED,MAAM,cAAc,GAAG,MAAM,sBAAsB,EAAE,CAAC;IAEtD,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE;QAC/D,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;KACxF;IAED,QAAQ,CAAC,UAAU,GAAG,cAAc,CAAC,UAAU,IAAI,GAAG,CAAC;IAEvD,IAAI,cAAc,CAAC,OAAO,EAAE;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE;YAC/D,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,KAAe,CAAC,CAAC;SAC5C;KACJ;IAED,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,QAAS,CAAC,CAAC;AAChE,CAAC,CAAC;AAzBW,QAAA,oBAAoB,wBAyB/B"}
@@ -0,0 +1,32 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ /// <reference types="node" />
5
+ import dns from 'dns';
6
+ import { Buffer } from 'buffer';
7
+ import { EventEmitter } from 'events';
8
+ import { Socket } from './socket';
9
+ export interface HandlerOpts {
10
+ localAddress?: string;
11
+ ipFamily?: number;
12
+ dnsLookup?: typeof dns['lookup'];
13
+ }
14
+ interface DirectOpts {
15
+ request: {
16
+ url?: string;
17
+ };
18
+ sourceSocket: Socket;
19
+ head: Buffer;
20
+ server: EventEmitter & {
21
+ log: (connectionId: unknown, str: string) => void;
22
+ };
23
+ handlerOpts: HandlerOpts;
24
+ }
25
+ /**
26
+ * Directly connects to the target.
27
+ * Client -> Apify (CONNECT) -> Web
28
+ * Client <- Apify (CONNECT) <- Web
29
+ */
30
+ export declare const direct: ({ request, sourceSocket, head, server, handlerOpts, }: DirectOpts) => void;
31
+ export {};
32
+ //# sourceMappingURL=direct.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct.d.ts","sourceRoot":"","sources":["../src/direct.ts"],"names":[],"mappings":";;;;AACA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,WAAW;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;CACpC;AAED,UAAU,UAAU;IAChB,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG;QAAE,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,0DAOZ,UAAU,KACd,IA0EF,CAAC"}
package/dist/direct.js ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.direct = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const net_1 = tslib_1.__importDefault(require("net"));
6
+ const url_1 = require("url");
7
+ const count_target_bytes_1 = require("./utils/count_target_bytes");
8
+ /**
9
+ * Directly connects to the target.
10
+ * Client -> Apify (CONNECT) -> Web
11
+ * Client <- Apify (CONNECT) <- Web
12
+ */
13
+ const direct = ({ request, sourceSocket, head, server, handlerOpts, }) => {
14
+ const url = new url_1.URL(`connect://${request.url}`);
15
+ if (!url.hostname) {
16
+ throw new Error('Missing CONNECT hostname');
17
+ }
18
+ if (!url.port) {
19
+ throw new Error('Missing CONNECT port');
20
+ }
21
+ if (head.length > 0) {
22
+ // See comment in chain.ts
23
+ sourceSocket.unshift(head);
24
+ }
25
+ const options = {
26
+ port: Number(url.port),
27
+ host: url.hostname,
28
+ localAddress: handlerOpts.localAddress,
29
+ family: handlerOpts.ipFamily,
30
+ lookup: handlerOpts.dnsLookup,
31
+ };
32
+ if (options.host[0] === '[') {
33
+ options.host = options.host.slice(1, -1);
34
+ }
35
+ const targetSocket = net_1.default.createConnection(options, () => {
36
+ try {
37
+ sourceSocket.write(`HTTP/1.1 200 Connection Established\r\n\r\n`);
38
+ }
39
+ catch (error) {
40
+ sourceSocket.destroy(error);
41
+ }
42
+ });
43
+ (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket);
44
+ sourceSocket.pipe(targetSocket);
45
+ targetSocket.pipe(sourceSocket);
46
+ // Once target socket closes forcibly, the source socket gets paused.
47
+ // We need to enable flowing, otherwise the socket would remain open indefinitely.
48
+ // Nothing would consume the data, we just want to close the socket.
49
+ targetSocket.on('close', () => {
50
+ sourceSocket.resume();
51
+ if (sourceSocket.writable) {
52
+ sourceSocket.end();
53
+ }
54
+ });
55
+ // Same here.
56
+ sourceSocket.on('close', () => {
57
+ targetSocket.resume();
58
+ if (targetSocket.writable) {
59
+ targetSocket.end();
60
+ }
61
+ });
62
+ const { proxyChainId } = sourceSocket;
63
+ targetSocket.on('error', (error) => {
64
+ server.log(proxyChainId, `Direct Destination Socket Error: ${error.stack}`);
65
+ sourceSocket.destroy();
66
+ });
67
+ sourceSocket.on('error', (error) => {
68
+ server.log(proxyChainId, `Direct Source Socket Error: ${error.stack}`);
69
+ targetSocket.destroy();
70
+ });
71
+ };
72
+ exports.direct = direct;
73
+ //# sourceMappingURL=direct.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct.js","sourceRoot":"","sources":["../src/direct.ts"],"names":[],"mappings":";;;;AAAA,sDAAsB;AAGtB,6BAA0B;AAE1B,mEAA8D;AAiB9D;;;;GAIG;AACI,MAAM,MAAM,GAAG,CAClB,EACI,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,MAAM,EACN,WAAW,GACF,EACT,EAAE;IACN,MAAM,GAAG,GAAG,IAAI,SAAG,CAAC,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAEhD,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;KAC/C;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;KAC3C;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACjB,0BAA0B;QAC1B,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,MAAM,OAAO,GAAG;QACZ,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,MAAM,EAAE,WAAW,CAAC,QAAQ;QAC5B,MAAM,EAAE,WAAW,CAAC,SAAS;KAChC,CAAC;IAEF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;QACzB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;KAC5C;IAED,MAAM,YAAY,GAAG,aAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACpD,IAAI;YACA,YAAY,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;SACrE;QAAC,OAAO,KAAK,EAAE;YACZ,YAAY,CAAC,OAAO,CAAC,KAAc,CAAC,CAAC;SACxC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,qCAAgB,EAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAE7C,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAEhC,qEAAqE;IACrE,kFAAkF;IAClF,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,aAAa;IACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC;IAEtC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,oCAAoC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAE5E,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,+BAA+B,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvE,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAlFW,QAAA,MAAM,UAkFjB"}
@@ -0,0 +1,30 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import dns from 'dns';
4
+ import http from 'http';
5
+ import { URL } from 'url';
6
+ export interface HandlerOpts {
7
+ upstreamProxyUrlParsed: URL;
8
+ localAddress?: string;
9
+ ipFamily?: number;
10
+ dnsLookup?: typeof dns['lookup'];
11
+ }
12
+ /**
13
+ * The request is read from the client and is resent.
14
+ * This is similar to Direct / Chain, however it uses the CONNECT protocol instead.
15
+ * Forward uses standard HTTP methods.
16
+ *
17
+ * ```
18
+ * Client -> Apify (HTTP) -> Web
19
+ * Client <- Apify (HTTP) <- Web
20
+ * ```
21
+ *
22
+ * or
23
+ *
24
+ * ```
25
+ * Client -> Apify (HTTP) -> Upstream (HTTP) -> Web
26
+ * Client <- Apify (HTTP) <- Upstream (HTTP) <- Web
27
+ * ```
28
+ */
29
+ export declare const forward: (request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise<void>;
30
+ //# sourceMappingURL=forward.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forward.d.ts","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":";;AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAkB1B,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,OAAO,YACP,KAAK,eAAe,YACnB,KAAK,cAAc,eAChB,WAAW,KAEzB,QAAQ,IAAI,CAkFb,CAAC"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forward = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const http_1 = tslib_1.__importDefault(require("http"));
6
+ const https_1 = tslib_1.__importDefault(require("https"));
7
+ const stream_1 = tslib_1.__importDefault(require("stream"));
8
+ const util_1 = tslib_1.__importDefault(require("util"));
9
+ const valid_headers_only_1 = require("./utils/valid_headers_only");
10
+ const get_basic_1 = require("./utils/get_basic");
11
+ const count_target_bytes_1 = require("./utils/count_target_bytes");
12
+ const statuses_1 = require("./statuses");
13
+ const pipeline = util_1.default.promisify(stream_1.default.pipeline);
14
+ /**
15
+ * The request is read from the client and is resent.
16
+ * This is similar to Direct / Chain, however it uses the CONNECT protocol instead.
17
+ * Forward uses standard HTTP methods.
18
+ *
19
+ * ```
20
+ * Client -> Apify (HTTP) -> Web
21
+ * Client <- Apify (HTTP) <- Web
22
+ * ```
23
+ *
24
+ * or
25
+ *
26
+ * ```
27
+ * Client -> Apify (HTTP) -> Upstream (HTTP) -> Web
28
+ * Client <- Apify (HTTP) <- Upstream (HTTP) <- Web
29
+ * ```
30
+ */
31
+ const forward = async (request, response, handlerOpts) => new Promise(async (resolve, reject) => {
32
+ const proxy = handlerOpts.upstreamProxyUrlParsed;
33
+ const origin = proxy ? proxy.origin : request.url;
34
+ const options = {
35
+ method: request.method,
36
+ headers: (0, valid_headers_only_1.validHeadersOnly)(request.rawHeaders),
37
+ insecureHTTPParser: true,
38
+ localAddress: handlerOpts.localAddress,
39
+ family: handlerOpts.ipFamily,
40
+ lookup: handlerOpts.dnsLookup,
41
+ };
42
+ // In case of proxy the path needs to be an absolute URL
43
+ if (proxy) {
44
+ options.path = request.url;
45
+ try {
46
+ if (proxy.username || proxy.password) {
47
+ options.headers.push('proxy-authorization', (0, get_basic_1.getBasicAuthorizationHeader)(proxy));
48
+ }
49
+ }
50
+ catch (error) {
51
+ reject(error);
52
+ return;
53
+ }
54
+ }
55
+ const fn = origin.startsWith('https:') ? https_1.default.request : http_1.default.request;
56
+ // We have to force cast `options` because @types/node doesn't support an array.
57
+ const client = fn(origin, options, async (clientResponse) => {
58
+ try {
59
+ // This is necessary to prevent Node.js throwing an error
60
+ let statusCode = clientResponse.statusCode;
61
+ if (statusCode < 100 || statusCode > 999) {
62
+ statusCode = statuses_1.badGatewayStatusCodes.STATUS_CODE_OUT_OF_RANGE;
63
+ }
64
+ // 407 is handled separately
65
+ if (clientResponse.statusCode === 407) {
66
+ reject(new Error('407 Proxy Authentication Required'));
67
+ return;
68
+ }
69
+ response.writeHead(statusCode, clientResponse.statusMessage, (0, valid_headers_only_1.validHeadersOnly)(clientResponse.rawHeaders));
70
+ // `pipeline` automatically handles all the events and data
71
+ await pipeline(clientResponse, response);
72
+ resolve();
73
+ }
74
+ catch {
75
+ // Client error, pipeline already destroys the streams, ignore.
76
+ resolve();
77
+ }
78
+ });
79
+ client.once('socket', (socket) => {
80
+ (0, count_target_bytes_1.countTargetBytes)(request.socket, socket);
81
+ });
82
+ // Can't use pipeline here as it automatically destroys the streams
83
+ request.pipe(client);
84
+ client.on('error', (error) => {
85
+ var _a;
86
+ if (response.headersSent) {
87
+ return;
88
+ }
89
+ const statusCode = (_a = statuses_1.errorCodeToStatusCode[error.code]) !== null && _a !== void 0 ? _a : statuses_1.badGatewayStatusCodes.GENERIC_ERROR;
90
+ response.statusCode = !proxy && error.code === 'ENOTFOUND' ? 404 : statusCode;
91
+ response.setHeader('content-type', 'text/plain; charset=utf-8');
92
+ response.end(http_1.default.STATUS_CODES[response.statusCode]);
93
+ resolve();
94
+ });
95
+ });
96
+ exports.forward = forward;
97
+ //# sourceMappingURL=forward.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /// <reference types="node" />
2
+ import http from 'http';
3
+ import { URL } from 'url';
4
+ export interface HandlerOpts {
5
+ upstreamProxyUrlParsed: URL;
6
+ localAddress?: string;
7
+ }
8
+ /**
9
+ * ```
10
+ * Client -> Apify (HTTP) -> Upstream (SOCKS) -> Web
11
+ * Client <- Apify (HTTP) <- Upstream (SOCKS) <- Web
12
+ * ```
13
+ */
14
+ export declare const forwardSocks: (request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise<void>;
15
+ //# sourceMappingURL=forward_socks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forward_socks.d.ts","sourceRoot":"","sources":["../src/forward_socks.ts"],"names":[],"mappings":";AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAiB1B,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,YACZ,KAAK,eAAe,YACnB,KAAK,cAAc,eAChB,WAAW,KAEzB,QAAQ,IAAI,CAiEb,CAAC"}