1
1
import { connect } from "node:http2" ;
2
- import type { ServerHttp2Stream } from "node:http2" ;
2
+ import type { ClientHttp2Session , ServerHttp2Stream } from "node:http2" ;
3
3
import { createServer } from "node:http" ;
4
4
import type {
5
5
IncomingHttpHeaders ,
@@ -64,32 +64,7 @@ export function usePreviewServer({
64
64
} ) {
65
65
/** Creates an HTTP/1 proxy that sends requests over HTTP/2. */
66
66
const proxyServer = useRef < Server > ( ) ;
67
- if ( ! proxyServer . current ) {
68
- proxyServer . current = createServer ( )
69
- . on ( "request" , function ( req , res ) {
70
- // log all requests
71
- console . log (
72
- new Date ( ) . toLocaleTimeString ( ) ,
73
- req . method ,
74
- req . url ,
75
- res . statusCode
76
- ) ;
77
- } )
78
- . on ( "upgrade" , ( req ) => {
79
- // log all websocket connections
80
- console . log (
81
- new Date ( ) . toLocaleTimeString ( ) ,
82
- req . method ,
83
- req . url ,
84
- 101 ,
85
- "(WebSocket)"
86
- ) ;
87
- } )
88
- . on ( "error" , ( err ) => {
89
- // log all connection errors
90
- console . error ( new Date ( ) . toLocaleTimeString ( ) , err ) ;
91
- } ) ;
92
- }
67
+ const proxy = ( proxyServer . current ??= createProxyServer ( ) ) ;
93
68
94
69
/**
95
70
* When we're not connected / getting a fresh token on changes,
@@ -106,8 +81,6 @@ export function usePreviewServer({
106
81
> ( [ ] ) ;
107
82
108
83
useEffect ( ( ) => {
109
- const proxy = proxyServer . current ;
110
-
111
84
// If we don't have a token, that means either we're just starting up,
112
85
// or we're refreshing the token.
113
86
if ( ! previewToken ) {
@@ -147,23 +120,7 @@ export function usePreviewServer({
147
120
cleanupListeners . push ( ( ) => remote . destroy ( ) ) ;
148
121
149
122
/** HTTP/2 -> HTTP/2 */
150
- function handleStream (
151
- stream : ServerHttp2Stream ,
152
- headers : IncomingHttpHeaders
153
- ) {
154
- addCfPreviewTokenHeader ( headers , previewToken . value ) ;
155
- headers [ ":authority" ] = previewToken . host ;
156
- const request = stream . pipe ( remote . request ( headers ) ) ;
157
- request . on ( "response" , ( responseHeaders : IncomingHttpHeaders ) => {
158
- rewriteRemoteHostToLocalHostInHeaders (
159
- responseHeaders ,
160
- previewToken . host ,
161
- port
162
- ) ;
163
- stream . respond ( responseHeaders ) ;
164
- request . pipe ( stream , { end : true } ) ;
165
- } ) ;
166
- }
123
+ const handleStream = createStreamHandler ( previewToken , remote , port ) ;
167
124
proxy . on ( "stream" , handleStream ) ;
168
125
cleanupListeners . push ( ( ) => proxy . off ( "stream" , handleStream ) ) ;
169
126
@@ -194,7 +151,7 @@ export function usePreviewServer({
194
151
}
195
152
const request = message . pipe ( remote . request ( headers ) ) ;
196
153
request . on ( "response" , ( responseHeaders ) => {
197
- const status = responseHeaders [ ":status" ] ;
154
+ const status = responseHeaders [ ":status" ] ?? 500 ;
198
155
rewriteRemoteHostToLocalHostInHeaders (
199
156
responseHeaders ,
200
157
previewToken . host ,
@@ -254,18 +211,18 @@ export function usePreviewServer({
254
211
return ( ) => {
255
212
cleanupListeners . forEach ( ( d ) => d ( ) ) ;
256
213
} ;
257
- } , [ previewToken , publicRoot , port ] ) ;
214
+ } , [ previewToken , publicRoot , port , proxy ] ) ;
258
215
259
216
// Start/stop the server whenever the
260
217
// containing component is mounted/unmounted.
261
218
useEffect ( ( ) => {
262
- proxyServer . current . listen ( port ) ;
219
+ proxy . listen ( port ) ;
263
220
console . log ( `⬣ Listening at http://localhost:${ port } ` ) ;
264
221
265
222
return ( ) => {
266
- proxyServer . current . close ( ) ;
223
+ proxy . close ( ) ;
267
224
} ;
268
- } , [ port ] ) ;
225
+ } , [ port , proxy ] ) ;
269
226
}
270
227
271
228
function createHandleAssetsRequest (
@@ -292,3 +249,54 @@ const HTTP1_HEADERS = new Set([
292
249
"transfer-encoding" ,
293
250
"http2-settings" ,
294
251
] ) ;
252
+
253
+ function createProxyServer ( ) {
254
+ return createServer ( )
255
+ . on ( "request" , function ( req , res ) {
256
+ // log all requests
257
+ console . log (
258
+ new Date ( ) . toLocaleTimeString ( ) ,
259
+ req . method ,
260
+ req . url ,
261
+ res . statusCode
262
+ ) ;
263
+ } )
264
+ . on ( "upgrade" , ( req ) => {
265
+ // log all websocket connections
266
+ console . log (
267
+ new Date ( ) . toLocaleTimeString ( ) ,
268
+ req . method ,
269
+ req . url ,
270
+ 101 ,
271
+ "(WebSocket)"
272
+ ) ;
273
+ } )
274
+ . on ( "error" , ( err ) => {
275
+ // log all connection errors
276
+ console . error ( new Date ( ) . toLocaleTimeString ( ) , err ) ;
277
+ } ) ;
278
+ }
279
+
280
+ function createStreamHandler (
281
+ previewToken : CfPreviewToken ,
282
+ remote : ClientHttp2Session ,
283
+ port : number
284
+ ) {
285
+ return function handleStream (
286
+ stream : ServerHttp2Stream ,
287
+ headers : IncomingHttpHeaders
288
+ ) {
289
+ addCfPreviewTokenHeader ( headers , previewToken . value ) ;
290
+ headers [ ":authority" ] = previewToken . host ;
291
+ const request = stream . pipe ( remote . request ( headers ) ) ;
292
+ request . on ( "response" , ( responseHeaders : IncomingHttpHeaders ) => {
293
+ rewriteRemoteHostToLocalHostInHeaders (
294
+ responseHeaders ,
295
+ previewToken . host ,
296
+ port
297
+ ) ;
298
+ stream . respond ( responseHeaders ) ;
299
+ request . pipe ( stream , { end : true } ) ;
300
+ } ) ;
301
+ } ;
302
+ }
0 commit comments