New test.
[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.IO;
38 using System.Net.Sockets;
39 using System.Runtime.Remoting.Messaging;
40 using System.Runtime.Serialization;
41 using System.Security.Cryptography.X509Certificates;
42 using System.Text;
43 using System.Threading;
44
45 namespace System.Net 
46 {
47         [Serializable]
48         public class HttpWebRequest : WebRequest, ISerializable
49         {
50                 Uri requestUri;
51                 Uri actualUri;
52                 bool hostChanged;
53                 bool allowAutoRedirect = true;
54                 bool allowBuffering = true;
55                 X509CertificateCollection certificates;
56                 string connectionGroup;
57                 long contentLength = -1;
58                 HttpContinueDelegate continueDelegate;
59                 CookieContainer cookieContainer;
60                 ICredentials credentials;
61                 bool haveResponse;              
62                 bool haveRequest;
63                 bool requestSent;
64                 WebHeaderCollection webHeaders = new WebHeaderCollection (true);
65                 bool keepAlive = true;
66                 int maxAutoRedirect = 50;
67                 string mediaType = String.Empty;
68                 string method = "GET";
69                 string initialMethod = "GET";
70                 bool pipelined = true;
71                 bool preAuthenticate;
72                 bool usedPreAuth;
73                 Version version = HttpVersion.Version11;
74                 Version actualVersion;
75                 IWebProxy proxy;
76                 bool sendChunked;
77                 ServicePoint servicePoint;
78                 int timeout = 100000;
79                 
80                 WebConnectionStream writeStream;
81                 HttpWebResponse webResponse;
82                 WebAsyncResult asyncWrite;
83                 WebAsyncResult asyncRead;
84                 EventHandler abortHandler;
85                 bool aborted;
86                 bool gotRequestStream;
87                 int redirects;
88                 bool expectContinue;
89                 bool authCompleted;
90                 byte[] bodyBuffer;
91                 int bodyBufferLength;
92                 bool getResponseCalled;
93                 Exception saved_exc;
94                 object locker = new object ();
95 #if NET_1_1
96                 int maxResponseHeadersLength;
97                 static int defaultMaxResponseHeadersLength;
98                 int readWriteTimeout = 300000; // ms
99                 
100                 // Constructors
101                 static HttpWebRequest ()
102                 {
103                         NetConfig config = ConfigurationSettings.GetConfig ("system.net/settings") as NetConfig;
104                         defaultMaxResponseHeadersLength = 64 * 1024;
105                         if (config != null) {
106                                 int x = config.MaxResponseHeadersLength;
107                                 if (x != -1)
108                                         x *= 64;
109
110                                 defaultMaxResponseHeadersLength = x;
111                         }
112                 }
113 #endif
114                 
115                 internal HttpWebRequest (Uri uri) 
116                 {
117                         this.requestUri = uri;
118                         this.actualUri = uri;
119                         this.proxy = GlobalProxySelection.Select;
120                 }               
121                 
122                 protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) 
123                 {
124                         SerializationInfo info = serializationInfo;
125
126                         requestUri = (Uri) info.GetValue ("requestUri", typeof (Uri));
127                         actualUri = (Uri) info.GetValue ("actualUri", typeof (Uri));
128                         allowAutoRedirect = info.GetBoolean ("allowAutoRedirect");
129                         allowBuffering = info.GetBoolean ("allowBuffering");
130                         certificates = (X509CertificateCollection) info.GetValue ("certificates", typeof (X509CertificateCollection));
131                         connectionGroup = info.GetString ("connectionGroup");
132                         contentLength = info.GetInt64 ("contentLength");
133                         webHeaders = (WebHeaderCollection) info.GetValue ("webHeaders", typeof (WebHeaderCollection));
134                         keepAlive = info.GetBoolean ("keepAlive");
135                         maxAutoRedirect = info.GetInt32 ("maxAutoRedirect");
136                         mediaType = info.GetString ("mediaType");
137                         method = info.GetString ("method");
138                         initialMethod = info.GetString ("initialMethod");
139                         pipelined = info.GetBoolean ("pipelined");
140                         version = (Version) info.GetValue ("version", typeof (Version));
141                         proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy));
142                         sendChunked = info.GetBoolean ("sendChunked");
143                         timeout = info.GetInt32 ("timeout");
144                         redirects = info.GetInt32 ("redirects");
145                 }
146                 
147                 // Properties
148                 
149                 public string Accept {
150                         get { return webHeaders ["Accept"]; }
151                         set {
152                                 CheckRequestStarted ();
153                                 webHeaders.RemoveAndAdd ("Accept", value);
154                         }
155                 }
156                 
157                 public Uri Address {
158                         get { return actualUri; }
159                 }
160                 
161                 public bool AllowAutoRedirect {
162                         get { return allowAutoRedirect; }
163                         set { this.allowAutoRedirect = value; }
164                 }
165                 
166                 public bool AllowWriteStreamBuffering {
167                         get { return allowBuffering; }
168                         set { allowBuffering = value; }
169                 }
170                 
171                 internal bool InternalAllowBuffering {
172                         get {
173                                 return (allowBuffering && (method == "PUT" || method == "POST"));
174                         }
175                 }
176                 
177                 public X509CertificateCollection ClientCertificates {
178                         get {
179                                 if (certificates == null)
180                                         certificates = new X509CertificateCollection ();
181
182                                 return certificates;
183                         }
184                 }
185                 
186                 public string Connection {
187                         get { return webHeaders ["Connection"]; }
188                         set {
189                                 CheckRequestStarted ();
190                                 string val = value;
191                                 if (val != null) 
192                                         val = val.Trim ().ToLower ();
193
194                                 if (val == null || val.Length == 0) {
195                                         webHeaders.RemoveInternal ("Connection");
196                                         return;
197                                 }
198
199                                 if (val == "keep-alive" || val == "close") 
200                                         throw new ArgumentException ("Keep-Alive and Close may not be set with this property");
201
202                                 if (keepAlive && val.IndexOf ("keep-alive") == -1)
203                                         value = value + ", Keep-Alive";
204                                 
205                                 webHeaders.RemoveAndAdd ("Connection", value);
206                         }
207                 }               
208                 
209                 public override string ConnectionGroupName { 
210                         get { return connectionGroup; }
211                         set { connectionGroup = value; }
212                 }
213                 
214                 public override long ContentLength { 
215                         get { return contentLength; }
216                         set { 
217                                 CheckRequestStarted ();
218                                 if (value < 0)
219                                         throw new ArgumentOutOfRangeException ("value", "Content-Length must be >= 0");
220                                         
221                                 contentLength = value;
222                         }
223                 }
224                 
225                 internal long InternalContentLength {
226                         set { contentLength = value; }
227                 }
228                 
229                 public override string ContentType { 
230                         get { return webHeaders ["Content-Type"]; }
231                         set {
232                                 if (value == null || value.Trim().Length == 0) {
233                                         webHeaders.RemoveInternal ("Content-Type");
234                                         return;
235                                 }
236                                 webHeaders.RemoveAndAdd ("Content-Type", value);
237                         }
238                 }
239                 
240                 public HttpContinueDelegate ContinueDelegate {
241                         get { return continueDelegate; }
242                         set { continueDelegate = value; }
243                 }
244                 
245                 public CookieContainer CookieContainer {
246                         get { return cookieContainer; }
247                         set { cookieContainer = value; }
248                 }
249                 
250                 public override ICredentials Credentials { 
251                         get { return credentials; }
252                         set { credentials = value; }
253                 }
254                 
255                 public string Expect {
256                         get { return webHeaders ["Expect"]; }
257                         set {
258                                 CheckRequestStarted ();
259                                 string val = value;
260                                 if (val != null)
261                                         val = val.Trim ().ToLower ();
262
263                                 if (val == null || val.Length == 0) {
264                                         webHeaders.RemoveInternal ("Expect");
265                                         return;
266                                 }
267
268                                 if (val == "100-continue")
269                                         throw new ArgumentException ("100-Continue cannot be set with this property.",
270                                                                      "value");
271                                 webHeaders.RemoveAndAdd ("Expect", value);
272                         }
273                 }
274                 
275                 public bool HaveResponse {
276                         get { return haveResponse; }
277                 }
278                 
279                 public override WebHeaderCollection Headers { 
280                         get { return webHeaders; }
281                         set {
282                                 CheckRequestStarted ();
283                                 WebHeaderCollection newHeaders = new WebHeaderCollection (true);
284                                 int count = value.Count;
285                                 for (int i = 0; i < count; i++) 
286                                         newHeaders.Add (value.GetKey (i), value.Get (i));
287
288                                 webHeaders = newHeaders;
289                         }
290                 }
291                 
292                 public DateTime IfModifiedSince {
293                         get { 
294                                 string str = webHeaders ["If-Modified-Since"];
295                                 if (str == null)
296                                         return DateTime.Now;
297                                 try {
298                                         return MonoHttpDate.Parse (str);
299                                 } catch (Exception) {
300                                         return DateTime.Now;
301                                 }
302                         }
303                         set {
304                                 CheckRequestStarted ();
305                                 // rfc-1123 pattern
306                                 webHeaders.SetInternal ("If-Modified-Since", 
307                                         value.ToUniversalTime ().ToString ("r", null));
308                                 // TODO: check last param when using different locale
309                         }
310                 }
311
312                 public bool KeepAlive {         
313                         get {
314                                 return keepAlive;
315                         }
316                         set {
317                                 keepAlive = value;
318                         }
319                 }
320                 
321                 public int MaximumAutomaticRedirections {
322                         get { return maxAutoRedirect; }
323                         set {
324                                 if (value <= 0)
325                                         throw new ArgumentException ("Must be > 0", "value");
326
327                                 maxAutoRedirect = value;
328                         }                       
329                 }
330
331 #if NET_1_1
332                 [MonoTODO ("Use this")]
333                 public int MaximumResponseHeadersLength {
334                         get { return maxResponseHeadersLength; }
335                         set { maxResponseHeadersLength = value; }
336                 }
337
338                 [MonoTODO ("Use this")]
339                 public static int DefaultMaximumResponseHeadersLength {
340                         get { return defaultMaxResponseHeadersLength; }
341                         set { defaultMaxResponseHeadersLength = value; }
342                 }
343
344                 public
345 #else
346                 internal
347 #endif
348                 int ReadWriteTimeout {
349                         get { return readWriteTimeout; }
350                         set {
351                                 if (requestSent)
352                                         throw new InvalidOperationException ("The request has already been sent.");
353
354                                 if (value < -1)
355                                         throw new ArgumentOutOfRangeException ("value", "Must be >= -1");
356
357                                 readWriteTimeout = value;
358                         }
359                 }
360                 
361                 public string MediaType {
362                         get { return mediaType; }
363                         set { 
364                                 mediaType = value;
365                         }
366                 }
367                 
368                 public override string Method { 
369                         get { return this.method; }
370                         set { 
371                                 if (value == null || value.Trim () == "")
372                                         throw new ArgumentException ("not a valid method");
373
374                                 method = value;
375                         }
376                 }
377                 
378                 public bool Pipelined {
379                         get { return pipelined; }
380                         set { pipelined = value; }
381                 }               
382                 
383                 public override bool PreAuthenticate { 
384                         get { return preAuthenticate; }
385                         set { preAuthenticate = value; }
386                 }
387                 
388                 public Version ProtocolVersion {
389                         get { return version; }
390                         set { 
391                                 if (value != HttpVersion.Version10 && value != HttpVersion.Version11)
392                                         throw new ArgumentException ("value");
393
394                                 version = value; 
395                         }
396                 }
397                 
398                 public override IWebProxy Proxy { 
399                         get { return proxy; }
400                         set { 
401                                 CheckRequestStarted ();
402                                 if (value == null)
403                                         throw new ArgumentNullException ("value");
404
405                                 proxy = value;
406                                 servicePoint = null; // we may need a new one
407                         }
408                 }
409                 
410                 public string Referer {
411                         get { return webHeaders ["Referer"]; }
412                         set {
413                                 CheckRequestStarted ();
414                                 if (value == null || value.Trim().Length == 0) {
415                                         webHeaders.RemoveInternal ("Referer");
416                                         return;
417                                 }
418                                 webHeaders.SetInternal ("Referer", value);
419                         }
420                 }
421
422                 public override Uri RequestUri { 
423                         get { return requestUri; }
424                 }
425                 
426                 public bool SendChunked {
427                         get { return sendChunked; }
428                         set {
429                                 CheckRequestStarted ();
430                                 sendChunked = value;
431                         }
432                 }
433                 
434                 public ServicePoint ServicePoint {
435                         get { return GetServicePoint (); }
436                 }
437                 
438                 public override int Timeout { 
439                         get { return timeout; }
440                         set {
441                                 if (value < -1)
442                                         throw new ArgumentOutOfRangeException ("value");
443
444                                 timeout = value;
445                         }
446                 }
447                 
448                 public string TransferEncoding {
449                         get { return webHeaders ["Transfer-Encoding"]; }
450                         set {
451                                 CheckRequestStarted ();
452                                 string val = value;
453                                 if (val != null)
454                                         val = val.Trim ().ToLower ();
455
456                                 if (val == null || val.Length == 0) {
457                                         webHeaders.RemoveInternal ("Transfer-Encoding");
458                                         return;
459                                 }
460
461                                 if (val == "chunked")
462                                         throw new ArgumentException ("Chunked encoding must be set with the SendChunked property");
463
464                                 if (!sendChunked)
465                                         throw new ArgumentException ("SendChunked must be True", "value");
466
467                                 webHeaders.RemoveAndAdd ("Transfer-Encoding", value);
468                         }
469                 }
470                 
471                 public string UserAgent {
472                         get { return webHeaders ["User-Agent"]; }
473                         set { webHeaders.SetInternal ("User-Agent", value); }
474                 }
475
476 #if NET_1_1
477                 bool unsafe_auth_blah;
478                 [MonoTODO]
479                 public bool UnsafeAuthenticatedConnectionSharing
480                 {
481                         get { return unsafe_auth_blah; }
482                         set { unsafe_auth_blah = value; }
483                 }
484 #endif
485
486                 internal bool GotRequestStream {
487                         get { return gotRequestStream; }
488                 }
489
490                 internal bool ExpectContinue {
491                         get { return expectContinue; }
492                         set { expectContinue = value; }
493                 }
494                 
495                 internal Uri AuthUri {
496                         get { return actualUri; }
497                 }
498                 
499                 internal bool ProxyQuery {
500                         get { return servicePoint.UsesProxy && !servicePoint.UseConnect; }
501                 }
502                 
503                 // Methods
504                 
505                 internal ServicePoint GetServicePoint ()
506                 {
507                         lock (locker) {
508                                 if (hostChanged || servicePoint == null) {
509                                         servicePoint = ServicePointManager.FindServicePoint (actualUri, proxy);
510                                         hostChanged = false;
511                                 }
512                         }
513
514                         return servicePoint;
515                 }
516                 
517                 public void AddRange (int range)
518                 {
519                         AddRange ("bytes", range);
520                 }
521                 
522                 public void AddRange (int from, int to)
523                 {
524                         AddRange ("bytes", from, to);
525                 }
526                 
527                 public void AddRange (string rangeSpecifier, int range)
528                 {
529                         if (rangeSpecifier == null)
530                                 throw new ArgumentNullException ("rangeSpecifier");
531                         string value = webHeaders ["Range"];
532                         if (value == null || value.Length == 0) 
533                                 value = rangeSpecifier + "=";
534                         else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
535                                 value += ",";
536                         else
537                                 throw new InvalidOperationException ("rangeSpecifier");
538                         webHeaders.RemoveAndAdd ("Range", value + range + "-"); 
539                 }
540                 
541                 public void AddRange (string rangeSpecifier, int from, int to)
542                 {
543                         if (rangeSpecifier == null)
544                                 throw new ArgumentNullException ("rangeSpecifier");
545                         if (from < 0 || to < 0 || from > to)
546                                 throw new ArgumentOutOfRangeException ();                       
547                         string value = webHeaders ["Range"];
548                         if (value == null || value.Length == 0) 
549                                 value = rangeSpecifier + "=";
550                         else if (value.ToLower ().StartsWith (rangeSpecifier.ToLower () + "="))
551                                 value += ",";
552                         else
553                                 throw new InvalidOperationException ("rangeSpecifier");
554                         webHeaders.RemoveAndAdd ("Range", value + from + "-" + to);     
555                 }
556 #if !NET_2_0
557                 public override int GetHashCode ()
558                 {
559                         return base.GetHashCode ();
560                 }
561 #endif
562                 
563                 void CommonChecks (bool putpost)
564                 {
565                         if (method == null)
566                                 throw new ProtocolViolationException ("Method is null.");
567
568                         if (putpost && ((!keepAlive || (contentLength == -1 && !sendChunked)) && !allowBuffering))
569                                 throw new ProtocolViolationException ("Content-Length not set");
570
571                         string transferEncoding = TransferEncoding;
572                         if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "")
573                                 throw new ProtocolViolationException ("SendChunked should be true.");
574                 }
575
576                 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) 
577                 {
578                         if (aborted)
579                                 throw new WebException ("The request was previosly aborted.");
580
581                         bool send = !(method == "GET" || method == "CONNECT" || method == "HEAD");
582                         if (method == null || !send)
583                                 throw new ProtocolViolationException ("Cannot send data when method is: " + method);
584
585                         CommonChecks (send);
586                         
587                         lock (locker)
588                         {
589                                 if (asyncWrite != null) {
590                                         throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
591                                                                 "method while a previous call is still in progress.");
592                                 }
593         
594                                 asyncWrite = new WebAsyncResult (this, callback, state);
595                                 initialMethod = method;
596                                 if (haveRequest) {
597                                         if (writeStream != null) {
598                                                 asyncWrite.SetCompleted (true, writeStream);
599                                                 asyncWrite.DoCallback ();
600                                                 return asyncWrite;
601                                         }
602                                 }
603                                 
604                                 gotRequestStream = true;
605                                 WebAsyncResult result = asyncWrite;
606                                 if (!requestSent) {
607                                         requestSent = true;
608                                         redirects = 0;
609                                         servicePoint = GetServicePoint ();
610                                         abortHandler = servicePoint.SendRequest (this, connectionGroup);
611                                 }
612                                 return result;
613                         }
614                 }
615
616                 public override Stream EndGetRequestStream (IAsyncResult asyncResult)
617                 {
618                         if (asyncResult == null)
619                                 throw new ArgumentNullException ("asyncResult");
620
621                         WebAsyncResult result = asyncResult as WebAsyncResult;
622                         if (result == null)
623                                 throw new ArgumentException ("Invalid IAsyncResult");
624
625                         asyncWrite = result;
626                         result.WaitUntilComplete ();
627
628                         Exception e = result.Exception;
629                         if (e != null)
630                                 throw e;
631
632                         return result.WriteStream;
633                 }
634                 
635                 public override Stream GetRequestStream()
636                 {
637                         IAsyncResult asyncResult = BeginGetRequestStream (null, null);
638                         asyncWrite = (WebAsyncResult) asyncResult;
639                         if (!asyncResult.AsyncWaitHandle.WaitOne (timeout, false)) {
640                                 Abort ();
641                                 throw new WebException ("The request timed out", WebExceptionStatus.Timeout);
642                         }
643
644                         return EndGetRequestStream (asyncResult);
645                 }
646
647                 void CheckIfForceWrite ()
648                 {
649                         if (writeStream == null || contentLength < 0 || !InternalAllowBuffering)
650                                 return;
651
652                         // This will write the POST/PUT if the write stream already has the expected
653                         // amount of bytes in it (ContentLength) (bug #77753).
654                         if (writeStream.WriteBufferLength == contentLength)
655                                 writeStream.WriteRequest ();
656                 }
657
658                 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
659                 {
660                         bool send = (method == "PUT" || method == "POST");
661                         if (send) {
662                                 if ((!KeepAlive || (ContentLength == -1 && !SendChunked)) && !AllowWriteStreamBuffering)
663                                         throw new ProtocolViolationException ("Content-Length not set");
664                         }
665
666                         CommonChecks (send);
667                         Monitor.Enter (this);
668                         getResponseCalled = true;
669                         if (asyncRead != null && !haveResponse) {
670                                 Monitor.Exit (this);
671                                 throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
672                                                         "method while a previous call is still in progress.");
673                         }
674
675                         CheckIfForceWrite ();
676                         asyncRead = new WebAsyncResult (this, callback, state);
677                         initialMethod = method;
678                         if (haveResponse) {
679                                 if (webResponse != null) {
680                                         Exception saved = saved_exc;
681                                         Monitor.Exit (this);
682                                         if (saved == null) {
683                                                 asyncRead.SetCompleted (true, webResponse);
684                                         } else {
685                                                 asyncRead.SetCompleted (true, saved);
686                                         }
687                                         asyncRead.DoCallback ();
688                                         return asyncRead;
689                                 }
690                         }
691                         
692                         if (!requestSent) {
693                                 requestSent = true;
694                                 redirects = 0;
695                                 servicePoint = GetServicePoint ();
696                                 abortHandler = servicePoint.SendRequest (this, connectionGroup);
697                         }
698
699                         Monitor.Exit (this);
700                         return asyncRead;
701                 }
702                 
703                 public override WebResponse EndGetResponse (IAsyncResult asyncResult)
704                 {
705                         if (asyncResult == null)
706                                 throw new ArgumentNullException ("asyncResult");
707
708                         WebAsyncResult result = asyncResult as WebAsyncResult;
709                         if (result == null)
710                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
711
712                         if (!result.WaitUntilComplete (timeout, false)) {
713                                 Abort ();
714                                 throw new WebException("The request timed out", WebExceptionStatus.Timeout);
715                         }
716
717                         if (result.GotException)
718                                 throw result.Exception;
719
720                         return result.Response;
721                 }
722
723                 public override WebResponse GetResponse()
724                 {
725                         WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
726                         return EndGetResponse (result);
727                 }
728                 
729                 public override void Abort ()
730                 {
731                         haveResponse = true;
732                         aborted = true;
733                         if (asyncWrite != null) {
734                                 WebAsyncResult r = asyncWrite;
735                                 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); 
736                                 r.SetCompleted (false, wexc);
737                                 r.DoCallback ();
738                                 asyncWrite = null;
739                         }                       
740
741                         if (asyncRead != null) {
742                                 WebAsyncResult r = asyncRead;
743                                 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled); 
744                                 r.SetCompleted (false, wexc);
745                                 r.DoCallback ();
746                                 asyncRead = null;
747                         }                       
748
749                         if (abortHandler != null) {
750                                 try {
751                                         abortHandler (this, EventArgs.Empty);
752                                 } catch (Exception) {}
753                                 abortHandler = null;
754                         }
755
756                         if (writeStream != null) {
757                                 try {
758                                         writeStream.Close ();
759                                         writeStream = null;
760                                 } catch {}
761                         }
762
763                         if (webResponse != null) {
764                                 try {
765                                         webResponse.Close ();
766                                         webResponse = null;
767                                 } catch {}
768                         }
769                 }               
770                 
771                 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
772                                                   StreamingContext streamingContext)
773                 {
774                         SerializationInfo info = serializationInfo;
775
776                         info.AddValue ("requestUri", requestUri, typeof (Uri));
777                         info.AddValue ("actualUri", actualUri, typeof (Uri));
778                         info.AddValue ("allowAutoRedirect", allowAutoRedirect);
779                         info.AddValue ("allowBuffering", allowBuffering);
780                         info.AddValue ("certificates", certificates, typeof (X509CertificateCollection));
781                         info.AddValue ("connectionGroup", connectionGroup);
782                         info.AddValue ("contentLength", contentLength);
783                         info.AddValue ("webHeaders", webHeaders, typeof (WebHeaderCollection));
784                         info.AddValue ("keepAlive", keepAlive);
785                         info.AddValue ("maxAutoRedirect", maxAutoRedirect);
786                         info.AddValue ("mediaType", mediaType);
787                         info.AddValue ("method", method);
788                         info.AddValue ("initialMethod", initialMethod);
789                         info.AddValue ("pipelined", pipelined);
790                         info.AddValue ("version", version, typeof (Version));
791                         info.AddValue ("proxy", proxy, typeof (IWebProxy));
792                         info.AddValue ("sendChunked", sendChunked);
793                         info.AddValue ("timeout", timeout);
794                         info.AddValue ("redirects", redirects);
795                 }
796                 
797                 void CheckRequestStarted () 
798                 {
799                         if (requestSent)
800                                 throw new InvalidOperationException ("request started");
801                 }
802
803                 internal void DoContinueDelegate (int statusCode, WebHeaderCollection headers)
804                 {
805                         if (continueDelegate != null)
806                                 continueDelegate (statusCode, headers);
807                 }
808                 
809                 bool Redirect (WebAsyncResult result, HttpStatusCode code)
810                 {
811                         redirects++;
812                         Exception e = null;
813                         string uriString = null;
814
815                         switch (code) {
816                         case HttpStatusCode.Ambiguous: // 300
817                                 e = new WebException ("Ambiguous redirect.");
818                                 break;
819                         case HttpStatusCode.MovedPermanently: // 301
820                         case HttpStatusCode.Redirect: // 302
821                         case HttpStatusCode.TemporaryRedirect: // 307
822                                 /* MS follows the redirect for POST too
823                                 if (method != "GET" && method != "HEAD") // 10.3
824                                         return false;
825                                 */
826
827                                 uriString = webResponse.Headers ["Location"];
828                                 break;
829                         case HttpStatusCode.SeeOther: //303
830                                 method = "GET";
831                                 uriString = webResponse.Headers ["Location"];
832                                 break;
833                         case HttpStatusCode.NotModified: // 304
834                                 return false;
835                         case HttpStatusCode.UseProxy: // 305
836                                 e = new NotImplementedException ("Proxy support not available.");
837                                 break;
838                         case HttpStatusCode.Unused: // 306
839                         default:
840                                 e = new ProtocolViolationException ("Invalid status code: " + (int) code);
841                                 break;
842                         }
843
844                         if (e != null)
845                                 throw e;
846
847                         if (uriString == null)
848                                 throw new WebException ("No Location header found for " + (int) code,
849                                                         WebExceptionStatus.ProtocolError);
850
851                         Uri prev = actualUri;
852                         try {
853                                 actualUri = new Uri (actualUri, uriString);
854                         } catch (Exception) {
855                                 throw new WebException (String.Format ("Invalid URL ({0}) for {1}",
856                                                                         uriString, (int) code),
857                                                                         WebExceptionStatus.ProtocolError);
858                         }
859
860                         hostChanged = (actualUri.Scheme != prev.Scheme || actualUri.Host != prev.Host ||
861                                         actualUri.Port != prev.Port);
862                         return true;
863                 }
864
865                 string GetHeaders ()
866                 {
867                         bool continue100 = false;
868                         if (contentLength != -1) {
869                                 if (contentLength > 0)
870                                         continue100 = true;
871                                 webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
872                                 webHeaders.RemoveInternal ("Transfer-Encoding");
873                         } else if (sendChunked) {
874                                 continue100 = true;
875                                 webHeaders.RemoveAndAdd ("Transfer-Encoding", "chunked");
876                                 webHeaders.RemoveInternal ("Content-Length");
877                         }
878
879                         if (actualVersion == HttpVersion.Version11 && continue100 &&
880                             servicePoint.SendContinue) { // RFC2616 8.2.3
881                                 webHeaders.RemoveAndAdd ("Expect" , "100-continue");
882                                 expectContinue = true;
883                         } else {
884                                 webHeaders.RemoveInternal ("Expect");
885                                 expectContinue = false;
886                         }
887
888                         string connectionHeader = (ProxyQuery) ? "Proxy-Connection" : "Connection";
889                         webHeaders.RemoveInternal ((!ProxyQuery) ? "Proxy-Connection" : "Connection");
890                         bool spoint10 = (servicePoint.ProtocolVersion == null ||
891                                          servicePoint.ProtocolVersion == HttpVersion.Version10);
892
893                         if (keepAlive && (version == HttpVersion.Version10 || spoint10)) {
894                                 webHeaders.RemoveAndAdd (connectionHeader, "keep-alive");
895                         } else if (!keepAlive && version == HttpVersion.Version11) {
896                                 webHeaders.RemoveAndAdd (connectionHeader, "close");
897                         }
898
899                         webHeaders.SetInternal ("Host", actualUri.Authority);
900                         if (cookieContainer != null) {
901                                 string cookieHeader = cookieContainer.GetCookieHeader (requestUri);
902                                 if (cookieHeader != "")
903                                         webHeaders.SetInternal ("Cookie", cookieHeader);
904                         }
905
906                         if (!usedPreAuth && preAuthenticate)
907                                 DoPreAuthenticate ();
908
909                         return webHeaders.ToString ();
910                 }
911
912                 void DoPreAuthenticate ()
913                 {
914                         webHeaders.RemoveInternal ("Proxy-Authorization");
915                         webHeaders.RemoveInternal ("Authorization");
916                         bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
917                         ICredentials creds = (!isProxy || credentials != null) ? credentials : proxy.Credentials;
918                         Authorization auth = AuthenticationManager.PreAuthenticate (this, creds);
919                         if (auth == null)
920                                 return;
921
922                         string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization";
923                         webHeaders [authHeader] = auth.Message;
924                         usedPreAuth = true;
925                 }
926                 
927                 internal void SetWriteStreamError (WebExceptionStatus status)
928                 {
929                         if (aborted)
930                                 return;
931
932                         WebAsyncResult r = asyncWrite;
933                         if (r == null)
934                                 r = asyncRead;
935
936                         if (r != null) {
937                                 r.SetCompleted (false, new WebException ("Error: " + status, status));
938                                 r.DoCallback ();
939                         }
940                 }
941
942                 internal void SendRequestHeaders ()
943                 {
944                         StringBuilder req = new StringBuilder ();
945                         string query;
946                         if (!ProxyQuery) {
947                                 query = actualUri.PathAndQuery;
948                         } else if (actualUri.IsDefaultPort) {
949                                 query = String.Format ("{0}://{1}{2}",  actualUri.Scheme,
950                                                                         actualUri.Host,
951                                                                         actualUri.PathAndQuery);
952                         } else {
953                                 query = String.Format ("{0}://{1}:{2}{3}", actualUri.Scheme,
954                                                                            actualUri.Host,
955                                                                            actualUri.Port,
956                                                                            actualUri.PathAndQuery);
957                         }
958                         
959                         if (servicePoint.ProtocolVersion != null && servicePoint.ProtocolVersion < version) {
960                                 actualVersion = servicePoint.ProtocolVersion;
961                         } else {
962                                 actualVersion = version;
963                         }
964
965                         req.AppendFormat ("{0} {1} HTTP/{2}.{3}\r\n", method, query,
966                                                                 actualVersion.Major, actualVersion.Minor);
967                         req.Append (GetHeaders ());
968                         string reqstr = req.ToString ();
969                         byte [] bytes = Encoding.UTF8.GetBytes (reqstr);
970                         writeStream.SetHeaders (bytes, 0, bytes.Length);
971                 }
972
973                 internal void SetWriteStream (WebConnectionStream stream)
974                 {
975                         if (aborted)
976                                 return;
977                         
978                         writeStream = stream;
979                         if (bodyBuffer != null) {
980                                 webHeaders.RemoveInternal ("Transfer-Encoding");
981                                 contentLength = bodyBufferLength;
982                                 writeStream.SendChunked = false;
983                         }
984
985                         SendRequestHeaders ();
986
987                         haveRequest = true;
988                         
989                         if (bodyBuffer != null) {
990                                 // The body has been written and buffered. The request "user"
991                                 // won't write it again, so we must do it.
992                                 writeStream.Write (bodyBuffer, 0, bodyBufferLength);
993                                 bodyBuffer = null;
994                                 writeStream.Close ();
995                         } else if (method == "PUT" || method == "POST") {
996                                 if (getResponseCalled && !writeStream.RequestWritten)
997                                         writeStream.WriteRequest ();
998                         }
999
1000                         if (asyncWrite != null) {
1001                                 asyncWrite.SetCompleted (false, stream);
1002                                 asyncWrite.DoCallback ();
1003                                 asyncWrite = null;
1004                         }
1005                 }
1006
1007                 internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
1008                 {
1009                         if (aborted)
1010                                 return;
1011                         string msg = String.Format ("Error getting response stream ({0}): {1}", where, status);
1012                         WebAsyncResult r = asyncRead;
1013                         if (r == null)
1014                                 r = asyncWrite;
1015
1016                         if (r != null) {
1017                                 WebException wexc;
1018                                 if (e is WebException) {
1019                                         wexc = (WebException) e;
1020                                 } else {
1021                                         wexc = new WebException (msg, e, status, null); 
1022                                 }
1023                                 r.SetCompleted (false, wexc);
1024                                 r.DoCallback ();
1025                                 asyncRead = null;
1026                                 asyncWrite = null;
1027                         }
1028                 }
1029
1030                 void CheckSendError (WebConnectionData data)
1031                 {
1032                         // Got here, but no one called GetResponse
1033                         int status = data.StatusCode;
1034                         if (status < 400 || status == 401 || status == 407)
1035                                 return;
1036
1037                         if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) {
1038                                 // The request has not been completely sent and we got here!
1039                                 // We should probably just close and cause an error in any case,
1040                                 saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse); 
1041                                 webResponse.ReadAll ();
1042                         }
1043                 }
1044
1045                 internal void SetResponseData (WebConnectionData data)
1046                 {
1047                         if (aborted) {
1048                                 if (data.stream != null)
1049                                         data.stream.Close ();
1050                                 return;
1051                         }
1052
1053                         WebException wexc = null;
1054                         try {
1055                                 webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer);
1056                                 haveResponse = true;
1057                         } catch (Exception e) {
1058                                 wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null); 
1059                                 if (data.stream != null)
1060                                         data.stream.Close ();
1061                         }
1062
1063                         if (wexc == null && (method == "POST" || method == "PUT")) {
1064                                 lock (locker) {
1065                                         CheckSendError (data);
1066                                         if (saved_exc != null)
1067                                                 wexc = (WebException) saved_exc;
1068                                 }
1069                         }
1070
1071                         WebAsyncResult r = asyncRead;
1072                         if (r != null) {
1073                                 if (wexc != null) {
1074                                         r.SetCompleted (false, wexc);
1075                                         r.DoCallback ();
1076                                         return;
1077                                 }
1078
1079                                 bool redirected;
1080                                 try {
1081                                         redirected = CheckFinalStatus (r);
1082                                         if (!redirected) {
1083                                                 r.SetCompleted (false, webResponse);
1084                                                 r.DoCallback ();
1085                                         } else {
1086                                                 if (webResponse != null) {
1087                                                         webResponse.Close ();
1088                                                         webResponse = null;
1089                                                 }
1090                                                 haveResponse = false;
1091                                                 webResponse = null;
1092                                                 r.Reset ();
1093                                                 servicePoint = GetServicePoint ();
1094                                                 abortHandler = servicePoint.SendRequest (this, connectionGroup);
1095                                         }
1096                                 } catch (WebException wexc2) {
1097                                         r.SetCompleted (false, wexc2);
1098                                         r.DoCallback ();
1099                                         return;
1100                                 } catch (Exception ex) {
1101                                         wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null); 
1102                                         r.SetCompleted (false, wexc);
1103                                         r.DoCallback ();
1104                                         return;
1105                                 }
1106                         }
1107                 }
1108
1109                 bool CheckAuthorization (WebResponse response, HttpStatusCode code)
1110                 {
1111                         authCompleted = false;
1112                         if (code == HttpStatusCode.Unauthorized && credentials == null)
1113                                 return false;
1114
1115                         bool isProxy = (code == HttpStatusCode.ProxyAuthenticationRequired);
1116                         if (isProxy && (proxy == null || proxy.Credentials == null))
1117                                 return false;
1118
1119                         string authHeader = response.Headers [(isProxy) ? "Proxy-Authenticate" : "WWW-Authenticate"];
1120                         if (authHeader == null)
1121                                 return false;
1122
1123                         ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
1124                         Authorization auth = AuthenticationManager.Authenticate (authHeader, this, creds);
1125                         if (auth == null)
1126                                 return false;
1127
1128                         webHeaders [(isProxy) ? "Proxy-Authorization" : "Authorization"] = auth.Message;
1129                         authCompleted = auth.Complete;
1130                         return true;
1131                 }
1132
1133                 // Returns true if redirected
1134                 bool CheckFinalStatus (WebAsyncResult result)
1135                 {
1136                         if (result.GotException)
1137                                 throw result.Exception;
1138
1139                         Exception throwMe = result.Exception;
1140                         bodyBuffer = null;
1141
1142                         HttpWebResponse resp = result.Response;
1143                         WebExceptionStatus protoError = WebExceptionStatus.ProtocolError;
1144                         HttpStatusCode code = 0;
1145                         if (throwMe == null && webResponse != null) {
1146                                 code = webResponse.StatusCode;
1147                                 if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
1148                                      (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
1149                                         if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
1150                                                 // Keep the written body, so it can be rewritten in the retry
1151                                                 if (InternalAllowBuffering) {
1152                                                         bodyBuffer = writeStream.WriteBuffer;
1153                                                         bodyBufferLength = writeStream.WriteBufferLength;
1154                                                         webResponse.Close ();
1155                                                         return true;
1156                                                 } else if (method != "PUT" && method != "POST") {
1157                                                         webResponse.Close ();
1158                                                         return true;
1159                                                 }
1160                                                 
1161                                                 writeStream.InternalClose ();
1162                                                 writeStream = null;
1163                                                 webResponse.Close ();
1164                                                 webResponse = null;
1165
1166                                                 throw new WebException ("This request requires buffering " +
1167                                                                         "of data for authentication or " +
1168                                                                         "redirection to be sucessful.");
1169                                         }
1170                                 }
1171
1172                                 if ((int) code >= 400) {
1173                                         string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1174                                                                     (int) code, webResponse.StatusDescription);
1175                                         throwMe = new WebException (err, null, protoError, webResponse);
1176                                         webResponse.ReadAll ();
1177                                 } else if ((int) code == 304 && allowAutoRedirect) {
1178                                         string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1179                                                                     (int) code, webResponse.StatusDescription);
1180                                         throwMe = new WebException (err, null, protoError, webResponse);
1181                                 } else if ((int) code >= 300 && allowAutoRedirect && redirects > maxAutoRedirect) {
1182                                         throwMe = new WebException ("Max. redirections exceeded.", null,
1183                                                                     protoError, webResponse);
1184                                         webResponse.ReadAll ();
1185                                 }
1186                         }
1187
1188                         if (throwMe == null) {
1189                                 bool b = false;
1190                                 int c = (int) code;
1191                                 if (allowAutoRedirect && c >= 300) {
1192                                         if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
1193                                                 bodyBuffer = writeStream.WriteBuffer;
1194                                                 bodyBufferLength = writeStream.WriteBufferLength;
1195                                         }
1196                                         b = Redirect (result, code);
1197                                 }
1198
1199                                 if (resp != null && c >= 300 && c != 304)
1200                                         resp.ReadAll ();
1201
1202                                 return b;
1203                         }
1204
1205                         if (writeStream != null) {
1206                                 writeStream.InternalClose ();
1207                                 writeStream = null;
1208                         }
1209
1210                         webResponse = null;
1211
1212                         throw throwMe;
1213                 }
1214         }
1215 }
1216