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"])); }
/// <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)); }
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); }
/// <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)); }
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; } })); }
// 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); }
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); }
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(); }
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); }
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)); }
public Task Start(SendFileFunc sendFile) { return(sendFile(path, range.Item1, (range.Item2 - range.Item1 + 1))); }
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); }
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(); })); }