velocious 1.0.680 → 1.0.682
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/background-jobs/worker.js +121 -15
- package/build/http-server/client/index.js +160 -0
- package/build/http-server/client/request-runner.js +13 -5
- package/build/http-server/client/response.js +142 -0
- package/build/http-server/worker-handler/in-process.js +20 -2
- package/build/http-server/worker-handler/worker-thread.js +4 -1
- package/build/src/background-jobs/worker.d.ts +59 -10
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +117 -16
- package/build/src/http-server/client/index.d.ts +69 -0
- package/build/src/http-server/client/index.d.ts.map +1 -1
- package/build/src/http-server/client/index.js +147 -1
- package/build/src/http-server/client/request-runner.d.ts.map +1 -1
- package/build/src/http-server/client/request-runner.js +16 -7
- package/build/src/http-server/client/response.d.ts +88 -0
- package/build/src/http-server/client/response.d.ts.map +1 -1
- package/build/src/http-server/client/response.js +136 -1
- package/build/src/http-server/worker-handler/in-process.d.ts.map +1 -1
- package/build/src/http-server/worker-handler/in-process.js +20 -3
- package/build/src/http-server/worker-handler/worker-thread.d.ts.map +1 -1
- package/build/src/http-server/worker-handler/worker-thread.js +4 -2
- package/package.json +1 -1
- package/src/background-jobs/worker.js +121 -15
- package/src/http-server/client/index.js +160 -0
- package/src/http-server/client/request-runner.js +13 -5
- package/src/http-server/client/response.js +142 -0
- package/src/http-server/worker-handler/in-process.js +20 -2
- package/src/http-server/worker-handler/worker-thread.js +4 -1
|
@@ -61,6 +61,11 @@ export default class VeoliciousHttpServerClient {
|
|
|
61
61
|
|
|
62
62
|
/** @type {Set<(result: "completed" | "aborted") => Promise<void>>} */
|
|
63
63
|
this.pendingFileResponses = new Set()
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Streams that have started but have not finished or been aborted yet.
|
|
67
|
+
* @type {Map<import("./response.js").default, {clientCount: number, request: import("./request.js").default}>} */
|
|
68
|
+
this._activeStreamResponses = new Map()
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
/**
|
|
@@ -181,6 +186,15 @@ export default class VeoliciousHttpServerClient {
|
|
|
181
186
|
|
|
182
187
|
this.requestRunners.push(requestRunner)
|
|
183
188
|
|
|
189
|
+
// A streaming response emits its headers and chunks to the client while
|
|
190
|
+
// the request is still running, so the response needs the owning client
|
|
191
|
+
// as its transport sink before the handler runs. Sub-requests (e.g.
|
|
192
|
+
// websocket request payloads) have no socket and keep transport null;
|
|
193
|
+
// their responses fail loudly if they attempt to stream.
|
|
194
|
+
const socketRequest = currentRequest
|
|
195
|
+
requestRunner.response.transport = this
|
|
196
|
+
requestRunner.response.transportRequest = socketRequest
|
|
197
|
+
|
|
184
198
|
requestRunner.events.on("done", this.requestDone)
|
|
185
199
|
requestRunner.run()
|
|
186
200
|
}
|
|
@@ -423,6 +437,15 @@ export default class VeoliciousHttpServerClient {
|
|
|
423
437
|
*/
|
|
424
438
|
async sendResponse(requestRunner) {
|
|
425
439
|
const response = digg(requestRunner, "response")
|
|
440
|
+
|
|
441
|
+
// A streaming response already emitted its status line, headers, every
|
|
442
|
+
// chunk, and the chunked terminator to the client while the request was
|
|
443
|
+
// running. Nothing left to send here — just log the completed request.
|
|
444
|
+
if (response.isStreaming()) {
|
|
445
|
+
await requestRunner.logCompletedRequest()
|
|
446
|
+
return
|
|
447
|
+
}
|
|
448
|
+
|
|
426
449
|
const request = requestRunner.getRequest()
|
|
427
450
|
const filePath = response.getFilePath()
|
|
428
451
|
const fileOnFinished = response.getFileOnFinished()
|
|
@@ -666,6 +689,143 @@ export default class VeoliciousHttpServerClient {
|
|
|
666
689
|
await Promise.all([...this.pendingFileResponses].map((settle) => settle("aborted")))
|
|
667
690
|
}
|
|
668
691
|
|
|
692
|
+
/**
|
|
693
|
+
* Sink the owning worker handler wires in for stream output. The
|
|
694
|
+
* in-process handler resolves it after the framed output has been enqueued
|
|
695
|
+
* for delivery to the socket, so stream chunks share the bounded, ordered
|
|
696
|
+
* delivery path (byte/frame limits and socket backpressure) instead of
|
|
697
|
+
* being buffered unboundedly for a stalled client. The worker-thread
|
|
698
|
+
* handler keeps null: its output crosses to the parent over IPC and no
|
|
699
|
+
* per-chunk acknowledgement is available.
|
|
700
|
+
* @type {((output: string) => Promise<void>) | null} */
|
|
701
|
+
streamOutputSink = null
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Narrows the response to the documented streaming transport shape.
|
|
705
|
+
* @param {import("./response.js").default} response - Response to stream.
|
|
706
|
+
* @returns {{streaming: boolean, streamEnded: boolean, streamAborted: boolean, headers: Record<string, string[]>, getStatusCode: () => number, getStatusMessage: () => string, streamCloseCallbacks: Set<() => void>}} - Streaming view of the response.
|
|
707
|
+
*/
|
|
708
|
+
_streamingResponse(response) {
|
|
709
|
+
return response
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Starts a live chunked stream for a socket-bound response: emits the
|
|
714
|
+
* status line and headers (with `Transfer-Encoding: chunked`) to the
|
|
715
|
+
* client immediately so subsequent `write()` chunks reach the client as
|
|
716
|
+
* they are produced.
|
|
717
|
+
* @param {import("./response.js").default} response - Response to stream.
|
|
718
|
+
* @param {import("./request.js").default} request - Socket-bound request.
|
|
719
|
+
* @returns {void} - No return value.
|
|
720
|
+
*/
|
|
721
|
+
beginStreamResponse(response, request) {
|
|
722
|
+
this._activeStreamResponses.set(response, {clientCount: this.clientCount, request})
|
|
723
|
+
|
|
724
|
+
const httpVersion = request.httpVersion()
|
|
725
|
+
|
|
726
|
+
// HTTP/1.0 has no chunked transfer encoding: terminate the stream with a
|
|
727
|
+
// connection close instead of a zero-length chunk.
|
|
728
|
+
if (httpVersion == "1.0" && !this.shouldCloseConnection(request)) {
|
|
729
|
+
response.setHeader("Connection", "Close")
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// The chunked framing owns the body length; a Content-Length header set
|
|
733
|
+
// by the application before stream() would desynchronize the framing.
|
|
734
|
+
response.removeHeader("Content-Length")
|
|
735
|
+
if (response.getHeader("Transfer-Encoding").length === 0) {
|
|
736
|
+
response.setHeader("Transfer-Encoding", "chunked")
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
response.setHeader("Date", new Date().toUTCString())
|
|
740
|
+
response.setHeader("Server", "Velocious")
|
|
741
|
+
|
|
742
|
+
const responseView = this._streamingResponse(response)
|
|
743
|
+
let headers = ""
|
|
744
|
+
headers += `HTTP/${httpVersion} ${responseView.getStatusCode()} ${responseView.getStatusMessage()}\r\n`
|
|
745
|
+
|
|
746
|
+
for (const headerKey in responseView.headers) {
|
|
747
|
+
for (const headerValue of responseView.headers[headerKey]) {
|
|
748
|
+
headers += `${headerKey}: ${headerValue}\r\n`
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
headers += "\r\n"
|
|
753
|
+
this.events.emit("output", headers)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Emits one chunked-encoded body chunk for an active stream.
|
|
758
|
+
* @param {string | Uint8Array} chunk - Chunk to emit.
|
|
759
|
+
* @returns {Promise<void>} - Settles after the chunk has been delivered to
|
|
760
|
+
* the client.
|
|
761
|
+
*/
|
|
762
|
+
async writeStreamChunk(chunk) {
|
|
763
|
+
const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : Buffer.from(chunk)
|
|
764
|
+
const frame = `${bytes.length.toString(16)}\r\n${bytes}\r\n`
|
|
765
|
+
if (this.streamOutputSink === null) {
|
|
766
|
+
this.events.emit("output", frame)
|
|
767
|
+
return
|
|
768
|
+
}
|
|
769
|
+
await this.streamOutputSink(frame)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Emits the zero-length chunked terminator for a stream that finished on a
|
|
774
|
+
* live connection, then runs its close callbacks.
|
|
775
|
+
* @param {import("./response.js").default} response - Response to finish.
|
|
776
|
+
* @returns {Promise<void>} - Settles after the terminator was delivered and
|
|
777
|
+
* the close callbacks ran.
|
|
778
|
+
*/
|
|
779
|
+
async endStreamResponse(response) {
|
|
780
|
+
if (response.streamEnded) return
|
|
781
|
+
response.streamEnded = true
|
|
782
|
+
this._activeStreamResponses.delete(response)
|
|
783
|
+
if (this.streamOutputSink === null) {
|
|
784
|
+
this.events.emit("output", "0\r\n\r\n")
|
|
785
|
+
} else {
|
|
786
|
+
await this.streamOutputSink("0\r\n\r\n")
|
|
787
|
+
}
|
|
788
|
+
await this._runStreamCloseCallbacks(response)
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Aborts every stream whose client connection went away: marks the stream
|
|
793
|
+
* aborted and runs the close callbacks so in-flight work can settle its
|
|
794
|
+
* resources. No terminator is emitted — the connection is already gone.
|
|
795
|
+
* @returns {Promise<void>} - Resolves after every stream settled.
|
|
796
|
+
*/
|
|
797
|
+
async abortStreamResponses() {
|
|
798
|
+
for (const response of [...this._activeStreamResponses.keys()]) {
|
|
799
|
+
if (response.streamAborted) continue
|
|
800
|
+
response.streamAborted = true
|
|
801
|
+
this._activeStreamResponses.delete(response)
|
|
802
|
+
await this._runStreamCloseCallbacks(response)
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Runs the close callbacks of a finished or aborted stream exactly once.
|
|
808
|
+
* @param {import("./response.js").default} response - Finished stream.
|
|
809
|
+
* @returns {Promise<void>} - Resolves after every callback ran.
|
|
810
|
+
*/
|
|
811
|
+
async _runStreamCloseCallbacks(response) {
|
|
812
|
+
if (response.streamCloseCallbacks.size === 0) return
|
|
813
|
+
|
|
814
|
+
for (const callback of response.streamCloseCallbacks) {
|
|
815
|
+
try {
|
|
816
|
+
callback()
|
|
817
|
+
} catch (error) {
|
|
818
|
+
const errorPayload = {
|
|
819
|
+
context: {clientCount: this.clientCount, stage: "stream-close-callback"},
|
|
820
|
+
error
|
|
821
|
+
}
|
|
822
|
+
this.configuration.getErrorEvents().emit("framework-error", errorPayload)
|
|
823
|
+
this.configuration.getErrorEvents().emit("all-error", {...errorPayload, errorType: "framework-error"})
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
response.streamCloseCallbacks.clear()
|
|
827
|
+
}
|
|
828
|
+
|
|
669
829
|
/**
|
|
670
830
|
* Runs should close connection.
|
|
671
831
|
* @param {import("./request.js").default | import("./websocket-request.js").default} request - Request object.
|
|
@@ -305,12 +305,20 @@ export default class VelociousHttpServerClientRequestRunner {
|
|
|
305
305
|
})
|
|
306
306
|
|
|
307
307
|
response.setStatus(500)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
308
|
+
if (response.isStreaming()) {
|
|
309
|
+
// The headers already went to the client, so a plain error body is
|
|
310
|
+
// impossible. The handler owns the stream lifecycle: it aborts the
|
|
311
|
+
// stream (terminator + close callbacks) or, when it still controls
|
|
312
|
+
// the connection, writes an error SSE frame.
|
|
313
|
+
response.abortStream()
|
|
314
|
+
} else {
|
|
315
|
+
try {
|
|
316
|
+
response.setErrorBody(error)
|
|
317
|
+
} catch (responseError) {
|
|
318
|
+
if (!(responseError instanceof HttpResponseBodyTooLargeError)) throw responseError
|
|
312
319
|
|
|
313
|
-
|
|
320
|
+
response.setBody("")
|
|
321
|
+
}
|
|
314
322
|
}
|
|
315
323
|
}
|
|
316
324
|
|
|
@@ -105,6 +105,41 @@ export default class VelociousHttpServerClientResponse {
|
|
|
105
105
|
* @type {boolean} */
|
|
106
106
|
compressionDisabled = false
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Whether this response has been switched to live chunked streaming. Once
|
|
110
|
+
* streaming has started, the status line and headers are emitted to the
|
|
111
|
+
* client immediately and every `write()` is emitted as it happens, instead
|
|
112
|
+
* of the whole body being buffered and sent once after the handler
|
|
113
|
+
* returns.
|
|
114
|
+
* @type {boolean} */
|
|
115
|
+
streaming = false
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the stream has been finished with `end()` (or finalized).
|
|
119
|
+
* @type {boolean} */
|
|
120
|
+
streamEnded = false
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Whether the client connection dropped while the stream was in flight.
|
|
124
|
+
* @type {boolean} */
|
|
125
|
+
streamAborted = false
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Transport sink wired in by the owning client so the response can emit
|
|
129
|
+
* headers and chunks to the socket-bound connection.
|
|
130
|
+
* @type {import("./index.js").default | null} */
|
|
131
|
+
transport = null
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The socket-bound request this response belongs to.
|
|
135
|
+
* @type {import("./request.js").default | null} */
|
|
136
|
+
transportRequest = null
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Callbacks fired when the client disconnects mid-stream.
|
|
140
|
+
* @type {Set<() => void>} */
|
|
141
|
+
streamCloseCallbacks = new Set()
|
|
142
|
+
|
|
108
143
|
/**
|
|
109
144
|
* Runs constructor.
|
|
110
145
|
* @param {object} args - Options object.
|
|
@@ -240,6 +275,113 @@ export default class VelociousHttpServerClientResponse {
|
|
|
240
275
|
this.body = value
|
|
241
276
|
}
|
|
242
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Whether this response is (or was) a live chunked stream.
|
|
280
|
+
* @returns {boolean} - Whether streaming has started.
|
|
281
|
+
*/
|
|
282
|
+
isStreaming() {
|
|
283
|
+
return this.streaming
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Whether the client disconnected mid-stream.
|
|
288
|
+
* @returns {boolean} - Whether the stream was aborted by the client.
|
|
289
|
+
*/
|
|
290
|
+
isStreamAborted() {
|
|
291
|
+
return this.streamAborted
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Switches this response to live chunked streaming. The status line and
|
|
296
|
+
* headers are emitted to the client immediately (with a
|
|
297
|
+
* `Transfer-Encoding: chunked` framing header) so `write()` chunks reach
|
|
298
|
+
* the client as they are produced — instead of the whole body being
|
|
299
|
+
* buffered and emitted once after the handler returns.
|
|
300
|
+
*
|
|
301
|
+
* Streaming requires a socket-bound HTTP request and an HTTP version that
|
|
302
|
+
* supports chunked framing, a status that may carry a body, and a
|
|
303
|
+
* non-HEAD request. It cannot be combined with a buffered body, a file
|
|
304
|
+
* response, or a second `stream()` call.
|
|
305
|
+
* @returns {void} - No return value.
|
|
306
|
+
*/
|
|
307
|
+
stream() {
|
|
308
|
+
if (this.streaming) throw new Error("The response is already streaming")
|
|
309
|
+
if (this.filePath !== null) throw new Error("A file response cannot be switched to streaming")
|
|
310
|
+
if (this.body !== null && this.body !== undefined) throw new Error("A buffered body was already set; call stream() before setBody() to stream instead")
|
|
311
|
+
if (this.transport === null) throw new Error("Streaming responses require a socket-bound HTTP request")
|
|
312
|
+
|
|
313
|
+
const request = this.transportRequest
|
|
314
|
+
if (!request) throw new Error("Streaming responses require a socket-bound HTTP request")
|
|
315
|
+
|
|
316
|
+
if (request.httpMethod() === "HEAD") throw new Error("HEAD responses cannot stream a body")
|
|
317
|
+
|
|
318
|
+
const statusCode = this.getStatusCode()
|
|
319
|
+
if ((statusCode >= 100 && statusCode < 200) || statusCode === 204 || statusCode === 304) {
|
|
320
|
+
throw new Error(`Status ${statusCode} cannot carry a streaming body`)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.streaming = true
|
|
324
|
+
this.transport.beginStreamResponse(this, request)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Emits one chunk to the client as soon as it is produced. Returns a
|
|
329
|
+
* promise that settles once the chunk has been delivered to the socket, so
|
|
330
|
+
* a relay loop can `await response.write(chunk)` and get socket
|
|
331
|
+
* backpressure without an ad-hoc drain wait. Rejects when the stream has
|
|
332
|
+
* ended or been aborted, or when the outbound delivery queue cannot accept
|
|
333
|
+
* the chunk (a stalled client).
|
|
334
|
+
* @param {string | Uint8Array} value - Chunk to emit.
|
|
335
|
+
* @returns {Promise<void>} - Settles after the chunk is delivered.
|
|
336
|
+
*/
|
|
337
|
+
async write(value) {
|
|
338
|
+
if (this.transport === null) throw new Error("write() requires an active streaming response")
|
|
339
|
+
if (!this.streaming) throw new Error("write() requires stream() to be called first")
|
|
340
|
+
if (this.streamEnded || this.streamAborted) return
|
|
341
|
+
|
|
342
|
+
await this.transport.writeStreamChunk(value)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Finishes an active stream: emits the chunked terminator and releases the
|
|
347
|
+
* response for completion logging. Settles after the terminator has been
|
|
348
|
+
* delivered to the socket.
|
|
349
|
+
* @returns {Promise<void>} - Settles after the stream is finished.
|
|
350
|
+
*/
|
|
351
|
+
async end() {
|
|
352
|
+
if (this.transport === null) throw new Error("end() requires an active streaming response")
|
|
353
|
+
if (!this.streaming) throw new Error("end() requires stream() to be called first")
|
|
354
|
+
if (this.streamEnded) return
|
|
355
|
+
|
|
356
|
+
await this.transport.endStreamResponse(this)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Registers a callback fired when the client disconnects mid-stream, so
|
|
361
|
+
* the handler can release whatever the in-flight work reserved. Fired at
|
|
362
|
+
* most once; also fires when the stream is finalized after the client is
|
|
363
|
+
* already gone.
|
|
364
|
+
* @param {() => void} callback - Disconnect callback.
|
|
365
|
+
* @returns {void} - No return value.
|
|
366
|
+
*/
|
|
367
|
+
onStreamClose(callback) {
|
|
368
|
+
this.streamCloseCallbacks.add(callback)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Terminates an active stream after a framework-level failure (the handler
|
|
373
|
+
* threw after `stream()` started): emits the chunked terminator and runs
|
|
374
|
+
* the close callbacks so in-flight work settles its resources. No-op when
|
|
375
|
+
* the stream already ended or was aborted.
|
|
376
|
+
* @returns {void} - No return value.
|
|
377
|
+
*/
|
|
378
|
+
abortStream() {
|
|
379
|
+
if (!this.streaming || this.streamEnded || this.transport === null) return
|
|
380
|
+
|
|
381
|
+
this.streamEnded = true
|
|
382
|
+
this.transport.endStreamResponse(this)
|
|
383
|
+
}
|
|
384
|
+
|
|
243
385
|
/**
|
|
244
386
|
* Runs get file path.
|
|
245
387
|
* @returns {string | null} - File path.
|
|
@@ -69,6 +69,17 @@ export default class VelociousHttpServerInProcessHandler {
|
|
|
69
69
|
}
|
|
70
70
|
})
|
|
71
71
|
|
|
72
|
+
// Streaming responses emit chunks while the request is running. Route
|
|
73
|
+
// their framed output through the counted delivery path so a stalled
|
|
74
|
+
// client cannot buffer engine chunks unboundedly: each chunk settles only
|
|
75
|
+
// after it has been delivered to the socket, which gives the handler
|
|
76
|
+
// backpressure, and the byte/frame limits bound what a silent client can
|
|
77
|
+
// retain before the connection is torn down.
|
|
78
|
+
httpClient.streamOutputSink = (output) => deliveryQueue.enqueueFrame({
|
|
79
|
+
byteLength: Buffer.byteLength(output),
|
|
80
|
+
delivery: () => serverClient.send(output)
|
|
81
|
+
})
|
|
82
|
+
|
|
72
83
|
httpClient.events.on("output", (output, {websocketFrame = false} = {}) => {
|
|
73
84
|
if (output !== null && output !== undefined) {
|
|
74
85
|
const delivery = () => serverClient.send(output)
|
|
@@ -101,9 +112,13 @@ export default class VelociousHttpServerInProcessHandler {
|
|
|
101
112
|
|
|
102
113
|
serverClient.events.on("close", () => {
|
|
103
114
|
deliveryQueue.destroy()
|
|
104
|
-
const cleanup =
|
|
115
|
+
const cleanup = Promise.all([
|
|
116
|
+
httpClient.abortPendingFileResponses(),
|
|
117
|
+
httpClient.abortStreamResponses()
|
|
118
|
+
])
|
|
119
|
+
.then(() => {})
|
|
105
120
|
.catch((error) => {
|
|
106
|
-
this.logger.warn("Failed to abort
|
|
121
|
+
this.logger.warn("Failed to abort responses after client close", error)
|
|
107
122
|
})
|
|
108
123
|
.finally(() => {
|
|
109
124
|
this.pendingClientCloseCleanups.delete(cleanup)
|
|
@@ -160,6 +175,9 @@ export default class VelociousHttpServerInProcessHandler {
|
|
|
160
175
|
httpClient.abortPendingFileResponses().catch((error) => {
|
|
161
176
|
this.logger.warn("Failed to abort file responses during shutdown", error)
|
|
162
177
|
}),
|
|
178
|
+
httpClient.abortStreamResponses().catch((error) => {
|
|
179
|
+
this.logger.warn("Failed to abort streaming responses during shutdown", error)
|
|
180
|
+
}),
|
|
163
181
|
serverClient.end().catch((error) => {
|
|
164
182
|
this.logger.warn("Failed to close client during shutdown", error)
|
|
165
183
|
})
|
|
@@ -217,7 +217,10 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
217
217
|
const settlements = []
|
|
218
218
|
const client = this.clients[clientCount]
|
|
219
219
|
|
|
220
|
-
if (client)
|
|
220
|
+
if (client) {
|
|
221
|
+
settlements.push(client.abortPendingFileResponses())
|
|
222
|
+
settlements.push(client.abortStreamResponses())
|
|
223
|
+
}
|
|
221
224
|
|
|
222
225
|
for (const [transferId, transfer] of this.fileTransfers) {
|
|
223
226
|
if (transfer.clientCount !== clientCount) continue
|