diff options
Diffstat (limited to 'SocketHttpListener.Portable/Net')
22 files changed, 0 insertions, 5801 deletions
diff --git a/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs b/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs deleted file mode 100644 index c6e7e538e..000000000 --- a/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System.Net; - -namespace SocketHttpListener.Net -{ - public delegate AuthenticationSchemes AuthenticationSchemeSelector(HttpListenerRequest httpRequest); -} diff --git a/SocketHttpListener.Portable/Net/ChunkStream.cs b/SocketHttpListener.Portable/Net/ChunkStream.cs deleted file mode 100644 index 3f3b4a667..000000000 --- a/SocketHttpListener.Portable/Net/ChunkStream.cs +++ /dev/null @@ -1,371 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; - -namespace SocketHttpListener.Net -{ - class ChunkStream - { - enum State - { - None, - PartialSize, - Body, - BodyFinished, - Trailer - } - - class Chunk - { - public byte[] Bytes; - public int Offset; - - public Chunk(byte[] chunk) - { - this.Bytes = chunk; - } - - public int Read(byte[] buffer, int offset, int size) - { - int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size; - Buffer.BlockCopy(Bytes, Offset, buffer, offset, nread); - Offset += nread; - return nread; - } - } - - internal WebHeaderCollection headers; - int chunkSize; - int chunkRead; - int totalWritten; - State state; - //byte [] waitBuffer; - StringBuilder saved; - bool sawCR; - bool gotit; - int trailerState; - List<Chunk> chunks; - - public ChunkStream(WebHeaderCollection headers) - { - this.headers = headers; - saved = new StringBuilder(); - chunks = new List<Chunk>(); - chunkSize = -1; - totalWritten = 0; - } - - public void ResetBuffer() - { - chunkSize = -1; - chunkRead = 0; - totalWritten = 0; - chunks.Clear(); - } - - public void WriteAndReadBack(byte[] buffer, int offset, int size, ref int read) - { - if (offset + read > 0) - Write(buffer, offset, offset + read); - read = Read(buffer, offset, size); - } - - public int Read(byte[] buffer, int offset, int size) - { - return ReadFromChunks(buffer, offset, size); - } - - int ReadFromChunks(byte[] buffer, int offset, int size) - { - int count = chunks.Count; - int nread = 0; - - var chunksForRemoving = new List<Chunk>(count); - for (int i = 0; i < count; i++) - { - Chunk chunk = (Chunk)chunks[i]; - - if (chunk.Offset == chunk.Bytes.Length) - { - chunksForRemoving.Add(chunk); - continue; - } - - nread += chunk.Read(buffer, offset + nread, size - nread); - if (nread == size) - break; - } - - foreach (var chunk in chunksForRemoving) - chunks.Remove(chunk); - - return nread; - } - - public void Write(byte[] buffer, int offset, int size) - { - if (offset < size) - InternalWrite(buffer, ref offset, size); - } - - void InternalWrite(byte[] buffer, ref int offset, int size) - { - if (state == State.None || state == State.PartialSize) - { - state = GetChunkSize(buffer, ref offset, size); - if (state == State.PartialSize) - return; - - saved.Length = 0; - sawCR = false; - gotit = false; - } - - if (state == State.Body && offset < size) - { - state = ReadBody(buffer, ref offset, size); - if (state == State.Body) - return; - } - - if (state == State.BodyFinished && offset < size) - { - state = ReadCRLF(buffer, ref offset, size); - if (state == State.BodyFinished) - return; - - sawCR = false; - } - - if (state == State.Trailer && offset < size) - { - state = ReadTrailer(buffer, ref offset, size); - if (state == State.Trailer) - return; - - saved.Length = 0; - sawCR = false; - gotit = false; - } - - if (offset < size) - InternalWrite(buffer, ref offset, size); - } - - public bool WantMore - { - get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); } - } - - public bool DataAvailable - { - get - { - int count = chunks.Count; - for (int i = 0; i < count; i++) - { - Chunk ch = (Chunk)chunks[i]; - if (ch == null || ch.Bytes == null) - continue; - if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length) - return (state != State.Body); - } - return false; - } - } - - public int TotalDataSize - { - get { return totalWritten; } - } - - public int ChunkLeft - { - get { return chunkSize - chunkRead; } - } - - State ReadBody(byte[] buffer, ref int offset, int size) - { - if (chunkSize == 0) - return State.BodyFinished; - - int diff = size - offset; - if (diff + chunkRead > chunkSize) - diff = chunkSize - chunkRead; - - byte[] chunk = new byte[diff]; - Buffer.BlockCopy(buffer, offset, chunk, 0, diff); - chunks.Add(new Chunk(chunk)); - offset += diff; - chunkRead += diff; - totalWritten += diff; - return (chunkRead == chunkSize) ? State.BodyFinished : State.Body; - - } - - State GetChunkSize(byte[] buffer, ref int offset, int size) - { - chunkRead = 0; - chunkSize = 0; - char c = '\0'; - while (offset < size) - { - c = (char)buffer[offset++]; - if (c == '\r') - { - if (sawCR) - ThrowProtocolViolation("2 CR found"); - - sawCR = true; - continue; - } - - if (sawCR && c == '\n') - break; - - if (c == ' ') - gotit = true; - - if (!gotit) - saved.Append(c); - - if (saved.Length > 20) - ThrowProtocolViolation("chunk size too long."); - } - - if (!sawCR || c != '\n') - { - if (offset < size) - ThrowProtocolViolation("Missing \\n"); - - try - { - if (saved.Length > 0) - { - chunkSize = Int32.Parse(RemoveChunkExtension(saved.ToString()), NumberStyles.HexNumber); - } - } - catch (Exception) - { - ThrowProtocolViolation("Cannot parse chunk size."); - } - - return State.PartialSize; - } - - chunkRead = 0; - try - { - chunkSize = Int32.Parse(RemoveChunkExtension(saved.ToString()), NumberStyles.HexNumber); - } - catch (Exception) - { - ThrowProtocolViolation("Cannot parse chunk size."); - } - - if (chunkSize == 0) - { - trailerState = 2; - return State.Trailer; - } - - return State.Body; - } - - static string RemoveChunkExtension(string input) - { - int idx = input.IndexOf(';'); - if (idx == -1) - return input; - return input.Substring(0, idx); - } - - State ReadCRLF(byte[] buffer, ref int offset, int size) - { - if (!sawCR) - { - if ((char)buffer[offset++] != '\r') - ThrowProtocolViolation("Expecting \\r"); - - sawCR = true; - if (offset == size) - return State.BodyFinished; - } - - if (sawCR && (char)buffer[offset++] != '\n') - ThrowProtocolViolation("Expecting \\n"); - - return State.None; - } - - State ReadTrailer(byte[] buffer, ref int offset, int size) - { - char c = '\0'; - - // short path - if (trailerState == 2 && (char)buffer[offset] == '\r' && saved.Length == 0) - { - offset++; - if (offset < size && (char)buffer[offset] == '\n') - { - offset++; - return State.None; - } - offset--; - } - - int st = trailerState; - string stString = "\r\n\r"; - while (offset < size && st < 4) - { - c = (char)buffer[offset++]; - if ((st == 0 || st == 2) && c == '\r') - { - st++; - continue; - } - - if ((st == 1 || st == 3) && c == '\n') - { - st++; - continue; - } - - if (st > 0) - { - saved.Append(stString.Substring(0, saved.Length == 0 ? st - 2 : st)); - st = 0; - if (saved.Length > 4196) - ThrowProtocolViolation("Error reading trailer (too long)."); - } - } - - if (st < 4) - { - trailerState = st; - if (offset < size) - ThrowProtocolViolation("Error reading trailer."); - - return State.Trailer; - } - - StringReader reader = new StringReader(saved.ToString()); - string line; - while ((line = reader.ReadLine()) != null && line != "") - headers.Add(line); - - return State.None; - } - - static void ThrowProtocolViolation(string message) - { - WebException we = new WebException(message, null, WebExceptionStatus.UnknownError, null); - //WebException we = new WebException(message, null, WebExceptionStatus.ServerProtocolViolation, null); - throw we; - } - } -} diff --git a/SocketHttpListener.Portable/Net/ChunkedInputStream.cs b/SocketHttpListener.Portable/Net/ChunkedInputStream.cs deleted file mode 100644 index 6dfd8d8a1..000000000 --- a/SocketHttpListener.Portable/Net/ChunkedInputStream.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net -{ - class ChunkedInputStream : RequestStream - { - bool disposed; - ChunkStream decoder; - HttpListenerContext context; - bool no_more_data; - - //class ReadBufferState - //{ - // public byte[] Buffer; - // public int Offset; - // public int Count; - // public int InitialCount; - // public HttpStreamAsyncResult Ares; - // public ReadBufferState(byte[] buffer, int offset, int count, - // HttpStreamAsyncResult ares) - // { - // Buffer = buffer; - // Offset = offset; - // Count = count; - // InitialCount = count; - // Ares = ares; - // } - //} - - public ChunkedInputStream(HttpListenerContext context, Stream stream, - byte[] buffer, int offset, int length) - : base(stream, buffer, offset, length) - { - this.context = context; - WebHeaderCollection coll = (WebHeaderCollection)context.Request.Headers; - decoder = new ChunkStream(coll); - } - - //public ChunkStream Decoder - //{ - // get { return decoder; } - // set { decoder = value; } - //} - - //public override int Read([In, Out] byte[] buffer, int offset, int count) - //{ - // IAsyncResult ares = BeginRead(buffer, offset, count, null, null); - // return EndRead(ares); - //} - - //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, - // AsyncCallback cback, object state) - //{ - // if (disposed) - // throw new ObjectDisposedException(GetType().ToString()); - - // if (buffer == null) - // throw new ArgumentNullException("buffer"); - - // int len = buffer.Length; - // if (offset < 0 || offset > len) - // throw new ArgumentOutOfRangeException("offset exceeds the size of buffer"); - - // if (count < 0 || offset > len - count) - // throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer"); - - // HttpStreamAsyncResult ares = new HttpStreamAsyncResult(); - // ares.Callback = cback; - // ares.State = state; - // if (no_more_data) - // { - // ares.Complete(); - // return ares; - // } - // int nread = decoder.Read(buffer, offset, count); - // offset += nread; - // count -= nread; - // if (count == 0) - // { - // // got all we wanted, no need to bother the decoder yet - // ares.Count = nread; - // ares.Complete(); - // return ares; - // } - // if (!decoder.WantMore) - // { - // no_more_data = nread == 0; - // ares.Count = nread; - // ares.Complete(); - // return ares; - // } - // ares.Buffer = new byte[8192]; - // ares.Offset = 0; - // ares.Count = 8192; - // ReadBufferState rb = new ReadBufferState(buffer, offset, count, ares); - // rb.InitialCount += nread; - // base.BeginRead(ares.Buffer, ares.Offset, ares.Count, OnRead, rb); - // return ares; - //} - - //void OnRead(IAsyncResult base_ares) - //{ - // ReadBufferState rb = (ReadBufferState)base_ares.AsyncState; - // HttpStreamAsyncResult ares = rb.Ares; - // try - // { - // int nread = base.EndRead(base_ares); - // decoder.Write(ares.Buffer, ares.Offset, nread); - // nread = decoder.Read(rb.Buffer, rb.Offset, rb.Count); - // rb.Offset += nread; - // rb.Count -= nread; - // if (rb.Count == 0 || !decoder.WantMore || nread == 0) - // { - // no_more_data = !decoder.WantMore && nread == 0; - // ares.Count = rb.InitialCount - rb.Count; - // ares.Complete(); - // return; - // } - // ares.Offset = 0; - // ares.Count = Math.Min(8192, decoder.ChunkLeft + 6); - // base.BeginRead(ares.Buffer, ares.Offset, ares.Count, OnRead, rb); - // } - // catch (Exception e) - // { - // context.Connection.SendError(e.Message, 400); - // ares.Complete(e); - // } - //} - - //public override int EndRead(IAsyncResult ares) - //{ - // if (disposed) - // throw new ObjectDisposedException(GetType().ToString()); - - // HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult; - // if (ares == null) - // throw new ArgumentException("Invalid IAsyncResult", "ares"); - - // if (!ares.IsCompleted) - // ares.AsyncWaitHandle.WaitOne(); - - // if (my_ares.Error != null) - // throw new HttpListenerException(400, "I/O operation aborted: " + my_ares.Error.Message); - - // return my_ares.Count; - //} - - //protected override void Dispose(bool disposing) - //{ - // if (!disposed) - // { - // disposed = true; - // base.Dispose(disposing); - // } - //} - } -} diff --git a/SocketHttpListener.Portable/Net/CookieHelper.cs b/SocketHttpListener.Portable/Net/CookieHelper.cs deleted file mode 100644 index 470507d6b..000000000 --- a/SocketHttpListener.Portable/Net/CookieHelper.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace SocketHttpListener.Net -{ - public static class CookieHelper - { - internal static CookieCollection Parse(string value, bool response) - { - return response - ? parseResponse(value) - : null; - } - - private static string[] splitCookieHeaderValue(string value) - { - return new List<string>(value.SplitHeaderValue(',', ';')).ToArray(); - } - - private static CookieCollection parseResponse(string value) - { - var cookies = new CookieCollection(); - - Cookie cookie = null; - var pairs = splitCookieHeaderValue(value); - for (int i = 0; i < pairs.Length; i++) - { - var pair = pairs[i].Trim(); - if (pair.Length == 0) - continue; - - if (pair.StartsWith("version", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Version = Int32.Parse(pair.GetValueInternal("=").Trim('"')); - } - else if (pair.StartsWith("expires", StringComparison.OrdinalIgnoreCase)) - { - var buffer = new StringBuilder(pair.GetValueInternal("="), 32); - if (i < pairs.Length - 1) - buffer.AppendFormat(", {0}", pairs[++i].Trim()); - - DateTime expires; - if (!DateTime.TryParseExact( - buffer.ToString(), - new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, - new CultureInfo("en-US"), - DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, - out expires)) - expires = DateTime.Now; - - if (cookie != null && cookie.Expires == DateTime.MinValue) - cookie.Expires = expires.ToLocalTime(); - } - else if (pair.StartsWith("max-age", StringComparison.OrdinalIgnoreCase)) - { - var max = Int32.Parse(pair.GetValueInternal("=").Trim('"')); - var expires = DateTime.Now.AddSeconds((double)max); - if (cookie != null) - cookie.Expires = expires; - } - else if (pair.StartsWith("path", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Path = pair.GetValueInternal("="); - } - else if (pair.StartsWith("domain", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Domain = pair.GetValueInternal("="); - } - else if (pair.StartsWith("port", StringComparison.OrdinalIgnoreCase)) - { - var port = pair.Equals("port", StringComparison.OrdinalIgnoreCase) - ? "\"\"" - : pair.GetValueInternal("="); - - if (cookie != null) - cookie.Port = port; - } - else if (pair.StartsWith("comment", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Comment = pair.GetValueInternal("=").UrlDecode(); - } - else if (pair.StartsWith("commenturl", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.CommentUri = pair.GetValueInternal("=").Trim('"').ToUri(); - } - else if (pair.StartsWith("discard", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Discard = true; - } - else if (pair.StartsWith("secure", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.Secure = true; - } - else if (pair.StartsWith("httponly", StringComparison.OrdinalIgnoreCase)) - { - if (cookie != null) - cookie.HttpOnly = true; - } - else - { - if (cookie != null) - cookies.Add(cookie); - - string name; - string val = String.Empty; - - var pos = pair.IndexOf('='); - if (pos == -1) - { - name = pair; - } - else if (pos == pair.Length - 1) - { - name = pair.Substring(0, pos).TrimEnd(' '); - } - else - { - name = pair.Substring(0, pos).TrimEnd(' '); - val = pair.Substring(pos + 1).TrimStart(' '); - } - - cookie = new Cookie(name, val); - } - } - - if (cookie != null) - cookies.Add(cookie); - - return cookies; - } - } -} diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs deleted file mode 100644 index 2106bbec5..000000000 --- a/SocketHttpListener.Portable/Net/EndPointListener.cs +++ /dev/null @@ -1,391 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using MediaBrowser.Model.Cryptography; -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 -{ - sealed class EndPointListener - { - HttpListener listener; - IpEndPointInfo endpoint; - IAcceptSocket sock; - Dictionary<ListenerPrefix,HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener> - List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*' - List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+' - ICertificate cert; - bool secure; - Dictionary<HttpConnection, HttpConnection> unregistered; - private readonly ILogger _logger; - private bool _closed; - private bool _enableDualMode; - private readonly ICryptoProvider _cryptoProvider; - private readonly IStreamFactory _streamFactory; - private readonly ISocketFactory _socketFactory; - private readonly ITextEncoding _textEncoding; - private readonly IMemoryStreamFactory _memoryStreamFactory; - private readonly IFileSystem _fileSystem; - private readonly IEnvironmentInfo _environment; - - public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) - { - this.listener = listener; - _logger = logger; - _cryptoProvider = cryptoProvider; - _streamFactory = streamFactory; - _socketFactory = socketFactory; - _memoryStreamFactory = memoryStreamFactory; - _textEncoding = textEncoding; - _fileSystem = fileSystem; - _environment = environment; - - this.secure = secure; - this.cert = cert; - - _enableDualMode = addr.Equals(IpAddressInfo.IPv6Any); - endpoint = new IpEndPointInfo(addr, port); - - prefixes = new Dictionary<ListenerPrefix, HttpListener>(); - unregistered = new Dictionary<HttpConnection, HttpConnection>(); - - CreateSocket(); - } - - internal HttpListener Listener - { - get - { - return listener; - } - } - - private void CreateSocket() - { - try - { - sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); - } - catch (SocketCreateException ex) - { - if (_enableDualMode && endpoint.IpAddress.Equals(IpAddressInfo.IPv6Any) && - (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) || - // mono on bsd is throwing this - string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase))) - { - endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port); - _enableDualMode = false; - sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); - } - else - { - throw; - } - } - - sock.Bind(endpoint); - - // This is the number TcpListener uses. - sock.Listen(2147483647); - - sock.StartAccept(ProcessAccept, () => _closed); - _closed = false; - } - - private async void ProcessAccept(IAcceptSocket accepted) - { - try - { - var listener = this; - - if (listener.secure && listener.cert == null) - { - accepted.Close(); - return; - } - - HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false); - - //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId); - lock (listener.unregistered) - { - listener.unregistered[conn] = conn; - } - conn.BeginReadRequest(); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ProcessAccept", ex); - } - } - - internal void RemoveConnection(HttpConnection conn) - { - lock (unregistered) - { - unregistered.Remove(conn); - } - } - - public bool BindContext(HttpListenerContext context) - { - HttpListenerRequest req = context.Request; - ListenerPrefix prefix; - HttpListener listener = SearchListener(req.Url, out prefix); - if (listener == null) - return false; - - context.Connection.Prefix = prefix; - return true; - } - - public void UnbindContext(HttpListenerContext context) - { - if (context == null || context.Request == null) - return; - - listener.UnregisterContext(context); - } - - HttpListener SearchListener(Uri uri, out ListenerPrefix prefix) - { - prefix = null; - if (uri == null) - return null; - - string host = uri.Host; - int port = uri.Port; - string path = WebUtility.UrlDecode(uri.AbsolutePath); - string path_slash = path[path.Length - 1] == '/' ? path : path + "/"; - - HttpListener best_match = null; - int best_length = -1; - - if (host != null && host != "") - { - var p_ro = prefixes; - foreach (ListenerPrefix p in p_ro.Keys) - { - string ppath = p.Path; - if (ppath.Length < best_length) - continue; - - if (p.Host != host || p.Port != port) - continue; - - if (path.StartsWith(ppath) || path_slash.StartsWith(ppath)) - { - best_length = ppath.Length; - best_match = (HttpListener)p_ro[p]; - prefix = p; - } - } - if (best_length != -1) - return best_match; - } - - List<ListenerPrefix> list = unhandled; - best_match = MatchFromList(host, path, list, out prefix); - if (path != path_slash && best_match == null) - best_match = MatchFromList(host, path_slash, list, out prefix); - if (best_match != null) - return best_match; - - list = all; - best_match = MatchFromList(host, path, list, out prefix); - if (path != path_slash && best_match == null) - best_match = MatchFromList(host, path_slash, list, out prefix); - if (best_match != null) - return best_match; - - return null; - } - - HttpListener MatchFromList(string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix) - { - prefix = null; - if (list == null) - return null; - - HttpListener best_match = null; - int best_length = -1; - - foreach (ListenerPrefix p in list) - { - string ppath = p.Path; - if (ppath.Length < best_length) - continue; - - if (path.StartsWith(ppath)) - { - best_length = ppath.Length; - best_match = p.Listener; - prefix = p; - } - } - - return best_match; - } - - void AddSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix) - { - if (coll == null) - return; - - foreach (ListenerPrefix p in coll) - { - if (p.Path == prefix.Path) //TODO: code - throw new HttpListenerException(400, "Prefix already in use."); - } - coll.Add(prefix); - } - - bool RemoveSpecial(List<ListenerPrefix> coll, ListenerPrefix prefix) - { - if (coll == null) - return false; - - int c = coll.Count; - for (int i = 0; i < c; i++) - { - ListenerPrefix p = (ListenerPrefix)coll[i]; - if (p.Path == prefix.Path) - { - coll.RemoveAt(i); - return true; - } - } - return false; - } - - void CheckIfRemove() - { - if (prefixes.Count > 0) - return; - - List<ListenerPrefix> list = unhandled; - if (list != null && list.Count > 0) - return; - - list = all; - if (list != null && list.Count > 0) - return; - - EndPointManager.RemoveEndPoint(this, endpoint); - } - - public void Close() - { - _closed = true; - sock.Close(); - lock (unregistered) - { - // - // Clone the list because RemoveConnection can be called from Close - // - var connections = new List<HttpConnection>(unregistered.Keys); - - foreach (HttpConnection c in connections) - c.Close(true); - unregistered.Clear(); - } - } - - public void AddPrefix(ListenerPrefix prefix, HttpListener listener) - { - List<ListenerPrefix> current; - List<ListenerPrefix> future; - if (prefix.Host == "*") - { - do - { - current = unhandled; - future = (current != null) ? current.ToList() : new List<ListenerPrefix>(); - prefix.Listener = listener; - AddSpecial(future, prefix); - } while (Interlocked.CompareExchange(ref unhandled, future, current) != current); - return; - } - - if (prefix.Host == "+") - { - do - { - current = all; - future = (current != null) ? current.ToList() : new List<ListenerPrefix>(); - prefix.Listener = listener; - AddSpecial(future, prefix); - } while (Interlocked.CompareExchange(ref all, future, current) != current); - return; - } - - Dictionary<ListenerPrefix, HttpListener> prefs; - Dictionary<ListenerPrefix, HttpListener> p2; - do - { - prefs = prefixes; - if (prefs.ContainsKey(prefix)) - { - HttpListener other = (HttpListener)prefs[prefix]; - if (other != listener) // TODO: code. - throw new HttpListenerException(400, "There's another listener for " + prefix); - return; - } - p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs); - p2[prefix] = listener; - } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs); - } - - public void RemovePrefix(ListenerPrefix prefix, HttpListener listener) - { - List<ListenerPrefix> current; - List<ListenerPrefix> future; - if (prefix.Host == "*") - { - do - { - current = unhandled; - future = (current != null) ? current.ToList() : new List<ListenerPrefix>(); - if (!RemoveSpecial(future, prefix)) - break; // Prefix not found - } while (Interlocked.CompareExchange(ref unhandled, future, current) != current); - CheckIfRemove(); - return; - } - - if (prefix.Host == "+") - { - do - { - current = all; - future = (current != null) ? current.ToList() : new List<ListenerPrefix>(); - if (!RemoveSpecial(future, prefix)) - break; // Prefix not found - } while (Interlocked.CompareExchange(ref all, future, current) != current); - CheckIfRemove(); - return; - } - - Dictionary<ListenerPrefix, HttpListener> prefs; - Dictionary<ListenerPrefix, HttpListener> p2; - do - { - prefs = prefixes; - if (!prefs.ContainsKey(prefix)) - break; - - p2 = new Dictionary<ListenerPrefix, HttpListener>(prefs); - p2.Remove(prefix); - } while (Interlocked.CompareExchange(ref prefixes, p2, prefs) != prefs); - CheckIfRemove(); - } - } -} diff --git a/SocketHttpListener.Portable/Net/EndPointManager.cs b/SocketHttpListener.Portable/Net/EndPointManager.cs deleted file mode 100644 index 6a00ed360..000000000 --- a/SocketHttpListener.Portable/Net/EndPointManager.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net -{ - sealed class EndPointManager - { - // Dictionary<IPAddress, Dictionary<int, EndPointListener>> - static Dictionary<string, Dictionary<int, EndPointListener>> ip_to_endpoints = new Dictionary<string, Dictionary<int, EndPointListener>>(); - - private EndPointManager() - { - } - - public static void AddListener(ILogger logger, HttpListener listener) - { - List<string> added = new List<string>(); - try - { - lock (ip_to_endpoints) - { - foreach (string prefix in listener.Prefixes) - { - AddPrefixInternal(logger, prefix, listener); - added.Add(prefix); - } - } - } - catch - { - foreach (string prefix in added) - { - RemovePrefix(logger, prefix, listener); - } - throw; - } - } - - public static void AddPrefix(ILogger logger, string prefix, HttpListener listener) - { - lock (ip_to_endpoints) - { - AddPrefixInternal(logger, prefix, listener); - } - } - - static void AddPrefixInternal(ILogger logger, string p, HttpListener listener) - { - ListenerPrefix lp = new ListenerPrefix(p); - if (lp.Path.IndexOf('%') != -1) - throw new HttpListenerException(400, "Invalid path."); - - if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1) // TODO: Code? - throw new HttpListenerException(400, "Invalid path."); - - // listens on all the interfaces if host name cannot be parsed by IPAddress. - EndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure).Result; - epl.AddPrefix(lp, listener); - } - - private static IpAddressInfo GetIpAnyAddress(HttpListener listener) - { - return listener.EnableDualMode ? IpAddressInfo.IPv6Any : IpAddressInfo.Any; - } - - static async Task<EndPointListener> GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure) - { - var networkManager = listener.NetworkManager; - - IpAddressInfo addr; - if (host == "*" || host == "+") - addr = GetIpAnyAddress(listener); - else if (networkManager.TryParseIpAddress(host, out addr) == false) - { - try - { - addr = (await networkManager.GetHostAddressesAsync(host).ConfigureAwait(false)).FirstOrDefault() ?? - GetIpAnyAddress(listener); - } - catch - { - addr = GetIpAnyAddress(listener); - } - } - - Dictionary<int, EndPointListener> p = null; // Dictionary<int, EndPointListener> - if (!ip_to_endpoints.TryGetValue(addr.Address, out p)) - { - p = new Dictionary<int, EndPointListener>(); - ip_to_endpoints[addr.Address] = p; - } - - EndPointListener epl = null; - if (p.ContainsKey(port)) - { - epl = (EndPointListener)p[port]; - } - else - { - epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo); - p[port] = epl; - } - - return epl; - } - - public static void RemoveEndPoint(EndPointListener epl, IpEndPointInfo ep) - { - lock (ip_to_endpoints) - { - // Dictionary<int, EndPointListener> p - Dictionary<int, EndPointListener> p; - if (ip_to_endpoints.TryGetValue(ep.IpAddress.Address, out p)) - { - p.Remove(ep.Port); - if (p.Count == 0) - { - ip_to_endpoints.Remove(ep.IpAddress.Address); - } - } - epl.Close(); - } - } - - public static void RemoveListener(ILogger logger, HttpListener listener) - { - lock (ip_to_endpoints) - { - foreach (string prefix in listener.Prefixes) - { - RemovePrefixInternal(logger, prefix, listener); - } - } - } - - public static void RemovePrefix(ILogger logger, string prefix, HttpListener listener) - { - lock (ip_to_endpoints) - { - RemovePrefixInternal(logger, prefix, listener); - } - } - - static void RemovePrefixInternal(ILogger logger, string prefix, HttpListener listener) - { - ListenerPrefix lp = new ListenerPrefix(prefix); - if (lp.Path.IndexOf('%') != -1) - return; - - if (lp.Path.IndexOf("//", StringComparison.Ordinal) != -1) - return; - - EndPointListener epl = GetEPListener(logger, lp.Host, lp.Port, listener, lp.Secure).Result; - epl.RemovePrefix(lp, listener); - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs deleted file mode 100644 index 65e7470f7..000000000 --- a/SocketHttpListener.Portable/Net/HttpConnection.cs +++ /dev/null @@ -1,558 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Cryptography; -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 -{ - sealed class HttpConnection - { - const int BufferSize = 8192; - IAcceptSocket sock; - Stream stream; - EndPointListener epl; - MemoryStream ms; - byte[] buffer; - HttpListenerContext context; - StringBuilder current_line; - ListenerPrefix prefix; - RequestStream i_stream; - Stream o_stream; - bool chunked; - int reuses; - bool context_bound; - bool secure; - int s_timeout = 300000; // 90k ms for first request, 15k ms from then on - IpEndPointInfo local_ep; - HttpListener last_listener; - int[] client_cert_errors; - ICertificate cert; - Stream ssl_stream; - - private readonly ILogger _logger; - private readonly ICryptoProvider _cryptoProvider; - private readonly IMemoryStreamFactory _memoryStreamFactory; - private readonly ITextEncoding _textEncoding; - private readonly IStreamFactory _streamFactory; - private readonly IFileSystem _fileSystem; - private readonly IEnvironmentInfo _environment; - - private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) - { - _logger = logger; - this.sock = sock; - this.epl = epl; - this.secure = secure; - this.cert = cert; - _cryptoProvider = cryptoProvider; - _memoryStreamFactory = memoryStreamFactory; - _textEncoding = textEncoding; - _fileSystem = fileSystem; - _environment = environment; - _streamFactory = streamFactory; - } - - private async Task InitStream() - { - if (secure == false) - { - stream = _streamFactory.CreateNetworkStream(sock, false); - } - else - { - //ssl_stream = epl.Listener.CreateSslStream(new NetworkStream(sock, false), false, (t, c, ch, e) => - //{ - // if (c == null) - // return true; - // var c2 = c as X509Certificate2; - // if (c2 == null) - // c2 = new X509Certificate2(c.GetRawCertData()); - // client_cert = c2; - // client_cert_errors = new int[] { (int)e }; - // return true; - //}); - //stream = ssl_stream.AuthenticatedStream; - - ssl_stream = _streamFactory.CreateSslStream(_streamFactory.CreateNetworkStream(sock, false), false); - await _streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert).ConfigureAwait(false); - stream = ssl_stream; - } - Init(); - } - - public static async Task<HttpConnection> Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) - { - var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem, environment); - - await connection.InitStream().ConfigureAwait(false); - - return connection; - } - - public Stream Stream - { - get - { - return stream; - } - } - - internal int[] ClientCertificateErrors - { - get { return client_cert_errors; } - } - - void Init() - { - if (ssl_stream != null) - { - //ssl_stream.AuthenticateAsServer(client_cert, true, (SslProtocols)ServicePointManager.SecurityProtocol, false); - //_streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert); - } - - context_bound = false; - i_stream = null; - o_stream = null; - prefix = null; - chunked = false; - ms = _memoryStreamFactory.CreateNew(); - position = 0; - input_state = InputState.RequestLine; - line_state = LineState.None; - context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem); - } - - public bool IsClosed - { - get { return (sock == null); } - } - - public int Reuses - { - get { return reuses; } - } - - public IpEndPointInfo LocalEndPoint - { - get - { - if (local_ep != null) - return local_ep; - - local_ep = (IpEndPointInfo)sock.LocalEndPoint; - return local_ep; - } - } - - public IpEndPointInfo RemoteEndPoint - { - get { return (IpEndPointInfo)sock.RemoteEndPoint; } - } - - public bool IsSecure - { - get { return secure; } - } - - public ListenerPrefix Prefix - { - get { return prefix; } - set { prefix = value; } - } - - public async Task BeginReadRequest() - { - if (buffer == null) - buffer = new byte[BufferSize]; - - try - { - //if (reuses == 1) - // s_timeout = 15000; - var nRead = await stream.ReadAsync(buffer, 0, BufferSize).ConfigureAwait(false); - - OnReadInternal(nRead); - } - catch (Exception ex) - { - OnReadInternalException(ms, ex); - } - } - - public RequestStream GetRequestStream(bool chunked, long contentlength) - { - if (i_stream == null) - { - byte[] buffer; - _memoryStreamFactory.TryGetBuffer(ms, out buffer); - - int length = (int)ms.Length; - ms = null; - if (chunked) - { - this.chunked = true; - //context.Response.SendChunked = true; - i_stream = new ChunkedInputStream(context, stream, buffer, position, length - position); - } - else - { - i_stream = new RequestStream(stream, buffer, position, length - position, contentlength); - } - } - return i_stream; - } - - public Stream GetResponseStream(bool isExpect100Continue = false) - { - // TODO: can we get this stream before reading the input? - if (o_stream == null) - { - //context.Response.DetermineIfChunked(); - - if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true) - { - var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure; - - o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger, _environment); - } - else - { - o_stream = stream; - using (var headerStream = ResponseStream.GetHeaders(context.Response, _memoryStreamFactory, false)) - { - headerStream.CopyTo(o_stream); - } - } - } - return o_stream; - } - - void OnReadInternal(int nread) - { - ms.Write(buffer, 0, nread); - if (ms.Length > 32768) - { - SendError("Bad request", 400); - Close(true); - return; - } - - if (nread == 0) - { - //if (ms.Length > 0) - // SendError (); // Why bother? - CloseSocket(); - Unbind(); - return; - } - - if (ProcessInput(ms)) - { - if (!context.HaveError) - context.Request.FinishInitialization(); - - if (context.HaveError) - { - SendError(); - Close(true); - return; - } - - if (!epl.BindContext(context)) - { - SendError("Invalid host", 400); - Close(true); - return; - } - HttpListener listener = epl.Listener; - if (last_listener != listener) - { - RemoveConnection(); - listener.AddConnection(this); - last_listener = listener; - } - - context_bound = true; - listener.RegisterContext(context); - return; - } - - BeginReadRequest(); - } - - private void OnReadInternalException(MemoryStream ms, Exception ex) - { - //_logger.ErrorException("Error in HttpConnection.OnReadInternal", ex); - - if (ms != null && ms.Length > 0) - SendError(); - if (sock != null) - { - CloseSocket(); - Unbind(); - } - } - - void RemoveConnection() - { - if (last_listener == null) - epl.RemoveConnection(this); - else - last_listener.RemoveConnection(this); - } - - enum InputState - { - RequestLine, - Headers - } - - enum LineState - { - None, - CR, - LF - } - - InputState input_state = InputState.RequestLine; - LineState line_state = LineState.None; - int position; - - // true -> done processing - // false -> need more input - bool ProcessInput(MemoryStream ms) - { - byte[] buffer; - _memoryStreamFactory.TryGetBuffer(ms, out buffer); - - int len = (int)ms.Length; - int used = 0; - string line; - - while (true) - { - if (context.HaveError) - return true; - - if (position >= len) - break; - - try - { - line = ReadLine(buffer, position, len - position, ref used); - position += used; - } - catch - { - context.ErrorMessage = "Bad request"; - context.ErrorStatus = 400; - return true; - } - - if (line == null) - break; - - if (line == "") - { - if (input_state == InputState.RequestLine) - continue; - current_line = null; - ms = null; - return true; - } - - if (input_state == InputState.RequestLine) - { - context.Request.SetRequestLine(line); - input_state = InputState.Headers; - } - else - { - try - { - context.Request.AddHeader(line); - } - catch (Exception e) - { - context.ErrorMessage = e.Message; - context.ErrorStatus = 400; - return true; - } - } - } - - if (used == len) - { - ms.SetLength(0); - position = 0; - } - return false; - } - - string ReadLine(byte[] buffer, int offset, int len, ref int used) - { - if (current_line == null) - current_line = new StringBuilder(128); - int last = offset + len; - used = 0; - - for (int i = offset; i < last && line_state != LineState.LF; i++) - { - used++; - byte b = buffer[i]; - if (b == 13) - { - line_state = LineState.CR; - } - else if (b == 10) - { - line_state = LineState.LF; - } - else - { - current_line.Append((char)b); - } - } - - string result = null; - if (line_state == LineState.LF) - { - line_state = LineState.None; - result = current_line.ToString(); - current_line.Length = 0; - } - - return result; - } - - public void SendError(string msg, int status) - { - try - { - HttpListenerResponse response = context.Response; - response.StatusCode = status; - response.ContentType = "text/html"; - string description = HttpListenerResponse.GetStatusDescription(status); - string str; - if (msg != null) - str = String.Format("<h1>{0} ({1})</h1>", description, msg); - else - str = String.Format("<h1>{0}</h1>", description); - - byte[] error = context.Response.ContentEncoding.GetBytes(str); - response.ContentLength64 = error.Length; - response.OutputStream.Write(error, 0, (int)error.Length); - response.Close(); - } - catch - { - // response was already closed - } - } - - public void SendError() - { - SendError(context.ErrorMessage, context.ErrorStatus); - } - - void Unbind() - { - if (context_bound) - { - epl.UnbindContext(context); - context_bound = false; - } - } - - public void Close() - { - Close(false); - } - - private void CloseSocket() - { - if (sock == null) - return; - - try - { - sock.Close(); - } - catch - { - } - finally - { - sock = null; - } - RemoveConnection(); - } - - internal void Close(bool force_close) - { - if (sock != null) - { - if (!context.Request.IsWebSocketRequest || force_close) - { - Stream st = GetResponseStream(); - if (st != null) - { - st.Dispose(); - } - - o_stream = null; - } - } - - if (sock != null) - { - force_close |= !context.Request.KeepAlive; - if (!force_close) - force_close = (string.Equals(context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase)); - /* - if (!force_close) { -// bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 || -// status_code == 413 || status_code == 414 || status_code == 500 || -// status_code == 503); - force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10); - } - */ - - if (!force_close && context.Request.FlushInput()) - { - reuses++; - Unbind(); - Init(); - BeginReadRequest(); - return; - } - - IAcceptSocket s = sock; - sock = null; - try - { - if (s != null) - s.Shutdown(true); - } - catch - { - } - finally - { - if (s != null) - s.Close(); - } - Unbind(); - RemoveConnection(); - return; - } - } - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/HttpListener.cs b/SocketHttpListener.Portable/Net/HttpListener.cs deleted file mode 100644 index b3e01425c..000000000 --- a/SocketHttpListener.Portable/Net/HttpListener.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using MediaBrowser.Common.Net; -using MediaBrowser.Model.Cryptography; -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 -{ - public sealed class HttpListener : IDisposable - { - internal ICryptoProvider CryptoProvider { get; private set; } - internal IStreamFactory StreamFactory { get; private set; } - internal ISocketFactory SocketFactory { get; private set; } - internal IFileSystem FileSystem { get; private set; } - internal ITextEncoding TextEncoding { get; private set; } - internal IMemoryStreamFactory MemoryStreamFactory { get; private set; } - internal INetworkManager NetworkManager { get; private set; } - internal IEnvironmentInfo EnvironmentInfo { get; private set; } - - public bool EnableDualMode { get; set; } - - AuthenticationSchemes auth_schemes; - HttpListenerPrefixCollection prefixes; - AuthenticationSchemeSelector auth_selector; - string realm; - bool unsafe_ntlm_auth; - bool listening; - bool disposed; - - Dictionary<HttpListenerContext, HttpListenerContext> registry; // Dictionary<HttpListenerContext,HttpListenerContext> - Dictionary<HttpConnection, HttpConnection> connections; - private ILogger _logger; - private ICertificate _certificate; - - public Action<HttpListenerContext> OnContext { get; set; } - - public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) - { - _logger = logger; - CryptoProvider = cryptoProvider; - StreamFactory = streamFactory; - SocketFactory = socketFactory; - NetworkManager = networkManager; - TextEncoding = textEncoding; - MemoryStreamFactory = memoryStreamFactory; - FileSystem = fileSystem; - EnvironmentInfo = environmentInfo; - prefixes = new HttpListenerPrefixCollection(logger, this); - registry = new Dictionary<HttpListenerContext, HttpListenerContext>(); - connections = new Dictionary<HttpConnection, HttpConnection>(); - auth_schemes = AuthenticationSchemes.Anonymous; - } - - public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) - :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) - { - } - - public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) - : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) - { - _certificate = certificate; - } - - public void LoadCert(ICertificate cert) - { - _certificate = cert; - } - - // TODO: Digest, NTLM and Negotiate require ControlPrincipal - public AuthenticationSchemes AuthenticationSchemes - { - get { return auth_schemes; } - set - { - CheckDisposed(); - auth_schemes = value; - } - } - - public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate - { - get { return auth_selector; } - set - { - CheckDisposed(); - auth_selector = value; - } - } - - public bool IsListening - { - get { return listening; } - } - - public static bool IsSupported - { - get { return true; } - } - - public HttpListenerPrefixCollection Prefixes - { - get - { - CheckDisposed(); - return prefixes; - } - } - - // TODO: use this - public string Realm - { - get { return realm; } - set - { - CheckDisposed(); - realm = value; - } - } - - public bool UnsafeConnectionNtlmAuthentication - { - get { return unsafe_ntlm_auth; } - set - { - CheckDisposed(); - unsafe_ntlm_auth = value; - } - } - - //internal IMonoSslStream CreateSslStream(Stream innerStream, bool ownsStream, MSI.MonoRemoteCertificateValidationCallback callback) - //{ - // lock (registry) - // { - // if (tlsProvider == null) - // tlsProvider = MonoTlsProviderFactory.GetProviderInternal(); - // if (tlsSettings == null) - // tlsSettings = MSI.MonoTlsSettings.CopyDefaultSettings(); - // if (tlsSettings.RemoteCertificateValidationCallback == null) - // tlsSettings.RemoteCertificateValidationCallback = callback; - // return tlsProvider.CreateSslStream(innerStream, ownsStream, tlsSettings); - // } - //} - - internal ICertificate Certificate - { - get { return _certificate; } - } - - public void Abort() - { - if (disposed) - return; - - if (!listening) - { - return; - } - - Close(true); - } - - public void Close() - { - if (disposed) - return; - - if (!listening) - { - disposed = true; - return; - } - - Close(true); - disposed = true; - } - - void Close(bool force) - { - CheckDisposed(); - EndPointManager.RemoveListener(_logger, this); - Cleanup(force); - } - - void Cleanup(bool close_existing) - { - lock (registry) - { - if (close_existing) - { - // Need to copy this since closing will call UnregisterContext - ICollection keys = registry.Keys; - var all = new HttpListenerContext[keys.Count]; - keys.CopyTo(all, 0); - registry.Clear(); - for (int i = all.Length - 1; i >= 0; i--) - all[i].Connection.Close(true); - } - - lock (connections) - { - ICollection keys = connections.Keys; - var conns = new HttpConnection[keys.Count]; - keys.CopyTo(conns, 0); - connections.Clear(); - for (int i = conns.Length - 1; i >= 0; i--) - conns[i].Close(true); - } - } - } - - internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context) - { - if (AuthenticationSchemeSelectorDelegate != null) - return AuthenticationSchemeSelectorDelegate(context.Request); - else - return auth_schemes; - } - - public void Start() - { - CheckDisposed(); - if (listening) - return; - - EndPointManager.AddListener(_logger, this); - listening = true; - } - - public void Stop() - { - CheckDisposed(); - listening = false; - Close(false); - } - - void IDisposable.Dispose() - { - if (disposed) - return; - - Close(true); //TODO: Should we force here or not? - disposed = true; - } - - internal void CheckDisposed() - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - } - - internal void RegisterContext(HttpListenerContext context) - { - if (OnContext != null && IsListening) - { - OnContext(context); - } - - lock (registry) - registry[context] = context; - } - - internal void UnregisterContext(HttpListenerContext context) - { - lock (registry) - registry.Remove(context); - } - - internal void AddConnection(HttpConnection cnc) - { - lock (connections) - { - connections[cnc] = cnc; - } - } - - internal void RemoveConnection(HttpConnection cnc) - { - lock (connections) - { - connections.Remove(cnc); - } - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs b/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs deleted file mode 100644 index faa26693d..000000000 --- a/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Security.Principal; - -namespace SocketHttpListener.Net -{ - public class HttpListenerBasicIdentity : GenericIdentity - { - string password; - - public HttpListenerBasicIdentity(string username, string password) - : base(username, "Basic") - { - this.password = password; - } - - public virtual string Password - { - get { return password; } - } - } - - public class GenericIdentity : IIdentity - { - private string m_name; - private string m_type; - - public GenericIdentity(string name) - { - if (name == null) - throw new System.ArgumentNullException("name"); - - m_name = name; - m_type = ""; - } - - public GenericIdentity(string name, string type) - { - if (name == null) - throw new System.ArgumentNullException("name"); - if (type == null) - throw new System.ArgumentNullException("type"); - - m_name = name; - m_type = type; - } - - public virtual string Name - { - get - { - return m_name; - } - } - - public virtual string AuthenticationType - { - get - { - return m_type; - } - } - - public virtual bool IsAuthenticated - { - get - { - return !m_name.Equals(""); - } - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpListenerContext.cs b/SocketHttpListener.Portable/Net/HttpListenerContext.cs deleted file mode 100644 index 58d769f22..000000000 --- a/SocketHttpListener.Portable/Net/HttpListenerContext.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Net; -using System.Security.Principal; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Text; -using SocketHttpListener.Net.WebSockets; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net -{ - public sealed class HttpListenerContext - { - HttpListenerRequest request; - HttpListenerResponse response; - IPrincipal user; - HttpConnection cnc; - string error; - int err_status = 400; - private readonly ICryptoProvider _cryptoProvider; - private readonly IMemoryStreamFactory _memoryStreamFactory; - private readonly ITextEncoding _textEncoding; - - internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem) - { - this.cnc = cnc; - _cryptoProvider = cryptoProvider; - _memoryStreamFactory = memoryStreamFactory; - _textEncoding = textEncoding; - request = new HttpListenerRequest(this, _textEncoding); - response = new HttpListenerResponse(this, logger, _textEncoding, fileSystem); - } - - internal int ErrorStatus - { - get { return err_status; } - set { err_status = value; } - } - - internal string ErrorMessage - { - get { return error; } - set { error = value; } - } - - internal bool HaveError - { - get { return (error != null); } - } - - internal HttpConnection Connection - { - get { return cnc; } - } - - public HttpListenerRequest Request - { - get { return request; } - } - - public HttpListenerResponse Response - { - get { return response; } - } - - public IPrincipal User - { - get { return user; } - } - - internal void ParseAuthentication(AuthenticationSchemes expectedSchemes) - { - if (expectedSchemes == AuthenticationSchemes.Anonymous) - return; - - // TODO: Handle NTLM/Digest modes - string header = request.Headers["Authorization"]; - if (header == null || header.Length < 2) - return; - - string[] authenticationData = header.Split(new char[] { ' ' }, 2); - if (string.Equals(authenticationData[0], "basic", StringComparison.OrdinalIgnoreCase)) - { - user = ParseBasicAuthentication(authenticationData[1]); - } - // TODO: throw if malformed -> 400 bad request - } - - internal IPrincipal ParseBasicAuthentication(string authData) - { - try - { - // Basic AUTH Data is a formatted Base64 String - //string domain = null; - string user = null; - string password = null; - int pos = -1; - var authDataBytes = Convert.FromBase64String(authData); - string authString = _textEncoding.GetDefaultEncoding().GetString(authDataBytes, 0, authDataBytes.Length); - - // The format is DOMAIN\username:password - // Domain is optional - - pos = authString.IndexOf(':'); - - // parse the password off the end - password = authString.Substring(pos + 1); - - // discard the password - authString = authString.Substring(0, pos); - - // check if there is a domain - pos = authString.IndexOf('\\'); - - if (pos > 0) - { - //domain = authString.Substring (0, pos); - user = authString.Substring(pos); - } - else - { - user = authString; - } - - HttpListenerBasicIdentity identity = new HttpListenerBasicIdentity(user, password); - // TODO: What are the roles MS sets - return new GenericPrincipal(identity, new string[0]); - } - catch (Exception) - { - // Invalid auth data is swallowed silently - return null; - } - } - - public HttpListenerWebSocketContext AcceptWebSocket(string protocol) - { - if (protocol != null) - { - if (protocol.Length == 0) - throw new ArgumentException("An empty string.", "protocol"); - - if (!protocol.IsToken()) - throw new ArgumentException("Contains an invalid character.", "protocol"); - } - - return new HttpListenerWebSocketContext(this, protocol, _cryptoProvider, _memoryStreamFactory); - } - } - - public class GenericPrincipal : IPrincipal - { - private IIdentity m_identity; - private string[] m_roles; - - public GenericPrincipal(IIdentity identity, string[] roles) - { - if (identity == null) - throw new ArgumentNullException("identity"); - - m_identity = identity; - if (roles != null) - { - m_roles = new string[roles.Length]; - for (int i = 0; i < roles.Length; ++i) - { - m_roles[i] = roles[i]; - } - } - else - { - m_roles = null; - } - } - - public virtual IIdentity Identity - { - get - { - return m_identity; - } - } - - public virtual bool IsInRole(string role) - { - if (role == null || m_roles == null) - return false; - - for (int i = 0; i < m_roles.Length; ++i) - { - if (m_roles[i] != null && String.Compare(m_roles[i], role, StringComparison.OrdinalIgnoreCase) == 0) - return true; - } - return false; - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs b/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs deleted file mode 100644 index 0b05539ee..000000000 --- a/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using MediaBrowser.Model.Logging; - -namespace SocketHttpListener.Net -{ - public class HttpListenerPrefixCollection : ICollection<string>, IEnumerable<string>, IEnumerable - { - List<string> prefixes = new List<string>(); - HttpListener listener; - - private ILogger _logger; - - internal HttpListenerPrefixCollection(ILogger logger, HttpListener listener) - { - _logger = logger; - this.listener = listener; - } - - public int Count - { - get { return prefixes.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public bool IsSynchronized - { - get { return false; } - } - - public void Add(string uriPrefix) - { - listener.CheckDisposed(); - ListenerPrefix.CheckUri(uriPrefix); - if (prefixes.Contains(uriPrefix)) - return; - - prefixes.Add(uriPrefix); - if (listener.IsListening) - EndPointManager.AddPrefix(_logger, uriPrefix, listener); - } - - public void Clear() - { - listener.CheckDisposed(); - prefixes.Clear(); - if (listener.IsListening) - EndPointManager.RemoveListener(_logger, listener); - } - - public bool Contains(string uriPrefix) - { - listener.CheckDisposed(); - return prefixes.Contains(uriPrefix); - } - - public void CopyTo(string[] array, int offset) - { - listener.CheckDisposed(); - prefixes.CopyTo(array, offset); - } - - public void CopyTo(Array array, int offset) - { - listener.CheckDisposed(); - ((ICollection)prefixes).CopyTo(array, offset); - } - - public IEnumerator<string> GetEnumerator() - { - return prefixes.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return prefixes.GetEnumerator(); - } - - public bool Remove(string uriPrefix) - { - listener.CheckDisposed(); - if (uriPrefix == null) - throw new ArgumentNullException("uriPrefix"); - - bool result = prefixes.Remove(uriPrefix); - if (result && listener.IsListening) - EndPointManager.RemovePrefix(_logger, uriPrefix, listener); - - return result; - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpListenerRequest.cs b/SocketHttpListener.Portable/Net/HttpListenerRequest.cs deleted file mode 100644 index cfbd49203..000000000 --- a/SocketHttpListener.Portable/Net/HttpListenerRequest.cs +++ /dev/null @@ -1,654 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.Text; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net -{ - public sealed class HttpListenerRequest - { - string[] accept_types; - Encoding content_encoding; - long content_length; - bool cl_set; - CookieCollection cookies; - WebHeaderCollection headers; - string method; - Stream input_stream; - Version version; - QueryParamCollection query_string; // check if null is ok, check if read-only, check case-sensitiveness - string raw_url; - Uri url; - Uri referrer; - string[] user_languages; - HttpListenerContext context; - bool is_chunked; - bool ka_set; - bool keep_alive; - - private readonly ITextEncoding _textEncoding; - - internal HttpListenerRequest(HttpListenerContext context, ITextEncoding textEncoding) - { - this.context = context; - _textEncoding = textEncoding; - headers = new WebHeaderCollection(); - version = HttpVersion.Version10; - } - - static char[] separators = new char[] { ' ' }; - - internal void SetRequestLine(string req) - { - string[] parts = req.Split(separators, 3); - if (parts.Length != 3) - { - context.ErrorMessage = "Invalid request line (parts)."; - return; - } - - method = parts[0]; - foreach (char c in method) - { - int ic = (int)c; - - if ((ic >= 'A' && ic <= 'Z') || - (ic > 32 && c < 127 && c != '(' && c != ')' && c != '<' && - c != '<' && c != '>' && c != '@' && c != ',' && c != ';' && - c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' && - c != ']' && c != '?' && c != '=' && c != '{' && c != '}')) - continue; - - context.ErrorMessage = "(Invalid verb)"; - return; - } - - raw_url = parts[1]; - if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/")) - { - context.ErrorMessage = "Invalid request line (version)."; - return; - } - - try - { - version = new Version(parts[2].Substring(5)); - if (version.Major < 1) - throw new Exception(); - } - catch - { - context.ErrorMessage = "Invalid request line (version)."; - return; - } - } - - void CreateQueryString(string query) - { - if (query == null || query.Length == 0) - { - query_string = new QueryParamCollection(); - return; - } - - query_string = new QueryParamCollection(); - if (query[0] == '?') - query = query.Substring(1); - string[] components = query.Split('&'); - foreach (string kv in components) - { - int pos = kv.IndexOf('='); - if (pos == -1) - { - query_string.Add(null, WebUtility.UrlDecode(kv)); - } - else - { - string key = WebUtility.UrlDecode(kv.Substring(0, pos)); - string val = WebUtility.UrlDecode(kv.Substring(pos + 1)); - - query_string.Add(key, val); - } - } - } - - internal void FinishInitialization() - { - string host = UserHostName; - if (version > HttpVersion.Version10 && (host == null || host.Length == 0)) - { - context.ErrorMessage = "Invalid host name"; - return; - } - - string path; - Uri raw_uri = null; - if (MaybeUri(raw_url.ToLowerInvariant()) && Uri.TryCreate(raw_url, UriKind.Absolute, out raw_uri)) - path = raw_uri.PathAndQuery; - else - path = raw_url; - - if ((host == null || host.Length == 0)) - host = UserHostAddress; - - if (raw_uri != null) - host = raw_uri.Host; - - int colon = host.LastIndexOf(':'); - if (colon >= 0) - host = host.Substring(0, colon); - - string base_uri = String.Format("{0}://{1}:{2}", - (IsSecureConnection) ? (IsWebSocketRequest ? "wss" : "https") : (IsWebSocketRequest ? "ws" : "http"), - host, LocalEndPoint.Port); - - if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out url)) - { - context.ErrorMessage = WebUtility.HtmlEncode("Invalid url: " + base_uri + path); - return; return; - } - - CreateQueryString(url.Query); - - if (version >= HttpVersion.Version11) - { - string t_encoding = Headers["Transfer-Encoding"]; - is_chunked = (t_encoding != null && String.Compare(t_encoding, "chunked", StringComparison.OrdinalIgnoreCase) == 0); - // 'identity' is not valid! - if (t_encoding != null && !is_chunked) - { - context.Connection.SendError(null, 501); - return; - } - } - - if (!is_chunked && !cl_set) - { - if (String.Compare(method, "POST", StringComparison.OrdinalIgnoreCase) == 0 || - String.Compare(method, "PUT", StringComparison.OrdinalIgnoreCase) == 0) - { - context.Connection.SendError(null, 411); - return; - } - } - - if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0) - { - var output = (ResponseStream)context.Connection.GetResponseStream(true); - - var _100continue = _textEncoding.GetASCIIEncoding().GetBytes("HTTP/1.1 100 Continue\r\n\r\n"); - - output.InternalWrite(_100continue, 0, _100continue.Length); - } - } - - static bool MaybeUri(string s) - { - int p = s.IndexOf(':'); - if (p == -1) - return false; - - if (p >= 10) - return false; - - return IsPredefinedScheme(s.Substring(0, p)); - } - - // - // Using a simple block of if's is twice as slow as the compiler generated - // switch statement. But using this tuned code is faster than the - // compiler generated code, with a million loops on x86-64: - // - // With "http": .10 vs .51 (first check) - // with "https": .16 vs .51 (second check) - // with "foo": .22 vs .31 (never found) - // with "mailto": .12 vs .51 (last check) - // - // - static bool IsPredefinedScheme(string scheme) - { - if (scheme == null || scheme.Length < 3) - return false; - - char c = scheme[0]; - if (c == 'h') - return (scheme == "http" || scheme == "https"); - if (c == 'f') - return (scheme == "file" || scheme == "ftp"); - - if (c == 'n') - { - c = scheme[1]; - if (c == 'e') - return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp"); - if (scheme == "nntp") - return true; - return false; - } - if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto")) - return true; - - return false; - } - - internal static string Unquote(String str) - { - int start = str.IndexOf('\"'); - int end = str.LastIndexOf('\"'); - if (start >= 0 && end >= 0) - str = str.Substring(start + 1, end - 1); - return str.Trim(); - } - - internal void AddHeader(string header) - { - int colon = header.IndexOf(':'); - if (colon == -1 || colon == 0) - { - context.ErrorMessage = "Bad Request"; - context.ErrorStatus = 400; - return; - } - - string name = header.Substring(0, colon).Trim(); - string val = header.Substring(colon + 1).Trim(); - string lower = name.ToLowerInvariant(); - headers.SetInternal(name, val); - switch (lower) - { - case "accept-language": - user_languages = val.Split(','); // yes, only split with a ',' - break; - case "accept": - accept_types = val.Split(','); // yes, only split with a ',' - break; - case "content-length": - try - { - //TODO: max. content_length? - content_length = Int64.Parse(val.Trim()); - if (content_length < 0) - context.ErrorMessage = "Invalid Content-Length."; - cl_set = true; - } - catch - { - context.ErrorMessage = "Invalid Content-Length."; - } - - break; - case "content-type": - { - var contents = val.Split(';'); - foreach (var content in contents) - { - var tmp = content.Trim(); - if (tmp.StartsWith("charset")) - { - var charset = tmp.GetValue("="); - if (charset != null && charset.Length > 0) - { - try - { - - // Support upnp/dlna devices - CONTENT-TYPE: text/xml ; charset="utf-8"\r\n - charset = charset.Trim('"'); - var index = charset.IndexOf('"'); - if (index != -1) charset = charset.Substring(0, index); - - content_encoding = Encoding.GetEncoding(charset); - } - catch - { - context.ErrorMessage = "Invalid Content-Type header: " + charset; - } - } - - break; - } - } - } - break; - case "referer": - try - { - referrer = new Uri(val); - } - catch - { - referrer = new Uri("http://someone.is.screwing.with.the.headers.com/"); - } - break; - case "cookie": - if (cookies == null) - cookies = new CookieCollection(); - - string[] cookieStrings = val.Split(new char[] { ',', ';' }); - Cookie current = null; - int version = 0; - foreach (string cookieString in cookieStrings) - { - string str = cookieString.Trim(); - if (str.Length == 0) - continue; - if (str.StartsWith("$Version")) - { - version = Int32.Parse(Unquote(str.Substring(str.IndexOf('=') + 1))); - } - else if (str.StartsWith("$Path")) - { - if (current != null) - current.Path = str.Substring(str.IndexOf('=') + 1).Trim(); - } - else if (str.StartsWith("$Domain")) - { - if (current != null) - current.Domain = str.Substring(str.IndexOf('=') + 1).Trim(); - } - else if (str.StartsWith("$Port")) - { - if (current != null) - current.Port = str.Substring(str.IndexOf('=') + 1).Trim(); - } - else - { - if (current != null) - { - cookies.Add(current); - } - current = new Cookie(); - int idx = str.IndexOf('='); - if (idx > 0) - { - current.Name = str.Substring(0, idx).Trim(); - current.Value = str.Substring(idx + 1).Trim(); - } - else - { - current.Name = str.Trim(); - current.Value = String.Empty; - } - current.Version = version; - } - } - if (current != null) - { - cookies.Add(current); - } - break; - } - } - - // returns true is the stream could be reused. - internal bool FlushInput() - { - if (!HasEntityBody) - return true; - - int length = 2048; - if (content_length > 0) - length = (int)Math.Min(content_length, (long)length); - - byte[] bytes = new byte[length]; - while (true) - { - // TODO: test if MS has a timeout when doing this - try - { - var task = InputStream.ReadAsync(bytes, 0, length); - var result = Task.WaitAll(new [] { task }, 1000); - if (!result) - { - return false; - } - if (task.Result <= 0) - { - return true; - } - } - catch (ObjectDisposedException e) - { - input_stream = null; - return true; - } - catch - { - return false; - } - } - } - - public string[] AcceptTypes - { - get { return accept_types; } - } - - public int ClientCertificateError - { - get - { - HttpConnection cnc = context.Connection; - //if (cnc.ClientCertificate == null) - // throw new InvalidOperationException("No client certificate"); - //int[] errors = cnc.ClientCertificateErrors; - //if (errors != null && errors.Length > 0) - // return errors[0]; - return 0; - } - } - - public Encoding ContentEncoding - { - get - { - if (content_encoding == null) - content_encoding = _textEncoding.GetDefaultEncoding(); - return content_encoding; - } - } - - public long ContentLength64 - { - get { return is_chunked ? -1 : content_length; } - } - - public string ContentType - { - get { return headers["content-type"]; } - } - - public CookieCollection Cookies - { - get - { - // TODO: check if the collection is read-only - if (cookies == null) - cookies = new CookieCollection(); - return cookies; - } - } - - public bool HasEntityBody - { - get { return (content_length > 0 || is_chunked); } - } - - public QueryParamCollection Headers - { - get { return headers; } - } - - public string HttpMethod - { - get { return method; } - } - - public Stream InputStream - { - get - { - if (input_stream == null) - { - if (is_chunked || content_length > 0) - input_stream = context.Connection.GetRequestStream(is_chunked, content_length); - else - input_stream = Stream.Null; - } - - return input_stream; - } - } - - public bool IsAuthenticated - { - get { return false; } - } - - public bool IsLocal - { - get { return RemoteEndPoint.IpAddress.Equals(IpAddressInfo.Loopback) || RemoteEndPoint.IpAddress.Equals(IpAddressInfo.IPv6Loopback) || LocalEndPoint.IpAddress.Equals(RemoteEndPoint.IpAddress); } - } - - public bool IsSecureConnection - { - get { return context.Connection.IsSecure; } - } - - public bool KeepAlive - { - get - { - if (ka_set) - return keep_alive; - - ka_set = true; - // 1. Connection header - // 2. Protocol (1.1 == keep-alive by default) - // 3. Keep-Alive header - string cnc = headers["Connection"]; - if (!String.IsNullOrEmpty(cnc)) - { - keep_alive = (0 == String.Compare(cnc, "keep-alive", StringComparison.OrdinalIgnoreCase)); - } - else if (version == HttpVersion.Version11) - { - keep_alive = true; - } - else - { - cnc = headers["keep-alive"]; - if (!String.IsNullOrEmpty(cnc)) - keep_alive = (0 != String.Compare(cnc, "closed", StringComparison.OrdinalIgnoreCase)); - } - return keep_alive; - } - } - - public IpEndPointInfo LocalEndPoint - { - get { return context.Connection.LocalEndPoint; } - } - - public Version ProtocolVersion - { - get { return version; } - } - - public QueryParamCollection QueryString - { - get { return query_string; } - } - - public string RawUrl - { - get { return raw_url; } - } - - public IpEndPointInfo RemoteEndPoint - { - get { return context.Connection.RemoteEndPoint; } - } - - public Guid RequestTraceIdentifier - { - get { return Guid.Empty; } - } - - public Uri Url - { - get { return url; } - } - - public Uri UrlReferrer - { - get { return referrer; } - } - - public string UserAgent - { - get { return headers["user-agent"]; } - } - - public string UserHostAddress - { - get { return LocalEndPoint.ToString(); } - } - - public string UserHostName - { - get { return headers["host"]; } - } - - public string[] UserLanguages - { - get { return user_languages; } - } - - public string ServiceName - { - get - { - return null; - } - } - - private bool _websocketRequestWasSet; - private bool _websocketRequest; - - /// <summary> - /// Gets a value indicating whether the request is a WebSocket connection request. - /// </summary> - /// <value> - /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>. - /// </value> - public bool IsWebSocketRequest - { - get - { - if (!_websocketRequestWasSet) - { - _websocketRequest = method == "GET" && - version > HttpVersion.Version10 && - headers.Contains("Upgrade", "websocket") && - headers.Contains("Connection", "Upgrade"); - - _websocketRequestWasSet = true; - } - - return _websocketRequest; - } - } - - public Task<ICertificate> GetClientCertificateAsync() - { - return Task.FromResult<ICertificate>(null); - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs deleted file mode 100644 index 3cb6a0d75..000000000 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ /dev/null @@ -1,525 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Text; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net -{ - public sealed class HttpListenerResponse : IDisposable - { - bool disposed; - Encoding content_encoding; - long content_length; - bool cl_set; - string content_type; - CookieCollection cookies; - WebHeaderCollection headers = new WebHeaderCollection(); - bool keep_alive = true; - Stream output_stream; - Version version = HttpVersion.Version11; - string location; - int status_code = 200; - string status_description = "OK"; - bool chunked; - HttpListenerContext context; - - internal bool HeadersSent; - internal object headers_lock = new object(); - - private readonly ILogger _logger; - private readonly ITextEncoding _textEncoding; - private readonly IFileSystem _fileSystem; - - internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding, IFileSystem fileSystem) - { - this.context = context; - _logger = logger; - _textEncoding = textEncoding; - _fileSystem = fileSystem; - } - - internal bool CloseConnection - { - get - { - return headers["Connection"] == "close"; - } - } - - public Encoding ContentEncoding - { - get - { - if (content_encoding == null) - content_encoding = _textEncoding.GetDefaultEncoding(); - return content_encoding; - } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - content_encoding = value; - } - } - - public long ContentLength64 - { - get { return content_length; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (HeadersSent) - throw new InvalidOperationException("Cannot be changed after headers are sent."); - - if (value < 0) - throw new ArgumentOutOfRangeException("Must be >= 0", "value"); - - cl_set = true; - content_length = value; - } - } - - public string ContentType - { - get { return content_type; } - set - { - // TODO: is null ok? - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - content_type = value; - } - } - - // RFC 2109, 2965 + the netscape specification at http://wp.netscape.com/newsref/std/cookie_spec.html - public CookieCollection Cookies - { - get - { - if (cookies == null) - cookies = new CookieCollection(); - return cookies; - } - set { cookies = value; } // null allowed? - } - - public WebHeaderCollection Headers - { - get { return headers; } - set - { - /** - * "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, or - * WWW-Authenticate header using the Headers property, an exception will be - * thrown. Use the KeepAlive or ContentLength64 properties to set these headers. - * You cannot set the Transfer-Encoding or WWW-Authenticate headers manually." - */ - // TODO: check if this is marked readonly after headers are sent. - headers = value; - } - } - - public bool KeepAlive - { - get { return keep_alive; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - keep_alive = value; - } - } - - public Stream OutputStream - { - get - { - if (output_stream == null) - output_stream = context.Connection.GetResponseStream(); - return output_stream; - } - } - - public Version ProtocolVersion - { - get { return version; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (value == null) - throw new ArgumentNullException("value"); - - if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1)) - throw new ArgumentException("Must be 1.0 or 1.1", "value"); - - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - version = value; - } - } - - public string RedirectLocation - { - get { return location; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - location = value; - } - } - - public bool SendChunked - { - get { return chunked; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - chunked = value; - } - } - - public int StatusCode - { - get { return status_code; } - set - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (value < 100 || value > 999) - throw new ProtocolViolationException("StatusCode must be between 100 and 999."); - status_code = value; - status_description = GetStatusDescription(value); - } - } - - internal static string GetStatusDescription(int code) - { - switch (code) - { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-Uri Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "Http Version Not Supported"; - case 507: return "Insufficient Storage"; - } - return ""; - } - - public string StatusDescription - { - get { return status_description; } - set - { - status_description = value; - } - } - - void IDisposable.Dispose() - { - Close(true); //TODO: Abort or Close? - } - - public void Abort() - { - if (disposed) - return; - - Close(true); - } - - public void AddHeader(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - - if (name == "") - throw new ArgumentException("'name' cannot be empty", "name"); - - //TODO: check for forbidden headers and invalid characters - if (value.Length > 65535) - throw new ArgumentOutOfRangeException("value"); - - headers.Set(name, value); - } - - public void AppendCookie(Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException("cookie"); - - Cookies.Add(cookie); - } - - public void AppendHeader(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - - if (name == "") - throw new ArgumentException("'name' cannot be empty", "name"); - - if (value.Length > 65535) - throw new ArgumentOutOfRangeException("value"); - - headers.Add(name, value); - } - - private void Close(bool force) - { - if (force) - { - _logger.Debug("HttpListenerResponse force closing HttpConnection"); - } - disposed = true; - context.Connection.Close(force); - } - - public void Close() - { - if (disposed) - return; - - Close(false); - } - - public void Redirect(string url) - { - StatusCode = 302; // Found - location = url; - } - - bool FindCookie(Cookie cookie) - { - string name = cookie.Name; - string domain = cookie.Domain; - string path = cookie.Path; - foreach (Cookie c in cookies) - { - if (name != c.Name) - continue; - if (domain != c.Domain) - continue; - if (path == c.Path) - return true; - } - - return false; - } - - public void DetermineIfChunked() - { - if (chunked) - { - return; - } - - Version v = context.Request.ProtocolVersion; - if (!cl_set && !chunked && v >= HttpVersion.Version11) - chunked = true; - if (!chunked && string.Equals(headers["Transfer-Encoding"], "chunked")) - { - chunked = true; - } - } - - internal void SendHeaders(bool closing, MemoryStream ms) - { - Encoding encoding = content_encoding; - if (encoding == null) - encoding = _textEncoding.GetDefaultEncoding(); - - if (content_type != null) - { - if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) == -1) - { - string enc_name = content_encoding.WebName; - headers.SetInternal("Content-Type", content_type + "; charset=" + enc_name); - } - else - { - headers.SetInternal("Content-Type", content_type); - } - } - - if (headers["Server"] == null) - headers.SetInternal("Server", "Mono-HTTPAPI/1.0"); - - CultureInfo inv = CultureInfo.InvariantCulture; - if (headers["Date"] == null) - headers.SetInternal("Date", DateTime.UtcNow.ToString("r", inv)); - - if (!chunked) - { - if (!cl_set && closing) - { - cl_set = true; - content_length = 0; - } - - if (cl_set) - headers.SetInternal("Content-Length", content_length.ToString(inv)); - } - - Version v = context.Request.ProtocolVersion; - if (!cl_set && !chunked && v >= HttpVersion.Version11) - chunked = true; - - /* Apache forces closing the connection for these status codes: - * HttpStatusCode.BadRequest 400 - * HttpStatusCode.RequestTimeout 408 - * HttpStatusCode.LengthRequired 411 - * HttpStatusCode.RequestEntityTooLarge 413 - * HttpStatusCode.RequestUriTooLong 414 - * HttpStatusCode.InternalServerError 500 - * HttpStatusCode.ServiceUnavailable 503 - */ - bool conn_close = status_code == 400 || status_code == 408 || status_code == 411 || - status_code == 413 || status_code == 414 || - status_code == 500 || - status_code == 503; - - if (conn_close == false) - conn_close = !context.Request.KeepAlive; - - // They sent both KeepAlive: true and Connection: close!? - if (!keep_alive || conn_close) - { - headers.SetInternal("Connection", "close"); - conn_close = true; - } - - if (chunked) - headers.SetInternal("Transfer-Encoding", "chunked"); - - //int reuses = context.Connection.Reuses; - //if (reuses >= 100) - //{ - // _logger.Debug("HttpListenerResponse - keep alive has exceeded 100 uses and will be closed."); - - // force_close_chunked = true; - // if (!conn_close) - // { - // headers.SetInternal("Connection", "close"); - // conn_close = true; - // } - //} - - if (!conn_close) - { - if (context.Request.ProtocolVersion <= HttpVersion.Version10) - headers.SetInternal("Connection", "keep-alive"); - } - - if (location != null) - headers.SetInternal("Location", location); - - if (cookies != null) - { - foreach (Cookie cookie in cookies) - headers.SetInternal("Set-Cookie", cookie.ToString()); - } - - headers.SetInternal("Status", status_code.ToString(CultureInfo.InvariantCulture)); - - using (StreamWriter writer = new StreamWriter(ms, encoding, 256, true)) - { - writer.Write("HTTP/{0} {1} {2}\r\n", version, status_code, status_description); - string headers_str = headers.ToStringMultiValue(); - writer.Write(headers_str); - writer.Flush(); - } - - int preamble = encoding.GetPreamble().Length; - if (output_stream == null) - output_stream = context.Connection.GetResponseStream(); - - /* Assumes that the ms was at position 0 */ - ms.Position = preamble; - HeadersSent = true; - } - - public void SetCookie(Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException("cookie"); - - if (cookies != null) - { - if (FindCookie(cookie)) - throw new ArgumentException("The cookie already exists."); - } - else - { - cookies = new CookieCollection(); - } - - cookies.Add(cookie); - } - - public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) - { - return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); - } - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/HttpStatusCode.cs b/SocketHttpListener.Portable/Net/HttpStatusCode.cs deleted file mode 100644 index 93da82ba0..000000000 --- a/SocketHttpListener.Portable/Net/HttpStatusCode.cs +++ /dev/null @@ -1,321 +0,0 @@ -namespace SocketHttpListener.Net -{ - /// <summary> - /// Contains the values of the HTTP status codes. - /// </summary> - /// <remarks> - /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in - /// <see href="http://tools.ietf.org/html/rfc2616#section-10">RFC 2616</see> for HTTP 1.1. - /// </remarks> - public enum HttpStatusCode - { - /// <summary> - /// Equivalent to status code 100. - /// Indicates that the client should continue with its request. - /// </summary> - Continue = 100, - /// <summary> - /// Equivalent to status code 101. - /// Indicates that the server is switching the HTTP version or protocol on the connection. - /// </summary> - SwitchingProtocols = 101, - /// <summary> - /// Equivalent to status code 200. - /// Indicates that the client's request has succeeded. - /// </summary> - OK = 200, - /// <summary> - /// Equivalent to status code 201. - /// Indicates that the client's request has been fulfilled and resulted in a new resource being - /// created. - /// </summary> - Created = 201, - /// <summary> - /// Equivalent to status code 202. - /// Indicates that the client's request has been accepted for processing, but the processing - /// hasn't been completed. - /// </summary> - Accepted = 202, - /// <summary> - /// Equivalent to status code 203. - /// Indicates that the returned metainformation is from a local or a third-party copy instead of - /// the origin server. - /// </summary> - NonAuthoritativeInformation = 203, - /// <summary> - /// Equivalent to status code 204. - /// Indicates that the server has fulfilled the client's request but doesn't need to return - /// an entity-body. - /// </summary> - NoContent = 204, - /// <summary> - /// Equivalent to status code 205. - /// Indicates that the server has fulfilled the client's request, and the user agent should - /// reset the document view which caused the request to be sent. - /// </summary> - ResetContent = 205, - /// <summary> - /// Equivalent to status code 206. - /// Indicates that the server has fulfilled the partial GET request for the resource. - /// </summary> - PartialContent = 206, - /// <summary> - /// <para> - /// Equivalent to status code 300. - /// Indicates that the requested resource corresponds to any of multiple representations. - /// </para> - /// <para> - /// MultipleChoices is a synonym for Ambiguous. - /// </para> - /// </summary> - MultipleChoices = 300, - /// <summary> - /// <para> - /// Equivalent to status code 300. - /// Indicates that the requested resource corresponds to any of multiple representations. - /// </para> - /// <para> - /// Ambiguous is a synonym for MultipleChoices. - /// </para> - /// </summary> - Ambiguous = 300, - /// <summary> - /// <para> - /// Equivalent to status code 301. - /// Indicates that the requested resource has been assigned a new permanent URI and - /// any future references to this resource should use one of the returned URIs. - /// </para> - /// <para> - /// MovedPermanently is a synonym for Moved. - /// </para> - /// </summary> - MovedPermanently = 301, - /// <summary> - /// <para> - /// Equivalent to status code 301. - /// Indicates that the requested resource has been assigned a new permanent URI and - /// any future references to this resource should use one of the returned URIs. - /// </para> - /// <para> - /// Moved is a synonym for MovedPermanently. - /// </para> - /// </summary> - Moved = 301, - /// <summary> - /// <para> - /// Equivalent to status code 302. - /// Indicates that the requested resource is located temporarily under a different URI. - /// </para> - /// <para> - /// Found is a synonym for Redirect. - /// </para> - /// </summary> - Found = 302, - /// <summary> - /// <para> - /// Equivalent to status code 302. - /// Indicates that the requested resource is located temporarily under a different URI. - /// </para> - /// <para> - /// Redirect is a synonym for Found. - /// </para> - /// </summary> - Redirect = 302, - /// <summary> - /// <para> - /// Equivalent to status code 303. - /// Indicates that the response to the request can be found under a different URI and - /// should be retrieved using a GET method on that resource. - /// </para> - /// <para> - /// SeeOther is a synonym for RedirectMethod. - /// </para> - /// </summary> - SeeOther = 303, - /// <summary> - /// <para> - /// Equivalent to status code 303. - /// Indicates that the response to the request can be found under a different URI and - /// should be retrieved using a GET method on that resource. - /// </para> - /// <para> - /// RedirectMethod is a synonym for SeeOther. - /// </para> - /// </summary> - RedirectMethod = 303, - /// <summary> - /// Equivalent to status code 304. - /// Indicates that the client has performed a conditional GET request and access is allowed, - /// but the document hasn't been modified. - /// </summary> - NotModified = 304, - /// <summary> - /// Equivalent to status code 305. - /// Indicates that the requested resource must be accessed through the proxy given by - /// the Location field. - /// </summary> - UseProxy = 305, - /// <summary> - /// Equivalent to status code 306. - /// This status code was used in a previous version of the specification, is no longer used, - /// and is reserved for future use. - /// </summary> - Unused = 306, - /// <summary> - /// <para> - /// Equivalent to status code 307. - /// Indicates that the requested resource is located temporarily under a different URI. - /// </para> - /// <para> - /// TemporaryRedirect is a synonym for RedirectKeepVerb. - /// </para> - /// </summary> - TemporaryRedirect = 307, - /// <summary> - /// <para> - /// Equivalent to status code 307. - /// Indicates that the requested resource is located temporarily under a different URI. - /// </para> - /// <para> - /// RedirectKeepVerb is a synonym for TemporaryRedirect. - /// </para> - /// </summary> - RedirectKeepVerb = 307, - /// <summary> - /// Equivalent to status code 400. - /// Indicates that the client's request couldn't be understood by the server due to - /// malformed syntax. - /// </summary> - BadRequest = 400, - /// <summary> - /// Equivalent to status code 401. - /// Indicates that the client's request requires user authentication. - /// </summary> - Unauthorized = 401, - /// <summary> - /// Equivalent to status code 402. - /// This status code is reserved for future use. - /// </summary> - PaymentRequired = 402, - /// <summary> - /// Equivalent to status code 403. - /// Indicates that the server understood the client's request but is refusing to fulfill it. - /// </summary> - Forbidden = 403, - /// <summary> - /// Equivalent to status code 404. - /// Indicates that the server hasn't found anything matching the request URI. - /// </summary> - NotFound = 404, - /// <summary> - /// Equivalent to status code 405. - /// Indicates that the method specified in the request line isn't allowed for the resource - /// identified by the request URI. - /// </summary> - MethodNotAllowed = 405, - /// <summary> - /// Equivalent to status code 406. - /// Indicates that the server doesn't have the appropriate resource to respond to the Accept - /// headers in the client's request. - /// </summary> - NotAcceptable = 406, - /// <summary> - /// Equivalent to status code 407. - /// Indicates that the client must first authenticate itself with the proxy. - /// </summary> - ProxyAuthenticationRequired = 407, - /// <summary> - /// Equivalent to status code 408. - /// Indicates that the client didn't produce a request within the time that the server was - /// prepared to wait. - /// </summary> - RequestTimeout = 408, - /// <summary> - /// Equivalent to status code 409. - /// Indicates that the client's request couldn't be completed due to a conflict on the server. - /// </summary> - Conflict = 409, - /// <summary> - /// Equivalent to status code 410. - /// Indicates that the requested resource is no longer available at the server and - /// no forwarding address is known. - /// </summary> - Gone = 410, - /// <summary> - /// Equivalent to status code 411. - /// Indicates that the server refuses to accept the client's request without a defined - /// Content-Length. - /// </summary> - LengthRequired = 411, - /// <summary> - /// Equivalent to status code 412. - /// Indicates that the precondition given in one or more of the request headers evaluated to - /// false when it was tested on the server. - /// </summary> - PreconditionFailed = 412, - /// <summary> - /// Equivalent to status code 413. - /// Indicates that the entity of the client's request is larger than the server is willing or - /// able to process. - /// </summary> - RequestEntityTooLarge = 413, - /// <summary> - /// Equivalent to status code 414. - /// Indicates that the request URI is longer than the server is willing to interpret. - /// </summary> - RequestUriTooLong = 414, - /// <summary> - /// Equivalent to status code 415. - /// Indicates that the entity of the client's request is in a format not supported by - /// the requested resource for the requested method. - /// </summary> - UnsupportedMediaType = 415, - /// <summary> - /// Equivalent to status code 416. - /// Indicates that none of the range specifier values in a Range request header overlap - /// the current extent of the selected resource. - /// </summary> - RequestedRangeNotSatisfiable = 416, - /// <summary> - /// Equivalent to status code 417. - /// Indicates that the expectation given in an Expect request header couldn't be met by - /// the server. - /// </summary> - ExpectationFailed = 417, - /// <summary> - /// Equivalent to status code 500. - /// Indicates that the server encountered an unexpected condition which prevented it from - /// fulfilling the client's request. - /// </summary> - InternalServerError = 500, - /// <summary> - /// Equivalent to status code 501. - /// Indicates that the server doesn't support the functionality required to fulfill the client's - /// request. - /// </summary> - NotImplemented = 501, - /// <summary> - /// Equivalent to status code 502. - /// Indicates that a gateway or proxy server received an invalid response from the upstream - /// server. - /// </summary> - BadGateway = 502, - /// <summary> - /// Equivalent to status code 503. - /// Indicates that the server is currently unable to handle the client's request due to - /// a temporary overloading or maintenance of the server. - /// </summary> - ServiceUnavailable = 503, - /// <summary> - /// Equivalent to status code 504. - /// Indicates that a gateway or proxy server didn't receive a timely response from the upstream - /// server or some other auxiliary server. - /// </summary> - GatewayTimeout = 504, - /// <summary> - /// Equivalent to status code 505. - /// Indicates that the server doesn't support the HTTP version used in the client's request. - /// </summary> - HttpVersionNotSupported = 505, - } -} diff --git a/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs b/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs deleted file mode 100644 index 518c45acb..000000000 --- a/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Threading; - -namespace SocketHttpListener.Net -{ - class HttpStreamAsyncResult : IAsyncResult - { - object locker = new object(); - ManualResetEvent handle; - bool completed; - - internal byte[] Buffer; - internal int Offset; - internal int Count; - internal AsyncCallback Callback; - internal object State; - internal int SynchRead; - internal Exception Error; - - public void Complete(Exception e) - { - Error = e; - Complete(); - } - - public void Complete() - { - lock (locker) - { - if (completed) - return; - - completed = true; - if (handle != null) - handle.Set(); - - if (Callback != null) - Callback.BeginInvoke(this, null, null); - } - } - - public object AsyncState - { - get { return State; } - } - - public WaitHandle AsyncWaitHandle - { - get - { - lock (locker) - { - if (handle == null) - handle = new ManualResetEvent(completed); - } - - return handle; - } - } - - public bool CompletedSynchronously - { - get { return (SynchRead == Count); } - } - - public bool IsCompleted - { - get - { - lock (locker) - { - return completed; - } - } - } - } -} diff --git a/SocketHttpListener.Portable/Net/HttpVersion.cs b/SocketHttpListener.Portable/Net/HttpVersion.cs deleted file mode 100644 index c0839b46d..000000000 --- a/SocketHttpListener.Portable/Net/HttpVersion.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace SocketHttpListener.Net -{ - // <remarks> - // </remarks> - public class HttpVersion - { - - public static readonly Version Version10 = new Version(1, 0); - public static readonly Version Version11 = new Version(1, 1); - - // pretty useless.. - public HttpVersion() { } - } -} diff --git a/SocketHttpListener.Portable/Net/ListenerPrefix.cs b/SocketHttpListener.Portable/Net/ListenerPrefix.cs deleted file mode 100644 index 2c314da50..000000000 --- a/SocketHttpListener.Portable/Net/ListenerPrefix.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using System.Net; -using MediaBrowser.Model.Net; - -namespace SocketHttpListener.Net -{ - sealed class ListenerPrefix - { - string original; - string host; - ushort port; - string path; - bool secure; - IpAddressInfo[] addresses; - public HttpListener Listener; - - public ListenerPrefix(string prefix) - { - this.original = prefix; - Parse(prefix); - } - - public override string ToString() - { - return original; - } - - public IpAddressInfo[] Addresses - { - get { return addresses; } - set { addresses = value; } - } - public bool Secure - { - get { return secure; } - } - - public string Host - { - get { return host; } - } - - public int Port - { - get { return (int)port; } - } - - public string Path - { - get { return path; } - } - - // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection. - public override bool Equals(object o) - { - ListenerPrefix other = o as ListenerPrefix; - if (other == null) - return false; - - return (original == other.original); - } - - public override int GetHashCode() - { - return original.GetHashCode(); - } - - void Parse(string uri) - { - ushort default_port = 80; - if (uri.StartsWith("https://")) - { - default_port = 443; - secure = true; - } - - int length = uri.Length; - int start_host = uri.IndexOf(':') + 3; - if (start_host >= length) - throw new ArgumentException("No host specified."); - - int colon = uri.IndexOf(':', start_host, length - start_host); - int root; - if (colon > 0) - { - host = uri.Substring(start_host, colon - start_host); - root = uri.IndexOf('/', colon, length - colon); - port = (ushort)Int32.Parse(uri.Substring(colon + 1, root - colon - 1)); - path = uri.Substring(root); - } - else - { - root = uri.IndexOf('/', start_host, length - start_host); - host = uri.Substring(start_host, root - start_host); - port = default_port; - path = uri.Substring(root); - } - if (path.Length != 1) - path = path.Substring(0, path.Length - 1); - } - - public static void CheckUri(string uri) - { - if (uri == null) - throw new ArgumentNullException("uriPrefix"); - - if (!uri.StartsWith("http://") && !uri.StartsWith("https://")) - throw new ArgumentException("Only 'http' and 'https' schemes are supported."); - - int length = uri.Length; - int start_host = uri.IndexOf(':') + 3; - if (start_host >= length) - throw new ArgumentException("No host specified."); - - int colon = uri.IndexOf(':', start_host, length - start_host); - if (start_host == colon) - throw new ArgumentException("No host specified."); - - int root; - if (colon > 0) - { - root = uri.IndexOf('/', colon, length - colon); - if (root == -1) - throw new ArgumentException("No path specified."); - - try - { - int p = Int32.Parse(uri.Substring(colon + 1, root - colon - 1)); - if (p <= 0 || p >= 65536) - throw new Exception(); - } - catch - { - throw new ArgumentException("Invalid port."); - } - } - else - { - root = uri.IndexOf('/', start_host, length - start_host); - if (root == -1) - throw new ArgumentException("No path specified."); - } - - if (uri[uri.Length - 1] != '/') - throw new ArgumentException("The prefix must end with '/'"); - } - } -} diff --git a/SocketHttpListener.Portable/Net/RequestStream.cs b/SocketHttpListener.Portable/Net/RequestStream.cs deleted file mode 100644 index 58030500d..000000000 --- a/SocketHttpListener.Portable/Net/RequestStream.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - -namespace SocketHttpListener.Net -{ - class RequestStream : Stream - { - byte[] buffer; - int offset; - int length; - long remaining_body; - bool disposed; - Stream stream; - - internal RequestStream(Stream stream, byte[] buffer, int offset, int length) - : this(stream, buffer, offset, length, -1) - { - } - - internal RequestStream(Stream stream, byte[] buffer, int offset, int length, long contentlength) - { - this.stream = stream; - this.buffer = buffer; - this.offset = offset; - this.length = length; - this.remaining_body = contentlength; - } - - public override bool CanRead - { - get { return true; } - } - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanWrite - { - get { return false; } - } - - 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) - { - disposed = true; - } - - public override void Flush() - { - } - - - // Returns 0 if we can keep reading from the base stream, - // > 0 if we read something from the buffer. - // -1 if we had a content length set and we finished reading that many bytes. - int FillFromBuffer(byte[] buffer, int off, int count) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (off < 0) - throw new ArgumentOutOfRangeException("offset", "< 0"); - if (count < 0) - throw new ArgumentOutOfRangeException("count", "< 0"); - int len = buffer.Length; - if (off > len) - throw new ArgumentException("destination offset is beyond array size"); - if (off > len - count) - throw new ArgumentException("Reading would overrun buffer"); - - if (this.remaining_body == 0) - return -1; - - if (this.length == 0) - return 0; - - int size = Math.Min(this.length, count); - if (this.remaining_body > 0) - size = (int)Math.Min(size, this.remaining_body); - - if (this.offset > this.buffer.Length - size) - { - size = Math.Min(size, this.buffer.Length - this.offset); - } - if (size == 0) - return 0; - - Buffer.BlockCopy(this.buffer, this.offset, buffer, off, size); - this.offset += size; - this.length -= size; - if (this.remaining_body > 0) - remaining_body -= size; - return size; - } - - public override int Read([In, Out] byte[] buffer, int offset, int count) - { - if (disposed) - throw new ObjectDisposedException(typeof(RequestStream).ToString()); - - // Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0 - int nread = FillFromBuffer(buffer, offset, count); - if (nread == -1) - { // No more bytes available (Content-Length) - return 0; - } - else if (nread > 0) - { - return nread; - } - - nread = stream.Read(buffer, offset, count); - if (nread > 0 && remaining_body > 0) - remaining_body -= nread; - return nread; - } - - public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (disposed) - throw new ObjectDisposedException(typeof(RequestStream).ToString()); - - int nread = FillFromBuffer(buffer, offset, count); - if (nread > 0 || nread == -1) - { - return Math.Max(0, nread); - } - - // Avoid reading past the end of the request to allow - // for HTTP pipelining - if (remaining_body >= 0 && count > remaining_body) - count = (int)Math.Min(Int32.MaxValue, remaining_body); - - nread = await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - if (remaining_body > 0 && nread > 0) - remaining_body -= nread; - return nread; - } - - //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, - // AsyncCallback cback, object state) - //{ - // if (disposed) - // throw new ObjectDisposedException(typeof(RequestStream).ToString()); - - // int nread = FillFromBuffer(buffer, offset, count); - // if (nread > 0 || nread == -1) - // { - // HttpStreamAsyncResult ares = new HttpStreamAsyncResult(); - // ares.Buffer = buffer; - // ares.Offset = offset; - // ares.Count = count; - // ares.Callback = cback; - // ares.State = state; - // ares.SynchRead = Math.Max(0, nread); - // ares.Complete(); - // return ares; - // } - - // // Avoid reading past the end of the request to allow - // // for HTTP pipelining - // if (remaining_body >= 0 && count > remaining_body) - // count = (int)Math.Min(Int32.MaxValue, remaining_body); - // return stream.BeginRead(buffer, offset, count, cback, state); - //} - - //public override int EndRead(IAsyncResult ares) - //{ - // if (disposed) - // throw new ObjectDisposedException(typeof(RequestStream).ToString()); - - // if (ares == null) - // throw new ArgumentNullException("async_result"); - - // if (ares is HttpStreamAsyncResult) - // { - // HttpStreamAsyncResult r = (HttpStreamAsyncResult)ares; - // if (!ares.IsCompleted) - // ares.AsyncWaitHandle.WaitOne(); - // return r.SynchRead; - // } - - // // Close on exception? - // int nread = stream.EndRead(ares); - // if (remaining_body > 0 && nread > 0) - // remaining_body -= nread; - // return nread; - //} - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, - // AsyncCallback cback, object state) - //{ - // throw new NotSupportedException(); - //} - - //public override void EndWrite(IAsyncResult async_result) - //{ - // throw new NotSupportedException(); - //} - } -} diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs deleted file mode 100644 index 9552fe8ca..000000000 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ /dev/null @@ -1,453 +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; - byte[] bytes = null; - MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); - 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); - } - - internal static MemoryStream GetHeaders(HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, 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) - { - stream.Write(buffer, offset, count); - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (disposed) - throw new ObjectDisposedException(GetType().ToString()); - - if (count == 0) - { - //return; - } - - byte[] bytes = null; - MemoryStream 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) - { - 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()); - - if (count == 0) - { - //return; - } - - byte[] bytes = null; - MemoryStream ms = GetHeaders(response, _memoryStreamFactory, 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); - } - - if (count > 0) - { - await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); - } - - if (chunked) - stream.Write(crlf, 0, 2); - } - - //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(); - } - - public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) - { - //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked && response.ContentLength64 > 8192) - //{ - // 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) - { - MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); - - byte[] buffer; - if (ms != null) - { - using (var msCopy = new MemoryStream()) - { - ms.CopyTo(msCopy); - buffer = msCopy.ToArray(); - } - } - else - { - return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); - } - - _logger.Info("Socket sending file {0} {1}", path, response.ContentLength64); - return _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken); - } - - private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) - { - var allowAsync = _environment.OperatingSystem != 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, 81920, cancellationToken).ConfigureAwait(false); - } - else - { - fs.CopyTo(targetStream, 81920); - } - } - } - } - - private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) - { - var array = new byte[81920]; - int bytesRead; - - while ((bytesRead = source.Read(array, 0, array.Length)) != 0) - { - if (bytesRead == 0) - { - break; - } - - 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[81920]; - int bytesRead; - - while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) - { - if (bytesRead == 0) - { - break; - } - - 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.Portable/Net/WebHeaderCollection.cs b/SocketHttpListener.Portable/Net/WebHeaderCollection.cs deleted file mode 100644 index d20f99b9b..000000000 --- a/SocketHttpListener.Portable/Net/WebHeaderCollection.cs +++ /dev/null @@ -1,391 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Net; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Text; -using MediaBrowser.Model.Services; - -namespace SocketHttpListener.Net -{ - [ComVisible(true)] - public class WebHeaderCollection : QueryParamCollection - { - [Flags] - internal enum HeaderInfo - { - Request = 1, - Response = 1 << 1, - MultiValue = 1 << 10 - } - - static readonly bool[] allowed_chars = { - false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, true, false, true, true, true, true, false, false, false, true, - true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false, - false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - false, true, false - }; - - static readonly Dictionary<string, HeaderInfo> headers; - HeaderInfo? headerRestriction; - HeaderInfo? headerConsistency; - - static WebHeaderCollection() - { - headers = new Dictionary<string, HeaderInfo>(StringComparer.OrdinalIgnoreCase) { - { "Allow", HeaderInfo.MultiValue }, - { "Accept", HeaderInfo.Request | HeaderInfo.MultiValue }, - { "Accept-Charset", HeaderInfo.MultiValue }, - { "Accept-Encoding", HeaderInfo.MultiValue }, - { "Accept-Language", HeaderInfo.MultiValue }, - { "Accept-Ranges", HeaderInfo.MultiValue }, - { "Age", HeaderInfo.Response }, - { "Authorization", HeaderInfo.MultiValue }, - { "Cache-Control", HeaderInfo.MultiValue }, - { "Cookie", HeaderInfo.MultiValue }, - { "Connection", HeaderInfo.Request | HeaderInfo.MultiValue }, - { "Content-Encoding", HeaderInfo.MultiValue }, - { "Content-Length", HeaderInfo.Request | HeaderInfo.Response }, - { "Content-Type", HeaderInfo.Request }, - { "Content-Language", HeaderInfo.MultiValue }, - { "Date", HeaderInfo.Request }, - { "Expect", HeaderInfo.Request | HeaderInfo.MultiValue}, - { "Host", HeaderInfo.Request }, - { "If-Match", HeaderInfo.MultiValue }, - { "If-Modified-Since", HeaderInfo.Request }, - { "If-None-Match", HeaderInfo.MultiValue }, - { "Keep-Alive", HeaderInfo.Response }, - { "Pragma", HeaderInfo.MultiValue }, - { "Proxy-Authenticate", HeaderInfo.MultiValue }, - { "Proxy-Authorization", HeaderInfo.MultiValue }, - { "Proxy-Connection", HeaderInfo.Request | HeaderInfo.MultiValue }, - { "Range", HeaderInfo.Request | HeaderInfo.MultiValue }, - { "Referer", HeaderInfo.Request }, - { "Set-Cookie", HeaderInfo.MultiValue }, - { "Set-Cookie2", HeaderInfo.MultiValue }, - { "Server", HeaderInfo.Response }, - { "TE", HeaderInfo.MultiValue }, - { "Trailer", HeaderInfo.MultiValue }, - { "Transfer-Encoding", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo.MultiValue }, - { "Translate", HeaderInfo.Request | HeaderInfo.Response }, - { "Upgrade", HeaderInfo.MultiValue }, - { "User-Agent", HeaderInfo.Request }, - { "Vary", HeaderInfo.MultiValue }, - { "Via", HeaderInfo.MultiValue }, - { "Warning", HeaderInfo.MultiValue }, - { "WWW-Authenticate", HeaderInfo.Response | HeaderInfo. MultiValue }, - { "SecWebSocketAccept", HeaderInfo.Response }, - { "SecWebSocketExtensions", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue }, - { "SecWebSocketKey", HeaderInfo.Request }, - { "Sec-WebSocket-Protocol", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue }, - { "SecWebSocketVersion", HeaderInfo.Response | HeaderInfo. MultiValue } - }; - } - - // Methods - - public void Add(string header) - { - if (header == null) - throw new ArgumentNullException("header"); - int pos = header.IndexOf(':'); - if (pos == -1) - throw new ArgumentException("no colon found", "header"); - - this.Add(header.Substring(0, pos), header.Substring(pos + 1)); - } - - public override void Add(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - - ThrowIfRestricted(name); - this.AddWithoutValidate(name, value); - } - - protected void AddWithoutValidate(string headerName, string headerValue) - { - if (!IsHeaderName(headerName)) - throw new ArgumentException("invalid header name: " + headerName, "headerName"); - if (headerValue == null) - headerValue = String.Empty; - else - headerValue = headerValue.Trim(); - if (!IsHeaderValue(headerValue)) - throw new ArgumentException("invalid header value: " + headerValue, "headerValue"); - - AddValue(headerName, headerValue); - } - - internal void AddValue(string headerName, string headerValue) - { - base.Add(headerName, headerValue); - } - - internal string[] GetValues_internal(string header, bool split) - { - if (header == null) - throw new ArgumentNullException("header"); - - string[] values = base.GetValues(header); - if (values == null || values.Length == 0) - return null; - - if (split && IsMultiValue(header)) - { - List<string> separated = null; - foreach (var value in values) - { - if (value.IndexOf(',') < 0) - { - if (separated != null) - separated.Add(value); - - continue; - } - - if (separated == null) - { - separated = new List<string>(values.Length + 1); - foreach (var v in values) - { - if (v == value) - break; - - separated.Add(v); - } - } - - var slices = value.Split(','); - var slices_length = slices.Length; - if (value[value.Length - 1] == ',') - --slices_length; - - for (int i = 0; i < slices_length; ++i) - { - separated.Add(slices[i].Trim()); - } - } - - if (separated != null) - return separated.ToArray(); - } - - return values; - } - - public override string[] GetValues(string header) - { - return GetValues_internal(header, true); - } - - public override string[] GetValues(int index) - { - string[] values = base.GetValues(index); - - if (values == null || values.Length == 0) - { - return null; - } - - return values; - } - - public static bool IsRestricted(string headerName) - { - return IsRestricted(headerName, false); - } - - public static bool IsRestricted(string headerName, bool response) - { - if (headerName == null) - throw new ArgumentNullException("headerName"); - - if (headerName.Length == 0) - throw new ArgumentException("empty string", "headerName"); - - if (!IsHeaderName(headerName)) - throw new ArgumentException("Invalid character in header"); - - HeaderInfo info; - if (!headers.TryGetValue(headerName, out info)) - return false; - - var flag = response ? HeaderInfo.Response : HeaderInfo.Request; - return (info & flag) != 0; - } - - public override void Set(string name, string value) - { - if (name == null) - throw new ArgumentNullException("name"); - if (!IsHeaderName(name)) - throw new ArgumentException("invalid header name"); - if (value == null) - value = String.Empty; - else - value = value.Trim(); - if (!IsHeaderValue(value)) - throw new ArgumentException("invalid header value"); - - ThrowIfRestricted(name); - base.Set(name, value); - } - - internal string ToStringMultiValue() - { - StringBuilder sb = new StringBuilder(); - - int count = base.Count; - for (int i = 0; i < count; i++) - { - string key = GetKey(i); - if (IsMultiValue(key)) - { - foreach (string v in GetValues(i)) - { - sb.Append(key) - .Append(": ") - .Append(v) - .Append("\r\n"); - } - } - else - { - sb.Append(key) - .Append(": ") - .Append(Get(i)) - .Append("\r\n"); - } - } - return sb.Append("\r\n").ToString(); - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - - int count = base.Count; - for (int i = 0; i < count; i++) - sb.Append(GetKey(i)) - .Append(": ") - .Append(Get(i)) - .Append("\r\n"); - - return sb.Append("\r\n").ToString(); - } - - - // Internal Methods - - // With this we don't check for invalid characters in header. See bug #55994. - internal void SetInternal(string header) - { - int pos = header.IndexOf(':'); - if (pos == -1) - throw new ArgumentException("no colon found", "header"); - - SetInternal(header.Substring(0, pos), header.Substring(pos + 1)); - } - - internal void SetInternal(string name, string value) - { - if (value == null) - value = String.Empty; - else - value = value.Trim(); - if (!IsHeaderValue(value)) - throw new ArgumentException("invalid header value"); - - if (IsMultiValue(name)) - { - base.Add(name, value); - } - else - { - base.Remove(name); - base.Set(name, value); - } - } - - // Private Methods - - public override int Remove(string name) - { - ThrowIfRestricted(name); - return base.Remove(name); - } - - protected void ThrowIfRestricted(string headerName) - { - if (!headerRestriction.HasValue) - return; - - HeaderInfo info; - if (!headers.TryGetValue(headerName, out info)) - return; - - if ((info & headerRestriction.Value) != 0) - throw new ArgumentException("This header must be modified with the appropriate property."); - } - - internal static bool IsMultiValue(string headerName) - { - if (headerName == null) - return false; - - HeaderInfo info; - return headers.TryGetValue(headerName, out info) && (info & HeaderInfo.MultiValue) != 0; - } - - internal static bool IsHeaderValue(string value) - { - // TEXT any 8 bit value except CTL's (0-31 and 127) - // but including \r\n space and \t - // after a newline at least one space or \t must follow - // certain header fields allow comments () - - int len = value.Length; - for (int i = 0; i < len; i++) - { - char c = value[i]; - if (c == 127) - return false; - if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t')) - return false; - if (c == '\n' && ++i < len) - { - c = value[i]; - if (c != ' ' && c != '\t') - return false; - } - } - - return true; - } - - internal static bool IsHeaderName(string name) - { - if (name == null || name.Length == 0) - return false; - - int len = name.Length; - for (int i = 0; i < len; i++) - { - char c = name[i]; - if (c > 126 || !allowed_chars[c]) - return false; - } - - return true; - } - } -} diff --git a/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs b/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs deleted file mode 100644 index 034ac17d2..000000000 --- a/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Net; -using System.Security.Principal; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Services; -using SocketHttpListener.Primitives; - -namespace SocketHttpListener.Net.WebSockets -{ - /// <summary> - /// Provides the properties used to access the information in a WebSocket connection request - /// received by the <see cref="HttpListener"/>. - /// </summary> - /// <remarks> - /// </remarks> - public class HttpListenerWebSocketContext : WebSocketContext - { - #region Private Fields - - private HttpListenerContext _context; - private WebSocket _websocket; - - #endregion - - #region Internal Constructors - - internal HttpListenerWebSocketContext( - HttpListenerContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory) - { - _context = context; - _websocket = new WebSocket(this, protocol, cryptoProvider, memoryStreamFactory); - } - - #endregion - - #region Internal Properties - - internal Stream Stream - { - get - { - return _context.Connection.Stream; - } - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the HTTP cookies included in the request. - /// </summary> - /// <value> - /// A <see cref="System.Net.CookieCollection"/> that contains the cookies. - /// </value> - public override CookieCollection CookieCollection - { - get - { - return _context.Request.Cookies; - } - } - - /// <summary> - /// Gets the HTTP headers included in the request. - /// </summary> - /// <value> - /// A <see cref="QueryParamCollection"/> that contains the headers. - /// </value> - public override QueryParamCollection Headers - { - get - { - return _context.Request.Headers; - } - } - - /// <summary> - /// Gets the value of the Host header included in the request. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the value of the Host header. - /// </value> - public override string Host - { - get - { - return _context.Request.Headers["Host"]; - } - } - - /// <summary> - /// Gets a value indicating whether the client is authenticated. - /// </summary> - /// <value> - /// <c>true</c> if the client is authenticated; otherwise, <c>false</c>. - /// </value> - public override bool IsAuthenticated - { - get - { - return _context.Request.IsAuthenticated; - } - } - - /// <summary> - /// Gets a value indicating whether the client connected from the local computer. - /// </summary> - /// <value> - /// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>. - /// </value> - public override bool IsLocal - { - get - { - return _context.Request.IsLocal; - } - } - - /// <summary> - /// Gets a value indicating whether the WebSocket connection is secured. - /// </summary> - /// <value> - /// <c>true</c> if the connection is secured; otherwise, <c>false</c>. - /// </value> - public override bool IsSecureConnection - { - get - { - return _context.Connection.IsSecure; - } - } - - /// <summary> - /// Gets a value indicating whether the request is a WebSocket connection request. - /// </summary> - /// <value> - /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>. - /// </value> - public override bool IsWebSocketRequest - { - get - { - return _context.Request.IsWebSocketRequest; - } - } - - /// <summary> - /// Gets the value of the Origin header included in the request. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the value of the Origin header. - /// </value> - public override string Origin - { - get - { - return _context.Request.Headers["Origin"]; - } - } - - /// <summary> - /// Gets the query string included in the request. - /// </summary> - /// <value> - /// A <see cref="QueryParamCollection"/> that contains the query string parameters. - /// </value> - public override QueryParamCollection QueryString - { - get - { - return _context.Request.QueryString; - } - } - - /// <summary> - /// Gets the URI requested by the client. - /// </summary> - /// <value> - /// A <see cref="Uri"/> that represents the requested URI. - /// </value> - public override Uri RequestUri - { - get - { - return _context.Request.Url; - } - } - - /// <summary> - /// Gets the value of the Sec-WebSocket-Key header included in the request. - /// </summary> - /// <remarks> - /// This property provides a part of the information used by the server to prove that it - /// received a valid WebSocket connection request. - /// </remarks> - /// <value> - /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key header. - /// </value> - public override string SecWebSocketKey - { - get - { - return _context.Request.Headers["Sec-WebSocket-Key"]; - } - } - - /// <summary> - /// Gets the values of the Sec-WebSocket-Protocol header included in the request. - /// </summary> - /// <remarks> - /// This property represents the subprotocols requested by the client. - /// </remarks> - /// <value> - /// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/> instance that provides - /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol - /// header. - /// </value> - public override IEnumerable<string> SecWebSocketProtocols - { - get - { - var protocols = _context.Request.Headers["Sec-WebSocket-Protocol"]; - if (protocols != null) - foreach (var protocol in protocols.Split(',')) - yield return protocol.Trim(); - } - } - - /// <summary> - /// Gets the value of the Sec-WebSocket-Version header included in the request. - /// </summary> - /// <remarks> - /// This property represents the WebSocket protocol version. - /// </remarks> - /// <value> - /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Version header. - /// </value> - public override string SecWebSocketVersion - { - get - { - return _context.Request.Headers["Sec-WebSocket-Version"]; - } - } - - /// <summary> - /// Gets the server endpoint as an IP address and a port number. - /// </summary> - /// <value> - /// </value> - public override IpEndPointInfo ServerEndPoint - { - get - { - return _context.Connection.LocalEndPoint; - } - } - - /// <summary> - /// Gets the client information (identity, authentication, and security roles). - /// </summary> - /// <value> - /// A <see cref="IPrincipal"/> that represents the client information. - /// </value> - public override IPrincipal User - { - get - { - return _context.User; - } - } - - /// <summary> - /// Gets the client endpoint as an IP address and a port number. - /// </summary> - /// <value> - /// </value> - public override IpEndPointInfo UserEndPoint - { - get - { - return _context.Connection.RemoteEndPoint; - } - } - - /// <summary> - /// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication - /// between client and server. - /// </summary> - /// <value> - /// A <see cref="SocketHttpListener.WebSocket"/>. - /// </value> - public override WebSocket WebSocket - { - get - { - return _websocket; - } - } - - #endregion - - #region Internal Methods - - internal void Close() - { - try - { - _context.Connection.Close(true); - } - catch - { - // catch errors sending the closing handshake - } - } - - internal void Close(HttpStatusCode code) - { - _context.Response.StatusCode = (int)code; - _context.Response.OutputStream.Dispose(); - } - - #endregion - - #region Public Methods - - /// <summary> - /// Returns a <see cref="string"/> that represents the current - /// <see cref="HttpListenerWebSocketContext"/>. - /// </summary> - /// <returns> - /// A <see cref="string"/> that represents the current - /// <see cref="HttpListenerWebSocketContext"/>. - /// </returns> - public override string ToString() - { - return _context.Request.ToString(); - } - - #endregion - } -} diff --git a/SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs b/SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs deleted file mode 100644 index 3ffa6e639..000000000 --- a/SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Net; -using System.Security.Principal; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Services; - -namespace SocketHttpListener.Net.WebSockets -{ - /// <summary> - /// Exposes the properties used to access the information in a WebSocket connection request. - /// </summary> - /// <remarks> - /// The WebSocketContext class is an abstract class. - /// </remarks> - public abstract class WebSocketContext - { - #region Protected Constructors - - /// <summary> - /// Initializes a new instance of the <see cref="WebSocketContext"/> class. - /// </summary> - protected WebSocketContext() - { - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the HTTP cookies included in the request. - /// </summary> - /// <value> - /// A <see cref="System.Net.CookieCollection"/> that contains the cookies. - /// </value> - public abstract CookieCollection CookieCollection { get; } - - /// <summary> - /// Gets the HTTP headers included in the request. - /// </summary> - /// <value> - /// A <see cref="QueryParamCollection"/> that contains the headers. - /// </value> - public abstract QueryParamCollection Headers { get; } - - /// <summary> - /// Gets the value of the Host header included in the request. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the value of the Host header. - /// </value> - public abstract string Host { get; } - - /// <summary> - /// Gets a value indicating whether the client is authenticated. - /// </summary> - /// <value> - /// <c>true</c> if the client is authenticated; otherwise, <c>false</c>. - /// </value> - public abstract bool IsAuthenticated { get; } - - /// <summary> - /// Gets a value indicating whether the client connected from the local computer. - /// </summary> - /// <value> - /// <c>true</c> if the client connected from the local computer; otherwise, <c>false</c>. - /// </value> - public abstract bool IsLocal { get; } - - /// <summary> - /// Gets a value indicating whether the WebSocket connection is secured. - /// </summary> - /// <value> - /// <c>true</c> if the connection is secured; otherwise, <c>false</c>. - /// </value> - public abstract bool IsSecureConnection { get; } - - /// <summary> - /// Gets a value indicating whether the request is a WebSocket connection request. - /// </summary> - /// <value> - /// <c>true</c> if the request is a WebSocket connection request; otherwise, <c>false</c>. - /// </value> - public abstract bool IsWebSocketRequest { get; } - - /// <summary> - /// Gets the value of the Origin header included in the request. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the value of the Origin header. - /// </value> - public abstract string Origin { get; } - - /// <summary> - /// Gets the query string included in the request. - /// </summary> - /// <value> - /// A <see cref="QueryParamCollection"/> that contains the query string parameters. - /// </value> - public abstract QueryParamCollection QueryString { get; } - - /// <summary> - /// Gets the URI requested by the client. - /// </summary> - /// <value> - /// A <see cref="Uri"/> that represents the requested URI. - /// </value> - public abstract Uri RequestUri { get; } - - /// <summary> - /// Gets the value of the Sec-WebSocket-Key header included in the request. - /// </summary> - /// <remarks> - /// This property provides a part of the information used by the server to prove that it - /// received a valid WebSocket connection request. - /// </remarks> - /// <value> - /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Key header. - /// </value> - public abstract string SecWebSocketKey { get; } - - /// <summary> - /// Gets the values of the Sec-WebSocket-Protocol header included in the request. - /// </summary> - /// <remarks> - /// This property represents the subprotocols requested by the client. - /// </remarks> - /// <value> - /// An <see cref="T:System.Collections.Generic.IEnumerable{string}"/> instance that provides - /// an enumerator which supports the iteration over the values of the Sec-WebSocket-Protocol - /// header. - /// </value> - public abstract IEnumerable<string> SecWebSocketProtocols { get; } - - /// <summary> - /// Gets the value of the Sec-WebSocket-Version header included in the request. - /// </summary> - /// <remarks> - /// This property represents the WebSocket protocol version. - /// </remarks> - /// <value> - /// A <see cref="string"/> that represents the value of the Sec-WebSocket-Version header. - /// </value> - public abstract string SecWebSocketVersion { get; } - - /// <summary> - /// Gets the server endpoint as an IP address and a port number. - /// </summary> - /// <value> - /// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint. - /// </value> - public abstract IpEndPointInfo ServerEndPoint { get; } - - /// <summary> - /// Gets the client information (identity, authentication, and security roles). - /// </summary> - /// <value> - /// A <see cref="IPrincipal"/> that represents the client information. - /// </value> - public abstract IPrincipal User { get; } - - /// <summary> - /// Gets the client endpoint as an IP address and a port number. - /// </summary> - /// <value> - /// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint. - /// </value> - public abstract IpEndPointInfo UserEndPoint { get; } - - /// <summary> - /// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication - /// between client and server. - /// </summary> - /// <value> - /// A <see cref="SocketHttpListener.WebSocket"/>. - /// </value> - public abstract WebSocket WebSocket { get; } - - #endregion - } -} |
