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);
        }
Exemple #2
0
        public async Task AppendDataAsync_Uses_The_Read_And_Write_Buffers_Correctly(int readBufferSize, int writeBufferSize, int fileSize, int expectedNumberOfWrites, int expectedNumberOfReads)
        {
            int numberOfReadsPerWrite = (int)Math.Ceiling((double)writeBufferSize / readBufferSize);

            var store = new TusDiskStore(_fixture.Path, false, new TusDiskBufferSize(writeBufferSize, readBufferSize));

            var totalNumberOfWrites         = 0;
            var totalNumberOfReads          = 0;
            var numberOfReadsSinceLastWrite = 0;
            int totalBytesWritten           = 0;

            var fileId = await store.CreateFileAsync(fileSize, null, CancellationToken.None);

            var requestStream = new RequestStreamFake(async(RequestStreamFake stream,
                                                            byte[] bufferToFill,
                                                            int offset,
                                                            int count,
                                                            CancellationToken cancellationToken)
                                                      =>
            {
                count.ShouldBe(readBufferSize);

                var bytesReadFromStream = await stream.ReadBackingStreamAsync(bufferToFill, offset, count, cancellationToken);

                // There should have been a write after the previous read.
                if (numberOfReadsSinceLastWrite > numberOfReadsPerWrite)
                {
                    // Calculate the amount of data that should have been written to disk so far.
                    var expectedFileSizeRightNow = CalculateExpectedFileSize(totalNumberOfReads, readBufferSize, writeBufferSize);

                    // Assert that the size on disk is correct.
                    GetLengthFromFileOnDisk().ShouldBe(expectedFileSizeRightNow);

                    totalNumberOfWrites++;

                    // Set to one as the write happened on the previous write, making this the second read since that write.
                    numberOfReadsSinceLastWrite = 1;
                }

                numberOfReadsSinceLastWrite++;
                totalNumberOfReads++;

                totalBytesWritten += bytesReadFromStream;

                return(bytesReadFromStream);
            }, new byte[fileSize]);

            await store.AppendDataAsync(fileId, requestStream, CancellationToken.None);

            GetLengthFromFileOnDisk().ShouldBe(fileSize);

            totalNumberOfWrites++;

            // -1 due to the last read returning 0 bytes as the stream is then drained
            Assert.Equal(expectedNumberOfReads, totalNumberOfReads - 1);
            Assert.Equal(expectedNumberOfWrites, totalNumberOfWrites);

            long GetLengthFromFileOnDisk()
            {
                return(new FileInfo(Path.Combine(_fixture.Path, fileId)).Length);
            }
        }
        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);
        }