2 // System.Net.HttpConnection
5 // Gonzalo Paniagua Javier (gonzalo@novell.com)
7 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 #if NET_2_0 && SECURITY_DEP
32 using System.Net.Sockets;
33 using System.Reflection;
35 using System.Security.Cryptography;
36 using System.Security.Cryptography.X509Certificates;
37 using Mono.Security.Protocol.Tls;
39 namespace System.Net {
40 sealed class HttpConnection
42 const int BufferSize = 8192;
48 HttpListenerContext context;
49 StringBuilder current_line;
50 ListenerPrefix prefix;
51 RequestStream i_stream;
52 ResponseStream o_stream;
57 AsymmetricAlgorithm key;
59 public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
65 if (secure == false) {
66 stream = new NetworkStream (sock, false);
69 throw new NotImplementedException ();
71 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
72 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
79 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
87 context_bound = false;
92 ms = new MemoryStream ();
94 input_state = InputState.RequestLine;
95 line_state = LineState.None;
96 context = new HttpListenerContext (this);
99 public int ChunkedUses {
100 get { return chunked_uses; }
103 public IPEndPoint LocalEndPoint {
104 get { return (IPEndPoint) sock.LocalEndPoint; }
107 public IPEndPoint RemoteEndPoint {
108 get { return (IPEndPoint) sock.RemoteEndPoint; }
111 public bool IsSecure {
112 get { return secure; }
115 public ListenerPrefix Prefix {
116 get { return prefix; }
117 set { prefix = value; }
120 public void BeginReadRequest ()
123 buffer = new byte [BufferSize];
125 stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
127 sock.Close (); // stream disposed
131 public RequestStream GetRequestStream (bool chunked, long contentlength)
133 if (i_stream == null) {
134 byte [] buffer = ms.GetBuffer ();
135 int length = (int) ms.Length;
139 context.Response.SendChunked = true;
140 i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
142 i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
148 public ResponseStream GetResponseStream ()
150 // TODO: can we get this stream before reading the input?
151 if (o_stream == null) {
152 HttpListener listener = context.Listener;
153 bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
154 o_stream = new ResponseStream (stream, context.Response, ign);
159 void OnRead (IAsyncResult ares)
161 // TODO: set a limit on ms length.
162 HttpConnection cnc = (HttpConnection) ares.AsyncState;
165 nread = stream.EndRead (ares);
166 ms.Write (buffer, 0, nread);
167 } catch (Exception e) {
168 //Console.WriteLine (e);
177 // SendError (); // Why bother?
182 if (ProcessInput (ms)) {
183 if (!context.HaveError)
184 context.Request.FinishInitialization ();
186 if (context.HaveError) {
192 if (!epl.BindContext (context)) {
193 SendError ("Invalid host", 400);
196 context_bound = true;
199 stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
213 InputState input_state = InputState.RequestLine;
214 LineState line_state = LineState.None;
217 // true -> done processing
218 // false -> need more input
219 bool ProcessInput (MemoryStream ms)
221 byte [] buffer = ms.GetBuffer ();
222 int len = (int) ms.Length;
225 while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
228 if (input_state == InputState.RequestLine)
235 if (input_state == InputState.RequestLine) {
236 context.Request.SetRequestLine (line);
237 input_state = InputState.Headers;
239 context.Request.AddHeader (line);
242 if (context.HaveError)
256 string ReadLine (byte [] buffer, int offset, int len, ref int used)
258 if (current_line == null)
259 current_line = new StringBuilder ();
260 int last = offset + len;
262 for (int i = offset; i < last && line_state != LineState.LF; i++) {
266 line_state = LineState.CR;
267 } else if (b == 10) {
268 line_state = LineState.LF;
270 current_line.Append ((char) b);
274 string result = null;
275 if (line_state == LineState.LF) {
276 line_state = LineState.None;
277 result = current_line.ToString ();
278 current_line.Length = 0;
284 public void SendError (string msg, int status)
286 HttpListenerResponse response = context.Response;
287 response.StatusCode = status;
288 response.ContentType = "text/html";
289 string description = HttpListenerResponse.GetStatusDescription (status);
292 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
294 str = String.Format ("<h1>{0}</h1>", description);
296 byte [] error = context.Response.ContentEncoding.GetBytes (str);
297 response.Close (error, false);
300 public void SendError ()
302 SendError (context.ErrorMessage, context.ErrorStatus);
308 epl.UnbindContext (context);
309 context_bound = false;
318 internal void Close (bool force_close)
321 Stream st = GetResponseStream ();
327 if (!force_close && chunked && context.Response.ForceCloseChunked == false) {
328 // Don't close. Keep working.
336 if (force_close || context.Response.Headers ["connection"] == "close") {
340 s.Shutdown (SocketShutdown.Both);