Skip to content

Commit 998f8ee

Browse files
Grpc endpoint parsing (#543)
* Adds new parsing logic Signed-off-by: Elena Kolevska <[email protected]> * wip Signed-off-by: Elena Kolevska <[email protected]> * Cleanup Signed-off-by: Elena Kolevska <[email protected]> * http endpoint fix Signed-off-by: Elena Kolevska <[email protected]> * Fixes linter errors Signed-off-by: Elena Kolevska <[email protected]> * Fixes Signed-off-by: Elena Kolevska <[email protected]> * Reorganises code Signed-off-by: Elena Kolevska <[email protected]> * Fixes tests Signed-off-by: Elena Kolevska <[email protected]> * Removes unneeded test (after rebase) Signed-off-by: Elena Kolevska <[email protected]> * Fixes rebase manual merge Signed-off-by: Elena Kolevska <[email protected]> * Make dapr DaprClient.options not partial again Signed-off-by: Elena Kolevska <[email protected]> * Reorganises the network classes Signed-off-by: Elena Kolevska <[email protected]> * Fixes linter errors Signed-off-by: Elena Kolevska <[email protected]> * Apply suggestions from code review Co-authored-by: Shubham Sharma <[email protected]> Signed-off-by: Elena Kolevska <[email protected]> * Adds changes from review Signed-off-by: Elena Kolevska <[email protected]> * Apply suggestions from code review Co-authored-by: Shubham Sharma <[email protected]> Signed-off-by: Elena Kolevska <[email protected]> * WIP adding comments from review Signed-off-by: Elena Kolevska <[email protected]> * Fixes linter Signed-off-by: Elena Kolevska <[email protected]> * Fixes import Signed-off-by: Elena Kolevska <[email protected]> * Accounts for ipv6 addresses for http endpoints Signed-off-by: Elena Kolevska <[email protected]> * Fix Signed-off-by: Elena Kolevska <[email protected]> * Fixes linter Signed-off-by: Elena Kolevska <[email protected]> --------- Signed-off-by: Elena Kolevska <[email protected]> Signed-off-by: Elena Kolevska <[email protected]> Co-authored-by: Shubham Sharma <[email protected]>
1 parent d2cc38e commit 998f8ee

File tree

12 files changed

+972
-629
lines changed

12 files changed

+972
-629
lines changed

src/implementation/Client/DaprClient.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,22 @@ export default class DaprClient {
8787
private readonly logger: Logger;
8888

8989
constructor(options: Partial<DaprClientOptions> = {}) {
90-
this.options = getClientOptions(options, Settings.getDefaultCommunicationProtocol(), undefined);
91-
this.logger = new Logger("DaprClient", "DaprClient", this.options.logger);
92-
93-
// Validation on port
94-
if (this.options.daprPort && !/^[0-9]+$/.test(this.options.daprPort)) {
90+
options = getClientOptions(options, Settings.getDefaultCommunicationProtocol(), undefined);
91+
this.logger = new Logger("DaprClient", "DaprClient", options.logger);
92+
93+
// Legacy validation on port
94+
// URI validation is done later, when we instantiate the HttpEndpoint or GrpcEndpoint
95+
// object in the HttpClient or GrpcClient constructor, but we need to
96+
// keep this additional check for backward compatibility
97+
// TODO: Remove this validation in the next major version
98+
if (options?.daprPort && !/^[0-9]+$/.test(options?.daprPort)) {
9599
throw new Error("DAPR_INCORRECT_SIDECAR_PORT");
96100
}
97101

98102
// Builder
99103
switch (options.communicationProtocol) {
100104
case CommunicationProtocolEnum.GRPC: {
101-
const client = new GRPCClient(this.options);
105+
const client = new GRPCClient(options);
102106
this.daprClient = client;
103107

104108
this.state = new GRPCClientState(client);
@@ -119,7 +123,7 @@ export default class DaprClient {
119123
}
120124
case CommunicationProtocolEnum.HTTP:
121125
default: {
122-
const client = new HTTPClient(this.options);
126+
const client = new HTTPClient(options);
123127
this.daprClient = client;
124128

125129
this.actor = new HTTPClientActor(client); // we use an abstractor here since we interface through a builder with the Actor Runtime
@@ -139,6 +143,17 @@ export default class DaprClient {
139143
break;
140144
}
141145
}
146+
147+
this.options = {
148+
daprHost: this.daprClient.options.daprHost,
149+
daprPort: this.daprClient.options.daprPort,
150+
communicationProtocol: this.daprClient.options.communicationProtocol,
151+
isKeepAlive: options.isKeepAlive,
152+
logger: options.logger,
153+
actor: options.actor,
154+
daprApiToken: options.daprApiToken,
155+
maxBodySizeMb: options.maxBodySizeMb,
156+
};
142157
}
143158

144159
static create(client: IClient): DaprClient {

src/implementation/Client/GRPCClient/GRPCClient.ts

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { Logger } from "../../../logger/Logger";
2020
import GRPCClientSidecar from "./sidecar";
2121
import DaprClient from "../DaprClient";
2222
import { SDK_VERSION } from "../../../version";
23+
import communicationProtocolEnum from "../../../enum/CommunicationProtocol.enum";
24+
import { GrpcEndpoint } from "../../../network/GrpcEndpoint";
2325

2426
export default class GRPCClient implements IClient {
2527
readonly options: DaprClientOptions;
@@ -29,17 +31,34 @@ export default class GRPCClient implements IClient {
2931
private readonly clientCredentials: grpc.ChannelCredentials;
3032
private readonly logger: Logger;
3133
private readonly grpcClientOptions: Partial<grpc.ClientOptions>;
34+
private daprEndpoint: GrpcEndpoint;
35+
36+
constructor(options: Partial<DaprClientOptions>) {
37+
this.daprEndpoint = this.generateEndpoint(options);
38+
39+
this.options = {
40+
daprHost: this.daprEndpoint.hostname,
41+
daprPort: this.daprEndpoint.port,
42+
communicationProtocol: communicationProtocolEnum.GRPC,
43+
isKeepAlive: options?.isKeepAlive,
44+
logger: options?.logger,
45+
actor: options?.actor,
46+
daprApiToken: options?.daprApiToken,
47+
maxBodySizeMb: options?.maxBodySizeMb,
48+
};
3249

33-
constructor(options: DaprClientOptions) {
34-
this.options = options;
3550
this.clientCredentials = this.generateCredentials();
3651
this.grpcClientOptions = this.generateChannelOptions();
3752

3853
this.logger = new Logger("GRPCClient", "GRPCClient", options.logger);
3954
this.isInitialized = false;
4055

4156
this.logger.info(`Opening connection to ${this.options.daprHost}:${this.options.daprPort}`);
42-
this.client = this.generateClient(this.options.daprHost, this.options.daprPort);
57+
this.client = new GrpcDaprClient(
58+
this.daprEndpoint.endpoint,
59+
this.getClientCredentials(),
60+
this.getGrpcClientOptions(),
61+
);
4362
}
4463

4564
async getClient(requiresInitialization = true): Promise<GrpcDaprClient> {
@@ -59,8 +78,24 @@ export default class GRPCClient implements IClient {
5978
return this.grpcClientOptions;
6079
}
6180

81+
private generateEndpoint(options: Partial<DaprClientOptions>): GrpcEndpoint {
82+
const host = options?.daprHost ?? Settings.getDefaultHost();
83+
const port = options?.daprPort ?? Settings.getDefaultGrpcPort();
84+
let uri = `${host}:${port}`;
85+
86+
if (!(options?.daprHost || options?.daprPort)) {
87+
// If neither host nor port are specified, check the endpoint environment variable.
88+
const endpoint = Settings.getDefaultGrpcEndpoint();
89+
if (endpoint != "") {
90+
uri = endpoint;
91+
}
92+
}
93+
94+
return new GrpcEndpoint(uri);
95+
}
96+
6297
private generateCredentials(): grpc.ChannelCredentials {
63-
if (this.options.daprHost.startsWith("https")) {
98+
if (this.daprEndpoint?.tls) {
6499
return grpc.ChannelCredentials.createSsl();
65100
}
66101
return grpc.ChannelCredentials.createInsecure();
@@ -93,26 +128,6 @@ export default class GRPCClient implements IClient {
93128
return options;
94129
}
95130

96-
private generateClient(host: string, port: string): GrpcDaprClient {
97-
return new GrpcDaprClient(
98-
GRPCClient.getEndpoint(host, port),
99-
this.getClientCredentials(),
100-
this.getGrpcClientOptions(),
101-
);
102-
}
103-
104-
// The grpc client doesn't allow http:// or https:// for grpc connections,
105-
// so we need to remove it, if it exists
106-
static getEndpoint(host: string, port: string): string {
107-
let endpoint = `${host}:${port}`;
108-
const parts = endpoint.split("://");
109-
if (parts.length > 1 && parts[0].startsWith("http")) {
110-
endpoint = parts[1];
111-
}
112-
113-
return endpoint;
114-
}
115-
116131
private generateInterceptors(): (options: any, nextCall: any) => grpc.InterceptingCall {
117132
return (options: any, nextCall: any) => {
118133
return new grpc.InterceptingCall(nextCall(options), {

src/implementation/Client/HTTPClient/HTTPClient.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { Logger } from "../../../logger/Logger";
2323
import HTTPClientSidecar from "./sidecar";
2424
import { SDK_VERSION } from "../../../version";
2525
import * as SerializerUtil from "../../../utils/Serializer.util";
26+
import communicationProtocolEnum from "../../../enum/CommunicationProtocol.enum";
27+
import { HttpEndpoint } from "../../../network/HttpEndpoint";
2628

2729
export default class HTTPClient implements IClient {
2830
readonly options: DaprClientOptions;
@@ -34,16 +36,25 @@ export default class HTTPClient implements IClient {
3436

3537
private static httpAgent: http.Agent;
3638
private static httpsAgent: https.Agent;
39+
private daprEndpoint: HttpEndpoint;
40+
41+
constructor(options: Partial<DaprClientOptions>) {
42+
this.daprEndpoint = this.generateEndpoint(options);
43+
44+
this.options = {
45+
daprHost: this.daprEndpoint.hostname,
46+
daprPort: this.daprEndpoint.port,
47+
communicationProtocol: communicationProtocolEnum.HTTP,
48+
isKeepAlive: options?.isKeepAlive,
49+
logger: options?.logger,
50+
actor: options?.actor,
51+
daprApiToken: options?.daprApiToken,
52+
maxBodySizeMb: options?.maxBodySizeMb,
53+
};
3754

38-
constructor(options: DaprClientOptions) {
39-
this.options = options;
4055
this.logger = new Logger("HTTPClient", "HTTPClient", this.options.logger);
4156
this.isInitialized = false;
42-
43-
this.clientUrl = `${this.options.daprHost}:${this.options.daprPort}/v1.0`;
44-
if (!this.clientUrl.startsWith("http://") && !this.clientUrl.startsWith("https://")) {
45-
this.clientUrl = `http://${this.clientUrl}`;
46-
}
57+
this.clientUrl = `${this.daprEndpoint.endpoint}/v1.0`;
4758

4859
if (!HTTPClient.client) {
4960
HTTPClient.client = fetch;
@@ -63,6 +74,22 @@ export default class HTTPClient implements IClient {
6374
}
6475
}
6576

77+
private generateEndpoint(options: Partial<DaprClientOptions>): HttpEndpoint {
78+
const host = options?.daprHost ?? Settings.getDefaultHost();
79+
const port = options?.daprPort ?? Settings.getDefaultHttpPort();
80+
let uri = `${host}:${port}`;
81+
82+
if (!(options?.daprHost || options?.daprPort)) {
83+
// If neither host nor port are specified, check the endpoint environment variable.
84+
const endpoint = Settings.getDefaultHttpEndpoint();
85+
if (endpoint != "") {
86+
uri = endpoint;
87+
}
88+
}
89+
90+
return new HttpEndpoint(uri);
91+
}
92+
6693
async getClient(requiresInitialization = true): Promise<typeof fetch> {
6794
// Ensure the sidecar has been started
6895
if (requiresInitialization && !this.isInitialized) {

src/implementation/Server/DaprServer.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,38 @@ export default class DaprServer {
4848
constructor(serverOptions: Partial<DaprServerOptions> = {}) {
4949
const communicationProtocol = serverOptions.communicationProtocol ?? Settings.getDefaultCommunicationProtocol();
5050
const clientOptions = getClientOptions(serverOptions.clientOptions, communicationProtocol, serverOptions?.logger);
51+
52+
// Legacy validation on port
53+
// URI validation is done later, when we instantiate the HttpEndpoint or GrpcEndpoint
54+
// object in the HttpClient or GrpcClient constructor, but we need to
55+
// keep this additional check for backward compatibility
56+
// TODO: Remove this validation in the next major version
57+
if (clientOptions?.daprPort && !/^[0-9]+$/.test(clientOptions?.daprPort)) {
58+
throw new Error("DAPR_INCORRECT_SIDECAR_PORT");
59+
}
60+
61+
this.client = new DaprClient(clientOptions);
62+
5163
this.serverOptions = {
5264
serverHost: serverOptions.serverHost ?? Settings.getDefaultHost(),
5365
serverPort: serverOptions.serverPort ?? Settings.getDefaultAppPort(communicationProtocol),
5466
communicationProtocol: communicationProtocol,
5567
maxBodySizeMb: serverOptions.maxBodySizeMb,
5668
serverHttp: serverOptions.serverHttp,
57-
clientOptions: clientOptions,
69+
clientOptions: this.client.options,
5870
logger: serverOptions.logger,
5971
};
6072
// Create a client to interface with the sidecar from the server side
61-
this.client = new DaprClient(clientOptions);
6273

6374
// If DAPR_SERVER_PORT was not set, we set it
6475
process.env.DAPR_SERVER_PORT = this.serverOptions.serverPort;
65-
process.env.DAPR_CLIENT_PORT = clientOptions.daprPort;
76+
process.env.DAPR_CLIENT_PORT = this.client.options.daprPort;
6677

6778
// Validation on port
6879
if (!/^[0-9]+$/.test(this.serverOptions.serverPort)) {
6980
throw new Error("DAPR_INCORRECT_SERVER_PORT");
7081
}
7182

72-
if (!/^[0-9]+$/.test(clientOptions.daprPort)) {
73-
throw new Error("DAPR_INCORRECT_SIDECAR_PORT");
74-
}
75-
7683
// Builder
7784
switch (serverOptions.communicationProtocol) {
7885
case CommunicationProtocolEnum.GRPC: {

src/network/AbstractEndpoint.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright 2023 The Dapr Authors
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
export abstract class Endpoint {
15+
protected _scheme = "";
16+
protected _hostname = "";
17+
protected _port = 0;
18+
protected _tls = false;
19+
protected _url: string;
20+
protected _endpoint = "";
21+
22+
protected constructor(url: string) {
23+
this._url = url;
24+
}
25+
26+
get tls(): boolean {
27+
return this._tls;
28+
}
29+
30+
get hostname(): string {
31+
return this._hostname;
32+
}
33+
34+
get scheme(): string {
35+
return this._scheme;
36+
}
37+
38+
get port(): string {
39+
return this._port === 0 ? "" : this._port.toString();
40+
}
41+
42+
get endpoint(): string {
43+
return this._endpoint;
44+
}
45+
}

0 commit comments

Comments
 (0)