Created
December 29, 2015 23:22
-
-
Save kendallmiller/a1e780023c072d517722 to your computer and use it in GitHub Desktop.
WebClient Execute Request Wrapper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// <summary> | |
| /// Synchronously execute the provided request. | |
| /// </summary> | |
| /// <param name="newRequest"></param> | |
| /// <param name="maxRetries">The maximum number of times to retry the connection. Use -1 to retry indefinitely.</param> | |
| public void ExecuteRequest(IWebRequest newRequest, int maxRetries) | |
| { | |
| if (newRequest == null) | |
| throw new ArgumentNullException("newRequest"); | |
| if ((newRequest.RequiresAuthentication) && (m_AuthenticationProvider == null)) | |
| throw new ArgumentException("The request requires authentication and no authentication provider is available.", "newRequest"); | |
| EnsureConnectionInitialized(); | |
| m_Cancel = false; | |
| bool requestComplete = false; | |
| bool lastCallWasAuthentication = false; //indicates if we just tried an auth, so another 401 means no go. | |
| int errorCount = 0; | |
| try | |
| { | |
| while ((m_Cancel == false) | |
| && (requestComplete == false) | |
| && ((maxRetries < 0) || (errorCount <= maxRetries))) | |
| { | |
| try | |
| { | |
| if (m_ConnectionState == ChannelConnectionState.Disconnected) | |
| SetConnectionState(ChannelConnectionState.Connecting); | |
| else if (m_ConnectionState == ChannelConnectionState.Connected) | |
| SetConnectionState(ChannelConnectionState.TransferingData); | |
| if((newRequest.RequiresAuthentication) && (m_AuthenticationProvider.IsAuthenticated == false)) | |
| { | |
| //no point in waiting for the failure, go ahead and authenticate now. | |
| Authenticate(); | |
| } | |
| //Now, because we know we're not MT-safe we can do this "pass around" | |
| m_RequestSupportsAuthentication = newRequest.SupportsAuthentication; | |
| newRequest.ProcessRequest(this); | |
| SetConnectionState(ChannelConnectionState.Connected); | |
| requestComplete = true; | |
| } | |
| catch (WebException ex) | |
| { | |
| //but WHY did we fail? | |
| switch (ex.Status) | |
| { | |
| //find the terminal failures... | |
| case WebExceptionStatus.MessageLengthLimitExceeded: | |
| throw; | |
| case WebExceptionStatus.ProtocolError: | |
| //get the inner web response to figure out exactly what the deal is. | |
| HttpWebResponse response = (HttpWebResponse)ex.Response; | |
| if (response.StatusCode == HttpStatusCode.NotFound) | |
| { | |
| //throw our dedicated file not found exception. | |
| throw new WebChannelFileNotFoundException("File not found", ex, response.ResponseUri); | |
| } | |
| if (response.StatusCode == HttpStatusCode.Unauthorized) //it's an auth error | |
| { | |
| if ((m_AuthenticationProvider != null) && newRequest.SupportsAuthentication //we can do an auth | |
| && (lastCallWasAuthentication == false)) //and we didn't just try to do an auth.. | |
| { | |
| if (EnableLogging) m_Logger.Write(LogMessageSeverity.Information, ex, true, LogCategory, "Attempting to authenticate to server", "Because we got an HTTP 401 error from the server we're going to attempt to authenticate with our server credentials and try again. Status Description:\r\n{0}", response.StatusDescription); | |
| lastCallWasAuthentication = true; | |
| Authenticate(); | |
| } | |
| else | |
| { | |
| //create a new exception that tells our caller it's an authorization problem. | |
| throw new WebChannelAuthorizationException("Username or password not valid", ex, response.ResponseUri); | |
| } | |
| } | |
| else if ((response.StatusCode == HttpStatusCode.MethodNotAllowed) && (m_UseCompatibilityMethods == false)) | |
| { | |
| //most likely we did a delete or put and the caller doesn't support that, enable compatibility methods. | |
| m_UseCompatibilityMethods = true; | |
| SetUseCompatiblilityMethodsOverride(m_HostName, m_UseCompatibilityMethods); //so we don't have to repeatedly run into this for this server | |
| if (EnableLogging) m_Logger.Write(LogMessageSeverity.Information, ex, true, LogCategory, "Switching to http method compatibility mode", "Because we got an HTTP 405 error from the server we're going to turn on Http method compatibility translation and try again. Status Description:\r\n{0}", response.StatusDescription); | |
| } | |
| else | |
| { | |
| //we don't have a specific error for this, just throw the underlying exception. | |
| throw; | |
| } | |
| break; | |
| default: | |
| //assume a retryable connection error | |
| if (EnableLogging) m_Logger.Write(LogMessageSeverity.Warning, ex, true, LogCategory, "Connection error while making web channel request", "We received a communication exception while executing the current request on our channel. Since it isn't an authentication exception we're going to retry the request.\r\nRequest:{0}\r\nError Count:{1}\r\nException:\r\n{0}", newRequest, errorCount, ex.Message); | |
| SetConnectionState(ChannelConnectionState.Disconnected); | |
| errorCount++; | |
| lastCallWasAuthentication = false; //so if we get another request to authenticate we'll give it a shot. | |
| if (CanRetry(maxRetries, errorCount)) | |
| { | |
| if (errorCount > 1) | |
| { | |
| //back down our rate. | |
| SleepForConnection(); | |
| } | |
| } | |
| else | |
| { | |
| //we can't retry any more - throw an exception | |
| if ((ex.Status == WebExceptionStatus.ConnectFailure) || (ex.Status == WebExceptionStatus.NameResolutionFailure)) | |
| { | |
| //throw a specific exception here. | |
| throw new WebChannelConnectFailureException("Unable to connect to the destination server", ex, (ex.Response == null) ? null : ex.Response.ResponseUri); | |
| } | |
| else if (ex.Status == WebExceptionStatus.ConnectionClosed) | |
| { | |
| throw new WebChannelConnectFailureException("Server refused connection", ex, (ex.Response == null) ? null : ex.Response.ResponseUri); | |
| } | |
| else | |
| { | |
| throw new WebChannelException(ex.Message, ex, (ex.Response == null) ? null : ex.Response.ResponseUri); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| catch (Exception ex) | |
| { | |
| //this is just here so we can log exceptions that we're letting get thrown. | |
| if (EnableLogging) m_Logger.Write(LogMessageSeverity.Verbose, ex, true, LogCategory, ex.Message, "While executing a web channel request an exception was thrown, which will be thrown to our caller.\r\nRequest: {0}\r\n", newRequest); | |
| throw; | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment