Skip to content

Commit 8434372

Browse files
coffee-mugfurrypet
andauthored
Added a hono version of workers examples (#21258)
* Added Hono version to Workers' examples * Added a hono version of workers examples - fixes * Added a hono version of woekrs examples - basic auth fix * PR #21258 - hono examples fixes * Fixed hono example for basic auth --------- Co-authored-by: Lucas Kostka <[email protected]>
1 parent d91907a commit 8434372

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2641
-186
lines changed

src/content/docs/workers/examples/103-early-hints.mdx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,47 @@ def on_fetch(request):
136136
return Response(HTML, headers=headers)
137137
```
138138

139+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
140+
141+
```ts
142+
import { Hono } from "hono";
143+
144+
const app = new Hono();
145+
146+
const CSS = "body { color: red; }";
147+
const HTML = `
148+
<!doctype html>
149+
<html lang="en">
150+
<head>
151+
<meta charset="utf-8">
152+
<title>Early Hints test</title>
153+
<link rel="stylesheet" href="/test.css">
154+
</head>
155+
<body>
156+
<h1>Early Hints test page</h1>
157+
</body>
158+
</html>
159+
`;
160+
161+
// Serve CSS file
162+
app.get("/test.css", (c) => {
163+
return c.body(CSS, {
164+
headers: {
165+
"content-type": "text/css",
166+
},
167+
});
168+
});
169+
170+
// Serve HTML with early hints
171+
app.get("*", (c) => {
172+
return c.html(HTML, {
173+
headers: {
174+
link: "</test.css>; rel=preload; as=style",
175+
},
176+
});
177+
});
178+
179+
export default app;
180+
```
181+
139182
</TabItem> </Tabs>

src/content/docs/workers/examples/ab-testing.mdx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,57 @@ async def on_fetch(request):
137137
return fetch(urlunparse(url))
138138
```
139139

140+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
141+
142+
```ts
143+
import { Hono } from "hono";
144+
import { getCookie, setCookie } from "hono/cookie";
145+
146+
const app = new Hono();
147+
148+
const NAME = "myExampleWorkersABTest";
149+
150+
// Enable passthrough to allow direct access to control and test routes
151+
app.all("/control/*", (c) => fetch(c.req.raw));
152+
app.all("/test/*", (c) => fetch(c.req.raw));
153+
154+
// Middleware to handle A/B testing logic
155+
app.use("*", async (c) => {
156+
const url = new URL(c.req.url);
157+
158+
// Determine which group this requester is in
159+
const abTestCookie = getCookie(c, NAME);
160+
161+
if (abTestCookie === "control") {
162+
// User is in control group
163+
url.pathname = "/control" + c.req.path;
164+
} else if (abTestCookie === "test") {
165+
// User is in test group
166+
url.pathname = "/test" + c.req.path;
167+
} else {
168+
// If there is no cookie, this is a new client
169+
// Choose a group and set the cookie (50/50 split)
170+
const group = Math.random() < 0.5 ? "test" : "control";
171+
172+
// Update URL path based on assigned group
173+
if (group === "control") {
174+
url.pathname = "/control" + c.req.path;
175+
} else {
176+
url.pathname = "/test" + c.req.path;
177+
}
178+
179+
// Set cookie to enable persistent A/B sessions
180+
setCookie(c, NAME, group, {
181+
path: "/",
182+
});
183+
}
184+
185+
const res = await fetch(url);
186+
187+
return c.body(res.body, res);
188+
});
189+
190+
export default app;
191+
```
192+
140193
</TabItem> </Tabs>

src/content/docs/workers/examples/accessing-the-cloudflare-object.mdx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,30 @@ export default {
5656
} satisfies ExportedHandler;
5757
```
5858

59+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
60+
61+
```ts
62+
import { Hono } from "hono";
63+
64+
const app = new Hono();
65+
66+
app.get("*", async (c) => {
67+
// Access the raw request to get the cf object
68+
const req = c.req.raw;
69+
70+
// Check if the cf object is available
71+
const data =
72+
req.cf !== undefined
73+
? req.cf
74+
: { error: "The `cf` object is not available inside the preview." };
75+
76+
// Return the data formatted with 2-space indentation
77+
return c.json(data);
78+
});
79+
80+
export default app;
81+
```
82+
5983
</TabItem> <TabItem label="Python" icon="seti:python">
6084

6185
```py

src/content/docs/workers/examples/aggregate-requests.mdx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ export default {
5858
} satisfies ExportedHandler;
5959
```
6060

61+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
62+
63+
```ts
64+
import { Hono } from "hono";
65+
66+
const app = new Hono();
67+
68+
app.get("*", async (c) => {
69+
// someHost is set up to return JSON responses
70+
const someHost = "https://jsonplaceholder.typicode.com";
71+
const url1 = someHost + "/todos/1";
72+
const url2 = someHost + "/todos/2";
73+
74+
// Fetch both URLs concurrently
75+
const responses = await Promise.all([fetch(url1), fetch(url2)]);
76+
77+
// Parse JSON responses concurrently
78+
const results = await Promise.all(responses.map((r) => r.json()));
79+
80+
// Return aggregated results
81+
return c.json(results);
82+
});
83+
84+
export default app;
85+
```
86+
6187
</TabItem> <TabItem label="Python" icon="seti:python">
6288

6389
```py

src/content/docs/workers/examples/alter-headers.mdx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,47 @@ async def on_fetch(request):
103103
return Response(response.body, headers=new_headers)
104104
```
105105

106+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
107+
108+
```ts
109+
import { Hono } from 'hono';
110+
111+
const app = new Hono();
112+
113+
app.use('*', async (c, next) => {
114+
// Process the request with the next middleware/handler
115+
await next();
116+
117+
// After the response is generated, we can modify its headers
118+
119+
// Add a custom header with a value
120+
c.res.headers.append(
121+
"x-workers-hello",
122+
"Hello from Cloudflare Workers with Hono"
123+
);
124+
125+
// Delete headers
126+
c.res.headers.delete("x-header-to-delete");
127+
c.res.headers.delete("x-header2-to-delete");
128+
129+
// Adjust the value for an existing header
130+
c.res.headers.set("x-header-to-change", "NewValue");
131+
});
132+
133+
app.get('*', async (c) => {
134+
// Fetch content from example.com
135+
const response = await fetch("https://example.com");
136+
137+
// Return the response body with original headers
138+
// (our middleware will modify the headers before sending)
139+
return new Response(response.body, {
140+
headers: response.headers
141+
});
142+
});
143+
144+
export default app;
145+
```
146+
106147
</TabItem> </Tabs>
107148

108149
You can also use the [`custom-headers-example` template](https://github.com/kristianfreeman/custom-headers-example) to deploy this code to your custom domain.

src/content/docs/workers/examples/auth-with-headers.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,39 @@ async def on_fetch(request):
9595
return Response("Sorry, you have supplied an invalid key.", status=403)
9696
```
9797

98+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
99+
100+
```ts
101+
import { Hono } from 'hono';
102+
103+
const app = new Hono();
104+
105+
// Add authentication middleware
106+
app.use('*', async (c, next) => {
107+
/**
108+
* Define authentication constants
109+
*/
110+
const PRESHARED_AUTH_HEADER_KEY = "X-Custom-PSK";
111+
const PRESHARED_AUTH_HEADER_VALUE = "mypresharedkey";
112+
113+
// Get the pre-shared key from the request header
114+
const psk = c.req.header(PRESHARED_AUTH_HEADER_KEY);
115+
116+
if (psk === PRESHARED_AUTH_HEADER_VALUE) {
117+
// Correct preshared header key supplied. Continue to the next handler.
118+
await next();
119+
} else {
120+
// Incorrect key supplied. Reject the request.
121+
return c.text("Sorry, you have supplied an invalid key.", 403);
122+
}
123+
});
124+
125+
// Handle all authenticated requests by passing through to origin
126+
app.all('*', async (c) => {
127+
return fetch(c.req.raw);
128+
});
129+
130+
export default app;
131+
```
132+
98133
</TabItem> </Tabs>

src/content/docs/workers/examples/basic-auth.mdx

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,15 @@ use worker::*;
266266

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

279279
match url.path() {
280280
"/" => Response::ok("Anyone can access the homepage."),
@@ -328,6 +328,56 @@ async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
328328
}
329329
_ => Response::error("Not Found.", 404),
330330
}
331+
331332
}
332-
```
333-
</TabItem> </Tabs>
333+
334+
````
335+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
336+
337+
```ts
338+
/**
339+
* Shows how to restrict access using the HTTP Basic schema with Hono.
340+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
341+
* @see https://tools.ietf.org/html/rfc7617
342+
*/
343+
344+
import { Hono } from "hono";
345+
import { basicAuth } from "hono/basic-auth";
346+
347+
// Define environment interface
348+
interface Env {
349+
Bindings: {
350+
USERNAME: string;
351+
PASSWORD: string;
352+
};
353+
}
354+
355+
const app = new Hono<Env>();
356+
357+
// Public homepage - accessible to everyone
358+
app.get("/", (c) => {
359+
return c.text("Anyone can access the homepage.");
360+
});
361+
362+
// Admin route - protected with Basic Auth
363+
app.get(
364+
"/admin",
365+
async (c, next) => {
366+
const auth = basicAuth({
367+
username: c.env.USERNAME,
368+
password: c.env.PASSWORD
369+
})
370+
371+
return await auth(c, next);
372+
},
373+
(c) => {
374+
return c.text("🎉 You have private access!", 200, {
375+
"Cache-Control": "no-store",
376+
});
377+
}
378+
);
379+
380+
export default app;
381+
````
382+
383+
</TabItem> </Tabs>

src/content/docs/workers/examples/block-on-tls.mdx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,42 @@ export default {
6969
} satisfies ExportedHandler;
7070
```
7171

72+
</TabItem> <TabItem label="Hono" icon="seti:typescript">
73+
74+
```ts
75+
import { Hono } from "hono";
76+
77+
const app = new Hono();
78+
79+
// Middleware to check TLS version
80+
app.use("*", async (c, next) => {
81+
// Access the raw request to get the cf object with TLS info
82+
const request = c.req.raw;
83+
const tlsVersion = request.cf?.tlsVersion;
84+
85+
// Allow only TLS versions 1.2 and 1.3
86+
if (tlsVersion !== "TLSv1.2" && tlsVersion !== "TLSv1.3") {
87+
return c.text("Please use TLS version 1.2 or higher.", 403);
88+
}
89+
90+
await next();
91+
92+
});
93+
94+
app.onError((err, c) => {
95+
console.error(
96+
"request.cf does not exist in the previewer, only in production",
97+
);
98+
return c.text(`Error in workers script: ${err.message}`, 500);
99+
});
100+
101+
app.get("/", async (c) => {
102+
return c.text(`TLS Version: ${c.req.raw.cf.tlsVersion}`);
103+
});
104+
105+
export default app;
106+
```
107+
72108
</TabItem> <TabItem label="Python" icon="seti:python">
73109

74110
```py

0 commit comments

Comments
 (0)