Skip to content

Commit ae61562

Browse files
authored
Merge pull request #1364 from murgatroid99/grpc-js_uri_parsing
grpc-js: Use a more structured representation of URIs internally
2 parents 238a91c + 98e4626 commit ae61562

12 files changed

+325
-191
lines changed

packages/grpc-js/src/channel.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { trace, log } from './logging';
3939
import { SubchannelAddress } from './subchannel';
4040
import { MaxMessageSizeFilterFactory } from './max-message-size-filter';
4141
import { mapProxyName } from './http_proxy';
42+
import { GrpcUri, parseUri, uriToString } from './uri-parser';
4243

4344
export enum ConnectivityState {
4445
CONNECTING,
@@ -136,8 +137,9 @@ export class ChannelImplementation implements Channel {
136137
private connectivityStateWatchers: ConnectivityStateWatcher[] = [];
137138
private defaultAuthority: string;
138139
private filterStackFactory: FilterStackFactory;
140+
private target: GrpcUri;
139141
constructor(
140-
private target: string,
142+
target: string,
141143
private readonly credentials: ChannelCredentials,
142144
private readonly options: ChannelOptions
143145
) {
@@ -164,14 +166,24 @@ export class ChannelImplementation implements Channel {
164166
);
165167
}
166168
}
169+
const originalTargetUri = parseUri(target);
170+
if (originalTargetUri === null) {
171+
throw new Error(`Could not parse target name "${target}"`);
172+
}
167173
if (this.options['grpc.default_authority']) {
168174
this.defaultAuthority = this.options['grpc.default_authority'] as string;
169175
} else {
170-
this.defaultAuthority = getDefaultAuthority(target);
176+
this.defaultAuthority = getDefaultAuthority(originalTargetUri);
171177
}
172-
const proxyMapResult = mapProxyName(target, options);
178+
const proxyMapResult = mapProxyName(originalTargetUri, options);
173179
this.target = proxyMapResult.target;
174180
this.options = Object.assign({}, this.options, proxyMapResult.extraOptions);
181+
182+
const targetUri = parseUri(target);
183+
if (targetUri === null) {
184+
throw new Error(`Could not parse target name "${target}"`);
185+
}
186+
this.target = targetUri;
175187
/* The global boolean parameter to getSubchannelPool has the inverse meaning to what
176188
* the grpc.use_local_subchannel_pool channel option means. */
177189
this.subchannelPool = getSubchannelPool(
@@ -422,7 +434,7 @@ export class ChannelImplementation implements Channel {
422434
}
423435

424436
getTarget() {
425-
return this.target;
437+
return uriToString(this.target);
426438
}
427439

428440
getConnectivityState(tryToConnect: boolean) {

packages/grpc-js/src/http_proxy.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
*
1616
*/
1717

18-
import { URL } from 'url';
1918
import { log } from './logging';
2019
import { LogVerbosity } from './constants';
2120
import { getDefaultAuthority } from './resolver';
22-
import { parseTarget } from './resolver-dns';
2321
import { Socket } from 'net';
2422
import * as http from 'http';
2523
import * as tls from 'tls';
@@ -30,6 +28,7 @@ import {
3028
subchannelAddressToString,
3129
} from './subchannel';
3230
import { ChannelOptions } from './channel-options';
31+
import { GrpcUri, parseUri, splitHostPort, uriToString } from './uri-parser';
3332

3433
const TRACER_NAME = 'proxy';
3534

@@ -61,31 +60,30 @@ function getProxyInfo(): ProxyInfo {
6160
} else {
6261
return {};
6362
}
64-
let proxyUrl: URL;
65-
try {
66-
proxyUrl = new URL(proxyEnv);
67-
} catch (e) {
63+
const proxyUrl = parseUri(proxyEnv);
64+
if (proxyUrl === null) {
6865
log(LogVerbosity.ERROR, `cannot parse value of "${envVar}" env var`);
6966
return {};
7067
}
71-
if (proxyUrl.protocol !== 'http:') {
68+
if (proxyUrl.scheme !== 'http') {
7269
log(
7370
LogVerbosity.ERROR,
74-
`"${proxyUrl.protocol}" scheme not supported in proxy URI`
71+
`"${proxyUrl.scheme}" scheme not supported in proxy URI`
7572
);
7673
return {};
7774
}
75+
const splitPath = proxyUrl.path.split('@');
76+
let host: string;
7877
let userCred: string | null = null;
79-
if (proxyUrl.username) {
80-
if (proxyUrl.password) {
81-
log(LogVerbosity.INFO, 'userinfo found in proxy URI');
82-
userCred = `${proxyUrl.username}:${proxyUrl.password}`;
83-
} else {
84-
userCred = proxyUrl.username;
85-
}
78+
if (splitPath.length === 2) {
79+
log(LogVerbosity.INFO, 'userinfo found in proxy URI');
80+
userCred = splitPath[0];
81+
host = splitPath[1];
82+
} else {
83+
host = proxyUrl.path;
8684
}
8785
const result: ProxyInfo = {
88-
address: proxyUrl.host,
86+
address: host,
8987
};
9088
if (userCred) {
9189
result.creds = userCred;
@@ -113,12 +111,12 @@ function getNoProxyHostList(): string[] {
113111
}
114112

115113
export interface ProxyMapResult {
116-
target: string;
114+
target: GrpcUri;
117115
extraOptions: ChannelOptions;
118116
}
119117

120118
export function mapProxyName(
121-
target: string,
119+
target: GrpcUri,
122120
options: ChannelOptions
123121
): ProxyMapResult {
124122
const noProxyResult: ProxyMapResult = {
@@ -129,32 +127,32 @@ export function mapProxyName(
129127
if (!proxyInfo.address) {
130128
return noProxyResult;
131129
}
132-
const parsedTarget = parseTarget(target);
133-
if (!parsedTarget) {
130+
const hostPort = splitHostPort(target.path);
131+
if (!hostPort) {
134132
return noProxyResult;
135133
}
136-
const serverHost = parsedTarget.host;
134+
const serverHost = hostPort.host;
137135
for (const host of getNoProxyHostList()) {
138136
if (host === serverHost) {
139137
trace('Not using proxy for target in no_proxy list: ' + target);
140138
return noProxyResult;
141139
}
142140
}
143141
const extraOptions: ChannelOptions = {
144-
'grpc.http_connect_target': target,
142+
'grpc.http_connect_target': uriToString(target),
145143
};
146144
if (proxyInfo.creds) {
147145
extraOptions['grpc.http_connect_creds'] = proxyInfo.creds;
148146
}
149147
return {
150-
target: `dns:${proxyInfo.address}`,
148+
target: { path: proxyInfo.address },
151149
extraOptions: extraOptions,
152150
};
153151
}
154152

155153
export interface ProxyConnectionResult {
156154
socket?: Socket;
157-
realTarget?: string;
155+
realTarget?: GrpcUri;
158156
}
159157

160158
export function getProxiedConnection(
@@ -166,9 +164,13 @@ export function getProxiedConnection(
166164
return Promise.resolve<ProxyConnectionResult>({});
167165
}
168166
const realTarget = channelOptions['grpc.http_connect_target'] as string;
169-
const parsedTarget = parseTarget(realTarget)!;
167+
const parsedTarget = parseUri(realTarget);
168+
if (parsedTarget === null) {
169+
return Promise.resolve<ProxyConnectionResult>({});
170+
}
170171
const options: http.RequestOptions = {
171172
method: 'CONNECT',
173+
path: parsedTarget.path,
172174
};
173175
// Connect to the subchannel address as a proxy
174176
if (isTcpSubchannelAddress(address)) {
@@ -177,11 +179,6 @@ export function getProxiedConnection(
177179
} else {
178180
options.socketPath = address.path;
179181
}
180-
if (parsedTarget.port === undefined) {
181-
options.path = parsedTarget.host;
182-
} else {
183-
options.path = `${parsedTarget.host}:${parsedTarget.port}`;
184-
}
185182
if ('grpc.http_connect_creds' in channelOptions) {
186183
options.headers = {
187184
'Proxy-Authorization':
@@ -205,23 +202,27 @@ export function getProxiedConnection(
205202
' through proxy ' +
206203
proxyAddressString
207204
);
205+
resolve({
206+
socket,
207+
realTarget: parsedTarget,
208+
});
208209
if ('secureContext' in connectionOptions) {
209210
/* The proxy is connecting to a TLS server, so upgrade this socket
210211
* connection to a TLS connection.
211212
* This is a workaround for https://github.com/nodejs/node/issues/32922
212213
* See https://github.com/grpc/grpc-node/pull/1369 for more info. */
213214
const cts = tls.connect({
214215
...connectionOptions,
215-
host: getDefaultAuthority(realTarget),
216+
host: getDefaultAuthority(parsedTarget),
216217
socket: socket,
217218
}, () => {
218-
resolve({ socket: cts, realTarget });
219+
resolve({ socket: cts, realTarget: parsedTarget });
219220
}
220221
);
221222
} else {
222223
resolve({
223224
socket,
224-
realTarget,
225+
realTarget: parsedTarget,
225226
});
226227
}
227228
} else {

0 commit comments

Comments
 (0)