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 X509Certificate2 cert;
58 AsymmetricAlgorithm key;
60 public HttpConnection (Socket sock, EndPointListener epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
67 if (secure == false) {
68 stream = new NetworkStream (sock, false);
70 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
71 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
77 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
85 context_bound = false;
90 ms = new MemoryStream ();
92 input_state = InputState.RequestLine;
93 line_state = LineState.None;
94 context = new HttpListenerContext (this);
97 public int ChunkedUses {
98 get { return chunked_uses; }
101 public IPEndPoint LocalEndPoint {
102 get { return (IPEndPoint) sock.LocalEndPoint; }
105 public IPEndPoint RemoteEndPoint {
106 get { return (IPEndPoint) sock.RemoteEndPoint; }
109 public bool IsSecure {
110 get { return secure; }
113 public ListenerPrefix Prefix {
114 get { return prefix; }
115 set { prefix = value; }
118 public void BeginReadRequest ()
121 buffer = new byte [BufferSize];
122 stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
125 public RequestStream GetRequestStream (bool chunked, long contentlength)
127 if (i_stream == null) {
128 byte [] buffer = ms.GetBuffer ();
129 int length = (int) ms.Length;
133 context.Response.SendChunked = true;
134 i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
136 i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
142 public ResponseStream GetResponseStream ()
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);
153 void OnRead (IAsyncResult ares)
155 // TODO: set a limit on ms length.
156 HttpConnection cnc = (HttpConnection) ares.AsyncState;
159 nread = stream.EndRead (ares);
160 ms.Write (buffer, 0, nread);
161 } catch (Exception e) {
162 Console.WriteLine (e);
171 // SendError (); // Why bother?
176 if (ProcessInput (ms)) {
177 if (!context.HaveError)
178 context.Request.FinishInitialization ();
180 if (context.HaveError) {
186 if (!epl.BindContext (context)) {
187 SendError ("Invalid host", 400);
190 context_bound = true;
193 stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
207 InputState input_state = InputState.RequestLine;
208 LineState line_state = LineState.None;
211 // true -> done processing
212 // false -> need more input
213 bool ProcessInput (MemoryStream ms)
215 byte [] buffer = ms.GetBuffer ();
216 int len = (int) ms.Length;
219 while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
222 if (input_state == InputState.RequestLine)
229 if (input_state == InputState.RequestLine) {
230 context.Request.SetRequestLine (line);
231 input_state = InputState.Headers;
233 context.Request.AddHeader (line);
236 if (context.HaveError)
250 string ReadLine (byte [] buffer, int offset, int len, ref int used)
252 if (current_line == null)
253 current_line = new StringBuilder ();
254 int last = offset + len;
256 for (int i = offset; i < last && line_state != LineState.LF; i++) {
260 line_state = LineState.CR;
261 } else if (b == 10) {
262 line_state = LineState.LF;
264 current_line.Append ((char) b);
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;
278 public void SendError (string msg, int status)
280 HttpListenerResponse response = context.Response;
281 response.StatusCode = status;
282 response.ContentType = "text/html";
283 string description = HttpListenerResponse.GetStatusDescription (status);
286 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
288 str = String.Format ("<h1>{0}</h1>", description);
290 byte [] error = context.Response.ContentEncoding.GetBytes (str);
291 response.Close (error, false);
294 public void SendError ()
296 SendError (context.ErrorMessage, context.ErrorStatus);
301 if (o_stream != null) {
302 Stream st = o_stream;
308 if (chunked && context.Response.ForceCloseChunked == false) {
309 // Don't close. Keep working.
318 s.Shutdown (SocketShutdown.Both);
321 epl.UnbindContext (context);