2 // System.Net.HttpWebRequest
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // (c) 2002 Lawrence Pit
9 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
10 // (c) 2004 Novell, Inc. (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
35 using System.Collections;
36 using System.Configuration;
37 using System.Globalization;
40 using System.Net.Cache;
41 using System.Net.Sockets;
42 using System.Runtime.Remoting.Messaging;
43 using System.Runtime.Serialization;
44 using System.Security.Cryptography.X509Certificates;
46 using System.Threading;
51 public class HttpWebRequest : WebRequest, ISerializable {
55 bool allowAutoRedirect = true;
56 bool allowBuffering = true;
57 X509CertificateCollection certificates;
58 string connectionGroup;
59 long contentLength = -1;
60 HttpContinueDelegate continueDelegate;
61 CookieContainer cookieContainer;
62 ICredentials credentials;
66 WebHeaderCollection webHeaders;
67 bool keepAlive = true;
68 int maxAutoRedirect = 50;
69 string mediaType = String.Empty;
70 string method = "GET";
71 string initialMethod = "GET";
72 bool pipelined = true;
75 Version version = HttpVersion.Version11;
77 Version actualVersion;
80 ServicePoint servicePoint;
83 WebConnectionStream writeStream;
84 HttpWebResponse webResponse;
85 WebAsyncResult asyncWrite;
86 WebAsyncResult asyncRead;
87 EventHandler abortHandler;
89 bool gotRequestStream;
95 bool getResponseCalled;
97 object locker = new object ();
98 bool finished_reading;
99 internal WebConnection WebConnection;
100 DecompressionMethods auto_decomp;
101 int maxResponseHeadersLength;
102 static int defaultMaxResponseHeadersLength;
103 int readWriteTimeout = 300000; // ms
110 NtlmAuthState ntlm_auth_state;
114 static HttpWebRequest ()
116 defaultMaxResponseHeadersLength = 64 * 1024;
118 NetConfig config = ConfigurationSettings.GetConfig ("system.net/settings") as NetConfig;
119 if (config != null) {
120 int x = config.MaxResponseHeadersLength;
124 defaultMaxResponseHeadersLength = x;
134 HttpWebRequest (Uri uri)
136 this.requestUri = uri;
137 this.actualUri = uri;
138 this.proxy = GlobalProxySelection.Select;
139 this.webHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
143 [Obsolete ("Serialization is obsoleted for this type", false)]
144 protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext)
146 SerializationInfo info = serializationInfo;
148 requestUri = (Uri) info.GetValue ("requestUri", typeof (Uri));
149 actualUri = (Uri) info.GetValue ("actualUri", typeof (Uri));
150 allowAutoRedirect = info.GetBoolean ("allowAutoRedirect");
151 allowBuffering = info.GetBoolean ("allowBuffering");
152 certificates = (X509CertificateCollection) info.GetValue ("certificates", typeof (X509CertificateCollection));
153 connectionGroup = info.GetString ("connectionGroup");
154 contentLength = info.GetInt64 ("contentLength");
155 webHeaders = (WebHeaderCollection) info.GetValue ("webHeaders", typeof (WebHeaderCollection));
156 keepAlive = info.GetBoolean ("keepAlive");
157 maxAutoRedirect = info.GetInt32 ("maxAutoRedirect");
158 mediaType = info.GetString ("mediaType");
159 method = info.GetString ("method");
160 initialMethod = info.GetString ("initialMethod");
161 pipelined = info.GetBoolean ("pipelined");
162 version = (Version) info.GetValue ("version", typeof (Version));
163 proxy = (IWebProxy) info.GetValue ("proxy", typeof (IWebProxy));
164 sendChunked = info.GetBoolean ("sendChunked");
165 timeout = info.GetInt32 ("timeout");
166 redirects = info.GetInt32 ("redirects");
167 host = info.GetString ("host");
172 public string Accept {
173 get { return webHeaders ["Accept"]; }
175 CheckRequestStarted ();
176 webHeaders.RemoveAndAdd ("Accept", value);
181 get { return actualUri; }
182 internal set { actualUri = value; } // Used by Ftp+proxy
185 public bool AllowAutoRedirect {
186 get { return allowAutoRedirect; }
187 set { this.allowAutoRedirect = value; }
190 public bool AllowWriteStreamBuffering {
191 get { return allowBuffering; }
192 set { allowBuffering = value; }
196 public virtual bool AllowReadStreamBuffering {
197 get { return allowBuffering; }
198 set { allowBuffering = value; }
202 static Exception GetMustImplement ()
204 return new NotImplementedException ();
207 public DecompressionMethods AutomaticDecompression
213 CheckRequestStarted ();
218 internal bool InternalAllowBuffering {
220 return (allowBuffering && (method != "HEAD" && method != "GET" &&
221 method != "MKCOL" && method != "CONNECT" &&
226 public X509CertificateCollection ClientCertificates {
228 if (certificates == null)
229 certificates = new X509CertificateCollection ();
235 throw GetMustImplement ();
239 public string Connection {
240 get { return webHeaders ["Connection"]; }
242 CheckRequestStarted ();
244 if (string.IsNullOrEmpty (value)) {
245 webHeaders.RemoveInternal ("Connection");
249 string val = value.ToLowerInvariant ();
250 if (val.Contains ("keep-alive") || val.Contains ("close"))
251 throw new ArgumentException ("Keep-Alive and Close may not be set with this property");
254 value = value + ", Keep-Alive";
256 webHeaders.RemoveAndAdd ("Connection", value);
260 public override string ConnectionGroupName {
261 get { return connectionGroup; }
262 set { connectionGroup = value; }
265 public override long ContentLength {
266 get { return contentLength; }
268 CheckRequestStarted ();
270 throw new ArgumentOutOfRangeException ("value", "Content-Length must be >= 0");
272 contentLength = value;
276 internal long InternalContentLength {
277 set { contentLength = value; }
280 internal bool ThrowOnError { get; set; }
282 public override string ContentType {
283 get { return webHeaders ["Content-Type"]; }
285 if (value == null || value.Trim().Length == 0) {
286 webHeaders.RemoveInternal ("Content-Type");
289 webHeaders.RemoveAndAdd ("Content-Type", value);
293 public HttpContinueDelegate ContinueDelegate {
294 get { return continueDelegate; }
295 set { continueDelegate = value; }
301 public CookieContainer CookieContainer {
302 get { return cookieContainer; }
303 set { cookieContainer = value; }
306 public override ICredentials Credentials {
307 get { return credentials; }
308 set { credentials = value; }
311 public DateTime Date {
313 string date = webHeaders ["Date"];
315 return DateTime.MinValue;
316 return DateTime.ParseExact (date, "r", CultureInfo.InvariantCulture).ToLocalTime ();
319 if (value.Equals (DateTime.MinValue))
320 webHeaders.RemoveInternal ("Date");
322 webHeaders.RemoveAndAdd ("Date", value.ToUniversalTime ().ToString ("r", CultureInfo.InvariantCulture));
329 public static new RequestCachePolicy DefaultCachePolicy
332 throw GetMustImplement ();
335 throw GetMustImplement ();
341 public static int DefaultMaximumErrorResponseLength
344 throw GetMustImplement ();
347 throw GetMustImplement ();
351 public string Expect {
352 get { return webHeaders ["Expect"]; }
354 CheckRequestStarted ();
357 val = val.Trim ().ToLower ();
359 if (val == null || val.Length == 0) {
360 webHeaders.RemoveInternal ("Expect");
364 if (val == "100-continue")
365 throw new ArgumentException ("100-Continue cannot be set with this property.",
367 webHeaders.RemoveAndAdd ("Expect", value);
374 public bool HaveResponse {
375 get { return haveResponse; }
378 public override WebHeaderCollection Headers {
379 get { return webHeaders; }
381 CheckRequestStarted ();
382 WebHeaderCollection newHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
383 int count = value.Count;
384 for (int i = 0; i < count; i++)
385 newHeaders.Add (value.GetKey (i), value.Get (i));
387 webHeaders = newHeaders;
399 return actualUri.Authority;
404 throw new ArgumentNullException ("value");
406 if (!CheckValidHost (actualUri.Scheme, value))
407 throw new ArgumentException ("Invalid host: " + value);
413 static bool CheckValidHost (string scheme, string val)
421 int idx = val.IndexOf ('/');
426 if (IPAddress.TryParse (val, out ipaddr))
429 string u = scheme + "://" + val + "/";
430 return Uri.IsWellFormedUriString (u, UriKind.Absolute);
433 public DateTime IfModifiedSince {
435 string str = webHeaders ["If-Modified-Since"];
439 return MonoHttpDate.Parse (str);
440 } catch (Exception) {
445 CheckRequestStarted ();
447 webHeaders.SetInternal ("If-Modified-Since",
448 value.ToUniversalTime ().ToString ("r", null));
449 // TODO: check last param when using different locale
453 public bool KeepAlive {
462 public int MaximumAutomaticRedirections {
463 get { return maxAutoRedirect; }
466 throw new ArgumentException ("Must be > 0", "value");
468 maxAutoRedirect = value;
472 [MonoTODO ("Use this")]
473 public int MaximumResponseHeadersLength {
474 get { return maxResponseHeadersLength; }
475 set { maxResponseHeadersLength = value; }
478 [MonoTODO ("Use this")]
479 public static int DefaultMaximumResponseHeadersLength {
480 get { return defaultMaxResponseHeadersLength; }
481 set { defaultMaxResponseHeadersLength = value; }
484 public int ReadWriteTimeout {
485 get { return readWriteTimeout; }
488 throw new InvalidOperationException ("The request has already been sent.");
491 throw new ArgumentOutOfRangeException ("value", "Must be >= -1");
493 readWriteTimeout = value;
499 public int ContinueTimeout {
500 get { throw new NotImplementedException (); }
501 set { throw new NotImplementedException (); }
505 public string MediaType {
506 get { return mediaType; }
512 public override string Method {
513 get { return this.method; }
515 if (value == null || value.Trim () == "")
516 throw new ArgumentException ("not a valid method");
518 method = value.ToUpperInvariant ();
519 if (method != "HEAD" && method != "GET" && method != "POST" && method != "PUT" &&
520 method != "DELETE" && method != "CONNECT" && method != "TRACE" &&
527 public bool Pipelined {
528 get { return pipelined; }
529 set { pipelined = value; }
532 public override bool PreAuthenticate {
533 get { return preAuthenticate; }
534 set { preAuthenticate = value; }
537 public Version ProtocolVersion {
538 get { return version; }
540 if (value != HttpVersion.Version10 && value != HttpVersion.Version11)
541 throw new ArgumentException ("value");
543 force_version = true;
548 public override IWebProxy Proxy {
549 get { return proxy; }
551 CheckRequestStarted ();
553 servicePoint = null; // we may need a new one
557 public string Referer {
558 get { return webHeaders ["Referer"]; }
560 CheckRequestStarted ();
561 if (value == null || value.Trim().Length == 0) {
562 webHeaders.RemoveInternal ("Referer");
565 webHeaders.SetInternal ("Referer", value);
569 public override Uri RequestUri {
570 get { return requestUri; }
573 public bool SendChunked {
574 get { return sendChunked; }
576 CheckRequestStarted ();
581 public ServicePoint ServicePoint {
582 get { return GetServicePoint (); }
585 internal ServicePoint ServicePointNoLock {
586 get { return servicePoint; }
589 [MonoTODO ("for portable library support")]
590 public virtual bool SupportsCookieContainer {
592 throw new NotImplementedException ();
596 public override int Timeout {
597 get { return timeout; }
600 throw new ArgumentOutOfRangeException ("value");
606 public string TransferEncoding {
607 get { return webHeaders ["Transfer-Encoding"]; }
609 CheckRequestStarted ();
612 val = val.Trim ().ToLower ();
614 if (val == null || val.Length == 0) {
615 webHeaders.RemoveInternal ("Transfer-Encoding");
619 if (val == "chunked")
620 throw new ArgumentException ("Chunked encoding must be set with the SendChunked property");
623 throw new ArgumentException ("SendChunked must be True", "value");
625 webHeaders.RemoveAndAdd ("Transfer-Encoding", value);
629 public override bool UseDefaultCredentials
631 get { return CredentialCache.DefaultCredentials == Credentials; }
632 set { Credentials = value ? CredentialCache.DefaultCredentials : null; }
635 public string UserAgent {
636 get { return webHeaders ["User-Agent"]; }
637 set { webHeaders.SetInternal ("User-Agent", value); }
640 bool unsafe_auth_blah;
641 public bool UnsafeAuthenticatedConnectionSharing
643 get { return unsafe_auth_blah; }
644 set { unsafe_auth_blah = value; }
647 internal bool GotRequestStream {
648 get { return gotRequestStream; }
651 internal bool ExpectContinue {
652 get { return expectContinue; }
653 set { expectContinue = value; }
656 internal Uri AuthUri {
657 get { return actualUri; }
660 internal bool ProxyQuery {
661 get { return servicePoint.UsesProxy && !servicePoint.UseConnect; }
666 internal ServicePoint GetServicePoint ()
669 if (hostChanged || servicePoint == null) {
670 servicePoint = ServicePointManager.FindServicePoint (actualUri, proxy);
678 public void AddRange (int range)
680 AddRange ("bytes", (long) range);
683 public void AddRange (int from, int to)
685 AddRange ("bytes", (long) from, (long) to);
688 public void AddRange (string rangeSpecifier, int range)
690 AddRange (rangeSpecifier, (long) range);
693 public void AddRange (string rangeSpecifier, int from, int to)
695 AddRange (rangeSpecifier, (long) from, (long) to);
702 void AddRange (long range)
704 AddRange ("bytes", (long) range);
712 void AddRange (long from, long to)
714 AddRange ("bytes", from, to);
722 void AddRange (string rangeSpecifier, long range)
724 if (rangeSpecifier == null)
725 throw new ArgumentNullException ("rangeSpecifier");
726 if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
727 throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
729 string r = webHeaders ["Range"];
731 r = rangeSpecifier + "=";
733 string old_specifier = r.Substring (0, r.IndexOf ('='));
734 if (String.Compare (old_specifier, rangeSpecifier, StringComparison.OrdinalIgnoreCase) != 0)
735 throw new InvalidOperationException ("A different range specifier is already in use");
739 string n = range.ToString (CultureInfo.InvariantCulture);
744 webHeaders.RemoveAndAdd ("Range", r);
752 void AddRange (string rangeSpecifier, long from, long to)
754 if (rangeSpecifier == null)
755 throw new ArgumentNullException ("rangeSpecifier");
756 if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
757 throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
758 if (from > to || from < 0)
759 throw new ArgumentOutOfRangeException ("from");
761 throw new ArgumentOutOfRangeException ("to");
763 string r = webHeaders ["Range"];
765 r = rangeSpecifier + "=";
769 r = String.Format ("{0}{1}-{2}", r, from, to);
770 webHeaders.RemoveAndAdd ("Range", r);
774 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state)
777 throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled);
779 bool send = !(method == "GET" || method == "CONNECT" || method == "HEAD" ||
781 if (method == null || !send)
782 throw new ProtocolViolationException ("Cannot send data when method is: " + method);
784 if (contentLength == -1 && !sendChunked && !allowBuffering && KeepAlive)
785 throw new ProtocolViolationException ("Content-Length not set");
787 string transferEncoding = TransferEncoding;
788 if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "")
789 throw new ProtocolViolationException ("SendChunked should be true.");
793 if (getResponseCalled)
794 throw new InvalidOperationException ("The operation cannot be performed once the request has been submitted.");
796 if (asyncWrite != null) {
797 throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
798 "method while a previous call is still in progress.");
801 asyncWrite = new WebAsyncResult (this, callback, state);
802 initialMethod = method;
804 if (writeStream != null) {
805 asyncWrite.SetCompleted (true, writeStream);
806 asyncWrite.DoCallback ();
811 gotRequestStream = true;
812 WebAsyncResult result = asyncWrite;
816 servicePoint = GetServicePoint ();
817 abortHandler = servicePoint.SendRequest (this, connectionGroup);
823 public override Stream EndGetRequestStream (IAsyncResult asyncResult)
825 if (asyncResult == null)
826 throw new ArgumentNullException ("asyncResult");
828 WebAsyncResult result = asyncResult as WebAsyncResult;
830 throw new ArgumentException ("Invalid IAsyncResult");
833 result.WaitUntilComplete ();
835 Exception e = result.Exception;
839 return result.WriteStream;
842 public override Stream GetRequestStream()
844 IAsyncResult asyncResult = asyncWrite;
845 if (asyncResult == null) {
846 asyncResult = BeginGetRequestStream (null, null);
847 asyncWrite = (WebAsyncResult) asyncResult;
850 if (!asyncResult.IsCompleted && !asyncResult.AsyncWaitHandle.WaitOne (timeout, false)) {
852 throw new WebException ("The request timed out", WebExceptionStatus.Timeout);
855 return EndGetRequestStream (asyncResult);
858 void CheckIfForceWrite ()
860 if (writeStream == null || writeStream.RequestWritten || !InternalAllowBuffering)
863 if (contentLength < 0 && writeStream.CanWrite == true && writeStream.WriteBufferLength < 0)
866 if (contentLength < 0 && writeStream.WriteBufferLength >= 0)
867 InternalContentLength = writeStream.WriteBufferLength;
869 if (contentLength < 0 && writeStream.CanWrite == true)
873 // This will write the POST/PUT if the write stream already has the expected
874 // amount of bytes in it (ContentLength) (bug #77753) or if the write stream
875 // contains data and it has been closed already (xamarin bug #1512).
877 if (writeStream.WriteBufferLength == contentLength || (contentLength == -1 && writeStream.CanWrite == false))
878 writeStream.WriteRequest ();
881 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
884 throw new WebException ("The request was canceled.", WebExceptionStatus.RequestCanceled);
887 throw new ProtocolViolationException ("Method is null.");
889 string transferEncoding = TransferEncoding;
890 if (!sendChunked && transferEncoding != null && transferEncoding.Trim () != "")
891 throw new ProtocolViolationException ("SendChunked should be true.");
893 Monitor.Enter (locker);
894 getResponseCalled = true;
895 if (asyncRead != null && !haveResponse) {
896 Monitor.Exit (locker);
897 throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
898 "method while a previous call is still in progress.");
901 CheckIfForceWrite ();
902 asyncRead = new WebAsyncResult (this, callback, state);
903 WebAsyncResult aread = asyncRead;
904 initialMethod = method;
906 Exception saved = saved_exc;
907 if (webResponse != null) {
908 Monitor.Exit (locker);
910 aread.SetCompleted (true, webResponse);
912 aread.SetCompleted (true, saved);
916 } else if (saved != null) {
917 Monitor.Exit (locker);
918 aread.SetCompleted (true, saved);
927 servicePoint = GetServicePoint ();
928 abortHandler = servicePoint.SendRequest (this, connectionGroup);
931 Monitor.Exit (locker);
935 public override WebResponse EndGetResponse (IAsyncResult asyncResult)
937 if (asyncResult == null)
938 throw new ArgumentNullException ("asyncResult");
940 WebAsyncResult result = asyncResult as WebAsyncResult;
942 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
944 if (!result.WaitUntilComplete (timeout, false)) {
946 throw new WebException("The request timed out", WebExceptionStatus.Timeout);
949 if (result.GotException)
950 throw result.Exception;
952 return result.Response;
956 public Stream EndGetRequestStream (IAsyncResult asyncResult, out TransportContext transportContext)
958 transportContext = null;
959 return EndGetRequestStream (asyncResult);
963 public override WebResponse GetResponse()
965 WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
966 return EndGetResponse (result);
969 internal bool FinishedReading {
970 get { return finished_reading; }
971 set { finished_reading = value; }
974 internal bool Aborted {
975 get { return Interlocked.CompareExchange (ref aborted, 0, 0) == 1; }
978 public override void Abort ()
980 if (Interlocked.CompareExchange (ref aborted, 1, 0) == 1)
983 if (haveResponse && finished_reading)
987 if (abortHandler != null) {
989 abortHandler (this, EventArgs.Empty);
990 } catch (Exception) {}
994 if (asyncWrite != null) {
995 WebAsyncResult r = asyncWrite;
996 if (!r.IsCompleted) {
998 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled);
999 r.SetCompleted (false, wexc);
1006 if (asyncRead != null) {
1007 WebAsyncResult r = asyncRead;
1008 if (!r.IsCompleted) {
1010 WebException wexc = new WebException ("Aborted.", WebExceptionStatus.RequestCanceled);
1011 r.SetCompleted (false, wexc);
1018 if (writeStream != null) {
1020 writeStream.Close ();
1025 if (webResponse != null) {
1027 webResponse.Close ();
1033 void ISerializable.GetObjectData (SerializationInfo serializationInfo,
1034 StreamingContext streamingContext)
1036 GetObjectData (serializationInfo, streamingContext);
1039 protected override void GetObjectData (SerializationInfo serializationInfo,
1040 StreamingContext streamingContext)
1042 SerializationInfo info = serializationInfo;
1044 info.AddValue ("requestUri", requestUri, typeof (Uri));
1045 info.AddValue ("actualUri", actualUri, typeof (Uri));
1046 info.AddValue ("allowAutoRedirect", allowAutoRedirect);
1047 info.AddValue ("allowBuffering", allowBuffering);
1048 info.AddValue ("certificates", certificates, typeof (X509CertificateCollection));
1049 info.AddValue ("connectionGroup", connectionGroup);
1050 info.AddValue ("contentLength", contentLength);
1051 info.AddValue ("webHeaders", webHeaders, typeof (WebHeaderCollection));
1052 info.AddValue ("keepAlive", keepAlive);
1053 info.AddValue ("maxAutoRedirect", maxAutoRedirect);
1054 info.AddValue ("mediaType", mediaType);
1055 info.AddValue ("method", method);
1056 info.AddValue ("initialMethod", initialMethod);
1057 info.AddValue ("pipelined", pipelined);
1058 info.AddValue ("version", version, typeof (Version));
1059 info.AddValue ("proxy", proxy, typeof (IWebProxy));
1060 info.AddValue ("sendChunked", sendChunked);
1061 info.AddValue ("timeout", timeout);
1062 info.AddValue ("redirects", redirects);
1063 info.AddValue ("host", host);
1066 void CheckRequestStarted ()
1069 throw new InvalidOperationException ("request started");
1072 internal void DoContinueDelegate (int statusCode, WebHeaderCollection headers)
1074 if (continueDelegate != null)
1075 continueDelegate (statusCode, headers);
1078 bool Redirect (WebAsyncResult result, HttpStatusCode code)
1082 string uriString = null;
1084 case HttpStatusCode.Ambiguous: // 300
1085 e = new WebException ("Ambiguous redirect.");
1087 case HttpStatusCode.MovedPermanently: // 301
1088 case HttpStatusCode.Redirect: // 302
1089 if (method == "POST")
1092 case HttpStatusCode.TemporaryRedirect: // 307
1094 case HttpStatusCode.SeeOther: //303
1097 case HttpStatusCode.NotModified: // 304
1099 case HttpStatusCode.UseProxy: // 305
1100 e = new NotImplementedException ("Proxy support not available.");
1102 case HttpStatusCode.Unused: // 306
1104 e = new ProtocolViolationException ("Invalid status code: " + (int) code);
1111 //contentLength = -1;
1112 //bodyBufferLength = 0;
1113 //bodyBuffer = null;
1114 uriString = webResponse.Headers ["Location"];
1116 if (uriString == null)
1117 throw new WebException ("No Location header found for " + (int) code,
1118 WebExceptionStatus.ProtocolError);
1120 Uri prev = actualUri;
1122 actualUri = new Uri (actualUri, uriString);
1123 } catch (Exception) {
1124 throw new WebException (String.Format ("Invalid URL ({0}) for {1}",
1125 uriString, (int) code),
1126 WebExceptionStatus.ProtocolError);
1129 hostChanged = (actualUri.Scheme != prev.Scheme || Host != prev.Authority);
1133 string GetHeaders ()
1135 bool continue100 = false;
1138 webHeaders.RemoveAndAdd ("Transfer-Encoding", "chunked");
1139 webHeaders.RemoveInternal ("Content-Length");
1140 } else if (contentLength != -1) {
1141 if (ntlm_auth_state != NtlmAuthState.Challenge) {
1142 if (contentLength > 0)
1145 webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
1147 webHeaders.SetInternal ("Content-Length", "0");
1149 webHeaders.RemoveInternal ("Transfer-Encoding");
1151 webHeaders.RemoveInternal ("Content-Length");
1154 if (actualVersion == HttpVersion.Version11 && continue100 &&
1155 servicePoint.SendContinue) { // RFC2616 8.2.3
1156 webHeaders.RemoveAndAdd ("Expect" , "100-continue");
1157 expectContinue = true;
1159 webHeaders.RemoveInternal ("Expect");
1160 expectContinue = false;
1163 bool proxy_query = ProxyQuery;
1164 string connectionHeader = (proxy_query) ? "Proxy-Connection" : "Connection";
1165 webHeaders.RemoveInternal ((!proxy_query) ? "Proxy-Connection" : "Connection");
1166 Version proto_version = servicePoint.ProtocolVersion;
1167 bool spoint10 = (proto_version == null || proto_version == HttpVersion.Version10);
1169 if (keepAlive && (version == HttpVersion.Version10 || spoint10)) {
1170 if (webHeaders[connectionHeader] == null
1171 || webHeaders[connectionHeader].IndexOf ("keep-alive", StringComparison.OrdinalIgnoreCase) == -1)
1172 webHeaders.RemoveAndAdd (connectionHeader, "keep-alive");
1173 } else if (!keepAlive && version == HttpVersion.Version11) {
1174 webHeaders.RemoveAndAdd (connectionHeader, "close");
1177 webHeaders.SetInternal ("Host", Host);
1178 if (cookieContainer != null) {
1179 string cookieHeader = cookieContainer.GetCookieHeader (actualUri);
1180 if (cookieHeader != "")
1181 webHeaders.RemoveAndAdd ("Cookie", cookieHeader);
1183 webHeaders.RemoveInternal ("Cookie");
1186 string accept_encoding = null;
1187 if ((auto_decomp & DecompressionMethods.GZip) != 0)
1188 accept_encoding = "gzip";
1189 if ((auto_decomp & DecompressionMethods.Deflate) != 0)
1190 accept_encoding = accept_encoding != null ? "gzip, deflate" : "deflate";
1191 if (accept_encoding != null)
1192 webHeaders.RemoveAndAdd ("Accept-Encoding", accept_encoding);
1194 if (!usedPreAuth && preAuthenticate)
1195 DoPreAuthenticate ();
1197 return webHeaders.ToString ();
1200 void DoPreAuthenticate ()
1202 bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
1203 ICredentials creds = (!isProxy || credentials != null) ? credentials : proxy.Credentials;
1204 Authorization auth = AuthenticationManager.PreAuthenticate (this, creds);
1208 webHeaders.RemoveInternal ("Proxy-Authorization");
1209 webHeaders.RemoveInternal ("Authorization");
1210 string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization";
1211 webHeaders [authHeader] = auth.Message;
1215 internal void SetWriteStreamError (WebExceptionStatus status, Exception exc)
1220 WebAsyncResult r = asyncWrite;
1228 msg = "Error: " + status;
1229 wex = new WebException (msg, status);
1231 msg = String.Format ("Error: {0} ({1})", status, exc.Message);
1232 wex = new WebException (msg, exc, status);
1234 r.SetCompleted (false, wex);
1239 internal void SendRequestHeaders (bool propagate_error)
1241 StringBuilder req = new StringBuilder ();
1244 query = actualUri.PathAndQuery;
1246 query = String.Format ("{0}://{1}{2}", actualUri.Scheme,
1248 actualUri.PathAndQuery);
1251 if (!force_version && servicePoint.ProtocolVersion != null && servicePoint.ProtocolVersion < version) {
1252 actualVersion = servicePoint.ProtocolVersion;
1254 actualVersion = version;
1257 req.AppendFormat ("{0} {1} HTTP/{2}.{3}\r\n", method, query,
1258 actualVersion.Major, actualVersion.Minor);
1259 req.Append (GetHeaders ());
1260 string reqstr = req.ToString ();
1261 byte [] bytes = Encoding.UTF8.GetBytes (reqstr);
1263 writeStream.SetHeaders (bytes);
1264 } catch (WebException wexc) {
1265 SetWriteStreamError (wexc.Status, wexc);
1266 if (propagate_error)
1268 } catch (Exception exc) {
1269 SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
1270 if (propagate_error)
1275 internal void SetWriteStream (WebConnectionStream stream)
1280 writeStream = stream;
1281 if (bodyBuffer != null) {
1282 webHeaders.RemoveInternal ("Transfer-Encoding");
1283 contentLength = bodyBufferLength;
1284 writeStream.SendChunked = false;
1287 SendRequestHeaders (false);
1291 if (bodyBuffer != null) {
1292 // The body has been written and buffered. The request "user"
1293 // won't write it again, so we must do it.
1294 if (ntlm_auth_state != NtlmAuthState.Challenge) {
1295 writeStream.Write (bodyBuffer, 0, bodyBufferLength);
1297 writeStream.Close ();
1299 } else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
1300 method != "TRACE") {
1301 if (getResponseCalled && !writeStream.RequestWritten)
1302 writeStream.WriteRequest ();
1305 if (asyncWrite != null) {
1306 asyncWrite.SetCompleted (false, stream);
1307 asyncWrite.DoCallback ();
1312 internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
1317 string msg = String.Format ("Error getting response stream ({0}): {1}", where, status);
1318 WebAsyncResult r = asyncRead;
1323 if (e is WebException) {
1324 wexc = (WebException) e;
1326 wexc = new WebException (msg, e, status, null);
1329 if (!r.IsCompleted) {
1330 r.SetCompleted (false, wexc);
1332 } else if (r == asyncWrite) {
1335 haveResponse = true;
1339 haveResponse = true;
1345 void CheckSendError (WebConnectionData data)
1347 // Got here, but no one called GetResponse
1348 int status = data.StatusCode;
1349 if (status < 400 || status == 401 || status == 407)
1352 if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) {
1353 // The request has not been completely sent and we got here!
1354 // We should probably just close and cause an error in any case,
1355 saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse);
1356 if (allowBuffering || sendChunked || writeStream.totalWritten >= contentLength) {
1357 webResponse.ReadAll ();
1359 writeStream.IgnoreIOErrors = true;
1364 void HandleNtlmAuth (WebAsyncResult r)
1366 WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
1368 WebConnection cnc = wce.Connection;
1369 cnc.PriorityRequest = this;
1370 bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
1371 ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
1372 if (creds != null) {
1373 cnc.NtlmCredential = creds.GetCredential (requestUri, "NTLM");
1374 cnc.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah;
1378 finished_reading = false;
1379 haveResponse = false;
1380 webResponse.ReadAll ();
1384 internal void SetResponseData (WebConnectionData data)
1388 if (data.stream != null)
1389 data.stream.Close ();
1393 WebException wexc = null;
1395 webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer);
1396 } catch (Exception e) {
1397 wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null);
1398 if (data.stream != null)
1399 data.stream.Close ();
1402 if (wexc == null && (method == "POST" || method == "PUT")) {
1403 CheckSendError (data);
1404 if (saved_exc != null)
1405 wexc = (WebException) saved_exc;
1408 WebAsyncResult r = asyncRead;
1410 bool forced = false;
1411 if (r == null && webResponse != null) {
1412 // This is a forced completion (302, 204)...
1414 r = new WebAsyncResult (null, null);
1415 r.SetCompleted (false, webResponse);
1420 haveResponse = true;
1422 r.SetCompleted (false, wexc);
1429 redirected = CheckFinalStatus (r);
1431 if (ntlm_auth_state != NtlmAuthState.None && authCompleted && webResponse != null
1432 && (int)webResponse.StatusCode < 400) {
1433 WebConnectionStream wce = webResponse.GetResponseStream () as WebConnectionStream;
1435 WebConnection cnc = wce.Connection;
1436 cnc.NtlmAuthenticated = true;
1440 // clear internal buffer so that it does not
1441 // hold possible big buffer (bug #397627)
1442 if (writeStream != null)
1443 writeStream.KillBuffer ();
1445 haveResponse = true;
1446 r.SetCompleted (false, webResponse);
1449 if (webResponse != null) {
1450 if (ntlm_auth_state != NtlmAuthState.None) {
1454 webResponse.Close ();
1456 finished_reading = false;
1457 haveResponse = false;
1460 servicePoint = GetServicePoint ();
1461 abortHandler = servicePoint.SendRequest (this, connectionGroup);
1463 } catch (WebException wexc2) {
1466 haveResponse = true;
1468 r.SetCompleted (false, wexc2);
1471 } catch (Exception ex) {
1472 wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null);
1475 haveResponse = true;
1477 r.SetCompleted (false, wexc);
1485 bool CheckAuthorization (WebResponse response, HttpStatusCode code)
1487 authCompleted = false;
1488 if (code == HttpStatusCode.Unauthorized && credentials == null)
1491 bool isProxy = (code == HttpStatusCode.ProxyAuthenticationRequired);
1492 if (isProxy && (proxy == null || proxy.Credentials == null))
1495 string [] authHeaders = response.Headers.GetValues_internal ( (isProxy) ? "Proxy-Authenticate" : "WWW-Authenticate", false);
1496 if (authHeaders == null || authHeaders.Length == 0)
1499 ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
1500 Authorization auth = null;
1501 foreach (string authHeader in authHeaders) {
1502 auth = AuthenticationManager.Authenticate (authHeader, this, creds);
1508 webHeaders [(isProxy) ? "Proxy-Authorization" : "Authorization"] = auth.Message;
1509 authCompleted = auth.Complete;
1510 bool is_ntlm = (auth.Module.AuthenticationType == "NTLM");
1512 ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1);
1516 // Returns true if redirected
1517 bool CheckFinalStatus (WebAsyncResult result)
1519 if (result.GotException) {
1521 throw result.Exception;
1524 Exception throwMe = result.Exception;
1526 HttpWebResponse resp = result.Response;
1527 WebExceptionStatus protoError = WebExceptionStatus.ProtocolError;
1528 HttpStatusCode code = 0;
1529 if (throwMe == null && webResponse != null) {
1530 code = webResponse.StatusCode;
1531 if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
1532 (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
1533 if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
1534 // Keep the written body, so it can be rewritten in the retry
1535 if (InternalAllowBuffering) {
1536 // NTLM: This is to avoid sending data in the 'challenge' request
1537 // We save it in the first request (first 401), don't send anything
1538 // in the challenge request and send it in the response request along
1539 // with the buffers kept form the first request.
1540 if (ntlm_auth_state != NtlmAuthState.Response) {
1541 bodyBuffer = writeStream.WriteBuffer;
1542 bodyBufferLength = writeStream.WriteBufferLength;
1545 } else if (method != "PUT" && method != "POST") {
1553 writeStream.InternalClose ();
1555 webResponse.Close ();
1559 throw new WebException ("This request requires buffering " +
1560 "of data for authentication or " +
1561 "redirection to be sucessful.");
1566 if ((int) code >= 400) {
1567 string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1568 (int) code, webResponse.StatusDescription);
1569 throwMe = new WebException (err, null, protoError, webResponse);
1570 webResponse.ReadAll ();
1571 } else if ((int) code == 304 && allowAutoRedirect) {
1572 string err = String.Format ("The remote server returned an error: ({0}) {1}.",
1573 (int) code, webResponse.StatusDescription);
1574 throwMe = new WebException (err, null, protoError, webResponse);
1575 } else if ((int) code >= 300 && allowAutoRedirect && redirects >= maxAutoRedirect) {
1576 throwMe = new WebException ("Max. redirections exceeded.", null,
1577 protoError, webResponse);
1578 webResponse.ReadAll ();
1583 if (throwMe == null) {
1586 if (allowAutoRedirect && c >= 300) {
1587 if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
1588 bodyBuffer = writeStream.WriteBuffer;
1589 bodyBufferLength = writeStream.WriteBufferLength;
1591 b = Redirect (result, code);
1592 if (b && ntlm_auth_state != 0)
1593 ntlm_auth_state = 0;
1596 if (resp != null && c >= 300 && c != 304)
1605 if (writeStream != null) {
1606 writeStream.InternalClose ();
1615 internal bool ReuseConnection {
1620 internal WebConnection StoredConnection;