bring Mono Security to monotouch
[mcs.git] / class / System / System.Net / WebConnection.cs
blob1e8fa8bc0f1229e740604df8a8dce5967276876d
1 //
2 // System.Net.WebConnection
3 //
4 // Authors:
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.IO;
32 using System.Collections;
33 using System.Net.Sockets;
34 using System.Reflection;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Text;
37 using System.Threading;
39 namespace System.Net
41 enum ReadState
43 None,
44 Status,
45 Headers,
46 Content
49 class WebConnection
51 ServicePoint sPoint;
52 Stream nstream;
53 Socket socket;
54 object socketLock = new object ();
55 WebExceptionStatus status;
56 WaitCallback initConn;
57 bool keepAlive;
58 byte [] buffer;
59 static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
60 EventHandler abortHandler;
61 AbortHelper abortHelper;
62 ReadState readState;
63 internal WebConnectionData Data;
64 bool chunkedRead;
65 ChunkStream chunkStream;
66 Queue queue;
67 bool reused;
68 int position;
69 bool busy;
70 HttpWebRequest priority_request;
71 NetworkCredential ntlm_credentials;
72 bool ntlm_authenticated;
73 #if NET_1_1
74 bool unsafe_sharing;
75 #endif
77 bool ssl;
78 bool certsAvailable;
79 Exception connect_exception;
80 static object classLock = new object ();
81 static Type sslStream;
82 static PropertyInfo piClient;
83 static PropertyInfo piServer;
84 static PropertyInfo piTrustFailure;
86 public WebConnection (WebConnectionGroup group, ServicePoint sPoint)
88 this.sPoint = sPoint;
89 buffer = new byte [4096];
90 readState = ReadState.None;
91 Data = new WebConnectionData ();
92 initConn = new WaitCallback (InitConnection);
93 queue = group.Queue;
94 abortHelper = new AbortHelper ();
95 abortHelper.Connection = this;
96 abortHandler = new EventHandler (abortHelper.Abort);
99 class AbortHelper {
100 public WebConnection Connection;
102 public void Abort (object sender, EventArgs args)
104 WebConnection other = ((HttpWebRequest) sender).WebConnection;
105 if (other == null)
106 other = Connection;
107 other.Abort (sender, args);
111 bool CanReuse ()
113 // The real condition is !(socket.Poll (0, SelectMode.SelectRead) || socket.Available != 0)
114 // but if there's data pending to read (!) we won't reuse the socket.
115 return (socket.Poll (0, SelectMode.SelectRead) == false);
118 void Connect (HttpWebRequest request)
120 lock (socketLock) {
121 if (socket != null && socket.Connected && status == WebExceptionStatus.Success) {
122 // Take the chunked stream to the expected state (State.None)
123 if (CanReuse () && CompleteChunkedRead ()) {
124 reused = true;
125 return;
129 reused = false;
130 if (socket != null) {
131 socket.Close();
132 socket = null;
135 chunkStream = null;
136 IPHostEntry hostEntry = sPoint.HostEntry;
138 if (hostEntry == null) {
139 status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure :
140 WebExceptionStatus.NameResolutionFailure;
141 return;
144 WebConnectionData data = Data;
145 foreach (IPAddress address in hostEntry.AddressList) {
146 socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
147 IPEndPoint remote = new IPEndPoint (address, sPoint.Address.Port);
148 #if NET_1_1
149 socket.SetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay, sPoint.UseNagleAlgorithm ? 0 : 1);
150 #endif
151 #if NET_2_0
152 socket.NoDelay = !sPoint.UseNagleAlgorithm;
153 if (!sPoint.CallEndPointDelegate (socket, remote)) {
154 socket.Close ();
155 socket = null;
156 status = WebExceptionStatus.ConnectFailure;
157 } else {
158 #endif
159 try {
160 if (request.Aborted)
161 return;
162 socket.Connect (remote);
163 status = WebExceptionStatus.Success;
164 break;
165 } catch (ThreadAbortException) {
166 // program exiting...
167 Socket s = socket;
168 socket = null;
169 if (s != null)
170 s.Close ();
171 return;
172 } catch (ObjectDisposedException exc) {
173 // socket closed from another thread
174 return;
175 } catch (Exception exc) {
176 Socket s = socket;
177 socket = null;
178 if (s != null)
179 s.Close ();
180 if (!request.Aborted)
181 status = WebExceptionStatus.ConnectFailure;
182 connect_exception = exc;
184 #if NET_2_0
186 #endif
191 static void EnsureSSLStreamAvailable ()
193 lock (classLock) {
194 if (sslStream != null)
195 return;
197 #if MONOTOUCH && SECURITY_DEP
198 sslStream = typeof (Mono.Security.Protocol.Tls.HttpsClientStream);
199 #else
200 // HttpsClientStream is an internal glue class in Mono.Security.dll
201 sslStream = Type.GetType ("Mono.Security.Protocol.Tls.HttpsClientStream, " +
202 Consts.AssemblyMono_Security, false);
204 if (sslStream == null) {
205 string msg = "Missing Mono.Security.dll assembly. " +
206 "Support for SSL/TLS is unavailable.";
208 throw new NotSupportedException (msg);
210 #endif
211 piClient = sslStream.GetProperty ("SelectedClientCertificate");
212 piServer = sslStream.GetProperty ("ServerCertificate");
213 piTrustFailure = sslStream.GetProperty ("TrustFailure");
217 bool CreateTunnel (HttpWebRequest request, Stream stream, out byte [] buffer)
219 StringBuilder sb = new StringBuilder ();
220 sb.Append ("CONNECT ");
221 sb.Append (request.Address.Host);
222 sb.Append (':');
223 sb.Append (request.Address.Port);
224 sb.Append (" HTTP/");
225 if (request.ServicePoint.ProtocolVersion == HttpVersion.Version11)
226 sb.Append ("1.1");
227 else
228 sb.Append ("1.0");
230 sb.Append ("\r\nHost: ");
231 sb.Append (request.Address.Authority);
232 string challenge = Data.Challenge;
233 Data.Challenge = null;
234 bool have_auth = (request.Headers ["Proxy-Authorization"] != null);
235 if (have_auth) {
236 sb.Append ("\r\nProxy-Authorization: ");
237 sb.Append (request.Headers ["Proxy-Authorization"]);
238 } else if (challenge != null && Data.StatusCode == 407) {
239 have_auth = true;
240 ICredentials creds = request.Proxy.Credentials;
241 Authorization auth = AuthenticationManager.Authenticate (challenge, request, creds);
242 if (auth != null) {
243 sb.Append ("\r\nProxy-Authorization: ");
244 sb.Append (auth.Message);
247 sb.Append ("\r\n\r\n");
249 Data.StatusCode = 0;
250 byte [] connectBytes = Encoding.Default.GetBytes (sb.ToString ());
251 stream.Write (connectBytes, 0, connectBytes.Length);
253 int status;
254 WebHeaderCollection result = ReadHeaders (request, stream, out buffer, out status);
255 if (!have_auth && result != null && status == 407) { // Needs proxy auth
256 Data.StatusCode = status;
257 Data.Challenge = result ["Proxy-Authenticate"];
258 return false;
259 } else if (status != 200) {
260 string msg = String.Format ("The remote server returned a {0} status code.", status);
261 HandleError (WebExceptionStatus.SecureChannelFailure, null, msg);
262 return false;
265 return (result != null);
268 WebHeaderCollection ReadHeaders (HttpWebRequest request, Stream stream, out byte [] retBuffer, out int status)
270 retBuffer = null;
271 status = 200;
273 byte [] buffer = new byte [1024];
274 MemoryStream ms = new MemoryStream ();
275 bool gotStatus = false;
276 WebHeaderCollection headers = null;
278 while (true) {
279 int n = stream.Read (buffer, 0, 1024);
280 if (n == 0) {
281 HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders");
282 return null;
285 ms.Write (buffer, 0, n);
286 int start = 0;
287 string str = null;
288 headers = new WebHeaderCollection ();
289 while (ReadLine (ms.GetBuffer (), ref start, (int) ms.Length, ref str)) {
290 if (str == null) {
291 if (ms.Length - start > 0) {
292 retBuffer = new byte [ms.Length - start];
293 Buffer.BlockCopy (ms.GetBuffer (), start, retBuffer, 0, retBuffer.Length);
295 return headers;
298 if (gotStatus) {
299 headers.Add (str);
300 continue;
303 int spaceidx = str.IndexOf (' ');
304 if (spaceidx == -1) {
305 HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2");
306 return null;
309 status = (int) UInt32.Parse (str.Substring (spaceidx + 1, 3));
310 gotStatus = true;
315 bool CreateStream (HttpWebRequest request)
317 try {
318 NetworkStream serverStream = new NetworkStream (socket, false);
320 if (request.Address.Scheme == Uri.UriSchemeHttps) {
321 ssl = true;
322 EnsureSSLStreamAvailable ();
323 if (!reused || nstream == null || nstream.GetType () != sslStream) {
324 byte [] buffer = null;
325 if (sPoint.UseConnect) {
326 bool ok = CreateTunnel (request, serverStream, out buffer);
327 if (!ok)
328 return false;
331 object[] args = new object [4] { serverStream,
332 request.ClientCertificates,
333 request, buffer};
334 nstream = (Stream) Activator.CreateInstance (sslStream, args);
335 certsAvailable = false;
337 // we also need to set ServicePoint.Certificate
338 // and ServicePoint.ClientCertificate but this can
339 // only be done later (after handshake - which is
340 // done only after a read operation).
341 } else {
342 ssl = false;
343 nstream = serverStream;
345 } catch (Exception) {
346 if (!request.Aborted)
347 status = WebExceptionStatus.ConnectFailure;
348 return false;
351 return true;
354 void HandleError (WebExceptionStatus st, Exception e, string where)
356 status = st;
357 lock (this) {
358 if (st == WebExceptionStatus.RequestCanceled)
359 Data = new WebConnectionData ();
362 if (e == null) { // At least we now where it comes from
363 try {
364 #if TARGET_JVM
365 throw new Exception ();
366 #else
367 throw new Exception (new System.Diagnostics.StackTrace ().ToString ());
368 #endif
369 } catch (Exception e2) {
370 e = e2;
374 HttpWebRequest req = null;
375 if (Data != null && Data.request != null)
376 req = Data.request;
378 Close (true);
379 if (req != null) {
380 req.FinishedReading = true;
381 req.SetResponseError (st, e, where);
385 static void ReadDone (IAsyncResult result)
387 WebConnection cnc = (WebConnection) result.AsyncState;
388 WebConnectionData data = cnc.Data;
389 Stream ns = cnc.nstream;
390 if (ns == null) {
391 cnc.Close (true);
392 return;
395 int nread = -1;
396 try {
397 nread = ns.EndRead (result);
398 } catch (Exception e) {
399 cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "ReadDone1");
400 return;
403 if (nread == 0) {
404 cnc.HandleError (WebExceptionStatus.ReceiveFailure, null, "ReadDone2");
405 return;
408 if (nread < 0) {
409 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadDone3");
410 return;
413 int pos = -1;
414 nread += cnc.position;
415 if (cnc.readState == ReadState.None) {
416 Exception exc = null;
417 try {
418 pos = cnc.GetResponse (cnc.buffer, nread);
419 } catch (Exception e) {
420 exc = e;
423 if (exc != null) {
424 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, exc, "ReadDone4");
425 return;
429 if (cnc.readState != ReadState.Content) {
430 int est = nread * 2;
431 int max = (est < cnc.buffer.Length) ? cnc.buffer.Length : est;
432 byte [] newBuffer = new byte [max];
433 Buffer.BlockCopy (cnc.buffer, 0, newBuffer, 0, nread);
434 cnc.buffer = newBuffer;
435 cnc.position = nread;
436 cnc.readState = ReadState.None;
437 InitRead (cnc);
438 return;
441 cnc.position = 0;
443 WebConnectionStream stream = new WebConnectionStream (cnc);
445 string contentType = data.Headers ["Transfer-Encoding"];
446 cnc.chunkedRead = (contentType != null && contentType.ToLower ().IndexOf ("chunked") != -1);
447 if (!cnc.chunkedRead) {
448 stream.ReadBuffer = cnc.buffer;
449 stream.ReadBufferOffset = pos;
450 stream.ReadBufferSize = nread;
451 stream.CheckResponseInBuffer ();
452 } else if (cnc.chunkStream == null) {
453 try {
454 cnc.chunkStream = new ChunkStream (cnc.buffer, pos, nread, data.Headers);
455 } catch (Exception e) {
456 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone5");
457 return;
459 } else {
460 cnc.chunkStream.ResetBuffer ();
461 try {
462 cnc.chunkStream.Write (cnc.buffer, pos, nread);
463 } catch (Exception e) {
464 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone6");
465 return;
469 data.stream = stream;
471 if (!ExpectContent (data.StatusCode) || data.request.Method == "HEAD")
472 stream.ForceCompletion ();
474 data.request.SetResponseData (data);
477 static bool ExpectContent (int statusCode)
479 return (statusCode >= 200 && statusCode != 204 && statusCode != 304);
482 internal void GetCertificates ()
484 // here the SSL negotiation have been done
485 X509Certificate client = (X509Certificate) piClient.GetValue (nstream, null);
486 X509Certificate server = (X509Certificate) piServer.GetValue (nstream, null);
487 sPoint.SetCertificates (client, server);
488 certsAvailable = (server != null);
491 internal static void InitRead (object state)
493 WebConnection cnc = (WebConnection) state;
494 Stream ns = cnc.nstream;
496 try {
497 int size = cnc.buffer.Length - cnc.position;
498 ns.BeginRead (cnc.buffer, cnc.position, size, readDoneDelegate, cnc);
499 } catch (Exception e) {
500 cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "InitRead");
504 int GetResponse (byte [] buffer, int max)
506 int pos = 0;
507 string line = null;
508 bool lineok = false;
509 bool isContinue = false;
510 bool emptyFirstLine = false;
511 do {
512 if (readState == ReadState.None) {
513 lineok = ReadLine (buffer, ref pos, max, ref line);
514 if (!lineok)
515 return -1;
517 if (line == null) {
518 emptyFirstLine = true;
519 continue;
521 emptyFirstLine = false;
523 readState = ReadState.Status;
525 string [] parts = line.Split (' ');
526 if (parts.Length < 2)
527 return -1;
529 if (String.Compare (parts [0], "HTTP/1.1", true) == 0) {
530 Data.Version = HttpVersion.Version11;
531 sPoint.SetVersion (HttpVersion.Version11);
532 } else {
533 Data.Version = HttpVersion.Version10;
534 sPoint.SetVersion (HttpVersion.Version10);
537 Data.StatusCode = (int) UInt32.Parse (parts [1]);
538 if (parts.Length >= 3)
539 Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
540 else
541 Data.StatusDescription = "";
543 if (pos >= max)
544 return pos;
547 emptyFirstLine = false;
548 if (readState == ReadState.Status) {
549 readState = ReadState.Headers;
550 Data.Headers = new WebHeaderCollection ();
551 ArrayList headers = new ArrayList ();
552 bool finished = false;
553 while (!finished) {
554 if (ReadLine (buffer, ref pos, max, ref line) == false)
555 break;
557 if (line == null) {
558 // Empty line: end of headers
559 finished = true;
560 continue;
563 if (line.Length > 0 && (line [0] == ' ' || line [0] == '\t')) {
564 int count = headers.Count - 1;
565 if (count < 0)
566 break;
568 string prev = (string) headers [count] + line;
569 headers [count] = prev;
570 } else {
571 headers.Add (line);
575 if (!finished)
576 return -1;
578 foreach (string s in headers)
579 Data.Headers.SetInternal (s);
581 if (Data.StatusCode == (int) HttpStatusCode.Continue) {
582 sPoint.SendContinue = true;
583 if (pos >= max)
584 return pos;
586 if (Data.request.ExpectContinue) {
587 Data.request.DoContinueDelegate (Data.StatusCode, Data.Headers);
588 // Prevent double calls when getting the
589 // headers in several packets.
590 Data.request.ExpectContinue = false;
593 readState = ReadState.None;
594 isContinue = true;
596 else {
597 readState = ReadState.Content;
598 return pos;
601 } while (emptyFirstLine || isContinue);
603 return -1;
606 void InitConnection (object state)
608 HttpWebRequest request = (HttpWebRequest) state;
609 request.WebConnection = this;
611 if (request.Aborted)
612 return;
614 keepAlive = request.KeepAlive;
615 Data = new WebConnectionData ();
616 Data.request = request;
617 retry:
618 Connect (request);
619 if (request.Aborted)
620 return;
622 if (status != WebExceptionStatus.Success) {
623 if (!request.Aborted) {
624 request.SetWriteStreamError (status, connect_exception);
625 Close (true);
627 return;
630 if (!CreateStream (request)) {
631 if (request.Aborted)
632 return;
634 WebExceptionStatus st = status;
635 if (Data.Challenge != null)
636 goto retry;
638 Exception cnc_exc = connect_exception;
639 connect_exception = null;
640 request.SetWriteStreamError (st, cnc_exc);
641 Close (true);
642 return;
645 readState = ReadState.None;
646 request.SetWriteStream (new WebConnectionStream (this, request));
649 internal EventHandler SendRequest (HttpWebRequest request)
651 if (request.Aborted)
652 return null;
654 lock (this) {
655 if (!busy) {
656 busy = true;
657 status = WebExceptionStatus.Success;
658 ThreadPool.QueueUserWorkItem (initConn, request);
659 } else {
660 lock (queue) {
661 queue.Enqueue (request);
666 return abortHandler;
669 void SendNext ()
671 lock (queue) {
672 if (queue.Count > 0) {
673 SendRequest ((HttpWebRequest) queue.Dequeue ());
678 internal void NextRead ()
680 lock (this) {
681 Data.request.FinishedReading = true;
682 string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection";
683 string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null;
684 bool keepAlive = (Data.Version == HttpVersion.Version11 && this.keepAlive);
685 if (cncHeader != null) {
686 cncHeader = cncHeader.ToLower ();
687 keepAlive = (this.keepAlive && cncHeader.IndexOf ("keep-alive") != -1);
690 if ((socket != null && !socket.Connected) ||
691 (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
692 Close (false);
695 busy = false;
696 if (priority_request != null) {
697 SendRequest (priority_request);
698 priority_request = null;
699 } else {
700 SendNext ();
705 static bool ReadLine (byte [] buffer, ref int start, int max, ref string output)
707 bool foundCR = false;
708 StringBuilder text = new StringBuilder ();
710 int c = 0;
711 while (start < max) {
712 c = (int) buffer [start++];
714 if (c == '\n') { // newline
715 if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
716 text.Length--;
718 foundCR = false;
719 break;
720 } else if (foundCR) {
721 text.Length--;
722 break;
725 if (c == '\r')
726 foundCR = true;
729 text.Append ((char) c);
732 if (c != '\n' && c != '\r')
733 return false;
735 if (text.Length == 0) {
736 output = null;
737 return (c == '\n' || c == '\r');
740 if (foundCR)
741 text.Length--;
743 output = text.ToString ();
744 return true;
748 internal IAsyncResult BeginRead (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
750 lock (this) {
751 if (Data.request != request)
752 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
753 if (nstream == null)
754 return null;
757 IAsyncResult result = null;
758 if (!chunkedRead || chunkStream.WantMore) {
759 try {
760 result = nstream.BeginRead (buffer, offset, size, cb, state);
761 cb = null;
762 } catch (Exception) {
763 HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead");
764 throw;
768 if (chunkedRead) {
769 WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size);
770 wr.InnerAsyncResult = result;
771 if (result == null) {
772 // Will be completed from the data in ChunkStream
773 wr.SetCompleted (true, (Exception) null);
774 wr.DoCallback ();
776 return wr;
779 return result;
782 internal int EndRead (HttpWebRequest request, IAsyncResult result)
784 lock (this) {
785 if (Data.request != request)
786 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
787 if (nstream == null)
788 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
791 int nbytes = 0;
792 WebAsyncResult wr = null;
793 IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult;
794 if (chunkedRead && (nsAsync is WebAsyncResult)) {
795 wr = (WebAsyncResult) nsAsync;
796 IAsyncResult inner = wr.InnerAsyncResult;
797 if (inner != null && !(inner is WebAsyncResult))
798 nbytes = nstream.EndRead (inner);
799 } else if (!(nsAsync is WebAsyncResult)) {
800 nbytes = nstream.EndRead (nsAsync);
801 wr = (WebAsyncResult) result;
804 if (chunkedRead) {
805 bool done = (nbytes == 0);
806 try {
807 chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
808 if (!done && nbytes == 0 && chunkStream.WantMore)
809 nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size);
810 } catch (Exception e) {
811 if (e is WebException)
812 throw e;
814 throw new WebException ("Invalid chunked data.", e,
815 WebExceptionStatus.ServerProtocolViolation, null);
818 if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) {
819 HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead");
820 throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
824 return (nbytes != 0) ? nbytes : -1;
827 // To be called on chunkedRead when we can read no data from the ChunkStream yet
828 int EnsureRead (byte [] buffer, int offset, int size)
830 byte [] morebytes = null;
831 int nbytes = 0;
832 while (nbytes == 0 && chunkStream.WantMore) {
833 int localsize = chunkStream.ChunkLeft;
834 if (localsize <= 0) // not read chunk size yet
835 localsize = 1024;
836 else if (localsize > 16384)
837 localsize = 16384;
839 if (morebytes == null || morebytes.Length < localsize)
840 morebytes = new byte [localsize];
842 int nread = nstream.Read (morebytes, 0, localsize);
843 if (nread <= 0)
844 return 0; // Error
846 chunkStream.Write (morebytes, 0, nread);
847 nbytes += chunkStream.Read (buffer, offset + nbytes, size - nbytes);
850 return nbytes;
853 bool CompleteChunkedRead()
855 if (!chunkedRead || chunkStream == null)
856 return true;
858 while (chunkStream.WantMore) {
859 int nbytes = nstream.Read (buffer, 0, buffer.Length);
860 if (nbytes <= 0)
861 return false; // Socket was disconnected
863 chunkStream.Write (buffer, 0, nbytes);
866 return true;
869 internal IAsyncResult BeginWrite (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
871 lock (this) {
872 if (Data.request != request)
873 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
874 if (nstream == null)
875 return null;
878 IAsyncResult result = null;
879 try {
880 result = nstream.BeginWrite (buffer, offset, size, cb, state);
881 } catch (Exception) {
882 status = WebExceptionStatus.SendFailure;
883 throw;
886 return result;
889 internal void EndWrite2 (HttpWebRequest request, IAsyncResult result)
891 if (request.FinishedReading)
892 return;
894 lock (this) {
895 if (Data.request != request)
896 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
897 if (nstream == null)
898 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
901 try {
902 nstream.EndWrite (result);
903 } catch (Exception exc) {
904 status = WebExceptionStatus.SendFailure;
905 if (exc.InnerException != null)
906 throw exc.InnerException;
907 throw;
911 internal bool EndWrite (HttpWebRequest request, IAsyncResult result)
913 if (request.FinishedReading)
914 return true;
916 lock (this) {
917 if (Data.request != request)
918 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
919 if (nstream == null)
920 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
923 try {
924 nstream.EndWrite (result);
925 return true;
926 } catch {
927 status = WebExceptionStatus.SendFailure;
928 return false;
932 internal int Read (HttpWebRequest request, byte [] buffer, int offset, int size)
934 lock (this) {
935 if (Data.request != request)
936 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
937 if (nstream == null)
938 return 0;
941 int result = 0;
942 try {
943 bool done = false;
944 if (!chunkedRead) {
945 result = nstream.Read (buffer, offset, size);
946 done = (result == 0);
949 if (chunkedRead) {
950 try {
951 chunkStream.WriteAndReadBack (buffer, offset, size, ref result);
952 if (!done && result == 0 && chunkStream.WantMore)
953 result = EnsureRead (buffer, offset, size);
954 } catch (Exception e) {
955 HandleError (WebExceptionStatus.ReceiveFailure, e, "chunked Read1");
956 throw;
959 if ((done || result == 0) && chunkStream.WantMore) {
960 HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked Read2");
961 throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
964 } catch (Exception e) {
965 HandleError (WebExceptionStatus.ReceiveFailure, e, "Read");
968 return result;
971 internal bool Write (HttpWebRequest request, byte [] buffer, int offset, int size, ref string err_msg)
973 err_msg = null;
974 lock (this) {
975 if (Data.request != request)
976 throw new ObjectDisposedException (typeof (NetworkStream).FullName);
977 if (nstream == null)
978 return false;
981 try {
982 nstream.Write (buffer, offset, size);
983 // here SSL handshake should have been done
984 if (ssl && !certsAvailable)
985 GetCertificates ();
986 } catch (Exception e) {
987 err_msg = e.Message;
988 WebExceptionStatus wes = WebExceptionStatus.SendFailure;
989 string msg = "Write: " + err_msg;
990 if (e is WebException) {
991 HandleError (wes, e, msg);
992 return false;
995 // if SSL is in use then check for TrustFailure
996 if (ssl && (bool) piTrustFailure.GetValue (nstream, null)) {
997 wes = WebExceptionStatus.TrustFailure;
998 msg = "Trust failure";
1001 HandleError (wes, e, msg);
1002 return false;
1004 return true;
1007 internal void Close (bool sendNext)
1009 lock (this) {
1010 if (nstream != null) {
1011 try {
1012 nstream.Close ();
1013 } catch {}
1014 nstream = null;
1017 if (socket != null) {
1018 try {
1019 socket.Close ();
1020 } catch {}
1021 socket = null;
1024 busy = false;
1025 Data = new WebConnectionData ();
1026 if (sendNext)
1027 SendNext ();
1031 void Abort (object sender, EventArgs args)
1033 lock (this) {
1034 lock (queue) {
1035 HttpWebRequest req = (HttpWebRequest) sender;
1036 if (Data.request == req) {
1037 if (!req.FinishedReading) {
1038 status = WebExceptionStatus.RequestCanceled;
1039 Close (false);
1040 if (queue.Count > 0) {
1041 Data.request = (HttpWebRequest) queue.Dequeue ();
1042 SendRequest (Data.request);
1045 return;
1048 req.FinishedReading = true;
1049 req.SetResponseError (WebExceptionStatus.RequestCanceled, null, "User aborted");
1050 if (queue.Count > 0 && queue.Peek () == sender) {
1051 queue.Dequeue ();
1052 } else if (queue.Count > 0) {
1053 object [] old = queue.ToArray ();
1054 queue.Clear ();
1055 for (int i = old.Length - 1; i >= 0; i--) {
1056 if (old [i] != sender)
1057 queue.Enqueue (old [i]);
1064 internal void ResetNtlm ()
1066 ntlm_authenticated = false;
1067 ntlm_credentials = null;
1068 unsafe_sharing = false;
1071 internal bool Busy {
1072 get { lock (this) return busy; }
1075 internal bool Connected {
1076 get {
1077 lock (this) {
1078 return (socket != null && socket.Connected);
1083 // -Used for NTLM authentication
1084 internal HttpWebRequest PriorityRequest {
1085 set { priority_request = value; }
1088 internal bool NtlmAuthenticated {
1089 get { return ntlm_authenticated; }
1090 set { ntlm_authenticated = value; }
1093 internal NetworkCredential NtlmCredential {
1094 get { return ntlm_credentials; }
1095 set { ntlm_credentials = value; }
1098 #if NET_1_1
1099 internal bool UnsafeAuthenticatedConnectionSharing {
1100 get { return unsafe_sharing; }
1101 set { unsafe_sharing = value; }
1103 #endif
1104 // -