aboutsummaryrefslogtreecommitdiff
path: root/SocketHttpListener.Portable/Net/ResponseStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'SocketHttpListener.Portable/Net/ResponseStream.cs')
-rw-r--r--SocketHttpListener.Portable/Net/ResponseStream.cs316
1 files changed, 316 insertions, 0 deletions
diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs
new file mode 100644
index 000000000..6ecbf9742
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/ResponseStream.cs
@@ -0,0 +1,316 @@
+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.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?
+ class ResponseStream : Stream
+ {
+ HttpListenerResponse response;
+ bool ignore_errors;
+ bool disposed;
+ bool trailer_sent;
+ Stream stream;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly ITextEncoding _textEncoding;
+
+ internal ResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ this.response = response;
+ this.ignore_errors = ignore_errors;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+ 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;
+ 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);
+ }
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ InternalWrite(msBuffer, (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 (IOException ex)
+ {
+ // Ignore error due to connection reset by peer
+ }
+ }
+ response.Close();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ MemoryStream GetHeaders(bool closing)
+ {
+ // SendHeaders works on shared headers
+ lock (response.headers_lock)
+ {
+ if (response.HeadersSent)
+ return null;
+ MemoryStream 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)
+ {
+ if (ignore_errors)
+ {
+ try
+ {
+ stream.Write(buffer, offset, count);
+ }
+ catch { }
+ }
+ else
+ {
+ stream.Write(buffer, offset, count);
+ }
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ 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(count, false);
+ ms.Write(bytes, 0, bytes.Length);
+ }
+
+ int new_count = Math.Min(count, 16384 - (int)ms.Position + (int)start);
+ ms.Write(buffer, offset, new_count);
+ count -= new_count;
+ offset += new_count;
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ InternalWrite(msBuffer, (int)start, (int)(ms.Length - start));
+ ms.SetLength(0);
+ ms.Capacity = 0; // 'dispose' the buffer in ms.
+ }
+ else if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ InternalWrite(bytes, 0, bytes.Length);
+ }
+
+ if (count > 0)
+ InternalWrite(buffer, offset, count);
+ if (chunked)
+ InternalWrite(crlf, 0, 2);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ 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(count, false);
+ ms.Write(bytes, 0, bytes.Length);
+ }
+ ms.Write(buffer, offset, count);
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ buffer = msBuffer;
+ offset = (int)start;
+ count = (int)(ms.Position - start);
+ }
+ else if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ InternalWrite(bytes, 0, bytes.Length);
+ }
+
+ try
+ {
+ if (count > 0)
+ {
+ await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (response.SendChunked)
+ stream.Write(crlf, 0, 2);
+ }
+ catch
+ {
+ if (!ignore_errors)
+ {
+ throw;
+ }
+ }
+ }
+
+ //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // 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(count, false);
+ // ms.Write(bytes, 0, bytes.Length);
+ // }
+ // ms.Write(buffer, offset, count);
+ // buffer = ms.ToArray();
+ // offset = (int)start;
+ // count = (int)(ms.Position - start);
+ // }
+ // else if (chunked)
+ // {
+ // bytes = GetChunkSizeBytes(count, false);
+ // InternalWrite(bytes, 0, bytes.Length);
+ // }
+
+ // return stream.BeginWrite(buffer, offset, count, cback, state);
+ //}
+
+ //public override void EndWrite(IAsyncResult ares)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // if (ignore_errors)
+ // {
+ // try
+ // {
+ // stream.EndWrite(ares);
+ // if (response.SendChunked)
+ // stream.Write(crlf, 0, 2);
+ // }
+ // catch { }
+ // }
+ // else {
+ // stream.EndWrite(ares);
+ // if (response.SendChunked)
+ // stream.Write(crlf, 0, 2);
+ // }
+ //}
+
+ public override int Read([In, Out] byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // throw new NotSupportedException();
+ //}
+
+ //public override int EndRead(IAsyncResult ares)
+ //{
+ // throw new NotSupportedException();
+ //}
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}