WebConnection.cs 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  1. //
  2. // System.Net.WebConnection
  3. //
  4. // Authors:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. //
  7. // (C) 2003 Ximian, Inc (http://www.ximian.com)
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.IO;
  30. using System.Collections;
  31. using System.Net.Sockets;
  32. using System.Reflection;
  33. using System.Security.Cryptography.X509Certificates;
  34. using System.Text;
  35. using System.Threading;
  36. #if SECURITY_DEP
  37. using Mono.Security.Protocol.Tls;
  38. #endif
  39. namespace System.Net
  40. {
  41. enum ReadState
  42. {
  43. None,
  44. Status,
  45. Headers,
  46. Content
  47. }
  48. class WebConnection
  49. {
  50. ServicePoint sPoint;
  51. Stream nstream;
  52. Socket socket;
  53. object socketLock = new object ();
  54. WebExceptionStatus status;
  55. WaitCallback initConn;
  56. bool keepAlive;
  57. byte [] buffer;
  58. static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
  59. EventHandler abortHandler;
  60. AbortHelper abortHelper;
  61. ReadState readState;
  62. internal WebConnectionData Data;
  63. bool chunkedRead;
  64. ChunkStream chunkStream;
  65. Queue queue;
  66. bool reused;
  67. int position;
  68. bool busy;
  69. HttpWebRequest priority_request;
  70. NetworkCredential ntlm_credentials;
  71. bool ntlm_authenticated;
  72. bool unsafe_sharing;
  73. bool ssl;
  74. bool certsAvailable;
  75. Exception connect_exception;
  76. static object classLock = new object ();
  77. static Type sslStream;
  78. static PropertyInfo piClient;
  79. static PropertyInfo piServer;
  80. static PropertyInfo piTrustFailure;
  81. #if MONOTOUCH
  82. static MethodInfo start_wwan;
  83. static WebConnection ()
  84. {
  85. Type type = Type.GetType ("MonoTouch.ObjCRuntime.Runtime, monotouch");
  86. start_wwan = type.GetMethod ("StartWWAN");
  87. }
  88. #endif
  89. public WebConnection (WebConnectionGroup group, ServicePoint sPoint)
  90. {
  91. this.sPoint = sPoint;
  92. buffer = new byte [4096];
  93. readState = ReadState.None;
  94. Data = new WebConnectionData ();
  95. initConn = new WaitCallback (state => {
  96. try {
  97. InitConnection (state);
  98. } catch {}
  99. });
  100. queue = group.Queue;
  101. abortHelper = new AbortHelper ();
  102. abortHelper.Connection = this;
  103. abortHandler = new EventHandler (abortHelper.Abort);
  104. }
  105. class AbortHelper {
  106. public WebConnection Connection;
  107. public void Abort (object sender, EventArgs args)
  108. {
  109. WebConnection other = ((HttpWebRequest) sender).WebConnection;
  110. if (other == null)
  111. other = Connection;
  112. other.Abort (sender, args);
  113. }
  114. }
  115. bool CanReuse ()
  116. {
  117. // The real condition is !(socket.Poll (0, SelectMode.SelectRead) || socket.Available != 0)
  118. // but if there's data pending to read (!) we won't reuse the socket.
  119. return (socket.Poll (0, SelectMode.SelectRead) == false);
  120. }
  121. void Connect (HttpWebRequest request)
  122. {
  123. lock (socketLock) {
  124. if (socket != null && socket.Connected && status == WebExceptionStatus.Success) {
  125. // Take the chunked stream to the expected state (State.None)
  126. if (CanReuse () && CompleteChunkedRead ()) {
  127. reused = true;
  128. return;
  129. }
  130. }
  131. reused = false;
  132. if (socket != null) {
  133. socket.Close();
  134. socket = null;
  135. }
  136. chunkStream = null;
  137. IPHostEntry hostEntry = sPoint.HostEntry;
  138. if (hostEntry == null) {
  139. #if MONOTOUCH
  140. if (start_wwan != null) {
  141. start_wwan.Invoke (null, new object [1] { sPoint.Address });
  142. hostEntry = sPoint.HostEntry;
  143. }
  144. if (hostEntry == null) {
  145. #endif
  146. status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure :
  147. WebExceptionStatus.NameResolutionFailure;
  148. return;
  149. #if MONOTOUCH
  150. }
  151. #endif
  152. }
  153. //WebConnectionData data = Data;
  154. foreach (IPAddress address in hostEntry.AddressList) {
  155. socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  156. IPEndPoint remote = new IPEndPoint (address, sPoint.Address.Port);
  157. socket.SetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay, sPoint.UseNagleAlgorithm ? 0 : 1);
  158. socket.NoDelay = !sPoint.UseNagleAlgorithm;
  159. if (!sPoint.CallEndPointDelegate (socket, remote)) {
  160. socket.Close ();
  161. socket = null;
  162. status = WebExceptionStatus.ConnectFailure;
  163. } else {
  164. try {
  165. if (request.Aborted)
  166. return;
  167. socket.Connect (remote);
  168. status = WebExceptionStatus.Success;
  169. break;
  170. } catch (ThreadAbortException) {
  171. // program exiting...
  172. Socket s = socket;
  173. socket = null;
  174. if (s != null)
  175. s.Close ();
  176. return;
  177. } catch (ObjectDisposedException) {
  178. // socket closed from another thread
  179. return;
  180. } catch (Exception exc) {
  181. Socket s = socket;
  182. socket = null;
  183. if (s != null)
  184. s.Close ();
  185. if (!request.Aborted)
  186. status = WebExceptionStatus.ConnectFailure;
  187. connect_exception = exc;
  188. }
  189. }
  190. }
  191. }
  192. }
  193. static void EnsureSSLStreamAvailable ()
  194. {
  195. lock (classLock) {
  196. if (sslStream != null)
  197. return;
  198. #if NET_2_1 && SECURITY_DEP
  199. sslStream = typeof (Mono.Security.Protocol.Tls.HttpsClientStream);
  200. #else
  201. // HttpsClientStream is an internal glue class in Mono.Security.dll
  202. sslStream = Type.GetType ("Mono.Security.Protocol.Tls.HttpsClientStream, " +
  203. Consts.AssemblyMono_Security, false);
  204. if (sslStream == null) {
  205. string msg = "Missing Mono.Security.dll assembly. " +
  206. "Support for SSL/TLS is unavailable.";
  207. throw new NotSupportedException (msg);
  208. }
  209. #endif
  210. piClient = sslStream.GetProperty ("SelectedClientCertificate");
  211. piServer = sslStream.GetProperty ("ServerCertificate");
  212. piTrustFailure = sslStream.GetProperty ("TrustFailure");
  213. }
  214. }
  215. bool CreateTunnel (HttpWebRequest request, Stream stream, out byte [] buffer)
  216. {
  217. StringBuilder sb = new StringBuilder ();
  218. sb.Append ("CONNECT ");
  219. sb.Append (request.Address.Host);
  220. sb.Append (':');
  221. sb.Append (request.Address.Port);
  222. sb.Append (" HTTP/");
  223. if (request.ServicePoint.ProtocolVersion == HttpVersion.Version11)
  224. sb.Append ("1.1");
  225. else
  226. sb.Append ("1.0");
  227. sb.Append ("\r\nHost: ");
  228. sb.Append (request.Address.Authority);
  229. string challenge = Data.Challenge;
  230. Data.Challenge = null;
  231. bool have_auth = (request.Headers ["Proxy-Authorization"] != null);
  232. if (have_auth) {
  233. sb.Append ("\r\nProxy-Authorization: ");
  234. sb.Append (request.Headers ["Proxy-Authorization"]);
  235. } else if (challenge != null && Data.StatusCode == 407) {
  236. have_auth = true;
  237. ICredentials creds = request.Proxy.Credentials;
  238. Authorization auth = AuthenticationManager.Authenticate (challenge, request, creds);
  239. if (auth != null) {
  240. sb.Append ("\r\nProxy-Authorization: ");
  241. sb.Append (auth.Message);
  242. }
  243. }
  244. sb.Append ("\r\n\r\n");
  245. Data.StatusCode = 0;
  246. byte [] connectBytes = Encoding.Default.GetBytes (sb.ToString ());
  247. stream.Write (connectBytes, 0, connectBytes.Length);
  248. int status;
  249. WebHeaderCollection result = ReadHeaders (request, stream, out buffer, out status);
  250. if (!have_auth && result != null && status == 407) { // Needs proxy auth
  251. Data.StatusCode = status;
  252. Data.Challenge = result ["Proxy-Authenticate"];
  253. return false;
  254. } else if (status != 200) {
  255. string msg = String.Format ("The remote server returned a {0} status code.", status);
  256. HandleError (WebExceptionStatus.SecureChannelFailure, null, msg);
  257. return false;
  258. }
  259. return (result != null);
  260. }
  261. WebHeaderCollection ReadHeaders (HttpWebRequest request, Stream stream, out byte [] retBuffer, out int status)
  262. {
  263. retBuffer = null;
  264. status = 200;
  265. byte [] buffer = new byte [1024];
  266. MemoryStream ms = new MemoryStream ();
  267. bool gotStatus = false;
  268. WebHeaderCollection headers = null;
  269. while (true) {
  270. int n = stream.Read (buffer, 0, 1024);
  271. if (n == 0) {
  272. HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders");
  273. return null;
  274. }
  275. ms.Write (buffer, 0, n);
  276. int start = 0;
  277. string str = null;
  278. headers = new WebHeaderCollection ();
  279. while (ReadLine (ms.GetBuffer (), ref start, (int) ms.Length, ref str)) {
  280. if (str == null) {
  281. if (ms.Length - start > 0) {
  282. retBuffer = new byte [ms.Length - start];
  283. Buffer.BlockCopy (ms.GetBuffer (), start, retBuffer, 0, retBuffer.Length);
  284. }
  285. return headers;
  286. }
  287. if (gotStatus) {
  288. headers.Add (str);
  289. continue;
  290. }
  291. int spaceidx = str.IndexOf (' ');
  292. if (spaceidx == -1) {
  293. HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2");
  294. return null;
  295. }
  296. status = (int) UInt32.Parse (str.Substring (spaceidx + 1, 3));
  297. gotStatus = true;
  298. }
  299. }
  300. }
  301. bool CreateStream (HttpWebRequest request)
  302. {
  303. try {
  304. NetworkStream serverStream = new NetworkStream (socket, false);
  305. if (request.Address.Scheme == Uri.UriSchemeHttps) {
  306. ssl = true;
  307. EnsureSSLStreamAvailable ();
  308. if (!reused || nstream == null || nstream.GetType () != sslStream) {
  309. byte [] buffer = null;
  310. if (sPoint.UseConnect) {
  311. bool ok = CreateTunnel (request, serverStream, out buffer);
  312. if (!ok)
  313. return false;
  314. }
  315. object[] args = new object [4] { serverStream,
  316. request.ClientCertificates,
  317. request, buffer};
  318. nstream = (Stream) Activator.CreateInstance (sslStream, args);
  319. #if SECURITY_DEP
  320. SslClientStream scs = (SslClientStream) nstream;
  321. var helper = new ServicePointManager.ChainValidationHelper (request);
  322. scs.ServerCertValidation2 += new CertificateValidationCallback2 (helper.ValidateChain);
  323. #endif
  324. certsAvailable = false;
  325. }
  326. // we also need to set ServicePoint.Certificate
  327. // and ServicePoint.ClientCertificate but this can
  328. // only be done later (after handshake - which is
  329. // done only after a read operation).
  330. } else {
  331. ssl = false;
  332. nstream = serverStream;
  333. }
  334. } catch (Exception) {
  335. if (!request.Aborted)
  336. status = WebExceptionStatus.ConnectFailure;
  337. return false;
  338. }
  339. return true;
  340. }
  341. void HandleError (WebExceptionStatus st, Exception e, string where)
  342. {
  343. status = st;
  344. lock (this) {
  345. if (st == WebExceptionStatus.RequestCanceled)
  346. Data = new WebConnectionData ();
  347. }
  348. if (e == null) { // At least we now where it comes from
  349. try {
  350. #if TARGET_JVM
  351. throw new Exception ();
  352. #else
  353. throw new Exception (new System.Diagnostics.StackTrace ().ToString ());
  354. #endif
  355. } catch (Exception e2) {
  356. e = e2;
  357. }
  358. }
  359. HttpWebRequest req = null;
  360. if (Data != null && Data.request != null)
  361. req = Data.request;
  362. Close (true);
  363. if (req != null) {
  364. req.FinishedReading = true;
  365. req.SetResponseError (st, e, where);
  366. }
  367. }
  368. static void ReadDone (IAsyncResult result)
  369. {
  370. WebConnection cnc = (WebConnection) result.AsyncState;
  371. WebConnectionData data = cnc.Data;
  372. Stream ns = cnc.nstream;
  373. if (ns == null) {
  374. cnc.Close (true);
  375. return;
  376. }
  377. int nread = -1;
  378. try {
  379. nread = ns.EndRead (result);
  380. } catch (Exception e) {
  381. cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "ReadDone1");
  382. return;
  383. }
  384. if (nread == 0) {
  385. cnc.HandleError (WebExceptionStatus.ReceiveFailure, null, "ReadDone2");
  386. return;
  387. }
  388. if (nread < 0) {
  389. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadDone3");
  390. return;
  391. }
  392. int pos = -1;
  393. nread += cnc.position;
  394. if (cnc.readState == ReadState.None) {
  395. Exception exc = null;
  396. try {
  397. pos = cnc.GetResponse (cnc.buffer, nread);
  398. } catch (Exception e) {
  399. exc = e;
  400. }
  401. if (exc != null) {
  402. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, exc, "ReadDone4");
  403. return;
  404. }
  405. }
  406. if (cnc.readState != ReadState.Content) {
  407. int est = nread * 2;
  408. int max = (est < cnc.buffer.Length) ? cnc.buffer.Length : est;
  409. byte [] newBuffer = new byte [max];
  410. Buffer.BlockCopy (cnc.buffer, 0, newBuffer, 0, nread);
  411. cnc.buffer = newBuffer;
  412. cnc.position = nread;
  413. cnc.readState = ReadState.None;
  414. InitRead (cnc);
  415. return;
  416. }
  417. cnc.position = 0;
  418. WebConnectionStream stream = new WebConnectionStream (cnc);
  419. string contentType = data.Headers ["Transfer-Encoding"];
  420. cnc.chunkedRead = (contentType != null && contentType.ToLower ().IndexOf ("chunked") != -1);
  421. if (!cnc.chunkedRead) {
  422. stream.ReadBuffer = cnc.buffer;
  423. stream.ReadBufferOffset = pos;
  424. stream.ReadBufferSize = nread;
  425. stream.CheckResponseInBuffer ();
  426. } else if (cnc.chunkStream == null) {
  427. try {
  428. cnc.chunkStream = new ChunkStream (cnc.buffer, pos, nread, data.Headers);
  429. } catch (Exception e) {
  430. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone5");
  431. return;
  432. }
  433. } else {
  434. cnc.chunkStream.ResetBuffer ();
  435. try {
  436. cnc.chunkStream.Write (cnc.buffer, pos, nread);
  437. } catch (Exception e) {
  438. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone6");
  439. return;
  440. }
  441. }
  442. data.stream = stream;
  443. if (!ExpectContent (data.StatusCode) || data.request.Method == "HEAD")
  444. stream.ForceCompletion ();
  445. data.request.SetResponseData (data);
  446. }
  447. static bool ExpectContent (int statusCode)
  448. {
  449. return (statusCode >= 200 && statusCode != 204 && statusCode != 304);
  450. }
  451. internal void GetCertificates ()
  452. {
  453. // here the SSL negotiation have been done
  454. X509Certificate client = (X509Certificate) piClient.GetValue (nstream, null);
  455. X509Certificate server = (X509Certificate) piServer.GetValue (nstream, null);
  456. sPoint.SetCertificates (client, server);
  457. certsAvailable = (server != null);
  458. }
  459. internal static void InitRead (object state)
  460. {
  461. WebConnection cnc = (WebConnection) state;
  462. Stream ns = cnc.nstream;
  463. try {
  464. int size = cnc.buffer.Length - cnc.position;
  465. ns.BeginRead (cnc.buffer, cnc.position, size, readDoneDelegate, cnc);
  466. } catch (Exception e) {
  467. cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "InitRead");
  468. }
  469. }
  470. int GetResponse (byte [] buffer, int max)
  471. {
  472. int pos = 0;
  473. string line = null;
  474. bool lineok = false;
  475. bool isContinue = false;
  476. bool emptyFirstLine = false;
  477. do {
  478. if (readState == ReadState.None) {
  479. lineok = ReadLine (buffer, ref pos, max, ref line);
  480. if (!lineok)
  481. return -1;
  482. if (line == null) {
  483. emptyFirstLine = true;
  484. continue;
  485. }
  486. emptyFirstLine = false;
  487. readState = ReadState.Status;
  488. string [] parts = line.Split (' ');
  489. if (parts.Length < 2)
  490. return -1;
  491. if (String.Compare (parts [0], "HTTP/1.1", true) == 0) {
  492. Data.Version = HttpVersion.Version11;
  493. sPoint.SetVersion (HttpVersion.Version11);
  494. } else {
  495. Data.Version = HttpVersion.Version10;
  496. sPoint.SetVersion (HttpVersion.Version10);
  497. }
  498. Data.StatusCode = (int) UInt32.Parse (parts [1]);
  499. if (parts.Length >= 3)
  500. Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
  501. else
  502. Data.StatusDescription = "";
  503. if (pos >= max)
  504. return pos;
  505. }
  506. emptyFirstLine = false;
  507. if (readState == ReadState.Status) {
  508. readState = ReadState.Headers;
  509. Data.Headers = new WebHeaderCollection ();
  510. ArrayList headers = new ArrayList ();
  511. bool finished = false;
  512. while (!finished) {
  513. if (ReadLine (buffer, ref pos, max, ref line) == false)
  514. break;
  515. if (line == null) {
  516. // Empty line: end of headers
  517. finished = true;
  518. continue;
  519. }
  520. if (line.Length > 0 && (line [0] == ' ' || line [0] == '\t')) {
  521. int count = headers.Count - 1;
  522. if (count < 0)
  523. break;
  524. string prev = (string) headers [count] + line;
  525. headers [count] = prev;
  526. } else {
  527. headers.Add (line);
  528. }
  529. }
  530. if (!finished)
  531. return -1;
  532. foreach (string s in headers)
  533. Data.Headers.SetInternal (s);
  534. if (Data.StatusCode == (int) HttpStatusCode.Continue) {
  535. sPoint.SendContinue = true;
  536. if (pos >= max)
  537. return pos;
  538. if (Data.request.ExpectContinue) {
  539. Data.request.DoContinueDelegate (Data.StatusCode, Data.Headers);
  540. // Prevent double calls when getting the
  541. // headers in several packets.
  542. Data.request.ExpectContinue = false;
  543. }
  544. readState = ReadState.None;
  545. isContinue = true;
  546. }
  547. else {
  548. readState = ReadState.Content;
  549. return pos;
  550. }
  551. }
  552. } while (emptyFirstLine || isContinue);
  553. return -1;
  554. }
  555. void InitConnection (object state)
  556. {
  557. HttpWebRequest request = (HttpWebRequest) state;
  558. request.WebConnection = this;
  559. if (request.Aborted)
  560. return;
  561. keepAlive = request.KeepAlive;
  562. Data = new WebConnectionData ();
  563. Data.request = request;
  564. retry:
  565. Connect (request);
  566. if (request.Aborted)
  567. return;
  568. if (status != WebExceptionStatus.Success) {
  569. if (!request.Aborted) {
  570. request.SetWriteStreamError (status, connect_exception);
  571. Close (true);
  572. }
  573. return;
  574. }
  575. if (!CreateStream (request)) {
  576. if (request.Aborted)
  577. return;
  578. WebExceptionStatus st = status;
  579. if (Data.Challenge != null)
  580. goto retry;
  581. Exception cnc_exc = connect_exception;
  582. connect_exception = null;
  583. request.SetWriteStreamError (st, cnc_exc);
  584. Close (true);
  585. return;
  586. }
  587. readState = ReadState.None;
  588. request.SetWriteStream (new WebConnectionStream (this, request));
  589. }
  590. internal EventHandler SendRequest (HttpWebRequest request)
  591. {
  592. if (request.Aborted)
  593. return null;
  594. lock (this) {
  595. if (!busy) {
  596. busy = true;
  597. status = WebExceptionStatus.Success;
  598. ThreadPool.QueueUserWorkItem (initConn, request);
  599. } else {
  600. lock (queue) {
  601. queue.Enqueue (request);
  602. }
  603. }
  604. }
  605. return abortHandler;
  606. }
  607. void SendNext ()
  608. {
  609. lock (queue) {
  610. if (queue.Count > 0) {
  611. SendRequest ((HttpWebRequest) queue.Dequeue ());
  612. }
  613. }
  614. }
  615. internal void NextRead ()
  616. {
  617. lock (this) {
  618. Data.request.FinishedReading = true;
  619. string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection";
  620. string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null;
  621. bool keepAlive = (Data.Version == HttpVersion.Version11 && this.keepAlive);
  622. if (cncHeader != null) {
  623. cncHeader = cncHeader.ToLower ();
  624. keepAlive = (this.keepAlive && cncHeader.IndexOf ("keep-alive") != -1);
  625. }
  626. if ((socket != null && !socket.Connected) ||
  627. (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
  628. Close (false);
  629. }
  630. busy = false;
  631. if (priority_request != null) {
  632. SendRequest (priority_request);
  633. priority_request = null;
  634. } else {
  635. SendNext ();
  636. }
  637. }
  638. }
  639. static bool ReadLine (byte [] buffer, ref int start, int max, ref string output)
  640. {
  641. bool foundCR = false;
  642. StringBuilder text = new StringBuilder ();
  643. int c = 0;
  644. while (start < max) {
  645. c = (int) buffer [start++];
  646. if (c == '\n') { // newline
  647. if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
  648. text.Length--;
  649. foundCR = false;
  650. break;
  651. } else if (foundCR) {
  652. text.Length--;
  653. break;
  654. }
  655. if (c == '\r')
  656. foundCR = true;
  657. text.Append ((char) c);
  658. }
  659. if (c != '\n' && c != '\r')
  660. return false;
  661. if (text.Length == 0) {
  662. output = null;
  663. return (c == '\n' || c == '\r');
  664. }
  665. if (foundCR)
  666. text.Length--;
  667. output = text.ToString ();
  668. return true;
  669. }
  670. internal IAsyncResult BeginRead (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
  671. {
  672. lock (this) {
  673. if (Data.request != request)
  674. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  675. if (nstream == null)
  676. return null;
  677. }
  678. IAsyncResult result = null;
  679. if (!chunkedRead || chunkStream.WantMore) {
  680. try {
  681. result = nstream.BeginRead (buffer, offset, size, cb, state);
  682. cb = null;
  683. } catch (Exception) {
  684. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead");
  685. throw;
  686. }
  687. }
  688. if (chunkedRead) {
  689. WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size);
  690. wr.InnerAsyncResult = result;
  691. if (result == null) {
  692. // Will be completed from the data in ChunkStream
  693. wr.SetCompleted (true, (Exception) null);
  694. wr.DoCallback ();
  695. }
  696. return wr;
  697. }
  698. return result;
  699. }
  700. internal int EndRead (HttpWebRequest request, IAsyncResult result)
  701. {
  702. lock (this) {
  703. if (Data.request != request)
  704. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  705. if (nstream == null)
  706. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  707. }
  708. int nbytes = 0;
  709. WebAsyncResult wr = null;
  710. IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult;
  711. if (chunkedRead && (nsAsync is WebAsyncResult)) {
  712. wr = (WebAsyncResult) nsAsync;
  713. IAsyncResult inner = wr.InnerAsyncResult;
  714. if (inner != null && !(inner is WebAsyncResult))
  715. nbytes = nstream.EndRead (inner);
  716. } else if (!(nsAsync is WebAsyncResult)) {
  717. nbytes = nstream.EndRead (nsAsync);
  718. wr = (WebAsyncResult) result;
  719. }
  720. if (chunkedRead) {
  721. bool done = (nbytes == 0);
  722. try {
  723. chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
  724. if (!done && nbytes == 0 && chunkStream.WantMore)
  725. nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size);
  726. } catch (Exception e) {
  727. if (e is WebException)
  728. throw e;
  729. throw new WebException ("Invalid chunked data.", e,
  730. WebExceptionStatus.ServerProtocolViolation, null);
  731. }
  732. if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) {
  733. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead");
  734. throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
  735. }
  736. }
  737. return (nbytes != 0) ? nbytes : -1;
  738. }
  739. // To be called on chunkedRead when we can read no data from the ChunkStream yet
  740. int EnsureRead (byte [] buffer, int offset, int size)
  741. {
  742. byte [] morebytes = null;
  743. int nbytes = 0;
  744. while (nbytes == 0 && chunkStream.WantMore) {
  745. int localsize = chunkStream.ChunkLeft;
  746. if (localsize <= 0) // not read chunk size yet
  747. localsize = 1024;
  748. else if (localsize > 16384)
  749. localsize = 16384;
  750. if (morebytes == null || morebytes.Length < localsize)
  751. morebytes = new byte [localsize];
  752. int nread = nstream.Read (morebytes, 0, localsize);
  753. if (nread <= 0)
  754. return 0; // Error
  755. chunkStream.Write (morebytes, 0, nread);
  756. nbytes += chunkStream.Read (buffer, offset + nbytes, size - nbytes);
  757. }
  758. return nbytes;
  759. }
  760. bool CompleteChunkedRead()
  761. {
  762. if (!chunkedRead || chunkStream == null)
  763. return true;
  764. while (chunkStream.WantMore) {
  765. int nbytes = nstream.Read (buffer, 0, buffer.Length);
  766. if (nbytes <= 0)
  767. return false; // Socket was disconnected
  768. chunkStream.Write (buffer, 0, nbytes);
  769. }
  770. return true;
  771. }
  772. internal IAsyncResult BeginWrite (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
  773. {
  774. lock (this) {
  775. if (Data.request != request)
  776. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  777. if (nstream == null)
  778. return null;
  779. }
  780. IAsyncResult result = null;
  781. try {
  782. result = nstream.BeginWrite (buffer, offset, size, cb, state);
  783. } catch (Exception) {
  784. status = WebExceptionStatus.SendFailure;
  785. throw;
  786. }
  787. return result;
  788. }
  789. internal void EndWrite2 (HttpWebRequest request, IAsyncResult result)
  790. {
  791. if (request.FinishedReading)
  792. return;
  793. lock (this) {
  794. if (Data.request != request)
  795. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  796. if (nstream == null)
  797. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  798. }
  799. try {
  800. nstream.EndWrite (result);
  801. } catch (Exception exc) {
  802. status = WebExceptionStatus.SendFailure;
  803. if (exc.InnerException != null)
  804. throw exc.InnerException;
  805. throw;
  806. }
  807. }
  808. internal bool EndWrite (HttpWebRequest request, IAsyncResult result)
  809. {
  810. if (request.FinishedReading)
  811. return true;
  812. lock (this) {
  813. if (Data.request != request)
  814. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  815. if (nstream == null)
  816. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  817. }
  818. try {
  819. nstream.EndWrite (result);
  820. return true;
  821. } catch {
  822. status = WebExceptionStatus.SendFailure;
  823. return false;
  824. }
  825. }
  826. internal int Read (HttpWebRequest request, byte [] buffer, int offset, int size)
  827. {
  828. lock (this) {
  829. if (Data.request != request)
  830. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  831. if (nstream == null)
  832. return 0;
  833. }
  834. int result = 0;
  835. try {
  836. bool done = false;
  837. if (!chunkedRead) {
  838. result = nstream.Read (buffer, offset, size);
  839. done = (result == 0);
  840. }
  841. if (chunkedRead) {
  842. try {
  843. chunkStream.WriteAndReadBack (buffer, offset, size, ref result);
  844. if (!done && result == 0 && chunkStream.WantMore)
  845. result = EnsureRead (buffer, offset, size);
  846. } catch (Exception e) {
  847. HandleError (WebExceptionStatus.ReceiveFailure, e, "chunked Read1");
  848. throw;
  849. }
  850. if ((done || result == 0) && chunkStream.WantMore) {
  851. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked Read2");
  852. throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
  853. }
  854. }
  855. } catch (Exception e) {
  856. HandleError (WebExceptionStatus.ReceiveFailure, e, "Read");
  857. }
  858. return result;
  859. }
  860. internal bool Write (HttpWebRequest request, byte [] buffer, int offset, int size, ref string err_msg)
  861. {
  862. err_msg = null;
  863. lock (this) {
  864. if (Data.request != request)
  865. throw new ObjectDisposedException (typeof (NetworkStream).FullName);
  866. if (nstream == null)
  867. return false;
  868. }
  869. try {
  870. nstream.Write (buffer, offset, size);
  871. // here SSL handshake should have been done
  872. if (ssl && !certsAvailable)
  873. GetCertificates ();
  874. } catch (Exception e) {
  875. err_msg = e.Message;
  876. WebExceptionStatus wes = WebExceptionStatus.SendFailure;
  877. string msg = "Write: " + err_msg;
  878. if (e is WebException) {
  879. HandleError (wes, e, msg);
  880. return false;
  881. }
  882. // if SSL is in use then check for TrustFailure
  883. if (ssl && (bool) piTrustFailure.GetValue (nstream, null)) {
  884. wes = WebExceptionStatus.TrustFailure;
  885. msg = "Trust failure";
  886. }
  887. HandleError (wes, e, msg);
  888. return false;
  889. }
  890. return true;
  891. }
  892. internal void Close (bool sendNext)
  893. {
  894. lock (this) {
  895. if (nstream != null) {
  896. try {
  897. nstream.Close ();
  898. } catch {}
  899. nstream = null;
  900. }
  901. if (socket != null) {
  902. try {
  903. socket.Close ();
  904. } catch {}
  905. socket = null;
  906. }
  907. busy = false;
  908. Data = new WebConnectionData ();
  909. if (sendNext)
  910. SendNext ();
  911. }
  912. }
  913. void Abort (object sender, EventArgs args)
  914. {
  915. lock (this) {
  916. lock (queue) {
  917. HttpWebRequest req = (HttpWebRequest) sender;
  918. if (Data.request == req) {
  919. if (!req.FinishedReading) {
  920. status = WebExceptionStatus.RequestCanceled;
  921. Close (false);
  922. if (queue.Count > 0) {
  923. Data.request = (HttpWebRequest) queue.Dequeue ();
  924. SendRequest (Data.request);
  925. }
  926. }
  927. return;
  928. }
  929. req.FinishedReading = true;
  930. req.SetResponseError (WebExceptionStatus.RequestCanceled, null, "User aborted");
  931. if (queue.Count > 0 && queue.Peek () == sender) {
  932. queue.Dequeue ();
  933. } else if (queue.Count > 0) {
  934. object [] old = queue.ToArray ();
  935. queue.Clear ();
  936. for (int i = old.Length - 1; i >= 0; i--) {
  937. if (old [i] != sender)
  938. queue.Enqueue (old [i]);
  939. }
  940. }
  941. }
  942. }
  943. }
  944. internal void ResetNtlm ()
  945. {
  946. ntlm_authenticated = false;
  947. ntlm_credentials = null;
  948. unsafe_sharing = false;
  949. }
  950. internal bool Busy {
  951. get { lock (this) return busy; }
  952. }
  953. internal bool Connected {
  954. get {
  955. lock (this) {
  956. return (socket != null && socket.Connected);
  957. }
  958. }
  959. }
  960. // -Used for NTLM authentication
  961. internal HttpWebRequest PriorityRequest {
  962. set { priority_request = value; }
  963. }
  964. internal bool NtlmAuthenticated {
  965. get { return ntlm_authenticated; }
  966. set { ntlm_authenticated = value; }
  967. }
  968. internal NetworkCredential NtlmCredential {
  969. get { return ntlm_credentials; }
  970. set { ntlm_credentials = value; }
  971. }
  972. internal bool UnsafeAuthenticatedConnectionSharing {
  973. get { return unsafe_sharing; }
  974. set { unsafe_sharing = value; }
  975. }
  976. // -
  977. }
  978. }