Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Port80 support (#568)
Browse files Browse the repository at this point in the history
* Initial work to convert port 80 for bst

* Fixed linting issues

* Change port to 5001 for test to avoid restrictions on port 80 on CI

* Small changes based on PR comments
  • Loading branch information
jkelvie authored Mar 13, 2019
1 parent 2a95916 commit 5d277e1
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 183 deletions.
14 changes: 10 additions & 4 deletions bin/bst-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import {BespokeServer} from "../lib/server/bespoke-server";
import * as program from "commander";

program
.command("start <webhookPort> <nodePort>")
.command("start <webhookPort> <nodePorts ...>")
.description("Starts the BST server")
.action(function () {
let webhookPort: number = parseInt(process.argv[3]);
let serverPort: number = parseInt(process.argv[4]);
let bespokeServer = new BespokeServer(webhookPort, serverPort);
const webhookPort = parseInt(process.argv[3]);

// We typically listen on multiple ports - 5000 and 80
// All the args after the webhook port are treated as node (tunnel) ports
const serverPorts: number[] = [];
for (let i = 4; i < process.argv.length; i++) {
serverPorts.push(parseInt(process.argv[i]));
}
const bespokeServer = new BespokeServer(webhookPort, serverPorts);
bespokeServer.start();
});

Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ RUN npm -version

RUN npm install

CMD node bin/bst-server.js start 443 5000
CMD node bin/bst-server.js start 443 5000 80
2 changes: 1 addition & 1 deletion lib/client/bst-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class BSTProxy {

private isSecure: boolean = false;
private bespokenHost: string = "proxy.bespoken.tools";
private bespokenPort: number = 5000;
private bespokenPort: number = 80;
private functionFile: string;
private functionName: string;
private httpPort: number;
Expand Down
40 changes: 12 additions & 28 deletions lib/server/bespoke-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,19 @@ export class BespokeServer {
private webhookManager: WebhookManager;
private uncaughtExceptionHandler: (err: Error) => void;

public constructor (private webhookPort: number, private nodePort: number) {}
public constructor (private webhookPort: number, private nodePorts: number[]) {}

public start (started?: () => void): void {
public async start (): Promise<void> {
BstStatistics.instance().start();
let self = this;
console.error("AWS_KEY: " + process.env["AWS_ACCESS_KEY_ID"]);

let count = 0;
// Make sure both NodeManager and WebhookManager have started
let callbackCounter = function () {
count++;
if (count === 2) {
if (started !== undefined && started !== null) {
started();
}
}
};

this.nodeManager = new NodeManager(this.nodePort);
this.nodeManager.start(callbackCounter);
this.nodeManager = new NodeManager(this.nodePorts);
await this.nodeManager.start();

this.webhookManager = new WebhookManager(this.webhookPort);
this.webhookManager.start(callbackCounter);
await this.webhookManager.start();

this.webhookManager.onWebhookReceived = function(webhookRequest: WebhookRequest) {
// Check if this is a ping
if (webhookRequest.isPing()) {
Expand Down Expand Up @@ -80,23 +70,17 @@ export class BespokeServer {
};

process.on("uncaughtException", this.uncaughtExceptionHandler);
process.on("unhandledRejection", this.uncaughtExceptionHandler);
}

public stop(callback: () => void): void {
public async stop(): Promise<void> {
BstStatistics.instance().stop();
// Use a counter to see that both callbacks have completed
// The beauty of Node - use non-synchronized counter variables like this safely :-)
let count = 0;
let callbackFunction = function () {
count++;
if (count === 2) {
callback();
}
};

LoggingHelper.info(Logger, "BespokenServer STOP");
process.removeListener("uncaughtException", this.uncaughtExceptionHandler);
this.nodeManager.stop(callbackFunction);
this.webhookManager.stop(callbackFunction);
process.removeListener("unhandledRejection", this.uncaughtExceptionHandler);
await this.nodeManager.stop();
await this.webhookManager.stop();
}

}
117 changes: 70 additions & 47 deletions lib/server/node-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,64 @@ export interface OnConnectCallback {
(node: Node): void;
}

// The node manager handles the TCP tunnels from the clients
// The node manager can (and typically does) listen on ports 5000 and 80
export class NodeManager {
public host: string = "0.0.0.0";
public nodes: {[id: string]: Node } = {};
private servers: NodeServer[];
public onConnect: OnConnectCallback = null;
public onNodeRemoved: (node: Node) => void = null; // Added for testability

private nodes: {[id: string]: Node } = {};
private server: Server;

constructor(private port: number) {}
constructor(private ports: number[]) {}

public node (nodeID: string): Node {
return this.nodes[nodeID];
}

public start (callback?: () => void) {
const self = this;
this.server = net.createServer(function(socket: Socket) {
public async start (): Promise<void> {
this.servers = [];
// Create listeners on as many ports as specified
for (const port of this.ports) {
const server = new NodeServer(this, port);
await server.start();
this.servers.push(server);
}
}

public static onKeepAliveReceived(node: Node): void {
// Reply with the same message on a Keep Alive
node.socketHandler.send(new SocketMessage(Global.KeepAliveMessage));
}

/**
* Calling stop tells the server to stop listening
* However, connections are not closed until all sockets disconnect, so loop through sockets and force a disconnect
* @param callback
*/
public async stop (): Promise<void> {
for (let key of Object.keys(this.nodes)) {
const node: Node = this.node(key);
node.socketHandler.disconnect();
LoggingHelper.info(Logger, "NODE CLOSING: " + node.id);
}

for (const server of this.servers) {
await server.stop();
}
}
}

// Sets up a listener for handling tunnels on the specified port
class NodeServer {
private server: Server;
public constructor(private nodeManager: NodeManager, private port: number) {}

public start(): Promise<void> {
this.server = net.createServer((socket: Socket) => {
let initialConnection = true;
let node: Node = null;
const socketHandler = new SocketHandler(socket, function(socketMessage: SocketMessage) {
const socketHandler = new SocketHandler(socket, (socketMessage: SocketMessage) => {
// We do special handling when we first connect
const strMessage: string = socketMessage.asString();

Expand All @@ -46,13 +84,13 @@ export class NodeManager {
const connectData = socketMessage.asJSON();

node = new Node(connectData.id, socketHandler);
self.nodes[node.id] = node;
this.nodeManager.nodes[node.id] = node;

socketHandler.send(new SocketMessage("ACK"));
initialConnection = false;

if (self.onConnect != null) {
self.onConnect(node);
if (this.nodeManager.onConnect != null) {
this.nodeManager.onConnect(node);
}

// Capture the connection
Expand All @@ -67,56 +105,41 @@ export class NodeManager {
});

// When the socket closes, remove it from the dictionary
socketHandler.onCloseCallback = function() {
socketHandler.onCloseCallback = () => {
if (node !== null) {
LoggingHelper.info(Logger, "NODE CLOSED: " + node.id);
delete self.nodes[node.id];
if (self.onNodeRemoved !== undefined && self.onNodeRemoved !== null) {
self.onNodeRemoved(node);
delete this.nodeManager.nodes[node.id];
if (this.nodeManager.onNodeRemoved !== undefined && this.nodeManager.onNodeRemoved !== null) {
this.nodeManager.onNodeRemoved(node);
}
}
};

// We have a connection - a socket object is assigned to the connection automatically
LoggingHelper.info(Logger, "NODE CONNECTED: " + socket.remoteAddress + ":" + socket.remotePort);

}).listen(this.port, this.host);
}).listen(this.port, this.nodeManager.host);

// Make a callback when the server starts up
this.server.on("listening", function () {
// On startup, call callback if defined
if (callback !== undefined && callback !== null) {
callback();
}
return new Promise((resolve) => {
this.server.on("listening", () => {
LoggingHelper.info(Logger, "NodeMgr Listening on " + this.nodeManager.host + ":" + this.port);
resolve();
});
});

LoggingHelper.info(Logger, "Listening on " + this.host + ":" + this.port);
}

private static onKeepAliveReceived(node: Node): void {
// Reply with the same message on a Keep Alive
node.socketHandler.send(new SocketMessage(Global.KeepAliveMessage));
}

/**
* Calling stop tells the server to stop listening
* However, connections are not closed until all sockets disconnect, so loop through sockets and force a disconnect
* @param callback
*/
public stop (callback: () => void): void {
for (let key of Object.keys(this.nodes)) {
const node: Node = this.node(key);
node.socketHandler.disconnect();
LoggingHelper.info(Logger, "NODE CLOSING: " + node.id);
}

this.server.close(function (error: any) {
if (error !== undefined) {
LoggingHelper.error(Logger, "ERROR! NodeManager not stopped: " + error);
} else {
LoggingHelper.info(Logger, "STOPPED");
callback();
}
stop(): Promise<void> {
return new Promise((resolve, reject) => {
this.server.close(function (error: any) {
if (error !== undefined) {
LoggingHelper.error(Logger, "ERROR! NodeManager not stopped: " + error);
reject(error);
} else {
LoggingHelper.info(Logger, "STOPPED");
resolve();
}
});
});
}
}
36 changes: 17 additions & 19 deletions lib/server/webhook-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,19 @@ export class WebhookManager {
this.host = "0.0.0.0";
}

public start(started?: () => void): void {
let self = this;

public start(): Promise<void> {
let socketIndex = 0;

const connectFunction = function(socket) {
const connectFunction = (socket) => {
let webhookRequest = new WebhookRequest(socket);
socketIndex++;
let socketKey = socketIndex;
self.socketMap[socketIndex] = socket;
this.socketMap[socketIndex] = socket;

socket.on("data", function(data: Buffer) {
socket.on("data", (data: Buffer) => {
webhookRequest.append(data);
if (webhookRequest.done()) {
self.onWebhookReceived(webhookRequest);
this.onWebhookReceived(webhookRequest);

// The calling socket just seems to stay open some times
// Would like to force it closed but don't know when to do it
Expand All @@ -45,8 +43,8 @@ export class WebhookManager {
}
});

socket.on("close", function () {
delete self.socketMap[socketKey];
socket.on("close", () => {
delete this.socketMap[socketKey];
});

};
Expand All @@ -68,25 +66,25 @@ export class WebhookManager {
this.server.on("secureConnection", connectFunction);
}

this.server.on("listening", function () {
if (started !== undefined && started !== null) {
started();
}
return new Promise((resolve, reject) => {
this.server.on("listening", () => {
LoggingHelper.info(Logger, "Webhook Listening on " + this.host + ":" + this.port);
resolve();
});
});
LoggingHelper.info(Logger, "Listening on " + this.host + ":" + this.port);
}

public stop (callback?: () => void): void {
public stop (): Promise<void> {
let self: WebhookManager = this;
for (let key in self.socketMap) {
let socket = self.socketMap[key];
socket.end();
}

this.server.close(function () {
if (callback !== undefined && callback !== null) {
callback();
}
return new Promise((resolve, reject) => {
this.server.close(function () {
resolve();
});
});
}
}
2 changes: 1 addition & 1 deletion test/alexa/sample-utterances-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe("SamplesUtterances", function() {

it("Passes error when bad file with no sample", function(done) {
SampleUtterances.fromFile("test/alexa/resources/SampleUtterancesNoUtterance.txt", function (utterances: SampleUtterances, error?: string) {
assert.equal(error, "Invalid sample utterance: AnotherTest");
assert.equal(error.trim(), "Invalid sample utterance: AnotherTest");
done();
});
});
Expand Down
7 changes: 4 additions & 3 deletions test/bin/bst-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ describe("bst-server", function() {

describe("start command", function() {
it("Starts", function(done) {
process.argv = command("node bst-server.js start 4000 5000");
process.argv = command("node bst-server.js start 4000 5000 80");
mockery.registerMock("../lib/server/bespoke-server", {
BespokeServer: function (port: number, port2: number) {
BespokeServer: function (port: number, ports: number[]) {
assert.equal(port, 4000);
assert.equal(port2, 5000);
assert.equal(ports[0], 5000);
assert.equal(ports[1], 80);

this.start = function () {
done();
Expand Down
Loading

0 comments on commit 5d277e1

Please sign in to comment.