diff options
Diffstat (limited to 'RSSDP')
| -rw-r--r-- | RSSDP/DeviceAvailableEventArgs.cs | 97 | ||||
| -rw-r--r-- | RSSDP/DeviceEventArgs.cs | 62 | ||||
| -rw-r--r-- | RSSDP/DeviceUnavailableEventArgs.cs | 104 | ||||
| -rw-r--r-- | RSSDP/DiscoveredSsdpDevice.cs | 168 | ||||
| -rw-r--r-- | RSSDP/DisposableManagedObjectBase.cs | 84 | ||||
| -rw-r--r-- | RSSDP/HttpRequestParser.cs | 150 | ||||
| -rw-r--r-- | RSSDP/HttpResponseParser.cs | 159 | ||||
| -rw-r--r-- | RSSDP/IEnumerableExtensions.cs | 36 | ||||
| -rw-r--r-- | RSSDP/ISsdpDeviceLocator.cs | 274 | ||||
| -rw-r--r-- | RSSDP/ISsdpDevicePublisher.cs | 57 | ||||
| -rw-r--r-- | RSSDP/RequestReceivedEventArgs.cs | 100 | ||||
| -rw-r--r-- | RSSDP/ResponseReceivedEventArgs.cs | 85 | ||||
| -rw-r--r-- | RSSDP/SsdpCommunicationsServer.cs | 31 | ||||
| -rw-r--r-- | RSSDP/SsdpConstants.cs | 100 | ||||
| -rw-r--r-- | RSSDP/SsdpDevice.cs | 4 | ||||
| -rw-r--r-- | RSSDP/SsdpDevicePublisher.cs | 8 | ||||
| -rw-r--r-- | RSSDP/SsdpEmbeddedDevice.cs | 100 | ||||
| -rw-r--r-- | RSSDP/SsdpRootDevice.cs | 114 |
18 files changed, 860 insertions, 873 deletions
diff --git a/RSSDP/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs index d80b4b65b..fe1c81566 100644 --- a/RSSDP/DeviceAvailableEventArgs.cs +++ b/RSSDP/DeviceAvailableEventArgs.cs @@ -6,57 +6,56 @@ using MediaBrowser.Model.Net; namespace Rssdp { - /// <summary> - /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceAvailable"/> event. - /// </summary> - public sealed class DeviceAvailableEventArgs : EventArgs - { + /// <summary> + /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceAvailable"/> event. + /// </summary> + public sealed class DeviceAvailableEventArgs : EventArgs + { public IpAddressInfo LocalIpAddress { get; set; } #region Fields private readonly DiscoveredSsdpDevice _DiscoveredDevice; - private readonly bool _IsNewlyDiscovered; - - #endregion - - #region Constructors - - /// <summary> - /// Full constructor. - /// </summary> - /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the available device.</param> - /// <param name="isNewlyDiscovered">A boolean value indicating whether or not this device came from the cache. See <see cref="IsNewlyDiscovered"/> for more detail.</param> - /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> - public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered) - { - if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); - - _DiscoveredDevice = discoveredDevice; - _IsNewlyDiscovered = isNewlyDiscovered; - } - - #endregion - - #region Public Properties - - /// <summary> - /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request. - /// </summary> - public bool IsNewlyDiscovered - { - get { return _IsNewlyDiscovered; } - } - - /// <summary> - /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovered details and allowing access to the full device description. - /// </summary> - public DiscoveredSsdpDevice DiscoveredDevice - { - get { return _DiscoveredDevice; } - } - - #endregion - - } -}
\ No newline at end of file + private readonly bool _IsNewlyDiscovered; + + #endregion + + #region Constructors + + /// <summary> + /// Full constructor. + /// </summary> + /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the available device.</param> + /// <param name="isNewlyDiscovered">A boolean value indicating whether or not this device came from the cache. See <see cref="IsNewlyDiscovered"/> for more detail.</param> + /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> + public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered) + { + if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); + + _DiscoveredDevice = discoveredDevice; + _IsNewlyDiscovered = isNewlyDiscovered; + } + + #endregion + + #region Public Properties + + /// <summary> + /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request. + /// </summary> + public bool IsNewlyDiscovered + { + get { return _IsNewlyDiscovered; } + } + + /// <summary> + /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovered details and allowing access to the full device description. + /// </summary> + public DiscoveredSsdpDevice DiscoveredDevice + { + get { return _DiscoveredDevice; } + } + + #endregion + } +} diff --git a/RSSDP/DeviceEventArgs.cs b/RSSDP/DeviceEventArgs.cs index 774d812ee..5fd441041 100644 --- a/RSSDP/DeviceEventArgs.cs +++ b/RSSDP/DeviceEventArgs.cs @@ -4,45 +4,45 @@ using System.Text; namespace Rssdp { - /// <summary> - /// Event arguments for the <see cref="SsdpDevice.DeviceAdded"/> and <see cref="SsdpDevice.DeviceRemoved"/> events. - /// </summary> - public sealed class DeviceEventArgs : EventArgs - { + /// <summary> + /// Event arguments for the <see cref="SsdpDevice.DeviceAdded"/> and <see cref="SsdpDevice.DeviceRemoved"/> events. + /// </summary> + public sealed class DeviceEventArgs : EventArgs + { - #region Fields + #region Fields - private readonly SsdpDevice _Device; + private readonly SsdpDevice _Device; - #endregion + #endregion - #region Constructors + #region Constructors - /// <summary> - /// Constructs a new instance for the specified <see cref="SsdpDevice"/>. - /// </summary> - /// <param name="device">The <see cref="SsdpDevice"/> associated with the event this argument class is being used for.</param> - /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception> - public DeviceEventArgs(SsdpDevice device) - { - if (device == null) throw new ArgumentNullException("device"); + /// <summary> + /// Constructs a new instance for the specified <see cref="SsdpDevice"/>. + /// </summary> + /// <param name="device">The <see cref="SsdpDevice"/> associated with the event this argument class is being used for.</param> + /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception> + public DeviceEventArgs(SsdpDevice device) + { + if (device == null) throw new ArgumentNullException("device"); - _Device = device; - } + _Device = device; + } - #endregion + #endregion - #region Public Properties + #region Public Properties - /// <summary> - /// Returns the <see cref="SsdpDevice"/> instance the event being raised for. - /// </summary> - public SsdpDevice Device - { - get { return _Device; } - } + /// <summary> + /// Returns the <see cref="SsdpDevice"/> instance the event being raised for. + /// </summary> + public SsdpDevice Device + { + get { return _Device; } + } - #endregion + #endregion - } -}
\ No newline at end of file + } +} diff --git a/RSSDP/DeviceUnavailableEventArgs.cs b/RSSDP/DeviceUnavailableEventArgs.cs index 171a834a0..1190cfc81 100644 --- a/RSSDP/DeviceUnavailableEventArgs.cs +++ b/RSSDP/DeviceUnavailableEventArgs.cs @@ -5,55 +5,55 @@ using System.Threading.Tasks; namespace Rssdp { - /// <summary> - /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceUnavailable"/> event. - /// </summary> - public sealed class DeviceUnavailableEventArgs : EventArgs - { - - #region Fields - - private readonly DiscoveredSsdpDevice _DiscoveredDevice; - private readonly bool _Expired; - - #endregion - - #region Constructors - - /// <summary> - /// Full constructor. - /// </summary> - /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the device that has become unavailable.</param> - /// <param name="expired">A boolean value indicating whether this device is unavailable because it expired, or because it explicitly sent a byebye notification.. See <see cref="Expired"/> for more detail.</param> - /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> - public DeviceUnavailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool expired) - { - if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); - - _DiscoveredDevice = discoveredDevice; - _Expired = expired; - } - - #endregion - - #region Public Properties - - /// <summary> - /// Returns true if the device is considered unavailable because it's cached information expired before a new alive notification or search result was received. Returns false if the device is unavailable because it sent an explicit notification of it's unavailability. - /// </summary> - public bool Expired - { - get { return _Expired; } - } - - /// <summary> - /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovery details of the removed device. - /// </summary> - public DiscoveredSsdpDevice DiscoveredDevice - { - get { return _DiscoveredDevice; } - } - - #endregion - } -}
\ No newline at end of file + /// <summary> + /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceUnavailable"/> event. + /// </summary> + public sealed class DeviceUnavailableEventArgs : EventArgs + { + + #region Fields + + private readonly DiscoveredSsdpDevice _DiscoveredDevice; + private readonly bool _Expired; + + #endregion + + #region Constructors + + /// <summary> + /// Full constructor. + /// </summary> + /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the device that has become unavailable.</param> + /// <param name="expired">A boolean value indicating whether this device is unavailable because it expired, or because it explicitly sent a byebye notification.. See <see cref="Expired"/> for more detail.</param> + /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception> + public DeviceUnavailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool expired) + { + if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice"); + + _DiscoveredDevice = discoveredDevice; + _Expired = expired; + } + + #endregion + + #region Public Properties + + /// <summary> + /// Returns true if the device is considered unavailable because it's cached information expired before a new alive notification or search result was received. Returns false if the device is unavailable because it sent an explicit notification of it's unavailability. + /// </summary> + public bool Expired + { + get { return _Expired; } + } + + /// <summary> + /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovery details of the removed device. + /// </summary> + public DiscoveredSsdpDevice DiscoveredDevice + { + get { return _DiscoveredDevice; } + } + + #endregion + } +} diff --git a/RSSDP/DiscoveredSsdpDevice.cs b/RSSDP/DiscoveredSsdpDevice.cs index 7e70817dd..0c5701d29 100644 --- a/RSSDP/DiscoveredSsdpDevice.cs +++ b/RSSDP/DiscoveredSsdpDevice.cs @@ -7,88 +7,88 @@ using System.Net.Http.Headers; namespace Rssdp { - /// <summary> - /// Represents a discovered device, containing basic information about the device and the location of it's full device description document. Also provides convenience methods for retrieving the device description document. - /// </summary> - /// <seealso cref="SsdpDevice"/> - /// <seealso cref="Rssdp.Infrastructure.ISsdpDeviceLocator"/> - public sealed class DiscoveredSsdpDevice - { - - #region Fields - - private DateTimeOffset _AsAt; - - #endregion - - #region Public Properties - - /// <summary> - /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. - /// </summary> - public string NotificationType { get; set; } - - /// <summary> - /// Sets or returns the universal service name (USN) of the device. - /// </summary> - public string Usn { get; set; } - - /// <summary> - /// Sets or returns a URL pointing to the device description document for this device. - /// </summary> - public Uri DescriptionLocation { get; set; } - - /// <summary> - /// Sets or returns the length of time this information is valid for (from the <see cref="AsAt"/> time). - /// </summary> - public TimeSpan CacheLifetime { get; set; } - - /// <summary> - /// Sets or returns the date and time this information was received. - /// </summary> - public DateTimeOffset AsAt - { - get { return _AsAt; } - set - { - if (_AsAt != value) - { - _AsAt = value; - } - } - } - - /// <summary> - /// Returns the headers from the SSDP device response message - /// </summary> - public HttpHeaders ResponseHeaders { get; set; } - - #endregion - - #region Public Methods - - /// <summary> - /// Returns true if this device information has expired, based on the current date/time, and the <see cref="CacheLifetime"/> & <see cref="AsAt"/> properties. - /// </summary> - /// <returns></returns> - public bool IsExpired() - { - return this.CacheLifetime == TimeSpan.Zero || this.AsAt.Add(this.CacheLifetime) <= DateTimeOffset.Now; - } - - #endregion - - #region Overrides - - /// <summary> - /// Returns the device's <see cref="Usn"/> value. - /// </summary> - /// <returns>A string containing the device's universal service name.</returns> - public override string ToString() - { - return this.Usn; - } - - #endregion - } + /// <summary> + /// Represents a discovered device, containing basic information about the device and the location of it's full device description document. Also provides convenience methods for retrieving the device description document. + /// </summary> + /// <seealso cref="SsdpDevice"/> + /// <seealso cref="Rssdp.Infrastructure.ISsdpDeviceLocator"/> + public sealed class DiscoveredSsdpDevice + { + + #region Fields + + private DateTimeOffset _AsAt; + + #endregion + + #region Public Properties + + /// <summary> + /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice. + /// </summary> + public string NotificationType { get; set; } + + /// <summary> + /// Sets or returns the universal service name (USN) of the device. + /// </summary> + public string Usn { get; set; } + + /// <summary> + /// Sets or returns a URL pointing to the device description document for this device. + /// </summary> + public Uri DescriptionLocation { get; set; } + + /// <summary> + /// Sets or returns the length of time this information is valid for (from the <see cref="AsAt"/> time). + /// </summary> + public TimeSpan CacheLifetime { get; set; } + + /// <summary> + /// Sets or returns the date and time this information was received. + /// </summary> + public DateTimeOffset AsAt + { + get { return _AsAt; } + set + { + if (_AsAt != value) + { + _AsAt = value; + } + } + } + + /// <summary> + /// Returns the headers from the SSDP device response message + /// </summary> + public HttpHeaders ResponseHeaders { get; set; } + + #endregion + + #region Public Methods + + /// <summary> + /// Returns true if this device information has expired, based on the current date/time, and the <see cref="CacheLifetime"/> & <see cref="AsAt"/> properties. + /// </summary> + /// <returns></returns> + public bool IsExpired() + { + return this.CacheLifetime == TimeSpan.Zero || this.AsAt.Add(this.CacheLifetime) <= DateTimeOffset.Now; + } + + #endregion + + #region Overrides + + /// <summary> + /// Returns the device's <see cref="Usn"/> value. + /// </summary> + /// <returns>A string containing the device's universal service name.</returns> + public override string ToString() + { + return this.Usn; + } + + #endregion + } } diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs index 7a0fdd45a..c1349dd5c 100644 --- a/RSSDP/DisposableManagedObjectBase.cs +++ b/RSSDP/DisposableManagedObjectBase.cs @@ -5,44 +5,44 @@ using System.Threading.Tasks; namespace Rssdp.Infrastructure { - /// <summary> - /// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property. - /// </summary> - public abstract class DisposableManagedObjectBase : IDisposable - { - - #region Public Methods - - /// <summary> - /// Override this method and dispose any objects you own the lifetime of if disposing is true; - /// </summary> - /// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param> - protected abstract void Dispose(bool disposing); - - /// <summary> - /// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true. - /// </summary> - /// <seealso cref="IsDisposed"/> - /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception> - /// <seealso cref="Dispose()"/> - protected virtual void ThrowIfDisposed() - { - if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); - } - - #endregion - - #region Public Properties - - /// <summary> - /// Sets or returns a boolean indicating whether or not this instance has been disposed. - /// </summary> - /// <seealso cref="Dispose()"/> - public bool IsDisposed - { - get; - private set; - } + /// <summary> + /// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property. + /// </summary> + public abstract class DisposableManagedObjectBase : IDisposable + { + + #region Public Methods + + /// <summary> + /// Override this method and dispose any objects you own the lifetime of if disposing is true; + /// </summary> + /// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param> + protected abstract void Dispose(bool disposing); + + /// <summary> + /// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true. + /// </summary> + /// <seealso cref="IsDisposed"/> + /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception> + /// <seealso cref="Dispose()"/> + protected virtual void ThrowIfDisposed() + { + if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); + } + + #endregion + + #region Public Properties + + /// <summary> + /// Sets or returns a boolean indicating whether or not this instance has been disposed. + /// </summary> + /// <seealso cref="Dispose()"/> + public bool IsDisposed + { + get; + private set; + } #endregion @@ -63,7 +63,7 @@ namespace Rssdp.Infrastructure return builder.ToString(); } - + #region IDisposable Members /// <summary> @@ -74,8 +74,8 @@ namespace Rssdp.Infrastructure /// </remarks> /// <seealso cref="IsDisposed"/> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification="We do exactly as asked, but CA doesn't seem to like us also setting the IsDisposed property. Too bad, it's a good idea and shouldn't cause an exception or anything likely to interfer with the dispose process.")] - public void Dispose() - { + public void Dispose() + { IsDisposed = true; Dispose(true); @@ -83,4 +83,4 @@ namespace Rssdp.Infrastructure #endregion } -}
\ No newline at end of file +} diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs index 8460e1ca5..8b027ebd5 100644 --- a/RSSDP/HttpRequestParser.cs +++ b/RSSDP/HttpRequestParser.cs @@ -8,70 +8,70 @@ using System.Threading.Tasks; namespace Rssdp.Infrastructure { - /// <summary> - /// Parses a string into a <see cref="System.Net.Http.HttpRequestMessage"/> or throws an exception. - /// </summary> - public sealed class HttpRequestParser : HttpParserBase<HttpRequestMessage> - { - - #region Fields & Constants - - private readonly string[] ContentHeaderNames = new string[] - { - "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" - }; - - #endregion - - #region Public Methods - - /// <summary> - /// Parses the specified data into a <see cref="System.Net.Http.HttpRequestMessage"/> instance. - /// </summary> - /// <param name="data">A string containing the data to parse.</param> - /// <returns>A <see cref="System.Net.Http.HttpRequestMessage"/> instance containing the parsed data.</returns> - public override System.Net.Http.HttpRequestMessage Parse(string data) - { - System.Net.Http.HttpRequestMessage retVal = null; - - try - { - retVal = new System.Net.Http.HttpRequestMessage(); - - Parse(retVal, retVal.Headers, data); - - return retVal; - } - finally - { - if (retVal != null) - retVal.Dispose(); - } - } - - #endregion - - #region Overrides - - /// <summary> - /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. - /// </summary> - /// <param name="data">The first line of the HTTP message to be parsed.</param> - /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> - protected override void ParseStatusLine(string data, HttpRequestMessage message) - { - if (data == null) throw new ArgumentNullException("data"); - if (message == null) throw new ArgumentNullException("message"); - - var parts = data.Split(' '); - if (parts.Length < 2) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data"); - - message.Method = new HttpMethod(parts[0].Trim()); - Uri requestUri; - if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri)) - message.RequestUri = requestUri; - else - System.Diagnostics.Debug.WriteLine(parts[1]); + /// <summary> + /// Parses a string into a <see cref="System.Net.Http.HttpRequestMessage"/> or throws an exception. + /// </summary> + public sealed class HttpRequestParser : HttpParserBase<HttpRequestMessage> + { + + #region Fields & Constants + + private readonly string[] ContentHeaderNames = new string[] + { + "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" + }; + + #endregion + + #region Public Methods + + /// <summary> + /// Parses the specified data into a <see cref="System.Net.Http.HttpRequestMessage"/> instance. + /// </summary> + /// <param name="data">A string containing the data to parse.</param> + /// <returns>A <see cref="System.Net.Http.HttpRequestMessage"/> instance containing the parsed data.</returns> + public override System.Net.Http.HttpRequestMessage Parse(string data) + { + System.Net.Http.HttpRequestMessage retVal = null; + + try + { + retVal = new System.Net.Http.HttpRequestMessage(); + + Parse(retVal, retVal.Headers, data); + + return retVal; + } + finally + { + if (retVal != null) + retVal.Dispose(); + } + } + + #endregion + + #region Overrides + + /// <summary> + /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. + /// </summary> + /// <param name="data">The first line of the HTTP message to be parsed.</param> + /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> + protected override void ParseStatusLine(string data, HttpRequestMessage message) + { + if (data == null) throw new ArgumentNullException("data"); + if (message == null) throw new ArgumentNullException("message"); + + var parts = data.Split(' '); + if (parts.Length < 2) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data"); + + message.Method = new HttpMethod(parts[0].Trim()); + Uri requestUri; + if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri)) + message.RequestUri = requestUri; + else + System.Diagnostics.Debug.WriteLine(parts[1]); if (parts.Length >= 3) { @@ -79,16 +79,16 @@ namespace Rssdp.Infrastructure } } - /// <summary> - /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). - /// </summary> - /// <param name="headerName">A string containing the name of the header to return the type of.</param> - protected override bool IsContentHeader(string headerName) - { - return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); - } + /// <summary> + /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). + /// </summary> + /// <param name="headerName">A string containing the name of the header to return the type of.</param> + protected override bool IsContentHeader(string headerName) + { + return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); + } - #endregion + #endregion - } -}
\ No newline at end of file + } +} diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs index 8ecb944c2..cbd5517b8 100644 --- a/RSSDP/HttpResponseParser.cs +++ b/RSSDP/HttpResponseParser.cs @@ -8,82 +8,82 @@ using System.Threading.Tasks; namespace Rssdp.Infrastructure { - /// <summary> - /// Parses a string into a <see cref="System.Net.Http.HttpResponseMessage"/> or throws an exception. - /// </summary> - public sealed class HttpResponseParser : HttpParserBase<System.Net.Http.HttpResponseMessage> - { - - #region Fields & Constants - - private readonly string[] ContentHeaderNames = new string[] - { - "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" - }; - - #endregion - - #region Public Methods - - /// <summary> - /// Parses the specified data into a <see cref="System.Net.Http.HttpResponseMessage"/> instance. - /// </summary> - /// <param name="data">A string containing the data to parse.</param> - /// <returns>A <see cref="System.Net.Http.HttpResponseMessage"/> instance containing the parsed data.</returns> - public override HttpResponseMessage Parse(string data) - { - System.Net.Http.HttpResponseMessage retVal = null; - try - { - retVal = new System.Net.Http.HttpResponseMessage(); - - Parse(retVal, retVal.Headers, data); - - return retVal; - } - catch - { - if (retVal != null) - retVal.Dispose(); - - throw; - } - } - - #endregion - - #region Overrides Methods - - /// <summary> - /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). - /// </summary> - /// <param name="headerName">A string containing the name of the header to return the type of.</param> - /// <returns>A boolean, true if th specified header relates to HTTP content, otherwise false.</returns> - protected override bool IsContentHeader(string headerName) - { - return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); - } - - /// <summary> - /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. - /// </summary> - /// <param name="data">The first line of the HTTP message to be parsed.</param> - /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> - protected override void ParseStatusLine(string data, HttpResponseMessage message) - { - if (data == null) throw new ArgumentNullException("data"); - if (message == null) throw new ArgumentNullException("message"); - - var parts = data.Split(' '); - if (parts.Length < 2) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data"); - - message.Version = ParseHttpVersion(parts[0].Trim()); - - int statusCode = -1; - if (!Int32.TryParse(parts[1].Trim(), out statusCode)) - throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data"); - - message.StatusCode = (HttpStatusCode)statusCode; + /// <summary> + /// Parses a string into a <see cref="System.Net.Http.HttpResponseMessage"/> or throws an exception. + /// </summary> + public sealed class HttpResponseParser : HttpParserBase<System.Net.Http.HttpResponseMessage> + { + + #region Fields & Constants + + private readonly string[] ContentHeaderNames = new string[] + { + "Allow", "Content-Disposition", "Content-Encoding", "Content-Language", "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type", "Expires", "Last-Modified" + }; + + #endregion + + #region Public Methods + + /// <summary> + /// Parses the specified data into a <see cref="System.Net.Http.HttpResponseMessage"/> instance. + /// </summary> + /// <param name="data">A string containing the data to parse.</param> + /// <returns>A <see cref="System.Net.Http.HttpResponseMessage"/> instance containing the parsed data.</returns> + public override HttpResponseMessage Parse(string data) + { + System.Net.Http.HttpResponseMessage retVal = null; + try + { + retVal = new System.Net.Http.HttpResponseMessage(); + + Parse(retVal, retVal.Headers, data); + + return retVal; + } + catch + { + if (retVal != null) + retVal.Dispose(); + + throw; + } + } + + #endregion + + #region Overrides Methods + + /// <summary> + /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). + /// </summary> + /// <param name="headerName">A string containing the name of the header to return the type of.</param> + /// <returns>A boolean, true if th specified header relates to HTTP content, otherwise false.</returns> + protected override bool IsContentHeader(string headerName) + { + return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase); + } + + /// <summary> + /// Used to parse the first line of an HTTP request or response and assign the values to the appropriate properties on the <paramref name="message"/>. + /// </summary> + /// <param name="data">The first line of the HTTP message to be parsed.</param> + /// <param name="message">Either a <see cref="System.Net.Http.HttpResponseMessage"/> or <see cref="System.Net.Http.HttpRequestMessage"/> to assign the parsed values to.</param> + protected override void ParseStatusLine(string data, HttpResponseMessage message) + { + if (data == null) throw new ArgumentNullException("data"); + if (message == null) throw new ArgumentNullException("message"); + + var parts = data.Split(' '); + if (parts.Length < 2) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data"); + + message.Version = ParseHttpVersion(parts[0].Trim()); + + int statusCode = -1; + if (!Int32.TryParse(parts[1].Trim(), out statusCode)) + throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data"); + + message.StatusCode = (HttpStatusCode)statusCode; if (parts.Length >= 3) { @@ -91,7 +91,6 @@ namespace Rssdp.Infrastructure } } - #endregion - - } -}
\ No newline at end of file + #endregion + } +} diff --git a/RSSDP/IEnumerableExtensions.cs b/RSSDP/IEnumerableExtensions.cs index f72073949..85a72c01d 100644 --- a/RSSDP/IEnumerableExtensions.cs +++ b/RSSDP/IEnumerableExtensions.cs @@ -5,24 +5,24 @@ using System.Text; namespace Rssdp.Infrastructure { - internal static class IEnumerableExtensions - { - public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector) - { - if (source == null) throw new ArgumentNullException("source"); - if (selector == null) throw new ArgumentNullException("selector"); + internal static class IEnumerableExtensions + { + public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector) + { + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); - return !source.Any() ? source : - source.Concat( - source - .SelectMany(i => selector(i).EmptyIfNull()) - .SelectManyRecursive(selector) - ); - } + return !source.Any() ? source : + source.Concat( + source + .SelectMany(i => selector(i).EmptyIfNull()) + .SelectManyRecursive(selector) + ); + } - public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source) - { - return source ?? Enumerable.Empty<T>(); - } - } + public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source) + { + return source ?? Enumerable.Empty<T>(); + } + } } diff --git a/RSSDP/ISsdpDeviceLocator.cs b/RSSDP/ISsdpDeviceLocator.cs index 3ab271836..32235c095 100644 --- a/RSSDP/ISsdpDeviceLocator.cs +++ b/RSSDP/ISsdpDeviceLocator.cs @@ -2,140 +2,140 @@ namespace Rssdp.Infrastructure { - /// <summary> - /// Interface for components that discover the existence of SSDP devices. - /// </summary> - /// <remarks> - /// <para>Discovering devices includes explicit search requests as well as listening for broadcast status notifications.</para> - /// </remarks> - /// <seealso cref="DiscoveredSsdpDevice"/> - /// <seealso cref="SsdpDevice"/> - /// <seealso cref="ISsdpDevicePublisher"/> - public interface ISsdpDeviceLocator - { - - #region Events - - /// <summary> - /// Event raised when a device becomes available or is found by a search request. - /// </summary> - /// <seealso cref="NotificationFilter"/> - /// <seealso cref="DeviceUnavailable"/> - /// <seealso cref="StartListeningForNotifications"/> - /// <seealso cref="StopListeningForNotifications"/> - event EventHandler<DeviceAvailableEventArgs> DeviceAvailable; - - /// <summary> - /// Event raised when a device explicitly notifies of shutdown or a device expires from the cache. - /// </summary> - /// <seeseealso cref="NotificationFilter"/> - /// <seealso cref="DeviceAvailable"/> - /// <seealso cref="StartListeningForNotifications"/> - /// <seealso cref="StopListeningForNotifications"/> - event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable; - - #endregion - - #region Properties - - /// <summary> - /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="DeviceAvailable"/> or <see cref="DeviceUnavailable"/> events. - /// </summary> - /// <remarks> - /// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para> - /// <para>Example filters follow;</para> - /// <example>upnp:rootdevice</example> - /// <example>urn:schemas-upnp-org:device:WANDevice:1</example> - /// <example>"uuid:9F15356CC-95FA-572E-0E99-85B456BD3012"</example> - /// </remarks> - /// <seealso cref="DeviceAvailable"/> - /// <seealso cref="DeviceUnavailable"/> - /// <seealso cref="StartListeningForNotifications"/> - /// <seealso cref="StopListeningForNotifications"/> - string NotificationFilter - { - get; - set; - } - - #endregion - - #region Methods - - #region SearchAsync Overloads - - /// <summary> - /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. - /// </summary> - /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> - System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(); - - /// <summary> - /// Performs a search for the specified search target (criteria) and default search timeout. - /// </summary> - /// <param name="searchTarget">The criteria for the search. Value can be; - /// <list type="table"> - /// <item><term>Root devices</term><description>upnp:rootdevice</description></item> - /// <item><term>Specific device by UUID</term><description>uuid:<device uuid></description></item> - /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item> - /// </list> - /// </param> - /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> - System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget); - - /// <summary> - /// Performs a search for the specified search target (criteria) and search timeout. - /// </summary> - /// <param name="searchTarget">The criteria for the search. Value can be; - /// <list type="table"> - /// <item><term>Root devices</term><description>upnp:rootdevice</description></item> - /// <item><term>Specific device by UUID</term><description>uuid:<device uuid></description></item> - /// <item><term>Device type</term><description>A device namespace and type in format of urn:<device namespace>:device:<device type>:<device version> i.e urn:schemas-upnp-org:device:Basic:1</description></item> - /// <item><term>Service type</term><description>A service namespace and type in format of urn:<service namespace>:service:<servicetype>:<service version> i.e urn:my-namespace:service:MyCustomService:1</description></item> - /// </list> - /// </param> - /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> - /// <remarks> - /// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para> - /// </remarks> - /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> - System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime); - - /// <summary> - /// Performs a search for all devices using the specified search timeout. - /// </summary> - /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> - /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> - System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime); - - #endregion - - /// <summary> - /// Starts listening for broadcast notifications of service availability. - /// </summary> - /// <remarks> - /// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para> - /// </remarks> - /// <seealso cref="StopListeningForNotifications"/> - /// <seealso cref="DeviceAvailable"/> - /// <seealso cref="DeviceUnavailable"/> - /// <seealso cref="NotificationFilter"/> - void StartListeningForNotifications(); - - /// <summary> - /// Stops listening for broadcast notifications of service availability. - /// </summary> - /// <remarks> - /// <para>Does nothing if this instance is not already listening for notifications.</para> - /// </remarks> - /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception> - /// <seealso cref="StartListeningForNotifications"/> - /// <seealso cref="DeviceAvailable"/> - /// <seealso cref="DeviceUnavailable"/> - /// <seealso cref="NotificationFilter"/> - void StopListeningForNotifications(); - - #endregion - - } -}
\ No newline at end of file + /// <summary> + /// Interface for components that discover the existence of SSDP devices. + /// </summary> + /// <remarks> + /// <para>Discovering devices includes explicit search requests as well as listening for broadcast status notifications.</para> + /// </remarks> + /// <seealso cref="DiscoveredSsdpDevice"/> + /// <seealso cref="SsdpDevice"/> + /// <seealso cref="ISsdpDevicePublisher"/> + public interface ISsdpDeviceLocator + { + + #region Events + + /// <summary> + /// Event raised when a device becomes available or is found by a search request. + /// </summary> + /// <seealso cref="NotificationFilter"/> + /// <seealso cref="DeviceUnavailable"/> + /// <seealso cref="StartListeningForNotifications"/> + /// <seealso cref="StopListeningForNotifications"/> + event EventHandler<DeviceAvailableEventArgs> DeviceAvailable; + + /// <summary> + /// Event raised when a device explicitly notifies of shutdown or a device expires from the cache. + /// </summary> + /// <seeseealso cref="NotificationFilter"/> + /// <seealso cref="DeviceAvailable"/> + /// <seealso cref="StartListeningForNotifications"/> + /// <seealso cref="StopListeningForNotifications"/> + event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable; + + #endregion + + #region Properties + + /// <summary> + /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="DeviceAvailable"/> or <see cref="DeviceUnavailable"/> events. + /// </summary> + /// <remarks> + /// <para>Device alive/byebye notifications whose NT header does not match this filter value will still be captured and cached internally, but will not raise events about device availability. Usually used with either a device type of uuid NT header value.</para> + /// <para>Example filters follow;</para> + /// <example>upnp:rootdevice</example> + /// <example>urn:schemas-upnp-org:device:WANDevice:1</example> + /// <example>"uuid:9F15356CC-95FA-572E-0E99-85B456BD3012"</example> + /// </remarks> + /// <seealso cref="DeviceAvailable"/> + /// <seealso cref="DeviceUnavailable"/> + /// <seealso cref="StartListeningForNotifications"/> + /// <seealso cref="StopListeningForNotifications"/> + string NotificationFilter + { + get; + set; + } + + #endregion + + #region Methods + + #region SearchAsync Overloads + + /// <summary> + /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results. + /// </summary> + /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> + System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(); + + /// <summary> + /// Performs a search for the specified search target (criteria) and default search timeout. + /// </summary> + /// <param name="searchTarget">The criteria for the search. Value can be; + /// <list type="table"> + /// <item><term>Root devices</term><description>upnp:rootdevice</description></item> + /// <item><term>Specific device by UUID</term><description>uuid:<device uuid></description></item> + /// <item><term>Device type</term><description>Fully qualified device type starting with urn: i.e urn:schemas-upnp-org:Basic:1</description></item> + /// </list> + /// </param> + /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> + System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget); + + /// <summary> + /// Performs a search for the specified search target (criteria) and search timeout. + /// </summary> + /// <param name="searchTarget">The criteria for the search. Value can be; + /// <list type="table"> + /// <item><term>Root devices</term><description>upnp:rootdevice</description></item> + /// <item><term>Specific device by UUID</term><description>uuid:<device uuid></description></item> + /// <item><term>Device type</term><description>A device namespace and type in format of urn:<device namespace>:device:<device type>:<device version> i.e urn:schemas-upnp-org:device:Basic:1</description></item> + /// <item><term>Service type</term><description>A service namespace and type in format of urn:<service namespace>:service:<servicetype>:<service version> i.e urn:my-namespace:service:MyCustomService:1</description></item> + /// </list> + /// </param> + /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> + /// <remarks> + /// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para> + /// </remarks> + /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> + System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime); + + /// <summary> + /// Performs a search for all devices using the specified search timeout. + /// </summary> + /// <param name="searchWaitTime">The amount of time to wait for network responses to the search request. Longer values will likely return more devices, but increase search time. A value between 1 and 5 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param> + /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns> + System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime); + + #endregion + + /// <summary> + /// Starts listening for broadcast notifications of service availability. + /// </summary> + /// <remarks> + /// <para>When called the system will listen for 'alive' and 'byebye' notifications. This can speed up searching, as well as provide dynamic notification of new devices appearing on the network, and previously discovered devices disappearing.</para> + /// </remarks> + /// <seealso cref="StopListeningForNotifications"/> + /// <seealso cref="DeviceAvailable"/> + /// <seealso cref="DeviceUnavailable"/> + /// <seealso cref="NotificationFilter"/> + void StartListeningForNotifications(); + + /// <summary> + /// Stops listening for broadcast notifications of service availability. + /// </summary> + /// <remarks> + /// <para>Does nothing if this instance is not already listening for notifications.</para> + /// </remarks> + /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception> + /// <seealso cref="StartListeningForNotifications"/> + /// <seealso cref="DeviceAvailable"/> + /// <seealso cref="DeviceUnavailable"/> + /// <seealso cref="NotificationFilter"/> + void StopListeningForNotifications(); + + #endregion + + } +} diff --git a/RSSDP/ISsdpDevicePublisher.cs b/RSSDP/ISsdpDevicePublisher.cs index b6ebc4176..b0924701f 100644 --- a/RSSDP/ISsdpDevicePublisher.cs +++ b/RSSDP/ISsdpDevicePublisher.cs @@ -3,35 +3,34 @@ using System.Threading.Tasks; namespace Rssdp.Infrastructure { - /// <summary> - /// Interface for components that publish the existence of SSDP devices. - /// </summary> - /// <remarks> - /// <para>Publishing a device includes sending notifications (alive and byebye) as well as responding to search requests when appropriate.</para> - /// </remarks> - /// <seealso cref="SsdpRootDevice"/> - /// <seealso cref="ISsdpDeviceLocator"/> - public interface ISsdpDevicePublisher - { - /// <summary> - /// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients. - /// </summary> - /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> - /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> - void AddDevice(SsdpRootDevice device); + /// <summary> + /// Interface for components that publish the existence of SSDP devices. + /// </summary> + /// <remarks> + /// <para>Publishing a device includes sending notifications (alive and byebye) as well as responding to search requests when appropriate.</para> + /// </remarks> + /// <seealso cref="SsdpRootDevice"/> + /// <seealso cref="ISsdpDeviceLocator"/> + public interface ISsdpDevicePublisher + { + /// <summary> + /// Adds a device (and it's children) to the list of devices being published by this server, making them discoverable to SSDP clients. + /// </summary> + /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> + /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> + void AddDevice(SsdpRootDevice device); - /// <summary> - /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable. - /// </summary> - /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> - /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> - Task RemoveDevice(SsdpRootDevice device); + /// <summary> + /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable. + /// </summary> + /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param> + /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns> + Task RemoveDevice(SsdpRootDevice device); - /// <summary> - /// Returns a read only list of devices being published by this instance. - /// </summary> - /// <seealso cref="SsdpDevice"/> - System.Collections.Generic.IEnumerable<SsdpRootDevice> Devices { get; } - - } + /// <summary> + /// Returns a read only list of devices being published by this instance. + /// </summary> + /// <seealso cref="SsdpDevice"/> + System.Collections.Generic.IEnumerable<SsdpRootDevice> Devices { get; } + } } diff --git a/RSSDP/RequestReceivedEventArgs.cs b/RSSDP/RequestReceivedEventArgs.cs index f4e367760..6a5b0f086 100644 --- a/RSSDP/RequestReceivedEventArgs.cs +++ b/RSSDP/RequestReceivedEventArgs.cs @@ -8,54 +8,52 @@ using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { - /// <summary> - /// Provides arguments for the <see cref="ISsdpCommunicationsServer.RequestReceived"/> event. - /// </summary> - public sealed class RequestReceivedEventArgs : EventArgs - { - - #region Fields - - private readonly HttpRequestMessage _Message; - private readonly IpEndPointInfo _ReceivedFrom; - - #endregion - - public IpAddressInfo LocalIpAddress { get; private set; } - - #region Constructors - - /// <summary> - /// Full constructor. - /// </summary> - public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress) - { - _Message = message; - _ReceivedFrom = receivedFrom; - LocalIpAddress = localIpAddress; - } - - #endregion - - #region Public Properties - - /// <summary> - /// The <see cref="HttpRequestMessage"/> that was received. - /// </summary> - public HttpRequestMessage Message - { - get { return _Message; } - } - - /// <summary> - /// The <see cref="UdpEndPoint"/> the request came from. - /// </summary> - public IpEndPointInfo ReceivedFrom - { - get { return _ReceivedFrom; } - } - - #endregion - - } -}
\ No newline at end of file + /// <summary> + /// Provides arguments for the <see cref="ISsdpCommunicationsServer.RequestReceived"/> event. + /// </summary> + public sealed class RequestReceivedEventArgs : EventArgs + { + #region Fields + + private readonly HttpRequestMessage _Message; + private readonly IpEndPointInfo _ReceivedFrom; + + #endregion + + public IpAddressInfo LocalIpAddress { get; private set; } + + #region Constructors + + /// <summary> + /// Full constructor. + /// </summary> + public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress) + { + _Message = message; + _ReceivedFrom = receivedFrom; + LocalIpAddress = localIpAddress; + } + + #endregion + + #region Public Properties + + /// <summary> + /// The <see cref="HttpRequestMessage"/> that was received. + /// </summary> + public HttpRequestMessage Message + { + get { return _Message; } + } + + /// <summary> + /// The <see cref="UdpEndPoint"/> the request came from. + /// </summary> + public IpEndPointInfo ReceivedFrom + { + get { return _ReceivedFrom; } + } + + #endregion + } +} diff --git a/RSSDP/ResponseReceivedEventArgs.cs b/RSSDP/ResponseReceivedEventArgs.cs index f67d5da90..5ec5376df 100644 --- a/RSSDP/ResponseReceivedEventArgs.cs +++ b/RSSDP/ResponseReceivedEventArgs.cs @@ -8,53 +8,52 @@ using MediaBrowser.Model.Net; namespace Rssdp.Infrastructure { - /// <summary> - /// Provides arguments for the <see cref="ISsdpCommunicationsServer.ResponseReceived"/> event. - /// </summary> - public sealed class ResponseReceivedEventArgs : EventArgs - { + /// <summary> + /// Provides arguments for the <see cref="ISsdpCommunicationsServer.ResponseReceived"/> event. + /// </summary> + public sealed class ResponseReceivedEventArgs : EventArgs + { public IpAddressInfo LocalIpAddress { get; set; } #region Fields private readonly HttpResponseMessage _Message; - private readonly IpEndPointInfo _ReceivedFrom; - - #endregion - - #region Constructors - - /// <summary> - /// Full constructor. - /// </summary> - public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom) - { - _Message = message; - _ReceivedFrom = receivedFrom; - } - - #endregion - - #region Public Properties - - /// <summary> - /// The <see cref="HttpResponseMessage"/> that was received. - /// </summary> - public HttpResponseMessage Message - { - get { return _Message; } - } - - /// <summary> - /// The <see cref="UdpEndPoint"/> the response came from. - /// </summary> - public IpEndPointInfo ReceivedFrom - { - get { return _ReceivedFrom; } - } - - #endregion - - } + private readonly IpEndPointInfo _ReceivedFrom; + + #endregion + + #region Constructors + + /// <summary> + /// Full constructor. + /// </summary> + public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom) + { + _Message = message; + _ReceivedFrom = receivedFrom; + } + + #endregion + + #region Public Properties + + /// <summary> + /// The <see cref="HttpResponseMessage"/> that was received. + /// </summary> + public HttpResponseMessage Message + { + get { return _Message; } + } + + /// <summary> + /// The <see cref="UdpEndPoint"/> the response came from. + /// </summary> + public IpEndPointInfo ReceivedFrom + { + get { return _ReceivedFrom; } + } + + #endregion + } } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 65ec0ca71..e2e5c4e9f 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -20,22 +20,19 @@ namespace Rssdp.Infrastructure #region Fields - /* - - We could technically use one socket listening on port 1900 for everything. - This should get both multicast (notifications) and unicast (search response) messages, however - this often doesn't work under Windows because the MS SSDP service is running. If that service - is running then it will steal the unicast messages and we will never see search responses. - Since stopping the service would be a bad idea (might not be allowed security wise and might - break other apps running on the system) the only other work around is to use two sockets. - - We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket). - We use a second socket, bound to a different local port, to send search requests and listen for - responses (_SendSocket). The responses are sent to the local port this socket is bound to, - which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local - port to use, we will default to 0 which allows the underlying system to auto-assign a free port. - - */ + /* We could technically use one socket listening on port 1900 for everything. + * This should get both multicast (notifications) and unicast (search response) messages, however + * this often doesn't work under Windows because the MS SSDP service is running. If that service + * is running then it will steal the unicast messages and we will never see search responses. + * Since stopping the service would be a bad idea (might not be allowed security wise and might + * break other apps running on the system) the only other work around is to use two sockets. + * + * We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket). + * We use a second socket, bound to a different local port, to send search requests and listen for + * responses (_SendSocket). The responses are sent to the local port this socket is bound to, + * which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local + * port to use, we will default to 0 which allows the underlying system to auto-assign a free port. + */ private object _BroadcastListenSocketSynchroniser = new object(); private ISocket _BroadcastListenSocket; @@ -443,7 +440,7 @@ namespace Rssdp.Infrastructure private void ProcessMessage(string data, IpEndPointInfo endPoint, IpAddressInfo receivedOnLocalIpAddress) { //Responses start with the HTTP version, prefixed with HTTP/ while - //requests start with a method which can vary and might be one we haven't + //requests start with a method which can vary and might be one we haven't //seen/don't know. We'll check if this message is a request or a response //by checking for the HTTP/ prefix on the start of the message. if (data.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase)) diff --git a/RSSDP/SsdpConstants.cs b/RSSDP/SsdpConstants.cs index 87f01f9e0..ab0ecb0f7 100644 --- a/RSSDP/SsdpConstants.cs +++ b/RSSDP/SsdpConstants.cs @@ -5,63 +5,63 @@ using System.Threading.Tasks; namespace Rssdp.Infrastructure { - /// <summary> - /// Provides constants for common values related to the SSDP protocols. - /// </summary> - public static class SsdpConstants - { + /// <summary> + /// Provides constants for common values related to the SSDP protocols. + /// </summary> + public static class SsdpConstants + { - /// <summary> - /// Multicast IP Address used for SSDP multicast messages. Values is 239.255.255.250. - /// </summary> - public const string MulticastLocalAdminAddress = "239.255.255.250"; - /// <summary> - /// The UDP port used for SSDP multicast messages. Values is 1900. - /// </summary> - public const int MulticastPort = 1900; - /// <summary> - /// The default multicase TTL for SSDP multicast messages. Value is 4. - /// </summary> - public const int SsdpDefaultMulticastTimeToLive = 4; + /// <summary> + /// Multicast IP Address used for SSDP multicast messages. Values is 239.255.255.250. + /// </summary> + public const string MulticastLocalAdminAddress = "239.255.255.250"; + /// <summary> + /// The UDP port used for SSDP multicast messages. Values is 1900. + /// </summary> + public const int MulticastPort = 1900; + /// <summary> + /// The default multicase TTL for SSDP multicast messages. Value is 4. + /// </summary> + public const int SsdpDefaultMulticastTimeToLive = 4; - internal const string MSearchMethod = "M-SEARCH"; + internal const string MSearchMethod = "M-SEARCH"; - internal const string SsdpDiscoverMessage = "ssdp:discover"; - internal const string SsdpDiscoverAllSTHeader = "ssdp:all"; + internal const string SsdpDiscoverMessage = "ssdp:discover"; + internal const string SsdpDiscoverAllSTHeader = "ssdp:all"; - internal const string SsdpDeviceDescriptionXmlNamespace = "urn:schemas-upnp-org:device-1-0"; + internal const string SsdpDeviceDescriptionXmlNamespace = "urn:schemas-upnp-org:device-1-0"; - /// <summary> - /// Default buffer size for receiving SSDP broadcasts. Value is 8192 (bytes). - /// </summary> - public const int DefaultUdpSocketBufferSize = 8192; - /// <summary> - /// The maximum possible buffer size for a UDP message. Value is 65507 (bytes). - /// </summary> - public const int MaxUdpSocketBufferSize = 65507; // Max possible UDP packet size on IPv4 without using 'jumbograms'. + /// <summary> + /// Default buffer size for receiving SSDP broadcasts. Value is 8192 (bytes). + /// </summary> + public const int DefaultUdpSocketBufferSize = 8192; + /// <summary> + /// The maximum possible buffer size for a UDP message. Value is 65507 (bytes). + /// </summary> + public const int MaxUdpSocketBufferSize = 65507; // Max possible UDP packet size on IPv4 without using 'jumbograms'. - /// <summary> - /// Namespace/prefix for UPnP device types. Values is schemas-upnp-org. - /// </summary> - public const string UpnpDeviceTypeNamespace = "schemas-upnp-org"; - /// <summary> - /// UPnP Root Device type. Value is upnp:rootdevice. - /// </summary> - public const string UpnpDeviceTypeRootDevice = "upnp:rootdevice"; - /// <summary> - /// The value is used by Windows Explorer for device searches instead of the UPNPDeviceTypeRootDevice constant. - /// Not sure why (different spec, bug, alternate protocol etc). Used to enable Windows Explorer support. - /// </summary> - public const string PnpDeviceTypeRootDevice = "pnp:rootdevice"; - /// <summary> - /// UPnP Basic Device type. Value is Basic. - /// </summary> - public const string UpnpDeviceTypeBasicDevice = "Basic"; + /// <summary> + /// Namespace/prefix for UPnP device types. Values is schemas-upnp-org. + /// </summary> + public const string UpnpDeviceTypeNamespace = "schemas-upnp-org"; + /// <summary> + /// UPnP Root Device type. Value is upnp:rootdevice. + /// </summary> + public const string UpnpDeviceTypeRootDevice = "upnp:rootdevice"; + /// <summary> + /// The value is used by Windows Explorer for device searches instead of the UPNPDeviceTypeRootDevice constant. + /// Not sure why (different spec, bug, alternate protocol etc). Used to enable Windows Explorer support. + /// </summary> + public const string PnpDeviceTypeRootDevice = "pnp:rootdevice"; + /// <summary> + /// UPnP Basic Device type. Value is Basic. + /// </summary> + public const string UpnpDeviceTypeBasicDevice = "Basic"; - internal const string SsdpKeepAliveNotification = "ssdp:alive"; - internal const string SsdpByeByeNotification = "ssdp:byebye"; + internal const string SsdpKeepAliveNotification = "ssdp:alive"; + internal const string SsdpByeByeNotification = "ssdp:byebye"; - internal const int UdpResendCount = 3; + internal const int UdpResendCount = 3; - } + } } diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs index ef6869c8b..865084d05 100644 --- a/RSSDP/SsdpDevice.cs +++ b/RSSDP/SsdpDevice.cs @@ -75,7 +75,7 @@ namespace Rssdp return rootDevice; } - + #region Public Properties #region UPnP Device Description Properties @@ -330,7 +330,7 @@ namespace Rssdp /// </summary> /// <param name="device">The <see cref="SsdpEmbeddedDevice"/> instance added to the <see cref="Devices"/> collection.</param> /// <seealso cref="AddDevice"/> - /// <seealso cref="DeviceAdded"/> + /// <seealso cref="DeviceAdded"/> protected virtual void OnDeviceAdded(SsdpEmbeddedDevice device) { var handlers = this.DeviceAdded; diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index cee9b7f91..30e1c5a0b 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -418,7 +418,7 @@ namespace Rssdp.Infrastructure var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - // If needed later for non-server devices, these headers will need to be dynamic + // If needed later for non-server devices, these headers will need to be dynamic values["HOST"] = "239.255.255.250:1900"; values["DATE"] = DateTime.UtcNow.ToString("r"); values["CACHE-CONTROL"] = "max-age = " + rootDevice.CacheLifetime.TotalSeconds; @@ -463,7 +463,7 @@ namespace Rssdp.Infrastructure var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - // If needed later for non-server devices, these headers will need to be dynamic + // If needed later for non-server devices, these headers will need to be dynamic values["HOST"] = "239.255.255.250:1900"; values["DATE"] = DateTime.UtcNow.ToString("r"); values["SERVER"] = string.Format("{0}/{1} UPnP/1.0 RSSDP/{2}", _OSName, _OSVersion, ServerVersion); @@ -538,9 +538,9 @@ namespace Rssdp.Infrastructure //According to SSDP/UPnP spec, ignore message if missing these headers. // Edit: But some devices do it anyway //if (!e.Message.Headers.Contains("MX")) - // WriteTrace("Ignoring search request - missing MX header."); + // WriteTrace("Ignoring search request - missing MX header."); //else if (!e.Message.Headers.Contains("MAN")) - // WriteTrace("Ignoring search request - missing MAN header."); + // WriteTrace("Ignoring search request - missing MAN header."); //else ProcessSearchRequest(GetFirstHeaderValue(e.Message.Headers, "MX"), GetFirstHeaderValue(e.Message.Headers, "ST"), e.ReceivedFrom, e.LocalIpAddress, CancellationToken.None); } diff --git a/RSSDP/SsdpEmbeddedDevice.cs b/RSSDP/SsdpEmbeddedDevice.cs index dca1ff5e3..0e02ce33c 100644 --- a/RSSDP/SsdpEmbeddedDevice.cs +++ b/RSSDP/SsdpEmbeddedDevice.cs @@ -4,54 +4,52 @@ using System.Text; namespace Rssdp { - /// <summary> - /// Represents a device that is a descendant of a <see cref="SsdpRootDevice"/> instance. - /// </summary> - public class SsdpEmbeddedDevice : SsdpDevice - { - - #region Fields - - private SsdpRootDevice _RootDevice; - - #endregion - - #region Constructors - - /// <summary> - /// Default constructor. - /// </summary> - public SsdpEmbeddedDevice() - { - } - - #endregion - - #region Public Properties - - /// <summary> - /// Returns the <see cref="SsdpRootDevice"/> that is this device's first ancestor. If this device is itself an <see cref="SsdpRootDevice"/>, then returns a reference to itself. - /// </summary> - public SsdpRootDevice RootDevice - { - get - { - return _RootDevice; - } - internal set - { - _RootDevice = value; - lock (this.Devices) - { - foreach (var embeddedDevice in this.Devices) - { - ((SsdpEmbeddedDevice)embeddedDevice).RootDevice = _RootDevice; - } - } - } - } - - #endregion - - } -}
\ No newline at end of file + /// <summary> + /// Represents a device that is a descendant of a <see cref="SsdpRootDevice"/> instance. + /// </summary> + public class SsdpEmbeddedDevice : SsdpDevice + { + + #region Fields + private SsdpRootDevice _RootDevice; + + #endregion + + #region Constructors + + /// <summary> + /// Default constructor. + /// </summary> + public SsdpEmbeddedDevice() + { + } + + #endregion + + #region Public Properties + + /// <summary> + /// Returns the <see cref="SsdpRootDevice"/> that is this device's first ancestor. If this device is itself an <see cref="SsdpRootDevice"/>, then returns a reference to itself. + /// </summary> + public SsdpRootDevice RootDevice + { + get + { + return _RootDevice; + } + internal set + { + _RootDevice = value; + lock (this.Devices) + { + foreach (var embeddedDevice in this.Devices) + { + ((SsdpEmbeddedDevice)embeddedDevice).RootDevice = _RootDevice; + } + } + } + } + + #endregion + } +} diff --git a/RSSDP/SsdpRootDevice.cs b/RSSDP/SsdpRootDevice.cs index eaf3f6a38..20644535a 100644 --- a/RSSDP/SsdpRootDevice.cs +++ b/RSSDP/SsdpRootDevice.cs @@ -6,74 +6,72 @@ using Rssdp.Infrastructure; namespace Rssdp { - /// <summary> - /// Represents a 'root' device, a device that has no parent. Used for publishing devices and for the root device in a tree of discovered devices. - /// </summary> - /// <remarks> - /// <para>Child (embedded) devices are represented by the <see cref="SsdpDevice"/> in the <see cref="SsdpDevice.Devices"/> property.</para> - /// <para>Root devices contain some information that applies to the whole device tree and is therefore not present on child devices, such as <see cref="CacheLifetime"/> and <see cref="Location"/>.</para> - /// </remarks> - public class SsdpRootDevice : SsdpDevice - { - - #region Fields + /// <summary> + /// Represents a 'root' device, a device that has no parent. Used for publishing devices and for the root device in a tree of discovered devices. + /// </summary> + /// <remarks> + /// <para>Child (embedded) devices are represented by the <see cref="SsdpDevice"/> in the <see cref="SsdpDevice.Devices"/> property.</para> + /// <para>Root devices contain some information that applies to the whole device tree and is therefore not present on child devices, such as <see cref="CacheLifetime"/> and <see cref="Location"/>.</para> + /// </remarks> + public class SsdpRootDevice : SsdpDevice + { + #region Fields - private Uri _UrlBase; + private Uri _UrlBase; - #endregion + #endregion - #region Constructors + #region Constructors - /// <summary> - /// Default constructor. - /// </summary> - public SsdpRootDevice() : base() - { - } + /// <summary> + /// Default constructor. + /// </summary> + public SsdpRootDevice() : base() + { + } - #endregion + #endregion - #region Public Properties + #region Public Properties - /// <summary> - /// Specifies how long clients can cache this device's details for. Optional but defaults to <see cref="TimeSpan.Zero"/> which means no-caching. Recommended value is half an hour. - /// </summary> - /// <remarks> - /// <para>Specifiy <see cref="TimeSpan.Zero"/> to indicate no caching allowed.</para> - /// <para>Also used to specify how often to rebroadcast alive notifications.</para> - /// <para>The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library.</para> - /// </remarks> - public TimeSpan CacheLifetime - { - get; set; - } + /// <summary> + /// Specifies how long clients can cache this device's details for. Optional but defaults to <see cref="TimeSpan.Zero"/> which means no-caching. Recommended value is half an hour. + /// </summary> + /// <remarks> + /// <para>Specifiy <see cref="TimeSpan.Zero"/> to indicate no caching allowed.</para> + /// <para>Also used to specify how often to rebroadcast alive notifications.</para> + /// <para>The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library.</para> + /// </remarks> + public TimeSpan CacheLifetime + { + get; set; + } - /// <summary> - /// Gets or sets the URL used to retrieve the description document for this device/tree. Required. - /// </summary> - public Uri Location { get; set; } + /// <summary> + /// Gets or sets the URL used to retrieve the description document for this device/tree. Required. + /// </summary> + public Uri Location { get; set; } - /// <summary> - /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. - /// </summary> - /// <remarks> - /// <para>Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL.</para> - /// </remarks> - public Uri UrlBase - { - get - { - return _UrlBase ?? this.Location; - } + /// <summary> + /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional. + /// </summary> + /// <remarks> + /// <para>Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL.</para> + /// </remarks> + public Uri UrlBase + { + get + { + return _UrlBase ?? this.Location; + } - set - { - _UrlBase = value; - } - } + set + { + _UrlBase = value; + } + } - #endregion - - } + #endregion + } } |
