/// <summary> /// Makes the API request /// </summary> /// <typeparam name="T">The type of response</typeparam> /// <param name="command">The command to call.</param> /// <param name="settings">The app id.</param> /// <param name="querystring">The querystring params.</param> /// <param name="rawDataHandler">The convertion handler for the data received.</param> /// <param name="requestHeaders">HTTP headers to add to the request</param> /// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <returns>A response</returns> public Task <Response <T> > SendRequestAsync <T>( MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > querystring, Func <string, T> rawDataHandler, Dictionary <string, string> requestHeaders, CancellationToken?cancellationToken) { this._lastSettings = settings; this._queryString = querystring; // Ensure URI building is exercised... Uri uri = this.UriBuilder.BuildUri(command, settings, querystring); // Ensure we call this method to make // sure the code gets a run through... string body = command.BuildRequestBody(); if (this._responseInfo != null) { command.SetAdditionalResponseInfo(this._responseInfo); } var response = this.NextFakeResponse.GetResponseOf <T>(); return(Task.FromResult(response)); }
/// <summary> /// Builds an API URI /// </summary> /// <param name="command">The command to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="queryParams">The querystring parameters</param> /// <returns> /// A Uri to call /// </returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown when an unknown method is used</exception> /// <exception cref="CountryCodeRequiredException">Thrown when a CountryCode is required but not supplied</exception> /// <exception cref="ApiCredentialsRequiredException">Thrown when an API Key has not been supplied</exception> /// <exception cref="System.ArgumentNullException"></exception> public Uri BuildUri(MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > queryParams) { if (command == null) { throw new ArgumentNullException("command"); } if (settings == null) { throw new ArgumentNullException("settings"); } // Build API url var url = new StringBuilder(); url.Append(command.BaseApiUri); this.AddCountryCode(url, command, settings.CountryCode); command.AppendUriPath(url); if (!command.RequiresEmptyQuerystring) { this.AppendQueryString(url, command, settings, queryParams); } return(new Uri(url.ToString())); }
/// <summary> /// Sets the request id for the given command /// </summary> /// <param name="cmd">The command</param> /// <param name="requestId">The request id</param> private void SetRequestId(MusicClientCommand cmd, Guid?requestId) { if (requestId.HasValue) { cmd.RequestId = requestId.Value; } }
/// <summary> /// Adds settings required for building a secure command /// </summary> /// <typeparam name="TIntermediate">The type of the intermediate object.</typeparam> /// <typeparam name="TResult">The type of the returned object.</typeparam> /// <param name="command">The command </param> /// <param name="requiresOauth">If true add oAuth headers. If false does not add oAuth headers</param> /// <returns>A task</returns> private async Task SetupSecureCommandAsync <TIntermediate, TResult>(MusicClientCommand <TIntermediate, TResult> command, bool requiresOauth = true) where TResult : Response { if (requiresOauth && this.AuthHeaderDataProvider != null) { command.UserId = await this.AuthHeaderDataProvider.GetUserIdAsync().ConfigureAwait(false); command.OAuth2 = new OAuth2(this.AuthHeaderDataProvider); } command.BaseApiUri = this.SecureApiBaseUrl; }
/// <summary> /// Makes the API request /// </summary> /// <typeparam name="T">The type of response</typeparam> /// <param name="command">The command to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <returns>A response for the API request.</returns> public async Task <Response <T> > SendRequestAsync <T>( MusicClientCommand <T> command, IMusicClientSettings settings, CancellationToken?cancellationToken) { return(await this.SendRequestAsync( command, settings, command.BuildQueryStringParams(), command.HandleRawData, await command.BuildRequestHeadersAsync(), cancellationToken)); }
/// <summary> /// Builds an API URI /// </summary> /// <param name="command">The command to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="querystringParams">The querystring parameters.</param> /// <returns> /// A Uri to call /// </returns> public Uri BuildUri(MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > querystringParams) { DirectoryInfo jsonDir = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, @"..\..\json")); if (jsonDir.Exists) { FileInfo[] json = jsonDir.GetFiles(this._filename); if (json.Length > 0) { return(new Uri("file://" + json[0].FullName.Replace(@"\", @"/"))); } } throw new FileNotFoundException("Could not find required test file in " + jsonDir.FullName); }
/// <summary> /// Validates and adds country code if required /// </summary> /// <param name="url">The url being built</param> /// <param name="command">The command to call.</param> /// <param name="countryCode">The country code.</param> protected virtual void AddCountryCode(StringBuilder url, MusicClientCommand command, string countryCode) { if (command.RequiresCountryCode && !command.UseBlankTerritory) { if (string.IsNullOrEmpty(countryCode)) { throw new CountryCodeRequiredException(); } url.AppendFormat("{0}/", countryCode); } else if (command.UseBlankTerritory) { url.AppendFormat("-/"); } }
/// <summary> /// Appends the appropriate query string parameters to the url /// </summary> /// <param name="url">The url being built.</param> /// <param name="command">The command for the url being built</param> /// <param name="settings">The music client settings.</param> /// <param name="queryParams">The query string.</param> private void AppendQueryString(StringBuilder url, MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > queryParams) { // Add required parameters this.AddAuthorisationParams(url, settings); url.AppendFormat("&domain=music"); if (!string.IsNullOrWhiteSpace(settings.Language)) { url.AppendFormat("&lang={0}", settings.Language); } // Add other parameters... if (queryParams != null) { foreach (KeyValuePair <string, string> pair in queryParams) { url.AppendFormat("&{0}={1}", pair.Key, pair.Value == null ? string.Empty : Uri.EscapeDataString(pair.Value)); } } }
/// <summary> /// Builds and gzips the request body if required /// </summary> /// <param name="command">The command</param> /// <param name="request">The request information</param> private static void BuildRequestBody(MusicClientCommand command, HttpRequestMessage request) { var requestBody = command.BuildRequestBody(); if (requestBody != null) { var content = new StringContent(requestBody, Encoding.UTF8); if (command.GzipRequestBody) { request.Content = new GzippedContent(content); request.Content.Headers.Add("Content-Encoding", "gzip"); } else { request.Content = content; } if (!string.IsNullOrWhiteSpace(command.ContentType)) { request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(command.ContentType); } } }
/// <summary> /// Makes the API request /// </summary> /// <typeparam name="T">The type of response</typeparam> /// <param name="command">The command to call.</param> /// <param name="settings">The app id.</param> /// <param name="querystring">The querystring params.</param> /// <param name="callback">The callback to hit when done.</param> /// <param name="requestHeaders">HTTP headers to add to the request</param> public void SendRequestAsync <T>( MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > querystring, IResponseCallback <T> callback, Dictionary <string, string> requestHeaders = null) { this._lastSettings = settings; this._queryString = querystring; // Ensure URI building is exercised... Uri uri = this.UriBuilder.BuildUri(command, settings, querystring); // Ensure we call this method to make // sure the code gets a run through... string body = command.BuildRequestBody(); if (this._responseInfo != null) { command.SetAdditionalResponseInfo(this._responseInfo); } this.NextFakeResponse.DoCallback <T>(callback); }
/// <summary> /// Builds an API URI /// </summary> /// <param name="command">The method to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="querystringParams">The querystring parameters.</param> /// <returns> /// A Uri to call /// </returns> public Uri BuildUri(MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > querystringParams) { return(this._uri); }
/// <summary> /// Makes the API request /// </summary> /// <typeparam name="T">The type of response item</typeparam> /// <param name="command">The command to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="queryParams">The querystring.</param> /// <param name="callback">The callback to hit when done.</param> /// <param name="requestHeaders">HTTP headers to add to the request</param> /// <exception cref="System.ArgumentNullException">Thrown when no callback is specified</exception> public void SendRequestAsync <T>( MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > queryParams, IResponseCallback <T> callback, Dictionary <string, string> requestHeaders = null) { if (callback == null) { throw new ArgumentNullException("callback"); } Uri uri = this.UriBuilder.BuildUri(command, settings, queryParams); DebugLogger.Instance.WriteLog("Calling {0}", uri.ToString()); TimedRequest request = new TimedRequest(uri); this.AddRequestHeaders(request.WebRequest, requestHeaders); this.TryBuildRequestBody( (IAsyncResult ar) => { if (request.HasTimedOut) { return; } WebResponse response = null; HttpWebResponse webResponse = null; T responseItem = default(T); HttpStatusCode?statusCode = null; Exception error = null; string responseBody = null; try { response = request.WebRequest.EndGetResponse(ar); webResponse = response as HttpWebResponse; if (webResponse != null) { statusCode = webResponse.StatusCode; command.SetAdditionalResponseInfo(new ResponseInfo(webResponse.ResponseUri, webResponse.Headers)); // Capture Server Time offset if we haven't already... this.DeriveServerTimeOffset(webResponse.Headers); } } catch (WebException ex) { DebugLogger.Instance.WriteVerboseInfo("Web Exception: {0} when calling {1}", ex, uri); error = ex; if (ex.Response != null) { response = ex.Response; webResponse = (HttpWebResponse)ex.Response; statusCode = webResponse.StatusCode; } } string contentType = null; if (response != null) { contentType = response.ContentType; try { using (Stream responseStream = this._gzipHandler.GetResponseStream(response)) { responseBody = responseStream.AsString(); responseItem = callback.ConvertFromRawResponse(responseBody); } } catch (Exception ex) { DebugLogger.Instance.WriteVerboseInfo("Exception: {0} trying to handle response stream when calling {1}", ex, uri); error = ex; responseItem = default(T); } } DoCallback(callback.Callback, responseItem, statusCode, contentType, error, responseBody, command.RequestId, uri, IsFake404(response)); }, () => DoCallback(callback.Callback, default(T), null, null, new ApiCallFailedException(), null, command.RequestId, uri), request, command); }
private void TryBuildRequestBody(AsyncCallback requestSuccessCallback, Action requestTimeoutCallback, TimedRequest request, MusicClientCommand apiMethod) { var requestBody = apiMethod.BuildRequestBody(); request.WebRequest.Method = apiMethod.HttpMethod.ToString().ToUpperInvariant(); if (requestBody != null) { var requestState = new RequestState(request, requestBody, requestSuccessCallback, requestTimeoutCallback); request.WebRequest.ContentType = apiMethod.ContentType; request.WebRequest.BeginGetRequestStream(this.RequestStreamCallback, requestState); } else { // No request body, just make the request immediately request.BeginGetResponse(requestSuccessCallback, requestTimeoutCallback, request); } }
/// <summary> /// Makes the API request /// </summary> /// <typeparam name="T">The type of response</typeparam> /// <param name="command">The command to call.</param> /// <param name="settings">The music client settings.</param> /// <param name="queryParams">The querystring.</param> /// <param name="rawDataHandler">The convertion handler for the data received.</param> /// <param name="requestHeaders">HTTP headers to add to the request</param> /// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <returns>A response for the API request.</returns> public async Task <Response <T> > SendRequestAsync <T>( MusicClientCommand command, IMusicClientSettings settings, List <KeyValuePair <string, string> > queryParams, Func <string, T> rawDataHandler, Dictionary <string, string> requestHeaders, CancellationToken?cancellationToken) { Uri uri = command.RawUri ?? this.UriBuilder.BuildUri(command, settings, queryParams); DebugLogger.Instance.WriteLog("Calling {0}", uri.ToString()); HttpRequestMessage request = new HttpRequestMessage(command.HttpMethod, uri); HttpMessageHandler handler = this.CreateHandler(command.FollowHttpRedirects); this.AddRequestHeaders(request, requestHeaders, settings); BuildRequestBody(command, request); using (HttpClient client = new HttpClient(handler)) { client.Timeout = TimeSpan.FromMilliseconds(MusicClient.RequestTimeout); HttpStatusCode?statusCode = null; bool? mixRadioHeaderFound = null; var activeCancellationToken = cancellationToken ?? CancellationToken.None; try { activeCancellationToken.ThrowIfCancellationRequested(); using (HttpResponseMessage response = await this._requestProxy.SendRequestAsync(client, request, activeCancellationToken).ConfigureAwait(false)) { statusCode = response.StatusCode; if (command.ExpectsMixRadioHeader) { mixRadioHeaderFound = response.Headers.Contains("X-MixRadio"); } var headers = new Dictionary <string, IEnumerable <string> >(); foreach (var header in response.Headers) { headers.Add(header.Key, header.Value.ToArray()); } //// Capture Server Time offset if we haven't already... this.DeriveServerTimeOffset(response.Headers.Date, response.Headers.Age); command.SetAdditionalResponseInfo(new ResponseInfo(response.RequestMessage.RequestUri, headers)); using (var content = response.Content) { string contentType = content.Headers.ContentType != null ? content.Headers.ContentType.MediaType : null; string responseBody = await content.ReadAsStringAsync().ConfigureAwait(false); if (!response.IsSuccessStatusCode && !IsFake404(response)) { DebugLogger.Instance.WriteException( new ApiCallFailedException(response.StatusCode), new KeyValuePair <string, string>("uri", uri.ToString()), new KeyValuePair <string, string>("errorResponseBody", responseBody), new KeyValuePair <string, string>("statusCode", statusCode.ToString()), new KeyValuePair <string, string>("mixRadioHeaderFound", mixRadioHeaderFound.HasValue ? mixRadioHeaderFound.ToString() : "unknown")); } T responseItem = rawDataHandler(responseBody); return(PrepareResponse(responseItem, statusCode, contentType, null, responseBody, command.RequestId, uri, mixRadioHeaderFound, IsFake404(response))); } } } catch (Exception ex) { if (ex is OperationCanceledException) { if (activeCancellationToken.IsCancellationRequested) { DebugLogger.Instance.WriteLog("OperationCanceledException thrown due to operation being cancelled."); return(PrepareResponse(default(T), statusCode, null, new ApiCallCancelledException(), null, command.RequestId, uri, false)); } else { DebugLogger.Instance.WriteLog("OperationCanceledException thrown without operation being cancelled."); // Because Xamarin.Android fails on limited networks by cancelling the task. return(PrepareResponse(default(T), statusCode, null, new NetworkLimitedException(), null, command.RequestId, uri, false)); } } if (mixRadioHeaderFound.HasValue && !mixRadioHeaderFound.Value) { return(PrepareResponse(default(T), statusCode, null, new NetworkLimitedException(), null, command.RequestId, uri, false)); } // This is a way to check if an SSL certificate has failed. // WebExceptionStatus.TrustFailure is not supported by PCL (http://msdn.microsoft.com/en-us/library/system.net.webexceptionstatus.aspx) var webException = ex as WebException; if (webException != null && webException.Status == WebExceptionStatus.SendFailure && !mixRadioHeaderFound.HasValue) { return(PrepareResponse(default(T), statusCode, null, new SendFailureException(), null, command.RequestId, uri, false)); } DebugLogger.Instance.WriteException( new ApiCallFailedException(statusCode), new KeyValuePair <string, string>("uri", uri.ToString()), new KeyValuePair <string, string>("statusCode", statusCode.HasValue ? statusCode.ToString() : "Timeout"), new KeyValuePair <string, string>("mixRadioHeaderFound", mixRadioHeaderFound.HasValue ? mixRadioHeaderFound.ToString() : "unknown")); return(PrepareResponse(default(T), statusCode, null, ex, null, command.RequestId, uri, mixRadioHeaderFound)); } } }