11const V4Transform = require ( '../../../auth/streamingV4/V4Transform' ) ;
22const TrailingChecksumTransform = require ( '../../../auth/streamingV4/trailingChecksumTransform' ) ;
33const ChecksumTransform = require ( '../../../auth/streamingV4/ChecksumTransform' ) ;
4- const {
5- getChecksumDataFromHeaders,
6- arsenalErrorFromChecksumError,
7- } = require ( '../../apiUtils/integrity/validateChecksums' ) ;
84const { errors, errorInstances, jsutil } = require ( 'arsenal' ) ;
95const { unsupportedSignatureChecksums } = require ( '../../../../constants' ) ;
106
117/**
128 * Prepares the request stream for data storage by wrapping it in the
139 * appropriate transform pipeline based on the x-amz-content-sha256 header.
14- * Always returns a ChecksumTransform as the final stream.
15- * If no checksum was sent by the client a CRC64NVME ChecksumTransform is returned.
10+ * The returned stream is always the primary ChecksumTransform (the stored
11+ * checksum). When a secondary checksum is requested it is inserted upstream
12+ * of the primary and exposed via secondaryChecksumStream.
1613 *
1714 * @param {object } request - incoming HTTP request with headers and body stream
1815 * @param {object|null } streamingV4Params - v4 streaming auth params (accessKey,
1916 * signatureFromRequest, region, scopeDate, timestamp, credentialScope), or
2017 * null/undefined for non-v4 requests
18+ * @param {object } checksums - checksum configuration
19+ * @param {object } checksums.primary - primary checksum
20+ * ({ algorithm, isTrailer, expected }) — validated and its digest returned
21+ * @param {object|null } checksums.secondary - optional secondary checksum
22+ * ({ algorithm, isTrailer, expected }) — only validated; used for MPU parts
2123 * @param {RequestLogger } log - request logger
2224 * @param {function } errCb - error callback invoked if a stream error occurs
23- * @return {{ error: Arsenal.Error|null, stream: ChecksumTransform|null } }
24- * error is set and stream is null if the request headers are invalid;
25- * otherwise error is null and stream is the ChecksumTransform to read from
25+ * @return {{ error: Arsenal.Error|null, stream: ChecksumTransform|null,
26+ * secondaryChecksumStream: ChecksumTransform|null }}
2627 */
27- function prepareStream ( request , streamingV4Params , log , errCb ) {
28+ function prepareStream ( request , streamingV4Params , checksums , log , errCb ) {
2829 const xAmzContentSHA256 = request . headers [ 'x-amz-content-sha256' ] ;
29-
30- const checksumAlgo = getChecksumDataFromHeaders ( request . headers ) ;
31- if ( checksumAlgo . error ) {
32- log . debug ( 'prepareStream invalid checksum headers' , checksumAlgo ) ;
33- return { error : arsenalErrorFromChecksumError ( checksumAlgo ) , stream : null } ;
34- }
30+ const { primary, secondary } = checksums ;
3531
3632 switch ( xAmzContentSHA256 ) {
3733 case 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD' : {
@@ -54,37 +50,49 @@ function prepareStream(request, streamingV4Params, log, errCb) {
5450 request . pipe ( v4Transform ) ;
5551 v4Transform . headers = request . headers ;
5652
57- const checksumedStream = new ChecksumTransform (
58- checksumAlgo . algorithm ,
59- checksumAlgo . expected ,
60- checksumAlgo . isTrailer ,
61- log ,
62- ) ;
63- checksumedStream . on ( 'error' , onStreamError ) ;
64- v4Transform . pipe ( checksumedStream ) ;
65- return { error : null , stream : checksumedStream } ;
53+ let secondaryChecksumStream = null ;
54+ let stream = v4Transform ;
55+ if ( secondary ) {
56+ secondaryChecksumStream = new ChecksumTransform (
57+ secondary . algorithm , secondary . expected ,
58+ secondary . isTrailer , log ) ;
59+ secondaryChecksumStream . on ( 'error' , onStreamError ) ;
60+ stream = v4Transform . pipe ( secondaryChecksumStream ) ;
61+ }
62+
63+ const primaryStream = new ChecksumTransform (
64+ primary . algorithm , primary . expected , primary . isTrailer , log ) ;
65+ primaryStream . on ( 'error' , onStreamError ) ;
66+ return { error : null , stream : stream . pipe ( primaryStream ) , secondaryChecksumStream } ;
6667 }
6768 case 'STREAMING-UNSIGNED-PAYLOAD-TRAILER' : {
68- // Use a once-guard so that auto-destroying both piped streams
69- // when one errors does not result in errCb being called twice.
7069 const onStreamError = jsutil . once ( errCb ) ;
7170 const trailingChecksumTransform = new TrailingChecksumTransform ( log ) ;
7271 trailingChecksumTransform . on ( 'error' , onStreamError ) ;
7372 request . pipe ( trailingChecksumTransform ) ;
7473 trailingChecksumTransform . headers = request . headers ;
7574
76- const checksumedStream = new ChecksumTransform (
77- checksumAlgo . algorithm ,
78- checksumAlgo . expected ,
79- checksumAlgo . isTrailer ,
80- log ,
81- ) ;
82- checksumedStream . on ( 'error' , onStreamError ) ;
83- trailingChecksumTransform . on ( 'trailer' , ( name , value ) => {
84- checksumedStream . setExpectedChecksum ( name , value ) ;
85- } ) ;
86- trailingChecksumTransform . pipe ( checksumedStream ) ;
87- return { error : null , stream : checksumedStream } ;
75+ let secondaryChecksumStream = null ;
76+ let stream = trailingChecksumTransform ;
77+ if ( secondary ) {
78+ secondaryChecksumStream = new ChecksumTransform (
79+ secondary . algorithm , secondary . expected ,
80+ secondary . isTrailer , log ) ;
81+ secondaryChecksumStream . on ( 'error' , onStreamError ) ;
82+ stream = trailingChecksumTransform . pipe ( secondaryChecksumStream ) ;
83+ trailingChecksumTransform . on ( 'trailer' , ( name , value ) => {
84+ secondaryChecksumStream . setExpectedChecksum ( name , value ) ;
85+ } ) ;
86+ }
87+
88+ const primaryStream = new ChecksumTransform ( primary . algorithm , primary . expected , primary . isTrailer , log ) ;
89+ primaryStream . on ( 'error' , onStreamError ) ;
90+ if ( ! secondary ) {
91+ trailingChecksumTransform . on ( 'trailer' , ( name , value ) => {
92+ primaryStream . setExpectedChecksum ( name , value ) ;
93+ } ) ;
94+ }
95+ return { error : null , stream : stream . pipe ( primaryStream ) , secondaryChecksumStream } ;
8896 }
8997 case 'UNSIGNED-PAYLOAD' : // Fallthrough
9098 default : {
@@ -95,15 +103,20 @@ function prepareStream(request, streamingV4Params, log, errCb) {
95103 } ;
96104 }
97105
98- const checksumedStream = new ChecksumTransform (
99- checksumAlgo . algorithm ,
100- checksumAlgo . expected ,
101- checksumAlgo . isTrailer ,
102- log ,
103- ) ;
104- checksumedStream . on ( 'error' , errCb ) ;
105- request . pipe ( checksumedStream ) ;
106- return { error : null , stream : checksumedStream } ;
106+ const onStreamError = secondary ? jsutil . once ( errCb ) : errCb ;
107+ let secondaryChecksumStream = null ;
108+ let stream = request ;
109+ if ( secondary ) {
110+ secondaryChecksumStream = new ChecksumTransform (
111+ secondary . algorithm , secondary . expected ,
112+ secondary . isTrailer , log ) ;
113+ secondaryChecksumStream . on ( 'error' , onStreamError ) ;
114+ stream = request . pipe ( secondaryChecksumStream ) ;
115+ }
116+
117+ const primaryStream = new ChecksumTransform ( primary . algorithm , primary . expected , primary . isTrailer , log ) ;
118+ primaryStream . on ( 'error' , onStreamError ) ;
119+ return { error : null , stream : stream . pipe ( primaryStream ) , secondaryChecksumStream } ;
107120 }
108121 }
109122}
0 commit comments