diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 1ad99fac5b..780b0a8897 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs @@ -34,16 +34,13 @@ namespace Emby.Dlna.PlayTo { var cancellationToken = CancellationToken.None; - using (var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header, logRequest, cancellationToken) + var url = NormalizeServiceUrl(baseUrl, service.ControlUrl); + using (var response = await PostSoapDataAsync(url, '\"' + service.ServiceType + '#' + command + '\"', postData, header, logRequest, cancellationToken) .ConfigureAwait(false)) + using (var stream = response.Content) + using (var reader = new StreamReader(stream, Encoding.UTF8)) { - using (var stream = response.Content) - { - using (var reader = new StreamReader(stream, Encoding.UTF8)) - { - return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); - } - } + return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace); } } @@ -121,15 +118,18 @@ namespace Emby.Dlna.PlayTo } } - private Task<HttpResponseInfo> PostSoapDataAsync(string url, + private Task<HttpResponseInfo> PostSoapDataAsync( + string url, string soapAction, string postData, string header, bool logRequest, CancellationToken cancellationToken) { - if (!soapAction.StartsWith("\"")) - soapAction = "\"" + soapAction + "\""; + if (soapAction[0] != '\"') + { + soapAction = '\"' + soapAction + '\"'; + } var options = new HttpRequestOptions { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 02d661d15a..c64922a931 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -430,7 +430,7 @@ namespace Emby.Server.Implementations /// Gets the current application user agent /// </summary> /// <value>The application user agent.</value> - public string ApplicationUserAgent => Name.Replace(' ','-') + "/" + ApplicationVersion; + public string ApplicationUserAgent => Name.Replace(' ','-') + '/' + ApplicationVersion; /// <summary> /// Gets the email address for use within a comment section of a user agent field. @@ -690,11 +690,6 @@ namespace Emby.Server.Implementations await HttpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, CancellationToken.None).ConfigureAwait(false); } - protected virtual IHttpClient CreateHttpClient() - { - return new HttpClientManager.HttpClientManager(ApplicationPaths, LoggerFactory, FileSystemManager, () => ApplicationUserAgent); - } - public static IStreamHelper StreamHelper { get; set; } /// <summary> @@ -720,7 +715,11 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton<TvDbClientManager>(); - HttpClient = CreateHttpClient(); + HttpClient = new HttpClientManager.HttpClientManager( + ApplicationPaths, + LoggerFactory.CreateLogger<HttpClientManager.HttpClientManager>(), + FileSystemManager, + () => ApplicationUserAgent); serviceCollection.AddSingleton(HttpClient); serviceCollection.AddSingleton(NetworkManager); diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index b82d55d0e6..ae62f34e06 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -4,8 +4,6 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -23,30 +21,24 @@ namespace Emby.Server.Implementations.HttpClientManager /// </summary> public class HttpClientManager : IHttpClient { - /// <summary> - /// When one request to a host times out, we'll ban all other requests for this period of time, to prevent scans from stalling - /// </summary> - private const int TimeoutSeconds = 30; - - /// <summary> - /// The _logger - /// </summary> private readonly ILogger _logger; - - /// <summary> - /// The _app paths - /// </summary> private readonly IApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; private readonly Func<string> _defaultUserAgentFn; + /// <summary> + /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. + /// DON'T dispose it after use. + /// </summary> + /// <value>The HTTP clients.</value> + private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>(); + /// <summary> /// Initializes a new instance of the <see cref="HttpClientManager" /> class. /// </summary> public HttpClientManager( IApplicationPaths appPaths, - ILoggerFactory loggerFactory, + ILogger<HttpClientManager> logger, IFileSystem fileSystem, Func<string> defaultUserAgentFn) { @@ -55,41 +47,28 @@ namespace Emby.Server.Implementations.HttpClientManager throw new ArgumentNullException(nameof(appPaths)); } - if (loggerFactory == null) + if (logger == null) { - throw new ArgumentNullException(nameof(loggerFactory)); + throw new ArgumentNullException(nameof(logger)); } - _logger = loggerFactory.CreateLogger(nameof(HttpClientManager)); + _logger = logger; _fileSystem = fileSystem; _appPaths = appPaths; _defaultUserAgentFn = defaultUserAgentFn; - - // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c - ServicePointManager.Expect100Continue = false; } /// <summary> - /// Holds a dictionary of http clients by host. Use GetHttpClient(host) to retrieve or create a client for web requests. - /// DON'T dispose it after use. - /// </summary> - /// <value>The HTTP clients.</value> - private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>(); - - /// <summary> - /// Gets + /// Gets the correct http client for the given url. /// </summary> - /// <param name="url">The host.</param> - /// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param> + /// <param name="url">The url.</param> /// <returns>HttpClient.</returns> - /// <exception cref="ArgumentNullException">host</exception> - private HttpClient GetHttpClient(string url, bool enableHttpCompression) + private HttpClient GetHttpClient(string url) { - var key = GetHostFromUrl(url) + enableHttpCompression; + var key = GetHostFromUrl(url); if (!_httpClients.TryGetValue(key, out var client)) { - client = new HttpClient() { BaseAddress = new Uri(url) @@ -109,24 +88,26 @@ namespace Emby.Server.Implementations.HttpClientManager if (!string.IsNullOrWhiteSpace(userInfo)) { _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url); - url = url.Replace(userInfo + "@", string.Empty); + url = url.Replace(userInfo + '@', string.Empty); } var request = new HttpRequestMessage(method, url); AddRequestHeaders(request, options); - if (options.EnableHttpCompression) + switch (options.DecompressionMethod) { - if (options.DecompressionMethod.HasValue - && options.DecompressionMethod.Value == CompressionMethod.Gzip) - { + case CompressionMethod.Deflate | CompressionMethod.Gzip: request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" }); - } - else - { + break; + case CompressionMethod.Deflate: request.Headers.Add(HeaderNames.AcceptEncoding, "deflate"); - } + break; + case CompressionMethod.Gzip: + request.Headers.Add(HeaderNames.AcceptEncoding, "gzip"); + break; + default: + break; } if (options.EnableKeepAlive) @@ -134,20 +115,8 @@ namespace Emby.Server.Implementations.HttpClientManager request.Headers.Add(HeaderNames.Connection, "Keep-Alive"); } - if (!string.IsNullOrEmpty(options.Host)) - { - request.Headers.Add(HeaderNames.Host, options.Host); - } - - if (!string.IsNullOrEmpty(options.Referer)) - { - request.Headers.Add(HeaderNames.Referer, options.Referer); - } - //request.Headers.Add(HeaderNames.CacheControl, "no-cache"); - //request.Headers.Add(HeaderNames., options.TimeoutMs; - /* if (!string.IsNullOrWhiteSpace(userInfo)) { @@ -188,9 +157,7 @@ namespace Emby.Server.Implementations.HttpClientManager /// <param name="options">The options.</param> /// <returns>Task{HttpResponseInfo}.</returns> public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options) - { - return SendAsync(options, HttpMethod.Get); - } + => SendAsync(options, HttpMethod.Get); /// <summary> /// Performs a GET request and returns the resulting stream @@ -209,8 +176,6 @@ namespace Emby.Server.Implementations.HttpClientManager /// <param name="options">The options.</param> /// <param name="httpMethod">The HTTP method.</param> /// <returns>Task{HttpResponseInfo}.</returns> - /// <exception cref="HttpException"> - /// </exception> public Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod) { var httpMethod2 = GetHttpMethod(httpMethod); @@ -223,8 +188,6 @@ namespace Emby.Server.Implementations.HttpClientManager /// <param name="options">The options.</param> /// <param name="httpMethod">The HTTP method.</param> /// <returns>Task{HttpResponseInfo}.</returns> - /// <exception cref="HttpException"> - /// </exception> public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod) { if (options.CacheMode == CacheMode.None) @@ -324,32 +287,37 @@ namespace Emby.Server.Implementations.HttpClientManager options.CancellationToken.ThrowIfCancellationRequested(); - var client = GetHttpClient(options.Url, options.EnableHttpCompression); + var client = GetHttpClient(options.Url); var httpWebRequest = GetRequestMessage(options, httpMethod); - if (options.RequestContentBytes != null || - !string.IsNullOrEmpty(options.RequestContent) || - httpMethod == HttpMethod.Post) + if (options.RequestContentBytes != null + || !string.IsNullOrEmpty(options.RequestContent) + || httpMethod == HttpMethod.Post) { - try + if (options.RequestContentBytes != null) { - httpWebRequest.Content = new StringContent(Encoding.UTF8.GetString(options.RequestContentBytes) ?? options.RequestContent ?? string.Empty); - - var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; + httpWebRequest.Content = new ByteArrayContent(options.RequestContentBytes); + } + else if (options.RequestContent != null) + { + httpWebRequest.Content = new StringContent(options.RequestContent); + } + else + { + httpWebRequest.Content = new ByteArrayContent(Array.Empty<byte>()); + } - if (options.AppendCharsetToMimeType) - { - contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; - } + // TODO: add correct content type + /* + var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; - httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType); - await client.SendAsync(httpWebRequest).ConfigureAwait(false); - } - catch (Exception ex) + if (options.AppendCharsetToMimeType) { - throw new HttpException(ex.Message) { IsTimedOut = true }; + contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\""; } + + httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);*/ } if (options.LogRequest) @@ -357,92 +325,53 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url); } - try - { - options.CancellationToken.ThrowIfCancellationRequested(); + options.CancellationToken.ThrowIfCancellationRequested(); - /*if (!options.BufferContent) - { - var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false); + if (!options.BufferContent) + { + var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false); - await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false); + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - options.CancellationToken.ThrowIfCancellationRequested(); - - return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response); - }*/ + options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false)) + var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return new HttpResponseInfo(response.Headers) { - await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - - options.CancellationToken.ThrowIfCancellationRequested(); - - using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - { - var memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream).ConfigureAwait(false); - memoryStream.Position = 0; - - return GetResponseInfo(response, memoryStream, memoryStream.Length, null); - } - } - } - catch (OperationCanceledException ex) - { - throw GetCancellationException(options, options.CancellationToken, ex); - } - } - - private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable) - { - var responseInfo = new HttpResponseInfo(disposable) - { - Content = content, - StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.Content.Headers.ContentType?.MediaType, - ContentLength = contentLength, - ResponseUrl = httpResponse.Content.Headers.ContentLocation?.ToString() - }; - - if (httpResponse.Headers != null) - { - SetHeaders(httpResponse.Content.Headers, responseInfo); + Content = stream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = stream.Length, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; } - return responseInfo; - } - - private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, string tempFile, long? contentLength) - { - var responseInfo = new HttpResponseInfo + using (var response = await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false)) { - TempFilePath = tempFile, - StatusCode = httpResponse.StatusCode, - ContentType = httpResponse.Content.Headers.ContentType?.MediaType, - ContentLength = contentLength - }; + await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); - if (httpResponse.Headers != null) - { - SetHeaders(httpResponse.Content.Headers, responseInfo); - } + options.CancellationToken.ThrowIfCancellationRequested(); - return responseInfo; - } + using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + { + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); + memoryStream.Position = 0; - private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo) - { - foreach (var key in headers) - { - responseInfo.Headers[key.Key] = string.Join(", ", key.Value); + return new HttpResponseInfo(response.Headers) + { + Content = memoryStream, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = memoryStream.Length, + ResponseUrl = response.Content.Headers.ContentLocation?.ToString() + }; + } } } public Task<HttpResponseInfo> Post(HttpRequestOptions options) - { - return SendAsync(options, HttpMethod.Post); - } + => SendAsync(options, HttpMethod.Post); /// <summary> /// Downloads the contents of a given url into a temporary location @@ -451,10 +380,8 @@ namespace Emby.Server.Implementations.HttpClientManager /// <returns>Task{System.String}.</returns> public async Task<string> GetTempFile(HttpRequestOptions options) { - using (var response = await GetTempFileResponse(options).ConfigureAwait(false)) - { - return response.TempFilePath; - } + var response = await GetTempFileResponse(options).ConfigureAwait(false); + return response.TempFilePath; } public async Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options) @@ -481,13 +408,13 @@ namespace Emby.Server.Implementations.HttpClientManager _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url); } - var client = GetHttpClient(options.Url, options.EnableHttpCompression); + var client = GetHttpClient(options.Url); try { options.CancellationToken.ThrowIfCancellationRequested(); - using (var response = (await client.SendAsync(httpWebRequest).ConfigureAwait(false))) + using (var response = (await client.SendAsync(httpWebRequest, options.CancellationToken).ConfigureAwait(false))) { await EnsureSuccessStatusCode(response, options).ConfigureAwait(false); @@ -501,8 +428,15 @@ namespace Emby.Server.Implementations.HttpClientManager options.Progress.Report(100); - var contentLength = response.Content.Headers.ContentLength; - return GetResponseInfo(response, tempFile, contentLength); + var responseInfo = new HttpResponseInfo(response.Headers) + { + TempFilePath = tempFile, + StatusCode = response.StatusCode, + ContentType = response.Content.Headers.ContentType?.MediaType, + ContentLength = response.Content.Headers.ContentLength + }; + + return responseInfo; } } catch (Exception ex) @@ -530,7 +464,7 @@ namespace Emby.Server.Implementations.HttpClientManager { if (options.LogErrors) { - _logger.LogError(webException, "Error {status} getting response from {url}", webException.Status, options.Url); + _logger.LogError(webException, "Error {Status} getting response from {Url}", webException.Status, options.Url); } var exception = new HttpException(webException.Message, webException); @@ -565,7 +499,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (options.LogErrors) { - _logger.LogError(ex, "Error getting response from {url}", options.Url); + _logger.LogError(ex, "Error getting response from {Url}", options.Url); } return ex; @@ -639,7 +573,7 @@ namespace Emby.Server.Implementations.HttpClientManager } var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - _logger.LogError(msg); + _logger.LogError("HTTP request failed with message: {Message}", msg); throw new HttpException(response.ReasonPhrase) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index dd636e6cdf..8dee7046e7 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV UserAgent = "Emby/3.0", // Shouldn't matter but may cause issues - EnableHttpCompression = false + DecompressionMethod = CompressionMethod.None }; using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false)) diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index f3f7477180..f5dffc22af 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -627,15 +627,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.EnableHttpCompression = true; - - // On windows 7 under .net core, this header is not getting added -#if NETSTANDARD2_0 - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; - } -#endif + options.DecompressionMethod = CompressionMethod.Deflate; try { @@ -665,15 +657,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings ListingsProviderInfo providerInfo) { // Schedules direct requires that the client support compression and will return a 400 response without it - options.EnableHttpCompression = true; - - // On windows 7 under .net core, this header is not getting added -#if NETSTANDARD2_0 - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - options.RequestHeaders[HeaderNames.AcceptEncoding] = "deflate"; - } -#endif + options.DecompressionMethod = CompressionMethod.Deflate; try { diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 69b10e6daa..d39c917835 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -73,11 +73,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings CancellationToken = cancellationToken, Url = path, Progress = new SimpleProgress<double>(), - DecompressionMethod = CompressionMethod.Gzip, - // It's going to come back gzipped regardless of this value // So we need to make sure the decompression method is set to gzip - EnableHttpCompression = true, + DecompressionMethod = CompressionMethod.Gzip, UserAgent = "Emby/3.0" diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index e8b34da0ca..60c914898c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts CancellationToken = CancellationToken.None, BufferContent = false, - EnableHttpCompression = false, + DecompressionMethod = CompressionMethod.None, }; foreach (var header in mediaSource.RequiredHttpHeaders) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index d4b10c8c84..95ce08f269 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -118,8 +118,20 @@ namespace Jellyfin.Server SQLitePCL.Batteries_V2.Init(); + // Increase the max http request limit + // The default connection limit is 10 for ASP.NET hosted applications and 2 for all others. + ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit); + + // Disable the "Expect: 100-Continue" header by default + // http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c + ServicePointManager.Expect100Continue = false; + +// CA5359: Do Not Disable Certificate Validation +#pragma warning disable CA5359 + // Allow all https requests ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }); +#pragma warning restore CA5359 var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths); diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 38e0ff0f55..0576a1a5d9 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using Microsoft.Net.Http.Headers; @@ -17,7 +16,7 @@ namespace MediaBrowser.Common.Net /// <value>The URL.</value> public string Url { get; set; } - public CompressionMethod? DecompressionMethod { get; set; } + public CompressionMethod DecompressionMethod { get; set; } /// <summary> /// Gets or sets the accept header. @@ -49,13 +48,21 @@ namespace MediaBrowser.Common.Net /// Gets or sets the referrer. /// </summary> /// <value>The referrer.</value> - public string Referer { get; set; } + public string Referer + { + get => GetHeaderValue(HeaderNames.Referer); + set => RequestHeaders[HeaderNames.Referer] = value; + } /// <summary> /// Gets or sets the host. /// </summary> /// <value>The host.</value> - public string Host { get; set; } + public string Host + { + get => GetHeaderValue(HeaderNames.Host); + set => RequestHeaders[HeaderNames.Host] = value; + } /// <summary> /// Gets or sets the progress. @@ -63,12 +70,6 @@ namespace MediaBrowser.Common.Net /// <value>The progress.</value> public IProgress<double> Progress { get; set; } - /// <summary> - /// Gets or sets a value indicating whether [enable HTTP compression]. - /// </summary> - /// <value><c>true</c> if [enable HTTP compression]; otherwise, <c>false</c>.</value> - public bool EnableHttpCompression { get; set; } - public Dictionary<string, string> RequestHeaders { get; private set; } public string RequestContentType { get; set; } @@ -104,13 +105,12 @@ namespace MediaBrowser.Common.Net /// </summary> public HttpRequestOptions() { - EnableHttpCompression = true; - RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); LogRequest = true; LogErrors = true; CacheMode = CacheMode.None; + DecompressionMethod = CompressionMethod.Deflate; } } @@ -120,9 +120,11 @@ namespace MediaBrowser.Common.Net Unconditional = 1 } + [Flags] public enum CompressionMethod { - Deflate, - Gzip + None = 0b00000001, + Deflate = 0b00000010, + Gzip = 0b00000100 } } diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs index 1866741676..cd9feabfe0 100644 --- a/MediaBrowser.Common/Net/HttpResponseInfo.cs +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Http.Headers; namespace MediaBrowser.Common.Net { @@ -50,26 +50,21 @@ namespace MediaBrowser.Common.Net /// Gets or sets the headers. /// </summary> /// <value>The headers.</value> - public Dictionary<string, string> Headers { get; set; } + public HttpResponseHeaders Headers { get; set; } - private readonly IDisposable _disposable; - - public HttpResponseInfo(IDisposable disposable) + public HttpResponseInfo() { - _disposable = disposable; - Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } - public HttpResponseInfo() + + public HttpResponseInfo(HttpResponseHeaders headers) { - Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + Headers = headers; } public void Dispose() { - if (_disposable != null) - { - _disposable.Dispose(); - } + // Only IDisposable for backwards compatibility } } }