2 // System.Net.HttpConnection
5 // Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
7 // Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com)
8 // Copyright (c) 2012 Xamarin, Inc. (http://xamarin.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 #if MONO_SECURITY_ALIAS
32 extern alias MonoSecurity;
35 #if MONO_SECURITY_ALIAS
36 using MSI = MonoSecurity::Mono.Security.Interface;
38 using MSI = Mono.Security.Interface;
42 using System.Net.Sockets;
44 using System.Threading;
45 using System.Net.Security;
46 using System.Security.Authentication;
47 using System.Security.Cryptography;
48 using System.Security.Cryptography.X509Certificates;
50 namespace System.Net {
51 sealed class HttpConnection
53 static AsyncCallback onread_cb = new AsyncCallback (OnRead);
54 const int BufferSize = 8192;
60 HttpListenerContext context;
61 StringBuilder current_line;
62 ListenerPrefix prefix;
63 RequestStream i_stream;
64 ResponseStream o_stream;
70 int s_timeout = 90000; // 90k ms for first request, 15k ms from then on
73 HttpListener last_listener;
74 int [] client_cert_errors;
75 X509Certificate2 client_cert;
78 public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate cert)
84 if (secure == false) {
85 stream = new NetworkStream (sock, false);
87 ssl_stream = epl.Listener.CreateSslStream (new NetworkStream (sock, false), false, (t, c, ch, e) => {
90 var c2 = c as X509Certificate2;
92 c2 = new X509Certificate2 (c.GetRawCertData ());
94 client_cert_errors = new int[] { (int)e };
99 timer = new Timer (OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
103 internal SslStream SslStream {
104 get { return ssl_stream; }
107 internal int [] ClientCertificateErrors {
108 get { return client_cert_errors; }
111 internal X509Certificate2 ClientCertificate {
112 get { return client_cert; }
117 if (ssl_stream != null) {
118 ssl_stream.AuthenticateAsServer (cert, true, (SslProtocols)ServicePointManager.SecurityProtocol, false);
121 context_bound = false;
126 ms = new MemoryStream ();
128 input_state = InputState.RequestLine;
129 line_state = LineState.None;
130 context = new HttpListenerContext (this);
133 public bool IsClosed {
134 get { return (sock == null); }
138 get { return reuses; }
141 public IPEndPoint LocalEndPoint {
143 if (local_ep != null)
146 local_ep = (IPEndPoint) sock.LocalEndPoint;
151 public IPEndPoint RemoteEndPoint {
152 get { return (IPEndPoint) sock.RemoteEndPoint; }
155 public bool IsSecure {
156 get { return secure; }
159 public ListenerPrefix Prefix {
160 get { return prefix; }
161 set { prefix = value; }
164 void OnTimeout (object unused)
170 public void BeginReadRequest ()
173 buffer = new byte [BufferSize];
177 timer.Change (s_timeout, Timeout.Infinite);
178 stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
180 timer.Change (Timeout.Infinite, Timeout.Infinite);
186 public RequestStream GetRequestStream (bool chunked, long contentlength)
188 if (i_stream == null) {
189 byte [] buffer = ms.GetBuffer ();
190 int length = (int) ms.Length;
194 context.Response.SendChunked = true;
195 i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
197 i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
203 public ResponseStream GetResponseStream ()
205 // TODO: can we get this stream before reading the input?
206 if (o_stream == null) {
207 HttpListener listener = context.Listener;
210 return new ResponseStream (stream, context.Response, true);
212 o_stream = new ResponseStream (stream, context.Response, listener.IgnoreWriteExceptions);
217 static void OnRead (IAsyncResult ares)
219 HttpConnection cnc = (HttpConnection) ares.AsyncState;
220 cnc.OnReadInternal (ares);
223 void OnReadInternal (IAsyncResult ares)
225 timer.Change (Timeout.Infinite, Timeout.Infinite);
228 nread = stream.EndRead (ares);
229 ms.Write (buffer, 0, nread);
230 if (ms.Length > 32768) {
231 SendError ("Bad request", 400);
236 if (ms != null && ms.Length > 0)
247 // SendError (); // Why bother?
253 if (ProcessInput (ms)) {
254 if (!context.HaveError)
255 context.Request.FinishInitialization ();
257 if (context.HaveError) {
263 if (!epl.BindContext (context)) {
264 SendError ("Invalid host", 400);
268 HttpListener listener = context.Listener;
269 if (last_listener != listener) {
271 listener.AddConnection (this);
272 last_listener = listener;
275 context_bound = true;
276 listener.RegisterContext (context);
279 stream.BeginRead (buffer, 0, BufferSize, onread_cb, this);
282 void RemoveConnection ()
284 if (last_listener == null)
285 epl.RemoveConnection (this);
287 last_listener.RemoveConnection (this);
301 InputState input_state = InputState.RequestLine;
302 LineState line_state = LineState.None;
305 // true -> done processing
306 // false -> need more input
307 bool ProcessInput (MemoryStream ms)
309 byte [] buffer = ms.GetBuffer ();
310 int len = (int) ms.Length;
315 if (context.HaveError)
322 line = ReadLine (buffer, position, len - position, ref used);
325 context.ErrorMessage = "Bad request";
326 context.ErrorStatus = 400;
334 if (input_state == InputState.RequestLine)
341 if (input_state == InputState.RequestLine) {
342 context.Request.SetRequestLine (line);
343 input_state = InputState.Headers;
346 context.Request.AddHeader (line);
347 } catch (Exception e) {
348 context.ErrorMessage = e.Message;
349 context.ErrorStatus = 400;
362 string ReadLine (byte [] buffer, int offset, int len, ref int used)
364 if (current_line == null)
365 current_line = new StringBuilder (128);
366 int last = offset + len;
368 for (int i = offset; i < last && line_state != LineState.LF; i++) {
372 line_state = LineState.CR;
373 } else if (b == 10) {
374 line_state = LineState.LF;
376 current_line.Append ((char) b);
380 string result = null;
381 if (line_state == LineState.LF) {
382 line_state = LineState.None;
383 result = current_line.ToString ();
384 current_line.Length = 0;
390 public void SendError (string msg, int status)
393 HttpListenerResponse response = context.Response;
394 response.StatusCode = status;
395 response.ContentType = "text/html";
396 string description = HttpListenerResponseHelper.GetStatusDescription (status);
399 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
401 str = String.Format ("<h1>{0}</h1>", description);
403 byte [] error = context.Response.ContentEncoding.GetBytes (str);
404 response.Close (error, false);
406 // response was already closed
410 public void SendError ()
412 SendError (context.ErrorMessage, context.ErrorStatus);
418 epl.UnbindContext (context);
419 context_bound = false;
442 internal void Close (bool force_close)
445 Stream st = GetResponseStream ();
453 force_close |= !context.Request.KeepAlive;
455 force_close = (context.Response.Headers ["connection"] == "close");
458 // bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
459 // status_code == 413 || status_code == 414 || status_code == 500 ||
460 // status_code == 503);
462 force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
466 if (!force_close && context.Request.FlushInput ()) {
467 if (chunked && context.Response.ForceCloseChunked == false) {
468 // Don't close. Keep working.
487 s.Shutdown (SocketShutdown.Both);