@@ -15,20 +15,13 @@ import { Accounting } from '../services/accounting.js'
1515 *
1616 * @type {Middleware<EgressTrackerContext, EgressTrackerContext, Environment> }
1717 */
18- export function withEgressHandler ( handler ) {
18+ export function withEgressHandler ( handler ) {
1919 return async ( req , env , ctx ) => {
2020 if ( env . FF_EGRESS_TRACKER_ENABLED !== 'true' ) {
2121 return handler ( req , env , ctx )
2222 }
2323
24- let response
25- try {
26- response = await handler ( req , env , ctx )
27- } catch ( error ) {
28- console . error ( 'Error in egress tracker handler:' , error )
29- throw error
30- }
31-
24+ const response = await handler ( req , env , ctx )
3225 if ( ! response . ok || ! response . body ) {
3326 return response
3427 }
@@ -38,17 +31,18 @@ export function withEgressHandler (handler) {
3831 serviceURL : env . ACCOUNTING_SERVICE_URL
3932 } )
4033
41- const { readable, writable } = createEgressPassThroughStream ( ctx , accounting , dataCid )
42-
43- try {
44- ctx . waitUntil ( response . body . pipeTo ( writable ) )
45- } catch ( error ) {
46- console . error ( 'Error in egress tracker handler:' , error )
47- // Original response in case of an error to avoid breaking the chain and serve the content
48- return response
49- }
34+ const responseBody = response . body . pipeThrough (
35+ createByteCountStream ( ( totalBytesServed ) => {
36+ // Non-blocking call to the accounting service to record egress
37+ if ( totalBytesServed > 0 ) {
38+ ctx . waitUntil (
39+ accounting . record ( dataCid , totalBytesServed , new Date ( ) . toISOString ( ) )
40+ ) ;
41+ }
42+ } )
43+ ) ;
5044
51- return new Response ( readable , {
45+ return new Response ( responseBody , {
5246 status : response . status ,
5347 statusText : response . statusText ,
5448 headers : response . headers
@@ -60,58 +54,39 @@ export function withEgressHandler (handler) {
6054 * Creates a TransformStream to count bytes served to the client.
6155 * It records egress when the stream is finalized without an error.
6256 *
63- * @param {import('@web3-storage/gateway-lib/middleware').Context } ctx - The context object.
64- * @param {AccountingService } accounting - The accounting service instance to record egress.
65- * @param {import('@web3-storage/gateway-lib/handlers').CID } dataCid - The CID of the served content.
66- * @returns {TransformStream } - The created TransformStream.
57+ * @param {(totalBytesServed: number) => void } onClose
58+ * @template {Uint8Array} T
59+ * @returns {TransformStream<T, T> } - The created TransformStream.
6760 */
68- function createEgressPassThroughStream ( ctx , accounting , dataCid ) {
61+ function createByteCountStream ( onClose ) {
6962 let totalBytesServed = 0
7063
7164 return new TransformStream ( {
72- /**
73- * The start function is called when the stream is being initialized.
74- * It resets the total bytes served to 0.
75- */
76- start ( ) {
77- totalBytesServed = 0
78- } ,
7965 /**
8066 * The transform function is called for each chunk of the response body.
8167 * It enqueues the chunk and updates the total bytes served.
8268 * If an error occurs, it signals an error to the controller and logs it.
8369 * The bytes are not counted in case of enqueuing an error.
84- * @param {Uint8Array } chunk
85- * @param {TransformStreamDefaultController } controller
8670 */
87- async transform ( chunk , controller ) {
71+ async transform ( chunk , controller ) {
8872 try {
8973 controller . enqueue ( chunk )
9074 totalBytesServed += chunk . byteLength
9175 } catch ( error ) {
92- console . error ( 'Error while counting egress bytes:' , error )
76+ console . error ( 'Error while counting bytes:' , error )
9377 controller . error ( error )
9478 }
9579 } ,
9680
9781 /**
9882 * The flush function is called when the stream is being finalized,
9983 * which is when the response is being sent to the client.
100- * So before the response is sent, we record the egress.
101- * It is called only once and it triggers a non-blocking call to the accounting service.
84+ * So before the response is sent, we record the egress using the callback.
10285 * If an error occurs, the egress is not recorded.
103- * NOTE: The flush function is NOT called in case of an stream error.
86+ * NOTE: The flush function is NOT called in case of a stream error.
10487 */
105- async flush ( controller ) {
106- try {
107- // Non-blocking call to the accounting service to record egress
108- if ( totalBytesServed > 0 ) {
109- ctx . waitUntil ( accounting . record ( dataCid , totalBytesServed , new Date ( ) . toISOString ( ) ) )
110- }
111- } catch ( error ) {
112- console . error ( 'Error while recording egress:' , error )
113- controller . error ( error )
114- }
88+ async flush ( ) {
89+ onClose ( totalBytesServed )
11590 }
11691 } )
11792}
0 commit comments