Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor proxy code to pass strict-null checks #248

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/loud-radios-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": patch
---

Refactor proxy code to pass strict-null checks
110 changes: 59 additions & 51 deletions packages/wrangler/src/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { connect } from "node:http2";
import type { ServerHttp2Stream } from "node:http2";
import type { ClientHttp2Session, ServerHttp2Stream } from "node:http2";
import { createServer } from "node:http";
import type {
IncomingHttpHeaders,
Expand Down Expand Up @@ -64,32 +64,7 @@ export function usePreviewServer({
}) {
/** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
const proxyServer = useRef<Server>();
if (!proxyServer.current) {
proxyServer.current = createServer()
.on("request", function (req, res) {
// log all requests
console.log(
new Date().toLocaleTimeString(),
req.method,
req.url,
res.statusCode
);
})
.on("upgrade", (req) => {
// log all websocket connections
console.log(
new Date().toLocaleTimeString(),
req.method,
req.url,
101,
"(WebSocket)"
);
})
.on("error", (err) => {
// log all connection errors
console.error(new Date().toLocaleTimeString(), err);
});
}
const proxy = (proxyServer.current ??= createProxyServer());

/**
* When we're not connected / getting a fresh token on changes,
Expand All @@ -106,8 +81,6 @@ export function usePreviewServer({
>([]);

useEffect(() => {
const proxy = proxyServer.current;

// If we don't have a token, that means either we're just starting up,
// or we're refreshing the token.
if (!previewToken) {
Expand Down Expand Up @@ -147,23 +120,7 @@ export function usePreviewServer({
cleanupListeners.push(() => remote.destroy());

/** HTTP/2 -> HTTP/2 */
function handleStream(
stream: ServerHttp2Stream,
headers: IncomingHttpHeaders
) {
addCfPreviewTokenHeader(headers, previewToken.value);
headers[":authority"] = previewToken.host;
const request = stream.pipe(remote.request(headers));
request.on("response", (responseHeaders: IncomingHttpHeaders) => {
rewriteRemoteHostToLocalHostInHeaders(
responseHeaders,
previewToken.host,
port
);
stream.respond(responseHeaders);
request.pipe(stream, { end: true });
});
}
const handleStream = createStreamHandler(previewToken, remote, port);
proxy.on("stream", handleStream);
cleanupListeners.push(() => proxy.off("stream", handleStream));

Expand Down Expand Up @@ -194,7 +151,7 @@ export function usePreviewServer({
}
const request = message.pipe(remote.request(headers));
request.on("response", (responseHeaders) => {
const status = responseHeaders[":status"];
const status = responseHeaders[":status"] ?? 500;
rewriteRemoteHostToLocalHostInHeaders(
responseHeaders,
previewToken.host,
Expand Down Expand Up @@ -254,18 +211,18 @@ export function usePreviewServer({
return () => {
cleanupListeners.forEach((d) => d());
};
}, [previewToken, publicRoot, port]);
}, [previewToken, publicRoot, port, proxy]);

// Start/stop the server whenever the
// containing component is mounted/unmounted.
useEffect(() => {
proxyServer.current.listen(port);
proxy.listen(port);
console.log(`⬣ Listening at http://localhost:${port}`);

return () => {
proxyServer.current.close();
proxy.close();
};
}, [port]);
}, [port, proxy]);
}

function createHandleAssetsRequest(
Expand All @@ -292,3 +249,54 @@ const HTTP1_HEADERS = new Set([
"transfer-encoding",
"http2-settings",
]);

function createProxyServer() {
return createServer()
.on("request", function (req, res) {
// log all requests
console.log(
new Date().toLocaleTimeString(),
req.method,
req.url,
res.statusCode
);
})
.on("upgrade", (req) => {
// log all websocket connections
console.log(
new Date().toLocaleTimeString(),
req.method,
req.url,
101,
"(WebSocket)"
);
})
.on("error", (err) => {
// log all connection errors
console.error(new Date().toLocaleTimeString(), err);
});
}

function createStreamHandler(
previewToken: CfPreviewToken,
remote: ClientHttp2Session,
port: number
) {
return function handleStream(
stream: ServerHttp2Stream,
headers: IncomingHttpHeaders
) {
addCfPreviewTokenHeader(headers, previewToken.value);
headers[":authority"] = previewToken.host;
const request = stream.pipe(remote.request(headers));
request.on("response", (responseHeaders: IncomingHttpHeaders) => {
rewriteRemoteHostToLocalHostInHeaders(
responseHeaders,
previewToken.host,
port
);
stream.respond(responseHeaders);
request.pipe(stream, { end: true });
});
};
}