diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
index 9e58ee57c..d6762d94b 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
@@ -114,15 +114,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
var outputStream = response.OutputStream;
// 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();
}
catch (Exception ex)
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 1564e4a41..e2671c988 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,3 +1,3 @@
using System.Reflection;
-[assembly: AssemblyVersion("3.2.17.11")]
+[assembly: AssemblyVersion("3.2.17.12")]
diff --git a/SocketHttpListener/Net/HttpConnection.cs b/SocketHttpListener/Net/HttpConnection.cs
index 848b80f99..627b671bf 100644
--- a/SocketHttpListener/Net/HttpConnection.cs
+++ b/SocketHttpListener/Net/HttpConnection.cs
@@ -218,7 +218,9 @@ namespace SocketHttpListener.Net
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;
}
diff --git a/SocketHttpListener/Net/HttpListenerRequest.cs b/SocketHttpListener/Net/HttpListenerRequest.cs
index cfbd49203..6a99eb078 100644
--- a/SocketHttpListener/Net/HttpListenerRequest.cs
+++ b/SocketHttpListener/Net/HttpListenerRequest.cs
@@ -181,7 +181,7 @@ namespace SocketHttpListener.Net
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");
diff --git a/SocketHttpListener/Net/HttpListenerResponse.cs b/SocketHttpListener/Net/HttpListenerResponse.cs
index 3cb6a0d75..185454ef6 100644
--- a/SocketHttpListener/Net/HttpListenerResponse.cs
+++ b/SocketHttpListener/Net/HttpListenerResponse.cs
@@ -519,7 +519,7 @@ namespace SocketHttpListener.Net
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);
}
}
}
\ No newline at end of file
diff --git a/SocketHttpListener/Net/HttpResponseStream.Managed.cs b/SocketHttpListener/Net/HttpResponseStream.Managed.cs
new file mode 100644
index 000000000..0a9efccb2
--- /dev/null
+++ b/SocketHttpListener/Net/HttpResponseStream.Managed.cs
@@ -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;
+ }
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/HttpResponseStream.cs b/SocketHttpListener/Net/HttpResponseStream.cs
new file mode 100644
index 000000000..f7140be66
--- /dev/null
+++ b/SocketHttpListener/Net/HttpResponseStream.cs
@@ -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);
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener/Net/ResponseStream.cs b/SocketHttpListener/Net/ResponseStream.cs
deleted file mode 100644
index 5949e3817..000000000
--- a/SocketHttpListener/Net/ResponseStream.cs
+++ /dev/null
@@ -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;
- }
- }
- }
- }
-}
diff --git a/SocketHttpListener/SocketHttpListener.csproj b/SocketHttpListener/SocketHttpListener.csproj
index dd2d2cf0f..3be51071c 100644
--- a/SocketHttpListener/SocketHttpListener.csproj
+++ b/SocketHttpListener/SocketHttpListener.csproj
@@ -70,11 +70,12 @@
+
+
-