Skip to content

Websocket to Container

Forwarding a Websocket request to a Container

Last updated View as MarkdownAgent setup

Forward an incoming WebSocket upgrade request through the Durable Object to the listening port on the Container.

src/index.jsjs
import { DurableObject } from "cloudflare:workers";

export class MyContainer extends DurableObject {
	constructor(ctx, env) {
		super(ctx, env);

		ctx.blockConcurrencyWhile(async () => {
			const container = ctx.container;
			await container.setInactivityTimeout(2 * 60 * 1000);

			if (!container.running) {
				container.start();
			}

			const port = container.getTcpPort(8080);
			let lastError;
			for (let attempt = 0; attempt < 50; attempt++) {
				try {
					await port.fetch("http://container/");
					return;
				} catch (error) {
					lastError = error;
					await scheduler.wait(100);
				}
			}
			throw lastError;
		});
	}

	fetch(request) {
		return this.ctx.container.getTcpPort(8080).fetch(request);
	}
}

export default {
	fetch(request, env) {
		return env.MY_CONTAINER.getByName("default").fetch(request);
	},
};
src/index.tsts
import { DurableObject } from "cloudflare:workers";

interface Env {
	MY_CONTAINER: DurableObjectNamespace<MyContainer>;
}

export class MyContainer extends DurableObject<Env> {
	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);

		ctx.blockConcurrencyWhile(async () => {
			const container = ctx.container!;
			await container.setInactivityTimeout(2 * 60 * 1000);

			if (!container.running) {
				container.start();
			}

			const port = container.getTcpPort(8080);
			let lastError: unknown;
			for (let attempt = 0; attempt < 50; attempt++) {
				try {
					await port.fetch("http://container/");
					return;
				} catch (error) {
					lastError = error;
					await scheduler.wait(100);
				}
			}
			throw lastError;
		});
	}

	fetch(request: Request): Promise<Response> {
		return this.ctx.container!.getTcpPort(8080).fetch(request);
	}
}

export default {
	fetch(request: Request, env: Env): Promise<Response> {
		return env.MY_CONTAINER.getByName("default").fetch(request);
	},
};
import { Container, getContainer } from "@cloudflare/containers";

export class MyContainer extends Container {
	defaultPort = 8080;
	sleepAfter = "2m";
}

export default {
	async fetch(request, env) {
		// gets default instance and forwards websocket from outside Worker
		return getContainer(env.MY_CONTAINER).fetch(request);
	},
};

View a full example in the Container class repository.

Was this helpful?