/// <summary>
        /// Processes a request to determine if it matches a known file, and if so, serves it.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext context)
        {
            var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger);

            if (!fileContext.ValidateMethod())
            {
                _logger.LogRequestMethodNotSupported(context.Request.Method);
            }
            else if (!fileContext.ValidatePath())
            {
                _logger.LogPathMismatch(fileContext.SubPath);
            }
            else if (!fileContext.LookupContentType())
            {
                _logger.LogFileTypeNotSupported(fileContext.SubPath);
            }
            else if (!fileContext.LookupFileInfo())
            {
                _logger.LogFileNotFound(fileContext.SubPath);
            }
            else
            {
                // If we get here, we can try to serve the file
                fileContext.ComprehendRequestHeaders();

                switch (fileContext.GetPreconditionState())
                {
                    case StaticFileContext.PreconditionState.Unspecified:
                    case StaticFileContext.PreconditionState.ShouldProcess:
                        if (fileContext.IsHeadMethod)
                        {
                            return fileContext.SendStatusAsync(Constants.Status200Ok);
                        }
                        if (fileContext.IsRangeRequest)
                        {
                            return fileContext.SendRangeAsync();
                        }

                        _logger.LogFileServed(fileContext.SubPath, fileContext.PhysicalPath);
                        return fileContext.SendAsync();

                    case StaticFileContext.PreconditionState.NotModified:
                        _logger.LogPathNotModified(fileContext.SubPath);
                        return fileContext.SendStatusAsync(Constants.Status304NotModified);

                    case StaticFileContext.PreconditionState.PreconditionFailed:
                        _logger.LogPreconditionFailed(fileContext.SubPath);
                        return fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);

                    default:
                        var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
                        Debug.Fail(exception.ToString());
                        throw exception;
                }
            }

            return _next(context);
        }
        public void LookupFileInfo_ReturnsFalse_IfFileDoesNotExist()
        {
            // Arrange
            var options = new StaticFileOptions();
            options.FileProvider = new TestFileProvider();
            var context = new StaticFileContext(new DefaultHttpContext(), options, PathString.Empty, NullLogger.Instance);

            // Act
            var validateResult = context.ValidatePath();
            var lookupResult = context.LookupFileInfo();

            // Assert
            Assert.True(validateResult);
            Assert.False(lookupResult);
        }
        /// <summary>
        /// Processes a request to determine if it matches a known file, and if so, serves it.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext context)
        {
            var fileContext = new StaticFileContext(context, _options, _matchUrl, _logger);
            if (fileContext.ValidateMethod()
                && fileContext.ValidatePath()
                && fileContext.LookupContentType()
                && fileContext.LookupFileInfo())
            {
                fileContext.ComprehendRequestHeaders();

                switch (fileContext.GetPreconditionState())
                {
                    case StaticFileContext.PreconditionState.Unspecified:
                    case StaticFileContext.PreconditionState.ShouldProcess:
                        if (fileContext.IsHeadMethod)
                        {
                            return fileContext.SendStatusAsync(Constants.Status200Ok);
                        }
                        if (fileContext.IsRangeRequest)
                        {
                            return fileContext.SendRangeAsync();
                        }
                        if (_logger.IsEnabled(LogLevel.Verbose))
                        {
                            _logger.LogVerbose(string.Format("Copying file {0} to the response body", fileContext.SubPath));
                        }
                        return fileContext.SendAsync();

                    case StaticFileContext.PreconditionState.NotModified:
                        if (_logger.IsEnabled(LogLevel.Verbose))
                        {
                            _logger.LogVerbose(string.Format("{0} not modified", fileContext.SubPath));
                        }
                        return fileContext.SendStatusAsync(Constants.Status304NotModified);

                    case StaticFileContext.PreconditionState.PreconditionFailed:
                        return fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);

                    default:
                        var exception = new NotImplementedException(fileContext.GetPreconditionState().ToString());
                        _logger.LogError("No precondition state specified", exception);
                        throw exception;
                }
            }

            return _next(context);
        }
        public void LookupFileInfo_ReturnsTrue_IfFileExists()
        {
            // Arrange
            var options = new StaticFileOptions();
            var fileProvider = new TestFileProvider();
            fileProvider.AddFile("/foo.txt", new TestFileInfo
            {
                LastModified = new DateTimeOffset(2014, 1, 2, 3, 4, 5, TimeSpan.Zero)
            });
            options.FileProvider = fileProvider;
            var pathString = new PathString("/test");
            var httpContext = new DefaultHttpContext();
            httpContext.Request.Path = new PathString("/test/foo.txt");
            var context = new StaticFileContext(httpContext, options, pathString, NullLogger.Instance);

            // Act
            context.ValidatePath();
            var result = context.LookupFileInfo();

            // Assert
            Assert.True(result);
        }
        /// <summary>
        /// Processes a request to determine if it matches a known file, and if so, serves it.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext context)
        {
            var fileContext = new StaticFileContext(context, _options, _matchUrl);
            if (fileContext.ValidateMethod()
                && fileContext.ValidatePath()
                && fileContext.LookupContentType()
                && fileContext.LookupFileInfo())
            {
                fileContext.ComprehendRequestHeaders();

                switch (fileContext.GetPreconditionState())
                {
                    case StaticFileContext.PreconditionState.Unspecified:
                    case StaticFileContext.PreconditionState.ShouldProcess:
                        if (fileContext.IsHeadMethod)
                        {
                            return fileContext.SendStatusAsync(Constants.Status200Ok);
                        }
                        if (fileContext.IsRangeRequest)
                        {
                            return fileContext.SendRangeAsync();
                        }
                        return fileContext.SendAsync();

                    case StaticFileContext.PreconditionState.NotModified:
                        return fileContext.SendStatusAsync(Constants.Status304NotModified);

                    case StaticFileContext.PreconditionState.PreconditionFailed:
                        return fileContext.SendStatusAsync(Constants.Status412PreconditionFailed);

                    default:
                        throw new NotImplementedException(fileContext.GetPreconditionState().ToString());
                }
            }

            return _next(context);
        }
        public void LookupFileInfo_ReturnsTrue_IfFileExists()
        {
            // Arrange
            var options      = new StaticFileOptions();
            var fileProvider = new TestFileProvider();

            fileProvider.AddFile("/foo.txt", new TestFileInfo
            {
                LastModified = new DateTimeOffset(2014, 1, 2, 3, 4, 5, TimeSpan.Zero)
            });
            options.FileProvider = fileProvider;
            var pathString  = new PathString("/test");
            var httpContext = new DefaultHttpContext();

            httpContext.Request.Path = new PathString("/test/foo.txt");
            var context = new StaticFileContext(httpContext, options, pathString, NullLogger.Instance);

            // Act
            context.ValidatePath();
            var result = context.LookupFileInfo();

            // Assert
            Assert.True(result);
        }