diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index c65593242e..1242520971 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; +using Emby.Common.Implementations.Networking; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; @@ -18,11 +19,6 @@ namespace Emby.Common.Implementations.Net // but that wasn't really the point so kept to YAGNI principal for now, even if the // interfaces are a bit ugly, specific and make assumptions. - /// - /// Used by RSSDP components to create implementations of the interface, to perform platform agnostic socket communications. - /// - private IPAddress _LocalIP; - private readonly ILogger _logger; public SocketFactory(ILogger logger) @@ -33,7 +29,6 @@ namespace Emby.Common.Implementations.Net } _logger = logger; - _LocalIP = IPAddress.Any; } public ISocket CreateSocket(IpAddressFamily family, MediaBrowser.Model.Net.SocketType socketType, MediaBrowser.Model.Net.ProtocolType protocolType, bool dualMode) @@ -66,7 +61,7 @@ namespace Emby.Common.Implementations.Net try { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - return new UdpSocket(retVal, localPort, _LocalIP); + return new UdpSocket(retVal, localPort, IPAddress.Any); } catch { @@ -80,9 +75,8 @@ namespace Emby.Common.Implementations.Net /// /// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port. /// - /// An integer specifying the local port to bind the socket to. /// An implementation of the interface used by RSSDP components to perform socket operations. - public IUdpSocket CreateSsdpUdpSocket(int localPort) + public IUdpSocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); @@ -91,8 +85,11 @@ namespace Emby.Common.Implementations.Net { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); - retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), _LocalIP)); - return new UdpSocket(retVal, localPort, _LocalIP); + + var localIp = NetworkManager.ToIPAddress(localIpAddress); + + retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp)); + return new UdpSocket(retVal, localPort, localIp); } catch { @@ -134,10 +131,13 @@ namespace Emby.Common.Implementations.Net //retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive); - retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), _LocalIP)); + + var localIp = IPAddress.Any; + + retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), localIp)); retVal.MulticastLoopback = true; - return new UdpSocket(retVal, localPort, _LocalIP); + return new UdpSocket(retVal, localPort, localIp); } catch { diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 367d2242c6..b2af9d162e 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -20,7 +20,6 @@ namespace Emby.Common.Implementations.Net private Socket _Socket; private int _LocalPort; - #endregion #region Constructors @@ -31,12 +30,19 @@ namespace Emby.Common.Implementations.Net _Socket = socket; _LocalPort = localPort; + LocalIPAddress = NetworkManager.ToIpAddressInfo(ip); _Socket.Bind(new IPEndPoint(ip, _LocalPort)); } #endregion + public IpAddressInfo LocalIPAddress + { + get; + private set; + } + #region IUdpSocket Members public Task ReceiveAsync() @@ -50,18 +56,18 @@ namespace Emby.Common.Implementations.Net state.TaskCompletionSource = tcs; #if NETSTANDARD1_6 - _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer),SocketFlags.None, state.EndPoint) + _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer),SocketFlags.None, state.RemoteEndPoint) .ContinueWith((task, asyncState) => { if (task.Status != TaskStatus.Faulted) { var receiveState = asyncState as AsyncReceiveState; - receiveState.EndPoint = task.Result.RemoteEndPoint; - ProcessResponse(receiveState, () => task.Result.ReceivedBytes); + receiveState.RemoteEndPoint = task.Result.RemoteEndPoint; + ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress); } }, state); #else - _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.EndPoint, ProcessResponse, state); + _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); #endif return tcs.Task; @@ -74,6 +80,8 @@ namespace Emby.Common.Implementations.Net if (buffer == null) throw new ArgumentNullException("messageData"); if (endPoint == null) throw new ArgumentNullException("endPoint"); + var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); + #if NETSTANDARD1_6 if (size != buffer.Length) @@ -83,14 +91,14 @@ namespace Emby.Common.Implementations.Net buffer = copy; } - _Socket.SendTo(buffer, new IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port)); + _Socket.SendTo(buffer, ipEndPoint); return Task.FromResult(true); #else var taskSource = new TaskCompletionSource(); try { - _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IpAddress.ToString()), endPoint.Port), result => + _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result => { try { @@ -109,7 +117,7 @@ namespace Emby.Common.Implementations.Net taskSource.TrySetException(ex); } - //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(endPoint.IPAddress), endPoint.Port)); + //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); return taskSource.Task; #endif @@ -133,19 +141,20 @@ namespace Emby.Common.Implementations.Net #region Private Methods - private static void ProcessResponse(AsyncReceiveState state, Func receiveData) + private static void ProcessResponse(AsyncReceiveState state, Func receiveData, IpAddressInfo localIpAddress) { try { var bytesRead = receiveData(); - var ipEndPoint = state.EndPoint as IPEndPoint; + var ipEndPoint = state.RemoteEndPoint as IPEndPoint; state.TaskCompletionSource.SetResult( - new SocketReceiveResult() + new SocketReceiveResult { Buffer = state.Buffer, ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint) + RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), + LocalIPAddress = localIpAddress } ); } @@ -182,15 +191,16 @@ namespace Emby.Common.Implementations.Net var state = asyncResult.AsyncState as AsyncReceiveState; try { - var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.EndPoint); + var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); - var ipEndPoint = state.EndPoint as IPEndPoint; + var ipEndPoint = state.RemoteEndPoint as IPEndPoint; state.TaskCompletionSource.SetResult( new SocketReceiveResult { Buffer = state.Buffer, ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint) + RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), + LocalIPAddress = LocalIPAddress } ); } @@ -211,13 +221,13 @@ namespace Emby.Common.Implementations.Net private class AsyncReceiveState { - public AsyncReceiveState(Socket socket, EndPoint endPoint) + public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint) { this.Socket = socket; - this.EndPoint = endPoint; + this.RemoteEndPoint = remoteEndPoint; } - public EndPoint EndPoint; + public EndPoint RemoteEndPoint; public byte[] Buffer = new byte[8192]; public Socket Socket { get; private set; } diff --git a/Emby.Common.Implementations/Networking/NetworkManager.cs b/Emby.Common.Implementations/Networking/NetworkManager.cs index a4f8f7ced0..b9100f9db3 100644 --- a/Emby.Common.Implementations/Networking/NetworkManager.cs +++ b/Emby.Common.Implementations/Networking/NetworkManager.cs @@ -27,7 +27,7 @@ namespace Emby.Common.Implementations.Networking private List _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); - public IEnumerable GetLocalIpAddresses() + public List GetLocalIpAddresses() { const int cacheMinutes = 5; diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs index 0bc44db170..ae983c5e75 100644 --- a/Emby.Dlna/ConnectionManager/ControlHandler.cs +++ b/Emby.Dlna/ConnectionManager/ControlHandler.cs @@ -14,7 +14,7 @@ namespace Emby.Dlna.ConnectionManager { private readonly DeviceProfile _profile; - protected override IEnumerable> GetResult(string methodName, Headers methodParams) + protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) { if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase)) { @@ -26,7 +26,7 @@ namespace Emby.Dlna.ConnectionManager private IEnumerable> HandleGetProtocolInfo() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Source", _profile.ProtocolInfo }, { "Sink", "" } diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index d77919e47a..98a151f295 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -65,7 +65,7 @@ namespace Emby.Dlna.ContentDirectory _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, mediaEncoder); } - protected override IEnumerable> GetResult(string methodName, Headers methodParams) + protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) { var deviceId = "test"; @@ -118,17 +118,20 @@ namespace Emby.Dlna.ContentDirectory _userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed, CancellationToken.None); - return new Headers(); + return new Dictionary(StringComparer.OrdinalIgnoreCase); } private IEnumerable> HandleGetSearchCapabilities() { - return new Headers(true) { { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } }; + return new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } + }; } private IEnumerable> HandleGetSortCapabilities() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" } }; @@ -136,7 +139,7 @@ namespace Emby.Dlna.ContentDirectory private IEnumerable> HandleGetSortExtensionCapabilities() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" } }; @@ -144,14 +147,14 @@ namespace Emby.Dlna.ContentDirectory private IEnumerable> HandleGetSystemUpdateID() { - var headers = new Headers(true); + var headers = new Dictionary(StringComparer.OrdinalIgnoreCase); headers.Add("Id", _systemUpdateId.ToString(_usCulture)); return headers; } private IEnumerable> HandleGetFeatureList() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "FeatureList", GetFeatureListXml() } }; @@ -159,7 +162,7 @@ namespace Emby.Dlna.ContentDirectory private IEnumerable> HandleXGetFeatureList() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "FeatureList", GetFeatureListXml() } }; @@ -183,12 +186,24 @@ namespace Emby.Dlna.ContentDirectory return builder.ToString(); } - private async Task>> HandleBrowse(Headers sparams, User user, string deviceId) + public string GetValueOrDefault(IDictionary sparams, string key, string defaultValue) + { + string val; + + if (sparams.TryGetValue(key, out val)) + { + return val; + } + + return defaultValue; + } + + private async Task>> HandleBrowse(IDictionary sparams, User user, string deviceId) { var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; - var filter = new Filter(sparams.GetValueOrDefault("Filter", "*")); - var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", "")); + var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); + var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); var provided = 0; @@ -294,11 +309,11 @@ namespace Emby.Dlna.ContentDirectory }; } - private async Task>> HandleSearch(Headers sparams, User user, string deviceId) + private async Task>> HandleSearch(IDictionary sparams, User user, string deviceId) { - var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", "")); - var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", "")); - var filter = new Filter(sparams.GetValueOrDefault("Filter", "*")); + var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", "")); + var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", "")); + var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*")); // sort example: dc:title, dc:date diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 0441cb3be0..4d1aacfec5 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -152,7 +152,6 @@ - diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 170b4cee0e..858b1ae9ec 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -54,6 +54,7 @@ namespace Emby.Dlna.Main private readonly ITimerFactory _timerFactory; private readonly ISocketFactory _socketFactory; private readonly IEnvironmentInfo _environmentInfo; + private readonly INetworkManager _networkManager; private ISsdpCommunicationsServer _communicationsServer; @@ -69,7 +70,7 @@ namespace Emby.Dlna.Main IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, - IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo) + IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, INetworkManager networkManager) { _config = config; _appHost = appHost; @@ -87,6 +88,7 @@ namespace Emby.Dlna.Main _socketFactory = socketFactory; _timerFactory = timerFactory; _environmentInfo = environmentInfo; + _networkManager = networkManager; _logger = logManager.GetLogger("Dlna"); } @@ -156,7 +158,7 @@ namespace Emby.Dlna.Main { if (_communicationsServer == null) { - _communicationsServer = new SsdpCommunicationsServer(_socketFactory) + _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger) { IsShared = true }; diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs index 5e232aeaca..daf46b1061 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs @@ -11,7 +11,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar { public class ControlHandler : BaseControlHandler { - protected override IEnumerable> GetResult(string methodName, Headers methodParams) + protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) { if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase)) return HandleIsAuthorized(); @@ -23,7 +23,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar private IEnumerable> HandleIsAuthorized() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Result", "1" } }; @@ -31,7 +31,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar private IEnumerable> HandleIsValidated() { - return new Headers(true) + return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "Result", "1" } }; diff --git a/Emby.Dlna/Server/Headers.cs b/Emby.Dlna/Server/Headers.cs deleted file mode 100644 index 47dd8e3215..0000000000 --- a/Emby.Dlna/Server/Headers.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -namespace Emby.Dlna.Server -{ - public class Headers : IDictionary - { - private readonly bool _asIs = false; - private readonly Dictionary _dict = new Dictionary(); - private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.IgnoreCase); - - public Headers(bool asIs) - { - _asIs = asIs; - } - - public Headers() - : this(asIs: false) - { - } - - public int Count - { - get - { - return _dict.Count; - } - } - public string HeaderBlock - { - get - { - var hb = new StringBuilder(); - foreach (var h in this) - { - hb.AppendFormat("{0}: {1}\r\n", h.Key, h.Value); - } - return hb.ToString(); - } - } - public bool IsReadOnly - { - get - { - return false; - } - } - public ICollection Keys - { - get - { - return _dict.Keys; - } - } - public ICollection Values - { - get - { - return _dict.Values; - } - } - - - public string this[string key] - { - get - { - return _dict[Normalize(key)]; - } - set - { - _dict[Normalize(key)] = value; - } - } - - - private string Normalize(string header) - { - if (!_asIs) - { - header = header.ToLower(); - } - header = header.Trim(); - if (!Validator.IsMatch(header)) - { - throw new ArgumentException("Invalid header: " + header); - } - return header; - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return _dict.GetEnumerator(); - } - - public void Add(KeyValuePair item) - { - Add(item.Key, item.Value); - } - - public void Add(string key, string value) - { - _dict.Add(Normalize(key), value); - } - - public void Clear() - { - _dict.Clear(); - } - - public bool Contains(KeyValuePair item) - { - var p = new KeyValuePair(Normalize(item.Key), item.Value); - return _dict.Contains(p); - } - - public bool ContainsKey(string key) - { - return _dict.ContainsKey(Normalize(key)); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - public IEnumerator> GetEnumerator() - { - return _dict.GetEnumerator(); - } - - public bool Remove(string key) - { - return _dict.Remove(Normalize(key)); - } - - public bool Remove(KeyValuePair item) - { - return Remove(item.Key); - } - - public override string ToString() - { - return string.Format("({0})", string.Join(", ", (from x in _dict - select string.Format("{0}={1}", x.Key, x.Value)))); - } - - public bool TryGetValue(string key, out string value) - { - return _dict.TryGetValue(Normalize(key), out value); - } - - public string GetValueOrDefault(string key, string defaultValue) - { - string val; - - if (TryGetValue(key, out val)) - { - return val; - } - - return defaultValue; - } - } -} diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 4ce047172e..3092589c12 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text; using System.Xml; using Emby.Dlna.Didl; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Xml; namespace Emby.Dlna.Service @@ -185,8 +186,7 @@ namespace Emby.Dlna.Service { using (var subReader = reader.ReadSubtree()) { - result.Headers = ParseFirstBodyChild(subReader); - + ParseFirstBodyChild(subReader, result.Headers); return result; } } @@ -204,10 +204,8 @@ namespace Emby.Dlna.Service return result; } - private Headers ParseFirstBodyChild(XmlReader reader) + private void ParseFirstBodyChild(XmlReader reader, IDictionary headers) { - var result = new Headers(); - reader.MoveToContent(); reader.Read(); @@ -216,25 +214,24 @@ namespace Emby.Dlna.Service { if (reader.NodeType == XmlNodeType.Element) { - result.Add(reader.LocalName, reader.ReadElementContentAsString()); + // TODO: Should we be doing this here, or should it be handled earlier when decoding the request? + headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString(); } else { reader.Read(); } } - - return result; } private class ControlRequestInfo { public string LocalName; public string NamespaceURI; - public Headers Headers = new Headers(); + public IDictionary Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); } - protected abstract IEnumerable> GetResult(string methodName, Headers methodParams); + protected abstract IEnumerable> GetResult(string methodName, IDictionary methodParams); private void LogRequest(ControlRequest request) { diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 8a5ae2c3a5..0e1f5a5517 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -518,7 +518,7 @@ namespace Emby.Server.Implementations.HttpServer return; } - var handler = HttpHandlerFactory.GetHandler(httpReq); + var handler = HttpHandlerFactory.GetHandler(httpReq, _logger); if (handler != null) { diff --git a/ServiceStack/HttpHandlerFactory.cs b/ServiceStack/HttpHandlerFactory.cs index d48bfeb5f8..5f4892d51c 100644 --- a/ServiceStack/HttpHandlerFactory.cs +++ b/ServiceStack/HttpHandlerFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; using ServiceStack.Host; @@ -9,12 +10,16 @@ namespace ServiceStack public class HttpHandlerFactory { // Entry point for HttpListener - public static RestHandler GetHandler(IHttpRequest httpReq) + public static RestHandler GetHandler(IHttpRequest httpReq, ILogger logger) { var pathInfo = httpReq.PathInfo; var pathParts = pathInfo.TrimStart('/').Split('/'); - if (pathParts.Length == 0) return null; + if (pathParts.Length == 0) + { + logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl); + return null; + } string contentType; var restPath = RestHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, out contentType);