/// <summary>
        /// Gets a custom exception that wraps the SOAP exception thrown
        /// by the server.
        /// </summary>
        /// <param name="ex">SOAPException that was thrown by the server.</param>
        /// <returns>A custom exception object that wraps the SOAP exception.
        /// </returns>
        protected override Exception GetCustomException(SoapException exception)
        {
            string defaultNs = GetDefaultNamespace();

            if (!string.IsNullOrEmpty(defaultNs) && exception.Detail != null)
            {
                // Extract the ApiExceptionFault node.
                XmlElement faultNode = GetFaultNode(exception, defaultNs, "ApiExceptionFault");

                if (faultNode != null)
                {
                    try {
                        AdWordsApiException awapiException = new AdWordsApiException(
                            SerializationUtilities.DeserializeFromXmlTextCustomRootNs(
                                faultNode.OuterXml, Assembly.GetExecutingAssembly().GetType(
                                    this.GetType().Namespace + ".ApiException"), defaultNs, "ApiExceptionFault"),
                            AdWordsErrorMessages.AnApiExceptionOccurred, exception);
                        if (AdWordsErrorHandler.IsOAuthTokenExpiredError(awapiException))
                        {
                            return(new AdWordsCredentialsExpiredException(
                                       (string)ContextStore.GetValue("OAuthHeader")));
                        }
                        else
                        {
                            return(awapiException);
                        }
                    } catch (Exception) {
                        // deserialization failed, but we can safely ignore it.
                    }
                }
            }
            return(new AdWordsApiException(null, AdWordsErrorMessages.AnApiExceptionOccurred, exception));
        }
        /// <summary>
        /// Downloads a report to stream.
        /// </summary>
        /// <param name="downloadUrl">The download url.</param>
        /// <param name="postBody">The POST body.</param>
        private ReportResponse DownloadReport(string downloadUrl, string postBody)
        {
            AdWordsErrorHandler errorHandler = new AdWordsErrorHandler(this.User as AdWordsUser);
              while (true) {
            WebResponse response = null;
            HttpWebRequest request = BuildRequest(downloadUrl, postBody);

            LogEntry logEntry = new LogEntry(User.Config, new DefaultDateTimeProvider());

            logEntry.LogRequest(request, postBody, HEADERS_TO_MASK);

            try {
              response = request.GetResponse();

              logEntry.LogResponse(response, false, "Response truncated.");
              logEntry.Flush();
              return new ReportResponse(response);
            } catch (WebException e) {
              Exception reportsException = null;

              string contents = HttpUtilities.GetErrorResponseBody(e);

              logEntry.LogResponse(e.Response, true, contents);
              logEntry.Flush();

              reportsException = ParseException(e, contents);

              if (AdWordsErrorHandler.IsOAuthTokenExpiredError(reportsException)) {
            reportsException = new AdWordsCredentialsExpiredException(
                request.Headers["Authorization"]);
              }
              if (errorHandler.ShouldRetry(reportsException)) {
            errorHandler.PrepareForRetry(reportsException);
              } else {
            throw reportsException;
              }
            }
              }
        }