using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.ApiInteraction { /// /// Class AsyncHttpClient /// public class AsyncHttpClient : IAsyncHttpClient { /// /// Gets or sets the HTTP client. /// /// The HTTP client. private HttpClient HttpClient { get; set; } /// /// Initializes a new instance of the class. /// public AsyncHttpClient(HttpMessageHandler handler) { HttpClient = new HttpClient(handler); } /// /// Initializes a new instance of the class. /// public AsyncHttpClient() { HttpClient = new HttpClient(); } /// /// Gets the stream async. /// /// The URL. /// The logger. /// The cancellation token. /// Task{Stream}. /// public async Task GetAsync(string url, ILogger logger, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); logger.Debug("Sending Http Get to {0}", url); try { var msg = await HttpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); EnsureSuccessStatusCode(msg); return await msg.Content.ReadAsStreamAsync().ConfigureAwait(false); } catch (HttpRequestException ex) { logger.ErrorException("Error getting response from " + url, ex); throw new HttpException(ex.Message, ex); } catch (OperationCanceledException ex) { throw GetCancellationException(url, cancellationToken, ex, logger); } catch (Exception ex) { logger.ErrorException("Error requesting {0}", ex, url); throw; } } /// /// Posts the async. /// /// The URL. /// Type of the content. /// Content of the post. /// The logger. /// The cancellation token. /// Task{Stream}. /// public async Task PostAsync(string url, string contentType, string postContent, ILogger logger, CancellationToken cancellationToken) { logger.Debug("Sending Http Post to {0}", url); var content = new StringContent(postContent, Encoding.UTF8, contentType); try { var msg = await HttpClient.PostAsync(url, content).ConfigureAwait(false); EnsureSuccessStatusCode(msg); return await msg.Content.ReadAsStreamAsync().ConfigureAwait(false); } catch (HttpRequestException ex) { logger.ErrorException("Error getting response from " + url, ex); throw new HttpException(ex.Message, ex); } catch (OperationCanceledException ex) { throw GetCancellationException(url, cancellationToken, ex, logger); } catch (Exception ex) { logger.ErrorException("Error posting {0}", ex, url); throw; } } /// /// Deletes the async. /// /// The URL. /// The logger. /// The cancellation token. /// Task. public async Task DeleteAsync(string url, ILogger logger, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); logger.Debug("Sending Http Delete to {0}", url); try { using (var msg = await HttpClient.DeleteAsync(url, cancellationToken).ConfigureAwait(false)) { EnsureSuccessStatusCode(msg); } } catch (HttpRequestException ex) { logger.ErrorException("Error getting response from " + url, ex); throw new HttpException(ex.Message, ex); } catch (OperationCanceledException ex) { throw GetCancellationException(url, cancellationToken, ex, logger); } catch (Exception ex) { logger.ErrorException("Error requesting {0}", ex, url); throw; } } /// /// Throws the cancellation exception. /// /// The URL. /// The cancellation token. /// The exception. /// The logger. /// Exception. private Exception GetCancellationException(string url, CancellationToken cancellationToken, OperationCanceledException exception, ILogger logger) { // If the HttpClient's timeout is reached, it will cancel the Task internally if (!cancellationToken.IsCancellationRequested) { var msg = string.Format("Connection to {0} timed out", url); logger.Error(msg); // Throw an HttpException so that the caller doesn't think it was cancelled by user code return new HttpException(msg, exception) { IsTimedOut = true }; } return exception; } /// /// Ensures the success status code. /// /// The response. /// private void EnsureSuccessStatusCode(HttpResponseMessage response) { if (!response.IsSuccessStatusCode) { throw new HttpException(response.ReasonPhrase) { StatusCode = response.StatusCode }; } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) { HttpClient.Dispose(); } } /// /// Sets the authorization header that should be supplied on every request /// /// The header. /// public void SetAuthorizationHeader(string header) { if (string.IsNullOrEmpty(header)) { HttpClient.DefaultRequestHeaders.Remove("Authorization"); } else { HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("MediaBrowser", header); } } } }