2 // System.Net.WebConnection
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
10 using System.Collections;
11 using System.Net.Sockets;
13 using System.Threading;
28 NetworkStream nstream;
30 WebExceptionStatus status;
31 WebConnectionGroup group;
34 WaitOrTimerCallback initConn;
35 internal ManualResetEvent dataAvailable;
39 internal static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
40 EventHandler abortHandler;
42 internal WebConnectionData Data;
43 WebConnectionStream prevStream;
45 ChunkStream chunkStream;
46 AutoResetEvent waitForContinue;
47 bool waitingForContinue;
49 public WebConnection (WebConnectionGroup group, ServicePoint sPoint)
53 queue = new ArrayList (1);
54 dataAvailable = new ManualResetEvent (true);
55 buffer = new byte [4096];
56 readState = ReadState.None;
57 Data = new WebConnectionData ();
58 initConn = new WaitOrTimerCallback (InitConnection);
59 abortHandler = new EventHandler (Abort);
62 public void Connect ()
64 if (socket != null && socket.Connected && status == WebExceptionStatus.Success)
68 if (socket != null && socket.Connected && status == WebExceptionStatus.Success)
73 socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
75 status = sPoint.Connect (socket);
80 bool CreateStream (HttpWebRequest request)
82 //TODO: create stream for https
84 nstream = new NetworkStream (socket, false);
85 } catch (Exception e) {
86 status = WebExceptionStatus.ConnectFailure;
93 void HandleError (WebExceptionStatus st, Exception e)
97 if (e == null) { // At least we noe where it comes from
99 throw new Exception ();
100 } catch (Exception e2) {
105 if (Data != null && Data.request != null)
106 Data.request.SetResponseError (st, e);
109 internal bool WaitForContinue (byte [] headers, int offset, int size)
112 if (!sPoint.SendContinue)
115 if (waitForContinue == null)
116 waitForContinue = new AutoResetEvent (false);
118 Write (headers, offset, size);
119 waitingForContinue = true;
120 bool result = waitForContinue.WaitOne (2000, false);
121 waitingForContinue = false;
123 sPoint.SendContinue = true;
124 if (Data.request.ExpectContinue)
125 Data.request.DoContinueDelegate (Data.StatusCode, Data.Headers);
127 sPoint.SendContinue = false;
133 static void ReadDone (IAsyncResult result)
135 WebConnection cnc = (WebConnection) result.AsyncState;
136 WebConnectionData data = cnc.Data;
137 NetworkStream ns = cnc.nstream;
142 cnc.dataAvailable.Reset ();
144 nread = ns.EndRead (result);
145 } catch (Exception e) {
146 cnc.status = WebExceptionStatus.ReceiveFailure;
147 cnc.HandleError (cnc.status, e);
148 cnc.dataAvailable.Set ();
153 Console.WriteLine ("nread == 0");
158 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null);
159 cnc.dataAvailable.Set ();
163 //Console.WriteLine (System.Text.Encoding.Default.GetString (cnc.buffer, 0, nread));
165 if (cnc.readState == ReadState.None) {
166 Exception exc = null;
168 pos = cnc.GetResponse (cnc.buffer, nread);
169 if (data.StatusCode == 100) {
170 cnc.readState = ReadState.None;
172 cnc.sPoint.SendContinue = true;
173 if (cnc.waitingForContinue) {
174 cnc.waitForContinue.Set ();
175 } else if (data.request.ExpectContinue) { // We get a 100 after waiting for it.
176 data.request.DoContinueDelegate (data.StatusCode, data.Headers);
181 } catch (Exception e) {
185 if (pos == -1 || exc != null) {
186 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, exc);
187 cnc.dataAvailable.Set ();
192 if (cnc.readState != ReadState.Content) {
193 cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null);
194 cnc.dataAvailable.Set ();
198 WebConnectionStream stream = new WebConnectionStream (cnc);
200 string contentType = data.Headers ["Transfer-Encoding"];
201 cnc.chunkedRead = (contentType != null && contentType.ToLower ().IndexOf ("chunked") != -1);
202 if (!cnc.chunkedRead) {
203 stream.ReadBuffer = cnc.buffer;
204 stream.ReadBufferOffset = pos;
205 stream.ReadBufferSize = nread;
206 stream.CheckComplete ();
207 } else if (cnc.chunkStream == null) {
208 cnc.chunkStream = new ChunkStream (cnc.buffer, pos, nread, data.Headers);
210 cnc.chunkStream.ResetBuffer ();
211 cnc.chunkStream.Write (cnc.buffer, pos, nread);
214 cnc.prevStream = stream;
215 data.stream = stream;
216 data.request.SetResponseData (data);
219 static void InitRead (object state)
221 WebConnection cnc = (WebConnection) state;
222 NetworkStream ns = cnc.nstream;
224 ns.BeginRead (cnc.buffer, 0, cnc.buffer.Length, readDoneDelegate, cnc);
225 } catch (Exception e) {
226 cnc.HandleError (WebExceptionStatus.ReceiveFailure, e);
227 cnc.dataAvailable.Set ();
231 int GetResponse (byte [] buffer, int max)
237 if (readState == ReadState.None) {
238 lineok = ReadLine (buffer, ref pos, max, ref line);
242 readState = ReadState.Status;
244 string [] parts = line.Split (' ');
245 if (parts.Length < 3)
248 if (String.Compare (parts [0], "HTTP/1.1", true) == 0) {
249 Data.Version = HttpVersion.Version11;
251 Data.Version = HttpVersion.Version10;
254 Data.StatusCode = (int) UInt32.Parse (parts [1]);
255 Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
260 if (readState == ReadState.Status) {
261 readState = ReadState.Headers;
262 Data.Headers = new WebHeaderCollection ();
263 ArrayList headers = new ArrayList ();
264 bool finished = false;
266 if (ReadLine (buffer, ref pos, max, ref line) == false)
270 // Empty line: end of headers
275 if (line.Length > 0 && (line [0] == ' ' || line [0] == '\t')) {
276 int count = headers.Count - 1;
280 string prev = (string) headers [count] + line;
281 headers [count] = prev;
288 // handle the error...
290 foreach (string s in headers)
291 Data.Headers.Add (s);
293 readState = ReadState.Content;
301 void InitConnection (object state, bool notUsed)
303 HttpWebRequest request = (HttpWebRequest) state;
305 status = WebExceptionStatus.RequestCanceled;
306 request.SetWriteStreamError (status);
311 if (status != WebExceptionStatus.Success) {
312 request.SetWriteStreamError (status);
317 if (!CreateStream (request)) {
318 request.SetWriteStreamError (status);
323 readState = ReadState.None;
324 request.SetWriteStream (new WebConnectionStream (this, request));
328 void BeginRequest (HttpWebRequest request)
331 keepAlive = request.KeepAlive;
333 Data.request = request;
336 ThreadPool.RegisterWaitForSingleObject (dataAvailable, initConn, request, -1, true);
339 internal EventHandler SendRequest (HttpWebRequest request)
341 Monitor.Enter (this);
343 if (prevStream != null && socket != null && socket.Connected) {
344 prevStream.ReadAll ();
351 BeginRequest (request);
360 internal void NextRead ()
362 Monitor.Enter (this);
363 string cncHeader = (Data.Headers != null) ? Data.Headers ["Connection"] : null;
364 // Bug in xsp...? It does not send Connection: close. Well. it's 1.0
365 if (Data.Version == HttpVersion.Version10 || (socket != null && !socket.Connected) || (!keepAlive ||
366 (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
370 if (queue.Count > 0) {
371 HttpWebRequest request = (HttpWebRequest) queue [0];
374 SendRequest (request);
381 static bool ReadLine (byte [] buffer, ref int start, int max, ref string output)
383 bool foundCR = false;
384 StringBuilder text = new StringBuilder ();
387 while (start < max) {
388 c = (int) buffer [start++];
390 if (c == '\n') { // newline
391 if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
396 } else if (foundCR) {
405 text.Append ((char) c);
408 if (c != '\n' && c != '\r')
411 if (text.Length == 0) {
413 return (c == '\n' || c == '\r');
419 output = text.ToString ();
423 internal IAsyncResult BeginRead (byte [] buffer, int offset, int size, AsyncCallback cb, object state)
428 IAsyncResult result = null;
429 if (!chunkedRead || chunkStream.WantMore) {
431 result = nstream.BeginRead (buffer, offset, size, cb, state);
432 } catch (Exception e) {
433 status = WebExceptionStatus.ReceiveFailure;
439 WebAsyncResult wr = new WebAsyncResult (null, null, buffer, offset, size);
440 wr.InnerAsyncResult = result;
447 internal int EndRead (IAsyncResult result)
453 WebAsyncResult wr = (WebAsyncResult) result;
455 if (wr.InnerAsyncResult != null)
456 nbytes = nstream.EndRead (wr.InnerAsyncResult);
458 chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
462 return nstream.EndRead (result);
465 internal IAsyncResult BeginWrite (byte [] buffer, int offset, int size, AsyncCallback cb, object state)
467 IAsyncResult result = null;
472 result = nstream.BeginWrite (buffer, offset, size, cb, state);
473 } catch (Exception e) {
474 status = WebExceptionStatus.SendFailure;
481 internal void EndWrite (IAsyncResult result)
484 nstream.EndWrite (result);
487 internal int Read (byte [] buffer, int offset, int size)
494 if (!chunkedRead || chunkStream.WantMore)
495 result = nstream.Read (buffer, offset, size);
498 chunkStream.WriteAndReadBack (buffer, offset, size, ref result);
499 } catch (Exception e) {
500 status = WebExceptionStatus.ReceiveFailure;
501 HandleError (status, e);
507 internal void Write (byte [] buffer, int offset, int size)
513 nstream.Write (buffer, offset, size);
514 } catch (Exception e) {
515 status = WebExceptionStatus.SendFailure;
516 HandleError (status, e);
522 if (nstream != null) {
529 if (socket != null) {
537 void Abort (object sender, EventArgs args)
539 HandleError (WebExceptionStatus.RequestCanceled, null);