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");
            }
        }
示例#2
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);
        }
示例#3
0
        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));
                    }
                }
            };
        }
示例#4
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);
        }
示例#11
0
 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;
 }
示例#12
0
        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"));
            }
        }
示例#13
0
        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);
        }
示例#15
0
        /// <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;
                }
            });
        }
示例#16
0
 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));
 }