using System.Configuration;
using System.Globalization;
using System.IO;
+using System.Net;
using System.Net.Cache;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
bool gotRequestStream;
int redirects;
bool expectContinue;
- bool authCompleted;
byte[] bodyBuffer;
int bodyBufferLength;
bool getResponseCalled;
Challenge,
Response
}
- NtlmAuthState ntlm_auth_state;
+ AuthorizationState auth_state, proxy_auth_state;
string host;
// Constructors
this.proxy = GlobalProxySelection.Select;
this.webHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
ThrowOnError = true;
+ ResetAuthorization ();
}
[Obsolete ("Serialization is obsoleted for this type", false)]
timeout = info.GetInt32 ("timeout");
redirects = info.GetInt32 ("redirects");
host = info.GetString ("host");
+ ResetAuthorization ();
+ }
+
+ void ResetAuthorization ()
+ {
+ auth_state = new AuthorizationState (this, false);
+ proxy_auth_state = new AuthorizationState (this, true);
}
// Properties
return result.Response;
}
+
+#if NET_3_5
+ public Stream EndGetRequestStream (IAsyncResult asyncResult, out TransportContext transportContext)
+ {
+ transportContext = null;
+ return EndGetRequestStream (asyncResult);
+ }
+#endif
public override WebResponse GetResponse()
{
webHeaders.RemoveAndAdd ("Transfer-Encoding", "chunked");
webHeaders.RemoveInternal ("Content-Length");
} else if (contentLength != -1) {
- if (ntlm_auth_state != NtlmAuthState.Challenge) {
+ if (auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge) {
+ // We don't send any body with the NTLM Challenge request.
+ webHeaders.SetInternal ("Content-Length", "0");
+ } else {
if (contentLength > 0)
continue100 = true;
webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
- } else {
- webHeaders.SetInternal ("Content-Length", "0");
}
webHeaders.RemoveInternal ("Transfer-Encoding");
} else {
}
}
- internal void SendRequestHeaders (bool propagate_error)
+ internal byte[] GetRequestHeaders ()
{
StringBuilder req = new StringBuilder ();
string query;
actualVersion.Major, actualVersion.Minor);
req.Append (GetHeaders ());
string reqstr = req.ToString ();
- byte [] bytes = Encoding.UTF8.GetBytes (reqstr);
- try {
- writeStream.SetHeaders (bytes);
- } catch (WebException wexc) {
- SetWriteStreamError (wexc.Status, wexc);
- if (propagate_error)
- throw;
- } catch (Exception exc) {
- SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
- if (propagate_error)
- throw;
- }
+ return Encoding.UTF8.GetBytes (reqstr);
}
internal void SetWriteStream (WebConnectionStream stream)
writeStream.SendChunked = false;
}
- SendRequestHeaders (false);
+ byte[] requestHeaders = GetRequestHeaders ();
+ WebAsyncResult result = new WebAsyncResult (new AsyncCallback (SetWriteStreamCB), null);
+ writeStream.SetHeadersAsync (requestHeaders, result);
+ }
+
+ void SetWriteStreamCB(IAsyncResult ar)
+ {
+ WebAsyncResult result = ar as WebAsyncResult;
+ if (result.Exception != null) {
+ WebException wexc = result.Exception as WebException;
+ if (wexc != null) {
+ SetWriteStreamError (wexc.Status, wexc);
+ return;
+ }
+ SetWriteStreamError (WebExceptionStatus.SendFailure, result.Exception);
+ return;
+ }
+
haveRequest = true;
-
+
if (bodyBuffer != null) {
// The body has been written and buffered. The request "user"
// won't write it again, so we must do it.
- if (ntlm_auth_state != NtlmAuthState.Challenge) {
+ if (auth_state.NtlmAuthState != NtlmAuthState.Challenge && proxy_auth_state.NtlmAuthState != NtlmAuthState.Challenge) {
+ // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
writeStream.Write (bodyBuffer, 0, bodyBufferLength);
bodyBuffer = null;
writeStream.Close ();
} else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
method != "TRACE") {
if (getResponseCalled && !writeStream.RequestWritten)
+ // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
writeStream.WriteRequest ();
}
if (asyncWrite != null) {
- asyncWrite.SetCompleted (false, stream);
+ asyncWrite.SetCompleted (false, writeStream);
asyncWrite.DoCallback ();
asyncWrite = null;
}
}
}
- void HandleNtlmAuth (WebAsyncResult r)
+ bool HandleNtlmAuth (WebAsyncResult r)
{
+ bool isProxy = webResponse.StatusCode == HttpStatusCode.ProxyAuthenticationRequired;
+ if ((isProxy ? proxy_auth_state.NtlmAuthState : auth_state.NtlmAuthState) == NtlmAuthState.None)
+ return false;
+
WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
if (wce != null) {
WebConnection cnc = wce.Connection;
cnc.PriorityRequest = this;
- bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
- ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
+ ICredentials creds = !isProxy ? credentials : proxy.Credentials;
if (creds != null) {
cnc.NtlmCredential = creds.GetCredential (requestUri, "NTLM");
cnc.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah;
haveResponse = false;
webResponse.ReadAll ();
webResponse = null;
+ return true;
}
internal void SetResponseData (WebConnectionData data)
return;
}
+ bool isProxy = ProxyQuery && !proxy.IsBypassed (actualUri);
+
bool redirected;
try {
redirected = CheckFinalStatus (r);
if (!redirected) {
- if (ntlm_auth_state != NtlmAuthState.None && authCompleted && webResponse != null
- && (int)webResponse.StatusCode < 400) {
+ if ((isProxy ? proxy_auth_state.IsNtlmAuthenticated : auth_state.IsNtlmAuthenticated) &&
+ webResponse != null && (int)webResponse.StatusCode < 400) {
WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
if (wce != null) {
WebConnection cnc = wce.Connection;
r.DoCallback ();
} else {
if (webResponse != null) {
- if (ntlm_auth_state != NtlmAuthState.None) {
- HandleNtlmAuth (r);
+ if (HandleNtlmAuth (r))
return;
- }
webResponse.Close ();
}
finished_reading = false;
}
}
- bool CheckAuthorization (WebResponse response, HttpStatusCode code)
+ struct AuthorizationState
{
- authCompleted = false;
- if (code == HttpStatusCode.Unauthorized && credentials == null)
- return false;
+ readonly HttpWebRequest request;
+ readonly bool isProxy;
+ bool isCompleted;
+ NtlmAuthState ntlm_auth_state;
- bool isProxy = (code == HttpStatusCode.ProxyAuthenticationRequired);
- if (isProxy && (proxy == null || proxy.Credentials == null))
- return false;
+ public bool IsCompleted {
+ get { return isCompleted; }
+ }
- string [] authHeaders = response.Headers.GetValues_internal ( (isProxy) ? "Proxy-Authenticate" : "WWW-Authenticate", false);
- if (authHeaders == null || authHeaders.Length == 0)
- return false;
+ public NtlmAuthState NtlmAuthState {
+ get { return ntlm_auth_state; }
+ }
- ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
- Authorization auth = null;
- foreach (string authHeader in authHeaders) {
- auth = AuthenticationManager.Authenticate (authHeader, this, creds);
- if (auth != null)
- break;
+ public bool IsNtlmAuthenticated {
+ get { return isCompleted && ntlm_auth_state != NtlmAuthState.None; }
+ }
+
+ public AuthorizationState (HttpWebRequest request, bool isProxy)
+ {
+ this.request = request;
+ this.isProxy = isProxy;
+ isCompleted = false;
+ ntlm_auth_state = NtlmAuthState.None;
+ }
+
+ public bool CheckAuthorization (WebResponse response, HttpStatusCode code)
+ {
+ isCompleted = false;
+ if (code == HttpStatusCode.Unauthorized && request.credentials == null)
+ return false;
+
+ // FIXME: This should never happen!
+ if (isProxy != (code == HttpStatusCode.ProxyAuthenticationRequired))
+ return false;
+
+ if (isProxy && (request.proxy == null || request.proxy.Credentials == null))
+ return false;
+
+ string [] authHeaders = response.Headers.GetValues_internal (isProxy ? "Proxy-Authenticate" : "WWW-Authenticate", false);
+ if (authHeaders == null || authHeaders.Length == 0)
+ return false;
+
+ ICredentials creds = (!isProxy) ? request.credentials : request.proxy.Credentials;
+ Authorization auth = null;
+ foreach (string authHeader in authHeaders) {
+ auth = AuthenticationManager.Authenticate (authHeader, request, creds);
+ if (auth != null)
+ break;
+ }
+ if (auth == null)
+ return false;
+ request.webHeaders [isProxy ? "Proxy-Authorization" : "Authorization"] = auth.Message;
+ isCompleted = auth.Complete;
+ bool is_ntlm = (auth.Module.AuthenticationType == "NTLM");
+ if (is_ntlm)
+ ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1);
+ return true;
+ }
+
+ public void Reset ()
+ {
+ isCompleted = false;
+ ntlm_auth_state = NtlmAuthState.None;
+ request.webHeaders.RemoveInternal (isProxy ? "Proxy-Authorization" : "Authorization");
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("{0}AuthState [{1}:{2}]", isProxy ? "Proxy" : "", isCompleted, ntlm_auth_state);
}
- if (auth == null)
- return false;
- webHeaders [(isProxy) ? "Proxy-Authorization" : "Authorization"] = auth.Message;
- authCompleted = auth.Complete;
- bool is_ntlm = (auth.Module.AuthenticationType == "NTLM");
- if (is_ntlm)
- ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1);
- return true;
+ }
+
+ bool CheckAuthorization (WebResponse response, HttpStatusCode code)
+ {
+ bool isProxy = code == HttpStatusCode.ProxyAuthenticationRequired;
+ return isProxy ? proxy_auth_state.CheckAuthorization (response, code) : auth_state.CheckAuthorization (response, code);
}
// Returns true if redirected
HttpStatusCode code = 0;
if (throwMe == null && webResponse != null) {
code = webResponse.StatusCode;
- if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
- (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
+ if ((!auth_state.IsCompleted && code == HttpStatusCode.Unauthorized && credentials != null) ||
+ (ProxyQuery && !proxy_auth_state.IsCompleted && code == HttpStatusCode.ProxyAuthenticationRequired)) {
if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
// Keep the written body, so it can be rewritten in the retry
if (InternalAllowBuffering) {
// We save it in the first request (first 401), don't send anything
// in the challenge request and send it in the response request along
// with the buffers kept form the first request.
- if (ntlm_auth_state != NtlmAuthState.Response) {
+ if (auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge) {
bodyBuffer = writeStream.WriteBuffer;
bodyBufferLength = writeStream.WriteBufferLength;
}
bool b = false;
int c = (int) code;
if (allowAutoRedirect && c >= 300) {
+ b = Redirect (result, code);
if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
bodyBuffer = writeStream.WriteBuffer;
bodyBufferLength = writeStream.WriteBufferLength;
}
- b = Redirect (result, code);
- if (b && ntlm_auth_state != 0)
- ntlm_auth_state = 0;
+ if (b && !unsafe_auth_blah) {
+ auth_state.Reset ();
+ proxy_auth_state.Reset ();
+ }
}
if (resp != null && c >= 300 && c != 304)