11using System ;
22using System . Collections . Concurrent ;
33using System . IO ;
4- using System . Net ;
5- using System . Security . Cryptography ;
6- using System . Text ;
74using System . Linq ;
5+ using System . Net ;
86using System . Threading ;
97using System . Threading . Tasks ;
108using ManagedCode . Communication ;
@@ -48,10 +46,13 @@ public async Task<Result> AppendChunkAsync(FileUploadPayload payload, Cancellati
4846
4947 var descriptor = payload . Payload ;
5048 var uploadId = ValidateUploadId ( ChunkUploadDescriptor . ResolveUploadId ( descriptor ) ) ;
49+ var chunkLength = payload . File . Length ;
5150 if ( descriptor . ChunkIndex <= 0 || descriptor . TotalChunks < 0 ||
5251 ( descriptor . TotalChunks > 0 && descriptor . ChunkIndex > descriptor . TotalChunks ) ||
53- descriptor . ChunkSize <= 0 || descriptor . FileSize < 0 || payload . File . Length > descriptor . ChunkSize )
52+ descriptor . ChunkSize <= 0 || descriptor . FileSize < 0 || chunkLength < 0 || chunkLength > descriptor . ChunkSize )
5453 return Result . Fail ( HttpStatusCode . BadRequest , "Invalid chunk index or declared size" ) ;
54+ if ( chunkLength > ChunkUploadSession . ResolveExpectedLength ( descriptor . TotalChunks , descriptor . FileSize ) )
55+ return Result . Fail ( HttpStatusCode . BadRequest , "Upload exceeds its declared length" ) ;
5556
5657 RemoveExpiredSessions ( ) ;
5758 var session = AdmitSession ( uploadId , descriptor ) ;
@@ -63,10 +64,14 @@ public async Task<Result> AppendChunkAsync(FileUploadPayload payload, Cancellati
6364 {
6465 if ( session . Closed )
6566 return Result . Fail ( "Upload session is closed" ) ;
67+ if ( TryExpireSession ( session , DateTimeOffset . UtcNow ) )
68+ return Result . Fail ( "Upload session has expired" ) ;
6669 if ( session . TotalChunks != descriptor . TotalChunks || session . FileSize != descriptor . FileSize )
6770 return Result . Fail ( HttpStatusCode . BadRequest , "Chunk does not match the upload session" ) ;
71+ if ( ! session . CanAcceptChunk ( descriptor . ChunkIndex , chunkLength ) )
72+ return Result . Fail ( HttpStatusCode . BadRequest , "Upload exceeds its declared length" ) ;
6873
69- await ChunkUploadFiles . StageChunkAsync ( session , payload , cancellationToken ) . ConfigureAwait ( false ) ;
74+ await ChunkUploadFiles . StageChunkAsync ( session , payload , chunkLength , cancellationToken ) . ConfigureAwait ( false ) ;
7075 return Result . Succeed ( ) ;
7176 }
7277 finally
@@ -84,8 +89,7 @@ public async Task<Result> AppendChunkAsync(FileUploadPayload payload, Cancellati
8489 if ( _options . MaxActiveSessions > 0 && _sessions . Count >= _options . MaxActiveSessions )
8590 return null ;
8691
87- var directoryKey = Convert . ToHexStringLower ( SHA256 . HashData ( Encoding . UTF8 . GetBytes ( uploadId ) ) ) ;
88- var directory = Path . Combine ( _options . TempPath , directoryKey ) ;
92+ var directory = Path . Combine ( _options . TempPath , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
8993 Directory . CreateDirectory ( directory ) ;
9094 var session = new ChunkUploadSession ( uploadId , descriptor . FileName ?? descriptor . UploadId ,
9195 descriptor . ContentType , descriptor . TotalChunks , descriptor . ChunkSize , descriptor . FileSize , directory ) ;
@@ -118,17 +122,18 @@ public async Task<Result<ChunkUploadCompleteResponse>> CompleteAsync(ChunkUpload
118122 acquired = true ;
119123 if ( session . Closed )
120124 return Result < ChunkUploadCompleteResponse > . Fail ( "Upload session is closed" ) ;
125+ if ( TryExpireSession ( session , DateTimeOffset . UtcNow ) )
126+ return Result < ChunkUploadCompleteResponse > . Fail ( "Upload session has expired" ) ;
121127
122128 session . Touch ( ) ;
123129 session . EnsureAllChunksPresent ( ) ;
124130 var orderedChunks = session . ChunkFiles
125131 . OrderBy ( x => x . Key )
126- . Select ( x => x . Value )
132+ . Select ( x => x . Value . Path )
127133 . ToArray ( ) ;
128134
129135 var mergedFilePath = Path . Combine ( session . WorkingDirectory , "merged.bin" ) ;
130- var expectedLength = session . TotalChunks == 0 && session . FileSize == 0 ? null : session . FileSize ;
131- var crc = await ChunkUploadFiles . MergeChunksAsync ( mergedFilePath , orderedChunks , expectedLength , cancellationToken ) . ConfigureAwait ( false ) ;
136+ var crc = await ChunkUploadFiles . MergeChunksAsync ( mergedFilePath , orderedChunks , session . ExpectedLength , cancellationToken ) . ConfigureAwait ( false ) ;
132137
133138 BlobMetadata ? metadata = null ;
134139 if ( request . CommitToStorage )
@@ -211,21 +216,31 @@ private void RemoveExpiredSessions()
211216 return ;
212217 }
213218
214- var expirationThreshold = DateTimeOffset . UtcNow - _options . SessionTtl ;
219+ var now = DateTimeOffset . UtcNow ;
215220 foreach ( var ( _, session ) in _sessions )
216221 {
217222 if ( ! session . Gate . Wait ( 0 ) )
218223 continue ;
219224
220225 try
221226 {
222- if ( ! session . Closed && session . LastTouchedUtc < expirationThreshold )
223- CloseSession ( session ) ;
227+ if ( ! session . Closed )
228+ TryExpireSession ( session , now ) ;
224229 }
225230 finally
226231 {
227232 session . Gate . Release ( ) ;
228233 }
229234 }
230235 }
236+
237+ private bool TryExpireSession ( ChunkUploadSession session , DateTimeOffset now )
238+ {
239+ var ttl = _options . SessionTtl ;
240+ if ( ttl <= TimeSpan . Zero || now - session . LastTouchedUtc < ttl )
241+ return false ;
242+
243+ CloseSession ( session ) ;
244+ return true ;
245+ }
231246}
0 commit comments