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