bool authCompleted;
byte[] bodyBuffer;
int bodyBufferLength;
+ bool getResponseCalled;
+ Exception saved_exc;
+ object locker = new object ();
#if NET_1_1
int maxResponseHeadersLength;
static int defaultMaxResponseHeadersLength;
- int readWriteTimeout;
+ int readWriteTimeout = 300000; // ms
// Constructors
static HttpWebRequest ()
public override string ContentType {
get { return webHeaders ["Content-Type"]; }
set {
- CheckRequestStarted ();
if (value == null || value.Trim().Length == 0) {
webHeaders.RemoveInternal ("Content-Type");
return;
set { defaultMaxResponseHeadersLength = value; }
}
- [MonoTODO ("Use this")]
- public int ReadWriteTimeout {
+ public
+#else
+ internal
+#endif
+ int ReadWriteTimeout {
get { return readWriteTimeout; }
- set { readWriteTimeout = value; }
+ set {
+ if (requestSent)
+ throw new InvalidOperationException ("The request has already been sent.");
+
+ if (value < -1)
+ throw new ArgumentOutOfRangeException ("value", "Must be >= -1");
+
+ readWriteTimeout = value;
+ }
}
-#endif
public string MediaType {
get { return mediaType; }
}
#if NET_1_1
+ bool unsafe_auth_blah;
[MonoTODO]
public bool UnsafeAuthenticatedConnectionSharing
{
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
+ get { return unsafe_auth_blah; }
+ set { unsafe_auth_blah = value; }
}
#endif
internal ServicePoint GetServicePoint ()
{
- if (!hostChanged && servicePoint != null)
- return servicePoint;
-
- lock (this) {
+ lock (locker) {
if (hostChanged || servicePoint == null) {
servicePoint = ServicePointManager.FindServicePoint (actualUri, proxy);
hostChanged = false;
value += ",";
else
throw new InvalidOperationException ("rangeSpecifier");
- webHeaders.SetInternal ("Range", value + range + "-");
+ webHeaders.RemoveAndAdd ("Range", value + range + "-");
}
public void AddRange (string rangeSpecifier, int from, int to)
value += ",";
else
throw new InvalidOperationException ("rangeSpecifier");
- webHeaders.SetInternal ("Range", value + from + "-" + to);
+ webHeaders.RemoveAndAdd ("Range", value + from + "-" + to);
}
-
+#if !NET_2_0
public override int GetHashCode ()
{
return base.GetHashCode ();
}
+#endif
void CommonChecks (bool putpost)
{
CommonChecks (send);
- lock (this)
+ lock (locker)
{
if (asyncWrite != null) {
throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
WebAsyncResult result = asyncWrite;
if (!requestSent) {
requestSent = true;
+ redirects = 0;
servicePoint = GetServicePoint ();
abortHandler = servicePoint.SendRequest (this, connectionGroup);
}
return EndGetRequestStream (asyncResult);
}
-
+
+ void CheckIfForceWrite ()
+ {
+ if (writeStream == null || contentLength < 0 || !InternalAllowBuffering)
+ return;
+
+ // This will write the POST/PUT if the write stream already has the expected
+ // amount of bytes in it (ContentLength) (bug #77753).
+ if (writeStream.WriteBufferLength == contentLength)
+ writeStream.WriteRequest ();
+ }
+
public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
{
bool send = (method == "PUT" || method == "POST");
CommonChecks (send);
Monitor.Enter (this);
+ getResponseCalled = true;
if (asyncRead != null && !haveResponse) {
Monitor.Exit (this);
throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
"method while a previous call is still in progress.");
}
+ CheckIfForceWrite ();
asyncRead = new WebAsyncResult (this, callback, state);
initialMethod = method;
if (haveResponse) {
if (webResponse != null) {
+ Exception saved = saved_exc;
Monitor.Exit (this);
- asyncRead.SetCompleted (true, webResponse);
+ if (saved == null) {
+ asyncRead.SetCompleted (true, webResponse);
+ } else {
+ asyncRead.SetCompleted (true, saved);
+ }
asyncRead.DoCallback ();
return asyncRead;
}
if (!requestSent) {
requestSent = true;
+ redirects = 0;
servicePoint = GetServicePoint ();
abortHandler = servicePoint.SendRequest (this, connectionGroup);
}
if (result == null)
throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
- redirects = 0;
- bool redirected = false;
- asyncRead = result;
- do {
- if (redirected) {
- haveResponse = false;
- result.Reset ();
- servicePoint = GetServicePoint ();
- abortHandler = servicePoint.SendRequest (this, connectionGroup);
- }
+ if (!result.WaitUntilComplete (timeout, false)) {
+ Abort ();
+ throw new WebException("The request timed out", WebExceptionStatus.Timeout);
+ }
- if (!result.WaitUntilComplete (timeout, false)) {
- Abort ();
- throw new WebException("The request timed out", WebExceptionStatus.Timeout);
- }
+ if (result.GotException)
+ throw result.Exception;
- redirected = CheckFinalStatus (result);
- } while (redirected);
-
return result.Response;
}
-
+
public override WebResponse GetResponse()
{
- if (haveResponse && webResponse != null)
- return webResponse;
-
WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
return EndGetResponse (result);
}
if (abortHandler != null) {
try {
abortHandler (this, EventArgs.Empty);
- } catch {}
+ } catch (Exception) {}
abortHandler = null;
}
case HttpStatusCode.MovedPermanently: // 301
case HttpStatusCode.Redirect: // 302
case HttpStatusCode.TemporaryRedirect: // 307
+ /* MS follows the redirect for POST too
if (method != "GET" && method != "HEAD") // 10.3
return false;
+ */
uriString = webResponse.Headers ["Location"];
break;
string GetHeaders ()
{
bool continue100 = false;
- if (gotRequestStream && contentLength != -1) {
- continue100 = true;
+ if (contentLength != -1) {
+ if (contentLength > 0)
+ continue100 = true;
webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
webHeaders.RemoveInternal ("Transfer-Encoding");
} else if (sendChunked) {
webHeaders.RemoveInternal ("Proxy-Authorization");
webHeaders.RemoveInternal ("Authorization");
bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
- ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
+ ICredentials creds = (!isProxy || credentials != null) ? credentials : proxy.Credentials;
Authorization auth = AuthenticationManager.PreAuthenticate (this, creds);
if (auth == null)
return;
- string authHeader = (isProxy) ? "Proxy-Authorization" : "Authorization";
+ string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization";
webHeaders [authHeader] = auth.Message;
usedPreAuth = true;
}
contentLength = bodyBufferLength;
writeStream.SendChunked = false;
}
-
+
SendRequestHeaders ();
haveRequest = true;
writeStream.Write (bodyBuffer, 0, bodyBufferLength);
bodyBuffer = null;
writeStream.Close ();
+ } else if (method == "PUT" || method == "POST") {
+ if (getResponseCalled && !writeStream.RequestWritten)
+ writeStream.WriteRequest ();
}
if (asyncWrite != null) {
}
}
- internal void SetResponseError (WebExceptionStatus status, Exception e)
+ internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
{
+ if (aborted)
+ return;
+ string msg = String.Format ("Error getting response stream ({0}): {1}", where, status);
WebAsyncResult r = asyncRead;
if (r == null)
r = asyncWrite;
if (r != null) {
- WebException wexc = new WebException ("Error getting response stream: " + status, e, status, null);
+ WebException wexc;
+ if (e is WebException) {
+ wexc = (WebException) e;
+ } else {
+ wexc = new WebException (msg, e, status, null);
+ }
r.SetCompleted (false, wexc);
r.DoCallback ();
asyncRead = null;
asyncWrite = null;
}
}
-
+
+ void CheckSendError (WebConnectionData data)
+ {
+ // Got here, but no one called GetResponse
+ int status = data.StatusCode;
+ if (status < 400 || status == 401 || status == 407)
+ return;
+
+ if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) {
+ // The request has not been completely sent and we got here!
+ // We should probably just close and cause an error in any case,
+ saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse);
+ webResponse.ReadAll ();
+ }
+ }
+
internal void SetResponseData (WebConnectionData data)
{
if (aborted) {
data.stream.Close ();
return;
}
-
- webResponse = new HttpWebResponse (actualUri, method, data, (cookieContainer != null));
- haveResponse = true;
+
+ WebException wexc = null;
+ try {
+ webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer);
+ haveResponse = true;
+ } catch (Exception e) {
+ wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null);
+ if (data.stream != null)
+ data.stream.Close ();
+ }
+
+ if (wexc == null && (method == "POST" || method == "PUT")) {
+ lock (locker) {
+ CheckSendError (data);
+ if (saved_exc != null)
+ wexc = (WebException) saved_exc;
+ }
+ }
WebAsyncResult r = asyncRead;
if (r != null) {
- r.SetCompleted (false, webResponse);
- r.DoCallback ();
+ if (wexc != null) {
+ r.SetCompleted (false, wexc);
+ r.DoCallback ();
+ return;
+ }
+
+ bool redirected;
+ try {
+ redirected = CheckFinalStatus (r);
+ if (!redirected) {
+ r.SetCompleted (false, webResponse);
+ r.DoCallback ();
+ } else {
+ if (webResponse != null) {
+ webResponse.Close ();
+ webResponse = null;
+ }
+ haveResponse = false;
+ webResponse = null;
+ r.Reset ();
+ servicePoint = GetServicePoint ();
+ abortHandler = servicePoint.SendRequest (this, connectionGroup);
+ }
+ } catch (WebException wexc2) {
+ r.SetCompleted (false, wexc2);
+ r.DoCallback ();
+ return;
+ } catch (Exception ex) {
+ wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null);
+ r.SetCompleted (false, wexc);
+ r.DoCallback ();
+ return;
+ }
}
}
WebExceptionStatus protoError = WebExceptionStatus.ProtocolError;
HttpStatusCode code = 0;
if (throwMe == null && webResponse != null) {
- code = webResponse.StatusCode;
+ code = webResponse.StatusCode;
if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
- code == HttpStatusCode.ProxyAuthenticationRequired)) {
+ (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
// Keep the written body, so it can be rewritten in the retry
if (InternalAllowBuffering) {
bodyBuffer = writeStream.WriteBuffer;
bodyBufferLength = writeStream.WriteBufferLength;
+ webResponse.Close ();
return true;
} else if (method != "PUT" && method != "POST") {
+ webResponse.Close ();
return true;
}
writeStream.InternalClose ();
writeStream = null;
+ webResponse.Close ();
webResponse = null;
+
throw new WebException ("This request requires buffering " +
"of data for authentication or " +
"redirection to be sucessful.");
string err = String.Format ("The remote server returned an error: ({0}) {1}.",
(int) code, webResponse.StatusDescription);
throwMe = new WebException (err, null, protoError, webResponse);
+ webResponse.ReadAll ();
} else if ((int) code == 304 && allowAutoRedirect) {
string err = String.Format ("The remote server returned an error: ({0}) {1}.",
(int) code, webResponse.StatusDescription);
} else if ((int) code >= 300 && allowAutoRedirect && redirects > maxAutoRedirect) {
throwMe = new WebException ("Max. redirections exceeded.", null,
protoError, webResponse);
+ webResponse.ReadAll ();
}
}
if (throwMe == null) {
bool b = false;
- if (allowAutoRedirect && (int) code >= 300)
+ int c = (int) code;
+ if (allowAutoRedirect && c >= 300) {
+ if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
+ bodyBuffer = writeStream.WriteBuffer;
+ bodyBufferLength = writeStream.WriteBufferLength;
+ }
b = Redirect (result, code);
+ }
+
+ if (resp != null && c >= 300 && c != 304)
+ resp.ReadAll ();
return b;
}
writeStream = null;
}
- if (webResponse != null)
- webResponse = null;
+ webResponse = null;
throw throwMe;
}