Luke Pulverenti 8 anni fa
parent
commit
71f7fc4e11

+ 2 - 8
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)

+ 1 - 1
SharedVersion.cs

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

+ 3 - 1
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;
         }

+ 1 - 1
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");
 

+ 1 - 1
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);
         }
     }
 }

+ 459 - 0
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;
+                }
+            }
+        }
+    }
+}

+ 139 - 0
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);
+            }
+        }
+    }
+}

+ 0 - 400
SocketHttpListener/Net/ResponseStream.cs

@@ -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;
-                }
-            }
-        }
-    }
-}

+ 2 - 1
SocketHttpListener/SocketHttpListener.csproj

@@ -70,11 +70,12 @@
     <Compile Include="Net\HttpListenerResponse.cs" />
     <Compile Include="Net\HttpRequestStream.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\HttpStreamAsyncResult.cs" />
     <Compile Include="Net\HttpVersion.cs" />
     <Compile Include="Net\ListenerPrefix.cs" />
-    <Compile Include="Net\ResponseStream.cs" />
     <Compile Include="Net\WebHeaderCollection.cs" />
     <Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" />
     <Compile Include="Net\WebSockets\WebSocketContext.cs" />