Merge pull request #555 from jack-pappas/sigaltstack-patch
[mono.git] / mcs / class / System / System.Net / HttpConnection.cs
1 //
2 // System.Net.HttpConnection
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
6 //
7 // Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
8 // Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
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
30 #if SECURITY_DEP
31
32 extern alias MonoSecurity;
33
34 using System.IO;
35 using System.Net.Sockets;
36 using System.Reflection;
37 using System.Text;
38 using System.Threading;
39 using System.Security.Cryptography;
40 using System.Security.Cryptography.X509Certificates;
41 using MonoSecurity::Mono.Security.Protocol.Tls;
42
43 namespace System.Net {
44         sealed class HttpConnection
45         {
46                 static AsyncCallback onread_cb = new AsyncCallback (OnRead);
47                 const int BufferSize = 8192;
48                 Socket sock;
49                 Stream stream;
50                 EndPointListener epl;
51                 MemoryStream ms;
52                 byte [] buffer;
53                 HttpListenerContext context;
54                 StringBuilder current_line;
55                 ListenerPrefix prefix;
56                 RequestStream i_stream;
57                 ResponseStream o_stream;
58                 bool chunked;
59                 int reuses;
60                 bool context_bound;
61                 bool secure;
62                 AsymmetricAlgorithm key;
63                 int s_timeout = 90000; // 90k ms for first request, 15k ms from then on
64                 Timer timer;
65                 IPEndPoint local_ep;
66                 HttpListener last_listener;
67                 int [] client_cert_errors;
68                 X509Certificate2 client_cert;
69
70                 public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
71                 {
72                         this.sock = sock;
73                         this.epl = epl;
74                         this.secure = secure;
75                         this.key = key;
76                         if (secure == false) {
77                                 stream = new NetworkStream (sock, false);
78                         } else {
79                                 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, true, false);
80                                 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
81                                 ssl_stream.ClientCertValidationDelegate += OnClientCertificateValidation;
82                                 stream = ssl_stream;
83                         }
84                         timer = new Timer (OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
85                         Init ();
86                 }
87
88                 internal int [] ClientCertificateErrors {
89                         get { return client_cert_errors; }
90                 }
91
92                 internal X509Certificate2 ClientCertificate {
93                         get { return client_cert; }
94                 }
95
96                 bool OnClientCertificateValidation (X509Certificate certificate, int[] errors)
97                 {
98                         if (certificate == null)
99                                 return true;
100                         X509Certificate2 cert = certificate as X509Certificate2;
101                         if (cert == null)
102                                 cert = new X509Certificate2 (certificate.GetRawCertData ());
103                         client_cert = cert;
104                         client_cert_errors = errors;
105                         return true;
106                 }
107
108                 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
109                 {
110                         return key;
111                 }
112
113                 void Init ()
114                 {
115                         context_bound = false;
116                         i_stream = null;
117                         o_stream = null;
118                         prefix = null;
119                         chunked = false;
120                         ms = new MemoryStream ();
121                         position = 0;
122                         input_state = InputState.RequestLine;
123                         line_state = LineState.None;
124                         context = new HttpListenerContext (this);
125                 }
126
127                 public bool IsClosed {
128                         get { return (sock == null); }
129                 }
130
131                 public int Reuses {
132                         get { return reuses; }
133                 }
134
135                 public IPEndPoint LocalEndPoint {
136                         get {
137                                 if (local_ep != null)
138                                         return local_ep;
139
140                                 local_ep = (IPEndPoint) sock.LocalEndPoint;
141                                 return local_ep;
142                         }
143                 }
144
145                 public IPEndPoint RemoteEndPoint {
146                         get { return (IPEndPoint) sock.RemoteEndPoint; }
147                 }
148
149                 public bool IsSecure {
150                         get { return secure; }
151                 }
152
153                 public ListenerPrefix Prefix {
154                         get { return prefix; }
155                         set { prefix = value; }
156                 }
157
158                 void OnTimeout (object unused)
159                 {
160                         CloseSocket ();
161                         Unbind ();
162                 }
163
164                 public void BeginReadRequest ()
165                 {
166                         if (buffer == null)
167                                 buffer = new byte [BufferSize];
168                         try {
169                                 if (reuses == 1)
170                                         s_timeout = 15000;
171                                 timer.Change (s_timeout, Timeout.Infinite);
172                                 stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
173                         } catch {
174                                 timer.Change (Timeout.Infinite, Timeout.Infinite);
175                                 CloseSocket ();
176                                 Unbind ();
177                         }
178                 }
179
180                 public RequestStream GetRequestStream (bool chunked, long contentlength)
181                 {
182                         if (i_stream == null) {
183                                 byte [] buffer = ms.GetBuffer ();
184                                 int length = (int) ms.Length;
185                                 ms = null;
186                                 if (chunked) {
187                                         this.chunked = true;
188                                         context.Response.SendChunked = true;
189                                         i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
190                                 } else {
191                                         i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
192                                 }
193                         }
194                         return i_stream;
195                 }
196
197                 public ResponseStream GetResponseStream ()
198                 {
199                         // TODO: can we get this stream before reading the input?
200                         if (o_stream == null) {
201                                 HttpListener listener = context.Listener;
202                                 bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
203                                 o_stream = new ResponseStream (stream, context.Response, ign);
204                         }
205                         return o_stream;
206                 }
207
208                 static void OnRead (IAsyncResult ares)
209                 {
210                         HttpConnection cnc = (HttpConnection) ares.AsyncState;
211                         cnc.OnReadInternal (ares);
212                 }
213
214                 void OnReadInternal (IAsyncResult ares)
215                 {
216                         timer.Change (Timeout.Infinite, Timeout.Infinite);
217                         int nread = -1;
218                         try {
219                                 nread = stream.EndRead (ares);
220                                 ms.Write (buffer, 0, nread);
221                                 if (ms.Length > 32768) {
222                                         SendError ("Bad request", 400);
223                                         Close (true);
224                                         return;
225                                 }
226                         } catch {
227                                 if (ms != null && ms.Length > 0)
228                                         SendError ();
229                                 if (sock != null) {
230                                         CloseSocket ();
231                                         Unbind ();
232                                 }
233                                 return;
234                         }
235
236                         if (nread == 0) {
237                                 //if (ms.Length > 0)
238                                 //      SendError (); // Why bother?
239                                 CloseSocket ();
240                                 Unbind ();
241                                 return;
242                         }
243
244                         if (ProcessInput (ms)) {
245                                 if (!context.HaveError)
246                                         context.Request.FinishInitialization ();
247
248                                 if (context.HaveError) {
249                                         SendError ();
250                                         Close (true);
251                                         return;
252                                 }
253
254                                 if (!epl.BindContext (context)) {
255                                         SendError ("Invalid host", 400);
256                                         Close (true);
257                                         return;
258                                 }
259                                 HttpListener listener = context.Listener;
260                                 if (last_listener != listener) {
261                                         RemoveConnection ();
262                                         listener.AddConnection (this);
263                                         last_listener = listener;
264                                 }
265
266                                 context_bound = true;
267                                 listener.RegisterContext (context);
268                                 return;
269                         }
270                         stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
271                 }
272
273                 void RemoveConnection ()
274                 {
275                         if (last_listener == null)
276                                 epl.RemoveConnection (this);
277                         else
278                                 last_listener.RemoveConnection (this);
279                 }
280
281                 enum InputState {
282                         RequestLine,
283                         Headers
284                 }
285
286                 enum LineState {
287                         None,
288                         CR,
289                         LF
290                 }
291
292                 InputState input_state = InputState.RequestLine;
293                 LineState line_state = LineState.None;
294                 int position;
295
296                 // true -> done processing
297                 // false -> need more input
298                 bool ProcessInput (MemoryStream ms)
299                 {
300                         byte [] buffer = ms.GetBuffer ();
301                         int len = (int) ms.Length;
302                         int used = 0;
303                         string line;
304
305                         try {
306                                 line = ReadLine (buffer, position, len - position, ref used);
307                                 position += used;
308                         } catch {
309                                 context.ErrorMessage = "Bad request";
310                                 context.ErrorStatus = 400;
311                                 return true;
312                         }
313
314                         do {
315                                 if (line == null)
316                                         break;
317                                 if (line == "") {
318                                         if (input_state == InputState.RequestLine)
319                                                 continue;
320                                         current_line = null;
321                                         ms = null;
322                                         return true;
323                                 }
324
325                                 if (input_state == InputState.RequestLine) {
326                                         context.Request.SetRequestLine (line);
327                                         input_state = InputState.Headers;
328                                 } else {
329                                         try {
330                                                 context.Request.AddHeader (line);
331                                         } catch (Exception e) {
332                                                 context.ErrorMessage = e.Message;
333                                                 context.ErrorStatus = 400;
334                                                 return true;
335                                         }
336                                 }
337
338                                 if (context.HaveError)
339                                         return true;
340
341                                 if (position >= len)
342                                         break;
343                                 try {
344                                         line = ReadLine (buffer, position, len - position, ref used);
345                                         position += used;
346                                 } catch {
347                                         context.ErrorMessage = "Bad request";
348                                         context.ErrorStatus = 400;
349                                         return true;
350                                 }
351                         } while (line != null);
352
353                         if (used == len) {
354                                 ms.SetLength (0);
355                                 position = 0;
356                         }
357                         return false;
358                 }
359
360                 string ReadLine (byte [] buffer, int offset, int len, ref int used)
361                 {
362                         if (current_line == null)
363                                 current_line = new StringBuilder (128);
364                         int last = offset + len;
365                         used = 0;
366                         for (int i = offset; i < last && line_state != LineState.LF; i++) {
367                                 used++;
368                                 byte b = buffer [i];
369                                 if (b == 13) {
370                                         line_state = LineState.CR;
371                                 } else if (b == 10) {
372                                         line_state = LineState.LF;
373                                 } else {
374                                         current_line.Append ((char) b);
375                                 }
376                         }
377
378                         string result = null;
379                         if (line_state == LineState.LF) {
380                                 line_state = LineState.None;
381                                 result = current_line.ToString ();
382                                 current_line.Length = 0;
383                         }
384
385                         return result;
386                 }
387
388                 public void SendError (string msg, int status)
389                 {
390                         try {
391                                 HttpListenerResponse response = context.Response;
392                                 response.StatusCode = status;
393                                 response.ContentType = "text/html";
394                                 string description = HttpListenerResponse.GetStatusDescription (status);
395                                 string str;
396                                 if (msg != null)
397                                         str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
398                                 else
399                                         str = String.Format ("<h1>{0}</h1>", description);
400
401                                 byte [] error = context.Response.ContentEncoding.GetBytes (str);
402                                 response.Close (error, false);
403                         } catch {
404                                 // response was already closed
405                         }
406                 }
407
408                 public void SendError ()
409                 {
410                         SendError (context.ErrorMessage, context.ErrorStatus);
411                 }
412
413                 void Unbind ()
414                 {
415                         if (context_bound) {
416                                 epl.UnbindContext (context);
417                                 context_bound = false;
418                         }
419                 }
420
421                 public void Close ()
422                 {
423                         Close (false);
424                 }
425
426                 void CloseSocket ()
427                 {
428                         if (sock == null)
429                                 return;
430
431                         try {
432                                 sock.Close ();
433                         } catch {
434                         } finally {
435                                 sock = null;
436                         }
437                         RemoveConnection ();
438                 }
439
440                 internal void Close (bool force_close)
441                 {
442                         if (sock != null) {
443                                 Stream st = GetResponseStream ();
444                                 if (st != null)
445                                         st.Close ();
446
447                                 o_stream = null;
448                         }
449
450                         if (sock != null) {
451                                 force_close |= !context.Request.KeepAlive;
452                                 if (!force_close)
453                                         force_close = (context.Response.Headers ["connection"] == "close");
454                                 /*
455                                 if (!force_close) {
456 //                                      bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
457 //                                                      status_code == 413 || status_code == 414 || status_code == 500 ||
458 //                                                      status_code == 503);
459
460                                         force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
461                                 }
462                                 */
463
464                                 if (!force_close && context.Request.FlushInput ()) {
465                                         if (chunked && context.Response.ForceCloseChunked == false) {
466                                                 // Don't close. Keep working.
467                                                 reuses++;
468                                                 Unbind ();
469                                                 Init ();
470                                                 BeginReadRequest ();
471                                                 return;
472                                         }
473
474                                         reuses++;
475                                         Unbind ();
476                                         Init ();
477                                         BeginReadRequest ();
478                                         return;
479                                 }
480
481                                 Socket s = sock;
482                                 sock = null;
483                                 try {
484                                         if (s != null)
485                                                 s.Shutdown (SocketShutdown.Both);
486                                 } catch {
487                                 } finally {
488                                         if (s != null)
489                                                 s.Close ();
490                                 }
491                                 Unbind ();
492                                 RemoveConnection ();
493                                 return;
494                         }
495                 }
496         }
497 }
498 #endif
499