Skip to content
43 changes: 43 additions & 0 deletions src/content/docs/workers/examples/103-early-hints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,47 @@ def on_fetch(request):
return Response(HTML, headers=headers)
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from "hono";

const app = new Hono();

const CSS = "body { color: red; }";
const HTML = `
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Early Hints test</title>
<link rel="stylesheet" href="/test.css">
</head>
<body>
<h1>Early Hints test page</h1>
</body>
</html>
`;

// Serve CSS file
app.get("/test.css", (c) => {
return c.body(CSS, {
headers: {
"content-type": "text/css",
},
});
});

// Serve HTML with early hints
app.get("*", (c) => {
return c.html(HTML, {
headers: {
link: "</test.css>; rel=preload; as=style",
},
});
});

export default app;
```

</TabItem> </Tabs>
53 changes: 53 additions & 0 deletions src/content/docs/workers/examples/ab-testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,57 @@ async def on_fetch(request):
return fetch(urlunparse(url))
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from "hono";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the following is best.

  • Use app.all() instead of c.req.path.startsWith(). It's Hono-way.
  • Fixed the logics.
  • Return the response with c.body() to use context values.
diff --git a/src/content/docs/workers/examples/ab-testing.mdx b/src/content/docs/workers/examples/ab-testing.mdx
index a4c8dfe16..22fedf721 100644
--- a/src/content/docs/workers/examples/ab-testing.mdx
+++ b/src/content/docs/workers/examples/ab-testing.mdx
@@ -148,23 +148,23 @@ const app = new Hono();
 const NAME = "myExampleWorkersABTest";

 // Middleware to handle A/B testing logic
-app.use("*", async (c) => {
-       // Enable Passthrough to allow direct access to control and test routes
-       if (c.req.path.startsWith("/control") || c.req.path.startsWith("/test")) {
-               return fetch(c.req.raw);
-       }
+
+// Enable Passthrough to allow direct access to control and test routes
+app.all("/control/*", (c) => fetch(c.req.raw));
+app.all("/test/*", (c) => fetch(c.req.raw));
+
+app.all("*", async (c) => {
+       const url = new URL(c.req.url);

        // Determine which group this requester is in
        const abTestCookie = getCookie(c, NAME);

        if (abTestCookie === "control") {
                // User is in control group
-               c.req.path = "/control" + c.req.path;
-               return fetch(url);
+               url.pathname = "/control" + c.req.path;
        } else if (abTestCookie === "test") {
                // User is in test group
-               url.pathname = "/test" + url.pathname;
-               return fetch(c.req.url);
+               url.pathname = "/test" + c.req.path;
        } else {
                // If there is no cookie, this is a new client
                // Choose a group and set the cookie (50/50 split)
@@ -172,17 +172,10 @@ app.use("*", async (c) => {

                // Update URL path based on assigned group
                if (group === "control") {
-                       c.req.path = "/control" + c.req.path;
+                       url.pathname = "/control" + c.req.path;
                } else {
-                       c.req.path = "/test" + c.req.path;
+                       url.pathname = "/test" + c.req.path;
                }
-
-               // Fetch from origin with modified path
-               const res = await fetch(c.req.url);
-
-               // Create a new response to avoid immutability issues
-               const newResponse = new Response(res.body, res);
-
                // Set cookie to enable persistent A/B sessions
                setCookie(c, NAME, group, {
                        path: "/",
@@ -191,15 +184,11 @@ app.use("*", async (c) => {
                        // httpOnly: true,
                        // sameSite: 'strict',
                });
+       }

-               // Copy the Set-Cookie header to the response
-               newResponse.headers.set(
-                       "Set-Cookie",
-                       c.res.headers.get("Set-Cookie") || "",
-               );
+       const res = await fetch(url);

-               return newResponse;
-       }
+       return c.body(res.body!, res);
 });

 export default app;

import { getCookie, setCookie } from "hono/cookie";

const app = new Hono();

const NAME = "myExampleWorkersABTest";

// Enable passthrough to allow direct access to control and test routes
app.all("/control/*", (c) => fetch(c.req.raw));
app.all("/test/*", (c) => fetch(c.req.raw));

// Middleware to handle A/B testing logic
app.use("*", async (c) => {
const url = new URL(c.req.url);

// Determine which group this requester is in
const abTestCookie = getCookie(c, NAME);

if (abTestCookie === "control") {
// User is in control group
url.pathname = "/control" + c.req.path;
} else if (abTestCookie === "test") {
// User is in test group
url.pathname = "/test" + c.req.path;
} else {
// If there is no cookie, this is a new client
// Choose a group and set the cookie (50/50 split)
const group = Math.random() < 0.5 ? "test" : "control";

// Update URL path based on assigned group
if (group === "control") {
url.pathname = "/control" + c.req.path;
} else {
url.pathname = "/test" + c.req.path;
}

// Set cookie to enable persistent A/B sessions
setCookie(c, NAME, group, {
path: "/",
});
}

const res = await fetch(url);

return c.body(res.body, res);
});

export default app;
```

</TabItem> </Tabs>
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@ export default {
} satisfies ExportedHandler;
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from "hono";

const app = new Hono();

app.get("*", async (c) => {
// Access the raw request to get the cf object
const req = c.req.raw;

// Check if the cf object is available
const data =
req.cf !== undefined
? req.cf
: { error: "The `cf` object is not available inside the preview." };

// Return the data formatted with 2-space indentation
return c.json(data);
});

export default app;
```

</TabItem> <TabItem label="Python" icon="seti:python">

```py
Expand Down
26 changes: 26 additions & 0 deletions src/content/docs/workers/examples/aggregate-requests.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ export default {
} satisfies ExportedHandler;
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from "hono";

const app = new Hono();

app.get("*", async (c) => {
// someHost is set up to return JSON responses
const someHost = "https://jsonplaceholder.typicode.com";
const url1 = someHost + "/todos/1";
const url2 = someHost + "/todos/2";

// Fetch both URLs concurrently
const responses = await Promise.all([fetch(url1), fetch(url2)]);

// Parse JSON responses concurrently
const results = await Promise.all(responses.map((r) => r.json()));

// Return aggregated results
return c.json(results);
});

export default app;
```

</TabItem> <TabItem label="Python" icon="seti:python">

```py
Expand Down
41 changes: 41 additions & 0 deletions src/content/docs/workers/examples/alter-headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,47 @@ async def on_fetch(request):
return Response(response.body, headers=new_headers)
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from 'hono';

const app = new Hono();

app.use('*', async (c, next) => {
// Process the request with the next middleware/handler
await next();

// After the response is generated, we can modify its headers

// Add a custom header with a value
c.res.headers.append(
"x-workers-hello",
"Hello from Cloudflare Workers with Hono"
);

// Delete headers
c.res.headers.delete("x-header-to-delete");
c.res.headers.delete("x-header2-to-delete");

// Adjust the value for an existing header
c.res.headers.set("x-header-to-change", "NewValue");
});

app.get('*', async (c) => {
// Fetch content from example.com
const response = await fetch("https://example.com");

// Return the response body with original headers
// (our middleware will modify the headers before sending)
return new Response(response.body, {
headers: response.headers
});
});

export default app;
```

</TabItem> </Tabs>

You can also use the [`custom-headers-example` template](https://github.com/kristianfreeman/custom-headers-example) to deploy this code to your custom domain.
35 changes: 35 additions & 0 deletions src/content/docs/workers/examples/auth-with-headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,39 @@ async def on_fetch(request):
return Response("Sorry, you have supplied an invalid key.", status=403)
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from 'hono';

const app = new Hono();

// Add authentication middleware
app.use('*', async (c, next) => {
/**
* Define authentication constants
*/
const PRESHARED_AUTH_HEADER_KEY = "X-Custom-PSK";
const PRESHARED_AUTH_HEADER_VALUE = "mypresharedkey";

// Get the pre-shared key from the request header
const psk = c.req.header(PRESHARED_AUTH_HEADER_KEY);

if (psk === PRESHARED_AUTH_HEADER_VALUE) {
// Correct preshared header key supplied. Continue to the next handler.
await next();
} else {
// Incorrect key supplied. Reject the request.
return c.text("Sorry, you have supplied an invalid key.", 403);
}
});

// Handle all authenticated requests by passing through to origin
app.all('*', async (c) => {
return fetch(c.req.raw);
});

export default app;
```

</TabItem> </Tabs>
72 changes: 61 additions & 11 deletions src/content/docs/workers/examples/basic-auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,15 @@ use worker::*;

#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let basic_user = "admin";
// You will need an admin password. This should be
// attached to your Worker as an encrypted secret.
// Refer to https://developers.cloudflare.com/workers/configuration/secrets/
let basic_pass = match env.secret("PASSWORD") {
Ok(s) => s.to_string(),
Err(_) => "password".to_string(),
};
let url = req.url()?;
let basic_user = "admin";
// You will need an admin password. This should be
// attached to your Worker as an encrypted secret.
// Refer to https://developers.cloudflare.com/workers/configuration/secrets/
let basic_pass = match env.secret("PASSWORD") {
Ok(s) => s.to_string(),
Err(_) => "password".to_string(),
};
let url = req.url()?;

match url.path() {
"/" => Response::ok("Anyone can access the homepage."),
Expand Down Expand Up @@ -328,6 +328,56 @@ async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
}
_ => Response::error("Not Found.", 404),
}

}
```
</TabItem> </Tabs>

````
</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
/**
* Shows how to restrict access using the HTTP Basic schema with Hono.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
* @see https://tools.ietf.org/html/rfc7617
*/

import { Hono } from "hono";
import { basicAuth } from "hono/basic-auth";

// Define environment interface
interface Env {
Bindings: {
USERNAME: string;
PASSWORD: string;
};
}

const app = new Hono<Env>();

// Public homepage - accessible to everyone
app.get("/", (c) => {
return c.text("Anyone can access the homepage.");
});

// Admin route - protected with Basic Auth
app.get(
"/admin",
async (c, next) => {
const auth = basicAuth({
username: c.env.USERNAME,
password: c.env.PASSWORD
})

return await auth(c, next);
},
(c) => {
return c.text("🎉 You have private access!", 200, {
"Cache-Control": "no-store",
});
}
);

export default app;
````

</TabItem> </Tabs>
36 changes: 36 additions & 0 deletions src/content/docs/workers/examples/block-on-tls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,42 @@ export default {
} satisfies ExportedHandler;
```

</TabItem> <TabItem label="Hono" icon="seti:typescript">

```ts
import { Hono } from "hono";

const app = new Hono();

// Middleware to check TLS version
app.use("*", async (c, next) => {
// Access the raw request to get the cf object with TLS info
const request = c.req.raw;
const tlsVersion = request.cf?.tlsVersion;

// Allow only TLS versions 1.2 and 1.3
if (tlsVersion !== "TLSv1.2" && tlsVersion !== "TLSv1.3") {
return c.text("Please use TLS version 1.2 or higher.", 403);
}

await next();

});

app.onError((err, c) => {
console.error(
"request.cf does not exist in the previewer, only in production",
);
return c.text(`Error in workers script: ${err.message}`, 500);
});

app.get("/", async (c) => {
return c.text(`TLS Version: ${c.req.raw.cf.tlsVersion}`);
});

export default app;
```

</TabItem> <TabItem label="Python" icon="seti:python">

```py
Expand Down
Loading