public Task Invoke(IDictionary <string, object> environment)
        {
            object obj;

            if (!environment.TryGetValue("sendfile.SendAsync", out obj))
            {
                throw new PlatformNotSupportedException("SendFile is not supported by this server");
            }
            SendFileFunc sendFile = (SendFileFunc)obj;

            return(sendFile(_filePath, 0, null, (CancellationToken)environment["owin.CallCancelled"]));
        }
示例#2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="sendFileFunc"></param>
        /// <param name="cancel"></param>
        /// <returns></returns>
        public Task SendFileAsync(Stream stream, SendFileFunc sendFileFunc, CancellationToken cancel)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (sendFileFunc == null)
            {
                throw new ArgumentNullException("sendFileFunc");
            }

            return(sendFileFunc(_fileName, _offset, _count, cancel));
        }
示例#3
0
        public void Attach()
        {
            // TODO: look to see if this Vary is already added?
            _response.Headers.Append("Vary", "Accept-Encoding");

            _originalIfNoneMatch = CleanRequestHeader("If-None-Match");
            _originalIfMatch     = CleanRequestHeader("If-Match");

            _originalResponseBody = _response.Body;
            _response.Body        = new SwitchingStream(this, _originalResponseBody);

            _originalSendFileAsyncDelegate = _response.Get <SendFileFunc>("sendfile.SendAsync");
            _response.Set <SendFileFunc>("sendfile.SendAsync", SendFileAsync);
        }
示例#4
0
        /// <summary>
        /// Sends the given file using the SendFile extension.
        /// </summary>
        /// <param name="response"></param>
        /// <param name="fileName">The full or relative path to the file.</param>
        /// <param name="offset">The offset in the file.</param>
        /// <param name="count">The number of types to send, or null to send the remainder of the file.</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task SendFileAsync(this IOwinResponse response, string fileName, long offset, long?count, CancellationToken cancellationToken)
        {
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }
            SendFileFunc sendFileFunc = response.Get <SendFileFunc>(Constants.SendFileAsyncKey);

            if (sendFileFunc == null)
            {
                throw new NotSupportedException(Resources.Exception_SendFileNotSupported);
            }

            return(sendFileFunc(fileName, offset, count, cancellationToken));
        }
示例#5
0
        public Task Invoke(IDictionary <string, object> env)
        {
            return(this.nextApp(env).Then(() =>
            {
                Response response = new Response(env);
                string file = response.GetHeader("X-SendFile");
                if (string.IsNullOrWhiteSpace(file))
                {
                    return TaskHelpers.Completed();
                }

                response.Headers.Remove("X-SendFile");

                // TODO: Convert from a relative URL path to an absolute local file path
                FileInfo fileInfo = new FileInfo(file);
                if (!fileInfo.Exists)
                {
                    // Let the server send a 500 error
                    throw new FileNotFoundException();
                }

                // TODO: For now the application is required to set content-length or chunked, and content-range.

                SendFileFunc sendFile = response.SendFileAsync;
                if (sendFile != null)
                {
                    return sendFile(file, 0, null, CancellationToken.None);
                }

                // Fall back to a manual copy

                Stream responseBody = response.Body;

                // Let the server send a 500 error
                Stream fileStream = fileInfo.OpenRead();
                try
                {
                    return fileStream.CopyToAsync(responseBody).Finally(() => fileStream.Close());
                }
                catch (Exception)
                {
                    fileStream.Close();
                    throw;
                }
            }));
        }
示例#6
0
        // When there is only a single range the bytes are sent directly in the body.
        internal Task SendRangeAsync()
        {
            bool rangeNotSatisfiable = false;

            if (_ranges.Count == 0)
            {
                rangeNotSatisfiable = true;
            }

            if (rangeNotSatisfiable)
            {
                // 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable)
                // SHOULD include a Content-Range field with a byte-range-resp-spec of "*". The instance-length specifies
                // the current length of the selected resource.  e.g. */length
                _response.Headers[Constants.ContentRange] = "bytes */" + _length.ToString(CultureInfo.InvariantCulture);
                ApplyResponseHeaders(Constants.Status416RangeNotSatisfiable);
                return(Constants.CompletedTask);
            }

            // Multi-range is not supported.
            Debug.Assert(_ranges.Count == 1);

            long start, length;

            _response.Headers[Constants.ContentRange] = ComputeContentRange(_ranges[0], out start, out length);
            _response.ContentLength = length;
            ApplyResponseHeaders(Constants.Status206PartialContent);

            string       physicalPath = _fileInfo.PhysicalPath;
            SendFileFunc sendFile     = _response.Get <SendFileFunc>(Constants.SendFileAsyncKey);

            if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
            {
                return(sendFile(physicalPath, start, length, _request.CallCancelled));
            }

            Stream readStream = _fileInfo.CreateReadStream();

            readStream.Seek(start, SeekOrigin.Begin); // TODO: What if !CanSeek?
            var  copyOperation = new StreamCopyOperation(readStream, _response.Body, length, _request.CallCancelled);
            Task task          = copyOperation.Start();

            task.ContinueWith(resultTask => readStream.Close(), TaskContinuationOptions.ExecuteSynchronously);
            return(task);
        }
示例#7
0
        public Task SendAsync()
        {
            ApplyResponseHeaders(Constants.Status200Ok);

            string       physicalPath = _fileInfo.PhysicalPath;
            SendFileFunc sendFile     = _response.Get <SendFileFunc>(Constants.SendFileAsyncKey);

            if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
            {
                return(sendFile(physicalPath, 0, _length, _request.CallCancelled));
            }

            Stream readStream    = _fileInfo.CreateReadStream();
            var    copyOperation = new StreamCopyOperation(readStream, _response.Body, _length, _request.CallCancelled);
            Task   task          = copyOperation.Start();

            task.ContinueWith(resultTask => readStream.Close(), TaskContinuationOptions.ExecuteSynchronously);
            return(task);
        }
示例#8
0
        public async Task SendAsync()
        {
            ApplyResponseHeaders(Constants.Status200Ok);

            string       physicalPath = _fileInfo.PhysicalPath;
            SendFileFunc sendFile     = _response.Get <SendFileFunc>(Constants.SendFileAsyncKey);

            if (sendFile != null && !string.IsNullOrEmpty(physicalPath))
            {
                await sendFile(physicalPath, 0, _length, _request.CallCancelled);
            }

            Stream readStream    = _fileInfo.CreateReadStream();
            var    copyOperation = new StreamCopyOperation(readStream, _response.Body, _length, _request.CallCancelled);

            await copyOperation.Start();

            readStream.Close();
        }
示例#9
0
        internal static Task SendResponseMessage(IDictionary <string, object> environment, HttpResponseMessage responseMessage, CancellationToken cancellationToken)
        {
            if (environment == null)
            {
                throw new ArgumentNullException("environment");
            }
            if (responseMessage == null)
            {
                throw new ArgumentNullException("responseMessage");
            }

            environment[Constants.ResponseStatusCodeKey]   = responseMessage.StatusCode;
            environment[Constants.ResponseReasonPhraseKey] = responseMessage.ReasonPhrase;
            var responseHeaders = Get <IDictionary <string, string[]> >(environment, Constants.ResponseHeadersKey);
            var responseBody    = Get <Stream>(environment, Constants.ResponseBodyKey);

            foreach (var kv in responseMessage.Headers)
            {
                responseHeaders[kv.Key] = kv.Value.ToArray();
            }
            if (responseMessage.Content != null)
            {
                // Trigger delayed/dynamic content-length calculations before enumerating the headers.
                responseMessage.Content.Headers.ContentLength = responseMessage.Content.Headers.ContentLength;

                foreach (var kv in responseMessage.Content.Headers)
                {
                    responseHeaders[kv.Key] = kv.Value.ToArray();
                }

                // Special case for static files
                FileContent  fileContent  = responseMessage.Content as FileContent;
                SendFileFunc sendFileFunc = Get <SendFileFunc>(environment, Constants.SendFileAsyncKey);
                if (fileContent != null && sendFileFunc != null)
                {
                    return(fileContent.SendFileAsync(responseBody, sendFileFunc, cancellationToken));
                }

                return(responseMessage.Content.CopyToAsync(responseBody));
            }
            return(TaskHelpers.Completed());
        }
 public static void SetFileSender(this IContext context, SendFileFunc send)
 {
     context.Environment.SetValue(OwinKeys.SendFile.Async, send);
 }
示例#11
0
        private Task Serve(IDictionary <string, object> env, string path)
        {
            Request  request  = new Request(env);
            Response response = new Response(env);

            var fileInfo = new FileInfo(path);
            var size     = fileInfo.Length;
            Tuple <long, long> range;

            if (!RangeHeader.IsValid(request.Headers))
            {
                response.StatusCode = OK;
                range = new Tuple <long, long>(0, size - 1);
            }
            else
            {
                var ranges = RangeHeader.Parse(request.Headers, size);

                if (ranges == null)
                {
                    // Unsatisfiable.  Return error and file size.
                    return(Fail(
                               RequestedRangeNotSatisfiable,
                               "Byte range unsatisfiable",
                               "Content-Range", "bytes */" + size)
                           .Invoke(env));
                }

                if (ranges.Count() > 1)
                {
                    // TODO: Support multiple byte ranges.
                    response.StatusCode = OK;
                    range = new Tuple <long, long>(0, size - 1);
                }
                else
                {
                    // Partial content
                    range = ranges.First();
                    response.StatusCode = PartialContent;
                    response.Headers.SetHeader("Content-Range", "bytes " + range.Item1 + "-" + range.Item2 + "/" + size);
                    size = range.Item2 - range.Item1 + 1;
                }
            }

            response.Headers
            .SetHeader("Last-Modified", fileInfo.LastWriteTimeUtc.ToHttpDateString())
            .SetHeader("Content-Type", Mime.MimeType(fileInfo.Extension, "text/plain"))
            .SetHeader("Content-Length", size.ToString(CultureInfo.InvariantCulture));

            if ("HEAD".Equals(request.Method, StringComparison.OrdinalIgnoreCase))
            {
                // Suppress the body.
                return(TaskHelpers.Completed());
            }

            FileBody body = new FileBody(path, range);

            SendFileFunc sendFile = env.Get <SendFileFunc>("sendfile.Func");

            if (sendFile != null)
            {
                return(body.Start(sendFile));
            }

            return(body.Start(response.OutputStream));
        }
        public static Task SendFile(this IContext context, string pathAndName, long startAt = 0, long?length = null)
        {
            SendFileFunc send = context.Environment.GetValueOrCreate(OwinKeys.SendFile.Async, () => SendFileNaive.GetSender(context));

            return(send(pathAndName, startAt, length, context.CancellationToken));
        }
示例#13
0
 public static void SetFileSender(this IContext context, SendFileFunc send) {
     context.Environment.SetValue(OwinKeys.SendFile.Async, send);
 }
示例#14
0
 public Task Start(SendFileFunc sendFile)
 {
     return(sendFile(path, range.Item1, (range.Item2 - range.Item1 + 1)));
 }
示例#15
0
 public Task Start(SendFileFunc sendFile)
 {
     return sendFile(path, range.Item1, (range.Item2 - range.Item1 + 1));
 }
        public void Attach()
        {
            // TODO: look to see if this Vary is already added?
            _response.Headers.Append("Vary", "Accept-Encoding");

            _originalIfNoneMatch = CleanRequestHeader("If-None-Match");
            _originalIfMatch = CleanRequestHeader("If-Match");

            _originalResponseBody = _response.Body;
            _response.Body = new SwitchingStream(this, _originalResponseBody);

            _originalSendFileAsyncDelegate = _response.Get<SendFileFunc>("sendfile.SendAsync");
            _response.Set<SendFileFunc>("sendfile.SendAsync", SendFileAsync);
        }
示例#17
0
        public Task Invoke(IDictionary <string, object> env)
        {
            Request       request         = new Request(env);
            Response      response        = new Response(env);
            Stream        orriginalStream = response.OutputStream;
            TriggerStream triggerStream   = new TriggerStream(orriginalStream);

            response.OutputStream = triggerStream;
            FilterStream filterStream            = null;
            bool         finalizeHeadersExecuted = false;

            Action finalizeHeaders = () =>
            {
                finalizeHeadersExecuted = true;
                if (IsStatusWithNoNoEntityBody(response.StatusCode) ||
                    response.Headers.ContainsKey("Content-Length") ||
                    response.Headers.ContainsKey("Transfer-Encoding"))
                {
                    return;
                }

                // Buffer
                response.Headers.SetHeader("Transfer-Encoding", "chunked");

                if ("HEAD".Equals(request.Method, StringComparison.OrdinalIgnoreCase))
                {
                    // Someone tried to write a body for a HEAD request. Suppress it.
                    triggerStream.InnerStream = Stream.Null;
                }
                else
                {
                    filterStream = new FilterStream(orriginalStream, OnWriteFilter);
                    triggerStream.InnerStream = filterStream;
                }
            };

            // Hook first write
            triggerStream.OnFirstWrite      = finalizeHeaders;
            env[OwinConstants.ResponseBody] = triggerStream;

            // Hook SendFileFunc
            SendFileFunc sendFile = env.Get <SendFileFunc>("sendfile.Func");

            if (sendFile != null)
            {
                SendFileFunc sendFileChunked = (name, offset, count) =>
                {
                    if (!finalizeHeadersExecuted)
                    {
                        finalizeHeaders();
                    }

                    if (filterStream == null)
                    {
                        // Due to headers we are not doing chunked, just pass through.
                        return(sendFile(name, offset, count));
                    }

                    count = count ?? new FileInfo(name).Length - offset;

                    // Insert chunking around the file body
                    ArraySegment <byte> prefix = ChunkPrefix((uint)count);
                    return(orriginalStream.WriteAsync(prefix.Array, prefix.Offset, prefix.Count)
                           .Then(() => orriginalStream.FlushAsync()) // Flush to ensure the data hits the wire before sendFile.
                           .Then(() => sendFile(name, offset, count))
                           .Then(() => orriginalStream.WriteAsync(EndOfChunk.Array, EndOfChunk.Offset, EndOfChunk.Count)));
                };
                env["sendfile.Func"] = sendFileChunked;
            }

            return(nextApp(env).Then(() =>
            {
                if (!finalizeHeadersExecuted)
                {
                    finalizeHeaders();
                }

                if (filterStream != null)
                {
                    // Write the chunked terminator
                    return orriginalStream.WriteAsync(FinalChunk.Array, FinalChunk.Offset, FinalChunk.Count);
                }

                return TaskHelpers.Completed();
            }));
        }