3.2.17.12

This commit is contained in:
Luke Pulverenti 2017-05-24 15:39:59 -04:00
parent f07af448fa
commit 71f7fc4e11
9 changed files with 608 additions and 413 deletions

View File

@ -114,15 +114,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
var outputStream = response.OutputStream; var outputStream = response.OutputStream;
// This is needed with compression // This is needed with compression
if (outputStream is ResponseStream)
{
//if (!string.IsNullOrWhiteSpace(GetHeader("Content-Encoding")))
{
outputStream.Flush(); outputStream.Flush();
}
outputStream.Dispose(); outputStream.Dispose();
}
response.Close(); response.Close();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -1,3 +1,3 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("3.2.17.11")] [assembly: AssemblyVersion("3.2.17.12")]

View File

@ -218,7 +218,9 @@ namespace SocketHttpListener.Net
var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure; var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure;
o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger, _environment); //o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger, _environment);
o_stream = new HttpResponseStream(stream, context.Response, false, _memoryStreamFactory, sock, supportsDirectSocketAccess, _environment, _fileSystem);
} }
return o_stream; return o_stream;
} }

View File

@ -181,7 +181,7 @@ namespace SocketHttpListener.Net
if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0) if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
{ {
var output = (ResponseStream)context.Connection.GetResponseStream(true); var output = (HttpResponseStream)context.Connection.GetResponseStream(true);
var _100continue = _textEncoding.GetASCIIEncoding().GetBytes("HTTP/1.1 100 Continue\r\n\r\n"); var _100continue = _textEncoding.GetASCIIEncoding().GetBytes("HTTP/1.1 100 Continue\r\n\r\n");

View File

@ -519,7 +519,7 @@ namespace SocketHttpListener.Net
public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{ {
return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); return ((HttpResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken);
} }
} }
} }

View File

@ -0,0 +1,459 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
namespace SocketHttpListener.Net
{
// Licensed to the .NET Foundation under one or more agreements.
// See the LICENSE file in the project root for more information.
//
// System.Net.ResponseStream
//
// Author:
// Gonzalo Paniagua Javier (gonzalo@novell.com)
//
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
internal partial class HttpResponseStream : Stream
{
private HttpListenerResponse _response;
private bool _ignore_errors;
private bool _trailer_sent;
private Stream _stream;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly IAcceptSocket _socket;
private readonly bool _supportsDirectSocketAccess;
private readonly IEnvironmentInfo _environment;
private readonly IFileSystem _fileSystem;
internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, IAcceptSocket socket, bool supportsDirectSocketAccess, IEnvironmentInfo environment, IFileSystem fileSystem)
{
_response = response;
_ignore_errors = ignore_errors;
_memoryStreamFactory = memoryStreamFactory;
_socket = socket;
_supportsDirectSocketAccess = supportsDirectSocketAccess;
_environment = environment;
_fileSystem = fileSystem;
_stream = stream;
}
private void DisposeCore()
{
byte[] bytes = null;
MemoryStream ms = GetHeaders(true);
bool chunked = _response.SendChunked;
if (_stream.CanWrite)
{
try
{
if (ms != null)
{
long start = ms.Position;
if (chunked && !_trailer_sent)
{
bytes = GetChunkSizeBytes(0, true);
ms.Position = ms.Length;
ms.Write(bytes, 0, bytes.Length);
}
InternalWrite(ms.GetBuffer(), (int)start, (int)(ms.Length - start));
_trailer_sent = true;
}
else if (chunked && !_trailer_sent)
{
bytes = GetChunkSizeBytes(0, true);
InternalWrite(bytes, 0, bytes.Length);
_trailer_sent = true;
}
}
catch (HttpListenerException)
{
// Ignore error due to connection reset by peer
}
}
_response.Close();
}
internal async Task WriteWebSocketHandshakeHeadersAsync()
{
if (_closed)
throw new ObjectDisposedException(GetType().ToString());
if (_stream.CanWrite)
{
MemoryStream ms = GetHeaders(closing: false, isWebSocketHandshake: true);
bool chunked = _response.SendChunked;
long start = ms.Position;
if (chunked)
{
byte[] bytes = GetChunkSizeBytes(0, true);
ms.Position = ms.Length;
ms.Write(bytes, 0, bytes.Length);
}
await InternalWriteAsync(ms.GetBuffer(), (int)start, (int)(ms.Length - start)).ConfigureAwait(false);
await _stream.FlushAsync().ConfigureAwait(false);
}
}
private MemoryStream GetHeaders(bool closing, bool isWebSocketHandshake = false)
{
// SendHeaders works on shared headers
lock (_response.headers_lock)
{
if (_response.HeadersSent)
return null;
var ms = _memoryStreamFactory.CreateNew();
_response.SendHeaders(closing, ms);
return ms;
}
//lock (_response._headersLock)
//{
// if (_response.SentHeaders)
// {
// return null;
// }
// MemoryStream ms = new MemoryStream();
// _response.SendHeaders(closing, ms, isWebSocketHandshake);
// return ms;
//}
}
private static byte[] s_crlf = new byte[] { 13, 10 };
private static byte[] GetChunkSizeBytes(int size, bool final)
{
string str = String.Format("{0:x}\r\n{1}", size, final ? "\r\n" : "");
return Encoding.ASCII.GetBytes(str);
}
internal void InternalWrite(byte[] buffer, int offset, int count)
{
if (_ignore_errors)
{
try
{
_stream.Write(buffer, offset, count);
}
catch { }
}
else
{
try
{
_stream.Write(buffer, offset, count);
}
catch (IOException ex)
{
throw new HttpListenerException(ex.HResult, ex.Message);
}
}
}
internal Task InternalWriteAsync(byte[] buffer, int offset, int count) =>
_ignore_errors ? InternalWriteIgnoreErrorsAsync(buffer, offset, count) : _stream.WriteAsync(buffer, offset, count);
private async Task InternalWriteIgnoreErrorsAsync(byte[] buffer, int offset, int count)
{
try { await _stream.WriteAsync(buffer, offset, count).ConfigureAwait(false); }
catch { }
}
private void WriteCore(byte[] buffer, int offset, int size)
{
if (size == 0)
return;
byte[] bytes = null;
MemoryStream ms = GetHeaders(false);
bool chunked = _response.SendChunked;
if (ms != null)
{
long start = ms.Position; // After the possible preamble for the encoding
ms.Position = ms.Length;
if (chunked)
{
bytes = GetChunkSizeBytes(size, false);
ms.Write(bytes, 0, bytes.Length);
}
int new_count = Math.Min(size, 16384 - (int)ms.Position + (int)start);
ms.Write(buffer, offset, new_count);
size -= new_count;
offset += new_count;
InternalWrite(ms.GetBuffer(), (int)start, (int)(ms.Length - start));
ms.SetLength(0);
ms.Capacity = 0; // 'dispose' the buffer in ms.
}
else if (chunked)
{
bytes = GetChunkSizeBytes(size, false);
InternalWrite(bytes, 0, bytes.Length);
}
if (size > 0)
InternalWrite(buffer, offset, size);
if (chunked)
InternalWrite(s_crlf, 0, 2);
}
private IAsyncResult BeginWriteCore(byte[] buffer, int offset, int size, AsyncCallback cback, object state)
{
if (_closed)
{
HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
ares._callback = cback;
ares._state = state;
ares.Complete();
return ares;
}
byte[] bytes = null;
MemoryStream ms = GetHeaders(false);
bool chunked = _response.SendChunked;
if (ms != null)
{
long start = ms.Position;
ms.Position = ms.Length;
if (chunked)
{
bytes = GetChunkSizeBytes(size, false);
ms.Write(bytes, 0, bytes.Length);
}
ms.Write(buffer, offset, size);
buffer = ms.GetBuffer();
offset = (int)start;
size = (int)(ms.Position - start);
}
else if (chunked)
{
bytes = GetChunkSizeBytes(size, false);
InternalWrite(bytes, 0, bytes.Length);
}
try
{
return _stream.BeginWrite(buffer, offset, size, cback, state);
}
catch (IOException ex)
{
if (_ignore_errors)
{
HttpStreamAsyncResult ares = new HttpStreamAsyncResult(this);
ares._callback = cback;
ares._state = state;
ares.Complete();
return ares;
}
else
{
throw new HttpListenerException(ex.HResult, ex.Message);
}
}
}
private void EndWriteCore(IAsyncResult asyncResult)
{
if (_closed)
return;
if (_ignore_errors)
{
try
{
_stream.EndWrite(asyncResult);
if (_response.SendChunked)
_stream.Write(s_crlf, 0, 2);
}
catch { }
}
else
{
try
{
_stream.EndWrite(asyncResult);
if (_response.SendChunked)
_stream.Write(s_crlf, 0, 2);
}
catch (IOException ex)
{
// NetworkStream wraps exceptions in IOExceptions; if the underlying socket operation
// failed because of invalid arguments or usage, propagate that error. Otherwise
// wrap the whole thing in an HttpListenerException. This is all to match Windows behavior.
if (ex.InnerException is ArgumentException || ex.InnerException is InvalidOperationException)
{
throw ex.InnerException;
}
throw new HttpListenerException(ex.HResult, ex.Message);
}
}
}
private bool EnableSendFileWithSocket
{
get { return false; }
}
public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !_response.SendChunked && _response.ContentLength64 > 8192)
{
if (EnableSendFileWithSocket)
{
return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken);
}
}
return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
}
private readonly byte[] _emptyBuffer = new byte[] { };
private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
var ms = GetHeaders(false);
byte[] preBuffer;
if (ms != null)
{
using (var msCopy = new MemoryStream())
{
ms.CopyTo(msCopy);
preBuffer = msCopy.ToArray();
}
}
else
{
return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
}
//_logger.Info("Socket sending file {0} {1}", path, response.ContentLength64);
return _socket.SendFile(path, preBuffer, _emptyBuffer, cancellationToken);
}
const int StreamCopyToBufferSize = 81920;
private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
var allowAsync = _environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
var fileOpenOptions = offset > 0
? FileOpenOptions.RandomAccess
: FileOpenOptions.SequentialScan;
if (allowAsync)
{
fileOpenOptions |= FileOpenOptions.Asynchronous;
}
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
{
if (offset > 0)
{
fs.Position = offset;
}
var targetStream = this;
if (count > 0)
{
if (allowAsync)
{
await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
}
else
{
await CopyToInternalAsyncWithSyncRead(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
}
}
else
{
if (allowAsync)
{
await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
else
{
fs.CopyTo(targetStream, StreamCopyToBufferSize);
}
}
}
}
private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
{
var array = new byte[StreamCopyToBufferSize];
int bytesRead;
while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
{
var bytesToWrite = Math.Min(bytesRead, copyLength);
if (bytesToWrite > 0)
{
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
}
copyLength -= bytesToWrite;
if (copyLength <= 0)
{
break;
}
}
}
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
{
var array = new byte[StreamCopyToBufferSize];
int bytesRead;
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
var bytesToWrite = Math.Min(bytesRead, copyLength);
if (bytesToWrite > 0)
{
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
}
copyLength -= bytesToWrite;
if (copyLength <= 0)
{
break;
}
}
}
}
}

View File

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SocketHttpListener.Net
{
internal sealed partial class HttpResponseStream : Stream
{
private bool _closed;
internal bool Closed => _closed;
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override void Flush() { }
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public override long Length
{
get
{
throw new NotImplementedException();
}
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
return base.BeginRead(buffer, offset, count, callback, state);
}
public override int EndRead(IAsyncResult asyncResult)
{
return base.EndRead(asyncResult);
}
public override void Write(byte[] buffer, int offset, int size)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (size < 0 || size > buffer.Length - offset)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
if (_closed)
{
return;
}
WriteCore(buffer, offset, size);
}
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0 || offset > buffer.Length)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
if (size < 0 || size > buffer.Length - offset)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
return BeginWriteCore(buffer, offset, size, callback, state);
}
public override void EndWrite(IAsyncResult asyncResult)
{
if (asyncResult == null)
{
throw new ArgumentNullException(nameof(asyncResult));
}
EndWriteCore(asyncResult);
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_closed)
{
return;
}
_closed = true;
DisposeCore();
}
}
finally
{
base.Dispose(disposing);
}
}
}
}

View File

@ -1,400 +0,0 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
namespace SocketHttpListener.Net
{
// FIXME: Does this buffer the response until Close?
// Update: we send a single packet for the first non-chunked Write
// What happens when we set content-length to X and write X-1 bytes then close?
// what if we don't set content-length at all?
public class ResponseStream : Stream
{
HttpListenerResponse response;
bool disposed;
bool trailer_sent;
Stream stream;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding;
private readonly IFileSystem _fileSystem;
private readonly IAcceptSocket _socket;
private readonly bool _supportsDirectSocketAccess;
private readonly ILogger _logger;
private readonly IEnvironmentInfo _environment;
internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess, ILogger logger, IEnvironmentInfo environment)
{
this.response = response;
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
_fileSystem = fileSystem;
_socket = socket;
_supportsDirectSocketAccess = supportsDirectSocketAccess;
_logger = logger;
_environment = environment;
this.stream = stream;
}
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
protected override void Dispose(bool disposing)
{
if (disposed == false)
{
disposed = true;
using (var ms = GetHeaders(response, _memoryStreamFactory, false))
{
if (stream.CanWrite)
{
try
{
bool chunked = response.SendChunked;
if (ms != null)
{
var start = ms.Position;
if (chunked && !trailer_sent)
{
trailer_sent = true;
var bytes = GetChunkSizeBytes(0, true);
ms.Position = ms.Length;
ms.Write(bytes, 0, bytes.Length);
ms.Position = start;
}
ms.CopyTo(stream);
}
else if (chunked && !trailer_sent)
{
trailer_sent = true;
var bytes = GetChunkSizeBytes(0, true);
stream.Write(bytes, 0, bytes.Length);
}
}
catch (IOException ex)
{
// Ignore error due to connection reset by peer
}
}
response.Close();
}
}
base.Dispose(disposing);
}
internal static MemoryStream GetHeaders(HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, bool closing)
{
// SendHeaders works on shared headers
lock (response.headers_lock)
{
if (response.HeadersSent)
return null;
var ms = memoryStreamFactory.CreateNew();
response.SendHeaders(closing, ms);
return ms;
}
}
public override void Flush()
{
}
static byte[] crlf = new byte[] { 13, 10 };
byte[] GetChunkSizeBytes(int size, bool final)
{
string str = String.Format("{0:x}\r\n{1}", size, final ? "\r\n" : "");
return _textEncoding.GetASCIIEncoding().GetBytes(str);
}
internal void InternalWrite(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
const int MsCopyBufferSize = 81920;
const int StreamCopyToBufferSize = 81920;
public override void Write(byte[] buffer, int offset, int count)
{
if (disposed)
throw new ObjectDisposedException(GetType().ToString());
if (count == 0)
{
return;
}
using (var ms = GetHeaders(response, _memoryStreamFactory, false))
{
bool chunked = response.SendChunked;
if (ms != null)
{
long start = ms.Position; // After the possible preamble for the encoding
ms.Position = ms.Length;
if (chunked)
{
var bytes = GetChunkSizeBytes(count, false);
ms.Write(bytes, 0, bytes.Length);
}
ms.Write(buffer, offset, count);
if (chunked)
{
ms.Write(crlf, 0, 2);
}
ms.Position = start;
ms.CopyTo(stream, MsCopyBufferSize);
return;
}
if (chunked)
{
var bytes = GetChunkSizeBytes(count, false);
stream.Write(bytes, 0, bytes.Length);
}
stream.Write(buffer, offset, count);
if (chunked)
stream.Write(crlf, 0, 2);
}
}
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (disposed)
throw new ObjectDisposedException(GetType().ToString());
if (count == 0)
{
return;
}
using (var ms = GetHeaders(response, _memoryStreamFactory, false))
{
bool chunked = response.SendChunked;
if (ms != null)
{
long start = ms.Position; // After the possible preamble for the encoding
ms.Position = ms.Length;
if (chunked)
{
var bytes = GetChunkSizeBytes(count, false);
ms.Write(bytes, 0, bytes.Length);
}
ms.Write(buffer, offset, count);
if (chunked)
{
ms.Write(crlf, 0, 2);
}
ms.Position = start;
await ms.CopyToAsync(stream, MsCopyBufferSize, cancellationToken).ConfigureAwait(false);
return;
}
if (chunked)
{
var bytes = GetChunkSizeBytes(count, false);
stream.Write(bytes, 0, bytes.Length);
}
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
if (chunked)
stream.Write(crlf, 0, 2);
}
}
public override int Read([In, Out] byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
private bool EnableSendFileWithSocket
{
get { return false; }
}
public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked && response.ContentLength64 > 8192)
{
if (EnableSendFileWithSocket)
{
return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken);
}
}
return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
}
private readonly byte[] _emptyBuffer = new byte[] { };
private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
var ms = GetHeaders(response, _memoryStreamFactory, false);
byte[] preBuffer;
if (ms != null)
{
using (var msCopy = new MemoryStream())
{
ms.CopyTo(msCopy);
preBuffer = msCopy.ToArray();
}
}
else
{
return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
}
_logger.Info("Socket sending file {0} {1}", path, response.ContentLength64);
return _socket.SendFile(path, preBuffer, _emptyBuffer, cancellationToken);
}
private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
var allowAsync = _environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
var fileOpenOptions = offset > 0
? FileOpenOptions.RandomAccess
: FileOpenOptions.SequentialScan;
if (allowAsync)
{
fileOpenOptions |= FileOpenOptions.Asynchronous;
}
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
{
if (offset > 0)
{
fs.Position = offset;
}
var targetStream = this;
if (count > 0)
{
if (allowAsync)
{
await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
}
else
{
await CopyToInternalAsyncWithSyncRead(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
}
}
else
{
if (allowAsync)
{
await fs.CopyToAsync(targetStream, StreamCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
else
{
fs.CopyTo(targetStream, StreamCopyToBufferSize);
}
}
}
}
private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
{
var array = new byte[StreamCopyToBufferSize];
int bytesRead;
while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
{
var bytesToWrite = Math.Min(bytesRead, copyLength);
if (bytesToWrite > 0)
{
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
}
copyLength -= bytesToWrite;
if (copyLength <= 0)
{
break;
}
}
}
private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
{
var array = new byte[StreamCopyToBufferSize];
int bytesRead;
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
var bytesToWrite = Math.Min(bytesRead, copyLength);
if (bytesToWrite > 0)
{
await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
}
copyLength -= bytesToWrite;
if (copyLength <= 0)
{
break;
}
}
}
}
}

View File

@ -70,11 +70,12 @@
<Compile Include="Net\HttpListenerResponse.cs" /> <Compile Include="Net\HttpListenerResponse.cs" />
<Compile Include="Net\HttpRequestStream.cs" /> <Compile Include="Net\HttpRequestStream.cs" />
<Compile Include="Net\HttpRequestStream.Managed.cs" /> <Compile Include="Net\HttpRequestStream.Managed.cs" />
<Compile Include="Net\HttpResponseStream.cs" />
<Compile Include="Net\HttpResponseStream.Managed.cs" />
<Compile Include="Net\HttpStatusCode.cs" /> <Compile Include="Net\HttpStatusCode.cs" />
<Compile Include="Net\HttpStreamAsyncResult.cs" /> <Compile Include="Net\HttpStreamAsyncResult.cs" />
<Compile Include="Net\HttpVersion.cs" /> <Compile Include="Net\HttpVersion.cs" />
<Compile Include="Net\ListenerPrefix.cs" /> <Compile Include="Net\ListenerPrefix.cs" />
<Compile Include="Net\ResponseStream.cs" />
<Compile Include="Net\WebHeaderCollection.cs" /> <Compile Include="Net\WebHeaderCollection.cs" />
<Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" /> <Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" />
<Compile Include="Net\WebSockets\WebSocketContext.cs" /> <Compile Include="Net\WebSockets\WebSocketContext.cs" />