UNPKG

9.36 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2019 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18Object.defineProperty(exports, "__esModule", { value: true });
19exports.getProxiedConnection = exports.mapProxyName = void 0;
20const logging_1 = require("./logging");
21const constants_1 = require("./constants");
22const resolver_1 = require("./resolver");
23const http = require("http");
24const tls = require("tls");
25const logging = require("./logging");
26const subchannel_address_1 = require("./subchannel-address");
27const uri_parser_1 = require("./uri-parser");
28const url_1 = require("url");
29const TRACER_NAME = 'proxy';
30function trace(text) {
31 logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, text);
32}
33function getProxyInfo() {
34 let proxyEnv = '';
35 let envVar = '';
36 /* Prefer using 'grpc_proxy'. Fallback on 'http_proxy' if it is not set.
37 * Also prefer using 'https_proxy' with fallback on 'http_proxy'. The
38 * fallback behavior can be removed if there's a demand for it.
39 */
40 if (process.env.grpc_proxy) {
41 envVar = 'grpc_proxy';
42 proxyEnv = process.env.grpc_proxy;
43 }
44 else if (process.env.https_proxy) {
45 envVar = 'https_proxy';
46 proxyEnv = process.env.https_proxy;
47 }
48 else if (process.env.http_proxy) {
49 envVar = 'http_proxy';
50 proxyEnv = process.env.http_proxy;
51 }
52 else {
53 return {};
54 }
55 let proxyUrl;
56 try {
57 proxyUrl = new url_1.URL(proxyEnv);
58 }
59 catch (e) {
60 logging_1.log(constants_1.LogVerbosity.ERROR, `cannot parse value of "${envVar}" env var`);
61 return {};
62 }
63 if (proxyUrl.protocol !== 'http:') {
64 logging_1.log(constants_1.LogVerbosity.ERROR, `"${proxyUrl.protocol}" scheme not supported in proxy URI`);
65 return {};
66 }
67 let userCred = null;
68 if (proxyUrl.username) {
69 if (proxyUrl.password) {
70 logging_1.log(constants_1.LogVerbosity.INFO, 'userinfo found in proxy URI');
71 userCred = `${proxyUrl.username}:${proxyUrl.password}`;
72 }
73 else {
74 userCred = proxyUrl.username;
75 }
76 }
77 const hostname = proxyUrl.hostname;
78 let port = proxyUrl.port;
79 /* The proxy URL uses the scheme "http:", which has a default port number of
80 * 80. We need to set that explicitly here if it is omitted because otherwise
81 * it will use gRPC's default port 443. */
82 if (port === '') {
83 port = '80';
84 }
85 const result = {
86 address: `${hostname}:${port}`,
87 };
88 if (userCred) {
89 result.creds = userCred;
90 }
91 trace('Proxy server ' + result.address + ' set by environment variable ' + envVar);
92 return result;
93}
94function getNoProxyHostList() {
95 /* Prefer using 'no_grpc_proxy'. Fallback on 'no_proxy' if it is not set. */
96 let noProxyStr = process.env.no_grpc_proxy;
97 let envVar = 'no_grpc_proxy';
98 if (!noProxyStr) {
99 noProxyStr = process.env.no_proxy;
100 envVar = 'no_proxy';
101 }
102 if (noProxyStr) {
103 trace('No proxy server list set by environment variable ' + envVar);
104 return noProxyStr.split(',');
105 }
106 else {
107 return [];
108 }
109}
110function mapProxyName(target, options) {
111 var _a;
112 const noProxyResult = {
113 target: target,
114 extraOptions: {},
115 };
116 if (((_a = options['grpc.enable_http_proxy']) !== null && _a !== void 0 ? _a : 1) === 0) {
117 return noProxyResult;
118 }
119 if (target.scheme === 'unix') {
120 return noProxyResult;
121 }
122 const proxyInfo = getProxyInfo();
123 if (!proxyInfo.address) {
124 return noProxyResult;
125 }
126 const hostPort = uri_parser_1.splitHostPort(target.path);
127 if (!hostPort) {
128 return noProxyResult;
129 }
130 const serverHost = hostPort.host;
131 for (const host of getNoProxyHostList()) {
132 if (host === serverHost) {
133 trace('Not using proxy for target in no_proxy list: ' + uri_parser_1.uriToString(target));
134 return noProxyResult;
135 }
136 }
137 const extraOptions = {
138 'grpc.http_connect_target': uri_parser_1.uriToString(target),
139 };
140 if (proxyInfo.creds) {
141 extraOptions['grpc.http_connect_creds'] = proxyInfo.creds;
142 }
143 return {
144 target: {
145 scheme: 'dns',
146 path: proxyInfo.address,
147 },
148 extraOptions: extraOptions,
149 };
150}
151exports.mapProxyName = mapProxyName;
152function getProxiedConnection(address, channelOptions, connectionOptions) {
153 if (!('grpc.http_connect_target' in channelOptions)) {
154 return Promise.resolve({});
155 }
156 const realTarget = channelOptions['grpc.http_connect_target'];
157 const parsedTarget = uri_parser_1.parseUri(realTarget);
158 if (parsedTarget === null) {
159 return Promise.resolve({});
160 }
161 const options = {
162 method: 'CONNECT',
163 path: parsedTarget.path,
164 };
165 const headers = {
166 Host: parsedTarget.path,
167 };
168 // Connect to the subchannel address as a proxy
169 if (subchannel_address_1.isTcpSubchannelAddress(address)) {
170 options.host = address.host;
171 options.port = address.port;
172 }
173 else {
174 options.socketPath = address.path;
175 }
176 if ('grpc.http_connect_creds' in channelOptions) {
177 headers['Proxy-Authorization'] =
178 'Basic ' +
179 Buffer.from(channelOptions['grpc.http_connect_creds']).toString('base64');
180 }
181 options.headers = headers;
182 const proxyAddressString = subchannel_address_1.subchannelAddressToString(address);
183 trace('Using proxy ' + proxyAddressString + ' to connect to ' + options.path);
184 return new Promise((resolve, reject) => {
185 const request = http.request(options);
186 request.once('connect', (res, socket, head) => {
187 var _a;
188 request.removeAllListeners();
189 socket.removeAllListeners();
190 if (res.statusCode === 200) {
191 trace('Successfully connected to ' +
192 options.path +
193 ' through proxy ' +
194 proxyAddressString);
195 if ('secureContext' in connectionOptions) {
196 /* The proxy is connecting to a TLS server, so upgrade this socket
197 * connection to a TLS connection.
198 * This is a workaround for https://github.com/nodejs/node/issues/32922
199 * See https://github.com/grpc/grpc-node/pull/1369 for more info. */
200 const targetPath = resolver_1.getDefaultAuthority(parsedTarget);
201 const hostPort = uri_parser_1.splitHostPort(targetPath);
202 const remoteHost = (_a = hostPort === null || hostPort === void 0 ? void 0 : hostPort.host) !== null && _a !== void 0 ? _a : targetPath;
203 const cts = tls.connect(Object.assign({ host: remoteHost, servername: remoteHost, socket: socket }, connectionOptions), () => {
204 trace('Successfully established a TLS connection to ' +
205 options.path +
206 ' through proxy ' +
207 proxyAddressString);
208 resolve({ socket: cts, realTarget: parsedTarget });
209 });
210 cts.on('error', (error) => {
211 trace('Failed to establish a TLS connection to ' +
212 options.path +
213 ' through proxy ' +
214 proxyAddressString +
215 ' with error ' +
216 error.message);
217 reject();
218 });
219 }
220 else {
221 trace('Successfully established a plaintext connection to ' +
222 options.path +
223 ' through proxy ' +
224 proxyAddressString);
225 resolve({
226 socket,
227 realTarget: parsedTarget,
228 });
229 }
230 }
231 else {
232 logging_1.log(constants_1.LogVerbosity.ERROR, 'Failed to connect to ' +
233 options.path +
234 ' through proxy ' +
235 proxyAddressString +
236 ' with status ' +
237 res.statusCode);
238 reject();
239 }
240 });
241 request.once('error', (err) => {
242 request.removeAllListeners();
243 logging_1.log(constants_1.LogVerbosity.ERROR, 'Failed to connect to proxy ' +
244 proxyAddressString +
245 ' with error ' +
246 err.message);
247 reject();
248 });
249 request.end();
250 });
251}
252exports.getProxiedConnection = getProxiedConnection;
253//# sourceMappingURL=http_proxy.js.map
\No newline at end of file