1 #define EMBEDDED_IN_1_0
4 // System.Net.HttpConnection
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
9 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
34 using System.Net.Sockets;
35 using System.Reflection;
37 using System.Security.Cryptography;
38 using System.Security.Cryptography.X509Certificates;
40 using Mono.Security.Protocol.Tls;
43 using System; using System.Net; namespace MonoHttp {
45 interface IHttpListenerContextBinder {
46 bool BindContext (HttpListenerContext context);
47 void UnbindContext (HttpListenerContext context);
50 sealed class HttpConnection
52 const int BufferSize = 8192;
55 IHttpListenerContextBinder epl;
58 HttpListenerContext context;
59 StringBuilder current_line;
60 ListenerPrefix prefix;
61 RequestStream i_stream;
62 ResponseStream o_stream;
67 AsymmetricAlgorithm key;
70 public HttpConnection (Socket sock, IHttpListenerContextBinder epl)
74 stream = new NetworkStream (sock, false);
78 public HttpConnection (Socket sock, IHttpListenerContextBinder epl, bool secure, X509Certificate2 cert, AsymmetricAlgorithm key)
84 if (secure == false) {
85 stream = new NetworkStream (sock, false);
88 throw new NotImplementedException ();
90 SslServerStream ssl_stream = new SslServerStream (new NetworkStream (sock, false), cert, false, false);
91 ssl_stream.PrivateKeyCertSelectionDelegate += OnPVKSelection;
99 AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost)
107 context_bound = false;
112 ms = new MemoryStream ();
114 input_state = InputState.RequestLine;
115 line_state = LineState.None;
116 context = new HttpListenerContext (this);
119 public int ChunkedUses {
120 get { return chunked_uses; }
123 public IPEndPoint LocalEndPoint {
124 get { return (IPEndPoint) sock.LocalEndPoint; }
127 public IPEndPoint RemoteEndPoint {
128 get { return (IPEndPoint) sock.RemoteEndPoint; }
131 public bool IsSecure {
132 get { return secure; }
135 public ListenerPrefix Prefix {
136 get { return prefix; }
137 set { prefix = value; }
140 public void BeginReadRequest ()
143 buffer = new byte [BufferSize];
145 stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
147 sock.Close (); // stream disposed
151 public RequestStream GetRequestStream (bool chunked, long contentlength)
153 if (i_stream == null) {
154 byte [] buffer = ms.GetBuffer ();
155 int length = (int) ms.Length;
159 context.Response.SendChunked = true;
160 i_stream = new ChunkedInputStream (context, stream, buffer, position, length - position);
162 i_stream = new RequestStream (stream, buffer, position, length - position, contentlength);
168 public ResponseStream GetResponseStream ()
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);
179 void OnRead (IAsyncResult ares)
181 // TODO: set a limit on ms length.
182 HttpConnection cnc = (HttpConnection) ares.AsyncState;
185 nread = stream.EndRead (ares);
186 ms.Write (buffer, 0, nread);
187 } catch (Exception e) {
188 //Console.WriteLine (e);
197 // SendError (); // Why bother?
202 if (ProcessInput (ms)) {
203 if (!context.HaveError)
204 context.Request.FinishInitialization ();
206 if (context.HaveError) {
212 if (!epl.BindContext (context)) {
213 SendError ("Invalid host", 400);
216 context_bound = true;
219 stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
233 InputState input_state = InputState.RequestLine;
234 LineState line_state = LineState.None;
237 // true -> done processing
238 // false -> need more input
239 bool ProcessInput (MemoryStream ms)
241 byte [] buffer = ms.GetBuffer ();
242 int len = (int) ms.Length;
245 while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
248 if (input_state == InputState.RequestLine)
255 if (input_state == InputState.RequestLine) {
256 context.Request.SetRequestLine (line);
257 input_state = InputState.Headers;
259 context.Request.AddHeader (line);
262 if (context.HaveError)
276 string ReadLine (byte [] buffer, int offset, int len, ref int used)
278 if (current_line == null)
279 current_line = new StringBuilder ();
280 int last = offset + len;
282 for (int i = offset; i < last && line_state != LineState.LF; i++) {
286 line_state = LineState.CR;
287 } else if (b == 10) {
288 line_state = LineState.LF;
290 current_line.Append ((char) b);
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;
304 public void SendError (string msg, int status)
306 HttpListenerResponse response = context.Response;
307 response.StatusCode = status;
308 response.ContentType = "text/html";
309 string description = HttpListenerResponse.GetStatusDescription (status);
312 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
314 str = String.Format ("<h1>{0}</h1>", description);
316 byte [] error = context.Response.ContentEncoding.GetBytes (str);
317 response.Close (error, false);
320 public void SendError ()
322 SendError (context.ErrorMessage, context.ErrorStatus);
328 epl.UnbindContext (context);
329 context_bound = false;
338 internal void Close (bool force_close)
341 Stream st = GetResponseStream ();
347 if (!force_close && chunked && context.Response.ForceCloseChunked == false) {
348 // Don't close. Keep working.
356 if (force_close || context.Response.Headers ["connection"] == "close") {
360 s.Shutdown (SocketShutdown.Both);