Skip to content

Commit 132139a

Browse files
committed
address pr reviews
1 parent 5f46450 commit 132139a

File tree

2 files changed

+148
-130
lines changed

2 files changed

+148
-130
lines changed

src/content/docs/workers/runtime-apis/nodejs/http.mdx

Lines changed: 110 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pcx_content_type: configuration
33
title: http
44
---
55

6-
import { Render } from "~/components"
6+
import { Render } from "~/components";
77

88
<Render file="nodejs-compat-howto" />
99

@@ -42,11 +42,11 @@ An implementation of the Node.js [`http.Agent`](https://nodejs.org/docs/latest/a
4242
An [Agent](https://nodejs.org/docs/latest/api/http.html#class-httpagent) manages HTTP connection reuse by maintaining request queues per host/port. In the Workers environment, however, such low-level management of the network connection, ports, etc, is not relevant because it is handled by the Cloudflare infrastructure instead. Accordingly, the implementation of `Agent` in Workers is a stub implementation that does not support connection pooling or keep-alive.
4343

4444
```js
45-
import { Agent } from 'node:http';
46-
import { strictEqual } from 'node:assert';
45+
import { Agent } from "node:http";
46+
import { strictEqual } from "node:assert";
4747

4848
const agent = new Agent();
49-
strictEqual(agent.protocol, 'http:');
49+
strictEqual(agent.protocol, "http:");
5050
```
5151

5252
## get
@@ -56,21 +56,29 @@ An implementation of the Node.js [`http.get`](https://nodejs.org/docs/latest/api
5656
The `get` method performs a GET request to the specified URL and invokes the callback with the response. It's a convenience method that simplifies making HTTP GET requests without manually configuring request options.
5757

5858
```js
59-
import { get } from 'node:http';
60-
import { strictEqual, ok } from 'node:assert';
61-
62-
get('http://docs.cloudflare.com/robots.txt', (res) => {
63-
// requests to http://docs.cloudflare.com get redirected to their https counterpart.
64-
strictEqual(res.statusCode, 301);
65-
let data = '';
66-
res.setEncoding('utf8');
67-
res.on('data', (chunk) => {
68-
data += chunk;
69-
});
70-
res.on('end', () => {
71-
ok(data.includes('301 Moved Permanently'));
72-
});
73-
});
59+
import { get } from "node:http";
60+
import { strictEqual, ok } from "node:assert";
61+
62+
export default {
63+
async fetch() {
64+
const { promise, resolve, reject } = Promise.withResolvers();
65+
get("http://docs.cloudflare.com/robots.txt", (res) => {
66+
// requests to http://docs.cloudflare.com get redirected to their https counterpart.
67+
strictEqual(res.statusCode, 301);
68+
let data = "";
69+
res.setEncoding("utf8");
70+
res.on("data", (chunk) => {
71+
data += chunk;
72+
});
73+
res.once("error", reject);
74+
res.on("end", () => {
75+
ok(data.includes("301 Moved Permanently"));
76+
resolve(new Response(data));
77+
});
78+
});
79+
return promise;
80+
},
81+
};
7482
```
7583

7684
## request
@@ -80,45 +88,43 @@ An implementation of the Node.js [`http.request`](https://nodejs.org/docs/latest
8088
The `request` method creates an HTTP request with customizable options like method, headers, and body. It provides full control over the request configuration and returns a [writable stream](https://nodejs.org/docs/latest/api/stream.html#class-streamwritable) for sending request data.
8189

8290
```js
83-
import { request } from 'node:http';
84-
import { strictEqual, ok } from 'node:assert';
85-
86-
const req = request({
87-
method: 'GET',
88-
protocol: 'http:',
89-
hostname: 'docs.cloudflare.com',
90-
path: '/'
91-
}, (res) => {
92-
// requests to http://docs.cloudflare.com get redirected to their https counterpart.
93-
strictEqual(res.statusCode, 301);
94-
95-
let data = '';
96-
res.setEncoding('utf8');
97-
res.on('data', (chunk) => {
98-
data += chunk;
99-
});
100-
res.on('end', () => {
101-
ok(data.includes('301 Moved Permanently'));
102-
});
103-
});
104-
req.end();
105-
```
106-
107-
```js
108-
import { request } from 'node:http';
109-
import { strictEqual } from 'node:assert';
110-
111-
const req = request(new URL('http://docs.cloudflare.com'), {
112-
method: 'GET',
113-
}, (res) => {
114-
// requests to http://docs.cloudflare.com get redirected to their https counterpart.
115-
strictEqual(res.statusCode, 301);
116-
});
117-
118-
req.end();
91+
import { request } from "node:http";
92+
import { strictEqual, ok } from "node:assert";
93+
94+
export default {
95+
async fetch() {
96+
const { promise, resolve, reject } = Promise.withResolvers();
97+
const req = request(
98+
{
99+
method: "GET",
100+
protocol: "http:",
101+
hostname: "docs.cloudflare.com",
102+
path: "/",
103+
},
104+
(res) => {
105+
// requests to http://docs.cloudflare.com get redirected to their https counterpart.
106+
strictEqual(res.statusCode, 301);
107+
108+
let data = "";
109+
res.setEncoding("utf8");
110+
res.on("data", (chunk) => {
111+
data += chunk;
112+
});
113+
res.once("error", reject);
114+
res.on("end", () => {
115+
ok(data.includes("301 Moved Permanently"));
116+
resolve(new Response(data));
117+
});
118+
},
119+
);
120+
req.end();
121+
return promise;
122+
},
123+
};
119124
```
120125

121126
The following options passed to the `request` method are not supported due to differences in the Cloudflare Workers implementation of the `node:http` module:
127+
122128
- `maxHeaderSize`
123129
- `insecureHTTPParser`
124130
- `createConnection`
@@ -131,14 +137,7 @@ An implementation of the Node.js [`http.OutgoingMessage`](https://nodejs.org/doc
131137

132138
The `OutgoingMessage` class is a base class for outgoing HTTP messages (both requests and responses). It provides methods for writing headers and body data, as well as for ending the message. `OutgoingMessage` extends from the [`Writable` stream class](https://nodejs.org/docs/latest/api/stream.html#class-streamwritable).
133139

134-
```js
135-
import { OutgoingMessage } from 'node:http';
136-
137-
const res = new OutgoingMessage();
138-
res.writeHead(200, { 'Content-Type': 'text/plain' });
139-
res.write('Hello, World!');
140-
res.end();
141-
```
140+
Both `ClientRequest` and `ServerResponse` both extend from and inherit from `OutgoingMessage`.
142141

143142
## IncomingMessage
144143

@@ -147,66 +146,76 @@ An implementation of the Node.js [`http.IncomingMessage`](https://nodejs.org/doc
147146
The `IncomingMessage` class represents an HTTP message (request or response). It provides methods for reading headers and body data. `IncomingMessage` extends from the `Readable` stream class.
148147

149148
```js
150-
import { get, IncomingMessage } from 'node:http';
151-
import { ok, strictEqual } from 'node:assert';
152-
153-
get('http://docs.cloudflare.com', (res) => {
154-
strictEqual(res.statusCode, 301);
155-
ok(res instanceof IncomingMessage);
156-
});
149+
import { get, IncomingMessage } from "node:http";
150+
import { ok, strictEqual } from "node:assert";
151+
152+
export default {
153+
async fetch() {
154+
const { promise, resolve } = Promise.withResolvers();
155+
get("http://docs.cloudflare.com", (res) => {
156+
strictEqual(res.statusCode, 301);
157+
ok(res instanceof IncomingMessage);
158+
resolve(new Response("ok"));
159+
});
160+
return promise;
161+
},
162+
};
157163
```
158164

159165
The Workers implementation includes a `cloudflare` property on `IncomingMessage` objects:
160166

161167
```js
162-
import { get } from 'node:http';
168+
import { createServer } from "node:http";
169+
import { httpServerHandler } from "cloudflare:node";
163170

164-
get('http://example.com', (res) => {
165-
// Access Cloudflare-specific request properties
171+
const server = createServer((req, res) => {
166172
console.log(res.cloudflare.cf.country);
167173
console.log(res.cloudflare.cf.ray);
174+
res.write("Hello, World!");
175+
res.end();
168176
});
177+
178+
server.listen(8080);
179+
180+
export default httpServerHandler({ port: 8080 });
169181
```
170182

171183
The `cloudflare.cf` property contains [Cloudflare-specific request properties](/workers/runtime-apis/request/#incomingrequestcfproperties).
172184

173185
The following differences exist between the Workers implementation and Node.js:
174186

175187
- Trailer headers are not supported
176-
- The `socket` attribute **does not extend from `net.Socket`** and only contains the following properties: `encrypted`, `remoteFamily`, `remoteAddress`, `remotePort`, `localAddress`, `localPort`, and `destroy()` method
188+
- The `socket` attribute **does not extend from `net.Socket`** and only contains the following properties: `encrypted`, `remoteFamily`, `remoteAddress`, `remotePort`, `localAddress`, `localPort`, and `destroy()` method.
189+
- The following `socket` attributes behave differently than their Node.js counterparts:
190+
- `remoteAddress` will return `127.0.0.1` when ran locally
191+
- `remotePort` will return a random port number between 2^15 and 2^16
192+
- `localAddress` will return the value of request's `host` header if exists. Otherwise, it will return `127.0.0.1`
193+
- `localPort` will return the port number assigned to the server instance
194+
- `req.socket.destroy()` falls through to `req.destroy()`
177195

178196
## createServer
179197

180198
An implementation of the Node.js [`http.createServer`](https://nodejs.org/docs/latest/api/http.html#httpcreateserveroptions-requestlistener) method.
181199

182-
The `createServer` method creates an HTTP server instance that can handle incoming requests. It's a convenience function that creates a new `Server` instance and optionally sets up a request listener callback.
200+
The `createServer` method creates an HTTP server instance that can handle incoming requests.
183201

184202
```js
185-
import { createServer } from 'node:http';
186-
import { httpServerHandler } from 'cloudflare:node';
203+
import { createServer } from "node:http";
204+
import { httpServerHandler } from "cloudflare:node";
187205

188206
const server = createServer((req, res) => {
189-
res.writeHead(200, { 'Content-Type': 'text/plain' });
190-
res.end('Hello from Node.js HTTP server!');
207+
res.writeHead(200, { "Content-Type": "text/plain" });
208+
res.end("Hello from Node.js HTTP server!");
191209
});
192210

193211
server.listen(8080);
194212
export default httpServerHandler({ port: 8080 });
195213
```
196214

197-
The `httpServerHandler` function integrates Node.js HTTP servers with the Cloudflare Workers request model. When a request arrives at your Worker, the handler automatically routes it to your Node.js server running on the specified port. This bridge allows you to use familiar Node.js server patterns while benefiting from the Workers runtime environment, including automatic scaling, edge deployment, and integration with other Cloudflare services.
215+
The `httpServerHandler` function integrates Node.js HTTP servers with the Cloudflare Workers request model. When a request arrives at your Worker, the handler automatically routes it to your Node.js server running on the specified port. Handler is using the specified port to determine which app to route the request to. This bridge allows you to use familiar Node.js server patterns while benefiting from the Workers runtime environment, including automatic scaling, edge deployment, and integration with other Cloudflare services.
198216

199217
:::note
200-
Failing to call `close()` on an HTTP server may result in the server persisting until the worker is destroyed. In most cases, this is not an issue since servers typically live for the lifetime of the worker. However, if you need to create multiple servers during a worker's lifetime or want explicit lifecycle control (such as in test scenarios), call `close()` when you're done with the server, or use explicit resource management:
201-
202-
```js
203-
import { createServer } from 'node:http';
204-
205-
await using server = createServer((req, res) => {
206-
res.end('Hello World');
207-
});
208-
// Server will be automatically closed when it goes out of scope
209-
```
218+
Failing to call `close()` on an HTTP server may result in the server persisting until the worker is destroyed. In most cases, this is not an issue since servers typically live for the lifetime of the worker. However, if you need to create multiple servers during a worker's lifetime or want explicit lifecycle control (such as in test scenarios), call `close()` when you're done with the server, or use [explicit resource management](https://v8.dev/features/explicit-resource-management).
210219
:::
211220

212221
## Server
@@ -215,15 +224,15 @@ An implementation of the Node.js [`http.Server`](https://nodejs.org/docs/latest/
215224

216225
The `Server` class represents an HTTP server and provides methods for handling incoming requests. It extends the Node.js `EventEmitter` class and can be used to create custom server implementations.
217226

218-
When using `httpServerHandler`, the port number specified in `server.listen()` acts as a routing key rather than an actual network port. The handler uses this port to determine which HTTP server instance should handle incoming requests, allowing multiple servers to coexist within the same Worker by using different port numbers for identification.
227+
When using `httpServerHandler`, the port number specified in `server.listen()` acts as a routing key rather than an actual network port. The handler uses this port to determine which HTTP server instance should handle incoming requests, allowing multiple servers to coexist within the same Worker by using different port numbers for identification. Using a port value of `0` (or `null` or `undefined`) will result in a random port number being assigned.
219228

220229
```js
221-
import { Server } from 'node:http';
222-
import { httpServerHandler } from 'cloudflare:node';
230+
import { Server } from "node:http";
231+
import { httpServerHandler } from "cloudflare:node";
223232

224233
const server = new Server((req, res) => {
225-
res.writeHead(200, { 'Content-Type': 'application/json' });
226-
res.end(JSON.stringify({ message: 'Hello from HTTP Server!' }));
234+
res.writeHead(200, { "Content-Type": "application/json" });
235+
res.end(JSON.stringify({ message: "Hello from HTTP Server!" }));
227236
});
228237

229238
server.listen(8080);
@@ -233,7 +242,7 @@ export default httpServerHandler({ port: 8080 });
233242
The following differences exist between the Workers implementation and Node.js:
234243

235244
- Connection management methods such as `closeAllConnections()` and `closeIdleConnections()` are not implemented
236-
- Only `listen()` variants with a port number or no parameters are supported: `listen()`, `listen(0, callback)`, `listen(callback)`, etc.
245+
- Only `listen()` variants with a port number or no parameters are supported: `listen()`, `listen(0, callback)`, `listen(callback)`, etc. For reference, see the [Node.js documentation](https://nodejs.org/docs/latest/api/net.html#serverlisten).
237246
- The following server options are not supported: `maxHeaderSize`, `insecureHTTPParser`, `keepAliveTimeout`, `connectionsCheckingInterval`
238247

239248
## ServerResponse
@@ -243,23 +252,23 @@ An implementation of the Node.js [`http.ServerResponse`](https://nodejs.org/docs
243252
The `ServerResponse` class represents the server-side response object that is passed to request handlers. It provides methods for writing response headers and body data, and extends the Node.js `Writable` stream class.
244253

245254
```js
246-
import { createServer } from 'node:http';
247-
import { httpServerHandler } from 'cloudflare:node';
255+
import { createServer } from "node:http";
256+
import { httpServerHandler } from "cloudflare:node";
248257

249258
const server = createServer((req, res) => {
250259
ok(res instanceof ServerResponse);
251260

252261
// Set multiple headers at once
253262
res.writeHead(200, {
254-
'Content-Type': 'application/json',
255-
'X-Custom-Header': 'Workers-HTTP'
263+
"Content-Type": "application/json",
264+
"X-Custom-Header": "Workers-HTTP",
256265
});
257266

258267
// Stream response data
259268
res.write('{"data": [');
260269
res.write('{"id": 1, "name": "Item 1"},');
261270
res.write('{"id": 2, "name": "Item 2"}');
262-
res.write(']}');
271+
res.write("]}");
263272

264273
// End the response
265274
res.end();

0 commit comments

Comments
 (0)