Browse Source

Merge pull request #2965 from MediaBrowser/dev

Dev
Luke 7 years ago
parent
commit
9b383f6b1a
44 changed files with 768 additions and 745 deletions
  1. 22 21
      Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
  2. 4 1
      Emby.Dlna/Eventing/EventManager.cs
  3. 17 10
      Emby.Dlna/PlayTo/SsdpHttpClient.cs
  4. 0 62
      Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
  5. 2 2
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  6. 3 0
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  7. 62 53
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  8. 22 16
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  9. 9 6
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs
  10. 5 0
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  11. 8 5
      Emby.Server.Implementations/News/NewsEntryPoint.cs
  12. 6 3
      Emby.Server.Implementations/Security/PluginSecurityManager.cs
  13. 6 3
      Emby.Server.Implementations/Session/HttpSessionController.cs
  14. 15 4
      Emby.Server.Implementations/Updates/InstallationManager.cs
  15. 14 13
      MediaBrowser.Api/Images/RemoteImageService.cs
  16. 0 47
      MediaBrowser.Common/Net/IHttpClient.cs
  17. 16 10
      MediaBrowser.Common/Updates/GithubUpdater.cs
  18. 10 4
      MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
  19. 5 5
      MediaBrowser.Providers/Manager/ProviderManager.cs
  20. 7 4
      MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
  21. 19 10
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  22. 50 44
      MediaBrowser.Providers/Movies/MovieDbSearch.cs
  23. 7 4
      MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
  24. 9 6
      MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
  25. 7 4
      MediaBrowser.Providers/Music/FanArtArtistProvider.cs
  26. 89 71
      MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
  27. 18 10
      MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
  28. 53 50
      MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
  29. 17 11
      MediaBrowser.Providers/Omdb/OmdbProvider.cs
  30. 15 9
      MediaBrowser.Providers/People/MovieDbPersonProvider.cs
  31. 7 4
      MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
  32. 5 2
      MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
  33. 5 2
      MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
  34. 33 24
      MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
  35. 17 10
      MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
  36. 61 52
      MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
  37. 1 0
      Mono.Nat/ISearcher.cs
  38. 0 1
      Mono.Nat/Mono.Nat.csproj
  39. 2 2
      Mono.Nat/NatUtility.cs
  40. 26 26
      Mono.Nat/Pmp/Searchers/PmpSearcher.cs
  41. 4 1
      Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
  42. 0 95
      Mono.Nat/Upnp/Upnp.cs
  43. 89 37
      Mono.Nat/Upnp/UpnpNatDevice.cs
  44. 1 1
      SharedVersion.cs

+ 22 - 21
Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs

@@ -43,37 +43,38 @@ namespace Emby.Dlna.ContentDirectory
 
             options.RequestContent = GetRequestBody(request);
 
-            var response = await _httpClient.SendAsync(options, "POST");
-
-            using (var reader = new StreamReader(response.Content))
+            using (var response = await _httpClient.SendAsync(options, "POST"))
             {
-                var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                using (var reader = new StreamReader(response.Content))
+                {
+                    var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
 
-                var queryResult = new QueryResult<ChannelItemInfo>();
+                    var queryResult = new QueryResult<ChannelItemInfo>();
 
-                if (doc.Document == null)
-                    return queryResult;
+                    if (doc.Document == null)
+                        return queryResult;
 
-                var responseElement = doc.Document.Descendants(UNamespace + "BrowseResponse").ToList();
+                    var responseElement = doc.Document.Descendants(UNamespace + "BrowseResponse").ToList();
 
-                var countElement = responseElement.Select(i => i.Element("TotalMatches")).FirstOrDefault(i => i != null);
-                var countValue = countElement == null ? null : countElement.Value;
+                    var countElement = responseElement.Select(i => i.Element("TotalMatches")).FirstOrDefault(i => i != null);
+                    var countValue = countElement == null ? null : countElement.Value;
 
-                int count;
-                if (!string.IsNullOrWhiteSpace(countValue) && int.TryParse(countValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
-                {
-                    queryResult.TotalRecordCount = count;
+                    int count;
+                    if (!string.IsNullOrWhiteSpace(countValue) && int.TryParse(countValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
+                    {
+                        queryResult.TotalRecordCount = count;
 
-                    var resultElement = responseElement.Select(i => i.Element("Result")).FirstOrDefault(i => i != null);
-                    var resultString = (string)resultElement;
+                        var resultElement = responseElement.Select(i => i.Element("Result")).FirstOrDefault(i => i != null);
+                        var resultString = (string)resultElement;
 
-                    if (resultElement != null)
-                    {
-                        var xElement = XElement.Parse(resultString);
+                        if (resultElement != null)
+                        {
+                            var xElement = XElement.Parse(resultString);
+                        }
                     }
-                }
 
-                return queryResult;
+                    return queryResult;
+                }
             }
         }
 

+ 4 - 1
Emby.Dlna/Eventing/EventManager.cs

@@ -182,7 +182,10 @@ namespace Emby.Dlna.Eventing
 
             try
             {
-                await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false);
+                using (await _httpClient.SendAsync(options, "NOTIFY").ConfigureAwait(false))
+                {
+
+                }
             }
             catch (OperationCanceledException)
             {

+ 17 - 10
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -31,14 +31,15 @@ namespace Emby.Dlna.PlayTo
             bool logRequest = true,
             string header = null)
         {
-            var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest)
-                .ConfigureAwait(false);
-
-            using (var stream = response.Content)
+            using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest)
+                .ConfigureAwait(false))
             {
-                using (var reader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                    using (var reader = new StreamReader(stream, Encoding.UTF8))
+                    {
+                        return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                    }
                 }
             }
         }
@@ -79,7 +80,10 @@ namespace Emby.Dlna.PlayTo
             options.RequestHeaders["NT"] = "upnp:event";
             options.RequestHeaders["TIMEOUT"] = "Second-" + timeOut.ToString(_usCulture);
 
-            await _httpClient.SendAsync(options, "SUBSCRIBE").ConfigureAwait(false);
+            using (await _httpClient.SendAsync(options, "SUBSCRIBE").ConfigureAwait(false))
+            {
+
+            }
         }
 
         public async Task<XDocument> GetDataAsync(string url)
@@ -94,11 +98,14 @@ namespace Emby.Dlna.PlayTo
 
             options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
 
-            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
             {
-                using (var reader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                    using (var reader = new StreamReader(stream, Encoding.UTF8))
+                    {
+                        return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
+                    }
                 }
             }
         }

+ 0 - 62
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -279,39 +279,9 @@ namespace Emby.Server.Implementations.HttpClientManager
         public async Task<Stream> Get(HttpRequestOptions options)
         {
             var response = await GetResponse(options).ConfigureAwait(false);
-
             return response.Content;
         }
 
-        /// <summary>
-        /// Performs a GET request and returns the resulting stream
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="resourcePool">The resource pool.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        public Task<Stream> Get(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
-        {
-            return Get(new HttpRequestOptions
-            {
-                Url = url,
-                ResourcePool = resourcePool,
-                CancellationToken = cancellationToken,
-                BufferContent = resourcePool != null
-            });
-        }
-
-        /// <summary>
-        /// Gets the specified URL.
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        public Task<Stream> Get(string url, CancellationToken cancellationToken)
-        {
-            return Get(url, null, cancellationToken);
-        }
-
         /// <summary>
         /// send as an asynchronous operation.
         /// </summary>
@@ -589,26 +559,6 @@ namespace Emby.Server.Implementations.HttpClientManager
             return response.Content;
         }
 
-        /// <summary>
-        /// Performs a POST request
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="postData">Params to add to the POST data.</param>
-        /// <param name="resourcePool">The resource pool.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>stream on success, null on failure</returns>
-        public Task<Stream> Post(string url, Dictionary<string, string> postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken)
-        {
-            return Post(new HttpRequestOptions
-            {
-                Url = url,
-                ResourcePool = resourcePool,
-                CancellationToken = cancellationToken,
-                BufferContent = resourcePool != null
-
-            }, postData);
-        }
-
         /// <summary>
         /// Downloads the contents of a given url into a temporary location
         /// </summary>
@@ -891,18 +841,6 @@ namespace Emby.Server.Implementations.HttpClientManager
             }
         }
 
-        /// <summary>
-        /// Posts the specified URL.
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="postData">The post data.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        public Task<Stream> Post(string url, Dictionary<string, string> postData, CancellationToken cancellationToken)
-        {
-            return Post(url, postData, null, cancellationToken);
-        }
-
         private Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
         {
             var taskCompletion = new TaskCompletionSource<WebResponse>();

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -55,10 +55,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 if (season.IndexNumber.HasValue)
                 {
                     var seasonNumber = season.IndexNumber.Value;
-                    
+
                     season.Name = seasonNumber == 0 ?
                         args.LibraryOptions.SeasonZeroDisplayName :
-                        string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture));
+                        string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture), args.GetLibraryOptions().PreferredMetadataLanguage);
                 }
 
                 return season;

+ 3 - 0
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1463,6 +1463,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     _timerProvider.AddOrUpdate(timer, false);
 
                     await SaveRecordingMetadata(timer, recordPath, seriesPath).ConfigureAwait(false);
+
+                    CreateRecordingFolders();
+
                     TriggerRefresh(recordPath);
                     EnforceKeepUpTo(timer, seriesPath);
                 };

+ 62 - 53
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -541,27 +541,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             try
             {
-                using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
+                using (var httpResponse = await Get(options, false, info).ConfigureAwait(false))
                 {
-                    var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
-
-                    if (root != null)
+                    using (Stream responce = httpResponse.Content)
                     {
-                        foreach (ScheduleDirect.Headends headend in root)
+                        var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
+
+                        if (root != null)
                         {
-                            foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+                            foreach (ScheduleDirect.Headends headend in root)
                             {
-                                lineups.Add(new NameIdPair
+                                foreach (ScheduleDirect.Lineup lineup in headend.lineups)
                                 {
-                                    Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
-                                    Id = lineup.uri.Substring(18)
-                                });
+                                    lineups.Add(new NameIdPair
+                                    {
+                                        Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
+                                        Id = lineup.uri.Substring(18)
+                                    });
+                                }
                             }
                         }
-                    }
-                    else
-                    {
-                        _logger.Info("No lineups available");
+                        else
+                        {
+                            _logger.Info("No lineups available");
+                        }
                     }
                 }
             }
@@ -671,13 +674,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             return await Post(options, false, providerInfo).ConfigureAwait(false);
         }
 
-        private async Task<Stream> Get(HttpRequestOptions options,
+        private async Task<HttpResponseInfo> Get(HttpRequestOptions options,
             bool enableRetry,
             ListingsProviderInfo providerInfo)
         {
             try
             {
-                return await _httpClient.Get(options).ConfigureAwait(false);
+                return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
             }
             catch (HttpException ex)
             {
@@ -797,11 +800,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             try
             {
-                using (var response = await Get(options, false, null).ConfigureAwait(false))
+                using (var httpResponse = await Get(options, false, null).ConfigureAwait(false))
                 {
-                    var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+                    using (var response = httpResponse.Content)
+                    {
+                        var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
 
-                    return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+                        return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+                    }
                 }
             }
             catch (HttpException ex)
@@ -879,53 +885,56 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             var list = new List<ChannelInfo>();
 
-            using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
+            using (var httpResponse = await Get(httpOptions, true, info).ConfigureAwait(false))
             {
-                var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
-                _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
-                _logger.Info("Mapping Stations to Channel");
-
-                var allStations = root.stations ?? new List<ScheduleDirect.Station>();
-
-                foreach (ScheduleDirect.Map map in root.map)
+                using (var response = httpResponse.Content)
                 {
-                    var channelNumber = GetChannelNumber(map);
-
-                    var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
-                    if (station == null)
-                    {
-                        station = new ScheduleDirect.Station
-                        {
-                            stationID = map.stationID
-                        };
-                    }
+                    var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
+                    _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
+                    _logger.Info("Mapping Stations to Channel");
 
-                    var name = channelNumber;
+                    var allStations = root.stations ?? new List<ScheduleDirect.Station>();
 
-                    var channelInfo = new ChannelInfo
+                    foreach (ScheduleDirect.Map map in root.map)
                     {
-                        Number = channelNumber,
-                        Name = name
-                    };
+                        var channelNumber = GetChannelNumber(map);
 
-                    if (station != null)
-                    {
-                        if (!string.IsNullOrWhiteSpace(station.name))
+                        var station = allStations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
+                        if (station == null)
                         {
-                            channelInfo.Name = station.name;
+                            station = new ScheduleDirect.Station
+                            {
+                                stationID = map.stationID
+                            };
                         }
 
-                        channelInfo.Id = station.stationID;
-                        channelInfo.CallSign = station.callsign;
+                        var name = channelNumber;
 
-                        if (station.logo != null)
+                        var channelInfo = new ChannelInfo
                         {
-                            channelInfo.ImageUrl = station.logo.URL;
-                            channelInfo.HasImage = true;
+                            Number = channelNumber,
+                            Name = name
+                        };
+
+                        if (station != null)
+                        {
+                            if (!string.IsNullOrWhiteSpace(station.name))
+                            {
+                                channelInfo.Name = station.name;
+                            }
+
+                            channelInfo.Id = station.stationID;
+                            channelInfo.CallSign = station.callsign;
+
+                            if (station.logo != null)
+                            {
+                                channelInfo.ImageUrl = station.logo.URL;
+                                channelInfo.HasImage = true;
+                            }
                         }
-                    }
 
-                    list.Add(channelInfo);
+                        list.Add(channelInfo);
+                    }
                 }
             }
 

+ 22 - 16
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -86,16 +86,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 CancellationToken = cancellationToken,
                 BufferContent = false
             };
-            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
             {
-                var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
-
-                if (info.ImportFavoritesOnly)
+                using (var stream = response.Content)
                 {
-                    lineup = lineup.Where(i => i.Favorite).ToList();
-                }
+                    var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
+
+                    if (info.ImportFavoritesOnly)
+                    {
+                        lineup = lineup.Where(i => i.Favorite).ToList();
+                    }
 
-                return lineup.Where(i => !i.DRM).ToList();
+                    return lineup.Where(i => !i.DRM).ToList();
+                }
             }
         }
 
@@ -143,26 +146,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             try
             {
-                using (var stream = await _httpClient.Get(new HttpRequestOptions()
+                using (var response = await _httpClient.SendAsync(new HttpRequestOptions()
                 {
                     Url = string.Format("{0}/discover.json", GetApiUrl(info, false)),
                     CancellationToken = cancellationToken,
                     TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
                     BufferContent = false
 
-                }).ConfigureAwait(false))
+                }, "GET").ConfigureAwait(false))
                 {
-                    var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
-
-                    if (!string.IsNullOrWhiteSpace(info.Id))
+                    using (var stream = response.Content)
                     {
-                        lock (_modelCache)
+                        var discoverResponse = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
+
+                        if (!string.IsNullOrWhiteSpace(info.Id))
                         {
-                            _modelCache[info.Id] = response;
+                            lock (_modelCache)
+                            {
+                                _modelCache[info.Id] = discoverResponse;
+                            }
                         }
-                    }
 
-                    return response;
+                        return discoverResponse;
+                    }
                 }
             }
             catch (HttpException ex)

+ 9 - 6
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs

@@ -92,14 +92,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
                     }, "GET").ConfigureAwait(false))
                     {
-                        Logger.Info("Opened HDHR stream from {0}", url);
+                        using (var stream = response.Content)
+                        {
+                            Logger.Info("Opened HDHR stream from {0}", url);
 
-                        Logger.Info("Beginning multicastStream.CopyUntilCancelled");
+                            Logger.Info("Beginning multicastStream.CopyUntilCancelled");
 
-                        FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
-                        using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
-                        {
-                            StreamHelper.CopyTo(response.Content, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
+                            FileSystem.CreateDirectory(FileSystem.GetDirectoryName(TempFilePath));
+                            using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None))
+                            {
+                                StreamHelper.CopyTo(stream, fileStream, 81920, () => Resolve(openTaskCompletionSource), cancellationToken);
+                            }
                         }
                     }
                 }

+ 5 - 0
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -315,6 +315,11 @@ namespace Emby.Server.Implementations.Localization
 
         public string GetLocalizedString(string phrase, string culture)
         {
+            if (string.IsNullOrWhiteSpace(culture))
+            {
+                culture = _configurationManager.Configuration.UICulture;
+            }
+
             var dictionary = GetLocalizationDictionary(culture);
 
             string value;

+ 8 - 5
Emby.Server.Implementations/News/NewsEntryPoint.cs

@@ -88,15 +88,18 @@ namespace Emby.Server.Implementations.News
                 BufferContent = false
             };
 
-            using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(requestOptions, "GET").ConfigureAwait(false))
             {
-                using (var reader = XmlReader.Create(stream))
+                using (var stream = response.Content)
                 {
-                    var news = ParseRssItems(reader).ToList();
+                    using (var reader = XmlReader.Create(stream))
+                    {
+                        var news = ParseRssItems(reader).ToList();
 
-                    _json.SerializeToFile(news, path);
+                        _json.SerializeToFile(news, path);
 
-                    await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
+                        await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
+                    }
                 }
             }
         }

+ 6 - 3
Emby.Server.Implementations/Security/PluginSecurityManager.cs

@@ -293,10 +293,13 @@ namespace Emby.Server.Implementations.Security
 
                     options.SetPostData(data);
 
-                    using (var json = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
+                    using (var response = (await _httpClient.Post(options).ConfigureAwait(false)))
                     {
-                        reg = _jsonSerializer.DeserializeFromStream<RegRecord>(json);
-                        success = true;
+                        using (var json = response.Content)
+                        {
+                            reg = _jsonSerializer.DeserializeFromStream<RegRecord>(json);
+                            success = true;
+                        }
                     }
 
                     if (reg.registered)

+ 6 - 3
Emby.Server.Implementations/Session/HttpSessionController.cs

@@ -66,19 +66,22 @@ namespace Emby.Server.Implementations.Session
             return SendMessage(name, new Dictionary<string, string>(), cancellationToken);
         }
 
-        private Task SendMessage(string name,
+        private async Task SendMessage(string name,
             Dictionary<string, string> args,
             CancellationToken cancellationToken)
         {
             var url = PostUrl + "/" + name + ToQueryString(args);
 
-            return _httpClient.Post(new HttpRequestOptions
+            using ((await _httpClient.Post(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = false
 
-            });
+            }).ConfigureAwait(false)))
+            {
+
+            }
         }
 
         public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)

+ 15 - 4
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -175,13 +175,24 @@ namespace Emby.Server.Implementations.Updates
                     { "systemid", _applicationHost.SystemId }
                 };
 
-                using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall?includeAllRuntimes=true", data, cancellationToken).ConfigureAwait(false))
+                var options = new HttpRequestOptions
                 {
-                    cancellationToken.ThrowIfCancellationRequested();
+                    Url = "https://www.mb3admin.com/admin/service/package/retrieveall?includeAllRuntimes=true",
+                    CancellationToken = cancellationToken
+                };
+
+                options.SetPostData(data);
+
+                using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
+                {
+                    using (var json = response.Content)
+                    {
+                        cancellationToken.ThrowIfCancellationRequested();
 
-                    var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(json);
+                        var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(json);
 
-                    return FilterPackages(packages, packageType, applicationVersion);
+                        return FilterPackages(packages, packageType, applicationVersion);
+                    }
                 }
             }
             else

+ 14 - 13
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -262,28 +262,29 @@ namespace MediaBrowser.Api.Images
         /// <returns>Task.</returns>
         private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
         {
-            var result = await _httpClient.GetResponse(new HttpRequestOptions
+            using (var result = await _httpClient.GetResponse(new HttpRequestOptions
             {
                 Url = url,
                 BufferContent = false
 
-            }).ConfigureAwait(false);
-
-            var ext = result.ContentType.Split('/').Last();
+            }).ConfigureAwait(false))
+            {
+                var ext = result.ContentType.Split('/').Last();
 
-            var fullCachePath = GetFullCachePath(urlHash + "." + ext);
+                var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 
-            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fullCachePath));
-            using (var stream = result.Content)
-            {
-                using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fullCachePath));
+                using (var stream = result.Content)
                 {
-                    await stream.CopyToAsync(filestream).ConfigureAwait(false);
+                    using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    {
+                        await stream.CopyToAsync(filestream).ConfigureAwait(false);
+                    }
                 }
-            }
 
-            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(pointerCachePath));
-            _fileSystem.WriteAllText(pointerCachePath, fullCachePath);
+                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(pointerCachePath));
+                _fileSystem.WriteAllText(pointerCachePath, fullCachePath);
+            }
         }
 
         /// <summary>

+ 0 - 47
MediaBrowser.Common/Net/IHttpClient.cs

@@ -18,24 +18,6 @@ namespace MediaBrowser.Common.Net
         /// <returns>Task{HttpResponseInfo}.</returns>
         Task<HttpResponseInfo> GetResponse(HttpRequestOptions options);
 
-        /// <summary>
-        /// Performs a GET request and returns the resulting stream
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="resourcePool">The resource pool.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
-        Task<Stream> Get(string url, SemaphoreSlim resourcePool, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Gets the specified URL.
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        Task<Stream> Get(string url, CancellationToken cancellationToken);
-
         /// <summary>
         /// Gets the specified options.
         /// </summary>
@@ -51,35 +33,6 @@ namespace MediaBrowser.Common.Net
         /// <returns>Task{HttpResponseInfo}.</returns>
         Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
 
-        /// <summary>
-        /// Performs a POST request
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="postData">Params to add to the POST data.</param>
-        /// <param name="resourcePool">The resource pool.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>stream on success, null on failure</returns>
-        /// <exception cref="System.ArgumentNullException">postData</exception>
-        /// <exception cref="MediaBrowser.Model.Net.HttpException"></exception>
-        Task<Stream> Post(string url, Dictionary<string, string> postData, SemaphoreSlim resourcePool, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Posts the specified URL.
-        /// </summary>
-        /// <param name="url">The URL.</param>
-        /// <param name="postData">The post data.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{Stream}.</returns>
-        Task<Stream> Post(string url, Dictionary<string, string> postData, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Posts the specified options with post data
-        /// </summary>
-        /// <param name="options">The options</param>
-        /// <param name="postData">The post data</param>
-        /// <returns>Task{Stream}</returns>
-        Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData);
-
         /// <summary>
         /// Posts the specified options.
         /// </summary>

+ 16 - 10
MediaBrowser.Common/Updates/GithubUpdater.cs

@@ -40,11 +40,14 @@ namespace MediaBrowser.Common.Updates
                 options.CacheLength = cacheLength;
             }
 
-            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
             {
-                var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+                using (var stream = response.Content)
+                {
+                    var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
 
-                return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+                    return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+                }
             }
         }
 
@@ -110,17 +113,20 @@ namespace MediaBrowser.Common.Updates
                 BufferContent = false
             };
 
-            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
             {
-                var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+                using (var stream = response.Content)
+                {
+                    var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
 
-                obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray();
+                    obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray();
 
-                list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
-                list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
-                list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Dev)).OrderByDescending(GetVersion).Take(1));
+                    list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
+                    list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
+                    list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Dev)).OrderByDescending(GetVersion).Take(1));
 
-                return list;
+                    return list;
+                }
             }
         }
 

+ 10 - 4
MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs

@@ -179,7 +179,7 @@ namespace MediaBrowser.Providers.BoxSets
 
             RootObject mainResult = null;
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -187,7 +187,10 @@ namespace MediaBrowser.Providers.BoxSets
 
             }).ConfigureAwait(false))
             {
-                mainResult = _json.DeserializeFromStream<RootObject>(json);
+                using (var json = response.Content)
+                {
+                    mainResult = _json.DeserializeFromStream<RootObject>(json);
+                }
             }
 
             cancellationToken.ThrowIfCancellationRequested();
@@ -204,7 +207,7 @@ namespace MediaBrowser.Providers.BoxSets
                         url += "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language);
                     }
 
-                    using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+                    using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
                     {
                         Url = url,
                         CancellationToken = cancellationToken,
@@ -212,7 +215,10 @@ namespace MediaBrowser.Providers.BoxSets
 
                     }).ConfigureAwait(false))
                     {
-                        mainResult = _json.DeserializeFromStream<RootObject>(json);
+                        using (var json = response.Content)
+                        {
+                            mainResult = _json.DeserializeFromStream<RootObject>(json);
+                        }
                     }
                 }
             }

+ 5 - 5
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -153,16 +153,16 @@ namespace MediaBrowser.Providers.Manager
 
         public async Task SaveImage(IHasMetadata item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
         {
-            var response = await _httpClient.GetResponse(new HttpRequestOptions
+            using (var response = await _httpClient.GetResponse(new HttpRequestOptions
             {
                 CancellationToken = cancellationToken,
                 Url = url,
                 BufferContent = false
 
-            }).ConfigureAwait(false);
-
-            await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken)
-                    .ConfigureAwait(false);
+            }).ConfigureAwait(false))
+            {
+                await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken).ConfigureAwait(false);
+            }
         }
 
         public Task SaveImage(IHasMetadata item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)

+ 7 - 4
MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs

@@ -274,17 +274,20 @@ namespace MediaBrowser.Providers.Movies
 
             try
             {
-                using (var response = await _httpClient.Get(new HttpRequestOptions
+                using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
                     BufferContent = true
 
-                }).ConfigureAwait(false))
+                }, "GET").ConfigureAwait(false))
                 {
-                    using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    using (var response = httpResponse.Content)
                     {
-                        await response.CopyToAsync(fileStream).ConfigureAwait(false);
+                        using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                        {
+                            await response.CopyToAsync(fileStream).ConfigureAwait(false);
+                        }
                     }
                 }
             }

+ 19 - 10
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -146,7 +146,7 @@ namespace MediaBrowser.Providers.Movies
                 return _tmdbSettings;
             }
 
-            using (var json = await GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = string.Format(TmdbConfigUrl, ApiKey),
                 CancellationToken = cancellationToken,
@@ -154,9 +154,12 @@ namespace MediaBrowser.Providers.Movies
 
             }).ConfigureAwait(false))
             {
-                _tmdbSettings = _jsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json);
+                using (var json = response.Content)
+                {
+                    _tmdbSettings = _jsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json);
 
-                return _tmdbSettings;
+                    return _tmdbSettings;
+                }
             }
         }
 
@@ -339,7 +342,7 @@ namespace MediaBrowser.Providers.Movies
 
             try
             {
-                using (var json = await GetMovieDbResponse(new HttpRequestOptions
+                using (var response = await GetMovieDbResponse(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
@@ -349,7 +352,10 @@ namespace MediaBrowser.Providers.Movies
 
                 }).ConfigureAwait(false))
                 {
-                    mainResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+                    using (var json = response.Content)
+                    {
+                        mainResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+                    }
                 }
             }
             catch (HttpException ex)
@@ -381,7 +387,7 @@ namespace MediaBrowser.Providers.Movies
                     url += "&include_image_language=" + GetImageLanguagesParam(language);
                 }
 
-                using (var json = await GetMovieDbResponse(new HttpRequestOptions
+                using (var response = await GetMovieDbResponse(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
@@ -391,9 +397,12 @@ namespace MediaBrowser.Providers.Movies
 
                 }).ConfigureAwait(false))
                 {
-                    var englishResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+                    using (var json = response.Content)
+                    {
+                        var englishResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
 
-                    mainResult.overview = englishResult.overview;
+                        mainResult.overview = englishResult.overview;
+                    }
                 }
             }
 
@@ -407,7 +416,7 @@ namespace MediaBrowser.Providers.Movies
         /// <summary>
         /// Gets the movie db response.
         /// </summary>
-        internal async Task<Stream> GetMovieDbResponse(HttpRequestOptions options)
+        internal async Task<HttpResponseInfo> GetMovieDbResponse(HttpRequestOptions options)
         {
             var delayTicks = (requestIntervalMs * 10000) - (DateTime.UtcNow.Ticks - _lastRequestTicks);
             var delayMs = Math.Min(delayTicks / 10000, requestIntervalMs);
@@ -423,7 +432,7 @@ namespace MediaBrowser.Providers.Movies
             options.BufferContent = true;
             options.UserAgent = "Emby/" + _appHost.ApplicationVersion;
 
-            return await _httpClient.Get(options).ConfigureAwait(false);
+            return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
         }
 
         /// <summary>

+ 50 - 44
MediaBrowser.Providers/Movies/MovieDbSearch.cs

@@ -154,7 +154,7 @@ namespace MediaBrowser.Providers.Movies
 
             var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, type);
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url3,
                 CancellationToken = cancellationToken,
@@ -162,38 +162,41 @@ namespace MediaBrowser.Providers.Movies
 
             }).ConfigureAwait(false))
             {
-                var searchResults = _json.DeserializeFromStream<TmdbMovieSearchResults>(json);
+                using (var json = response.Content)
+                {
+                    var searchResults = _json.DeserializeFromStream<TmdbMovieSearchResults>(json);
 
-                var results = searchResults.results ?? new List<TmdbMovieSearchResult>();
+                    var results = searchResults.results ?? new List<TmdbMovieSearchResult>();
 
-                return results
-                    .Select(i =>
-                    {
-                        var remoteResult = new RemoteSearchResult
+                    return results
+                        .Select(i =>
                         {
-                            SearchProviderName = MovieDbProvider.Current.Name,
-                            Name = i.title ?? i.name ?? i.original_title,
-                            ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path
-                        };
+                            var remoteResult = new RemoteSearchResult
+                            {
+                                SearchProviderName = MovieDbProvider.Current.Name,
+                                Name = i.title ?? i.name ?? i.original_title,
+                                ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path
+                            };
 
-                        if (!string.IsNullOrWhiteSpace(i.release_date))
-                        {
-                            DateTime r;
+                            if (!string.IsNullOrWhiteSpace(i.release_date))
+                            {
+                                DateTime r;
 
                             // These dates are always in this exact format
                             if (DateTime.TryParseExact(i.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r))
-                            {
-                                remoteResult.PremiereDate = r.ToUniversalTime();
-                                remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
+                                {
+                                    remoteResult.PremiereDate = r.ToUniversalTime();
+                                    remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
+                                }
                             }
-                        }
 
-                        remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs));
+                            remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs));
 
-                        return remoteResult;
+                            return remoteResult;
 
-                    })
-                    .ToList();
+                        })
+                        .ToList();
+                }
             }
         }
 
@@ -206,7 +209,7 @@ namespace MediaBrowser.Providers.Movies
 
             var url3 = string.Format(Search3, WebUtility.UrlEncode(name), ApiKey, language, "tv");
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url3,
                 CancellationToken = cancellationToken,
@@ -214,38 +217,41 @@ namespace MediaBrowser.Providers.Movies
 
             }).ConfigureAwait(false))
             {
-                var searchResults = _json.DeserializeFromStream<TmdbTvSearchResults>(json);
+                using (var json = response.Content)
+                {
+                    var searchResults = _json.DeserializeFromStream<TmdbTvSearchResults>(json);
 
-                var results = searchResults.results ?? new List<TvResult>();
+                    var results = searchResults.results ?? new List<TvResult>();
 
-                return results
-                    .Select(i =>
-                    {
-                        var remoteResult = new RemoteSearchResult
+                    return results
+                        .Select(i =>
                         {
-                            SearchProviderName = MovieDbProvider.Current.Name,
-                            Name = i.name ?? i.original_name,
-                            ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path
-                        };
+                            var remoteResult = new RemoteSearchResult
+                            {
+                                SearchProviderName = MovieDbProvider.Current.Name,
+                                Name = i.name ?? i.original_name,
+                                ImageUrl = string.IsNullOrWhiteSpace(i.poster_path) ? null : baseImageUrl + i.poster_path
+                            };
 
-                        if (!string.IsNullOrWhiteSpace(i.first_air_date))
-                        {
-                            DateTime r;
+                            if (!string.IsNullOrWhiteSpace(i.first_air_date))
+                            {
+                                DateTime r;
 
                             // These dates are always in this exact format
                             if (DateTime.TryParseExact(i.first_air_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r))
-                            {
-                                remoteResult.PremiereDate = r.ToUniversalTime();
-                                remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
+                                {
+                                    remoteResult.PremiereDate = r.ToUniversalTime();
+                                    remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year;
+                                }
                             }
-                        }
 
-                        remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs));
+                            remoteResult.SetProviderId(MetadataProviders.Tmdb, i.id.ToString(EnUs));
 
-                        return remoteResult;
+                            return remoteResult;
 
-                    })
-                    .ToList();
+                        })
+                        .ToList();
+                }
             }
         }
 

+ 7 - 4
MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs

@@ -161,16 +161,19 @@ namespace MediaBrowser.Providers.Music
 
 			_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
 
-            using (var response = await _httpClient.Get(new HttpRequestOptions
+            using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-                using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                using (var response = httpResponse.Content)
                 {
-                    await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
+                    using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    {
+                        await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
+                    }
                 }
             }
         }

+ 9 - 6
MediaBrowser.Providers/Music/AudioDbArtistProvider.cs

@@ -147,19 +147,22 @@ namespace MediaBrowser.Providers.Music
 
             var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
 
-            using (var response = await _httpClient.Get(new HttpRequestOptions
+            using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = true
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-				_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
-
-                using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                using (var response = httpResponse.Content)
                 {
-                    await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+
+                    using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    {
+                        await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
+                    }
                 }
             }
         }

+ 7 - 4
MediaBrowser.Providers/Music/FanArtArtistProvider.cs

@@ -251,17 +251,20 @@ namespace MediaBrowser.Providers.Music
 
             try
             {
-                using (var response = await _httpClient.Get(new HttpRequestOptions
+                using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
                     BufferContent = true
 
-                }).ConfigureAwait(false))
+                }, "GET").ConfigureAwait(false))
                 {
-                    using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    using (var response = httpResponse.Content)
                     {
-                        await response.CopyToAsync(saveFileStream).ConfigureAwait(false);
+                        using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                        {
+                            await response.CopyToAsync(saveFileStream).ConfigureAwait(false);
+                        }
                     }
                 }
             }

+ 89 - 71
MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs

@@ -83,9 +83,12 @@ namespace MediaBrowser.Providers.Music
 
             if (!string.IsNullOrWhiteSpace(url))
             {
-                using (var stream = await GetMusicBrainzResponse(url, isNameSearch, forceMusicBrainzProper, cancellationToken).ConfigureAwait(false))
+                using (var response = await GetMusicBrainzResponse(url, isNameSearch, forceMusicBrainzProper, cancellationToken).ConfigureAwait(false))
                 {
-                    return GetResultsFromResponse(stream);
+                    using (var stream = response.Content)
+                    {
+                        return GetResultsFromResponse(stream);
+                    }
                 }
             }
 
@@ -226,19 +229,22 @@ namespace MediaBrowser.Providers.Music
                 WebUtility.UrlEncode(albumName),
                 artistId);
 
-            using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
             {
-                using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    var settings = _xmlSettings.Create(false);
+                    using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                    {
+                        var settings = _xmlSettings.Create(false);
 
-                    settings.CheckCharacters = false;
-                    settings.IgnoreProcessingInstructions = true;
-                    settings.IgnoreComments = true;
+                        settings.CheckCharacters = false;
+                        settings.IgnoreProcessingInstructions = true;
+                        settings.IgnoreComments = true;
 
-                    using (var reader = XmlReader.Create(oReader, settings))
-                    {
-                        return ReleaseResult.Parse(reader).FirstOrDefault();
+                        using (var reader = XmlReader.Create(oReader, settings))
+                        {
+                            return ReleaseResult.Parse(reader).FirstOrDefault();
+                        }
                     }
                 }
             }
@@ -250,19 +256,22 @@ namespace MediaBrowser.Providers.Music
                 WebUtility.UrlEncode(albumName),
                 WebUtility.UrlEncode(artistName));
 
-            using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
             {
-                using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    var settings = _xmlSettings.Create(false);
+                    using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                    {
+                        var settings = _xmlSettings.Create(false);
 
-                    settings.CheckCharacters = false;
-                    settings.IgnoreProcessingInstructions = true;
-                    settings.IgnoreComments = true;
+                        settings.CheckCharacters = false;
+                        settings.IgnoreProcessingInstructions = true;
+                        settings.IgnoreComments = true;
 
-                    using (var reader = XmlReader.Create(oReader, settings))
-                    {
-                        return ReleaseResult.Parse(reader).FirstOrDefault();
+                        using (var reader = XmlReader.Create(oReader, settings))
+                        {
+                            return ReleaseResult.Parse(reader).FirstOrDefault();
+                        }
                     }
                 }
             }
@@ -431,23 +440,26 @@ namespace MediaBrowser.Providers.Music
         {
             var url = string.Format("/ws/2/release?release-group={0}", releaseGroupId);
 
-            using (var stream = await GetMusicBrainzResponse(url, true, true, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetMusicBrainzResponse(url, true, true, cancellationToken).ConfigureAwait(false))
             {
-                using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    var settings = _xmlSettings.Create(false);
-
-                    settings.CheckCharacters = false;
-                    settings.IgnoreProcessingInstructions = true;
-                    settings.IgnoreComments = true;
-
-                    using (var reader = XmlReader.Create(oReader, settings))
+                    using (var oReader = new StreamReader(stream, Encoding.UTF8))
                     {
-                        var result = ReleaseResult.Parse(reader).FirstOrDefault();
+                        var settings = _xmlSettings.Create(false);
+
+                        settings.CheckCharacters = false;
+                        settings.IgnoreProcessingInstructions = true;
+                        settings.IgnoreComments = true;
 
-                        if (result != null)
+                        using (var reader = XmlReader.Create(oReader, settings))
                         {
-                            return result.ReleaseId;
+                            var result = ReleaseResult.Parse(reader).FirstOrDefault();
+
+                            if (result != null)
+                            {
+                                return result.ReleaseId;
+                            }
                         }
                     }
                 }
@@ -466,53 +478,56 @@ namespace MediaBrowser.Providers.Music
         {
             var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId);
 
-            using (var stream = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false))
             {
-                using (var oReader = new StreamReader(stream, Encoding.UTF8))
+                using (var stream = response.Content)
                 {
-                    var settings = _xmlSettings.Create(false);
-
-                    settings.CheckCharacters = false;
-                    settings.IgnoreProcessingInstructions = true;
-                    settings.IgnoreComments = true;
-
-                    using (var reader = XmlReader.Create(oReader, settings))
+                    using (var oReader = new StreamReader(stream, Encoding.UTF8))
                     {
-                        reader.MoveToContent();
-                        reader.Read();
+                        var settings = _xmlSettings.Create(false);
 
-                        // Loop through each element
-                        while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+                        settings.CheckCharacters = false;
+                        settings.IgnoreProcessingInstructions = true;
+                        settings.IgnoreComments = true;
+
+                        using (var reader = XmlReader.Create(oReader, settings))
                         {
-                            if (reader.NodeType == XmlNodeType.Element)
+                            reader.MoveToContent();
+                            reader.Read();
+
+                            // Loop through each element
+                            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
                             {
-                                switch (reader.Name)
+                                if (reader.NodeType == XmlNodeType.Element)
                                 {
-                                    case "release-group-list":
-                                        {
-                                            if (reader.IsEmptyElement)
+                                    switch (reader.Name)
+                                    {
+                                        case "release-group-list":
                                             {
-                                                reader.Read();
-                                                continue;
+                                                if (reader.IsEmptyElement)
+                                                {
+                                                    reader.Read();
+                                                    continue;
+                                                }
+                                                using (var subReader = reader.ReadSubtree())
+                                                {
+                                                    return GetFirstReleaseGroupId(subReader);
+                                                }
                                             }
-                                            using (var subReader = reader.ReadSubtree())
+                                        default:
                                             {
-                                                return GetFirstReleaseGroupId(subReader);
+                                                reader.Skip();
+                                                break;
                                             }
-                                        }
-                                    default:
-                                        {
-                                            reader.Skip();
-                                            break;
-                                        }
+                                    }
+                                }
+                                else
+                                {
+                                    reader.Read();
                                 }
                             }
-                            else
-                            {
-                                reader.Read();
-                            }
+                            return null;
                         }
-                        return null;
                     }
                 }
             }
@@ -598,11 +613,14 @@ namespace MediaBrowser.Providers.Music
                         UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion
                     };
 
-                    using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+                    using (var response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false))
                     {
-                        var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
+                        using (var stream = response.Content)
+                        {
+                            var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
 
-                        list = results;
+                            list = results;
+                        }
                     }
                     _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
                 }
@@ -626,7 +644,7 @@ namespace MediaBrowser.Providers.Music
             return list;
         }
 
-        internal Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
+        internal Task<HttpResponseInfo> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
         {
             return GetMusicBrainzResponse(url, isSearch, false, cancellationToken);
         }
@@ -634,7 +652,7 @@ namespace MediaBrowser.Providers.Music
         /// <summary>
         /// Gets the music brainz response.
         /// </summary>
-        internal async Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken)
+        internal async Task<HttpResponseInfo> GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken)
         {
             var urlInfo = await GetMbzUrl(forceMusicBrainzProper).ConfigureAwait(false);
             var throttleMs = urlInfo.throttleMs;
@@ -656,7 +674,7 @@ namespace MediaBrowser.Providers.Music
                 BufferContent = throttleMs > 0
             };
 
-            return await _httpClient.Get(options).ConfigureAwait(false);
+            return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
         }
 
         public int Order

+ 18 - 10
MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs

@@ -35,10 +35,12 @@ namespace MediaBrowser.Providers.Music
             {
                 var url = string.Format("/ws/2/artist/?query=arid:{0}", musicBrainzId);
 
-                using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken)
-                            .ConfigureAwait(false))
+                using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false))
                 {
-                    return GetResultsFromResponse(stream);
+                    using (var stream = response.Content)
+                    {
+                        return GetResultsFromResponse(stream);
+                    }
                 }
             }
             else
@@ -48,13 +50,16 @@ namespace MediaBrowser.Providers.Music
 
                 var url = String.Format("/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch));
 
-                using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+                using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
                 {
-                    var results = GetResultsFromResponse(stream);
-
-                    if (results.Count > 0)
+                    using (var stream = response.Content)
                     {
-                        return results;
+                        var results = GetResultsFromResponse(stream);
+
+                        if (results.Count > 0)
+                        {
+                            return results;
+                        }
                     }
                 }
 
@@ -63,9 +68,12 @@ namespace MediaBrowser.Providers.Music
                     // Try again using the search with accent characters url
                     url = String.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
 
-                    using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+                    using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
                     {
-                        return GetResultsFromResponse(stream);
+                        using (var stream = response.Content)
+                        {
+                            return GetResultsFromResponse(stream);
+                        }
                     }
                 }
             }

+ 53 - 50
MediaBrowser.Providers/Omdb/OmdbItemProvider.cs

@@ -126,65 +126,68 @@ namespace MediaBrowser.Providers.Omdb
 
             var url =  OmdbProvider.GetOmdbUrl(urlQuery, cancellationToken);
 
-            using (var stream = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
+            using (var response = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
             {
-                var resultList = new List<SearchResult>();
-
-                if (isSearch)
+                using (var stream = response.Content)
                 {
-                    var searchResultList = _jsonSerializer.DeserializeFromStream<SearchResultList>(stream);
-                    if (searchResultList != null && searchResultList.Search != null)
-                    {
-                        resultList.AddRange(searchResultList.Search);
-                    }
-                }
-                else
-                {
-                    var result = _jsonSerializer.DeserializeFromStream<SearchResult>(stream);
-                    if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
-                    {
-                        resultList.Add(result);
-                    }
-                }
+                    var resultList = new List<SearchResult>();
 
-                return resultList.Select(result =>
-                {
-                    var item = new RemoteSearchResult
+                    if (isSearch)
                     {
-                        IndexNumber = searchInfo.IndexNumber,
-                        Name = result.Title,
-                        ParentIndexNumber = searchInfo.ParentIndexNumber,
-                        SearchProviderName = Name
-                    };
-
-                    if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue)
-                    {
-                        item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value;
-                    }
-
-                    item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
-
-                    int parsedYear;
-                    if (result.Year.Length > 0
-                        && int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Any, CultureInfo.InvariantCulture, out parsedYear))
-                    {
-                        item.ProductionYear = parsedYear;
+                        var searchResultList = _jsonSerializer.DeserializeFromStream<SearchResultList>(stream);
+                        if (searchResultList != null && searchResultList.Search != null)
+                        {
+                            resultList.AddRange(searchResultList.Search);
+                        }
                     }
-
-                    DateTime released;
-                    if (!string.IsNullOrEmpty(result.Released)
-                        && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out released))
+                    else
                     {
-                        item.PremiereDate = released;
+                        var result = _jsonSerializer.DeserializeFromStream<SearchResult>(stream);
+                        if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
+                        {
+                            resultList.Add(result);
+                        }
                     }
 
-                    if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase))
+                    return resultList.Select(result =>
                     {
-                        item.ImageUrl = result.Poster;
-                    }
-
-                    return item;
-                });
+                        var item = new RemoteSearchResult
+                        {
+                            IndexNumber = searchInfo.IndexNumber,
+                            Name = result.Title,
+                            ParentIndexNumber = searchInfo.ParentIndexNumber,
+                            SearchProviderName = Name
+                        };
+
+                        if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue)
+                        {
+                            item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value;
+                        }
+
+                        item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
+
+                        int parsedYear;
+                        if (result.Year.Length > 0
+                            && int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Any, CultureInfo.InvariantCulture, out parsedYear))
+                        {
+                            item.ProductionYear = parsedYear;
+                        }
+
+                        DateTime released;
+                        if (!string.IsNullOrEmpty(result.Released)
+                            && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out released))
+                        {
+                            item.PremiereDate = released;
+                        }
+
+                        if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase))
+                        {
+                            item.ImageUrl = result.Poster;
+                        }
+
+                        return item;
+                    });
+                }
             }
         }
 

+ 17 - 11
MediaBrowser.Providers/Omdb/OmdbProvider.cs

@@ -301,11 +301,14 @@ namespace MediaBrowser.Providers.Omdb
 
             var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), cancellationToken);
 
-            using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
             {
-                var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
-                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
-                _jsonSerializer.SerializeToFile(rootObject, path);
+                using (var stream = response.Content)
+                {
+                    var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+                    _jsonSerializer.SerializeToFile(rootObject, path);
+                }
             }
 
             return path;
@@ -335,25 +338,28 @@ namespace MediaBrowser.Providers.Omdb
 
             var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), cancellationToken);
 
-            using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
+            using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
             {
-                var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream);
-                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
-                _jsonSerializer.SerializeToFile(rootObject, path);
+                using (var stream = response.Content)
+                {
+                    var rootObject = _jsonSerializer.DeserializeFromStream<SeasonRootObject>(stream);
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
+                    _jsonSerializer.SerializeToFile(rootObject, path);
+                }
             }
 
             return path;
         }
 
-        public static Task<Stream> GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken)
+        public static Task<HttpResponseInfo> GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken)
         {
-            return httpClient.Get(new HttpRequestOptions
+            return httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = true,
                 EnableDefaultUserAgent = true
-            });
+            }, "GET");
         }
 
         internal string GetDataFilePath(string imdbId)

+ 15 - 9
MediaBrowser.Providers/People/MovieDbPersonProvider.cs

@@ -91,7 +91,7 @@ namespace MediaBrowser.Providers.People
 
             var url = string.Format(@"https://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), MovieDbProvider.ApiKey);
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -99,10 +99,13 @@ namespace MediaBrowser.Providers.People
 
             }).ConfigureAwait(false))
             {
-                var result = _jsonSerializer.DeserializeFromStream<PersonSearchResults>(json) ??
-                             new PersonSearchResults();
+                using (var json = response.Content)
+                {
+                    var result = _jsonSerializer.DeserializeFromStream<PersonSearchResults>(json) ??
+                                 new PersonSearchResults();
 
-                return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl));
+                    return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl));
+                }
             }
         }
 
@@ -223,7 +226,7 @@ namespace MediaBrowser.Providers.People
 
             var url = string.Format(@"https://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", MovieDbProvider.ApiKey, id);
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -231,11 +234,14 @@ namespace MediaBrowser.Providers.People
 
             }).ConfigureAwait(false))
             {
-                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath));
-
-                using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                using (var json = response.Content)
                 {
-                    await json.CopyToAsync(fs).ConfigureAwait(false);
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(dataFilePath));
+
+                    using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    {
+                        await json.CopyToAsync(fs).ConfigureAwait(false);
+                    }
                 }
             }
         }

+ 7 - 4
MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs

@@ -316,17 +316,20 @@ namespace MediaBrowser.Providers.TV
 
             try
             {
-                using (var response = await _httpClient.Get(new HttpRequestOptions
+                using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
                     BufferContent = true
 
-                }).ConfigureAwait(false))
+                }, "GET").ConfigureAwait(false))
                 {
-                    using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                    using (var response = httpResponse.Content)
                     {
-                        await response.CopyToAsync(fileStream).ConfigureAwait(false);
+                        using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
+                        {
+                            await response.CopyToAsync(fileStream).ConfigureAwait(false);
+                        }
                     }
                 }
             }

+ 5 - 2
MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs

@@ -125,7 +125,7 @@ namespace MediaBrowser.Providers.TV
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -133,7 +133,10 @@ namespace MediaBrowser.Providers.TV
 
             }).ConfigureAwait(false))
             {
-                return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+                using (var json = response.Content)
+                {
+                    return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+                }
             }
         }
 

+ 5 - 2
MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs

@@ -214,7 +214,7 @@ namespace MediaBrowser.Providers.TV
 
             cancellationToken.ThrowIfCancellationRequested();
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -222,7 +222,10 @@ namespace MediaBrowser.Providers.TV
 
             }).ConfigureAwait(false))
             {
-                return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+                using (var json = response.Content)
+                {
+                    return _jsonSerializer.DeserializeFromStream<RootObject>(json);
+                }
             }
         }
 

+ 33 - 24
MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs

@@ -352,7 +352,7 @@ namespace MediaBrowser.Providers.TV
 
             RootObject mainResult;
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -360,11 +360,14 @@ namespace MediaBrowser.Providers.TV
 
             }).ConfigureAwait(false))
             {
-                mainResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
-
-                if (!string.IsNullOrEmpty(language))
+                using (var json = response.Content)
                 {
-                    mainResult.ResultLanguage = language;
+                    mainResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
+
+                    if (!string.IsNullOrEmpty(language))
+                    {
+                        mainResult.ResultLanguage = language;
+                    }
                 }
             }
 
@@ -386,7 +389,7 @@ namespace MediaBrowser.Providers.TV
                     url += "&include_image_language=" + MovieDbProvider.GetImageLanguagesParam(language);
                 }
 
-                using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+                using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
                 {
                     Url = url,
                     CancellationToken = cancellationToken,
@@ -394,10 +397,13 @@ namespace MediaBrowser.Providers.TV
 
                 }).ConfigureAwait(false))
                 {
-                    var englishResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
+                    using (var json = response.Content)
+                    {
+                        var englishResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
 
-                    mainResult.overview = englishResult.overview;
-                    mainResult.ResultLanguage = "en";
+                        mainResult.overview = englishResult.overview;
+                        mainResult.ResultLanguage = "en";
+                    }
                 }
             }
 
@@ -449,7 +455,7 @@ namespace MediaBrowser.Providers.TV
                 MovieDbProvider.ApiKey,
                 externalSource);
 
-            using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+            using (var response = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
@@ -457,27 +463,30 @@ namespace MediaBrowser.Providers.TV
 
             }).ConfigureAwait(false))
             {
-                var result = _jsonSerializer.DeserializeFromStream<MovieDbSearch.ExternalIdLookupResult>(json);
-
-                if (result != null && result.tv_results != null)
+                using (var json = response.Content)
                 {
-                    var tv = result.tv_results.FirstOrDefault();
+                    var result = _jsonSerializer.DeserializeFromStream<MovieDbSearch.ExternalIdLookupResult>(json);
 
-                    if (tv != null)
+                    if (result != null && result.tv_results != null)
                     {
-                        var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
-                        var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
+                        var tv = result.tv_results.FirstOrDefault();
 
-                        var remoteResult = new RemoteSearchResult
+                        if (tv != null)
                         {
-                            Name = tv.name,
-                            SearchProviderName = Name,
-                            ImageUrl = string.IsNullOrWhiteSpace(tv.poster_path) ? null : tmdbImageUrl + tv.poster_path
-                        };
+                            var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false);
+                            var tmdbImageUrl = tmdbSettings.images.secure_base_url + "original";
 
-                        remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.id.ToString(_usCulture));
+                            var remoteResult = new RemoteSearchResult
+                            {
+                                Name = tv.name,
+                                SearchProviderName = Name,
+                                ImageUrl = string.IsNullOrWhiteSpace(tv.poster_path) ? null : tmdbImageUrl + tv.poster_path
+                            };
 
-                        return remoteResult;
+                            remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.id.ToString(_usCulture));
+
+                            return remoteResult;
+                        }
                     }
                 }
             }

+ 17 - 10
MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs

@@ -142,16 +142,20 @@ namespace MediaBrowser.Providers.TV
             if (string.IsNullOrEmpty(lastUpdateTime))
             {
                 // First get tvdb server time
-                using (var stream = await _httpClient.Get(new HttpRequestOptions
+                using (var response = await _httpClient.SendAsync(new HttpRequestOptions
                 {
                     Url = ServerTimeUrl,
                     CancellationToken = cancellationToken,
                     EnableHttpCompression = true,
                     BufferContent = false
 
-                }).ConfigureAwait(false))
+                }, "GET").ConfigureAwait(false))
                 {
-                    newUpdateTime = GetUpdateTime(stream);
+                    // First get tvdb server time
+                    using (var stream = response.Content)
+                    {
+                        newUpdateTime = GetUpdateTime(stream);
+                    }
                 }
 
                 existingDirectories.AddRange(missingSeries);
@@ -238,23 +242,26 @@ namespace MediaBrowser.Providers.TV
         private async Task<Tuple<IEnumerable<string>, string>> GetSeriesIdsToUpdate(IEnumerable<string> existingSeriesIds, string lastUpdateTime, CancellationToken cancellationToken)
         {
             // First get last time
-            using (var stream = await _httpClient.Get(new HttpRequestOptions
+            using (var response = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = string.Format(UpdatesUrl, lastUpdateTime),
                 CancellationToken = cancellationToken,
                 EnableHttpCompression = true,
                 BufferContent = false
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-                var data = GetUpdatedSeriesIdList(stream);
+                using (var stream = response.Content)
+                {
+                    var data = GetUpdatedSeriesIdList(stream);
 
-                var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+                    var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
 
-                var seriesList = data.Item1
-                    .Where(i => !string.IsNullOrWhiteSpace(i) && existingDictionary.ContainsKey(i));
+                    var seriesList = data.Item1
+                        .Where(i => !string.IsNullOrWhiteSpace(i) && existingDictionary.ContainsKey(i));
 
-                return new Tuple<IEnumerable<string>, string>(seriesList, data.Item2);
+                    return new Tuple<IEnumerable<string>, string>(seriesList, data.Item2);
+                }
             }
         }
 

+ 61 - 52
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs

@@ -216,24 +216,27 @@ namespace MediaBrowser.Providers.TV
 
             var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, NormalizeLanguage(preferredMetadataLanguage));
 
-            using (var zipStream = await _httpClient.Get(new HttpRequestOptions
+            using (var response = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = false
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-                // Delete existing files
-                DeleteXmlFiles(seriesDataPath);
-
-                // Copy to memory stream because we need a seekable stream
-                using (var ms = _memoryStreamProvider.CreateNew())
+                using (var zipStream = response.Content)
                 {
-                    await zipStream.CopyToAsync(ms).ConfigureAwait(false);
+                    // Delete existing files
+                    DeleteXmlFiles(seriesDataPath);
+
+                    // Copy to memory stream because we need a seekable stream
+                    using (var ms = _memoryStreamProvider.CreateNew())
+                    {
+                        await zipStream.CopyToAsync(ms).ConfigureAwait(false);
 
-                    ms.Position = 0;
-                    _zipClient.ExtractAllFromZip(ms, seriesDataPath, true);
+                        ms.Position = 0;
+                        _zipClient.ExtractAllFromZip(ms, seriesDataPath, true);
+                    }
                 }
             }
 
@@ -260,15 +263,18 @@ namespace MediaBrowser.Providers.TV
         {
             var url = string.Format(GetSeriesByImdbId, id, NormalizeLanguage(language));
 
-            using (var result = await _httpClient.Get(new HttpRequestOptions
+            using (var response = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = false
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-                return FindSeriesId(result);
+                using (var result = response.Content)
+                {
+                    return FindSeriesId(result);
+                }
             }
         }
 
@@ -514,64 +520,67 @@ namespace MediaBrowser.Providers.TV
 
             var comparableName = GetComparableName(name);
 
-            using (var stream = await _httpClient.Get(new HttpRequestOptions
+            using (var response = await _httpClient.SendAsync(new HttpRequestOptions
             {
                 Url = url,
                 CancellationToken = cancellationToken,
                 BufferContent = false
 
-            }).ConfigureAwait(false))
+            }, "GET").ConfigureAwait(false))
             {
-                var settings = _xmlSettings.Create(false);
+                using (var stream = response.Content)
+                {
+                    var settings = _xmlSettings.Create(false);
 
-                settings.CheckCharacters = false;
-                settings.IgnoreProcessingInstructions = true;
-                settings.IgnoreComments = true;
+                    settings.CheckCharacters = false;
+                    settings.IgnoreProcessingInstructions = true;
+                    settings.IgnoreComments = true;
 
-                using (var streamReader = new StreamReader(stream, Encoding.UTF8))
-                {
-                    // Use XmlReader for best performance
-                    using (var reader = XmlReader.Create(streamReader, settings))
+                    using (var streamReader = new StreamReader(stream, Encoding.UTF8))
                     {
-                        reader.MoveToContent();
-                        reader.Read();
-
-                        // Loop through each element
-                        while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+                        // Use XmlReader for best performance
+                        using (var reader = XmlReader.Create(streamReader, settings))
                         {
-                            cancellationToken.ThrowIfCancellationRequested();
+                            reader.MoveToContent();
+                            reader.Read();
 
-                            if (reader.NodeType == XmlNodeType.Element)
+                            // Loop through each element
+                            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
                             {
-                                switch (reader.Name)
+                                cancellationToken.ThrowIfCancellationRequested();
+
+                                if (reader.NodeType == XmlNodeType.Element)
                                 {
-                                    case "Series":
-                                        {
-                                            if (reader.IsEmptyElement)
-                                            {
-                                                reader.Read();
-                                                continue;
-                                            }
-                                            using (var subtree = reader.ReadSubtree())
+                                    switch (reader.Name)
+                                    {
+                                        case "Series":
                                             {
-                                                var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName);
-                                                if (searchResult != null)
+                                                if (reader.IsEmptyElement)
                                                 {
-                                                    searchResult.SearchProviderName = Name;
-                                                    searchResults.Add(searchResult);
+                                                    reader.Read();
+                                                    continue;
                                                 }
+                                                using (var subtree = reader.ReadSubtree())
+                                                {
+                                                    var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName);
+                                                    if (searchResult != null)
+                                                    {
+                                                        searchResult.SearchProviderName = Name;
+                                                        searchResults.Add(searchResult);
+                                                    }
+                                                }
+                                                break;
                                             }
-                                            break;
-                                        }
 
-                                    default:
-                                        reader.Skip();
-                                        break;
+                                        default:
+                                            reader.Skip();
+                                            break;
+                                    }
+                                }
+                                else
+                                {
+                                    reader.Read();
                                 }
-                            }
-                            else
-                            {
-                                reader.Read();
                             }
                         }
                     }

+ 1 - 0
Mono.Nat/ISearcher.cs

@@ -33,6 +33,7 @@ using System.Collections.Generic;
 using System.Text;
 using System.Net.Sockets;
 using System.Net;
+using System.Threading.Tasks;
 
 namespace Mono.Nat
 {

+ 0 - 1
Mono.Nat/Mono.Nat.csproj

@@ -67,7 +67,6 @@
     <Compile Include="Upnp\Messages\Responses\CreatePortMappingResponseMessage.cs" />
     <Compile Include="Upnp\Messages\UpnpMessage.cs" />
     <Compile Include="Upnp\Searchers\UpnpSearcher.cs" />
-    <Compile Include="Upnp\Upnp.cs" />
     <Compile Include="Upnp\UpnpNatDevice.cs" />
   </ItemGroup>
   <ItemGroup>

+ 2 - 2
Mono.Nat/NatUtility.cs

@@ -208,14 +208,14 @@ namespace Mono.Nat
             }
         }
 
-        public static void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol)
+        public static async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint, NatProtocol protocol)
         {
             switch (protocol)
             {
                 case NatProtocol.Upnp:
                     var searcher = new UpnpSearcher(Logger, HttpClient);
                     searcher.DeviceFound += Searcher_DeviceFound;
-                    searcher.Handle(localAddress, deviceInfo, endpoint);
+                    await searcher.Handle(localAddress, deviceInfo, endpoint).ConfigureAwait(false);
                     break;
                 default:
                     throw new ArgumentException("Unexpected protocol: " + protocol);

+ 26 - 26
Mono.Nat/Pmp/Searchers/PmpSearcher.cs

@@ -42,13 +42,13 @@ namespace Mono.Nat
 {
     internal class PmpSearcher : ISearcher
     {
-		static PmpSearcher instance = new PmpSearcher();
-        
-		
-		public static PmpSearcher Instance
-		{
-			get { return instance; }
-		}
+        static PmpSearcher instance = new PmpSearcher();
+
+
+        public static PmpSearcher Instance
+        {
+            get { return instance; }
+        }
 
         private int timeout;
         private DateTime nextSearch;
@@ -143,21 +143,21 @@ namespace Mono.Nat
         }
 
         public async void Search()
-		{
-			foreach (UdpClient s in sockets)
-			{
-				try
-				{
-					await Search(s).ConfigureAwait(false);
-				}
-				catch
-				{
-					// Ignore any search errors
-				}
-			}
-		}
-
-		async Task Search (UdpClient client)
+        {
+            foreach (UdpClient s in sockets)
+            {
+                try
+                {
+                    await Search(s).ConfigureAwait(false);
+                }
+                catch
+                {
+                    // Ignore any search errors
+                }
+            }
+        }
+
+        async Task Search(UdpClient client)
         {
             // Sort out the time for the next search first. The spec says the 
             // timeout should double after each attempt. Once it reaches 64 seconds
@@ -175,10 +175,10 @@ namespace Mono.Nat
 
             // The nat-pmp search message. Must be sent to GatewayIP:53531
             byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
-		    foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
-		    {
-		        await client.SendAsync(buffer, buffer.Length, gatewayEndpoint).ConfigureAwait(false);
-		    }
+            foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
+            {
+                await client.SendAsync(buffer, buffer.Length, gatewayEndpoint).ConfigureAwait(false);
+            }
         }
 
         bool IsSearchAddress(IPAddress address)

+ 4 - 1
Mono.Nat/Upnp/Searchers/UpnpSearcher.cs

@@ -39,6 +39,7 @@ using System.Net.NetworkInformation;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Dlna;
+using System.Threading.Tasks;
 
 namespace Mono.Nat
 {
@@ -61,7 +62,7 @@ namespace Mono.Nat
 		{
 		}
 
-        public void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
+        public async Task Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
         {
             // No matter what, this method should never throw an exception. If something goes wrong
             // we should still be in a position to handle the next reply correctly.
@@ -82,6 +83,8 @@ namespace Mono.Nat
                 UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);
 
                 NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
+                await d.GetServicesList().ConfigureAwait(false);
+
                 OnDeviceFound(new DeviceEventArgs(d));
             }
             catch (Exception ex)

+ 0 - 95
Mono.Nat/Upnp/Upnp.cs

@@ -1,95 +0,0 @@
-//
-// Authors:
-//   Alan McGovern alan.mcgovern@gmail.com
-//   Ben Motmans <ben.motmans@gmail.com>
-//   Nicholas Terry <nick.i.terry@gmail.com>
-//
-// Copyright (C) 2006 Alan McGovern
-// Copyright (C) 2007 Ben Motmans
-// Copyright (C) 2014 Nicholas Terry
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-
-namespace Mono.Nat.Upnp
-{
-    internal class Upnp
-    {
-        protected readonly ILogger Logger;
-        protected readonly IHttpClient HttpClient;
-
-        public Upnp(ILogger logger, IHttpClient httpClient)
-        {
-            Logger = logger;
-            HttpClient = httpClient;
-        }
-
-        public virtual Task<UpnpNatDevice> Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
-        {
-            // Convert it to a string for easy parsing
-            string dataString = null;
-
-
-            string urn;
-            dataString = Encoding.UTF8.GetString(response);
-
-            if (NatUtility.Verbose)
-                NatUtility.Log("UPnP Response: {0}", dataString);
-
-            /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection. 
-                Any other device type is no good to us for this purpose. See the IGP overview paper 
-                page 5 for an overview of device types and their hierarchy.
-                http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */
-
-            /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
-                version it is and apply the correct URN. */
-
-            /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
-                prefix. */
-
-            string log = "UPnP Response: Router advertised a '{0}' service";
-            StringComparison c = StringComparison.OrdinalIgnoreCase;
-            if (dataString.IndexOf("urn:schemas-upnp-org:service:WANIPConnection:", c) != -1)
-            {
-                urn = "urn:schemas-upnp-org:service:WANIPConnection:1";
-                NatUtility.Log(log, "urn:schemas-upnp-org:service:WANIPConnection:1");
-            }
-            else if (dataString.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:", c) != -1)
-            {
-                urn = "urn:schemas-upnp-org:service:WANPPPConnection:1";
-                NatUtility.Log(log, "urn:schemas-upnp-org:service:WANPPPConnection:");
-            }
-            else
-                throw new NotSupportedException("Received non-supported device type");
-
-            // We have an internet gateway device now
-            var device = new UpnpNatDevice(localAddress, dataString, urn, Logger, HttpClient);
-            return Task.FromResult(device);
-        }
-    }
-}

+ 89 - 37
Mono.Nat/Upnp/UpnpNatDevice.cs

@@ -90,52 +90,101 @@ namespace Mono.Nat.Upnp
             }
         }
 
-        internal UpnpNatDevice(IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger, IHttpClient httpClient)
+        public async Task GetServicesList()
         {
-            _logger = logger;
-            _httpClient = httpClient;
-            this.LastSeen = DateTime.Now;
-            this.localAddress = localAddress;
+            // Create a HTTPWebRequest to download the list of services the device offers
+            var message = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger);
 
-            // Split the string at the "location" section so i can extract the ipaddress and service description url
-            string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.OrdinalIgnoreCase) + 9).Split('\r')[0];
-            this.serviceType = serviceType;
-
-            // Make sure we have no excess whitespace
-            locationDetails = locationDetails.Trim();
-
-            // FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
-            // Are we going to get addresses with the "http://" attached?
-            if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
+            using (var response = await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
             {
-                NatUtility.Log("Found device at: {0}", locationDetails);
-                // This bit strings out the "http://" from the string
-                locationDetails = locationDetails.Substring(7);
+                OnServicesReceived(response);
+            }
+        }
 
-                // We then split off the end of the string to get something like: 192.168.0.3:241 in our string
-                string hostAddressAndPort = locationDetails.Remove(locationDetails.IndexOf('/'));
+        private void OnServicesReceived(HttpResponseInfo response)
+        {
+            int abortCount = 0;
+            int bytesRead = 0;
+            byte[] buffer = new byte[10240];
+            StringBuilder servicesXml = new StringBuilder();
+            XmlDocument xmldoc = new XmlDocument();
 
-                // From this we parse out the IP address and Port
-                if (hostAddressAndPort.IndexOf(':') > 0)
+            using (var s = response.Content)
+            {
+                if (response.StatusCode != HttpStatusCode.OK)
                 {
-                    this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort.Remove(hostAddressAndPort.IndexOf(':'))),
-                    Convert.ToUInt16(hostAddressAndPort.Substring(hostAddressAndPort.IndexOf(':') + 1), System.Globalization.CultureInfo.InvariantCulture));
+                    NatUtility.Log("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
+                    return; // FIXME: This the best thing to do??
                 }
-                else
+
+                while (true)
                 {
-                    // there is no port specified, use default port (80)
-                    this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort), 80);
+                    bytesRead = s.Read(buffer, 0, buffer.Length);
+                    servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
+                    try
+                    {
+                        xmldoc.LoadXml(servicesXml.ToString());
+                        break;
+                    }
+                    catch (XmlException)
+                    {
+                        // If we can't receive the entire XML within 500ms, then drop the connection
+                        // Unfortunately not all routers supply a valid ContentLength (mine doesn't)
+                        // so this hack is needed to keep testing our recieved data until it gets successfully
+                        // parsed by the xmldoc. Without this, the code will never pick up my router.
+                        if (abortCount++ > 50)
+                        {
+                            return;
+                        }
+                        NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
+                        System.Threading.Thread.Sleep(10);
+                    }
                 }
 
-                NatUtility.Log("Parsed device as: {0}", this.hostEndPoint.ToString());
+                NatUtility.Log("{0}: Parsed services list", HostEndPoint);
+                XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
+                ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0");
+                XmlNodeList nodes = xmldoc.SelectNodes("//*/ns:serviceList", ns);
 
-                // The service description URL is the remainder of the "locationDetails" string. The bit that was originally after the ip
-                // and port information
-                this.serviceDescriptionUrl = locationDetails.Substring(locationDetails.IndexOf('/'));
-            }
-            else
-            {
-                logger.Warn("Couldn't decode address: " + deviceDetails);
+                foreach (XmlNode node in nodes)
+                {
+                    //Go through each service there
+                    foreach (XmlNode service in node.ChildNodes)
+                    {
+                        //If the service is a WANIPConnection, then we have what we want
+                        string type = service["serviceType"].InnerText;
+                        NatUtility.Log("{0}: Found service: {1}", HostEndPoint, type);
+                        StringComparison c = StringComparison.OrdinalIgnoreCase;
+                        // TODO: Add support for version 2 of UPnP.
+                        if (type.Equals("urn:schemas-upnp-org:service:WANPPPConnection:1", c) ||
+                            type.Equals("urn:schemas-upnp-org:service:WANIPConnection:1", c))
+                        {
+                            this.controlUrl = service["controlURL"].InnerText;
+                            NatUtility.Log("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
+                            try
+                            {
+                                Uri u = new Uri(controlUrl);
+                                if (u.IsAbsoluteUri)
+                                {
+                                    EndPoint old = hostEndPoint;
+                                    this.hostEndPoint = new IPEndPoint(IPAddress.Parse(u.Host), u.Port);
+                                    NatUtility.Log("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
+                                    this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
+                                    NatUtility.Log("{0}: New control url: {1}", HostEndPoint, controlUrl);
+                                }
+                            }
+                            catch
+                            {
+                                NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
+                            }
+                            NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
+                            return;
+                        }
+                    }
+                }
+
+                //If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
+                //So we don't invoke the callback, so this device is never added to our lists
             }
         }
 
@@ -171,10 +220,13 @@ namespace Mono.Nat.Upnp
             get { return serviceType; }
         }
 
-        public override Task CreatePortMap(Mapping mapping)
+        public override async Task CreatePortMap(Mapping mapping)
         {
             CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
-            return _httpClient.SendAsync(message.Encode(), message.Method);
+            using (await _httpClient.SendAsync(message.Encode(), message.Method).ConfigureAwait(false))
+            {
+
+            }
         }
 
         public override bool Equals(object obj)

+ 1 - 1
SharedVersion.cs

@@ -1,3 +1,3 @@
 using System.Reflection;
 
-[assembly: AssemblyVersion("3.2.33.13")]
+[assembly: AssemblyVersion("3.2.33.14")]