public async Task Supports_Async_Configuration_Factories() { var urlPath = $"/{Guid.NewGuid().ToString()}"; var tusConfiguration = new DefaultTusConfiguration { UrlPath = urlPath, Store = Substitute.For <ITusStore>() }; // Empty configuration using (var server = TestServerFactory.Create(app => { // ReSharper disable once AccessToModifiedClosure app.UseTus(async request => { await Task.Delay(10); return(tusConfiguration); }); })) { var response = await server.CreateRequest(urlPath).SendAsync("OPTIONS"); response.StatusCode.ShouldBe(HttpStatusCode.NoContent); response.ShouldContainHeader("Tus-Resumable", "1.0.0"); } }
private static void StartCleanupJob(IAppBuilder app, DefaultTusConfiguration tusConfiguration) { var expiration = tusConfiguration.Expiration; if (expiration == null) { Console.WriteLine("Not running cleanup job as no expiration has been set."); return; } var expirationStore = (ITusExpirationStore)tusConfiguration.Store; var onAppDisposingToken = new OwinContext(app.Properties).Get <CancellationToken>("host.OnAppDisposing"); Task.Run(async() => { while (!onAppDisposingToken.IsCancellationRequested) { Console.WriteLine("Running cleanup job..."); var numberOfRemovedFiles = await expirationStore.RemoveExpiredFilesAsync(onAppDisposingToken); Console.WriteLine( $"Removed {numberOfRemovedFiles} expired files. Scheduled to run again in {expiration.Timeout.TotalMilliseconds} ms"); await Task.Delay(expiration.Timeout, onAppDisposingToken); } }, onAppDisposingToken); }
public OptionsTests() { var store = (ITusStore)Substitute.For(new[] { typeof(ITusStore), typeof(ITusCreationStore), typeof(ITusTerminationStore), typeof(ITusChecksumStore), typeof(ITusConcatenationStore), typeof(ITusExpirationStore), typeof(ITusCreationDeferLengthStore) }, new object[0]); // ReSharper disable once PossibleNullReferenceException ((ITusChecksumStore)store).GetSupportedAlgorithmsAsync(CancellationToken.None).ReturnsForAnyArgs(new[] { "sha1" }); _mockTusConfiguration = new DefaultTusConfiguration { Store = store, UrlPath = "/files", Events = new Events { OnAuthorizeAsync = ctx => { _onAuthorizeWasCalled = true; _onAuthorizeWasCalledWithIntent = ctx.Intent; return(Task.FromResult(0)); } } }; }
internal ExpirationHelper(DefaultTusConfiguration configuration) { _expirationStore = configuration.Store as ITusExpirationStore; _expiration = configuration.Expiration; _isSupported = _expirationStore != null && _expiration != null; _getSystemTime = configuration.GetSystemTime; }
public ExpiredFilesCleanupJob(FileUploadService fileUploadService, ILogger <ExpiredFilesCleanupJob> logger) { _logger = logger; DefaultTusConfiguration config = fileUploadService.CreateTusConfiguration(); _expirationStore = (ITusExpirationStore)config.Store; _expiration = config.Expiration; }
public async Task Expiration_Is_Updated_After_File_Write_If_Sliding_Expiration_Is_Used() { var fileId = Guid.NewGuid().ToString("n"); var tusStore = Substitute.For <ITusStore, ITusCreationStore, ITusExpirationStore>(); var tusCreationStore = (ITusCreationStore)tusStore; var tusExpirationStore = (ITusExpirationStore)tusStore; tusCreationStore.CreateFileAsync(1, null, CancellationToken.None).ReturnsForAnyArgs(fileId); tusStore.AppendDataAsync(fileId, Arg.Any <Stream>(), Arg.Any <CancellationToken>()).Returns(3); tusStore.FileExistAsync(fileId, Arg.Any <CancellationToken>()).Returns(true); tusStore.GetUploadLengthAsync(fileId, Arg.Any <CancellationToken>()).Returns(1); var expiration = new SlidingExpiration(TimeSpan.FromHours(24)); DateTimeOffset?uploadExpiresAt = null; using (var server = TestServerFactory.Create(app => { var config = new DefaultTusConfiguration { Store = tusStore, UrlPath = "/files", Expiration = expiration, }; config.Events = new Events { OnAuthorizeAsync = ctx => { if (ctx.Intent != IntentType.WriteFile) { return(Task.FromResult(0)); } // Emulate that OnCreateComplete took 10 sec to complete. var fakeSystemTime = DateTimeOffset.UtcNow.AddSeconds(10); config.MockSystemTime(fakeSystemTime); uploadExpiresAt = fakeSystemTime.Add(expiration.Timeout); return(Task.FromResult(0)); } }; app.UseTus(_ => config); })) { var response = await server .CreateTusResumableRequest("/files") .AddHeader("Upload-Length", "100") .AddBody() .SendAsync("POST"); response.StatusCode.ShouldBe(HttpStatusCode.Created); // Once for file creation, once for writing the data. await tusExpirationStore.Received(2).SetExpirationAsync(fileId, Arg.Any <DateTimeOffset>(), Arg.Any <CancellationToken>()); response.ShouldContainHeader("Upload-Expires", uploadExpiresAt.Value.ToString("R")); } }
public ExpiredFilesCleanupService( IApplicationLifetime applicationLifetime, DefaultTusConfiguration tusConfiguration, ILoggerFactory loggerFactory) { _applicationLifetime = applicationLifetime; _expirationStore = (ITusExpirationStore)tusConfiguration.Store; _expiration = tusConfiguration.Expiration; _logger = loggerFactory.CreateLogger <ExpiredFilesCleanupService>(); }
public void Validate_Throws_A_TusConfigurationException_If_Store_Is_Missing() { var config = new DefaultTusConfiguration { UrlPath = "/files" }; var exception = Assert.Throws <TusConfigurationException>(() => config.Validate()); Assert.Equal("Store cannot be null.", exception.Message); }
public void Validate_Throws_A_TusConfigurationException_If_UrlPath_Is_Missing(string urlPath) { var config = new DefaultTusConfiguration { Store = Substitute.For <ITusStore>(), UrlPath = urlPath }; var exception = Assert.Throws <TusConfigurationException>(() => config.Validate()); Assert.Equal("UrlPath cannot be empty.", exception.Message); }
public void Validate_Throws_A_TusConfigurationException_If_MetadataParsingStrategy_Is_Invalid(int metadataParsingStrategyAsInt) { var config = new DefaultTusConfiguration { Store = Substitute.For <ITusStore>(), UrlPath = "/files", MetadataParsingStrategy = (MetadataParsingStrategy)metadataParsingStrategyAsInt }; var exception = Assert.Throws <TusConfigurationException>(() => config.Validate()); Assert.Equal("MetadataParsingStrategy is not a valid value.", exception.Message); }
public IndexModel( ILogger <IndexModel> logger, IUploadStore streamStore, ApplicationDbContext context, UserManager <User> userManager, DefaultTusConfiguration defaultTusConfiguration, IOptionsMonitor <DeletionOptions> delectionOptions) { _logger = logger; _streamStore = streamStore; _context = context; _userManager = userManager; _defaultTusConfiguration = defaultTusConfiguration; _delectionOptions = delectionOptions; }
public async Task Post_Requests_Contain_Upload_Expires_Header_For_Partial_Uploads_If_Expiration_Is_Configured( string methodToUse) { var store = (ITusStore)Substitute.For(new[] { typeof(ITusStore), typeof(ITusCreationStore), typeof(ITusConcatenationStore), typeof(ITusExpirationStore) }, new object[0] ); var concatenationStore = (ITusConcatenationStore)store; concatenationStore.CreatePartialFileAsync(-1, null, CancellationToken.None).ReturnsForAnyArgs(Guid.NewGuid().ToString()); var now = DateTimeOffset.Now; using (var server = TestServerFactory.Create(app => { var config = new DefaultTusConfiguration { Store = store, UrlPath = "/files", Expiration = new AbsoluteExpiration(TimeSpan.FromMinutes(10)) }; config.MockSystemTime(now); app.UseTus(_ => config); })) { var response = await server .CreateRequest("/files") .AddTusResumableHeader() .AddHeader("Upload-Length", "1") .AddHeader("Upload-Concat", "partial") .OverrideHttpMethodIfNeeded("POST", methodToUse) .SendAsync(methodToUse); response.StatusCode.ShouldBe(HttpStatusCode.Created); response.ShouldContainHeader("Upload-Expires", now.Add(TimeSpan.FromMinutes(10)).ToString("r")); } }
public OptionsTests() { var store = (ITusStore)Substitute.For(new[] { typeof(ITusStore), typeof(ITusCreationStore), typeof(ITusTerminationStore), typeof(ITusChecksumStore), typeof(ITusConcatenationStore), typeof(ITusExpirationStore), typeof(ITusCreationDeferLengthStore) }, new object[0]); // ReSharper disable once PossibleNullReferenceException ((ITusChecksumStore)store).GetSupportedAlgorithmsAsync(CancellationToken.None).ReturnsForAnyArgs(new[] { "sha1" }); _mockTusConfiguration = new DefaultTusConfiguration { Store = store, UrlPath = "/files" }; }
public static async Task HandleRoute(HttpContext context) { DefaultTusConfiguration config = context.RequestServices.GetRequiredService <DefaultTusConfiguration>(); if (!(config.Store is ITusReadableStore store)) { return; } string fileId = (string)context.Request.RouteValues["fileId"]; ITusFile file = await store.GetFileAsync(fileId, context.RequestAborted); if (file == null) { context.Response.StatusCode = 404; await context.Response.WriteAsync($"File with id {fileId} was not found.", context.RequestAborted); return; } System.IO.Stream fileStream = await file.GetContentAsync(context.RequestAborted); Dictionary <string, Metadata> metadata = await file.GetMetadataAsync(context.RequestAborted); context.Response.ContentType = GetContentTypeOrDefault(metadata); context.Response.ContentLength = fileStream.Length; if (metadata.TryGetValue("name", out var nameMeta)) { context.Response.Headers.Add("Content-Disposition", new[] { $"attachment; filename=\"{nameMeta.GetString(Encoding.UTF8)}\"" }); } using (fileStream) await fileStream.CopyToAsync(context.Response.Body, 81920, context.RequestAborted); }
/// <summary> /// Use a simple middleware that allows downloading of files from a tusdotnet store. /// </summary> /// <param name="app"></param> /// <param name="tusConfiguration"></param> private static void SetupDownloadFeature(IAppBuilder app, DefaultTusConfiguration tusConfiguration) { var readableStore = (ITusReadableStore)tusConfiguration.Store; app.Use(async(context, next) => { if (!context.Request.Method.Equals("get", StringComparison.InvariantCultureIgnoreCase)) { await next.Invoke(); return; } if (context.Request.Uri.LocalPath.StartsWith(tusConfiguration.UrlPath, StringComparison.Ordinal)) { var fileId = context.Request.Uri.LocalPath.Replace(tusConfiguration.UrlPath, "").Trim('/', ' '); if (!string.IsNullOrEmpty(fileId)) { var file = await readableStore.GetFileAsync(fileId, context.Request.CallCancelled); if (file == null) { context.Response.StatusCode = 404; await context.Response.WriteAsync($"File with id {fileId} was not found.", context.Request.CallCancelled); return; } var fileStream = await file.GetContentAsync(context.Request.CallCancelled); var metadata = await file.GetMetadataAsync(context.Request.CallCancelled); context.Response.ContentType = metadata.ContainsKey("contentType") ? metadata["contentType"].GetString(Encoding.UTF8) : "application/octet-stream"; if (metadata.TryGetValue("name", out var nameMetadata)) { context.Response.Headers.Add("Content-Disposition", new[] { $"attachment; filename=\"{nameMetadata.GetString(Encoding.UTF8)}\"" }); } using (fileStream) { await fileStream.CopyToAsync(context.Response.Body, 81920, context.Request.CallCancelled); } return; } } switch (context.Request.Uri.LocalPath) { case "/": context.Response.ContentType = "text/html"; await context.Response.WriteAsync(File.ReadAllText("../../index.html"), context.Request.CallCancelled); break; case "/tus.js": context.Response.ContentType = "application/js"; await context.Response.WriteAsync(File.ReadAllText("../../tus.js"), context.Request.CallCancelled); break; default: context.Response.StatusCode = 404; break; } }); }
public ExpiredFilesCleanupService(ILogger <ExpiredFilesCleanupService> logger, DefaultTusConfiguration config) { _logger = logger; _expirationStore = (ITusExpirationStore)config.Store; _expiration = config.Expiration; }
public static bool RequestIsForTusEndpoint(Uri requestUri, DefaultTusConfiguration configuration) { return(requestUri.LocalPath.StartsWith(configuration.UrlPath, StringComparison.OrdinalIgnoreCase)); }