@@ -47,6 +47,7 @@ for (const source of imageSource) {
4747 name : `${ workerName } ` ,
4848 main : "src/index.ts" ,
4949 compatibility_date : "2025-04-03" ,
50+ compatibility_flags : [ "experimental" , "enable_ctx_exports" ] ,
5051 containers : [
5152 {
5253 image : "./Dockerfile" ,
@@ -72,7 +73,13 @@ for (const source of imageSource) {
7273 await helper . seed ( {
7374 "wrangler.json" : JSON . stringify ( wranglerConfig ) ,
7475 "src/index.ts" : dedent `
75- import { DurableObject } from "cloudflare:workers";
76+ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
77+
78+ export class TestService extends WorkerEntrypoint {
79+ async fetch(req: Request) {
80+ return new Response("hello from worker");
81+ }
82+ }
7683
7784 export class E2EContainer extends DurableObject<Env> {
7885 container: globalThis.Container;
@@ -101,6 +108,22 @@ for (const source of imageSource) {
101108 .getTcpPort(8080)
102109 .fetch("http://foo/bar/baz");
103110 return new Response(await res.text());
111+
112+ case "/setup-intercept":
113+ await this.container.interceptOutboundHttp(
114+ "11.0.0.1:80",
115+ this.ctx.exports.TestService({ props: {} })
116+ );
117+ return new Response("Intercept setup done");
118+
119+ case "/fetch-intercept":
120+ const interceptRes = await this.container
121+ .getTcpPort(8080)
122+ .fetch("http://foo/intercept", {
123+ headers: { "x-host": "11.0.0.1:80" },
124+ });
125+ return new Response(await interceptRes.text());
126+
104127 default:
105128 return new Response("Hi from Container DO");
106129 }
@@ -126,6 +149,23 @@ for (const source of imageSource) {
126149 const { createServer } = require("http");
127150
128151 const server = createServer(function (req, res) {
152+ if (req.url === "/intercept") {
153+ const targetHost = req.headers["x-host"] || "11.0.0.1";
154+ fetch("http://" + targetHost)
155+ .then(function (result) { return result.text(); })
156+ .then(function (body) {
157+ res.writeHead(200);
158+ res.write(body);
159+ res.end();
160+ })
161+ .catch(function (err) {
162+ res.writeHead(500);
163+ res.write(targetHost + " " + err.message);
164+ res.end();
165+ });
166+ return;
167+ }
168+
129169 res.writeHead(200, { "Content-Type": "text/plain" });
130170 res.write("Hello World! Have an env var! " + process.env.MESSAGE);
131171 res.end();
@@ -241,6 +281,29 @@ for (const source of imageSource) {
241281 { timeout : 5_000 }
242282 ) ;
243283
284+ // Set up egress HTTP interception so the container can call back to the worker
285+ response = await fetch ( `${ ready . url } /setup-intercept` , {
286+ signal : AbortSignal . timeout ( 5_000 ) ,
287+ headers : { "MF-Disable-Pretty-Error" : "true" } ,
288+ } ) ;
289+ text = await response . text ( ) ;
290+ expect ( response . status ) . toBe ( 200 ) ;
291+ expect ( text ) . toBe ( "Intercept setup done" ) ;
292+
293+ // Fetch through the container's /intercept route which curls back to the worker
294+ await vi . waitFor (
295+ async ( ) => {
296+ response = await fetch ( `${ ready . url } /fetch-intercept` , {
297+ signal : AbortSignal . timeout ( 5_000 ) ,
298+ headers : { "MF-Disable-Pretty-Error" : "true" } ,
299+ } ) ;
300+ text = await response . text ( ) ;
301+ expect ( response . status ) . toBe ( 200 ) ;
302+ expect ( text ) . toBe ( "hello from worker" ) ;
303+ } ,
304+ { timeout : 10_000 }
305+ ) ;
306+
244307 // Check that a container is running using `docker ps`
245308 const ids = getContainerIds ( "e2econtainer" ) ;
246309 expect ( ids . length ) . toBe ( 1 ) ;
0 commit comments