Skip to content

Commit

Permalink
[event-hubs] re-add support for custom endpoint address (Azure#13287)
Browse files Browse the repository at this point in the history
We had reverted the custom endpoint address changes in Azure#13096 so that we could do a core-amqp release.

This PR reverts that revert and removes the 'beta' moniker from the event hubs version number.

Original PR: Azure#12909
  • Loading branch information
chradek authored and ljian3377 committed Jan 22, 2021
1 parent 1192e08 commit 12859d0
Show file tree
Hide file tree
Showing 19 changed files with 519 additions and 18 deletions.
10 changes: 8 additions & 2 deletions sdk/core/core-amqp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# Release History

## 2.0.2 (Unreleased)

## 2.1.0 (Unreleased)

- Adds the ability to configure the `amqpHostname` and `port` that a `ConnectionContextBase` will use when connecting to a service.
The `host` field refers to the DNS host or IP address of the service, whereas the `amqpHostname`
is the fully qualified host name of the service. Normally `host` and `amqpHostname` will be the same.
However if your network does not allow connecting to the service via the public host,
you can specify a custom host (e.g. an application gateway) via the `host` field and continue
using the public host as the `amqpHostname`.

## 2.0.1 (2021-01-07)

Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-amqp/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@azure/core-amqp",
"sdk-type": "client",
"version": "2.0.2",
"version": "2.1.0",
"description": "Common library for amqp based azure sdks like @azure/event-hubs.",
"author": "Microsoft Corporation",
"license": "MIT",
Expand Down
2 changes: 2 additions & 0 deletions sdk/core/core-amqp/review/core-amqp.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ export enum ConditionErrorNameMapper {

// @public
export interface ConnectionConfig {
amqpHostname?: string;
connectionString: string;
endpoint: string;
entityPath?: string;
host: string;
port?: number;
sharedAccessKey: string;
sharedAccessKeyName: string;
webSocket?: WebSocketImpl;
Expand Down
7 changes: 4 additions & 3 deletions sdk/core/core-amqp/src/ConnectionContextBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ export const ConnectionContextBase = {
const connectionOptions: ConnectionOptions = {
transport: Constants.TLS,
host: parameters.config.host,
hostname: parameters.config.host,
hostname: parameters.config.amqpHostname ?? parameters.config.host,
username: parameters.config.sharedAccessKeyName,
port: 5671,
port: parameters.config.port ?? 5671,
reconnect: false,
properties: {
product: parameters.connectionProperties.product,
Expand All @@ -147,10 +147,11 @@ export const ConnectionContextBase = {
const host = parameters.config.host;
const endpoint = parameters.config.webSocketEndpointPath || "";
const socketOptions = parameters.config.webSocketConstructorOptions || {};
const port = parameters.config.port ?? 443;

connectionOptions.webSocketOptions = {
webSocket: socket,
url: `wss://${host}:443/${endpoint}`,
url: `wss://${host}:${port}/${endpoint}`,
protocol: ["AMQPWSB10"],
options: socketOptions
};
Expand Down
17 changes: 15 additions & 2 deletions sdk/core/core-amqp/src/connectionConfig/connectionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,22 @@ export interface ConnectionConfig {
*/
endpoint: string;
/**
* @property {string} host - The host "<yournamespace>.servicebus.windows.net".
* The DNS hostname or IP address of the service.
* Typically of the form "<yournamespace>.servicebus.windows.net" unless connecting
* to the service through an intermediary.
*/
host: string;
/**
* The fully qualified name of the host to connect to.
* This field can be used by AMQP proxies to determine the correct back-end service to
* connect the client to.
* Typically of the form "<yournamespace>.servicebus.windows.net".
*/
amqpHostname?: string;
/**
* The port number.
*/
port?: number;
/**
* @property {string} connectionString - The connection string.
*/
Expand Down Expand Up @@ -135,7 +148,7 @@ export const ConnectionConfig = {
throw new TypeError("Missing 'entityPath' in configuration");
}
if (config.entityPath != undefined) {
config.entityPath = String(config.entityPath);
config.entityPath = String(config.entityPath);
}

if (!isSharedAccessSignature(config.connectionString)) {
Expand Down
209 changes: 209 additions & 0 deletions sdk/core/core-amqp/test/context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,215 @@ describe("ConnectionContextBase", function() {
done();
});

it("should set host and hostname to the same value by default", function() {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.hostname!.should.equal("hostname.servicebus.windows.net");
context.connection.options.host!.should.equal("hostname.servicebus.windows.net");
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.cbsSession.should.instanceOf(CbsClient);
});

it("should allow setting host and hostname to different values", function() {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
config.amqpHostname = "127.0.0.1";
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.hostname!.should.equal("127.0.0.1");
context.connection.options.host!.should.equal("hostname.servicebus.windows.net");
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.cbsSession.should.instanceOf(CbsClient);
});

it("should allow specifying a port", function() {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
config.port = 1111;
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.port!.should.equal(1111);
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.cbsSession.should.instanceOf(CbsClient);
});

it("should have a default port (5671)", function() {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.port!.should.equal(5671);
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.cbsSession.should.instanceOf(CbsClient);
});

it("should allow setting host and hostname to different values when using websockets", function() {
const websockets: any = () => {};
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
config.webSocket = websockets;
config.amqpHostname = config.host;
config.host = "127.0.0.1";
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.host!.should.equal("127.0.0.1");
context.connection.options.hostname!.should.equal("hostname.servicebus.windows.net");
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.connection.options.webSocketOptions!.url.should.equal(`wss://127.0.0.1:443/`);
context.cbsSession.should.instanceOf(CbsClient);
});

it("should have a default port when using websockets (443)", function() {
const websockets: any = () => {};
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
config.webSocket = websockets;
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.connection.options.webSocketOptions!.url.should.equal(
`wss://hostname.servicebus.windows.net:443/`
);
context.cbsSession.should.instanceOf(CbsClient);
});

it("should allow specifying a port when using websockets", function() {
const websockets: any = () => {};
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const path = "mypath";
const config = ConnectionConfig.create(connectionString, path);
config.webSocket = websockets;
config.port = 1111;
const context = ConnectionContextBase.create({
config: config,
connectionProperties: {
product: "MSJSClient",
userAgent: "/js-amqp-client",
version: "1.0.0"
}
});
should.exist(context.config);
should.exist(context.connection);
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
context.connection.options.port!.should.equal(1111);
context.wasConnectionCloseCalled.should.equal(false);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
context.connection.options.properties!.version.should.equal("1.0.0");
context.connection.options.webSocketOptions!.url.should.equal(
`wss://hostname.servicebus.windows.net:1111/`
);
context.cbsSession.should.instanceOf(CbsClient);
});

if (isNode) {
it("should accept a websocket constructor in Node", async () => {
const connectionString =
Expand Down
8 changes: 7 additions & 1 deletion sdk/eventhub/event-hubs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Release History

## 5.3.2 (Unreleased)
## 5.4.0 (Unreleased)

- Adds the `customEndpointAddress` field to `EventHubClientOptions`.
This allows for specifying a custom endpoint to use when communicating
with the Event Hubs service, which is useful when your network does not
allow communicating to the standard Event Hubs endpoint.
Resolves [#12901](https://github.com/Azure/azure-sdk-for-js/issues/12901).

- Updates documentation for `EventData` to call out that the `body` field
must be converted to a byte array or `Buffer` when cross-language
Expand Down
4 changes: 2 additions & 2 deletions sdk/eventhub/event-hubs/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@azure/event-hubs",
"sdk-type": "client",
"version": "5.3.2",
"version": "5.4.0",
"description": "Azure Event Hubs SDK for JS.",
"author": "Microsoft Corporation",
"license": "MIT",
Expand Down Expand Up @@ -89,7 +89,7 @@
},
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-amqp": "^2.0.0",
"@azure/core-amqp": "^2.1.0",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@azure/core-tracing": "1.0.0-preview.9",
"@azure/core-auth": "^1.1.3",
Expand Down
1 change: 1 addition & 0 deletions sdk/eventhub/event-hubs/review/event-hubs.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface EventDataBatch {

// @public
export interface EventHubClientOptions {
customEndpointAddress?: string;
retryOptions?: RetryOptions;
userAgent?: string;
webSocketOptions?: WebSocketOptions;
Expand Down
2 changes: 1 addition & 1 deletion sdk/eventhub/event-hubs/rollup.base.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const input = "dist-esm/src/index.js";
const production = process.env.NODE_ENV === "production";

export function nodeConfig(test = false) {
const externalNodeBuiltins = ["events", "util", "os"];
const externalNodeBuiltins = ["events", "util", "os", "url"];
const baseConfig = {
input: input,
external: depNames.concat(externalNodeBuiltins),
Expand Down
4 changes: 4 additions & 0 deletions sdk/eventhub/event-hubs/src/connectionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,10 @@ export function createConnectionContext(
config = EventHubConnectionConfig.create(connectionString);
}

if (options?.customEndpointAddress) {
EventHubConnectionConfig.setCustomEndpointAddress(config, options.customEndpointAddress);
}

ConnectionConfig.validate(config);

return ConnectionContext.create(config, credential, options);
Expand Down
Loading

0 comments on commit 12859d0

Please sign in to comment.