diff options
| author | Luke <luke.pulverenti@gmail.com> | 2017-05-31 15:40:34 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-05-31 15:40:34 -0400 |
| commit | 91176d9ccc1dde8155c10411c70e62a9f4b059d5 (patch) | |
| tree | 21365f5a8dd09534a53d9f88d2a7a3116f3f3f98 /SocketHttpListener.Portable | |
| parent | c37c9a75073b1b9caa3af2c3bc62abd837bd630e (diff) | |
| parent | 4e10daf646e0788409f2bc52ef70effa2616e3f3 (diff) | |
Merge pull request #2677 from MediaBrowser/beta
Beta
Diffstat (limited to 'SocketHttpListener.Portable')
49 files changed, 0 insertions, 9521 deletions
diff --git a/SocketHttpListener.Portable/ByteOrder.cs b/SocketHttpListener.Portable/ByteOrder.cs deleted file mode 100644 index f5db52fd7..000000000 --- a/SocketHttpListener.Portable/ByteOrder.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SocketHttpListener -{ - /// <summary> - /// Contains the values that indicate whether the byte order is a Little-endian or Big-endian. - /// </summary> - public enum ByteOrder : byte - { - /// <summary> - /// Indicates a Little-endian. - /// </summary> - Little, - /// <summary> - /// Indicates a Big-endian. - /// </summary> - Big - } -} diff --git a/SocketHttpListener.Portable/CloseEventArgs.cs b/SocketHttpListener.Portable/CloseEventArgs.cs deleted file mode 100644 index b1bb4b196..000000000 --- a/SocketHttpListener.Portable/CloseEventArgs.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Text; - -namespace SocketHttpListener -{ - /// <summary> - /// Contains the event data associated with a <see cref="WebSocket.OnClose"/> event. - /// </summary> - /// <remarks> - /// A <see cref="WebSocket.OnClose"/> event occurs when the WebSocket connection has been closed. - /// If you would like to get the reason for the close, you should access the <see cref="Code"/> or - /// <see cref="Reason"/> property. - /// </remarks> - public class CloseEventArgs : EventArgs - { - #region Private Fields - - private bool _clean; - private ushort _code; - private string _reason; - - #endregion - - #region Internal Constructors - - internal CloseEventArgs (PayloadData payload) - { - var data = payload.ApplicationData; - var len = data.Length; - _code = len > 1 - ? data.SubArray (0, 2).ToUInt16 (ByteOrder.Big) - : (ushort) CloseStatusCode.NoStatusCode; - - _reason = len > 2 - ? GetUtf8String(data.SubArray (2, len - 2)) - : String.Empty; - } - - private string GetUtf8String(byte[] bytes) - { - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the status code for the close. - /// </summary> - /// <value> - /// A <see cref="ushort"/> that represents the status code for the close if any. - /// </value> - public ushort Code { - get { - return _code; - } - } - - /// <summary> - /// Gets the reason for the close. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the reason for the close if any. - /// </value> - public string Reason { - get { - return _reason; - } - } - - /// <summary> - /// Gets a value indicating whether the WebSocket connection has been closed cleanly. - /// </summary> - /// <value> - /// <c>true</c> if the WebSocket connection has been closed cleanly; otherwise, <c>false</c>. - /// </value> - public bool WasClean { - get { - return _clean; - } - - internal set { - _clean = value; - } - } - - #endregion - } -} diff --git a/SocketHttpListener.Portable/CloseStatusCode.cs b/SocketHttpListener.Portable/CloseStatusCode.cs deleted file mode 100644 index 62a268bce..000000000 --- a/SocketHttpListener.Portable/CloseStatusCode.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace SocketHttpListener -{ - /// <summary> - /// Contains the values of the status code for the WebSocket connection close. - /// </summary> - /// <remarks> - /// <para> - /// The values of the status code are defined in - /// <see href="http://tools.ietf.org/html/rfc6455#section-7.4">Section 7.4</see> - /// of RFC 6455. - /// </para> - /// <para> - /// "Reserved value" must not be set as a status code in a close control frame - /// by an endpoint. It's designated for use in applications expecting a status - /// code to indicate that the connection was closed due to the system grounds. - /// </para> - /// </remarks> - public enum CloseStatusCode : ushort - { - /// <summary> - /// Equivalent to close status 1000. - /// Indicates a normal close. - /// </summary> - Normal = 1000, - /// <summary> - /// Equivalent to close status 1001. - /// Indicates that an endpoint is going away. - /// </summary> - Away = 1001, - /// <summary> - /// Equivalent to close status 1002. - /// Indicates that an endpoint is terminating the connection due to a protocol error. - /// </summary> - ProtocolError = 1002, - /// <summary> - /// Equivalent to close status 1003. - /// Indicates that an endpoint is terminating the connection because it has received - /// an unacceptable type message. - /// </summary> - IncorrectData = 1003, - /// <summary> - /// Equivalent to close status 1004. - /// Still undefined. Reserved value. - /// </summary> - Undefined = 1004, - /// <summary> - /// Equivalent to close status 1005. - /// Indicates that no status code was actually present. Reserved value. - /// </summary> - NoStatusCode = 1005, - /// <summary> - /// Equivalent to close status 1006. - /// Indicates that the connection was closed abnormally. Reserved value. - /// </summary> - Abnormal = 1006, - /// <summary> - /// Equivalent to close status 1007. - /// Indicates that an endpoint is terminating the connection because it has received - /// a message that contains a data that isn't consistent with the type of the message. - /// </summary> - InconsistentData = 1007, - /// <summary> - /// Equivalent to close status 1008. - /// Indicates that an endpoint is terminating the connection because it has received - /// a message that violates its policy. - /// </summary> - PolicyViolation = 1008, - /// <summary> - /// Equivalent to close status 1009. - /// Indicates that an endpoint is terminating the connection because it has received - /// a message that is too big to process. - /// </summary> - TooBig = 1009, - /// <summary> - /// Equivalent to close status 1010. - /// Indicates that the client is terminating the connection because it has expected - /// the server to negotiate one or more extension, but the server didn't return them - /// in the handshake response. - /// </summary> - IgnoreExtension = 1010, - /// <summary> - /// Equivalent to close status 1011. - /// Indicates that the server is terminating the connection because it has encountered - /// an unexpected condition that prevented it from fulfilling the request. - /// </summary> - ServerError = 1011, - /// <summary> - /// Equivalent to close status 1015. - /// Indicates that the connection was closed due to a failure to perform a TLS handshake. - /// Reserved value. - /// </summary> - TlsHandshakeFailure = 1015 - } -} diff --git a/SocketHttpListener.Portable/CompressionMethod.cs b/SocketHttpListener.Portable/CompressionMethod.cs deleted file mode 100644 index 36a48d94c..000000000 --- a/SocketHttpListener.Portable/CompressionMethod.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace SocketHttpListener -{ - /// <summary> - /// Contains the values of the compression method used to compress the message on the WebSocket - /// connection. - /// </summary> - /// <remarks> - /// The values of the compression method are defined in - /// <see href="http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-09">Compression - /// Extensions for WebSocket</see>. - /// </remarks> - public enum CompressionMethod : byte - { - /// <summary> - /// Indicates non compression. - /// </summary> - None, - /// <summary> - /// Indicates using DEFLATE. - /// </summary> - Deflate - } -} diff --git a/SocketHttpListener.Portable/ErrorEventArgs.cs b/SocketHttpListener.Portable/ErrorEventArgs.cs deleted file mode 100644 index bf1d6fc95..000000000 --- a/SocketHttpListener.Portable/ErrorEventArgs.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -namespace SocketHttpListener -{ - /// <summary> - /// Contains the event data associated with a <see cref="WebSocket.OnError"/> event. - /// </summary> - /// <remarks> - /// A <see cref="WebSocket.OnError"/> event occurs when the <see cref="WebSocket"/> gets an error. - /// If you would like to get the error message, you should access the <see cref="Message"/> - /// property. - /// </remarks> - public class ErrorEventArgs : EventArgs - { - #region Private Fields - - private string _message; - - #endregion - - #region Internal Constructors - - internal ErrorEventArgs (string message) - { - _message = message; - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the error message. - /// </summary> - /// <value> - /// A <see cref="string"/> that represents the error message. - /// </value> - public string Message { - get { - return _message; - } - } - - #endregion - } -} diff --git a/SocketHttpListener.Portable/Ext.cs b/SocketHttpListener.Portable/Ext.cs deleted file mode 100644 index 87f0887ed..000000000 --- a/SocketHttpListener.Portable/Ext.cs +++ /dev/null @@ -1,1083 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.IO.Compression; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; -using SocketHttpListener.Net; -using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; -using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode; - -namespace SocketHttpListener -{ - /// <summary> - /// Provides a set of static methods for the websocket-sharp. - /// </summary> - public static class Ext - { - #region Private Const Fields - - private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; - - #endregion - - #region Private Methods - - private static MemoryStream compress(this Stream stream) - { - var output = new MemoryStream(); - if (stream.Length == 0) - return output; - - stream.Position = 0; - using (var ds = new DeflateStream(output, CompressionMode.Compress, true)) - { - stream.CopyTo(ds); - //ds.Close(); // "BFINAL" set to 1. - output.Position = 0; - - return output; - } - } - - private static byte[] decompress(this byte[] value) - { - if (value.Length == 0) - return value; - - using (var input = new MemoryStream(value)) - { - return input.decompressToArray(); - } - } - - private static MemoryStream decompress(this Stream stream) - { - var output = new MemoryStream(); - if (stream.Length == 0) - return output; - - stream.Position = 0; - using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true)) - { - ds.CopyTo(output, true); - return output; - } - } - - private static byte[] decompressToArray(this Stream stream) - { - using (var decomp = stream.decompress()) - { - return decomp.ToArray(); - } - } - - private static byte[] readBytes(this Stream stream, byte[] buffer, int offset, int length) - { - var len = stream.Read(buffer, offset, length); - if (len < 1) - return buffer.SubArray(0, offset); - - var tmp = 0; - while (len < length) - { - tmp = stream.Read(buffer, offset + len, length - len); - if (tmp < 1) - break; - - len += tmp; - } - - return len < length - ? buffer.SubArray(0, offset + len) - : buffer; - } - - private static bool readBytes( - this Stream stream, byte[] buffer, int offset, int length, Stream dest) - { - var bytes = stream.readBytes(buffer, offset, length); - var len = bytes.Length; - dest.Write(bytes, 0, len); - - return len == offset + length; - } - - #endregion - - #region Internal Methods - - internal static byte[] Append(this ushort code, string reason) - { - using (var buffer = new MemoryStream()) - { - var tmp = code.ToByteArrayInternally(ByteOrder.Big); - buffer.Write(tmp, 0, 2); - if (reason != null && reason.Length > 0) - { - tmp = Encoding.UTF8.GetBytes(reason); - buffer.Write(tmp, 0, tmp.Length); - } - - return buffer.ToArray(); - } - } - - internal static string CheckIfClosable(this WebSocketState state) - { - return state == WebSocketState.Closing - ? "While closing the WebSocket connection." - : state == WebSocketState.Closed - ? "The WebSocket connection has already been closed." - : null; - } - - internal static string CheckIfOpen(this WebSocketState state) - { - return state == WebSocketState.Connecting - ? "A WebSocket connection isn't established." - : state == WebSocketState.Closing - ? "While closing the WebSocket connection." - : state == WebSocketState.Closed - ? "The WebSocket connection has already been closed." - : null; - } - - internal static string CheckIfValidControlData(this byte[] data, string paramName) - { - return data.Length > 125 - ? String.Format("'{0}' length must be less.", paramName) - : null; - } - - internal static string CheckIfValidSendData(this byte[] data) - { - return data == null - ? "'data' must not be null." - : null; - } - - internal static string CheckIfValidSendData(this string data) - { - return data == null - ? "'data' must not be null." - : null; - } - - internal static Stream Compress(this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.compress() - : stream; - } - - internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition) - { - foreach (T elm in source) - if (condition(elm)) - return true; - - return false; - } - - internal static void CopyTo(this Stream src, Stream dest, bool setDefaultPosition) - { - var readLen = 0; - var bufferLen = 256; - var buffer = new byte[bufferLen]; - while ((readLen = src.Read(buffer, 0, bufferLen)) > 0) - { - dest.Write(buffer, 0, readLen); - } - - if (setDefaultPosition) - dest.Position = 0; - } - - internal static byte[] Decompress(this byte[] value, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? value.decompress() - : value; - } - - internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method) - { - return method == CompressionMethod.Deflate - ? stream.decompressToArray() - : stream.ToByteArray(); - } - - /// <summary> - /// Determines whether the specified <see cref="int"/> equals the specified <see cref="char"/>, - /// and invokes the specified Action<int> delegate at the same time. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="value"/> equals <paramref name="c"/>; - /// otherwise, <c>false</c>. - /// </returns> - /// <param name="value"> - /// An <see cref="int"/> to compare. - /// </param> - /// <param name="c"> - /// A <see cref="char"/> to compare. - /// </param> - /// <param name="action"> - /// An Action<int> delegate that references the method(s) called at - /// the same time as comparing. An <see cref="int"/> parameter to pass to - /// the method(s) is <paramref name="value"/>. - /// </param> - /// <exception cref="ArgumentOutOfRangeException"> - /// <paramref name="value"/> isn't between 0 and 255. - /// </exception> - internal static bool EqualsWith(this int value, char c, Action<int> action) - { - if (value < 0 || value > 255) - throw new ArgumentOutOfRangeException("value"); - - action(value); - return value == c - 0; - } - - internal static string GetMessage(this CloseStatusCode code) - { - return code == CloseStatusCode.ProtocolError - ? "A WebSocket protocol error has occurred." - : code == CloseStatusCode.IncorrectData - ? "An incorrect data has been received." - : code == CloseStatusCode.Abnormal - ? "An exception has occurred." - : code == CloseStatusCode.InconsistentData - ? "An inconsistent data has been received." - : code == CloseStatusCode.PolicyViolation - ? "A policy violation has occurred." - : code == CloseStatusCode.TooBig - ? "A too big data has been received." - : code == CloseStatusCode.IgnoreExtension - ? "WebSocket client did not receive expected extension(s)." - : code == CloseStatusCode.ServerError - ? "WebSocket server got an internal error." - : code == CloseStatusCode.TlsHandshakeFailure - ? "An error has occurred while handshaking." - : String.Empty; - } - - internal static string GetNameInternal(this string nameAndValue, string separator) - { - var i = nameAndValue.IndexOf(separator); - return i > 0 - ? nameAndValue.Substring(0, i).Trim() - : null; - } - - internal static string GetValueInternal(this string nameAndValue, string separator) - { - var i = nameAndValue.IndexOf(separator); - return i >= 0 && i < nameAndValue.Length - 1 - ? nameAndValue.Substring(i + 1).Trim() - : null; - } - - internal static bool IsCompressionExtension(this string value, CompressionMethod method) - { - return value.StartsWith(method.ToExtensionString()); - } - - internal static bool IsPortNumber(this int value) - { - return value > 0 && value < 65536; - } - - internal static bool IsReserved(this ushort code) - { - return code == (ushort)CloseStatusCode.Undefined || - code == (ushort)CloseStatusCode.NoStatusCode || - code == (ushort)CloseStatusCode.Abnormal || - code == (ushort)CloseStatusCode.TlsHandshakeFailure; - } - - internal static bool IsReserved(this CloseStatusCode code) - { - return code == CloseStatusCode.Undefined || - code == CloseStatusCode.NoStatusCode || - code == CloseStatusCode.Abnormal || - code == CloseStatusCode.TlsHandshakeFailure; - } - - internal static bool IsText(this string value) - { - var len = value.Length; - for (var i = 0; i < len; i++) - { - char c = value[i]; - if (c < 0x20 && !"\r\n\t".Contains(c)) - return false; - - if (c == 0x7f) - return false; - - if (c == '\n' && ++i < len) - { - c = value[i]; - if (!" \t".Contains(c)) - return false; - } - } - - return true; - } - - internal static bool IsToken(this string value) - { - foreach (char c in value) - if (c < 0x20 || c >= 0x7f || _tspecials.Contains(c)) - return false; - - return true; - } - - internal static string Quote(this string value) - { - return value.IsToken() - ? value - : String.Format("\"{0}\"", value.Replace("\"", "\\\"")); - } - - internal static byte[] ReadBytes(this Stream stream, int length) - { - return stream.readBytes(new byte[length], 0, length); - } - - internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength) - { - using (var result = new MemoryStream()) - { - var count = length / bufferLength; - var rem = (int)(length % bufferLength); - - var buffer = new byte[bufferLength]; - var end = false; - for (long i = 0; i < count; i++) - { - if (!stream.readBytes(buffer, 0, bufferLength, result)) - { - end = true; - break; - } - } - - if (!end && rem > 0) - stream.readBytes(new byte[rem], 0, rem, result); - - return result.ToArray(); - } - } - - internal static async Task<byte[]> ReadBytesAsync(this Stream stream, int length) - { - var buffer = new byte[length]; - - var len = await stream.ReadAsync(buffer, 0, length).ConfigureAwait(false); - var bytes = len < 1 - ? new byte[0] - : len < length - ? stream.readBytes(buffer, len, length - len) - : buffer; - - return bytes; - } - - internal static string RemovePrefix(this string value, params string[] prefixes) - { - var i = 0; - foreach (var prefix in prefixes) - { - if (value.StartsWith(prefix)) - { - i = prefix.Length; - break; - } - } - - return i > 0 - ? value.Substring(i) - : value; - } - - internal static T[] Reverse<T>(this T[] array) - { - var len = array.Length; - T[] reverse = new T[len]; - - var end = len - 1; - for (var i = 0; i <= end; i++) - reverse[i] = array[end - i]; - - return reverse; - } - - internal static IEnumerable<string> SplitHeaderValue( - this string value, params char[] separator) - { - var len = value.Length; - var separators = new string(separator); - - var buffer = new StringBuilder(32); - var quoted = false; - var escaped = false; - - char c; - for (var i = 0; i < len; i++) - { - c = value[i]; - if (c == '"') - { - if (escaped) - escaped = !escaped; - else - quoted = !quoted; - } - else if (c == '\\') - { - if (i < len - 1 && value[i + 1] == '"') - escaped = true; - } - else if (separators.Contains(c)) - { - if (!quoted) - { - yield return buffer.ToString(); - buffer.Length = 0; - - continue; - } - } - else { - } - - buffer.Append(c); - } - - if (buffer.Length > 0) - yield return buffer.ToString(); - } - - internal static byte[] ToByteArray(this Stream stream) - { - using (var output = new MemoryStream()) - { - stream.Position = 0; - stream.CopyTo(output); - - return output.ToArray(); - } - } - - internal static byte[] ToByteArrayInternally(this ushort value, ByteOrder order) - { - var bytes = BitConverter.GetBytes(value); - if (!order.IsHostOrder()) - Array.Reverse(bytes); - - return bytes; - } - - internal static byte[] ToByteArrayInternally(this ulong value, ByteOrder order) - { - var bytes = BitConverter.GetBytes(value); - if (!order.IsHostOrder()) - Array.Reverse(bytes); - - return bytes; - } - - internal static string ToExtensionString( - this CompressionMethod method, params string[] parameters) - { - if (method == CompressionMethod.None) - return String.Empty; - - var m = String.Format("permessage-{0}", method.ToString().ToLower()); - if (parameters == null || parameters.Length == 0) - return m; - - return String.Format("{0}; {1}", m, parameters.ToString("; ")); - } - - internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) - { - return new List<TSource>(source); - } - - internal static ushort ToUInt16(this byte[] src, ByteOrder srcOrder) - { - return BitConverter.ToUInt16(src.ToHostOrder(srcOrder), 0); - } - - internal static ulong ToUInt64(this byte[] src, ByteOrder srcOrder) - { - return BitConverter.ToUInt64(src.ToHostOrder(srcOrder), 0); - } - - internal static string TrimEndSlash(this string value) - { - value = value.TrimEnd('/'); - return value.Length > 0 - ? value - : "/"; - } - - internal static string Unquote(this string value) - { - var start = value.IndexOf('\"'); - var end = value.LastIndexOf('\"'); - if (start < end) - value = value.Substring(start + 1, end - start - 1).Replace("\\\"", "\""); - - return value.Trim(); - } - - internal static void WriteBytes(this Stream stream, byte[] value) - { - using (var src = new MemoryStream(value)) - { - src.CopyTo(stream); - } - } - - #endregion - - #region Public Methods - - /// <summary> - /// Determines whether the specified <see cref="string"/> contains any of characters - /// in the specified array of <see cref="char"/>. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>; - /// otherwise, <c>false</c>. - /// </returns> - /// <param name="value"> - /// A <see cref="string"/> to test. - /// </param> - /// <param name="chars"> - /// An array of <see cref="char"/> that contains characters to find. - /// </param> - public static bool Contains(this string value, params char[] chars) - { - return chars == null || chars.Length == 0 - ? true - : value == null || value.Length == 0 - ? false - : value.IndexOfAny(chars) != -1; - } - - /// <summary> - /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry - /// with the specified <paramref name="name"/>. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="collection"/> contains the entry - /// with <paramref name="name"/>; otherwise, <c>false</c>. - /// </returns> - /// <param name="collection"> - /// A <see cref="QueryParamCollection"/> to test. - /// </param> - /// <param name="name"> - /// A <see cref="string"/> that represents the key of the entry to find. - /// </param> - public static bool Contains(this QueryParamCollection collection, string name) - { - return collection == null || collection.Count == 0 - ? false - : collection[name] != null; - } - - /// <summary> - /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry - /// with the specified both <paramref name="name"/> and <paramref name="value"/>. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="collection"/> contains the entry - /// with both <paramref name="name"/> and <paramref name="value"/>; - /// otherwise, <c>false</c>. - /// </returns> - /// <param name="collection"> - /// A <see cref="QueryParamCollection"/> to test. - /// </param> - /// <param name="name"> - /// A <see cref="string"/> that represents the key of the entry to find. - /// </param> - /// <param name="value"> - /// A <see cref="string"/> that represents the value of the entry to find. - /// </param> - public static bool Contains(this QueryParamCollection collection, string name, string value) - { - if (collection == null || collection.Count == 0) - return false; - - var values = collection[name]; - if (values == null) - return false; - - foreach (var v in values.Split(',')) - if (v.Trim().Equals(value, StringComparison.OrdinalIgnoreCase)) - return true; - - return false; - } - - /// <summary> - /// Emits the specified <see cref="EventHandler"/> delegate if it isn't <see langword="null"/>. - /// </summary> - /// <param name="eventHandler"> - /// A <see cref="EventHandler"/> to emit. - /// </param> - /// <param name="sender"> - /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>. - /// </param> - /// <param name="e"> - /// A <see cref="EventArgs"/> that contains no event data. - /// </param> - public static void Emit(this EventHandler eventHandler, object sender, EventArgs e) - { - if (eventHandler != null) - eventHandler(sender, e); - } - - /// <summary> - /// Emits the specified <c>EventHandler<TEventArgs></c> delegate - /// if it isn't <see langword="null"/>. - /// </summary> - /// <param name="eventHandler"> - /// An <c>EventHandler<TEventArgs></c> to emit. - /// </param> - /// <param name="sender"> - /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>. - /// </param> - /// <param name="e"> - /// A <c>TEventArgs</c> that represents the event data. - /// </param> - /// <typeparam name="TEventArgs"> - /// The type of the event data generated by the event. - /// </typeparam> - public static void Emit<TEventArgs>( - this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e) - where TEventArgs : EventArgs - { - if (eventHandler != null) - eventHandler(sender, e); - } - - /// <summary> - /// Gets the collection of the HTTP cookies from the specified HTTP <paramref name="headers"/>. - /// </summary> - /// <returns> - /// A <see cref="CookieCollection"/> that receives a collection of the HTTP cookies. - /// </returns> - /// <param name="headers"> - /// A <see cref="QueryParamCollection"/> that contains a collection of the HTTP headers. - /// </param> - /// <param name="response"> - /// <c>true</c> if <paramref name="headers"/> is a collection of the response headers; - /// otherwise, <c>false</c>. - /// </param> - public static CookieCollection GetCookies(this QueryParamCollection headers, bool response) - { - var name = response ? "Set-Cookie" : "Cookie"; - return headers == null || !headers.Contains(name) - ? new CookieCollection() - : CookieHelper.Parse(headers[name], response); - } - - /// <summary> - /// Gets the description of the specified HTTP status <paramref name="code"/>. - /// </summary> - /// <returns> - /// A <see cref="string"/> that represents the description of the HTTP status code. - /// </returns> - /// <param name="code"> - /// One of <see cref="HttpStatusCode"/> enum values, indicates the HTTP status codes. - /// </param> - public static string GetDescription(this HttpStatusCode code) - { - return ((int)code).GetStatusDescription(); - } - - /// <summary> - /// Gets the name from the specified <see cref="string"/> that contains a pair of name and - /// value separated by a separator string. - /// </summary> - /// <returns> - /// A <see cref="string"/> that represents the name if any; otherwise, <c>null</c>. - /// </returns> - /// <param name="nameAndValue"> - /// A <see cref="string"/> that contains a pair of name and value separated by a separator - /// string. - /// </param> - /// <param name="separator"> - /// A <see cref="string"/> that represents a separator string. - /// </param> - public static string GetName(this string nameAndValue, string separator) - { - return (nameAndValue != null && nameAndValue.Length > 0) && - (separator != null && separator.Length > 0) - ? nameAndValue.GetNameInternal(separator) - : null; - } - - /// <summary> - /// Gets the name and value from the specified <see cref="string"/> that contains a pair of - /// name and value separated by a separator string. - /// </summary> - /// <returns> - /// A <c>KeyValuePair<string, string></c> that represents the name and value if any. - /// </returns> - /// <param name="nameAndValue"> - /// A <see cref="string"/> that contains a pair of name and value separated by a separator - /// string. - /// </param> - /// <param name="separator"> - /// A <see cref="string"/> that represents a separator string. - /// </param> - public static KeyValuePair<string, string> GetNameAndValue( - this string nameAndValue, string separator) - { - var name = nameAndValue.GetName(separator); - var value = nameAndValue.GetValue(separator); - return name != null - ? new KeyValuePair<string, string>(name, value) - : new KeyValuePair<string, string>(null, null); - } - - /// <summary> - /// Gets the description of the specified HTTP status <paramref name="code"/>. - /// </summary> - /// <returns> - /// A <see cref="string"/> that represents the description of the HTTP status code. - /// </returns> - /// <param name="code"> - /// An <see cref="int"/> that represents the HTTP status code. - /// </param> - public static string GetStatusDescription(this 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 String.Empty; - } - - /// <summary> - /// Gets the value from the specified <see cref="string"/> that contains a pair of name and - /// value separated by a separator string. - /// </summary> - /// <returns> - /// A <see cref="string"/> that represents the value if any; otherwise, <c>null</c>. - /// </returns> - /// <param name="nameAndValue"> - /// A <see cref="string"/> that contains a pair of name and value separated by a separator - /// string. - /// </param> - /// <param name="separator"> - /// A <see cref="string"/> that represents a separator string. - /// </param> - public static string GetValue(this string nameAndValue, string separator) - { - return (nameAndValue != null && nameAndValue.Length > 0) && - (separator != null && separator.Length > 0) - ? nameAndValue.GetValueInternal(separator) - : null; - } - - /// <summary> - /// Determines whether the specified <see cref="ByteOrder"/> is host - /// (this computer architecture) byte order. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="order"/> is host byte order; - /// otherwise, <c>false</c>. - /// </returns> - /// <param name="order"> - /// One of the <see cref="ByteOrder"/> enum values, to test. - /// </param> - public static bool IsHostOrder(this ByteOrder order) - { - // true : !(true ^ true) or !(false ^ false) - // false: !(true ^ false) or !(false ^ true) - return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little)); - } - - /// <summary> - /// Determines whether the specified <see cref="string"/> is a predefined scheme. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="value"/> is a predefined scheme; otherwise, <c>false</c>. - /// </returns> - /// <param name="value"> - /// A <see cref="string"/> to test. - /// </param> - public static bool IsPredefinedScheme(this string value) - { - if (value == null || value.Length < 2) - return false; - - var c = value[0]; - if (c == 'h') - return value == "http" || value == "https"; - - if (c == 'w') - return value == "ws" || value == "wss"; - - if (c == 'f') - return value == "file" || value == "ftp"; - - if (c == 'n') - { - c = value[1]; - return c == 'e' - ? value == "news" || value == "net.pipe" || value == "net.tcp" - : value == "nntp"; - } - - return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto"); - } - - /// <summary> - /// Determines whether the specified <see cref="string"/> is a URI string. - /// </summary> - /// <returns> - /// <c>true</c> if <paramref name="value"/> may be a URI string; otherwise, <c>false</c>. - /// </returns> - /// <param name="value"> - /// A <see cref="string"/> to test. - /// </param> - public static bool MaybeUri(this string value) - { - if (value == null || value.Length == 0) - return false; - - var i = value.IndexOf(':'); - if (i == -1) - return false; - - if (i >= 10) - return false; - - return value.Substring(0, i).IsPredefinedScheme(); - } - - /// <summary> - /// Retrieves a sub-array from the specified <paramref name="array"/>. - /// A sub-array starts at the specified element position. - /// </summary> - /// <returns> - /// An array of T that receives a sub-array, or an empty array of T if any problems - /// with the parameters. - /// </returns> - /// <param name="array"> - /// An array of T that contains the data to retrieve a sub-array. - /// </param> - /// <param name="startIndex"> - /// An <see cref="int"/> that contains the zero-based starting position of a sub-array - /// in <paramref name="array"/>. - /// </param> - /// <param name="length"> - /// An <see cref="int"/> that contains the number of elements to retrieve a sub-array. - /// </param> - /// <typeparam name="T"> - /// The type of elements in the <paramref name="array"/>. - /// </typeparam> - public static T[] SubArray<T>(this T[] array, int startIndex, int length) - { - if (array == null || array.Length == 0) - return new T[0]; - - if (startIndex < 0 || length <= 0) - return new T[0]; - - if (startIndex + length > array.Length) - return new T[0]; - - if (startIndex == 0 && array.Length == length) - return array; - - T[] subArray = new T[length]; - Array.Copy(array, startIndex, subArray, 0, length); - - return subArray; - } - - /// <summary> - /// Converts the order of the specified array of <see cref="byte"/> to the host byte order. - /// </summary> - /// <returns> - /// An array of <see cref="byte"/> converted from <paramref name="src"/>. - /// </returns> - /// <param name="src"> - /// An array of <see cref="byte"/> to convert. - /// </param> - /// <param name="srcOrder"> - /// One of the <see cref="ByteOrder"/> enum values, indicates the byte order of - /// <paramref name="src"/>. - /// </param> - /// <exception cref="ArgumentNullException"> - /// <paramref name="src"/> is <see langword="null"/>. - /// </exception> - public static byte[] ToHostOrder(this byte[] src, ByteOrder srcOrder) - { - if (src == null) - throw new ArgumentNullException("src"); - - return src.Length > 1 && !srcOrder.IsHostOrder() - ? src.Reverse() - : src; - } - - /// <summary> - /// Converts the specified <paramref name="array"/> to a <see cref="string"/> that - /// concatenates the each element of <paramref name="array"/> across the specified - /// <paramref name="separator"/>. - /// </summary> - /// <returns> - /// A <see cref="string"/> converted from <paramref name="array"/>, - /// or <see cref="String.Empty"/> if <paramref name="array"/> is empty. - /// </returns> - /// <param name="array"> - /// An array of T to convert. - /// </param> - /// <param name="separator"> - /// A <see cref="string"/> that represents the separator string. - /// </param> - /// <typeparam name="T"> - /// The type of elements in <paramref name="array"/>. - /// </typeparam> - /// <exception cref="ArgumentNullException"> - /// <paramref name="array"/> is <see langword="null"/>. - /// </exception> - public static string ToString<T>(this T[] array, string separator) - { - if (array == null) - throw new ArgumentNullException("array"); - - var len = array.Length; - if (len == 0) - return String.Empty; - - if (separator == null) - separator = String.Empty; - - var buff = new StringBuilder(64); - (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator)); - - buff.Append(array[len - 1].ToString()); - return buff.ToString(); - } - - /// <summary> - /// Executes the specified <c>Action<int></c> delegate <paramref name="n"/> times. - /// </summary> - /// <param name="n"> - /// An <see cref="int"/> is the number of times to execute. - /// </param> - /// <param name="action"> - /// An <c>Action<int></c> delegate that references the method(s) to execute. - /// An <see cref="int"/> parameter to pass to the method(s) is the zero-based count of - /// iteration. - /// </param> - public static void Times(this int n, Action<int> action) - { - if (n > 0 && action != null) - for (int i = 0; i < n; i++) - action(i); - } - - /// <summary> - /// Converts the specified <see cref="string"/> to a <see cref="Uri"/>. - /// </summary> - /// <returns> - /// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or <see langword="null"/> - /// if <paramref name="uriString"/> isn't successfully converted. - /// </returns> - /// <param name="uriString"> - /// A <see cref="string"/> to convert. - /// </param> - public static Uri ToUri(this string uriString) - { - Uri res; - return Uri.TryCreate( - uriString, uriString.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out res) - ? res - : null; - } - - /// <summary> - /// URL-decodes the specified <see cref="string"/>. - /// </summary> - /// <returns> - /// A <see cref="string"/> that receives the decoded string, or the <paramref name="value"/> - /// if it's <see langword="null"/> or empty. - /// </returns> - /// <param name="value"> - /// A <see cref="string"/> to decode. - /// </param> - public static string UrlDecode(this string value) - { - return value == null || value.Length == 0 - ? value - : WebUtility.UrlDecode(value); - } - - #endregion - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/Fin.cs b/SocketHttpListener.Portable/Fin.cs deleted file mode 100644 index f91401b99..000000000 --- a/SocketHttpListener.Portable/Fin.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace SocketHttpListener -{ - internal enum Fin : byte - { - More = 0x0, - Final = 0x1 - } -} diff --git a/SocketHttpListener.Portable/HttpBase.cs b/SocketHttpListener.Portable/HttpBase.cs deleted file mode 100644 index 5172ba497..000000000 --- a/SocketHttpListener.Portable/HttpBase.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Text; -using System.Threading; -using MediaBrowser.Model.Services; - -namespace SocketHttpListener -{ - internal abstract class HttpBase - { - #region Private Fields - - private QueryParamCollection _headers; - private Version _version; - - #endregion - - #region Internal Fields - - internal byte[] EntityBodyData; - - #endregion - - #region Protected Fields - - protected const string CrLf = "\r\n"; - - #endregion - - #region Protected Constructors - - protected HttpBase(Version version, QueryParamCollection headers) - { - _version = version; - _headers = headers; - } - - #endregion - - #region Public Properties - - public string EntityBody - { - get - { - var data = EntityBodyData; - - return data != null && data.Length > 0 - ? getEncoding(_headers["Content-Type"]).GetString(data, 0, data.Length) - : String.Empty; - } - } - - public QueryParamCollection Headers - { - get - { - return _headers; - } - } - - public Version ProtocolVersion - { - get - { - return _version; - } - } - - #endregion - - #region Private Methods - - private static Encoding getEncoding(string contentType) - { - if (contentType == null || contentType.Length == 0) - return Encoding.UTF8; - - var i = contentType.IndexOf("charset=", StringComparison.Ordinal); - if (i == -1) - return Encoding.UTF8; - - var charset = contentType.Substring(i + 8); - i = charset.IndexOf(';'); - if (i != -1) - charset = charset.Substring(0, i).TrimEnd(); - - return Encoding.GetEncoding(charset.Trim('"')); - } - - #endregion - - #region Public Methods - - public byte[] ToByteArray() - { - return Encoding.UTF8.GetBytes(ToString()); - } - - #endregion - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/HttpResponse.cs b/SocketHttpListener.Portable/HttpResponse.cs deleted file mode 100644 index 5aca28c7c..000000000 --- a/SocketHttpListener.Portable/HttpResponse.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.IO; -using System.Net; -using System.Text; -using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode; -using HttpVersion = SocketHttpListener.Net.HttpVersion; -using System.Linq; -using MediaBrowser.Model.Services; - -namespace SocketHttpListener -{ - internal class HttpResponse : HttpBase - { - #region Private Fields - - private string _code; - private string _reason; - - #endregion - - #region Private Constructors - - private HttpResponse(string code, string reason, Version version, QueryParamCollection headers) - : base(version, headers) - { - _code = code; - _reason = reason; - } - - #endregion - - #region Internal Constructors - - internal HttpResponse(HttpStatusCode code) - : this(code, code.GetDescription()) - { - } - - internal HttpResponse(HttpStatusCode code, string reason) - : this(((int)code).ToString(), reason, HttpVersion.Version11, new QueryParamCollection()) - { - Headers["Server"] = "websocket-sharp/1.0"; - } - - #endregion - - #region Public Properties - - public CookieCollection Cookies - { - get - { - return Headers.GetCookies(true); - } - } - - public bool IsProxyAuthenticationRequired - { - get - { - return _code == "407"; - } - } - - public bool IsUnauthorized - { - get - { - return _code == "401"; - } - } - - public bool IsWebSocketResponse - { - get - { - var headers = Headers; - return ProtocolVersion > HttpVersion.Version10 && - _code == "101" && - headers.Contains("Upgrade", "websocket") && - headers.Contains("Connection", "Upgrade"); - } - } - - public string Reason - { - get - { - return _reason; - } - } - - public string StatusCode - { - get - { - return _code; - } - } - - #endregion - - #region Internal Methods - - internal static HttpResponse CreateCloseResponse(HttpStatusCode code) - { - var res = new HttpResponse(code); - res.Headers["Connection"] = "close"; - - return res; - } - - internal static HttpResponse CreateWebSocketResponse() - { - var res = new HttpResponse(HttpStatusCode.SwitchingProtocols); - - var headers = res.Headers; - headers["Upgrade"] = "websocket"; - headers["Connection"] = "Upgrade"; - - return res; - } - - #endregion - - #region Public Methods - - public void SetCookies(CookieCollection cookies) - { - if (cookies == null || cookies.Count == 0) - return; - - var headers = Headers; - var sorted = cookies.OfType<Cookie>().OrderBy(i => i.Name).ToList(); - - foreach (var cookie in sorted) - headers.Add("Set-Cookie", cookie.ToString()); - } - - public override string ToString() - { - var output = new StringBuilder(64); - output.AppendFormat("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf); - - var headers = Headers; - foreach (var key in headers.Keys) - output.AppendFormat("{0}: {1}{2}", key, headers[key], CrLf); - - output.Append(CrLf); - - var entity = EntityBody; - if (entity.Length > 0) - output.Append(entity); - - return output.ToString(); - } - - #endregion - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/Mask.cs b/SocketHttpListener.Portable/Mask.cs deleted file mode 100644 index adc2f098e..000000000 --- a/SocketHttpListener.Portable/Mask.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace SocketHttpListener -{ - internal enum Mask : byte - { - Unmask = 0x0, - Mask = 0x1 - } -} diff --git a/SocketHttpListener.Portable/MessageEventArgs.cs b/SocketHttpListener.Portable/MessageEventArgs.cs deleted file mode 100644 index 9dbadb9ab..000000000 --- a/SocketHttpListener.Portable/MessageEventArgs.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Text; - -namespace SocketHttpListener -{ - /// <summary> - /// Contains the event data associated with a <see cref="WebSocket.OnMessage"/> event. - /// </summary> - /// <remarks> - /// A <see cref="WebSocket.OnMessage"/> event occurs when the <see cref="WebSocket"/> receives - /// a text or binary data frame. - /// If you want to get the received data, you access the <see cref="MessageEventArgs.Data"/> or - /// <see cref="MessageEventArgs.RawData"/> property. - /// </remarks> - public class MessageEventArgs : EventArgs - { - #region Private Fields - - private string _data; - private Opcode _opcode; - private byte[] _rawData; - - #endregion - - #region Internal Constructors - - internal MessageEventArgs (Opcode opcode, byte[] data) - { - _opcode = opcode; - _rawData = data; - _data = convertToString (opcode, data); - } - - internal MessageEventArgs (Opcode opcode, PayloadData payload) - { - _opcode = opcode; - _rawData = payload.ApplicationData; - _data = convertToString (opcode, _rawData); - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the received data as a <see cref="string"/>. - /// </summary> - /// <value> - /// A <see cref="string"/> that contains the received data. - /// </value> - public string Data { - get { - return _data; - } - } - - /// <summary> - /// Gets the received data as an array of <see cref="byte"/>. - /// </summary> - /// <value> - /// An array of <see cref="byte"/> that contains the received data. - /// </value> - public byte [] RawData { - get { - return _rawData; - } - } - - /// <summary> - /// Gets the type of the received data. - /// </summary> - /// <value> - /// One of the <see cref="Opcode"/> values, indicates the type of the received data. - /// </value> - public Opcode Type { - get { - return _opcode; - } - } - - #endregion - - #region Private Methods - - private static string convertToString (Opcode opcode, byte [] data) - { - return data.Length == 0 - ? String.Empty - : opcode == Opcode.Text - ? Encoding.UTF8.GetString (data, 0, data.Length) - : opcode.ToString (); - } - - #endregion - } -} 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 - } -} diff --git a/SocketHttpListener.Portable/Opcode.cs b/SocketHttpListener.Portable/Opcode.cs deleted file mode 100644 index 62b7d8585..000000000 --- a/SocketHttpListener.Portable/Opcode.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace SocketHttpListener -{ - /// <summary> - /// Contains the values of the opcode that indicates the type of a WebSocket frame. - /// </summary> - /// <remarks> - /// The values of the opcode are defined in - /// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455. - /// </remarks> - public enum Opcode : byte - { - /// <summary> - /// Equivalent to numeric value 0. - /// Indicates a continuation frame. - /// </summary> - Cont = 0x0, - /// <summary> - /// Equivalent to numeric value 1. - /// Indicates a text frame. - /// </summary> - Text = 0x1, - /// <summary> - /// Equivalent to numeric value 2. - /// Indicates a binary frame. - /// </summary> - Binary = 0x2, - /// <summary> - /// Equivalent to numeric value 8. - /// Indicates a connection close frame. - /// </summary> - Close = 0x8, - /// <summary> - /// Equivalent to numeric value 9. - /// Indicates a ping frame. - /// </summary> - Ping = 0x9, - /// <summary> - /// Equivalent to numeric value 10. - /// Indicates a pong frame. - /// </summary> - Pong = 0xa - } -} diff --git a/SocketHttpListener.Portable/PayloadData.cs b/SocketHttpListener.Portable/PayloadData.cs deleted file mode 100644 index a6318da2b..000000000 --- a/SocketHttpListener.Portable/PayloadData.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace SocketHttpListener -{ - internal class PayloadData : IEnumerable<byte> - { - #region Private Fields - - private byte [] _applicationData; - private byte [] _extensionData; - private bool _masked; - - #endregion - - #region Public Const Fields - - public const ulong MaxLength = long.MaxValue; - - #endregion - - #region Public Constructors - - public PayloadData () - : this (new byte [0], new byte [0], false) - { - } - - public PayloadData (byte [] applicationData) - : this (new byte [0], applicationData, false) - { - } - - public PayloadData (string applicationData) - : this (new byte [0], Encoding.UTF8.GetBytes (applicationData), false) - { - } - - public PayloadData (byte [] applicationData, bool masked) - : this (new byte [0], applicationData, masked) - { - } - - public PayloadData (byte [] extensionData, byte [] applicationData, bool masked) - { - _extensionData = extensionData; - _applicationData = applicationData; - _masked = masked; - } - - #endregion - - #region Internal Properties - - internal bool ContainsReservedCloseStatusCode { - get { - return _applicationData.Length > 1 && - _applicationData.SubArray (0, 2).ToUInt16 (ByteOrder.Big).IsReserved (); - } - } - - #endregion - - #region Public Properties - - public byte [] ApplicationData { - get { - return _applicationData; - } - } - - public byte [] ExtensionData { - get { - return _extensionData; - } - } - - public bool IsMasked { - get { - return _masked; - } - } - - public ulong Length { - get { - return (ulong) (_extensionData.Length + _applicationData.Length); - } - } - - #endregion - - #region Private Methods - - private static void mask (byte [] src, byte [] key) - { - for (long i = 0; i < src.Length; i++) - src [i] = (byte) (src [i] ^ key [i % 4]); - } - - #endregion - - #region Public Methods - - public IEnumerator<byte> GetEnumerator () - { - foreach (byte b in _extensionData) - yield return b; - - foreach (byte b in _applicationData) - yield return b; - } - - public void Mask (byte [] maskingKey) - { - if (_extensionData.Length > 0) - mask (_extensionData, maskingKey); - - if (_applicationData.Length > 0) - mask (_applicationData, maskingKey); - - _masked = !_masked; - } - - public byte [] ToByteArray () - { - return _extensionData.Length > 0 - ? new List<byte> (this).ToArray () - : _applicationData; - } - - public override string ToString () - { - return BitConverter.ToString (ToByteArray ()); - } - - #endregion - - #region Explicitly Implemented Interface Members - - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/SocketHttpListener.Portable/Primitives/HttpListenerException.cs b/SocketHttpListener.Portable/Primitives/HttpListenerException.cs deleted file mode 100644 index 7b383fd23..000000000 --- a/SocketHttpListener.Portable/Primitives/HttpListenerException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SocketHttpListener.Primitives -{ - public class HttpListenerException : Exception - { - public HttpListenerException(int statusCode, string message) - : base(message) - { - - } - } -} diff --git a/SocketHttpListener.Portable/Primitives/ICertificate.cs b/SocketHttpListener.Portable/Primitives/ICertificate.cs deleted file mode 100644 index 1289da13d..000000000 --- a/SocketHttpListener.Portable/Primitives/ICertificate.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SocketHttpListener.Primitives -{ - public interface ICertificate - { - } -} diff --git a/SocketHttpListener.Portable/Primitives/IStreamFactory.cs b/SocketHttpListener.Portable/Primitives/IStreamFactory.cs deleted file mode 100644 index 57e21e31b..000000000 --- a/SocketHttpListener.Portable/Primitives/IStreamFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; - -namespace SocketHttpListener.Primitives -{ - public interface IStreamFactory - { - Stream CreateNetworkStream(IAcceptSocket acceptSocket, bool ownsSocket); - Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen); - - Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate); - } -} diff --git a/SocketHttpListener.Portable/Primitives/ITextEncoding.cs b/SocketHttpListener.Portable/Primitives/ITextEncoding.cs deleted file mode 100644 index b10145687..000000000 --- a/SocketHttpListener.Portable/Primitives/ITextEncoding.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Text; - -namespace SocketHttpListener.Primitives -{ - public static class TextEncodingExtensions - { - public static Encoding GetDefaultEncoding(this ITextEncoding encoding) - { - return Encoding.UTF8; - } - } -} diff --git a/SocketHttpListener.Portable/Properties/AssemblyInfo.cs b/SocketHttpListener.Portable/Properties/AssemblyInfo.cs deleted file mode 100644 index 870426460..000000000 --- a/SocketHttpListener.Portable/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Resources; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("SocketHttpListener.Portable")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SocketHttpListener.Portable")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SocketHttpListener.Portable/Rsv.cs b/SocketHttpListener.Portable/Rsv.cs deleted file mode 100644 index 668059b8a..000000000 --- a/SocketHttpListener.Portable/Rsv.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace SocketHttpListener -{ - internal enum Rsv : byte - { - Off = 0x0, - On = 0x1 - } -} diff --git a/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj b/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj deleted file mode 100644 index ee902462b..000000000 --- a/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj +++ /dev/null @@ -1,108 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> - <PropertyGroup> - <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}</ProjectGuid> - <OutputType>Library</OutputType> - <AppDesignerFolder>Properties</AppDesignerFolder> - <RootNamespace>SocketHttpListener.Portable</RootNamespace> - <AssemblyName>SocketHttpListener.Portable</AssemblyName> - <DefaultLanguage>en-US</DefaultLanguage> - <FileAlignment>512</FileAlignment> - <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> - <TargetFrameworkProfile>Profile7</TargetFrameworkProfile> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>pdbonly</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <ItemGroup> - <Compile Include="ByteOrder.cs" /> - <Compile Include="CloseEventArgs.cs" /> - <Compile Include="CloseStatusCode.cs" /> - <Compile Include="CompressionMethod.cs" /> - <Compile Include="ErrorEventArgs.cs" /> - <Compile Include="Ext.cs" /> - <Compile Include="Fin.cs" /> - <Compile Include="HttpBase.cs" /> - <Compile Include="HttpResponse.cs" /> - <Compile Include="Mask.cs" /> - <Compile Include="MessageEventArgs.cs" /> - <Compile Include="Net\AuthenticationSchemeSelector.cs" /> - <Compile Include="Net\ChunkedInputStream.cs" /> - <Compile Include="Net\ChunkStream.cs" /> - <Compile Include="Net\CookieHelper.cs" /> - <Compile Include="Net\EndPointListener.cs" /> - <Compile Include="Net\EndPointManager.cs" /> - <Compile Include="Net\HttpConnection.cs" /> - <Compile Include="Net\HttpListener.cs" /> - <Compile Include="Net\HttpListenerBasicIdentity.cs" /> - <Compile Include="Net\HttpListenerContext.cs" /> - <Compile Include="Net\HttpListenerPrefixCollection.cs" /> - <Compile Include="Net\HttpListenerRequest.cs" /> - <Compile Include="Net\HttpListenerResponse.cs" /> - <Compile Include="Net\HttpStatusCode.cs" /> - <Compile Include="Net\HttpStreamAsyncResult.cs" /> - <Compile Include="Net\HttpVersion.cs" /> - <Compile Include="Net\ListenerPrefix.cs" /> - <Compile Include="Net\RequestStream.cs" /> - <Compile Include="Net\ResponseStream.cs" /> - <Compile Include="Net\WebHeaderCollection.cs" /> - <Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" /> - <Compile Include="Net\WebSockets\WebSocketContext.cs" /> - <Compile Include="Opcode.cs" /> - <Compile Include="PayloadData.cs" /> - <Compile Include="Primitives\HttpListenerException.cs" /> - <Compile Include="Primitives\ICertificate.cs" /> - <Compile Include="Primitives\IStreamFactory.cs" /> - <Compile Include="Primitives\ITextEncoding.cs" /> - <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Rsv.cs" /> - <Compile Include="WebSocket.cs" /> - <Compile Include="WebSocketException.cs" /> - <Compile Include="WebSocketFrame.cs" /> - <Compile Include="WebSocketState.cs" /> - </ItemGroup> - <ItemGroup> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> - <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project> - <Name>MediaBrowser.Common</Name> - </ProjectReference> - <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> - <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project> - <Name>MediaBrowser.Model</Name> - </ProjectReference> - </ItemGroup> - <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> - <PropertyGroup> - <PostBuildEvent> - </PostBuildEvent> - </PropertyGroup> - <!-- To modify your build process, add your task inside one of the targets below and uncomment it. - Other similar extension points exist, see Microsoft.Common.targets. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project>
\ No newline at end of file diff --git a/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets b/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets deleted file mode 100644 index e69ce0e64..000000000 --- a/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Target Name="EmitMSBuildWarning" BeforeTargets="Build"> - <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." /> - </Target> -</Project>
\ No newline at end of file diff --git a/SocketHttpListener.Portable/WebSocket.cs b/SocketHttpListener.Portable/WebSocket.cs deleted file mode 100644 index 9966d3fcf..000000000 --- a/SocketHttpListener.Portable/WebSocket.cs +++ /dev/null @@ -1,887 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.IO; -using SocketHttpListener.Net.WebSockets; -using SocketHttpListener.Primitives; -using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode; - -namespace SocketHttpListener -{ - /// <summary> - /// Implements the WebSocket interface. - /// </summary> - /// <remarks> - /// The WebSocket class provides a set of methods and properties for two-way communication using - /// the WebSocket protocol (<see href="http://tools.ietf.org/html/rfc6455">RFC 6455</see>). - /// </remarks> - public class WebSocket : IDisposable - { - #region Private Fields - - private string _base64Key; - private Action _closeContext; - private CompressionMethod _compression; - private WebSocketContext _context; - private CookieCollection _cookies; - private string _extensions; - private AutoResetEvent _exitReceiving; - private object _forConn; - private object _forEvent; - private object _forMessageEventQueue; - private object _forSend; - private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - private Func<WebSocketContext, string> - _handshakeRequestChecker; - private Queue<MessageEventArgs> _messageEventQueue; - private uint _nonceCount; - private string _origin; - private bool _preAuth; - private string _protocol; - private string[] _protocols; - private Uri _proxyUri; - private volatile WebSocketState _readyState; - private AutoResetEvent _receivePong; - private bool _secure; - private Stream _stream; - private Uri _uri; - private const string _version = "13"; - private readonly IMemoryStreamFactory _memoryStreamFactory; - - private readonly ICryptoProvider _cryptoProvider; - - #endregion - - #region Internal Fields - - internal const int FragmentLength = 1016; // Max value is int.MaxValue - 14. - - #endregion - - #region Internal Constructors - - // As server - internal WebSocket(HttpListenerWebSocketContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory) - { - _context = context; - _protocol = protocol; - _cryptoProvider = cryptoProvider; - _memoryStreamFactory = memoryStreamFactory; - - _closeContext = context.Close; - _secure = context.IsSecureConnection; - _stream = context.Stream; - - init(); - } - - #endregion - - // As server - internal Func<WebSocketContext, string> CustomHandshakeRequestChecker - { - get - { - return _handshakeRequestChecker ?? (context => null); - } - - set - { - _handshakeRequestChecker = value; - } - } - - internal bool IsConnected - { - get - { - return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; - } - } - - /// <summary> - /// Gets the state of the WebSocket connection. - /// </summary> - /// <value> - /// One of the <see cref="WebSocketState"/> enum values, indicates the state of the WebSocket - /// connection. The default value is <see cref="WebSocketState.Connecting"/>. - /// </value> - public WebSocketState ReadyState - { - get - { - return _readyState; - } - } - - #region Public Events - - /// <summary> - /// Occurs when the WebSocket connection has been closed. - /// </summary> - public event EventHandler<CloseEventArgs> OnClose; - - /// <summary> - /// Occurs when the <see cref="WebSocket"/> gets an error. - /// </summary> - public event EventHandler<ErrorEventArgs> OnError; - - /// <summary> - /// Occurs when the <see cref="WebSocket"/> receives a message. - /// </summary> - public event EventHandler<MessageEventArgs> OnMessage; - - /// <summary> - /// Occurs when the WebSocket connection has been established. - /// </summary> - public event EventHandler OnOpen; - - #endregion - - #region Private Methods - - // As server - private bool acceptHandshake() - { - var msg = checkIfValidHandshakeRequest(_context); - if (msg != null) - { - error("An error has occurred while connecting: " + msg); - Close(HttpStatusCode.BadRequest); - - return false; - } - - if (_protocol != null && - !_context.SecWebSocketProtocols.Contains(protocol => protocol == _protocol)) - _protocol = null; - - ////var extensions = _context.Headers["Sec-WebSocket-Extensions"]; - ////if (extensions != null && extensions.Length > 0) - //// processSecWebSocketExtensionsHeader(extensions); - - return sendHttpResponse(createHandshakeResponse()); - } - - // As server - private string checkIfValidHandshakeRequest(WebSocketContext context) - { - var headers = context.Headers; - return context.RequestUri == null - ? "Invalid request url." - : !context.IsWebSocketRequest - ? "Not WebSocket connection request." - : !validateSecWebSocketKeyHeader(headers["Sec-WebSocket-Key"]) - ? "Invalid Sec-WebSocket-Key header." - : !validateSecWebSocketVersionClientHeader(headers["Sec-WebSocket-Version"]) - ? "Invalid Sec-WebSocket-Version header." - : CustomHandshakeRequestChecker(context); - } - - private void close(CloseStatusCode code, string reason, bool wait) - { - close(new PayloadData(((ushort)code).Append(reason)), !code.IsReserved(), wait); - } - - private void close(PayloadData payload, bool send, bool wait) - { - lock (_forConn) - { - if (_readyState == WebSocketState.Closing || _readyState == WebSocketState.Closed) - { - return; - } - - _readyState = WebSocketState.Closing; - } - - var e = new CloseEventArgs(payload); - e.WasClean = - closeHandshake( - send ? WebSocketFrame.CreateCloseFrame(Mask.Unmask, payload).ToByteArray() : null, - wait ? 1000 : 0, - closeServerResources); - - _readyState = WebSocketState.Closed; - try - { - OnClose.Emit(this, e); - } - catch (Exception ex) - { - error("An exception has occurred while OnClose.", ex); - } - } - - private bool closeHandshake(byte[] frameAsBytes, int millisecondsTimeout, Action release) - { - var sent = frameAsBytes != null && writeBytes(frameAsBytes); - var received = - millisecondsTimeout == 0 || - (sent && _exitReceiving != null && _exitReceiving.WaitOne(millisecondsTimeout)); - - release(); - if (_receivePong != null) - { - _receivePong.Dispose(); - _receivePong = null; - } - - if (_exitReceiving != null) - { - _exitReceiving.Dispose(); - _exitReceiving = null; - } - - var result = sent && received; - - return result; - } - - // As server - private void closeServerResources() - { - if (_closeContext == null) - return; - - _closeContext(); - _closeContext = null; - _stream = null; - _context = null; - } - - private bool concatenateFragmentsInto(Stream dest) - { - while (true) - { - var frame = WebSocketFrame.Read(_stream, true); - if (frame.IsFinal) - { - /* FINAL */ - - // CONT - if (frame.IsContinuation) - { - dest.WriteBytes(frame.PayloadData.ApplicationData); - break; - } - - // PING - if (frame.IsPing) - { - processPingFrame(frame); - continue; - } - - // PONG - if (frame.IsPong) - { - processPongFrame(frame); - continue; - } - - // CLOSE - if (frame.IsClose) - return processCloseFrame(frame); - } - else - { - /* MORE */ - - // CONT - if (frame.IsContinuation) - { - dest.WriteBytes(frame.PayloadData.ApplicationData); - continue; - } - } - - // ? - return processUnsupportedFrame( - frame, - CloseStatusCode.IncorrectData, - "An incorrect data has been received while receiving fragmented data."); - } - - return true; - } - - // As server - private HttpResponse createHandshakeCloseResponse(HttpStatusCode code) - { - var res = HttpResponse.CreateCloseResponse(code); - res.Headers["Sec-WebSocket-Version"] = _version; - - return res; - } - - // As server - private HttpResponse createHandshakeResponse() - { - var res = HttpResponse.CreateWebSocketResponse(); - - var headers = res.Headers; - headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key); - - if (_protocol != null) - headers["Sec-WebSocket-Protocol"] = _protocol; - - if (_extensions != null) - headers["Sec-WebSocket-Extensions"] = _extensions; - - if (_cookies.Count > 0) - res.SetCookies(_cookies); - - return res; - } - - private MessageEventArgs dequeueFromMessageEventQueue() - { - lock (_forMessageEventQueue) - return _messageEventQueue.Count > 0 - ? _messageEventQueue.Dequeue() - : null; - } - - private void enqueueToMessageEventQueue(MessageEventArgs e) - { - lock (_forMessageEventQueue) - _messageEventQueue.Enqueue(e); - } - - private void error(string message, Exception exception) - { - try - { - if (exception != null) - { - message += ". Exception.Message: " + exception.Message; - } - OnError.Emit(this, new ErrorEventArgs(message)); - } - catch (Exception ex) - { - } - } - - private void error(string message) - { - try - { - OnError.Emit(this, new ErrorEventArgs(message)); - } - catch (Exception ex) - { - } - } - - private void init() - { - _compression = CompressionMethod.None; - _cookies = new CookieCollection(); - _forConn = new object(); - _forEvent = new object(); - _forSend = new object(); - _messageEventQueue = new Queue<MessageEventArgs>(); - _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; - _readyState = WebSocketState.Connecting; - } - - private void open() - { - try - { - startReceiving(); - - lock (_forEvent) - { - try - { - OnOpen.Emit(this, EventArgs.Empty); - } - catch (Exception ex) - { - processException(ex, "An exception has occurred while OnOpen."); - } - } - } - catch (Exception ex) - { - processException(ex, "An exception has occurred while opening."); - } - } - - private bool processCloseFrame(WebSocketFrame frame) - { - var payload = frame.PayloadData; - close(payload, !payload.ContainsReservedCloseStatusCode, false); - - return false; - } - - private bool processDataFrame(WebSocketFrame frame) - { - var e = frame.IsCompressed - ? new MessageEventArgs( - frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) - : new MessageEventArgs(frame.Opcode, frame.PayloadData); - - enqueueToMessageEventQueue(e); - return true; - } - - private void processException(Exception exception, string message) - { - var code = CloseStatusCode.Abnormal; - var reason = message; - if (exception is WebSocketException) - { - var wsex = (WebSocketException)exception; - code = wsex.Code; - reason = wsex.Message; - } - - error(message ?? code.GetMessage(), exception); - if (_readyState == WebSocketState.Connecting) - Close(HttpStatusCode.BadRequest); - else - close(code, reason ?? code.GetMessage(), false); - } - - private bool processFragmentedFrame(WebSocketFrame frame) - { - return frame.IsContinuation // Not first fragment - ? true - : processFragments(frame); - } - - private bool processFragments(WebSocketFrame first) - { - using (var buff = _memoryStreamFactory.CreateNew()) - { - buff.WriteBytes(first.PayloadData.ApplicationData); - if (!concatenateFragmentsInto(buff)) - return false; - - byte[] data; - if (_compression != CompressionMethod.None) - { - data = buff.DecompressToArray(_compression); - } - else - { - data = buff.ToArray(); - } - - enqueueToMessageEventQueue(new MessageEventArgs(first.Opcode, data)); - return true; - } - } - - private bool processPingFrame(WebSocketFrame frame) - { - var mask = Mask.Unmask; - - return true; - } - - private bool processPongFrame(WebSocketFrame frame) - { - _receivePong.Set(); - - return true; - } - - private bool processUnsupportedFrame(WebSocketFrame frame, CloseStatusCode code, string reason) - { - processException(new WebSocketException(code, reason), null); - - return false; - } - - private bool processWebSocketFrame(WebSocketFrame frame) - { - return frame.IsCompressed && _compression == CompressionMethod.None - ? processUnsupportedFrame( - frame, - CloseStatusCode.IncorrectData, - "A compressed data has been received without available decompression method.") - : frame.IsFragmented - ? processFragmentedFrame(frame) - : frame.IsData - ? processDataFrame(frame) - : frame.IsPing - ? processPingFrame(frame) - : frame.IsPong - ? processPongFrame(frame) - : frame.IsClose - ? processCloseFrame(frame) - : processUnsupportedFrame(frame, CloseStatusCode.PolicyViolation, null); - } - - private bool send(Opcode opcode, Stream stream) - { - lock (_forSend) - { - var src = stream; - var compressed = false; - var sent = false; - try - { - if (_compression != CompressionMethod.None) - { - stream = stream.Compress(_compression); - compressed = true; - } - - sent = send(opcode, Mask.Unmask, stream, compressed); - if (!sent) - error("Sending a data has been interrupted."); - } - catch (Exception ex) - { - error("An exception has occurred while sending a data.", ex); - } - finally - { - if (compressed) - stream.Dispose(); - - src.Dispose(); - } - - return sent; - } - } - - private bool send(Opcode opcode, Mask mask, Stream stream, bool compressed) - { - var len = stream.Length; - - /* Not fragmented */ - - if (len == 0) - return send(Fin.Final, opcode, mask, new byte[0], compressed); - - var quo = len / FragmentLength; - var rem = (int)(len % FragmentLength); - - byte[] buff = null; - if (quo == 0) - { - buff = new byte[rem]; - return stream.Read(buff, 0, rem) == rem && - send(Fin.Final, opcode, mask, buff, compressed); - } - - buff = new byte[FragmentLength]; - if (quo == 1 && rem == 0) - return stream.Read(buff, 0, FragmentLength) == FragmentLength && - send(Fin.Final, opcode, mask, buff, compressed); - - /* Send fragmented */ - - // Begin - if (stream.Read(buff, 0, FragmentLength) != FragmentLength || - !send(Fin.More, opcode, mask, buff, compressed)) - return false; - - var n = rem == 0 ? quo - 2 : quo - 1; - for (long i = 0; i < n; i++) - if (stream.Read(buff, 0, FragmentLength) != FragmentLength || - !send(Fin.More, Opcode.Cont, mask, buff, compressed)) - return false; - - // End - if (rem == 0) - rem = FragmentLength; - else - buff = new byte[rem]; - - return stream.Read(buff, 0, rem) == rem && - send(Fin.Final, Opcode.Cont, mask, buff, compressed); - } - - private bool send(Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed) - { - lock (_forConn) - { - if (_readyState != WebSocketState.Open) - { - return false; - } - - return writeBytes( - WebSocketFrame.CreateWebSocketFrame(fin, opcode, mask, data, compressed).ToByteArray()); - } - } - - private Task sendAsync(Opcode opcode, Stream stream) - { - var completionSource = new TaskCompletionSource<bool>(); - Task.Run(() => - { - try - { - send(opcode, stream); - completionSource.TrySetResult(true); - } - catch (Exception ex) - { - completionSource.TrySetException(ex); - } - }); - return completionSource.Task; - } - - // As server - private bool sendHttpResponse(HttpResponse response) - { - return writeBytes(response.ToByteArray()); - } - - private void startReceiving() - { - if (_messageEventQueue.Count > 0) - _messageEventQueue.Clear(); - - _exitReceiving = new AutoResetEvent(false); - _receivePong = new AutoResetEvent(false); - - Action receive = null; - receive = () => WebSocketFrame.ReadAsync( - _stream, - true, - frame => - { - if (processWebSocketFrame(frame) && _readyState != WebSocketState.Closed) - { - receive(); - - if (!frame.IsData) - return; - - lock (_forEvent) - { - try - { - var e = dequeueFromMessageEventQueue(); - if (e != null && _readyState == WebSocketState.Open) - OnMessage.Emit(this, e); - } - catch (Exception ex) - { - processException(ex, "An exception has occurred while OnMessage."); - } - } - } - else if (_exitReceiving != null) - { - _exitReceiving.Set(); - } - }, - ex => processException(ex, "An exception has occurred while receiving a message.")); - - receive(); - } - - // As server - private bool validateSecWebSocketKeyHeader(string value) - { - if (value == null || value.Length == 0) - return false; - - _base64Key = value; - return true; - } - - // As server - private bool validateSecWebSocketVersionClientHeader(string value) - { - return true; - //return value != null && value == _version; - } - - private bool writeBytes(byte[] data) - { - try - { - _stream.Write(data, 0, data.Length); - return true; - } - catch (Exception ex) - { - return false; - } - } - - #endregion - - #region Internal Methods - - // As server - internal void Close(HttpResponse response) - { - _readyState = WebSocketState.Closing; - - sendHttpResponse(response); - closeServerResources(); - - _readyState = WebSocketState.Closed; - } - - // As server - internal void Close(HttpStatusCode code) - { - Close(createHandshakeCloseResponse(code)); - } - - // As server - public void ConnectAsServer() - { - try - { - if (acceptHandshake()) - { - _readyState = WebSocketState.Open; - open(); - } - } - catch (Exception ex) - { - processException(ex, "An exception has occurred while connecting."); - } - } - - private string CreateResponseKey(string base64Key) - { - var buff = new StringBuilder(base64Key, 64); - buff.Append(_guid); - var src = _cryptoProvider.ComputeSHA1(Encoding.UTF8.GetBytes(buff.ToString())); - - return Convert.ToBase64String(src); - } - - #endregion - - #region Public Methods - - /// <summary> - /// Closes the WebSocket connection, and releases all associated resources. - /// </summary> - public void Close() - { - var msg = _readyState.CheckIfClosable(); - if (msg != null) - { - error(msg); - - return; - } - - var send = _readyState == WebSocketState.Open; - close(new PayloadData(), send, send); - } - - /// <summary> - /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/> - /// and <see cref="string"/>, and releases all associated resources. - /// </summary> - /// <remarks> - /// This method emits a <see cref="OnError"/> event if the size - /// of <paramref name="reason"/> is greater than 123 bytes. - /// </remarks> - /// <param name="code"> - /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code - /// indicating the reason for the close. - /// </param> - /// <param name="reason"> - /// A <see cref="string"/> that represents the reason for the close. - /// </param> - public void Close(CloseStatusCode code, string reason) - { - byte[] data = null; - var msg = _readyState.CheckIfClosable() ?? - (data = ((ushort)code).Append(reason)).CheckIfValidControlData("reason"); - - if (msg != null) - { - error(msg); - - return; - } - - var send = _readyState == WebSocketState.Open && !code.IsReserved(); - close(new PayloadData(data), send, send); - } - - /// <summary> - /// Sends a binary <paramref name="data"/> asynchronously using the WebSocket connection. - /// </summary> - /// <remarks> - /// This method doesn't wait for the send to be complete. - /// </remarks> - /// <param name="data"> - /// An array of <see cref="byte"/> that represents the binary data to send. - /// </param> - /// An Action<bool> delegate that references the method(s) called when the send is - /// complete. A <see cref="bool"/> passed to this delegate is <c>true</c> if the send is - /// complete successfully; otherwise, <c>false</c>. - public Task SendAsync(byte[] data) - { - var msg = _readyState.CheckIfOpen() ?? data.CheckIfValidSendData(); - if (msg != null) - { - throw new Exception(msg); - } - - return sendAsync(Opcode.Binary, _memoryStreamFactory.CreateNew(data)); - } - - /// <summary> - /// Sends a text <paramref name="data"/> asynchronously using the WebSocket connection. - /// </summary> - /// <remarks> - /// This method doesn't wait for the send to be complete. - /// </remarks> - /// <param name="data"> - /// A <see cref="string"/> that represents the text data to send. - /// </param> - /// An Action<bool> delegate that references the method(s) called when the send is - /// complete. A <see cref="bool"/> passed to this delegate is <c>true</c> if the send is - /// complete successfully; otherwise, <c>false</c>. - public Task SendAsync(string data) - { - var msg = _readyState.CheckIfOpen() ?? data.CheckIfValidSendData(); - if (msg != null) - { - throw new Exception(msg); - } - - return sendAsync(Opcode.Text, _memoryStreamFactory.CreateNew(Encoding.UTF8.GetBytes(data))); - } - - #endregion - - #region Explicit Interface Implementation - - /// <summary> - /// Closes the WebSocket connection, and releases all associated resources. - /// </summary> - /// <remarks> - /// This method closes the WebSocket connection with <see cref="CloseStatusCode.Away"/>. - /// </remarks> - void IDisposable.Dispose() - { - Close(CloseStatusCode.Away, null); - } - - #endregion - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/WebSocketException.cs b/SocketHttpListener.Portable/WebSocketException.cs deleted file mode 100644 index 260721317..000000000 --- a/SocketHttpListener.Portable/WebSocketException.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; - -namespace SocketHttpListener -{ - /// <summary> - /// The exception that is thrown when a <see cref="WebSocket"/> gets a fatal error. - /// </summary> - public class WebSocketException : Exception - { - #region Internal Constructors - - internal WebSocketException () - : this (CloseStatusCode.Abnormal, null, null) - { - } - - internal WebSocketException (string message) - : this (CloseStatusCode.Abnormal, message, null) - { - } - - internal WebSocketException (CloseStatusCode code) - : this (code, null, null) - { - } - - internal WebSocketException (string message, Exception innerException) - : this (CloseStatusCode.Abnormal, message, innerException) - { - } - - internal WebSocketException (CloseStatusCode code, string message) - : this (code, message, null) - { - } - - internal WebSocketException (CloseStatusCode code, string message, Exception innerException) - : base (message ?? code.GetMessage (), innerException) - { - Code = code; - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the status code indicating the cause for the exception. - /// </summary> - /// <value> - /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code indicating - /// the cause for the exception. - /// </value> - public CloseStatusCode Code { - get; private set; - } - - #endregion - } -} diff --git a/SocketHttpListener.Portable/WebSocketFrame.cs b/SocketHttpListener.Portable/WebSocketFrame.cs deleted file mode 100644 index 44fa4a5dc..000000000 --- a/SocketHttpListener.Portable/WebSocketFrame.cs +++ /dev/null @@ -1,578 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace SocketHttpListener -{ - internal class WebSocketFrame : IEnumerable<byte> - { - #region Private Fields - - private byte[] _extPayloadLength; - private Fin _fin; - private Mask _mask; - private byte[] _maskingKey; - private Opcode _opcode; - private PayloadData _payloadData; - private byte _payloadLength; - private Rsv _rsv1; - private Rsv _rsv2; - private Rsv _rsv3; - - #endregion - - #region Internal Fields - - internal static readonly byte[] EmptyUnmaskPingData; - - #endregion - - #region Static Constructor - - static WebSocketFrame() - { - EmptyUnmaskPingData = CreatePingFrame(Mask.Unmask).ToByteArray(); - } - - #endregion - - #region Private Constructors - - private WebSocketFrame() - { - } - - #endregion - - #region Internal Constructors - - internal WebSocketFrame(Opcode opcode, PayloadData payload) - : this(Fin.Final, opcode, Mask.Mask, payload, false) - { - } - - internal WebSocketFrame(Opcode opcode, Mask mask, PayloadData payload) - : this(Fin.Final, opcode, mask, payload, false) - { - } - - internal WebSocketFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payload) - : this(fin, opcode, mask, payload, false) - { - } - - internal WebSocketFrame( - Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed) - { - _fin = fin; - _rsv1 = isData(opcode) && compressed ? Rsv.On : Rsv.Off; - _rsv2 = Rsv.Off; - _rsv3 = Rsv.Off; - _opcode = opcode; - _mask = mask; - - var len = payload.Length; - if (len < 126) - { - _payloadLength = (byte)len; - _extPayloadLength = new byte[0]; - } - else if (len < 0x010000) - { - _payloadLength = (byte)126; - _extPayloadLength = ((ushort)len).ToByteArrayInternally(ByteOrder.Big); - } - else - { - _payloadLength = (byte)127; - _extPayloadLength = len.ToByteArrayInternally(ByteOrder.Big); - } - - if (mask == Mask.Mask) - { - _maskingKey = createMaskingKey(); - payload.Mask(_maskingKey); - } - else - { - _maskingKey = new byte[0]; - } - - _payloadData = payload; - } - - #endregion - - #region Public Properties - - public byte[] ExtendedPayloadLength - { - get - { - return _extPayloadLength; - } - } - - public Fin Fin - { - get - { - return _fin; - } - } - - public bool IsBinary - { - get - { - return _opcode == Opcode.Binary; - } - } - - public bool IsClose - { - get - { - return _opcode == Opcode.Close; - } - } - - public bool IsCompressed - { - get - { - return _rsv1 == Rsv.On; - } - } - - public bool IsContinuation - { - get - { - return _opcode == Opcode.Cont; - } - } - - public bool IsControl - { - get - { - return _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong; - } - } - - public bool IsData - { - get - { - return _opcode == Opcode.Binary || _opcode == Opcode.Text; - } - } - - public bool IsFinal - { - get - { - return _fin == Fin.Final; - } - } - - public bool IsFragmented - { - get - { - return _fin == Fin.More || _opcode == Opcode.Cont; - } - } - - public bool IsMasked - { - get - { - return _mask == Mask.Mask; - } - } - - public bool IsPerMessageCompressed - { - get - { - return (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On; - } - } - - public bool IsPing - { - get - { - return _opcode == Opcode.Ping; - } - } - - public bool IsPong - { - get - { - return _opcode == Opcode.Pong; - } - } - - public bool IsText - { - get - { - return _opcode == Opcode.Text; - } - } - - public ulong Length - { - get - { - return 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length; - } - } - - public Mask Mask - { - get - { - return _mask; - } - } - - public byte[] MaskingKey - { - get - { - return _maskingKey; - } - } - - public Opcode Opcode - { - get - { - return _opcode; - } - } - - public PayloadData PayloadData - { - get - { - return _payloadData; - } - } - - public byte PayloadLength - { - get - { - return _payloadLength; - } - } - - public Rsv Rsv1 - { - get - { - return _rsv1; - } - } - - public Rsv Rsv2 - { - get - { - return _rsv2; - } - } - - public Rsv Rsv3 - { - get - { - return _rsv3; - } - } - - #endregion - - #region Private Methods - - private byte[] createMaskingKey() - { - var key = new byte[4]; - var rand = new Random(); - rand.NextBytes(key); - - return key; - } - - private static bool isControl(Opcode opcode) - { - return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong; - } - - private static bool isData(Opcode opcode) - { - return opcode == Opcode.Text || opcode == Opcode.Binary; - } - - private static WebSocketFrame read(byte[] header, Stream stream, bool unmask) - { - /* Header */ - - // FIN - var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More; - // RSV1 - var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off; - // RSV2 - var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off; - // RSV3 - var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off; - // Opcode - var opcode = (Opcode)(header[0] & 0x0f); - // MASK - var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask; - // Payload Length - var payloadLen = (byte)(header[1] & 0x7f); - - // Check if correct frame. - var incorrect = isControl(opcode) && fin == Fin.More - ? "A control frame is fragmented." - : !isData(opcode) && rsv1 == Rsv.On - ? "A non data frame is compressed." - : null; - - if (incorrect != null) - throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect); - - // Check if consistent frame. - if (isControl(opcode) && payloadLen > 125) - throw new WebSocketException( - CloseStatusCode.InconsistentData, - "The length of payload data of a control frame is greater than 125 bytes."); - - var frame = new WebSocketFrame(); - frame._fin = fin; - frame._rsv1 = rsv1; - frame._rsv2 = rsv2; - frame._rsv3 = rsv3; - frame._opcode = opcode; - frame._mask = mask; - frame._payloadLength = payloadLen; - - /* Extended Payload Length */ - - var size = payloadLen < 126 - ? 0 - : payloadLen == 126 - ? 2 - : 8; - - var extPayloadLen = size > 0 ? stream.ReadBytes(size) : new byte[0]; - if (size > 0 && extPayloadLen.Length != size) - throw new WebSocketException( - "The 'Extended Payload Length' of a frame cannot be read from the data source."); - - frame._extPayloadLength = extPayloadLen; - - /* Masking Key */ - - var masked = mask == Mask.Mask; - var maskingKey = masked ? stream.ReadBytes(4) : new byte[0]; - if (masked && maskingKey.Length != 4) - throw new WebSocketException( - "The 'Masking Key' of a frame cannot be read from the data source."); - - frame._maskingKey = maskingKey; - - /* Payload Data */ - - ulong len = payloadLen < 126 - ? payloadLen - : payloadLen == 126 - ? extPayloadLen.ToUInt16(ByteOrder.Big) - : extPayloadLen.ToUInt64(ByteOrder.Big); - - byte[] data = null; - if (len > 0) - { - // Check if allowable payload data length. - if (payloadLen > 126 && len > PayloadData.MaxLength) - throw new WebSocketException( - CloseStatusCode.TooBig, - "The length of 'Payload Data' of a frame is greater than the allowable length."); - - data = payloadLen > 126 - ? stream.ReadBytes((long)len, 1024) - : stream.ReadBytes((int)len); - - //if (data.LongLength != (long)len) - // throw new WebSocketException( - // "The 'Payload Data' of a frame cannot be read from the data source."); - } - else - { - data = new byte[0]; - } - - var payload = new PayloadData(data, masked); - if (masked && unmask) - { - payload.Mask(maskingKey); - frame._mask = Mask.Unmask; - frame._maskingKey = new byte[0]; - } - - frame._payloadData = payload; - return frame; - } - - #endregion - - #region Internal Methods - - internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data) - { - return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data)); - } - - internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload) - { - return new WebSocketFrame(Opcode.Close, mask, payload); - } - - internal static WebSocketFrame CreateCloseFrame(Mask mask, CloseStatusCode code, string reason) - { - return new WebSocketFrame( - Opcode.Close, mask, new PayloadData(((ushort)code).Append(reason))); - } - - internal static WebSocketFrame CreatePingFrame(Mask mask) - { - return new WebSocketFrame(Opcode.Ping, mask, new PayloadData()); - } - - internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data) - { - return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data)); - } - - internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload) - { - return new WebSocketFrame(Opcode.Pong, mask, payload); - } - - internal static WebSocketFrame CreateWebSocketFrame( - Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed) - { - return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed); - } - - internal static WebSocketFrame Read(Stream stream) - { - return Read(stream, true); - } - - internal static WebSocketFrame Read(Stream stream, bool unmask) - { - var header = stream.ReadBytes(2); - if (header.Length != 2) - throw new WebSocketException( - "The header part of a frame cannot be read from the data source."); - - return read(header, stream, unmask); - } - - internal static async void ReadAsync( - Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error) - { - try - { - var header = await stream.ReadBytesAsync(2).ConfigureAwait(false); - if (header.Length != 2) - throw new WebSocketException( - "The header part of a frame cannot be read from the data source."); - - var frame = read(header, stream, unmask); - if (completed != null) - completed(frame); - } - catch (Exception ex) - { - if (error != null) - { - error(ex); - } - } - } - - #endregion - - #region Public Methods - - public IEnumerator<byte> GetEnumerator() - { - foreach (var b in ToByteArray()) - yield return b; - } - - public void Print(bool dumped) - { - //Console.WriteLine(dumped ? dump(this) : print(this)); - } - - public byte[] ToByteArray() - { - using (var buff = new MemoryStream()) - { - var header = (int)_fin; - header = (header << 1) + (int)_rsv1; - header = (header << 1) + (int)_rsv2; - header = (header << 1) + (int)_rsv3; - header = (header << 4) + (int)_opcode; - header = (header << 1) + (int)_mask; - header = (header << 7) + (int)_payloadLength; - buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2); - - if (_payloadLength > 125) - buff.Write(_extPayloadLength, 0, _extPayloadLength.Length); - - if (_mask == Mask.Mask) - buff.Write(_maskingKey, 0, _maskingKey.Length); - - if (_payloadLength > 0) - { - var payload = _payloadData.ToByteArray(); - if (_payloadLength < 127) - buff.Write(payload, 0, payload.Length); - else - buff.WriteBytes(payload); - } - - return buff.ToArray(); - } - } - - public override string ToString() - { - return BitConverter.ToString(ToByteArray()); - } - - #endregion - - #region Explicitly Implemented Interface Members - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } -}
\ No newline at end of file diff --git a/SocketHttpListener.Portable/WebSocketState.cs b/SocketHttpListener.Portable/WebSocketState.cs deleted file mode 100644 index 73b3a49dd..000000000 --- a/SocketHttpListener.Portable/WebSocketState.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SocketHttpListener -{ - /// <summary> - /// Contains the values of the state of the WebSocket connection. - /// </summary> - /// <remarks> - /// The values of the state are defined in - /// <see href="http://www.w3.org/TR/websockets/#dom-websocket-readystate">The WebSocket - /// API</see>. - /// </remarks> - public enum WebSocketState : ushort - { - /// <summary> - /// Equivalent to numeric value 0. - /// Indicates that the connection has not yet been established. - /// </summary> - Connecting = 0, - /// <summary> - /// Equivalent to numeric value 1. - /// Indicates that the connection is established and the communication is possible. - /// </summary> - Open = 1, - /// <summary> - /// Equivalent to numeric value 2. - /// Indicates that the connection is going through the closing handshake or - /// the <c>WebSocket.Close</c> method has been invoked. - /// </summary> - Closing = 2, - /// <summary> - /// Equivalent to numeric value 3. - /// Indicates that the connection has been closed or couldn't be opened. - /// </summary> - Closed = 3 - } -} diff --git a/SocketHttpListener.Portable/packages.config b/SocketHttpListener.Portable/packages.config deleted file mode 100644 index 2aae715b5..000000000 --- a/SocketHttpListener.Portable/packages.config +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="MediaBrowser.Common" version="3.0.689" targetFramework="portable45-net45+win8" /> - <package id="Patterns.Logging" version="1.0.0.6" targetFramework="portable45-net45+win8" /> -</packages>
\ No newline at end of file diff --git a/SocketHttpListener.Portable/project.json b/SocketHttpListener.Portable/project.json deleted file mode 100644 index fbbe9eaf3..000000000 --- a/SocketHttpListener.Portable/project.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "frameworks":{ - "netstandard1.6":{ - "dependencies":{ - "NETStandard.Library":"1.6.0", - } - }, - ".NETPortable,Version=v4.5,Profile=Profile7":{ - "buildOptions": { - "define": [ ] - }, - "frameworkAssemblies":{ - - } - } - } -}
\ No newline at end of file |
