Skip to content

Commit a19f1b7

Browse files
committed
Minor code fix
1 parent 572bdb9 commit a19f1b7

File tree

1 file changed

+121
-128
lines changed

1 file changed

+121
-128
lines changed

src/content/docs/durable-objects/examples/websocket-hibernation-server.mdx

Lines changed: 121 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -28,144 +28,137 @@ import { DurableObject } from 'cloudflare:workers';
2828

2929
// Worker
3030
export default {
31-
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
32-
if (request.url.endsWith('/websocket')) {
33-
// Expect to receive a WebSocket Upgrade request.
34-
// If there is one, accept the request and return a WebSocket Response.
35-
const upgradeHeader = request.headers.get('Upgrade');
36-
if (!upgradeHeader || upgradeHeader !== 'websocket') {
37-
return new Response('Worker expected Upgrade: websocket', {
38-
status: 426,
39-
});
40-
}
41-
42-
if (request.method !== 'GET') {
43-
return new Response('Worker expected GET method', {
44-
status: 400,
45-
});
46-
}
47-
48-
// Since we are hard coding the Durable Object ID by providing the constant name 'foo',
49-
// all requests to this Worker will be sent to the same Durable Object instance.
50-
let id = env.WEBSOCKET_HIBERNATION_SERVER.idFromName('foo');
51-
let stub = env.WEBSOCKET_HIBERNATION_SERVER.get(id);
52-
53-
return stub.fetch(request);
54-
}
55-
return stub.fetch(request);
56-
}
57-
58-
return new Response(
59-
`Supported endpoints:
31+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
32+
if (request.url.endsWith('/websocket')) {
33+
// Expect to receive a WebSocket Upgrade request.
34+
// If there is one, accept the request and return a WebSocket Response.
35+
const upgradeHeader = request.headers.get('Upgrade');
36+
if (!upgradeHeader || upgradeHeader !== 'websocket') {
37+
return new Response('Worker expected Upgrade: websocket', {
38+
status: 426,
39+
});
40+
}
41+
42+
if (request.method !== 'GET') {
43+
return new Response('Worker expected GET method', {
44+
status: 400,
45+
});
46+
}
47+
48+
// Since we are hard coding the Durable Object ID by providing the constant name 'foo',
49+
// all requests to this Worker will be sent to the same Durable Object instance.
50+
let id = env.WEBSOCKET_HIBERNATION_SERVER.idFromName('foo');
51+
let stub = env.WEBSOCKET_HIBERNATION_SERVER.get(id);
52+
53+
return stub.fetch(request);
54+
}
55+
56+
return new Response(
57+
`Supported endpoints:
6058
/websocket: Expects a WebSocket upgrade request`,
61-
{
62-
status: 200,
63-
headers: {
64-
'Content-Type': 'text/plain',
65-
},
66-
}
67-
);
68-
},
59+
{
60+
status: 200,
61+
headers: {
62+
'Content-Type': 'text/plain',
63+
},
64+
}
65+
);
66+
}
6967
};
7068

7169
// Durable Object
7270
export class WebSocketHibernationServer extends DurableObject {
73-
// Keeps track of all WebSocket connections
74-
// When the DO hibernates, gets reconstructed in the constructor
75-
sessions: Map<WebSocket, { [key: string]: string }>;
76-
77-
constructor(ctx: DurableObjectState, env: Env) {
78-
super(ctx, env);
79-
this.sessions = new Map();
80-
81-
// As part of constructing the Durable Object,
82-
// we wake up any hibernating WebSockets and
83-
// place them back in the `sessions` map.
84-
85-
// Get all WebSocket connections from the DO
86-
this.ctx.getWebSockets().forEach((ws) => {
87-
let attachment = ws.deserializeAttachment();
88-
if (ws.deserializeAttachment()) {
89-
// If we previously attached state to our WebSocket,
90-
// let's add it to `sessions` map to restore the state of the connection.
91-
const { ...session } = attachment;
92-
this.sessions.set(ws, { ...session });
93-
}
94-
});
95-
96-
// Sets an application level auto response that does not wake hibernated WebSockets.
97-
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair('ping', 'pong'));
98-
}
99-
100-
async fetch(request: Request): Promise<Response> {
101-
// Creates two ends of a WebSocket connection.
102-
const webSocketPair = new WebSocketPair();
103-
const [client, server] = Object.values(webSocketPair);
104-
105-
// Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
106-
// request within the Durable Object. It has the effect of "accepting" the connection,
107-
// and allowing the WebSocket to send and receive messages.
108-
// Unlike `ws.accept()`, `this.ctx.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
109-
// is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
110-
// the connection is open. During periods of inactivity, the Durable Object can be evicted
111-
// from memory, but the WebSocket connection will remain open. If at some later point the
112-
// WebSocket receives a message, the runtime will recreate the Durable Object
113-
// (run the `constructor`) and deliver the message to the appropriate handler.
114-
this.ctx.acceptWebSocket(server);
115-
116-
// Generate a random UUID for the session.
117-
const id = crypto.randomUUID();
118-
119-
// Attach the session ID to the WebSocket connection and serialize it.
120-
// This is necessary to restore the state of the connection when the Durable Object wakes up.
121-
server.serializeAttachment({ id });
122-
123-
// Add the WebSocket connection to the map of active sessions.
124-
this.sessions.set(server, { id });
125-
126-
return new Response(null, {
127-
status: 101,
128-
webSocket: client,
129-
});
130-
}
131-
return new Response(null, {
132-
status: 101,
133-
webSocket: client,
134-
});
135-
}
136-
137-
async webSocketMessage(ws: WebSocket, message: ArrayBuffer | string) {
138-
// Get the session associated with the WebSocket connection.
139-
const session = this.sessions.get(ws)!;
140-
141-
// Upon receiving a message from the client, the server replies with the same message, the session ID of the connection,
142-
// and the total number of connections with the "[Durable Object]: " prefix
143-
ws.send(`[Durable Object] message: ${message}, from: ${session.id}. Total connections: ${this.sessions.size}`);
144-
145-
// Send a message to all WebSocket connections, loop over all the connected WebSockets.
146-
this.sessions.forEach((attachment, session) => {
147-
session.send(`[Durable Object] message: ${message}, from: ${attachment.id}. Total connections: ${this.sessions.size}`);
148-
});
149-
150-
// Send a message to all WebSocket connections except the connection (ws),
151-
// loop over all the connected WebSockets and filter out the connection (ws).
152-
this.sessions.forEach((attachment, session) => {
153-
if (session !== ws) {
154-
session.send(`[Durable Object] message: ${message}, from: ${attachment.id}. Total connections: ${this.sessions.size}`);
155-
}
156-
});
157-
}
158-
159-
async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) {
160-
// If the client closes the connection, the runtime will invoke the webSocketClose() handler.
161-
ws.close(code, 'Durable Object is closing WebSocket');
162-
}
71+
// Keeps track of all WebSocket connections
72+
// When the DO hibernates, gets reconstructed in the constructor
73+
sessions: Map<WebSocket, { [key: string]: string }>;
74+
75+
constructor(ctx: DurableObjectState, env: Env) {
76+
super(ctx, env);
77+
this.sessions = new Map();
78+
79+
// As part of constructing the Durable Object,
80+
// we wake up any hibernating WebSockets and
81+
// place them back in the `sessions` map.
82+
83+
// Get all WebSocket connections from the DO
84+
this.ctx.getWebSockets().forEach((ws) => {
85+
let attachment = ws.deserializeAttachment();
86+
if (ws.deserializeAttachment()) {
87+
// If we previously attached state to our WebSocket,
88+
// let's add it to `sessions` map to restore the state of the connection.
89+
const { ...session } = attachment;
90+
this.sessions.set(ws, { ...session });
91+
}
92+
});
93+
94+
// Sets an application level auto response that does not wake hibernated WebSockets.
95+
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair('ping', 'pong'));
96+
}
97+
98+
async fetch(request: Request): Promise<Response> {
99+
// Creates two ends of a WebSocket connection.
100+
const webSocketPair = new WebSocketPair();
101+
const [client, server] = Object.values(webSocketPair);
102+
103+
// Calling `acceptWebSocket()` informs the runtime that this WebSocket is to begin terminating
104+
// request within the Durable Object. It has the effect of "accepting" the connection,
105+
// and allowing the WebSocket to send and receive messages.
106+
// Unlike `ws.accept()`, `this.ctx.acceptWebSocket(ws)` informs the Workers Runtime that the WebSocket
107+
// is "hibernatable", so the runtime does not need to pin this Durable Object to memory while
108+
// the connection is open. During periods of inactivity, the Durable Object can be evicted
109+
// from memory, but the WebSocket connection will remain open. If at some later point the
110+
// WebSocket receives a message, the runtime will recreate the Durable Object
111+
// (run the `constructor`) and deliver the message to the appropriate handler.
112+
this.ctx.acceptWebSocket(server);
113+
114+
// Generate a random UUID for the session.
115+
const id = crypto.randomUUID();
116+
117+
// Attach the session ID to the WebSocket connection and serialize it.
118+
// This is necessary to restore the state of the connection when the Durable Object wakes up.
119+
server.serializeAttachment({ id });
120+
121+
// Add the WebSocket connection to the map of active sessions.
122+
this.sessions.set(server, { id });
123+
124+
return new Response(null, {
125+
status: 101,
126+
webSocket: client,
127+
});
128+
}
129+
130+
async webSocketMessage(ws: WebSocket, message: ArrayBuffer | string) {
131+
// Get the session associated with the WebSocket connection.
132+
const session = this.sessions.get(ws)!;
133+
134+
// Upon receiving a message from the client, the server replies with the same message, the session ID of the connection,
135+
// and the total number of connections with the "[Durable Object]: " prefix
136+
ws.send(`[Durable Object] message: ${message}, from: ${session.id}. Total connections: ${this.sessions.size}`);
137+
138+
// Send a message to all WebSocket connections, loop over all the connected WebSockets.
139+
this.sessions.forEach((attachment, session) => {
140+
session.send(`[Durable Object] message: ${message}, from: ${attachment.id}. Total connections: ${this.sessions.size}`);
141+
});
142+
143+
// Send a message to all WebSocket connections except the connection (ws),
144+
// loop over all the connected WebSockets and filter out the connection (ws).
145+
this.sessions.forEach((attachment, session) => {
146+
if (session !== ws) {
147+
session.send(`[Durable Object] message: ${message}, from: ${attachment.id}. Total connections: ${this.sessions.size}`);
148+
}
149+
});
150+
}
151+
152+
async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) {
153+
// If the client closes the connection, the runtime will invoke the webSocketClose() handler.
154+
ws.close(code, 'Durable Object is closing WebSocket');
155+
}
163156
}
164157

165158
```
166159
</TypeScriptExample>
167160

168-
Finally, configure your `wrangler.toml` file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
161+
Finally, configure your Wrangler file to include a Durable Object [binding](/durable-objects/get-started/#4-configure-durable-object-bindings) and [migration](/durable-objects/reference/durable-objects-migrations/) based on the namespace and class name chosen previously.
169162

170163
<WranglerConfig>
171164

0 commit comments

Comments
 (0)