Skip to content

Commit

Permalink
Use span based Upload-Concat parser
Browse files Browse the repository at this point in the history
  • Loading branch information
smatsson committed Oct 11, 2021
1 parent d7d7bd1 commit 0206fb8
Show file tree
Hide file tree
Showing 11 changed files with 689 additions and 448 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.Text;
using tusdotnet.Models.Concatenation;

namespace tusdotnet.benchmark.Benchmarks
{
[MemoryDiagnoser]
public class UploadConcatParserBenchmark
{
private const string _uploadConcatHeader = "final;/files/partial1 http://localhost:1321/files/partial2";
private const string _urlPath = "/files";

[Benchmark(Baseline = true)]
public FileConcat UploadConcatParserStringBased()
{
return Parsers.UploadConcatParserHelpers.UploadConcatParserStringBased.ParseAndValidate(_uploadConcatHeader, _urlPath).Type;
}

[Benchmark()]
public FileConcat UploadConcatParserSpanBased()
{
return Parsers.UploadConcatParserHelpers.UploadConcatParserSpanBased.ParseAndValidate(_uploadConcatHeader, _urlPath).Type;
}


}
}
4 changes: 3 additions & 1 deletion Source/tusdotnet.benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Threading.Tasks;
using BenchmarkDotNet.Running;
//using tusdotnet.benchmark.Benchmarks;
using tusdotnet.benchmark.Benchmarks;

namespace tusdotnet.benchmark
{
Expand All @@ -13,6 +13,8 @@ public static void Main()
//var summary = BenchmarkRunner.Run<NoTusResumableHeader>();
//var summary = BenchmarkRunner.Run<RequestIsNotForTusEndpoint>();

var summary = BenchmarkRunner.Run<UploadConcatParserBenchmark>();

//new MetadataParser().TestNewWithTextRead();
//Test().GetAwaiter().GetResult();
}
Expand Down
4 changes: 0 additions & 4 deletions Source/tusdotnet.benchmark/tusdotnet.benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@
<ProjectReference Include="..\tusdotnet\tusdotnet.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Benchmarks\" />
</ItemGroup>

</Project>
621 changes: 300 additions & 321 deletions Source/tusdotnet.test/Tests/ConcatenationTests.cs

Large diffs are not rendered by default.

114 changes: 60 additions & 54 deletions Source/tusdotnet.test/Tests/ModelTests/UploadConcatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,72 @@

namespace tusdotnet.test.Tests.ModelTests
{
public class UploadConcatTests
{
[Fact]
public void Can_Parse_Upload_Concat_Header()
{
// None
var uploadConcat = new UploadConcat(null);
uploadConcat.IsValid.ShouldBeTrue();
uploadConcat.Type.ShouldBeNull();
public class UploadConcatTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
public void Can_Parse_Upload_Concat_Header_When_Empty(string value)
{
var uploadConcat = new UploadConcat(value);
uploadConcat.IsValid.ShouldBeTrue();
uploadConcat.Type.ShouldBeNull();
}

uploadConcat = new UploadConcat("");
uploadConcat.IsValid.ShouldBeTrue();
uploadConcat.Type.ShouldBeNull();
[Fact]
public void Can_Parse_Upload_Concat_Header_For_Partial()
{
var uploadConcat = new UploadConcat("partial");
uploadConcat.IsValid.ShouldBeTrue();
uploadConcat.Type.ShouldBeOfType(typeof(FileConcatPartial));

// Partial
uploadConcat = new UploadConcat("partial");
uploadConcat.IsValid.ShouldBeTrue();
uploadConcat.Type.ShouldBeOfType(typeof(FileConcatPartial));
}

// Final
uploadConcat = new UploadConcat("final;/files/file1 /files/file2", "/files");
uploadConcat.IsValid.ShouldBeTrue();
var finalConcat = uploadConcat.Type as FileConcatFinal;
finalConcat.ShouldNotBeNull();
// ReSharper disable once PossibleNullReferenceException
finalConcat.Files.Length.ShouldBe(2);
finalConcat.Files[0].ShouldBe("file1");
finalConcat.Files[1].ShouldBe("file2");
}
[Theory]
[InlineData("final;/files/file1 /files/file2", 2, "file1,file2")]
[InlineData("final;/files/file1 http://localhost/files/file2 https://example.org/files/file3?queryparam=123 https://example.org/files/file4?queryparam=123#111 https://example.org/files/file5#123", 5, "file1,file2,file3,file4,file5")]
public void Can_Parse_Upload_Concat_Header_For_Final(string uploadConcatHeader, int expectedFileCount, string expectedFileIdCsv)
{
var uploadConcat = new UploadConcat(uploadConcatHeader, "/files");
uploadConcat.IsValid.ShouldBeTrue();
var finalConcat = uploadConcat.Type as FileConcatFinal;
finalConcat.ShouldNotBeNull();
finalConcat.Files.Length.ShouldBe(expectedFileCount);

[Fact]
public void Sets_Error_If_Type_Is_Not_Final_Nor_Partial()
{
var uploadConcat = new UploadConcat("somevalue");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Upload-Concat header is invalid. Valid values are \"partial\" and \"final\" followed by a list of files to concatenate");
}
var expectedFiles = expectedFileIdCsv.Split(',');
expectedFiles.Length.ShouldBe(expectedFileCount);

[Fact]
public void Sets_Error_If_Final_Does_Not_Contain_Files()
{
var uploadConcat = new UploadConcat("final");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Unable to parse Upload-Concat header");
for (int i = 0; i < finalConcat.Files.Length; i++)
{
finalConcat.Files[i].ShouldBe(expectedFiles[i]);
}
}

uploadConcat = new UploadConcat("final;");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Unable to parse Upload-Concat header");
[Fact]
public void Sets_Error_If_Type_Is_Not_Final_Nor_Partial()
{
var uploadConcat = new UploadConcat("somevalue");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Header Upload-Concat: Header is invalid. Valid values are \"partial\" and \"final\" followed by a list of file urls to concatenate");
}

uploadConcat = new UploadConcat("final; ");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Unable to parse Upload-Concat header");
}
[Theory]
[InlineData("final")]
[InlineData("final;")]
[InlineData("final; ")]
public void Sets_Error_If_Final_Does_Not_Contain_Files(string uploadConcatHeader)
{
var uploadConcat = new UploadConcat(uploadConcatHeader);
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Header Upload-Concat: Header is invalid. Valid values are \"partial\" and \"final\" followed by a list of file urls to concatenate");
}

[Fact]
public void Sets_Error_If_Final_Contains_Files_For_Other_UrlPath()
{
var uploadConcat = new UploadConcat("final;/otherfiles/file1 /otherfiles/file2", "/files");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Unable to parse Upload-Concat header");
}
}
[Fact]
public void Sets_Error_If_Final_Contains_Files_For_Other_UrlPath()
{
var uploadConcat = new UploadConcat("final;/otherfiles/file1 /otherfiles/file2", "/files");
uploadConcat.IsValid.ShouldBeFalse();
uploadConcat.ErrorMessage.ShouldBe("Header Upload-Concat: Header is invalid. Valid values are \"partial\" and \"final\" followed by a list of file urls to concatenate");
}
}
}
77 changes: 9 additions & 68 deletions Source/tusdotnet/Models/Concatenation/UploadConcat.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using tusdotnet.Parsers;

namespace tusdotnet.Models.Concatenation
{
/// <summary>
/// Container for uploaded file concatenation information.
/// </summary>
public class UploadConcat
/// <summary>
/// Container for uploaded file concatenation information.
/// </summary>
public class UploadConcat
{
/// <summary>
/// The type of concatenation used. Is null if no concatenation info was provided or if the info is invalid.
Expand Down Expand Up @@ -51,69 +50,11 @@ public UploadConcat(string uploadConcat, string urlPath)
return;
}

var temp = uploadConcat.Split(';');
var result = UploadConcatParser.ParseAndValidate(uploadConcat, urlPath);

// Unable to parse Upload-Concat header
var type = temp[0].ToLower();
switch (type)
{
case "partial":
Type = new FileConcatPartial();
break;
case "final":
Type = ParseFinal(temp, urlPath);
break;
default:
IsValid = false;
ErrorMessage = "Upload-Concat header is invalid. Valid values are \"partial\" and \"final\" followed by a list of files to concatenate";
return;
}
}

/// <summary>
/// Parses the "final" concatenation type based on the parts provided.
/// Will validate and strip the url path provided to make sure that all files are in the same store.
/// </summary>
/// <param name="parts">The separated parts of the Upload-Concat header</param>
/// <param name="urlPath">The UrlPath property in the ITusConfiguration</param>
/// <returns>THe parse final concatenation</returns>
// ReSharper disable once SuggestBaseTypeForParameter
private FileConcatFinal ParseFinal(string[] parts, string urlPath)
{
if (parts.Length < 2)
{
IsValid = false;
ErrorMessage = "Unable to parse Upload-Concat header";
return null;
}

var fileUris = parts[1].Split(' ');
var fileIds = new List<string>(fileUris.Length);

foreach (var fileUri in fileUris)
{
if (string.IsNullOrWhiteSpace(fileUri) || !Uri.TryCreate(fileUri, UriKind.RelativeOrAbsolute, out Uri uri))
{
IsValid = false;
ErrorMessage = "Unable to parse Upload-Concat header";
break;
}

var localPath = uri.IsAbsoluteUri
? uri.LocalPath
: uri.ToString();

if (!localPath.StartsWith(urlPath, StringComparison.OrdinalIgnoreCase))
{
IsValid = false;
ErrorMessage = "Unable to parse Upload-Concat header";
break;
}

fileIds.Add(localPath.Substring(urlPath.Length).Trim('/'));
}

return new FileConcatFinal(fileIds.ToArray());
IsValid = result.Success;
ErrorMessage= result.ErrorMessage;
Type = result.Type;
}
}
}
18 changes: 18 additions & 0 deletions Source/tusdotnet/Parsers/UploadConcatParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using tusdotnet.Parsers.UploadConcatParserHelpers;

namespace tusdotnet.Parsers
{
internal static class UploadConcatParser
{
internal static UploadConcatParserResult ParseAndValidate(string uploadConcatHeader, string urlPath)
{
#if NETCOREAPP3_1_OR_GREATER

return UploadConcatParserSpanBased.ParseAndValidate(uploadConcatHeader, urlPath);
#else
return UploadConcatParserStringBased.ParseAndValidate(uploadConcatHeader, urlPath);

#endif
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using tusdotnet.Constants;

namespace tusdotnet.Parsers.UploadConcatParserHelpers
{
internal class UploadConcatParserErrorTexts
{
internal static string HEADER_IS_INVALID = $"Header {HeaderConstants.UploadConcat}: Header is invalid. Valid values are \"partial\" and \"final\" followed by a list of file urls to concatenate";
}
}
Loading

0 comments on commit 0206fb8

Please sign in to comment.