public async Task Handles_Abrupt_Disconnects_Gracefully(string pipeline) { // This test differs in structure from the others as this test needs to test that tusdotnet handles pipeline specifics correctly. // We can only emulate the pipelines so this test might not be 100% accurate. // Also we must bypass the TestServer and go directly for the TusProtcolHandler as we would otherwise introduce yet another pipeline. var pipelineDetails = PipelineDisconnectEmulationDataAttribute.GetInfo(pipeline); var cts = new CancellationTokenSource(); var store = Substitute.For <ITusStore>().WithExistingFile("testfile", uploadLength: 10, uploadOffset: 5); var requestStream = Substitute.For <Stream>(); requestStream.ReadAsync(Arg.Any <byte[]>(), Arg.Any <int>(), Arg.Any <int>(), Arg.Any <CancellationToken>()) .ThrowsForAnyArgs(_ => { if (pipelineDetails.FlagsCancellationTokenAsCancelled) { cts.Cancel(); } throw pipelineDetails.ExceptionThatIsThrown; }); store.AppendDataAsync("testfile", Arg.Any <Stream>(), Arg.Any <CancellationToken>()) .ReturnsForAnyArgs <Task <long> >(async callInfo => await callInfo.Arg <Stream>().ReadAsync(null, 0, 0, callInfo.Arg <CancellationToken>())); var responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var responseStatus = HttpStatusCode.OK; var response = new ResponseAdapter { Body = new MemoryStream(), SetHeader = (key, value) => responseHeaders[key] = value, SetStatus = status => responseStatus = status }; var context = new ContextAdapter { CancellationToken = cts.Token, Configuration = new DefaultTusConfiguration { UrlPath = "/files", Store = store }, Request = new RequestAdapter("/files") { Headers = new Dictionary <string, List <string> >(StringComparer.OrdinalIgnoreCase) { { "Content-Type", new List <string>(1) { "application/offset+octet-stream" } }, { "Tus-Resumable", new List <string>(1) { "1.0.0" } }, { "Upload-Offset", new List <string>(1) { "5" } } }, Method = "PATCH", Body = requestStream, RequestUri = new Uri("https://localhost:8080/files/testfile") }, Response = response }; var handled = await TusProtocolHandlerIntentBased.Invoke(context); handled.ShouldBe(ResultType.StopExecution); responseStatus.ShouldBe(HttpStatusCode.OK); responseHeaders.Count.ShouldBe(0); response.Body.Length.ShouldBe(0); }
public async Task Checksum_Is_Verified_If_Client_Disconnects() { var cts = new CancellationTokenSource(); var store = Substitute.For <ITusStore, ITusChecksumStore>(); store.FileExistAsync("checksum", CancellationToken.None).ReturnsForAnyArgs(true); store.GetUploadOffsetAsync("checksum", Arg.Any <CancellationToken>()).Returns(5); store.GetUploadLengthAsync("checksum", Arg.Any <CancellationToken>()).Returns(10); // Cancel token source to emulate a client disconnect store.AppendDataAsync("checksum", Arg.Any <Stream>(), Arg.Any <CancellationToken>()).Returns(5).AndDoes(_ => cts.Cancel()); var cstore = (ITusChecksumStore)store; cstore.GetSupportedAlgorithmsAsync(CancellationToken.None).ReturnsForAnyArgs(new[] { "sha1" }); cstore.VerifyChecksumAsync(null, null, null, CancellationToken.None).ReturnsForAnyArgs(false); var responseStatusCode = HttpStatusCode.OK; var responseStream = new MemoryStream(); await TusProtocolHandlerIntentBased.Invoke(new ContextAdapter { CancellationToken = cts.Token, Configuration = new DefaultTusConfiguration { Store = store, UrlPath = "/files", }, Request = new RequestAdapter("/files") { Body = new MemoryStream(), RequestUri = new System.Uri("https://localhost/files/checksum"), Headers = new Dictionary <string, List <string> > { { Constants.HeaderConstants.TusResumable, new List <string> { Constants.HeaderConstants.TusResumableValue } }, // Just random gibberish as checksum { Constants.HeaderConstants.UploadChecksum, new List <string> { "sha1 Kq5sNclPz7QV2+lfQIuc6R7oRu0=" } }, { Constants.HeaderConstants.UploadOffset, new List <string> { "5" } }, { Constants.HeaderConstants.ContentType, new List <string> { "application/offset+octet-stream" } }, }, Method = "PATCH" }, Response = new ResponseAdapter { Body = responseStream, SetHeader = (key, value) => { }, SetStatus = status => responseStatusCode = status } }); await cstore.ReceivedWithAnyArgs().VerifyChecksumAsync(null, null, null, CancellationToken.None); responseStatusCode.ShouldBe((HttpStatusCode)460); responseStream.Seek(0, SeekOrigin.Begin); using (var sr = new StreamReader(responseStream)) { sr.ReadToEnd().ShouldBe("Header Upload-Checksum does not match the checksum of the file"); } }