* Makefile: Add a System.Core.dll reference; embed monodoc.xml as a
[mono.git] / mcs / class / System.Runtime.Remoting / MonoHttp / HttpConnection.cs
1 #define EMBEDDED_IN_1_0
2
3 //
4 // System.Net.HttpConnection
5 //
6 // Author:
7 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //
9 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
10 //
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.
29 //
30
31 #if EMBEDDED_IN_1_0
32
33 using System.IO;
34 using System.Net.Sockets;
35 using System.Reflection;
36 using System.Text;
37 using System.Security.Cryptography;
38 using System.Security.Cryptography.X509Certificates;
39 #if !EMBEDDED_IN_1_0
40 using Mono.Security.Protocol.Tls;
41 #endif
42
43 using System; using System.Net; namespace MonoHttp {
44
45         interface IHttpListenerContextBinder {
46                 bool BindContext (HttpListenerContext context);
47                 void UnbindContext (HttpListenerContext context);
48         }
49
50         sealed class HttpConnection
51         {
52                 const int BufferSize = 8192;
53                 Socket sock;
54                 Stream stream;
55                 IHttpListenerContextBinder epl;
56                 MemoryStream ms;
57                 byte [] buffer;
58                 HttpListenerContext context;
59                 StringBuilder current_line;
60                 ListenerPrefix prefix;
61                 RequestStream i_stream;
62                 ResponseStream o_stream;
63                 bool chunked;
64                 int chunked_uses;
65                 bool context_bound;
66                 bool secure;
67                 AsymmetricAlgorithm key;
68
69 #if EMBEDDED_IN_1_0
70                 public HttpConnection (Socket sock, IHttpListenerContextBinder epl)
71                 {
72                         this.sock = sock;
73                         this.epl = epl;
74                         stream = new NetworkStream (sock, false);
75                         Init ();
76                 }
77 #else
78                 public HttpConnection (Socket sock, IHttpListenerContextBinder epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
79                 {
80                         this.sock = sock;
81                         this.epl = epl;
82                         this.secure = secure;
83                         this.key = key;
84                         if (secure == false) {
85                                 stream = new NetworkStream (sock, false);
86                         } else {
87 #if EMBEDDED_IN_1_0
88                                 throw new NotImplementedException ();
89 #else
90                                 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
91                                 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
92                                 stream = ssl_stream;
93 #endif
94                         }
95                         Init ();
96                 }
97 #endif
98
99                 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
100                 {
101                         return key;
102                 }
103
104
105                 void Init ()
106                 {
107                         context_bound = false;
108                         i_stream = null;
109                         o_stream = null;
110                         prefix = null;
111                         chunked = false;
112                         ms = new MemoryStream ();
113                         position = 0;
114                         input_state = InputState.RequestLine;
115                         line_state = LineState.None;
116                         context = new HttpListenerContext (this);
117                 }
118
119                 public int ChunkedUses {
120                         get { return chunked_uses; }
121                 }
122
123                 public IPEndPoint LocalEndPoint {
124                         get { return (IPEndPoint) sock.LocalEndPoint; }
125                 }
126
127                 public IPEndPoint RemoteEndPoint {
128                         get { return (IPEndPoint) sock.RemoteEndPoint; }
129                 }
130
131                 public bool IsSecure {
132                         get { return secure; }
133                 }
134
135                 public ListenerPrefix Prefix {
136                         get { return prefix; }
137                         set { prefix = value; }
138                 }
139
140                 public void BeginReadRequest ()
141                 {
142                         if (buffer == null)
143                                 buffer = new byte [BufferSize];
144                         try {
145                                 stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
146                         } catch {
147                                 sock.Close (); // stream disposed
148                         }
149                 }
150
151                 public RequestStream GetRequestStream (bool chunked, long contentlength)
152                 {
153                         if (i_stream == null) {
154                                 byte [] buffer = ms.GetBuffer ();
155                                 int length = (int) ms.Length;
156                                 ms = null;
157                                 if (chunked) {
158                                         this.chunked = true;
159                                         context.Response.SendChunked = true;
160                                         i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
161                                 } else {
162                                         i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
163                                 }
164                         }
165                         return i_stream;
166                 }
167
168                 public ResponseStream GetResponseStream ()
169                 {
170                         // TODO: can we get this stream before reading the input?
171                         if (o_stream == null) {
172                                 HttpListener listener = context.Listener;
173                                 bool ign = false;// ? true : listener.IgnoreWriteExceptions;
174                                 o_stream = new ResponseStream (stream, context.Response, ign);
175                         }
176                         return o_stream;
177                 }
178
179                 void OnRead (IAsyncResult ares)
180                 {
181                         // TODO: set a limit on ms length.
182                         HttpConnection cnc = (HttpConnection) ares.AsyncState;
183                         int nread = -1;
184                         try {
185                                 nread = stream.EndRead (ares);
186                                 ms.Write (buffer, 0, nread);
187                         } catch (Exception e) {
188                                 //Console.WriteLine (e);
189                                 if (ms.Length > 0)
190                                         SendError ();
191                                 sock.Close ();
192                                 return;
193                         }
194
195                         if (nread == 0) {
196                                 //if (ms.Length > 0)
197                                 //      SendError (); // Why bother?
198                                 sock.Close ();
199                                 return;
200                         }
201
202                         if (ProcessInput (ms)) {
203                                 if (!context.HaveError)
204                                         context.Request.FinishInitialization ();
205
206                                 if (context.HaveError) {
207                                         SendError ();
208                                         Close ();
209                                         return;
210                                 }
211
212                                 if (!epl.BindContext (context)) {
213                                         SendError ("Invalid host", 400);
214                                         Close ();
215                                 }
216                                 context_bound = true;
217                                 return;
218                         }
219                         stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
220                 }
221
222                 enum InputState {
223                         RequestLine,
224                         Headers
225                 }
226
227                 enum LineState {
228                         None,
229                         CR,
230                         LF
231                 }
232
233                 InputState input_state = InputState.RequestLine;
234                 LineState line_state = LineState.None;
235                 int position;
236
237                 // true -> done processing
238                 // false -> need more input
239                 bool ProcessInput (MemoryStream ms)
240                 {
241                         byte [] buffer = ms.GetBuffer ();
242                         int len = (int) ms.Length;
243                         int used = 0;
244                         string line;
245                         while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
246                                 position += used;
247                                 if (line == "") {
248                                         if (input_state == InputState.RequestLine)
249                                                 continue;
250                                         current_line = null;
251                                         ms = null;
252                                         return true;
253                                 }
254
255                                 if (input_state == InputState.RequestLine) {
256                                         context.Request.SetRequestLine (line);
257                                         input_state = InputState.Headers;
258                                 } else {
259                                         context.Request.AddHeader (line);
260                                 }
261
262                                 if (context.HaveError)
263                                         return true;
264
265                                 if (position >= len)
266                                         break;
267                         }
268
269                         if (used == len) {
270                                 ms.SetLength (0);
271                                 position = 0;
272                         }
273                         return false;
274                 }
275
276                 string ReadLine (byte [] buffer, int offset, int len, ref int used)
277                 {
278                         if (current_line == null)
279                                 current_line = new StringBuilder ();
280                         int last = offset + len;
281                         used = 0;
282                         for (int i = offset; i < last && line_state != LineState.LF; i++) {
283                                 used++;
284                                 byte b = buffer [i];
285                                 if (b == 13) {
286                                         line_state = LineState.CR;
287                                 } else if (b == 10) {
288                                         line_state = LineState.LF;
289                                 } else {
290                                         current_line.Append ((char) b);
291                                 }
292                         }
293
294                         string result = null;
295                         if (line_state == LineState.LF) {
296                                 line_state = LineState.None;
297                                 result = current_line.ToString ();
298                                 current_line.Length = 0;
299                         }
300
301                         return result;
302                 }
303
304                 public void SendError (string msg, int status)
305                 {
306                         HttpListenerResponse response = context.Response;
307                         response.StatusCode = status;
308                         response.ContentType = "text/html";
309                         string description = HttpListenerResponse.GetStatusDescription (status);
310                         string str;
311                         if (msg != null)
312                                 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
313                         else
314                                 str = String.Format ("<h1>{0}</h1>", description);
315
316                         byte [] error = context.Response.ContentEncoding.GetBytes (str);
317                         response.Close (error, false);
318                 }
319
320                 public void SendError ()
321                 {
322                         SendError (context.ErrorMessage, context.ErrorStatus);
323                 }
324
325                 void Unbind ()
326                 {
327                         if (context_bound) {
328                                 epl.UnbindContext (context);
329                                 context_bound = false;
330                         }
331                 }
332
333                 public void Close ()
334                 {
335                         Close (false);
336                 }
337
338                 internal void Close (bool force_close)
339                 {
340                         if (sock != null) {
341                                 Stream st = GetResponseStream ();
342                                 st.Close ();
343                                 o_stream = null;
344                         }
345
346                         if (sock != null) {
347                                 if (!force_close && chunked && context.Response.ForceCloseChunked == false) {
348                                         // Don't close. Keep working.
349                                         chunked_uses++;
350                                         Unbind ();
351                                         Init ();
352                                         BeginReadRequest ();
353                                         return;
354                                 }
355
356                                 if (force_close || context.Response.Headers ["connection"] == "close") {
357                                         Socket s = sock;
358                                         sock = null;
359                                         try {
360                                                 s.Shutdown (SocketShutdown.Both);
361                                         } catch {
362                                         } finally {
363                                                 s.Close ();
364                                         }
365                                         Unbind ();
366                                 } else {
367                                         Unbind ();
368                                         Init ();
369                                         BeginReadRequest ();
370                                         return;
371                                 }
372                         }
373                 }
374         }
375 }
376 #endif
377