maven-proxy 1.0.1 → 1.0.3

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.
@@ -1,236 +1,236 @@
1
- import net from "node:net";
2
- import tls from "node:tls";
3
- import { ProxyAgent } from "proxy-agent";
4
-
5
- function normalizeHostname(hostname) {
6
- return String(hostname || "")
7
- .trim()
8
- .replace(/^\[/, "")
9
- .replace(/\]$/, "")
10
- .toLowerCase();
11
- }
12
-
13
- function safeDecode(value) {
14
- try {
15
- return decodeURIComponent(value);
16
- } catch {
17
- return value;
18
- }
19
- }
20
-
21
- function buildProxyAuthHeader(proxyUrl) {
22
- if (!proxyUrl.username && !proxyUrl.password) {
23
- return "";
24
- }
25
-
26
- const username = safeDecode(proxyUrl.username || "");
27
- const password = safeDecode(proxyUrl.password || "");
28
- const token = Buffer.from(`${username}:${password}`).toString("base64");
29
- return `Basic ${token}`;
30
- }
31
-
32
- function parseStatusCode(responseHeader) {
33
- const statusLine = responseHeader.split("\r\n")[0] || "";
34
- const match = statusLine.match(/^HTTP\/\d\.\d\s+(\d{3})/i);
35
- const code = match ? Number.parseInt(match[1], 10) : 0;
36
- return { code, statusLine };
37
- }
38
-
39
- function createSocketToProxy(proxyUrl, timeoutMs) {
40
- const port = proxyUrl.port
41
- ? Number.parseInt(proxyUrl.port, 10)
42
- : proxyUrl.protocol === "https:"
43
- ? 443
44
- : 80;
45
-
46
- if (proxyUrl.protocol === "https:") {
47
- return tls.connect({
48
- host: proxyUrl.hostname,
49
- port,
50
- servername: proxyUrl.hostname,
51
- timeout: timeoutMs,
52
- });
53
- }
54
-
55
- return net.connect({
56
- host: proxyUrl.hostname,
57
- port,
58
- timeout: timeoutMs,
59
- });
60
- }
61
-
62
- export class UpstreamProxyManager {
63
- constructor(config, matchesDomain) {
64
- this.config = config;
65
- this.matchesDomain = matchesDomain;
66
- this.agentCache = new Map();
67
- }
68
-
69
- shouldBypass(hostname) {
70
- const host = normalizeHostname(hostname);
71
- if (!host) {
72
- return true;
73
- }
74
-
75
- const patterns = [
76
- ...(this.config.upstreamNoProxyDomains || []),
77
- ...(this.config.upstreamIgnoreDomains || []),
78
- ];
79
-
80
- const uniquePatterns = [...new Set(patterns.map((item) => String(item).trim()).filter(Boolean))];
81
- if (uniquePatterns.length === 0) {
82
- return false;
83
- }
84
-
85
- if (uniquePatterns.includes("*")) {
86
- return true;
87
- }
88
-
89
- return this.matchesDomain(host, uniquePatterns);
90
- }
91
-
92
- getProxyUrlFor(protocol, hostname) {
93
- if (this.shouldBypass(hostname)) {
94
- return "";
95
- }
96
-
97
- if (protocol === "https:") {
98
- return (
99
- this.config.upstreamHttpsProxyUrl ||
100
- this.config.upstreamProxyUrl ||
101
- this.config.upstreamHttpProxyUrl ||
102
- ""
103
- );
104
- }
105
-
106
- return (
107
- this.config.upstreamHttpProxyUrl ||
108
- this.config.upstreamProxyUrl ||
109
- ""
110
- );
111
- }
112
-
113
- getAgentForUrl(urlObj) {
114
- const proxyUrl = this.getProxyUrlFor(urlObj.protocol, urlObj.hostname);
115
- if (!proxyUrl) {
116
- return undefined;
117
- }
118
-
119
- const cacheKey = `${proxyUrl}`;
120
- if (!this.agentCache.has(cacheKey)) {
121
- this.agentCache.set(cacheKey, new ProxyAgent(proxyUrl));
122
- }
123
-
124
- return this.agentCache.get(cacheKey);
125
- }
126
-
127
- hasProxyFor(protocol, hostname) {
128
- return Boolean(this.getProxyUrlFor(protocol, hostname));
129
- }
130
-
131
- async createConnectTunnel(targetHost, targetPort, timeoutMs) {
132
- const proxyUrlText = this.getProxyUrlFor("https:", targetHost);
133
- if (!proxyUrlText) {
134
- throw new Error("Upstream proxy is not configured for CONNECT");
135
- }
136
-
137
- const proxyUrl = new URL(proxyUrlText);
138
- if (proxyUrl.protocol !== "http:" && proxyUrl.protocol !== "https:") {
139
- throw new Error(`Unsupported upstream proxy protocol for CONNECT: ${proxyUrl.protocol}`);
140
- }
141
-
142
- const authHeader = buildProxyAuthHeader(proxyUrl);
143
-
144
- const socket = createSocketToProxy(proxyUrl, timeoutMs);
145
- const connectEvent = proxyUrl.protocol === "https:" ? "secureConnect" : "connect";
146
-
147
- return new Promise((resolve, reject) => {
148
- let settled = false;
149
- let bytes = 0;
150
- const chunks = [];
151
-
152
- const cleanup = () => {
153
- socket.removeListener(connectEvent, onConnectReady);
154
- socket.removeListener("data", onData);
155
- socket.removeListener("timeout", onTimeout);
156
- socket.removeListener("error", onError);
157
- };
158
-
159
- const fail = (error) => {
160
- if (settled) {
161
- return;
162
- }
163
- settled = true;
164
- cleanup();
165
- socket.destroy();
166
- reject(error);
167
- };
168
-
169
- const succeed = (bufferedData) => {
170
- if (settled) {
171
- return;
172
- }
173
- settled = true;
174
- cleanup();
175
- socket.setTimeout(0);
176
- resolve({ socket, bufferedData });
177
- };
178
-
179
- const onConnectReady = () => {
180
- const headers = [
181
- `CONNECT ${targetHost}:${targetPort} HTTP/1.1`,
182
- `Host: ${targetHost}:${targetPort}`,
183
- "Proxy-Connection: Keep-Alive",
184
- ];
185
-
186
- if (authHeader) {
187
- headers.push(`Proxy-Authorization: ${authHeader}`);
188
- }
189
-
190
- const payload = `${headers.join("\r\n")}\r\n\r\n`;
191
- socket.write(payload);
192
- };
193
-
194
- const onData = (chunk) => {
195
- chunks.push(chunk);
196
- bytes += chunk.length;
197
-
198
- if (bytes > 128 * 1024) {
199
- fail(new Error("Upstream proxy CONNECT response is too large"));
200
- return;
201
- }
202
-
203
- const merged = Buffer.concat(chunks, bytes);
204
- const boundary = merged.indexOf("\r\n\r\n");
205
- if (boundary === -1) {
206
- return;
207
- }
208
-
209
- const headerBuffer = merged.slice(0, boundary + 4);
210
- const headerText = headerBuffer.toString("latin1");
211
- const { code, statusLine } = parseStatusCode(headerText);
212
-
213
- if (code !== 200) {
214
- fail(new Error(`Upstream proxy CONNECT failed: ${statusLine || "unknown response"}`));
215
- return;
216
- }
217
-
218
- const rest = merged.slice(boundary + 4);
219
- succeed(rest);
220
- };
221
-
222
- const onTimeout = () => {
223
- fail(new Error(`Upstream proxy CONNECT timeout after ${timeoutMs}ms`));
224
- };
225
-
226
- const onError = (error) => {
227
- fail(error);
228
- };
229
-
230
- socket.on(connectEvent, onConnectReady);
231
- socket.on("data", onData);
232
- socket.on("timeout", onTimeout);
233
- socket.on("error", onError);
234
- });
235
- }
236
- }
1
+ import net from "node:net";
2
+ import tls from "node:tls";
3
+ import { ProxyAgent } from "proxy-agent";
4
+
5
+ function normalizeHostname(hostname) {
6
+ return String(hostname || "")
7
+ .trim()
8
+ .replace(/^\[/, "")
9
+ .replace(/\]$/, "")
10
+ .toLowerCase();
11
+ }
12
+
13
+ function safeDecode(value) {
14
+ try {
15
+ return decodeURIComponent(value);
16
+ } catch {
17
+ return value;
18
+ }
19
+ }
20
+
21
+ function buildProxyAuthHeader(proxyUrl) {
22
+ if (!proxyUrl.username && !proxyUrl.password) {
23
+ return "";
24
+ }
25
+
26
+ const username = safeDecode(proxyUrl.username || "");
27
+ const password = safeDecode(proxyUrl.password || "");
28
+ const token = Buffer.from(`${username}:${password}`).toString("base64");
29
+ return `Basic ${token}`;
30
+ }
31
+
32
+ function parseStatusCode(responseHeader) {
33
+ const statusLine = responseHeader.split("\r\n")[0] || "";
34
+ const match = statusLine.match(/^HTTP\/\d\.\d\s+(\d{3})/i);
35
+ const code = match ? Number.parseInt(match[1], 10) : 0;
36
+ return { code, statusLine };
37
+ }
38
+
39
+ function createSocketToProxy(proxyUrl, timeoutMs) {
40
+ const port = proxyUrl.port
41
+ ? Number.parseInt(proxyUrl.port, 10)
42
+ : proxyUrl.protocol === "https:"
43
+ ? 443
44
+ : 80;
45
+
46
+ if (proxyUrl.protocol === "https:") {
47
+ return tls.connect({
48
+ host: proxyUrl.hostname,
49
+ port,
50
+ servername: proxyUrl.hostname,
51
+ timeout: timeoutMs,
52
+ });
53
+ }
54
+
55
+ return net.connect({
56
+ host: proxyUrl.hostname,
57
+ port,
58
+ timeout: timeoutMs,
59
+ });
60
+ }
61
+
62
+ export class UpstreamProxyManager {
63
+ constructor(config, matchesDomain) {
64
+ this.config = config;
65
+ this.matchesDomain = matchesDomain;
66
+ this.agentCache = new Map();
67
+ }
68
+
69
+ shouldBypass(hostname) {
70
+ const host = normalizeHostname(hostname);
71
+ if (!host) {
72
+ return true;
73
+ }
74
+
75
+ const patterns = [
76
+ ...(this.config.upstreamNoProxyDomains || []),
77
+ ...(this.config.upstreamIgnoreDomains || []),
78
+ ];
79
+
80
+ const uniquePatterns = [...new Set(patterns.map((item) => String(item).trim()).filter(Boolean))];
81
+ if (uniquePatterns.length === 0) {
82
+ return false;
83
+ }
84
+
85
+ if (uniquePatterns.includes("*")) {
86
+ return true;
87
+ }
88
+
89
+ return this.matchesDomain(host, uniquePatterns);
90
+ }
91
+
92
+ getProxyUrlFor(protocol, hostname) {
93
+ if (this.shouldBypass(hostname)) {
94
+ return "";
95
+ }
96
+
97
+ if (protocol === "https:") {
98
+ return (
99
+ this.config.upstreamHttpsProxyUrl ||
100
+ this.config.upstreamProxyUrl ||
101
+ this.config.upstreamHttpProxyUrl ||
102
+ ""
103
+ );
104
+ }
105
+
106
+ return (
107
+ this.config.upstreamHttpProxyUrl ||
108
+ this.config.upstreamProxyUrl ||
109
+ ""
110
+ );
111
+ }
112
+
113
+ getAgentForUrl(urlObj) {
114
+ const proxyUrl = this.getProxyUrlFor(urlObj.protocol, urlObj.hostname);
115
+ if (!proxyUrl) {
116
+ return undefined;
117
+ }
118
+
119
+ const cacheKey = `${proxyUrl}`;
120
+ if (!this.agentCache.has(cacheKey)) {
121
+ this.agentCache.set(cacheKey, new ProxyAgent(proxyUrl));
122
+ }
123
+
124
+ return this.agentCache.get(cacheKey);
125
+ }
126
+
127
+ hasProxyFor(protocol, hostname) {
128
+ return Boolean(this.getProxyUrlFor(protocol, hostname));
129
+ }
130
+
131
+ async createConnectTunnel(targetHost, targetPort, timeoutMs) {
132
+ const proxyUrlText = this.getProxyUrlFor("https:", targetHost);
133
+ if (!proxyUrlText) {
134
+ throw new Error("Upstream proxy is not configured for CONNECT");
135
+ }
136
+
137
+ const proxyUrl = new URL(proxyUrlText);
138
+ if (proxyUrl.protocol !== "http:" && proxyUrl.protocol !== "https:") {
139
+ throw new Error(`Unsupported upstream proxy protocol for CONNECT: ${proxyUrl.protocol}`);
140
+ }
141
+
142
+ const authHeader = buildProxyAuthHeader(proxyUrl);
143
+
144
+ const socket = createSocketToProxy(proxyUrl, timeoutMs);
145
+ const connectEvent = proxyUrl.protocol === "https:" ? "secureConnect" : "connect";
146
+
147
+ return new Promise((resolve, reject) => {
148
+ let settled = false;
149
+ let bytes = 0;
150
+ const chunks = [];
151
+
152
+ const cleanup = () => {
153
+ socket.removeListener(connectEvent, onConnectReady);
154
+ socket.removeListener("data", onData);
155
+ socket.removeListener("timeout", onTimeout);
156
+ socket.removeListener("error", onError);
157
+ };
158
+
159
+ const fail = (error) => {
160
+ if (settled) {
161
+ return;
162
+ }
163
+ settled = true;
164
+ cleanup();
165
+ socket.destroy();
166
+ reject(error);
167
+ };
168
+
169
+ const succeed = (bufferedData) => {
170
+ if (settled) {
171
+ return;
172
+ }
173
+ settled = true;
174
+ cleanup();
175
+ socket.setTimeout(0);
176
+ resolve({ socket, bufferedData });
177
+ };
178
+
179
+ const onConnectReady = () => {
180
+ const headers = [
181
+ `CONNECT ${targetHost}:${targetPort} HTTP/1.1`,
182
+ `Host: ${targetHost}:${targetPort}`,
183
+ "Proxy-Connection: Keep-Alive",
184
+ ];
185
+
186
+ if (authHeader) {
187
+ headers.push(`Proxy-Authorization: ${authHeader}`);
188
+ }
189
+
190
+ const payload = `${headers.join("\r\n")}\r\n\r\n`;
191
+ socket.write(payload);
192
+ };
193
+
194
+ const onData = (chunk) => {
195
+ chunks.push(chunk);
196
+ bytes += chunk.length;
197
+
198
+ if (bytes > 128 * 1024) {
199
+ fail(new Error("Upstream proxy CONNECT response is too large"));
200
+ return;
201
+ }
202
+
203
+ const merged = Buffer.concat(chunks, bytes);
204
+ const boundary = merged.indexOf("\r\n\r\n");
205
+ if (boundary === -1) {
206
+ return;
207
+ }
208
+
209
+ const headerBuffer = merged.slice(0, boundary + 4);
210
+ const headerText = headerBuffer.toString("latin1");
211
+ const { code, statusLine } = parseStatusCode(headerText);
212
+
213
+ if (code !== 200) {
214
+ fail(new Error(`Upstream proxy CONNECT failed: ${statusLine || "unknown response"}`));
215
+ return;
216
+ }
217
+
218
+ const rest = merged.slice(boundary + 4);
219
+ succeed(rest);
220
+ };
221
+
222
+ const onTimeout = () => {
223
+ fail(new Error(`Upstream proxy CONNECT timeout after ${timeoutMs}ms`));
224
+ };
225
+
226
+ const onError = (error) => {
227
+ fail(error);
228
+ };
229
+
230
+ socket.on(connectEvent, onConnectReady);
231
+ socket.on("data", onData);
232
+ socket.on("timeout", onTimeout);
233
+ socket.on("error", onError);
234
+ });
235
+ }
236
+ }