Browse Source

add request logging

Luke Pulverenti 8 years ago
parent
commit
401a6b8f4a

+ 13 - 13
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.
 
-        /// <summary>
-        /// Used by RSSDP components to create implementations of the <see cref="IUdpSocket"/> interface, to perform platform agnostic socket communications.
-        /// </summary>
-        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
         /// <summary>
         /// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
         /// </summary>
-        /// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
         /// <returns>An implementation of the <see cref="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
-        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
             {

+ 28 - 18
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<SocketReceiveResult> ReceiveAsync()
@@ -50,18 +56,18 @@ namespace Emby.Common.Implementations.Net
             state.TaskCompletionSource = tcs;
 
 #if NETSTANDARD1_6
-            _Socket.ReceiveFromAsync(new ArraySegment<Byte>(state.Buffer),SocketFlags.None, state.EndPoint)
+            _Socket.ReceiveFromAsync(new ArraySegment<Byte>(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<bool>();
 
             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<int> receiveData)
+        private static void ProcessResponse(AsyncReceiveState state, Func<int> 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; }

+ 1 - 1
Emby.Common.Implementations/Networking/NetworkManager.cs

@@ -27,7 +27,7 @@ namespace Emby.Common.Implementations.Networking
         private List<IpAddressInfo> _localIpAddresses;
         private readonly object _localIpAddressSyncLock = new object();
 
-        public IEnumerable<IpAddressInfo> GetLocalIpAddresses()
+        public List<IpAddressInfo> GetLocalIpAddresses()
         {
             const int cacheMinutes = 5;
 

+ 2 - 2
Emby.Dlna/ConnectionManager/ControlHandler.cs

@@ -14,7 +14,7 @@ namespace Emby.Dlna.ConnectionManager
     {
         private readonly DeviceProfile _profile;
 
-        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
         {
             if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
             {
@@ -26,7 +26,7 @@ namespace Emby.Dlna.ConnectionManager
 
         private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
                 { "Source", _profile.ProtocolInfo },
                 { "Sink", "" }

+ 30 - 15
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<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> 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<string,string>(StringComparer.OrdinalIgnoreCase);
         }
 
         private IEnumerable<KeyValuePair<string, string>> 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<string, string>(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<KeyValuePair<string, string>> HandleGetSortCapabilities()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(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<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(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<KeyValuePair<string, string>> HandleGetSystemUpdateID()
         {
-            var headers = new Headers(true);
+            var headers = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
             headers.Add("Id", _systemUpdateId.ToString(_usCulture));
             return headers;
         }
 
         private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
                 { "FeatureList", GetFeatureListXml() }
             };
@@ -159,7 +162,7 @@ namespace Emby.Dlna.ContentDirectory
 
         private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
                 { "FeatureList", GetFeatureListXml() }
             };
@@ -183,12 +186,24 @@ namespace Emby.Dlna.ContentDirectory
             return builder.ToString();
         }
 
-        private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(Headers sparams, User user, string deviceId)
+        public string GetValueOrDefault(IDictionary<string, string> sparams, string key, string defaultValue)
+        {
+            string val;
+
+            if (sparams.TryGetValue(key, out val))
+            {
+                return val;
+            }
+
+            return defaultValue;
+        }
+
+        private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(IDictionary<string, string> 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<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
+        private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(IDictionary<string, string> 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
 

+ 0 - 1
Emby.Dlna/Emby.Dlna.csproj

@@ -152,7 +152,6 @@
     <Compile Include="Profiles\XboxOneProfile.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Server\DescriptionXmlBuilder.cs" />
-    <Compile Include="Server\Headers.cs" />
     <Compile Include="Server\UpnpDevice.cs" />
     <Compile Include="Service\BaseControlHandler.cs" />
     <Compile Include="Service\BaseService.cs" />

+ 4 - 2
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
                     };

+ 3 - 3
Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs

@@ -11,7 +11,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
 {
     public class ControlHandler : BaseControlHandler
     {
-        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+        protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
         {
             if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
                 return HandleIsAuthorized();
@@ -23,7 +23,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
 
         private IEnumerable<KeyValuePair<string, string>> HandleIsAuthorized()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
                 { "Result", "1" }
             };
@@ -31,7 +31,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
 
         private IEnumerable<KeyValuePair<string, string>> HandleIsValidated()
         {
-            return new Headers(true)
+            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
                 { "Result", "1" }
             };

+ 0 - 169
Emby.Dlna/Server/Headers.cs

@@ -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<string, string>
-    {
-        private readonly bool _asIs = false;
-        private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
-        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<string> Keys
-        {
-            get
-            {
-                return _dict.Keys;
-            }
-        }
-        public ICollection<string> 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<string, string> 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<string, string> item)
-        {
-            var p = new KeyValuePair<string, string>(Normalize(item.Key), item.Value);
-            return _dict.Contains(p);
-        }
-
-        public bool ContainsKey(string key)
-        {
-            return _dict.ContainsKey(Normalize(key));
-        }
-
-        public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
-        {
-            throw new NotImplementedException();
-        }
-
-        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
-        {
-            return _dict.GetEnumerator();
-        }
-
-        public bool Remove(string key)
-        {
-            return _dict.Remove(Normalize(key));
-        }
-
-        public bool Remove(KeyValuePair<string, string> 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;
-        }
-    }
-}

+ 7 - 10
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<string,string> 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<string, string> Headers = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
         }
 
-        protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams);
+        protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
 
         private void LogRequest(ControlRequest request)
         {

+ 1 - 1
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)
                 {

+ 7 - 2
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);