Пример #1
0
        /// <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));
        }
Пример #2
0
        /// <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()));
        }
Пример #3
0
 /// <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;
     }
 }
Пример #4
0
        /// <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;
        }
Пример #5
0
 /// <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));
 }
Пример #6
0
        /// <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);
        }
Пример #7
0
        /// <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("-/");
            }
        }
Пример #8
0
        /// <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));
                }
            }
        }
Пример #9
0
        /// <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);
                }
            }
        }
Пример #10
0
        /// <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);
        }
Пример #11
0
 /// <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);
 }
Пример #12
0
        /// <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);
        }
Пример #13
0
        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);
            }
        }
Пример #14
0
        /// <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));
                }
            }
        }