1
+ const onFinishedStream = require ( "on-finished" ) ;
2
+
3
+ const escapeHtml = require ( "./escapeHtml" ) ;
4
+
1
5
/** @typedef {import("../index.js").IncomingMessage } IncomingMessage */
2
6
/** @typedef {import("../index.js").ServerResponse } ServerResponse */
3
7
@@ -88,6 +92,18 @@ function setHeaderForResponse(res, name, value) {
88
92
res . setHeader ( name , value ) ;
89
93
}
90
94
95
+ /**
96
+ * @template {ServerResponse} Response
97
+ * @param {Response } res
98
+ */
99
+ function clearHeadersForResponse ( res ) {
100
+ const headers = getHeaderNames ( res ) ;
101
+
102
+ for ( let i = 0 ; i < headers . length ; i ++ ) {
103
+ res . removeHeader ( headers [ i ] ) ;
104
+ }
105
+ }
106
+
91
107
/**
92
108
* @template {ServerResponse} Response
93
109
* @param {Response } res
@@ -108,6 +124,76 @@ function setStatusCode(res, code) {
108
124
res . statusCode = code ;
109
125
}
110
126
127
+ /**
128
+ * @param {import("fs").ReadStream } stream stream
129
+ * @param {boolean } suppress do need suppress?
130
+ * @returns {void }
131
+ */
132
+ function destroyStream ( stream , suppress ) {
133
+ if ( typeof stream . destroy === "function" ) {
134
+ stream . destroy ( ) ;
135
+ }
136
+
137
+ if ( typeof stream . close === "function" ) {
138
+ // Node.js core bug workaround
139
+ stream . on (
140
+ "open" ,
141
+ /**
142
+ * @this {import("fs").ReadStream}
143
+ */
144
+ function onOpenClose ( ) {
145
+ // @ts -ignore
146
+ if ( typeof this . fd === "number" ) {
147
+ // actually close down the fd
148
+ this . close ( ) ;
149
+ }
150
+ } ,
151
+ ) ;
152
+ }
153
+
154
+ if ( typeof stream . addListener === "function" && suppress ) {
155
+ stream . removeAllListeners ( "error" ) ;
156
+ stream . addListener ( "error" , ( ) => { } ) ;
157
+ }
158
+ }
159
+
160
+ /** @type {Record<number, string> } */
161
+ const statuses = {
162
+ 404 : "Not Found" ,
163
+ 500 : "Internal Server Error" ,
164
+ } ;
165
+
166
+ /**
167
+ * @template {ServerResponse} Response
168
+ * @param {Response } res response
169
+ * @param {number } status status
170
+ * @returns {void }
171
+ */
172
+ function sendError ( res , status ) {
173
+ const msg = statuses [ status ] || String ( status ) ;
174
+ const doc = `<!DOCTYPE html>
175
+ <html lang="en">
176
+ <head>
177
+ <meta charset="utf-8">
178
+ <title>Error</title>
179
+ </head>
180
+ <body>
181
+ <pre>${ escapeHtml ( msg ) } </pre>
182
+ </body>
183
+ </html>` ;
184
+
185
+ // Clear existing headers
186
+ clearHeadersForResponse ( res ) ;
187
+ // Send basic response
188
+ setStatusCode ( res , status ) ;
189
+ setHeaderForResponse ( res , "Content-Type" , "text/html; charset=UTF-8" ) ;
190
+ setHeaderForResponse ( res , "Content-Length" , Buffer . byteLength ( doc ) ) ;
191
+ setHeaderForResponse ( res , "Content-Security-Policy" , "default-src 'none'" ) ;
192
+ setHeaderForResponse ( res , "X-Content-Type-Options" , "nosniff" ) ;
193
+
194
+ res . end ( doc ) ;
195
+ }
196
+
111
197
/**
112
198
* @template {IncomingMessage} Request
113
199
* @template {ServerResponse} Response
@@ -125,13 +211,42 @@ function send(req, res, bufferOtStream, byteLength) {
125
211
126
212
if ( req . method === "HEAD" ) {
127
213
res . end ( ) ;
128
-
129
214
return ;
130
215
}
131
216
132
217
/** @type {import("fs").ReadStream } */
133
218
( bufferOtStream ) . pipe ( res ) ;
134
219
220
+ // Cleanup
221
+ const cleanup = ( ) => {
222
+ destroyStream (
223
+ /** @type {import("fs").ReadStream } */ ( bufferOtStream ) ,
224
+ true ,
225
+ ) ;
226
+ } ;
227
+
228
+ // Response finished, cleanup
229
+ onFinishedStream ( res , cleanup ) ;
230
+
231
+ // error handling
232
+ /** @type {import("fs").ReadStream } */
233
+ ( bufferOtStream ) . on ( "error" , ( error ) => {
234
+ // clean up stream early
235
+ cleanup ( ) ;
236
+
237
+ // Handle Error
238
+ switch ( /** @type {NodeJS.ErrnoException } */ ( error ) . code ) {
239
+ case "ENAMETOOLONG" :
240
+ case "ENOENT" :
241
+ case "ENOTDIR" :
242
+ sendError ( res , 404 ) ;
243
+ break ;
244
+ default :
245
+ sendError ( res , 500 ) ;
246
+ break ;
247
+ }
248
+ } ) ;
249
+
135
250
return ;
136
251
}
137
252
@@ -141,7 +256,6 @@ function send(req, res, bufferOtStream, byteLength) {
141
256
) {
142
257
/** @type {Response & ExpectedResponse } */
143
258
( res ) . send ( bufferOtStream ) ;
144
-
145
259
return ;
146
260
}
147
261
0 commit comments