Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sample nodejs host #495

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch sample ts host",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\samples\\ts\\host\\out\\index.js",
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"console": "integratedTerminal"
}
]
}
1 change: 1 addition & 0 deletions samples/ts/host/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
out/
1 change: 1 addition & 0 deletions samples/ts/host/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registy=https://registry.npmjs.org/
6 changes: 6 additions & 0 deletions samples/ts/host/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"files.exclude": {
"out/": true,
"node_modules/": true,
}
}
156 changes: 156 additions & 0 deletions samples/ts/host/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import {
ManagementApiVersions,
ProductHeaderValue,
TunnelManagementHttpClient,
TunnelRequestOptions,
} from "@microsoft/dev-tunnels-management";
import {
Tunnel,
TunnelAccessControlEntryType,
TunnelAccessScopes,
TunnelEndpoint,
} from "@microsoft/dev-tunnels-contracts";
import {
TunnelRelayTunnelHost,
} from "@microsoft/dev-tunnels-connections";
import * as http from 'http';

const portNumber = 8080;
const protocol = "http";

function startServer(): Promise<http.Server> {
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello, world!\n');
});

return new Promise((resolve) => {
server.listen(portNumber, () => {
console.log(`Server running at ${protocol}://localhost:${portNumber}/`);
resolve(server);
})
});;
}

async function createTunnelAndConnectHost(tunnelManagementClient: TunnelManagementHttpClient) {
const newTunnel: Tunnel = {
ports: [{
portNumber,
protocol,
}],
accessControl: {
entries: [{
type: TunnelAccessControlEntryType.Anonymous,
subjects: [],
scopes: [TunnelAccessScopes.Connect],
}],
},
};

const tunnelRequestOptions: TunnelRequestOptions = {
tokenScopes: [TunnelAccessScopes.Host, TunnelAccessScopes.Connect],
includePorts: true,
};

const tunnel = await tunnelManagementClient.createTunnel(
newTunnel,
tunnelRequestOptions
);

const host = new TunnelRelayTunnelHost(tunnelManagementClient);
host.trace = (level, eventId, msg, err) => {
console.log(`host: ${msg}`);
};

await host.connect(tunnel!);

console.log('Tunnel host connected:');
const clientAccessToken = tunnel.accessTokens?.[TunnelAccessScopes.Connect];
if (clientAccessToken) {
console.log(` Client access token: ${clientAccessToken}`);
}

tunnel.ports?.forEach(({portNumber, portForwardingUris}) => {
console.log(` Tunnel port: ${portNumber}`);
if (portForwardingUris) {
portForwardingUris.forEach((uri) => console.log(` Port forwarding URI: ${uri}`));
} else {
newTunnel.endpoints?.forEach((endpoint) => {
console.log(` Port forwarding URI: ${TunnelEndpoint.getPortUri(endpoint, portNumber)}`);
});
}
});

return { tunnel, host };
}

function readAnyKey() {
return new Promise<void>((resolve) => {
// Debugger console may not support raw mode. Check if it's available.
if (typeof process.stdin.setRawMode === "function") {
process.stdin.setRawMode(true);
}

process.stdin.resume();
process.stdin.once("data", () => {
if (typeof process.stdin.setRawMode === "function") {
process.stdin.setRawMode(false);
}

resolve();
});
});
}

const aadToken = process.env.AAD_TOKEN;
if (!aadToken) {
console.error("AAD_TOKEN environment variable is required. You can get your AAD token by running 'devtunnels login --verbose' and copying the token from the output.");
process.exit(1);
}

function createTunnelManagementClient() {
const userAgent: ProductHeaderValue = {
name: "devtunnels-sample-host",
version: "1.0.0",
};

const tunnelManagementClient = new TunnelManagementHttpClient(
userAgent,
ManagementApiVersions.Version20230927preview,
() => Promise.resolve(`Bearer ${aadToken}`)
);

tunnelManagementClient.trace = (msg) => {
console.log(`tunnel: ${msg}`);
}

return tunnelManagementClient;
}

async function main() {
try {
const server = await startServer();

const tunnelManagementClient = createTunnelManagementClient();

console.log("\nCreating tunnel and starting host...");
const { tunnel, host } = await createTunnelAndConnectHost(tunnelManagementClient);
console.log("\nPress any key to exit...\n");
await readAnyKey();

console.log("\nStopping tunnel host...");
host.dispose();
server.close();

console.log("\nDeleting tunnel...");
await tunnelManagementClient.deleteTunnel(tunnel);

} catch (err) {
console.error(err);
}

process.exit();
}

main();
Loading
Loading