using System.Net.Http; using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Net; namespace SpotifyAPI.Web.Http { public class APIConnector : IAPIConnector { private readonly Uri _baseAddress; private readonly IAuthenticator _authenticator; private readonly IJSONSerializer _jsonSerializer; private readonly IHTTPClient _httpClient; private readonly IRetryHandler _retryHandler; private readonly IHTTPLogger _httpLogger; public APIConnector(Uri baseAddress, IAuthenticator authenticator) : this(baseAddress, authenticator, new NewtonsoftJSONSerializer(), new NetHttpClient(), null, null) { } public APIConnector( Uri baseAddress, IAuthenticator authenticator, IJSONSerializer jsonSerializer, IHTTPClient httpClient, IRetryHandler retryHandler, IHTTPLogger httpLogger) { _baseAddress = baseAddress; _authenticator = authenticator; _jsonSerializer = jsonSerializer; _httpClient = httpClient; _retryHandler = retryHandler; _httpLogger = httpLogger; } public Task Delete(Uri uri) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Delete); } public Task Delete(Uri uri, IDictionary parameters) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Delete, parameters); } public Task Delete(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Delete, parameters, body); } public async Task Delete(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); var response = await SendAPIRequestDetailed(uri, HttpMethod.Delete, parameters, body).ConfigureAwait(false); return response.StatusCode; } public Task Get(Uri uri) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Get); } public Task Get(Uri uri, IDictionary parameters) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Get, parameters); } public async Task Get(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); var response = await SendAPIRequestDetailed(uri, HttpMethod.Get, parameters, body).ConfigureAwait(false); return response.StatusCode; } public Task Post(Uri uri) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Post); } public Task Post(Uri uri, IDictionary parameters) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Post, parameters); } public Task Post(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Post, parameters, body); } public Task Post(Uri uri, IDictionary parameters, object body, Dictionary headers) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Post, parameters, body, headers); } public async Task Post(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); var response = await SendAPIRequestDetailed(uri, HttpMethod.Post, parameters, body).ConfigureAwait(false); return response.StatusCode; } public Task Put(Uri uri) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Put); } public Task Put(Uri uri, IDictionary parameters) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Put, parameters); } public Task Put(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); return SendAPIRequest(uri, HttpMethod.Put, parameters, body); } public async Task Put(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); var response = await SendAPIRequestDetailed(uri, HttpMethod.Put, parameters, body).ConfigureAwait(false); return response.StatusCode; } public async Task PutRaw(Uri uri, IDictionary parameters, object body) { Ensure.ArgumentNotNull(uri, nameof(uri)); var response = await SendRawRequest(uri, HttpMethod.Put, parameters, body).ConfigureAwait(false); return response.StatusCode; } public void SetRequestTimeout(TimeSpan timeout) { _httpClient.SetRequestTimeout(timeout); } private IRequest CreateRequest( Uri uri, HttpMethod method, IDictionary parameters, object body, IDictionary headers ) { Ensure.ArgumentNotNull(uri, nameof(uri)); Ensure.ArgumentNotNull(method, nameof(method)); return new Request(headers ?? new Dictionary(), parameters ?? new Dictionary()) { BaseAddress = _baseAddress, Endpoint = uri, Method = method, Body = body }; } private async Task> DoSerializedRequest(IRequest request) { _jsonSerializer.SerializeRequest(request); var response = await DoRequest(request).ConfigureAwait(false); return _jsonSerializer.DeserializeResponse(response); } private async Task DoRequest(IRequest request) { await ApplyAuthenticator(request).ConfigureAwait(false); _httpLogger?.OnRequest(request); IResponse response = await _httpClient.DoRequest(request).ConfigureAwait(false); _httpLogger?.OnResponse(response); if (_retryHandler != null) { response = await _retryHandler.HandleRetry(request, response, async (newRequest) => { await ApplyAuthenticator(request).ConfigureAwait(false); var newResponse = await _httpClient.DoRequest(request).ConfigureAwait(false); _httpLogger?.OnResponse(newResponse); return newResponse; }).ConfigureAwait(false); } ProcessErrors(response); return response; } private async Task ApplyAuthenticator(IRequest request) { if (_authenticator != null && !request.Endpoint.IsAbsoluteUri || request.Endpoint.AbsoluteUri.Contains("https://api.spotify.com", StringComparison.InvariantCulture)) { await _authenticator.Apply(request, this).ConfigureAwait(false); } } public Task SendRawRequest( Uri uri, HttpMethod method, IDictionary parameters = null, object body = null, IDictionary headers = null ) { var request = CreateRequest(uri, method, parameters, body, headers); return DoRequest(request); } public async Task SendAPIRequest( Uri uri, HttpMethod method, IDictionary parameters = null, object body = null, IDictionary headers = null ) { var request = CreateRequest(uri, method, parameters, body, headers); IAPIResponse apiResponse = await DoSerializedRequest(request).ConfigureAwait(false); return apiResponse.Body; } public async Task SendAPIRequestDetailed( Uri uri, HttpMethod method, IDictionary parameters = null, object body = null, IDictionary headers = null ) { var request = CreateRequest(uri, method, parameters, body, headers); var response = await DoSerializedRequest(request).ConfigureAwait(false); return response.Response; } private static void ProcessErrors(IResponse response) { Ensure.ArgumentNotNull(response, nameof(response)); if ((int)response.StatusCode >= 200 && (int)response.StatusCode < 400) { return; } throw response.StatusCode switch { HttpStatusCode.Unauthorized => new APIUnauthorizedException(response), _ => new APIException(response), }; } } }