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
;
39 using Mono
.Security
.Protocol
.Tls
;
42 namespace System
.Net
{
44 interface IHttpListenerContextBinder
{
45 bool BindContext (HttpListenerContext context
);
46 void UnbindContext (HttpListenerContext context
);
49 sealed class HttpConnection
51 const int BufferSize
= 8192;
54 IHttpListenerContextBinder epl
;
57 HttpListenerContext context
;
58 StringBuilder current_line
;
59 ListenerPrefix prefix
;
60 RequestStream i_stream
;
61 ResponseStream o_stream
;
66 AsymmetricAlgorithm key
;
69 public HttpConnection (Socket sock
, IHttpListenerContextBinder epl
)
73 stream
= new NetworkStream (sock
, false);
77 public HttpConnection (Socket sock
, IHttpListenerContextBinder epl
, bool secure
, X509Certificate2 cert
, AsymmetricAlgorithm key
)
83 if (secure
== false) {
84 stream
= new NetworkStream (sock
, false);
86 SslServerStream ssl_stream
= new SslServerStream (new NetworkStream (sock
, false), cert
, false, false);
87 ssl_stream
.PrivateKeyCertSelectionDelegate
+= OnPVKSelection
;
95 AsymmetricAlgorithm
OnPVKSelection (X509Certificate certificate
, string targetHost
)
103 context_bound
= false;
108 ms
= new MemoryStream ();
110 input_state
= InputState
.RequestLine
;
111 line_state
= LineState
.None
;
112 context
= new HttpListenerContext (this);
115 public int ChunkedUses
{
116 get { return chunked_uses; }
119 public IPEndPoint LocalEndPoint
{
120 get { return (IPEndPoint) sock.LocalEndPoint; }
123 public IPEndPoint RemoteEndPoint
{
124 get { return (IPEndPoint) sock.RemoteEndPoint; }
127 public bool IsSecure
{
128 get { return secure; }
131 public ListenerPrefix Prefix
{
132 get { return prefix; }
133 set { prefix = value; }
136 public void BeginReadRequest ()
139 buffer
= new byte [BufferSize
];
140 stream
.BeginRead (buffer
, 0, BufferSize
, OnRead
, this);
143 public RequestStream
GetRequestStream (bool chunked
, long contentlength
)
145 if (i_stream
== null) {
146 byte [] buffer
= ms
.GetBuffer ();
147 int length
= (int) ms
.Length
;
151 context
.Response
.SendChunked
= true;
152 i_stream
= new ChunkedInputStream (context
, stream
, buffer
, position
, length
- position
);
154 i_stream
= new RequestStream (stream
, buffer
, position
, length
- position
, contentlength
);
160 public ResponseStream
GetResponseStream ()
162 // TODO: can we get this stream before reading the input?
163 if (o_stream
== null) {
164 HttpListener listener
= context
.Listener
;
165 bool ign
= (listener
== null) ? true : listener
.IgnoreWriteExceptions
;
166 o_stream
= new ResponseStream (stream
, context
.Response
, ign
);
171 void OnRead (IAsyncResult ares
)
173 // TODO: set a limit on ms length.
174 HttpConnection cnc
= (HttpConnection
) ares
.AsyncState
;
177 nread
= stream
.EndRead (ares
);
178 ms
.Write (buffer
, 0, nread
);
179 } catch (Exception e
) {
180 Console
.WriteLine (e
);
189 // SendError (); // Why bother?
194 if (ProcessInput (ms
)) {
195 if (!context
.HaveError
)
196 context
.Request
.FinishInitialization ();
198 if (context
.HaveError
) {
204 if (!epl
.BindContext (context
)) {
205 SendError ("Invalid host", 400);
208 context_bound
= true;
211 stream
.BeginRead (buffer
, 0, BufferSize
, OnRead
, cnc
);
225 InputState input_state
= InputState
.RequestLine
;
226 LineState line_state
= LineState
.None
;
229 // true -> done processing
230 // false -> need more input
231 bool ProcessInput (MemoryStream ms
)
233 byte [] buffer
= ms
.GetBuffer ();
234 int len
= (int) ms
.Length
;
237 while ((line
= ReadLine (buffer
, position
, len
- position
, ref used
)) != null) {
240 if (input_state
== InputState
.RequestLine
)
247 if (input_state
== InputState
.RequestLine
) {
248 context
.Request
.SetRequestLine (line
);
249 input_state
= InputState
.Headers
;
251 context
.Request
.AddHeader (line
);
254 if (context
.HaveError
)
268 string ReadLine (byte [] buffer
, int offset
, int len
, ref int used
)
270 if (current_line
== null)
271 current_line
= new StringBuilder ();
272 int last
= offset
+ len
;
274 for (int i
= offset
; i
< last
&& line_state
!= LineState
.LF
; i
++) {
278 line_state
= LineState
.CR
;
279 } else if (b
== 10) {
280 line_state
= LineState
.LF
;
282 current_line
.Append ((char) b
);
286 string result
= null;
287 if (line_state
== LineState
.LF
) {
288 line_state
= LineState
.None
;
289 result
= current_line
.ToString ();
290 current_line
.Length
= 0;
296 public void SendError (string msg
, int status
)
298 HttpListenerResponse response
= context
.Response
;
299 response
.StatusCode
= status
;
300 response
.ContentType
= "text/html";
301 string description
= HttpListenerResponse
.GetStatusDescription (status
);
304 str
= String
.Format ("<h1>{0} ({1})</h1>", description
, msg
);
306 str
= String
.Format ("<h1>{0}</h1>", description
);
308 byte [] error
= context
.Response
.ContentEncoding
.GetBytes (str
);
309 response
.Close (error
, false);
312 public void SendError ()
314 SendError (context
.ErrorMessage
, context
.ErrorStatus
);
319 if (o_stream
!= null) {
320 Stream st
= o_stream
;
326 if (chunked
&& context
.Response
.ForceCloseChunked
== false) {
327 // Don't close. Keep working.
337 s
.Shutdown (SocketShutdown
.Both
);
342 epl
.UnbindContext (context
);