Merge branch 'master' into msbuilddll2
[mono.git] / mcs / class / System / System.Net / HttpWebRequest.cs
1 //
2 // System.Net.HttpWebRequest
3 //
4 // Authors:
5 //      Lawrence Pit (loz@cable.a2000.nl)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002 Lawrence Pit
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 // (c) 2004 Novell, Inc. (http://www.novell.com)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Collections;
36 using System.Configuration;
37 using System.Globalization;
38 using System.IO;
39 using System.Net;
40 using System.Net.Cache;
41 using System.Net.Sockets;
42 using System.Runtime.Remoting.Messaging;
43 using System.Runtime.Serialization;
44 using System.Security.Cryptography.X509Certificates;
45 using System.Text;
46 using System.Threading;
47
48 namespace System.Net 
49 {
50         [Serializable]
51         public class HttpWebRequest : WebRequest, ISerializable {
52                 Uri requestUri;
53                 Uri actualUri;
54                 bool hostChanged;
55                 bool allowAutoRedirect = true;
56                 bool allowBuffering = true;
57                 X509CertificateCollection certificates;
58                 string connectionGroup;
59                 long contentLength = -1;
60                 HttpContinueDelegate continueDelegate;
61                 CookieContainer cookieContainer;
62                 ICredentials credentials;
63                 bool haveResponse;              
64                 bool haveRequest;
65                 bool requestSent;
66                 WebHeaderCollection webHeaders;
67                 bool keepAlive = true;
68                 int maxAutoRedirect = 50;
69                 string mediaType = String.Empty;
70                 string method = "GET";
71                 string initialMethod = "GET";
72                 bool pipelined = true;
73                 bool preAuthenticate;
74                 bool usedPreAuth;
75                 Version version = HttpVersion.Version11;
76                 bool force_version;
77                 Version actualVersion;
78                 IWebProxy proxy;
79                 bool sendChunked;
80                 ServicePoint servicePoint;
81                 int timeout = 100000;
82                 
83                 WebConnectionStream writeStream;
84                 HttpWebResponse webResponse;
85                 WebAsyncResult asyncWrite;
86                 WebAsyncResult asyncRead;
87                 EventHandler abortHandler;
88                 int aborted;
89                 bool gotRequestStream;
90                 int redirects;
91                 bool expectContinue;
92                 bool authCompleted;
93                 byte[] bodyBuffer;
94                 int bodyBufferLength;
95                 bool getResponseCalled;
96                 Exception saved_exc;
97                 object locker = new object ();
98                 bool finished_reading;
99                 internal WebConnection WebConnection;
100                 DecompressionMethods auto_decomp;
101                 int maxResponseHeadersLength;
102                 static int defaultMaxResponseHeadersLength;
103                 int readWriteTimeout = 300000; // ms
104
105                 enum NtlmAuthState {
106                         None,
107                         Challenge,
108                         Response
109                 }
110                 NtlmAuthState ntlm_auth_state;
111                 string host;
112
113                 // Constructors
114                 static HttpWebRequest ()
115                 {
116                         defaultMaxResponseHeadersLength = 64 * 1024;
117 #if !NET_2_1
118                         NetConfig config = ConfigurationSettings.GetConfig ("system.net/settings") as NetConfig;
119                         if (config != null) {
120                                 int x = config.MaxResponseHeadersLength;
121                                 if (x != -1)
122                                         x *= 64;
123
124                                 defaultMaxResponseHeadersLength = x;
125                         }
126 #endif
127                 }
128
129 #if NET_2_1
130                 public
131 #else
132                 internal
133 #endif
134                 HttpWebRequest (Uri uri) 
135                 {
136                         this.requestUri = uri;
137                         this.actualUri = uri;
138                         this.proxy = GlobalProxySelection.Select;
139                         this.webHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
140                         ThrowOnError = true;
141                 }
142                 
143                 [Obsolete ("Serialization is obsoleted for this type", false)]
144                 protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) 
145                 {
146                         SerializationInfo info = serializationInfo;
147
148                         requestUri = (Uri) info.GetValue ("requestUri", typeof (Uri));
149                         actualUri = (Uri) info.GetValue ("actualUri", typeof (Uri));
150                         allowAutoRedirect = info.GetBoolean ("allowAutoRedirect");
151                         allowBuffering = info.GetBoolean ("allowBuffering");
152                         certificates = (X509CertificateCollection) info.GetValue ("certificates", typeof (X509CertificateCollection));
153                         connectionGroup = info.GetString ("connectionGroup");
154                         contentLength = info.GetInt64 ("contentLength");
155                         webHeaders = (WebHeaderCollection) info.GetValue ("webHeaders", typeof (WebHeaderCollection));
156                         keepAlive = info.GetBoolean ("keepAlive");
157                         maxAutoRedirect = info.GetInt32 ("maxAutoRedirect");
158                         mediaType = info.GetString ("mediaType");
159                         method = info.GetString ("method");
160                         initialMethod = info.GetString ("initialMethod");
161                         pipelined = info.GetBoolean ("pipelined");
162                         version = (Version) info.GetValue ("version", typeof (Version));
163                         proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy));
164                         sendChunked = info.GetBoolean ("sendChunked");
165                         timeout = info.GetInt32 ("timeout");
166                         redirects = info.GetInt32 ("redirects");
167                         host = info.GetString ("host");
168                 }
169                 
170                 // Properties
171
172                 public string Accept {
173                         get { return webHeaders ["Accept"]; }
174                         set {
175                                 CheckRequestStarted ();
176                                 webHeaders.RemoveAndAdd ("Accept", value);
177                         }
178                 }
179                 
180                 public Uri Address {
181                         get { return actualUri; }
182                         internal set { actualUri = value; } // Used by Ftp+proxy
183                 }
184                 
185                 public bool AllowAutoRedirect {
186                         get { return allowAutoRedirect; }
187                         set { this.allowAutoRedirect = value; }
188                 }
189                 
190                 public bool AllowWriteStreamBuffering {
191                         get { return allowBuffering; }
192                         set { allowBuffering = value; }
193                 }
194                 
195 #if NET_4_5
196                 public virtual bool AllowReadStreamBuffering {
197                         get { return allowBuffering; }
198                         set { allowBuffering = value; }
199                 }
200 #endif
201
202                 static Exception GetMustImplement ()
203                 {
204                         return new NotImplementedException ();
205                 }
206                 
207                 public DecompressionMethods AutomaticDecompression
208                 {
209                         get {
210                                 return auto_decomp;
211                         }
212                         set {
213                                 CheckRequestStarted ();
214                                 auto_decomp = value;
215                         }
216                 }
217                 
218                 internal bool InternalAllowBuffering {
219                         get {
220                                 return (allowBuffering && (method != "HEAD" && method != "GET" &&
221                                                         method != "MKCOL" && method != "CONNECT" &&
222                                                         method != "TRACE"));
223                         }
224                 }
225                 
226                 public X509CertificateCollection ClientCertificates {
227                         get {
228                                 if (certificates == null)
229                                         certificates = new X509CertificateCollection ();
230
231                                 return certificates;
232                         }
233                         [MonoTODO]
234                         set {
235                                 throw GetMustImplement ();
236                         }
237                 }
238                 
239                 public string Connection {
240                         get { return webHeaders ["Connection"]; }
241                         set {
242                                 CheckRequestStarted ();
243
244                                 if (string.IsNullOrEmpty (value)) {
245                                         webHeaders.RemoveInternal ("Connection");
246                                         return;
247                                 }
248
249                                 string val = value.ToLowerInvariant ();
250                                 if (val.Contains ("keep-alive") || val.Contains ("close"))
251                                         throw new ArgumentException ("Keep-Alive and Close may not be set with this property");
252
253                                 if (keepAlive)
254                                         value = value + ", Keep-Alive";
255                                 
256                                 webHeaders.RemoveAndAdd ("Connection", value);
257                         }
258                 }               
259                 
260                 public override string ConnectionGroupName { 
261                         get { return connectionGroup; }
262                         set { connectionGroup = value; }
263                 }
264                 
265                 public override long ContentLength { 
266                         get { return contentLength; }
267                         set { 
268                                 CheckRequestStarted ();
269                                 if (value < 0)
270                                         throw new ArgumentOutOfRangeException ("value", "Content-Length must be >= 0");
271                                         
272                                 contentLength = value;
273                         }
274                 }
275                 
276                 internal long InternalContentLength {
277                         set { contentLength = value; }
278                 }
279                         
280                 internal bool ThrowOnError { get; set; }
281                 
282                 public override string ContentType { 
283                         get { return webHeaders ["Content-Type"]; }
284                         set {
285                                 if (value == null || value.Trim().Length == 0) {
286                                         webHeaders.RemoveInternal ("Content-Type");
287                                         return;
288                                 }
289                                 webHeaders.RemoveAndAdd ("Content-Type", value);
290                         }
291                 }
292                 
293                 public HttpContinueDelegate ContinueDelegate {
294                         get { return continueDelegate; }
295                         set { continueDelegate = value; }
296                 }
297                 
298 #if NET_4_5
299                 virtual
300 #endif
301                 public CookieContainer CookieContainer {
302                         get { return cookieContainer; }
303                         set { cookieContainer = value; }
304                 }
305                 
306                 public override ICredentials Credentials { 
307                         get { return credentials; }
308                         set { credentials = value; }
309                 }
310 #if NET_4_0
311                 public DateTime Date {
312                         get {
313                                 string date = webHeaders ["Date"];
314                                 if (date == null)
315                                         return DateTime.MinValue;
316                                 return DateTime.ParseExact (date, "r", CultureInfo.InvariantCulture).ToLocalTime ();
317                         }
318                         set {
319                                 if (value.Equals (DateTime.MinValue))
320                                         webHeaders.RemoveInternal ("Date");
321                                 else
322                                         webHeaders.RemoveAndAdd ("Date", value.ToUniversalTime ().ToString ("r", CultureInfo.InvariantCulture));
323                         }
324                 }
325 #endif
326
327 #if !NET_2_1
328                 [MonoTODO]
329                 public static new RequestCachePolicy DefaultCachePolicy
330                 {
331                         get {
332                                 throw GetMustImplement ();
333                         }
334                         set {
335                                 throw GetMustImplement ();
336                         }
337                 }
338 #endif
339                 
340                 [MonoTODO]
341                 public static int DefaultMaximumErrorResponseLength
342                 {
343                         get {
344                                 throw GetMustImplement ();
345                         }
346                         set {
347                                 throw GetMustImplement ();
348                         }
349                 }
350                 
351                 public string Expect {
352                         get { return webHeaders ["Expect"]; }
353                         set {
354                                 CheckRequestStarted ();
355                                 string val = value;
356                                 if (val != null)
357                                         val = val.Trim ().ToLower ();
358
359                                 if (val == null || val.Length == 0) {
360                                         webHeaders.RemoveInternal ("Expect");
361                                         return;
362                                 }
363
364                                 if (val == "100-continue")
365                                         throw new ArgumentException ("100-Continue cannot be set with this property.",
366                                                                      "value");
367                                 webHeaders.RemoveAndAdd ("Expect", value);
368                         }
369                 }
370                 
371 #if NET_4_5
372                 virtual
373 #endif
374                 public bool HaveResponse {
375                         get { return haveResponse; }
376                 }
377                 
378                 public override WebHeaderCollection Headers { 
379                         get { return webHeaders; }
380                         set {
381                                 CheckRequestStarted ();
382                                 WebHeaderCollection newHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
383                                 int count = value.Count;
384                                 for (int i = 0; i < count; i++) 
385                                         newHeaders.Add (value.GetKey (i), value.Get (i));
386
387                                 webHeaders = newHeaders;
388                         }
389                 }
390                 
391 #if NET_4_0
392                 public
393 #else
394                 internal
395 #endif
396                 string Host {
397                         get {
398                                 if (host == null)
399                                         return actualUri.Authority;
400                                 return host;
401                         }
402                         set {
403                                 if (value == null)
404                                         throw new ArgumentNullException ("value");
405
406                                 if (!CheckValidHost (actualUri.Scheme, value))
407                                         throw new ArgumentException ("Invalid host: " + value);
408
409                                 host = value;
410                         }
411                 }
412
413                 static bool CheckValidHost (string scheme, string val)
414                 {
415                         if (val.Length == 0)
416                                 return false;
417
418                         if (val [0] == '.')
419                                 return false;
420
421                         int idx = val.IndexOf ('/');
422                         if (idx >= 0)
423                                 return false;
424
425                         IPAddress ipaddr;
426                         if (IPAddress.TryParse (val, out ipaddr))
427                                 return true;
428
429                         string u = scheme + "://" + val + "/";
430                         return Uri.IsWellFormedUriString (u, UriKind.Absolute);
431                 }
432
433                 public DateTime IfModifiedSince {
434                         get { 
435                                 string str = webHeaders ["If-Modified-Since"];
436                                 if (str == null)
437                                         return DateTime.Now;
438                                 try {
439                                         return MonoHttpDate.Parse (str);
440                                 } catch (Exception) {
441                                         return DateTime.Now;
442                                 }
443                         }
444                         set {
445                                 CheckRequestStarted ();
446                                 // rfc-1123 pattern
447                                 webHeaders.SetInternal ("If-Modified-Since", 
448                                         value.ToUniversalTime ().ToString ("r", null));
449                                 // TODO: check last param when using different locale
450                         }
451                 }
452
453                 public bool KeepAlive {         
454                         get {
455                                 return keepAlive;
456                         }
457                         set {
458                                 keepAlive = value;
459                         }
460                 }
461                 
462                 public int MaximumAutomaticRedirections {
463                         get { return maxAutoRedirect; }
464                         set {
465                                 if (value <= 0)
466                                         throw new ArgumentException ("Must be > 0", "value");
467
468                                 maxAutoRedirect = value;
469                         }                       
470                 }
471
472                 [MonoTODO ("Use this")]
473                 public int MaximumResponseHeadersLength {
474                         get { return maxResponseHeadersLength; }
475                         set { maxResponseHeadersLength = value; }
476                 }
477
478                 [MonoTODO ("Use this")]
479                 public static int DefaultMaximumResponseHeadersLength {
480                         get { return defaultMaxResponseHeadersLength; }
481                         set { defaultMaxResponseHeadersLength = value; }
482                 }
483
484                 public  int ReadWriteTimeout {
485                         get { return readWriteTimeout; }
486                         set {
487                                 if (requestSent)
488                                         throw new InvalidOperationException ("The request has already been sent.");
489
490                                 if (value < -1)
491                                         throw new ArgumentOutOfRangeException ("value", "Must be >= -1");
492
493                                 readWriteTimeout = value;
494                         }
495                 }
496                 
497 #if NET_4_5
498                 [MonoTODO]
499                 public int ContinueTimeout {
500                         get { throw new NotImplementedException (); }
501                         set { throw new NotImplementedException (); }
502                 }
503 #endif
504                 
505                 public string MediaType {
506                         get { return mediaType; }
507                         set { 
508                                 mediaType = value;
509                         }
510                 }
511                 
512                 public override string Method { 
513                         get { return this.method; }
514                         set { 
515                                 if (value == null || value.Trim () == "")
516                                         throw new ArgumentException ("not a valid method");
517
518                                 method = value.ToUpperInvariant ();
519                                 if (method != "HEAD" && method != "GET" && method != "POST" && method != "PUT" &&
520                                         method != "DELETE" && method != "CONNECT" && method != "TRACE" &&
521                                         method != "MKCOL") {
522                                         method = value;
523                                 }
524                         }
525                 }
526                 
527                 public bool Pipelined {
528                         get { return pipelined; }
529                         set { pipelined = value; }
530                 }               
531                 
532                 public override bool PreAuthenticate { 
533                         get { return preAuthenticate; }
534                         set { preAuthenticate = value; }
535                 }
536                 
537                 public Version ProtocolVersion {
538                         get { return version; }
539                         set { 
540                                 if (value != HttpVersion.Version10 && value != HttpVersion.Version11)
541                                         throw new ArgumentException ("value");
542
543                                 force_version = true;
544                                 version = value; 
545                         }
546                 }
547                 
548                 public override IWebProxy Proxy { 
549                         get { return proxy; }
550                         set { 
551                                 CheckRequestStarted ();
552                                 proxy = value;
553                                 servicePoint = null; // we may need a new one
554                         }
555                 }
556                 
557                 public string Referer {
558                         get { return webHeaders ["Referer"]; }
559                         set {
560                                 CheckRequestStarted ();
561                                 if (value == null || value.Trim().Length == 0) {
562                                         webHeaders.RemoveInternal ("Referer");
563                                         return;
564                                 }
565                                 webHeaders.SetInternal ("Referer", value);
566                         }
567                 }
568
569                 public override Uri RequestUri { 
570                         get { return requestUri; }
571                 }
572                 
573                 public bool SendChunked {
574                         get { return sendChunked; }
575                         set {
576                                 CheckRequestStarted ();
577                                 sendChunked = value;
578                         }
579                 }
580                 
581                 public ServicePoint ServicePoint {
582                         get { return GetServicePoint (); }
583                 }
584
585                 internal ServicePoint ServicePointNoLock {
586                         get { return servicePoint; }
587                 }
588 #if NET_4_0
589                 [MonoTODO ("for portable library support")]
590                 public virtual bool SupportsCookieContainer { 
591                         get {
592                                 throw new NotImplementedException ();
593                         }
594                 }
595 #endif
596                 public override int Timeout { 
597                         get { return timeout; }
598                         set {
599                                 if (value < -1)
600                                         throw new ArgumentOutOfRangeException ("value");
601
602                                 timeout = value;
603                         }
604                 }
605                 
606                 public string TransferEncoding {
607                         get { return webHeaders ["Transfer-Encoding"]; }
608                         set {
609                                 CheckRequestStarted ();
610                                 string val = value;
611                                 if (val != null)
612                                         val = val.Trim ().ToLower ();
613
614                                 if (val == null || val.Length == 0) {
615                                         webHeaders.RemoveInternal ("Transfer-Encoding");
616                                         return;
617                                 }
618
619                                 if (val == "chunked")
620                                         throw new ArgumentException ("Chunked encoding must be set with the SendChunked property");
621
622                                 if (!sendChunked)
623                                         throw new ArgumentException ("SendChunked must be True", "value");
624
625                                 webHeaders.RemoveAndAdd ("Transfer-Encoding", value);
626                         }
627                 }
628
629                 public override bool UseDefaultCredentials
630                 {
631                         get { return CredentialCache.DefaultCredentials == Credentials; }
632                         set { Credentials = value ? CredentialCache.DefaultCredentials : null; }
633                 }
634                 
635                 public string UserAgent {
636                         get { return webHeaders ["User-Agent"]; }
637                         set { webHeaders.SetInternal ("User-Agent", value); }
638                 }
639
640                 bool unsafe_auth_blah;
641                 public bool UnsafeAuthenticatedConnectionSharing
642                 {
643                         get { return unsafe_auth_blah; }
644                         set { unsafe_auth_blah = value; }
645                 }
646
647                 internal bool GotRequestStream {
648                         get { return gotRequestStream; }
649                 }
650
651                 internal bool ExpectContinue {
652                         get { return expectContinue; }
653                         set { expectContinue = value; }
654                 }
655                 
656                 internal Uri AuthUri {
657                         get { return actualUri; }
658                 }
659                 
660                 internal bool ProxyQuery {
661                         get { return servicePoint.UsesProxy && !servicePoint.UseConnect; }
662                 }
663                 
664                 // Methods
665                 
666                 internal ServicePoint GetServicePoint ()
667                 {
668                         lock (locker) {
669                                 if (hostChanged || servicePoint == null) {
670                                         servicePoint = ServicePointManager.FindServicePoint (actualUri, proxy);
671                                         hostChanged = false;
672                                 }
673                         }
674
675                         return servicePoint;
676                 }
677                 
678                 public void AddRange (int range)
679                 {
680                         AddRange ("bytes", (long) range);
681                 }
682                 
683                 public void AddRange (int from, int to)
684                 {
685                         AddRange ("bytes", (long) from, (long) to);
686                 }
687                 
688                 public void AddRange (string rangeSpecifier, int range)
689                 {
690                         AddRange (rangeSpecifier, (long) range);
691                 }
692                 
693                 public void AddRange (string rangeSpecifier, int from, int to)
694                 {
695                         AddRange (rangeSpecifier, (long) from, (long) to);
696                 }
697 #if NET_4_0
698                 public
699 #else
700                 internal
701 #endif
702                 void AddRange (long range)
703                 {
704                         AddRange ("bytes", (long) range);
705                 }
706
707 #if NET_4_0
708                 public
709 #else
710                 internal
711 #endif
712                 void AddRange (long from, long to)
713                 {
714                         AddRange ("bytes", from, to);
715                 }
716
717 #if NET_4_0
718                 public
719 #else
720                 internal
721 #endif
722                 void AddRange (string rangeSpecifier, long range)
723                 {
724                         if (rangeSpecifier == null)
725                                 throw new ArgumentNullException ("rangeSpecifier");
726                         if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
727                                 throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
728
729                         string r = webHeaders ["Range"];
730                         if (r == null)
731                                 r = rangeSpecifier + "=";
732                         else {
733                                 string old_specifier = r.Substring (0, r.IndexOf ('='));
734                                 if (String.Compare (old_specifier, rangeSpecifier, StringComparison.OrdinalIgnoreCase) != 0)
735                                         throw new InvalidOperationException ("A different range specifier is already in use");
736                                 r += ",";
737                         }
738
739                         string n = range.ToString (CultureInfo.InvariantCulture);
740                         if (range < 0)
741                                 r = r + "0" + n;
742                         else
743                                 r = r + n + "-";
744                         webHeaders.RemoveAndAdd ("Range", r);
745                 }
746
747 #if NET_4_0
748                 public
749 #else
750                 internal
751 #endif
752                 void AddRange (string rangeSpecifier, long from, long to)
753                 {
754                         if (rangeSpecifier == null)
755                                 throw new ArgumentNullException ("rangeSpecifier");
756                         if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
757                                 throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
758                         if (from > to || from < 0)
759                                 throw new ArgumentOutOfRangeException ("from");
760                         if (to < 0)
761                                 throw new ArgumentOutOfRangeException ("to");
762
763                         string r = webHeaders ["Range"];
764                         if (r == null)
765                                 r = rangeSpecifier + "=";
766                         else
767                                 r += ",";
768
769                         r = String.Format ("{0}{1}-{2}", r, from, to);
770                         webHeaders.RemoveAndAdd ("Range", r);
771                 }
772
773                 
774                 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) 
775                 {
776                         if (Aborted)
777                                 throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled);
778
779                         bool send = !(method == "GET" || method == "CONNECT" || method == "HEAD" ||
780                                         method == "TRACE");
781                         if (method == null || !send)
782                                 throw new ProtocolViolationException ("Cannot send data when method is: " + method);
783
784                         if (contentLength == -1 && !sendChunked && !allowBuffering && KeepAlive)
785                                 throw new ProtocolViolationException ("Content-Length not set");
786
787                         string transferEncoding = TransferEncoding;
788                         if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "")
789                                 throw new ProtocolViolationException ("SendChunked should be true.");
790
791                         lock (locker)
792                         {
793                                 if (getResponseCalled)
794                                         throw new InvalidOperationException ("The operation cannot be performed once the request has been submitted.");
795
796                                 if (asyncWrite != null) {
797                                         throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
798                                                                 "method while a previous call is still in progress.");
799                                 }
800         
801                                 asyncWrite = new WebAsyncResult (this, callback, state);
802                                 initialMethod = method;
803                                 if (haveRequest) {
804                                         if (writeStream != null) {
805                                                 asyncWrite.SetCompleted (true, writeStream);
806                                                 asyncWrite.DoCallback ();
807                                                 return asyncWrite;
808                                         }
809                                 }
810                                 
811                                 gotRequestStream = true;
812                                 WebAsyncResult result = asyncWrite;
813                                 if (!requestSent) {
814                                         requestSent = true;
815                                         redirects = 0;
816                                         servicePoint = GetServicePoint ();
817                                         abortHandler = servicePoint.SendRequest (this, connectionGroup);
818                                 }
819                                 return result;
820                         }
821                 }
822
823                 public override Stream EndGetRequestStream (IAsyncResult asyncResult)
824                 {
825                         if (asyncResult == null)
826                                 throw new ArgumentNullException ("asyncResult");
827
828                         WebAsyncResult result = asyncResult as WebAsyncResult;
829                         if (result == null)
830                                 throw new ArgumentException ("Invalid IAsyncResult");
831
832                         asyncWrite = result;
833                         result.WaitUntilComplete ();
834
835                         Exception e = result.Exception;
836                         if (e != null)
837                                 throw e;
838
839                         return result.WriteStream;
840                 }
841                 
842                 public override Stream GetRequestStream()
843                 {
844                         IAsyncResult asyncResult = asyncWrite;
845                         if (asyncResult == null) {
846                                 asyncResult = BeginGetRequestStream (null, null);
847                                 asyncWrite = (WebAsyncResult) asyncResult;
848                         }
849
850                         if (!asyncResult.IsCompleted && !asyncResult.AsyncWaitHandle.WaitOne (timeout, false)) {
851                                 Abort ();
852                                 throw new WebException ("The request timed out", WebExceptionStatus.Timeout);
853                         }
854
855                         return EndGetRequestStream (asyncResult);
856                 }
857
858                 void CheckIfForceWrite ()
859                 {
860                         if (writeStream == null || writeStream.RequestWritten || !InternalAllowBuffering)
861                                 return;
862 #if NET_4_0
863                         if (contentLength < 0 && writeStream.CanWrite == true && writeStream.WriteBufferLength < 0)
864                                 return;
865
866                         if (contentLength < 0 && writeStream.WriteBufferLength >= 0)
867                                 InternalContentLength = writeStream.WriteBufferLength;
868 #else
869                         if (contentLength < 0 && writeStream.CanWrite == true)
870                                 return;
871 #endif
872
873                         // This will write the POST/PUT if the write stream already has the expected
874                         // amount of bytes in it (ContentLength) (bug #77753) or if the write stream
875                         // contains data and it has been closed already (xamarin bug #1512).
876
877                         if (writeStream.WriteBufferLength == contentLength || (contentLength == -1 && writeStream.CanWrite == false))
878                                 writeStream.WriteRequest ();
879                 }
880
881                 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
882                 {
883                         if (Aborted)
884                                 throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled);
885
886                         if (method == null)
887                                 throw new ProtocolViolationException ("Method is null.");
888
889                         string transferEncoding = TransferEncoding;
890                         if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "")
891                                 throw new ProtocolViolationException ("SendChunked should be true.");
892
893                         Monitor.Enter (locker);
894                         getResponseCalled = true;
895                         if (asyncRead != null && !haveResponse) {
896                                 Monitor.Exit (locker);
897                                 throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
898                                                         "method while a previous call is still in progress.");
899                         }
900
901                         CheckIfForceWrite ();
902                         asyncRead = new WebAsyncResult (this, callback, state);
903                         WebAsyncResult aread = asyncRead;
904                         initialMethod = method;
905                         if (haveResponse) {
906                                 Exception saved = saved_exc;
907                                 if (webResponse != null) {
908                                         Monitor.Exit (locker);
909                                         if (saved == null) {
910                                                 aread.SetCompleted (true, webResponse);
911                                         } else {
912                                                 aread.SetCompleted (true, saved);
913                                         }
914                                         aread.DoCallback ();
915                                         return aread;
916                                 } else if (saved != null) {
917                                         Monitor.Exit (locker);
918                                         aread.SetCompleted (true, saved);
919                                         aread.DoCallback ();
920                                         return aread;
921                                 }
922                         }
923                         
924                         if (!requestSent) {
925                                 requestSent = true;
926                                 redirects = 0;
927                                 servicePoint = GetServicePoint ();
928                                 abortHandler = servicePoint.SendRequest (this, connectionGroup);
929                         }
930
931                         Monitor.Exit (locker);
932                         return aread;
933                 }
934                 
935                 public override WebResponse EndGetResponse (IAsyncResult asyncResult)
936                 {
937                         if (asyncResult == null)
938                                 throw new ArgumentNullException ("asyncResult");
939
940                         WebAsyncResult result = asyncResult as WebAsyncResult;
941                         if (result == null)
942                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
943
944                         if (!result.WaitUntilComplete (timeout, false)) {
945                                 Abort ();
946                                 throw new WebException("The request timed out", WebExceptionStatus.Timeout);
947                         }
948
949                         if (result.GotException)
950                                 throw result.Exception;
951
952                         return result.Response;
953                 }
954                 
955 #if NET_3_5
956                 public Stream EndGetRequestStream (IAsyncResult asyncResult, out TransportContext transportContext)
957                 {
958                         transportContext = null;
959                         return EndGetRequestStream (asyncResult);
960                 }
961 #endif
962
963                 public override WebResponse GetResponse()
964                 {
965                         WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
966                         return EndGetResponse (result);
967                 }
968                 
969                 internal bool FinishedReading {
970                         get { return finished_reading; }
971                         set { finished_reading = value; }
972                 }
973
974                 internal bool Aborted {
975                         get { return Interlocked.CompareExchange (ref aborted, 0, 0) == 1; }
976                 }
977
978                 public override void Abort ()
979                 {
980                         if (Interlocked.CompareExchange (ref aborted, 1, 0) == 1)
981                                 return;
982
983                         if (haveResponse && finished_reading)
984                                 return;
985
986                         haveResponse = true;
987                         if (abortHandler != null) {
988                                 try {
989                                         abortHandler (this, EventArgs.Empty);
990                                 } catch (Exception) {}
991                                 abortHandler = null;
992                         }
993
994                         if (asyncWrite != null) {
995                                 WebAsyncResult r = asyncWrite;
996                                 if (!r.IsCompleted) {
997                                         try {
998                                                 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); 
999                                                 r.SetCompleted (false, wexc);
1000                                                 r.DoCallback ();
1001                                         } catch {}
1002                                 }
1003                                 asyncWrite = null;
1004                         }                       
1005
1006                         if (asyncRead != null) {
1007                                 WebAsyncResult r = asyncRead;
1008                                 if (!r.IsCompleted) {
1009                                         try {
1010                                                 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); 
1011                                                 r.SetCompleted (false, wexc);
1012                                                 r.DoCallback ();
1013                                         } catch {}
1014                                 }
1015                                 asyncRead = null;
1016                         }                       
1017
1018                         if (writeStream != null) {
1019                                 try {
1020                                         writeStream.Close ();
1021                                         writeStream = null;
1022                                 } catch {}
1023                         }
1024
1025                         if (webResponse != null) {
1026                                 try {
1027                                         webResponse.Close ();
1028                                         webResponse = null;
1029                                 } catch {}
1030                         }
1031                 }               
1032                 
1033                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
1034                                                   StreamingContext streamingContext)
1035                 {
1036                         GetObjectData (serializationInfo, streamingContext);
1037                 }
1038
1039                 protected override void GetObjectData (SerializationInfo serializationInfo,
1040                         StreamingContext streamingContext)
1041                 {
1042                         SerializationInfo info = serializationInfo;
1043
1044                         info.AddValue ("requestUri", requestUri, typeof (Uri));
1045                         info.AddValue ("actualUri", actualUri, typeof (Uri));
1046                         info.AddValue ("allowAutoRedirect", allowAutoRedirect);
1047                         info.AddValue ("allowBuffering", allowBuffering);
1048                         info.AddValue ("certificates", certificates, typeof (X509CertificateCollection));
1049                         info.AddValue ("connectionGroup", connectionGroup);
1050                         info.AddValue ("contentLength", contentLength);
1051                         info.AddValue ("webHeaders", webHeaders, typeof (WebHeaderCollection));
1052                         info.AddValue ("keepAlive", keepAlive);
1053                         info.AddValue ("maxAutoRedirect", maxAutoRedirect);
1054                         info.AddValue ("mediaType", mediaType);
1055                         info.AddValue ("method", method);
1056                         info.AddValue ("initialMethod", initialMethod);
1057                         info.AddValue ("pipelined", pipelined);
1058                         info.AddValue ("version", version, typeof (Version));
1059                         info.AddValue ("proxy", proxy, typeof (IWebProxy));
1060                         info.AddValue ("sendChunked", sendChunked);
1061                         info.AddValue ("timeout", timeout);
1062                         info.AddValue ("redirects", redirects);
1063                         info.AddValue ("host", host);
1064                 }
1065                 
1066                 void CheckRequestStarted () 
1067                 {
1068                         if (requestSent)
1069                                 throw new InvalidOperationException ("request started");
1070                 }
1071
1072                 internal void DoContinueDelegate (int statusCode, WebHeaderCollection headers)
1073                 {
1074                         if (continueDelegate != null)
1075                                 continueDelegate (statusCode, headers);
1076                 }
1077                 
1078                 bool Redirect (WebAsyncResult result, HttpStatusCode code)
1079                 {
1080                         redirects++;
1081                         Exception e = null;
1082                         string uriString = null;
1083                         switch (code) {
1084                         case HttpStatusCode.Ambiguous: // 300
1085                                 e = new WebException ("Ambiguous redirect.");
1086                                 break;
1087                         case HttpStatusCode.MovedPermanently: // 301
1088                         case HttpStatusCode.Redirect: // 302
1089                                 if (method == "POST")
1090                                         method = "GET";
1091                                 break;
1092                         case HttpStatusCode.TemporaryRedirect: // 307
1093                                 break;
1094                         case HttpStatusCode.SeeOther: //303
1095                                 method = "GET";
1096                                 break;
1097                         case HttpStatusCode.NotModified: // 304
1098                                 return false;
1099                         case HttpStatusCode.UseProxy: // 305
1100                                 e = new NotImplementedException ("Proxy support not available.");
1101                                 break;
1102                         case HttpStatusCode.Unused: // 306
1103                         default:
1104                                 e = new ProtocolViolationException ("Invalid status code: " + (int) code);
1105                                 break;
1106                         }
1107
1108                         if (e != null)
1109                                 throw e;
1110
1111                         //contentLength = -1;
1112                         //bodyBufferLength = 0;
1113                         //bodyBuffer = null;
1114                         uriString = webResponse.Headers ["Location"];
1115
1116                         if (uriString == null)
1117                                 throw new WebException ("No Location header found for " + (int) code,
1118                                                         WebExceptionStatus.ProtocolError);
1119
1120                         Uri prev = actualUri;
1121                         try {
1122                                 actualUri = new Uri (actualUri, uriString);
1123                         } catch (Exception) {
1124                                 throw new WebException (String.Format ("Invalid URL ({0}) for {1}",
1125                                                                         uriString, (int) code),
1126                                                                         WebExceptionStatus.ProtocolError);
1127                         }
1128
1129                         hostChanged = (actualUri.Scheme != prev.Scheme || Host != prev.Authority);
1130                         return true;
1131                 }
1132
1133                 string GetHeaders ()
1134                 {
1135                         bool continue100 = false;
1136                         if (sendChunked) {
1137                                 continue100 = true;
1138                                 webHeaders.RemoveAndAdd ("Transfer-Encoding", "chunked");
1139                                 webHeaders.RemoveInternal ("Content-Length");
1140                         } else if (contentLength != -1) {
1141                                 if (ntlm_auth_state != NtlmAuthState.Challenge) {
1142                                         if (contentLength > 0)
1143                                                 continue100 = true;
1144
1145                                         webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
1146                                 } else {
1147                                         webHeaders.SetInternal ("Content-Length", "0");
1148                                 }
1149                                 webHeaders.RemoveInternal ("Transfer-Encoding");
1150                         } else {
1151                                 webHeaders.RemoveInternal ("Content-Length");
1152                         }
1153
1154                         if (actualVersion == HttpVersion.Version11 && continue100 &&
1155                             servicePoint.SendContinue) { // RFC2616 8.2.3
1156                                 webHeaders.RemoveAndAdd ("Expect" , "100-continue");
1157                                 expectContinue = true;
1158                         } else {
1159                                 webHeaders.RemoveInternal ("Expect");
1160                                 expectContinue = false;
1161                         }
1162
1163                         bool proxy_query = ProxyQuery;
1164                         string connectionHeader = (proxy_query) ? "Proxy-Connection" : "Connection";
1165                         webHeaders.RemoveInternal ((!proxy_query) ? "Proxy-Connection" : "Connection");
1166                         Version proto_version = servicePoint.ProtocolVersion;
1167                         bool spoint10 = (proto_version == null || proto_version == HttpVersion.Version10);
1168
1169                         if (keepAlive && (version == HttpVersion.Version10 || spoint10)) {
1170                                 if (webHeaders[connectionHeader] == null
1171                                     || webHeaders[connectionHeader].IndexOf ("keep-alive", StringComparison.OrdinalIgnoreCase) == -1)
1172                                         webHeaders.RemoveAndAdd (connectionHeader, "keep-alive");
1173                         } else if (!keepAlive && version == HttpVersion.Version11) {
1174                                 webHeaders.RemoveAndAdd (connectionHeader, "close");
1175                         }
1176
1177                         webHeaders.SetInternal ("Host", Host);
1178                         if (cookieContainer != null) {
1179                                 string cookieHeader = cookieContainer.GetCookieHeader (actualUri);
1180                                 if (cookieHeader != "")
1181                                         webHeaders.RemoveAndAdd ("Cookie", cookieHeader);
1182                                 else
1183                                         webHeaders.RemoveInternal ("Cookie");
1184                         }
1185
1186                         string accept_encoding = null;
1187                         if ((auto_decomp & DecompressionMethods.GZip) != 0)
1188                                 accept_encoding = "gzip";
1189                         if ((auto_decomp & DecompressionMethods.Deflate) != 0)
1190                                 accept_encoding = accept_encoding != null ? "gzip, deflate" : "deflate";
1191                         if (accept_encoding != null)
1192                                 webHeaders.RemoveAndAdd ("Accept-Encoding", accept_encoding);
1193
1194                         if (!usedPreAuth && preAuthenticate)
1195                                 DoPreAuthenticate ();
1196
1197                         return webHeaders.ToString ();
1198                 }
1199
1200                 void DoPreAuthenticate ()
1201                 {
1202                         bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
1203                         ICredentials creds = (!isProxy || credentials != null) ? credentials : proxy.Credentials;
1204                         Authorization auth = AuthenticationManager.PreAuthenticate (this, creds);
1205                         if (auth == null)
1206                                 return;
1207
1208                         webHeaders.RemoveInternal ("Proxy-Authorization");
1209                         webHeaders.RemoveInternal ("Authorization");
1210                         string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization";
1211                         webHeaders [authHeader] = auth.Message;
1212                         usedPreAuth = true;
1213                 }
1214                 
1215                 internal void SetWriteStreamError (WebExceptionStatus status, Exception exc)
1216                 {
1217                         if (Aborted)
1218                                 return;
1219
1220                         WebAsyncResult r = asyncWrite;
1221                         if (r == null)
1222                                 r = asyncRead;
1223
1224                         if (r != null) {
1225                                 string msg;
1226                                 WebException wex;
1227                                 if (exc == null) {
1228                                         msg = "Error: " + status;
1229                                         wex = new WebException (msg, status);
1230                                 } else {
1231                                         msg = String.Format ("Error: {0} ({1})", status, exc.Message);
1232                                         wex = new WebException (msg, exc, status);
1233                                 }
1234                                 r.SetCompleted (false, wex);
1235                                 r.DoCallback ();
1236                         }
1237                 }
1238
1239                 internal void SendRequestHeaders (bool propagate_error)
1240                 {
1241                         StringBuilder req = new StringBuilder ();
1242                         string query;
1243                         if (!ProxyQuery) {
1244                                 query = actualUri.PathAndQuery;
1245                         } else {
1246                                 query = String.Format ("{0}://{1}{2}",  actualUri.Scheme,
1247                                                                         Host,
1248                                                                         actualUri.PathAndQuery);
1249                         }
1250                         
1251                         if (!force_version && servicePoint.ProtocolVersion != null && servicePoint.ProtocolVersion < version) {
1252                                 actualVersion = servicePoint.ProtocolVersion;
1253                         } else {
1254                                 actualVersion = version;
1255                         }
1256
1257                         req.AppendFormat ("{0} {1} HTTP/{2}.{3}\r\n", method, query,
1258                                                                 actualVersion.Major, actualVersion.Minor);
1259                         req.Append (GetHeaders ());
1260                         string reqstr = req.ToString ();
1261                         byte [] bytes = Encoding.UTF8.GetBytes (reqstr);
1262                         try {
1263                                 writeStream.SetHeaders (bytes);
1264                         } catch (WebException wexc) {
1265                                 SetWriteStreamError (wexc.Status, wexc);
1266                                 if (propagate_error)
1267                                         throw;
1268                         } catch (Exception exc) {
1269                                 SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
1270                                 if (propagate_error)
1271                                         throw;
1272                         }
1273                 }
1274
1275                 internal void SetWriteStream (WebConnectionStream stream)
1276                 {
1277                         if (Aborted)
1278                                 return;
1279                         
1280                         writeStream = stream;
1281                         if (bodyBuffer != null) {
1282                                 webHeaders.RemoveInternal ("Transfer-Encoding");
1283                                 contentLength = bodyBufferLength;
1284                                 writeStream.SendChunked = false;
1285                         }
1286
1287                         SendRequestHeaders (false);
1288
1289                         haveRequest = true;
1290                         
1291                         if (bodyBuffer != null) {
1292                                 // The body has been written and buffered. The request "user"
1293                                 // won't write it again, so we must do it.
1294                                 if (ntlm_auth_state != NtlmAuthState.Challenge) {
1295                                         writeStream.Write (bodyBuffer, 0, bodyBufferLength);
1296                                         bodyBuffer = null;
1297                                         writeStream.Close ();
1298                                 }
1299                         } else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
1300                                         method != "TRACE") {
1301                                 if (getResponseCalled && !writeStream.RequestWritten)
1302                                         writeStream.WriteRequest ();
1303                         }
1304
1305                         if (asyncWrite != null) {
1306                                 asyncWrite.SetCompleted (false, stream);
1307                                 asyncWrite.DoCallback ();
1308                                 asyncWrite = null;
1309                         }
1310                 }
1311
1312                 internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
1313                 {
1314                         if (Aborted)
1315                                 return;
1316                         lock (locker) {
1317                         string msg = String.Format ("Error getting response stream ({0}): {1}", where, status);
1318                         WebAsyncResult r = asyncRead;
1319                         if (r == null)
1320                                 r = asyncWrite;
1321
1322                         WebException wexc;
1323                         if (e is WebException) {
1324                                 wexc = (WebException) e;
1325                         } else {
1326                                 wexc = new WebException (msg, e, status, null); 
1327                         }
1328                         if (r != null) {
1329                                 if (!r.IsCompleted) {
1330                                         r.SetCompleted (false, wexc);
1331                                         r.DoCallback ();
1332                                 } else if (r == asyncWrite) {
1333                                         saved_exc = wexc;
1334                                 }
1335                                 haveResponse = true;
1336                                 asyncRead = null;
1337                                 asyncWrite = null;
1338                         } else {
1339                                 haveResponse = true;
1340                                 saved_exc = wexc;
1341                         }
1342                         }
1343                 }
1344
1345                 void CheckSendError (WebConnectionData data)
1346                 {
1347                         // Got here, but no one called GetResponse
1348                         int status = data.StatusCode;
1349                         if (status < 400 || status == 401 || status == 407)
1350                                 return;
1351
1352                         if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) {
1353                                 // The request has not been completely sent and we got here!
1354                                 // We should probably just close and cause an error in any case,
1355                                 saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse); 
1356                                 if (allowBuffering || sendChunked || writeStream.totalWritten >= contentLength) {
1357                                         webResponse.ReadAll ();
1358                                 } else {
1359                                         writeStream.IgnoreIOErrors = true;
1360                                 }
1361                         }
1362                 }
1363
1364                 void HandleNtlmAuth (WebAsyncResult r)
1365                 {
1366                         WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
1367                         if (wce != null) {
1368                                 WebConnection cnc = wce.Connection;
1369                                 cnc.PriorityRequest = this;
1370                                 bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
1371                                 ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
1372                                 if (creds != null) {
1373                                         cnc.NtlmCredential = creds.GetCredential (requestUri, "NTLM");
1374                                         cnc.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah;
1375                                 }
1376                         }
1377                         r.Reset ();
1378                         finished_reading = false;
1379                         haveResponse = false;
1380                         webResponse.ReadAll ();
1381                         webResponse = null;
1382                 }
1383
1384                 internal void SetResponseData (WebConnectionData data)
1385                 {
1386                         lock (locker) {
1387                         if (Aborted) {
1388                                 if (data.stream != null)
1389                                         data.stream.Close ();
1390                                 return;
1391                         }
1392
1393                         WebException wexc = null;
1394                         try {
1395                                 webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer);
1396                         } catch (Exception e) {
1397                                 wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null); 
1398                                 if (data.stream != null)
1399                                         data.stream.Close ();
1400                         }
1401
1402                         if (wexc == null && (method == "POST" || method == "PUT")) {
1403                                 CheckSendError (data);
1404                                 if (saved_exc != null)
1405                                         wexc = (WebException) saved_exc;
1406                         }
1407
1408                         WebAsyncResult r = asyncRead;
1409
1410                         bool forced = false;
1411                         if (r == null && webResponse != null) {
1412                                 // This is a forced completion (302, 204)...
1413                                 forced = true;
1414                                 r = new WebAsyncResult (null, null);
1415                                 r.SetCompleted (false, webResponse);
1416                         }
1417
1418                         if (r != null) {
1419                                 if (wexc != null) {
1420                                         haveResponse = true;
1421                                         if (!r.IsCompleted)
1422                                                 r.SetCompleted (false, wexc);
1423                                         r.DoCallback ();
1424                                         return;
1425                                 }
1426
1427                                 bool redirected;
1428                                 try {
1429                                         redirected = CheckFinalStatus (r);
1430                                         if (!redirected) {
1431                                                 if (ntlm_auth_state != NtlmAuthState.None && authCompleted && webResponse != null
1432                                                         && (int)webResponse.StatusCode < 400) {
1433                                                         WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
1434                                                         if (wce != null) {
1435                                                                 WebConnection cnc = wce.Connection;
1436                                                                 cnc.NtlmAuthenticated = true;
1437                                                         }
1438                                                 }
1439
1440                                                 // clear internal buffer so that it does not
1441                                                 // hold possible big buffer (bug #397627)
1442                                                 if (writeStream != null)
1443                                                         writeStream.KillBuffer ();
1444
1445                                                 haveResponse = true;
1446                                                 r.SetCompleted (false, webResponse);
1447                                                 r.DoCallback ();
1448                                         } else {
1449                                                 if (webResponse != null) {
1450                                                         if (ntlm_auth_state != NtlmAuthState.None) {
1451                                                                 HandleNtlmAuth (r);
1452                                                                 return;
1453                                                         }
1454                                                         webResponse.Close ();
1455                                                 }
1456                                                 finished_reading = false;
1457                                                 haveResponse = false;
1458                                                 webResponse = null;
1459                                                 r.Reset ();
1460                                                 servicePoint = GetServicePoint ();
1461                                                 abortHandler = servicePoint.SendRequest (this, connectionGroup);
1462                                         }
1463                                 } catch (WebException wexc2) {
1464                                         if (forced) {
1465                                                 saved_exc = wexc2;
1466                                                 haveResponse = true;
1467                                         }
1468                                         r.SetCompleted (false, wexc2);
1469                                         r.DoCallback ();
1470                                         return;
1471                                 } catch (Exception ex) {
1472                                         wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null); 
1473                                         if (forced) {
1474                                                 saved_exc = wexc;
1475                                                 haveResponse = true;
1476                                         }
1477                                         r.SetCompleted (false, wexc);
1478                                         r.DoCallback ();
1479                                         return;
1480                                 }
1481                         }
1482                         }
1483                 }
1484
1485                 bool CheckAuthorization (WebResponse response, HttpStatusCode code)
1486                 {
1487                         authCompleted = false;
1488                         if (code == HttpStatusCode.Unauthorized && credentials == null)
1489                                 return false;
1490
1491                         bool isProxy = (code == HttpStatusCode.ProxyAuthenticationRequired);
1492                         if (isProxy && (proxy == null || proxy.Credentials == null))
1493                                 return false;
1494
1495                         string [] authHeaders = response.Headers.GetValues_internal ( (isProxy) ? "Proxy-Authenticate" : "WWW-Authenticate", false);
1496                         if (authHeaders == null || authHeaders.Length == 0)
1497                                 return false;
1498
1499                         ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
1500                         Authorization auth = null;
1501                         foreach (string authHeader in authHeaders) {
1502                                 auth = AuthenticationManager.Authenticate (authHeader, this, creds);
1503                                 if (auth != null)
1504                                         break;
1505                         }
1506                         if (auth == null)
1507                                 return false;
1508                         webHeaders [(isProxy) ? "Proxy-Authorization" : "Authorization"] = auth.Message;
1509                         authCompleted = auth.Complete;
1510                         bool is_ntlm = (auth.Module.AuthenticationType == "NTLM");
1511                         if (is_ntlm)
1512                                 ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1);
1513                         return true;
1514                 }
1515
1516                 // Returns true if redirected
1517                 bool CheckFinalStatus (WebAsyncResult result)
1518                 {
1519                         if (result.GotException) {
1520                                 bodyBuffer = null;
1521                                 throw result.Exception;
1522                         }
1523
1524                         Exception throwMe = result.Exception;
1525
1526                         HttpWebResponse resp = result.Response;
1527                         WebExceptionStatus protoError = WebExceptionStatus.ProtocolError;
1528                         HttpStatusCode code = 0;
1529                         if (throwMe == null && webResponse != null) {
1530                                 code = webResponse.StatusCode;
1531                                 if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
1532                                      (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
1533                                         if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
1534                                                 // Keep the written body, so it can be rewritten in the retry
1535                                                 if (InternalAllowBuffering) {
1536                                                         // NTLM: This is to avoid sending data in the 'challenge' request
1537                                                         // We save it in the first request (first 401), don't send anything
1538                                                         // in the challenge request and send it in the response request along
1539                                                         // with the buffers kept form the first request.
1540                                                         if (ntlm_auth_state != NtlmAuthState.Response) {
1541                                                                 bodyBuffer = writeStream.WriteBuffer;
1542                                                                 bodyBufferLength = writeStream.WriteBufferLength;
1543                                                         }
1544                                                         return true;
1545                                                 } else if (method != "PUT" && method != "POST") {
1546                                                         bodyBuffer = null;
1547                                                         return true;
1548                                                 }
1549
1550                                                 if (!ThrowOnError)
1551                                                         return false;
1552                                                         
1553                                                 writeStream.InternalClose ();
1554                                                 writeStream = null;
1555                                                 webResponse.Close ();
1556                                                 webResponse = null;
1557                                                 bodyBuffer = null;
1558                                                         
1559                                                 throw new WebException ("This request requires buffering " +
1560                                                                         "of data for authentication or " +
1561                                                                         "redirection to be sucessful.");
1562                                         }
1563                                 }
1564
1565                                 bodyBuffer = null;
1566                                 if ((int) code >= 400) {
1567                                         string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1568                                                                     (int) code, webResponse.StatusDescription);
1569                                         throwMe = new WebException (err, null, protoError, webResponse);
1570                                         webResponse.ReadAll ();
1571                                 } else if ((int) code == 304 && allowAutoRedirect) {
1572                                         string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1573                                                                     (int) code, webResponse.StatusDescription);
1574                                         throwMe = new WebException (err, null, protoError, webResponse);
1575                                 } else if ((int) code >= 300 && allowAutoRedirect && redirects >= maxAutoRedirect) {
1576                                         throwMe = new WebException ("Max. redirections exceeded.", null,
1577                                                                     protoError, webResponse);
1578                                         webResponse.ReadAll ();
1579                                 }
1580                         }
1581
1582                         bodyBuffer = null;
1583                         if (throwMe == null) {
1584                                 bool b = false;
1585                                 int c = (int) code;
1586                                 if (allowAutoRedirect && c >= 300) {
1587                                         if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
1588                                                 bodyBuffer = writeStream.WriteBuffer;
1589                                                 bodyBufferLength = writeStream.WriteBufferLength;
1590                                         }
1591                                         b = Redirect (result, code);
1592                                         if (b && ntlm_auth_state != 0)
1593                                                 ntlm_auth_state = 0;
1594                                 }
1595
1596                                 if (resp != null && c >= 300 && c != 304)
1597                                         resp.ReadAll ();
1598
1599                                 return b;
1600                         }
1601                                 
1602                         if (!ThrowOnError)
1603                                 return false;
1604
1605                         if (writeStream != null) {
1606                                 writeStream.InternalClose ();
1607                                 writeStream = null;
1608                         }
1609
1610                         webResponse = null;
1611
1612                         throw throwMe;
1613                 }
1614
1615                 internal bool ReuseConnection {
1616                         get;
1617                         set;
1618                 }
1619
1620                 internal WebConnection StoredConnection;
1621         }
1622 }
1623