internal RequestState(TimedRequest request, string requestBody, AsyncCallback successCallback, Action timeoutCallback)
 {
     this.TimedRequest    = request;
     this.RequestBody     = requestBody;
     this.SuccessCallback = successCallback;
     this.TimeoutCallback = timeoutCallback;
 }
        /// <summary>
        /// Writes request data to the request stream
        /// </summary>
        /// <param name="ar">The async response</param>
        private void RequestStreamCallback(IAsyncResult ar)
        {
            var requestState = (RequestState)ar.AsyncState;

            try
            {
                Stream streamResponse = requestState.TimedRequest.WebRequest.EndGetRequestStream(ar);
                byte[] byteArray      = Encoding.UTF8.GetBytes(requestState.RequestBody);
                streamResponse.Write(byteArray, 0, byteArray.Length);
                streamResponse.Dispose();

                TimedRequest request = requestState.TimedRequest;
                request.BeginGetResponse(requestState.SuccessCallback, requestState.TimeoutCallback, request);
            }
            catch (WebException ex)
            {
                DebugLogger.Instance.WriteLog("WebException in RequestStreamCallback: {0}", ex);
                requestState.TimeoutCallback();
            }
        }
        /// <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);
            }
        }