FtpWebRequest is always 2.0, remove unnecessary ifdefs
[mono.git] / mcs / class / System / System.Net / HttpConnection.cs
1 //
2 // System.Net.HttpConnection
3 //
4 // Author:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if NET_2_0 && SECURITY_DEP
30
31 using System.IO;
32 using System.Net.Sockets;
33 using System.Reflection;
34 using System.Text;
35 using System.Security.Cryptography;
36 using System.Security.Cryptography.X509Certificates;
37 using Mono.Security.Protocol.Tls;
38
39 namespace System.Net {
40         sealed class HttpConnection
41         {
42                 const int BufferSize = 8192;
43                 Socket sock;
44                 Stream stream;
45                 EndPointListener epl;
46                 MemoryStream ms;
47                 byte [] buffer;
48                 HttpListenerContext context;
49                 StringBuilder current_line;
50                 ListenerPrefix prefix;
51                 RequestStream i_stream;
52                 ResponseStream o_stream;
53                 bool chunked;
54                 int chunked_uses;
55                 bool context_bound;
56                 bool secure;
57                 X509Certificate2 cert;
58                 AsymmetricAlgorithm key;
59
60                 public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
61                 {
62                         this.sock = sock;
63                         this.epl = epl;
64                         this.secure = secure;
65                         this.cert = cert;
66                         this.key = key;
67                         if (secure == false) {
68                                 stream = new NetworkStream (sock, false);
69                         } else {
70                                 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
71                                 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
72                                 stream = ssl_stream;
73                         }
74                         Init ();
75                 }
76
77                 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
78                 {
79                         return key;
80                 }
81
82
83                 void Init ()
84                 {
85                         context_bound = false;
86                         i_stream = null;
87                         o_stream = null;
88                         prefix = null;
89                         chunked = false;
90                         ms = new MemoryStream ();
91                         position = 0;
92                         input_state = InputState.RequestLine;
93                         line_state = LineState.None;
94                         context = new HttpListenerContext (this);
95                 }
96
97                 public int ChunkedUses {
98                         get { return chunked_uses; }
99                 }
100
101                 public IPEndPoint LocalEndPoint {
102                         get { return (IPEndPoint) sock.LocalEndPoint; }
103                 }
104
105                 public IPEndPoint RemoteEndPoint {
106                         get { return (IPEndPoint) sock.RemoteEndPoint; }
107                 }
108
109                 public bool IsSecure {
110                         get { return secure; }
111                 }
112
113                 public ListenerPrefix Prefix {
114                         get { return prefix; }
115                         set { prefix = value; }
116                 }
117
118                 public void BeginReadRequest ()
119                 {
120                         if (buffer == null)
121                                 buffer = new byte [BufferSize];
122                         stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
123                 }
124
125                 public RequestStream GetRequestStream (bool chunked, long contentlength)
126                 {
127                         if (i_stream == null) {
128                                 byte [] buffer = ms.GetBuffer ();
129                                 int length = (int) ms.Length;
130                                 ms = null;
131                                 if (chunked) {
132                                         this.chunked = true;
133                                         context.Response.SendChunked = true;
134                                         i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
135                                 } else {
136                                         i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
137                                 }
138                         }
139                         return i_stream;
140                 }
141
142                 public ResponseStream GetResponseStream ()
143                 {
144                         // TODO: can we get this stream before reading the input?
145                         if (o_stream == null) {
146                                 HttpListener listener = context.Listener;
147                                 bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
148                                 o_stream = new ResponseStream (stream, context.Response, ign);
149                         }
150                         return o_stream;
151                 }
152
153                 void OnRead (IAsyncResult ares)
154                 {
155                         // TODO: set a limit on ms length.
156                         HttpConnection cnc = (HttpConnection) ares.AsyncState;
157                         int nread = -1;
158                         try {
159                                 nread = stream.EndRead (ares);
160                                 ms.Write (buffer, 0, nread);
161                         } catch (Exception e) {
162                                 Console.WriteLine (e);
163                                 if (ms.Length > 0)
164                                         SendError ();
165                                 sock.Close ();
166                                 return;
167                         }
168
169                         if (nread == 0) {
170                                 //if (ms.Length > 0)
171                                 //      SendError (); // Why bother?
172                                 sock.Close ();
173                                 return;
174                         }
175
176                         if (ProcessInput (ms)) {
177                                 if (!context.HaveError)
178                                         context.Request.FinishInitialization ();
179
180                                 if (context.HaveError) {
181                                         SendError ();
182                                         Close ();
183                                         return;
184                                 }
185
186                                 if (!epl.BindContext (context)) {
187                                         SendError ("Invalid host", 400);
188                                         Close ();
189                                 }
190                                 context_bound = true;
191                                 return;
192                         }
193                         stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
194                 }
195
196                 enum InputState {
197                         RequestLine,
198                         Headers
199                 }
200
201                 enum LineState {
202                         None,
203                         CR,
204                         LF
205                 }
206
207                 InputState input_state = InputState.RequestLine;
208                 LineState line_state = LineState.None;
209                 int position;
210
211                 // true -> done processing
212                 // false -> need more input
213                 bool ProcessInput (MemoryStream ms)
214                 {
215                         byte [] buffer = ms.GetBuffer ();
216                         int len = (int) ms.Length;
217                         int used = 0;
218                         string line;
219                         while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
220                                 position += used;
221                                 if (line == "") {
222                                         if (input_state == InputState.RequestLine)
223                                                 continue;
224                                         current_line = null;
225                                         ms = null;
226                                         return true;
227                                 }
228
229                                 if (input_state == InputState.RequestLine) {
230                                         context.Request.SetRequestLine (line);
231                                         input_state = InputState.Headers;
232                                 } else {
233                                         context.Request.AddHeader (line);
234                                 }
235
236                                 if (context.HaveError)
237                                         return true;
238
239                                 if (position >= len)
240                                         break;
241                         }
242
243                         if (used == len) {
244                                 ms.SetLength (0);
245                                 position = 0;
246                         }
247                         return false;
248                 }
249
250                 string ReadLine (byte [] buffer, int offset, int len, ref int used)
251                 {
252                         if (current_line == null)
253                                 current_line = new StringBuilder ();
254                         int last = offset + len;
255                         used = 0;
256                         for (int i = offset; i < last && line_state != LineState.LF; i++) {
257                                 used++;
258                                 byte b = buffer [i];
259                                 if (b == 13) {
260                                         line_state = LineState.CR;
261                                 } else if (b == 10) {
262                                         line_state = LineState.LF;
263                                 } else {
264                                         current_line.Append ((char) b);
265                                 }
266                         }
267
268                         string result = null;
269                         if (line_state == LineState.LF) {
270                                 line_state = LineState.None;
271                                 result = current_line.ToString ();
272                                 current_line.Length = 0;
273                         }
274
275                         return result;
276                 }
277
278                 public void SendError (string msg, int status)
279                 {
280                         HttpListenerResponse response = context.Response;
281                         response.StatusCode = status;
282                         response.ContentType = "text/html";
283                         string description = HttpListenerResponse.GetStatusDescription (status);
284                         string str;
285                         if (msg != null)
286                                 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
287                         else
288                                 str = String.Format ("<h1>{0}</h1>", description);
289
290                         byte [] error = context.Response.ContentEncoding.GetBytes (str);
291                         response.Close (error, false);
292                 }
293
294                 public void SendError ()
295                 {
296                         SendError (context.ErrorMessage, context.ErrorStatus);
297                 }
298
299                 public void Close ()
300                 {
301                         if (o_stream != null) {
302                                 Stream st = o_stream;
303                                 st.Close ();
304                                 o_stream = null;
305                         }
306
307                         if (sock != null) {
308                                 if (chunked && context.Response.ForceCloseChunked == false) {
309                                         // Don't close. Keep working.
310                                         chunked_uses++;
311                                         Init ();
312                                         BeginReadRequest ();
313                                         return;
314                                 }
315
316                                 Socket s = sock;
317                                 sock = null;
318                                 s.Shutdown (SocketShutdown.Both);
319                                 s.Close ();
320                                 if (context_bound)
321                                         epl.UnbindContext (context);
322                         }
323                 }
324         }
325 }
326 #endif
327