public async Task VerifyChecksumAsync_Data_Is_Truncated_If_Verification_Fails_Using_Single_Chunk() { // Checksum is for "hello world" const string incorrectChecksum = "Kq5sNclPz7QV2+lfQIuc6R7oRu0="; const string message = "Hello World 12345!!@@åäö"; var buffer = new UTF8Encoding(false).GetBytes(message); var bytesWritten = 0L; var fileId = await _fixture.Store.CreateFileAsync(buffer.Length, null, CancellationToken.None); // Emulate several requests do { // Create a new store and cancellation token source on each request as one would do in a real scenario. _fixture.CreateNewStore(); var cts = new CancellationTokenSource(); var requestStream = new RequestStreamFake( (stream, bufferToFill, offset, count, ct) => { // Emulate that the request completed if (bytesWritten == buffer.Length) { return(Task.FromResult(0)); } // Emulate client disconnect after two bytes read. if (stream.Position == 2) { cts.Cancel(); cts.Token.ThrowIfCancellationRequested(); } bytesWritten += 2; return(stream.ReadBackingStreamAsync(bufferToFill, offset, 2, ct)); }, buffer.Skip((int)bytesWritten).ToArray()); await ClientDisconnectGuard.ExecuteAsync( () => _fixture.Store.AppendDataAsync(fileId, requestStream, cts.Token), cts.Token); } while (bytesWritten < buffer.Length); var checksumOk = await _fixture.Store .VerifyChecksumAsync(fileId, "sha1", Convert.FromBase64String(incorrectChecksum), CancellationToken.None); // File should not have been saved. checksumOk.ShouldBeFalse(); var filePath = Path.Combine(_fixture.Path, fileId); new FileInfo(filePath).Length.ShouldBe(0); }
internal override async Task <bool> Handle(ContextAdapter context) { var creationDeferLengthStore = context.Configuration.Store as ITusCreationDeferLengthStore; var fileId = context.GetFileId(); var cancellationToken = context.CancellationToken; var response = context.Response; if (creationDeferLengthStore != null && context.Request.Headers.ContainsKey(HeaderConstants.UploadLength)) { var uploadLength = long.Parse(context.Request.GetHeader(HeaderConstants.UploadLength)); await creationDeferLengthStore.SetUploadLengthAsync(fileId, uploadLength, cancellationToken); } var bytesWritten = 0L; var clientDisconnected = await ClientDisconnectGuard.ExecuteAsync( async() => bytesWritten = await context.Configuration.Store.AppendDataAsync(fileId, context.Request.Body, cancellationToken), cancellationToken); if (clientDisconnected) { return(true); } var expires = await GetOrUpdateExpires(context); if (!await MatchChecksum(context)) { return(await response.Error((HttpStatusCode)460, "Header Upload-Checksum does not match the checksum of the file")); } var fileOffset = long.Parse(context.Request.GetHeader(HeaderConstants.UploadOffset)); response.SetHeader(HeaderConstants.TusResumable, HeaderConstants.TusResumableValue); response.SetHeader(HeaderConstants.UploadOffset, (fileOffset + bytesWritten).ToString()); if (expires.HasValue) { response.SetHeader(HeaderConstants.UploadExpires, expires.Value.ToString("R")); } response.SetStatus((int)HttpStatusCode.NoContent); // Run OnUploadComplete if it has been provided. await RunOnUploadComplete(context, fileOffset, bytesWritten); return(true); }
public async Task VerifyChecksumAsync_Data_Is_Truncated_If_Verification_Fails_Using_Multiple_Chunks() { var chunks = new[] { new Tuple <string, string, bool>("Hello ", "lka6E6To6r7KT1JZv9faQdNooaY=", true), new Tuple <string, string, bool> ("World ", "eh6F0TXbUPbEiz7TtUFJ7WzNb9Q=", true), new Tuple <string, string, bool> ("12345!!@@åäö", "eh6F0TXbUPbEiz7TtUFJ7WzNb9Q=", false) }; var encoding = new UTF8Encoding(false); var totalSize = 0; var fileId = await _fixture.Store.CreateFileAsync(chunks.Sum(f => encoding.GetBytes(f.Item1).Length), null, CancellationToken.None); foreach (var chunk in chunks) { var data = chunk.Item1; var checksum = chunk.Item2; var isValidChecksum = chunk.Item3; var dataBuffer = encoding.GetBytes(data); var bytesWritten = 0L; // Emulate several requests do { // Create a new store and cancellation token source on each request as one would do in a real scenario. _fixture.CreateNewStore(); var cts = new CancellationTokenSource(); var buffer = new RequestStreamFake( (stream, bufferToFill, offset, count, ct) => { // Emulate that the request completed if (bytesWritten == dataBuffer.Length) { return(Task.FromResult(0)); } // Emulate client disconnect after two bytes read. if (stream.Position == 2) { cts.Cancel(); cts.Token.ThrowIfCancellationRequested(); } bytesWritten += 2; return(stream.ReadBackingStreamAsync(bufferToFill, offset, 2, ct)); }, dataBuffer.Skip((int)bytesWritten).ToArray()); await ClientDisconnectGuard.ExecuteAsync( () => _fixture.Store.AppendDataAsync(fileId, buffer, cts.Token), cts.Token); } while (bytesWritten < dataBuffer.Length); var checksumOk = await _fixture.Store.VerifyChecksumAsync(fileId, "sha1", Convert.FromBase64String(checksum), CancellationToken.None); checksumOk.ShouldBe(isValidChecksum); totalSize += isValidChecksum ? dataBuffer.Length : 0; } var filePath = Path.Combine(_fixture.Path, fileId); new FileInfo(filePath).Length.ShouldBe(totalSize); }