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.
30 using System.Net.Sockets;
32 namespace System.Net {
33 sealed class HttpConnection
35 const int BufferSize = 8192;
41 HttpListenerContext context;
43 StringBuilder current_line;
44 ListenerPrefix prefix;
45 RequestStream i_stream;
46 ResponseStream o_stream;
51 public HttpConnection (Socket sock, EndPointListener epl, bool secure)
54 stream = new NetworkStream (sock, false);
62 context_bound = false;
67 ms = new MemoryStream ();
69 input_state = InputState.RequestLine;
70 line_state = LineState.None;
71 context = new HttpListenerContext (this);
74 public int ChunkedUses {
75 get { return chunked_uses; }
78 public IPEndPoint LocalEndPoint {
79 get { return (IPEndPoint) sock.LocalEndPoint; }
82 public IPEndPoint RemoteEndPoint {
83 get { return (IPEndPoint) sock.RemoteEndPoint; }
86 public bool IsSecure {
87 get { return secure; }
90 public ListenerPrefix Prefix {
91 get { return prefix; }
92 set { prefix = value; }
95 public void BeginReadRequest ()
98 buffer = new byte [BufferSize];
99 stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
102 public RequestStream GetRequestStream (bool chunked)
104 if (i_stream == null) {
105 byte [] buffer = ms.GetBuffer ();
106 int length = (int) ms.Length;
110 context.Response.SendChunked = true;
111 i_stream = new ChunkedInputStream (context, sock, buffer, position, length);
113 i_stream = new RequestStream (sock, buffer, position, length);
119 public ResponseStream GetResponseStream ()
121 // TODO: can we get this stream before reading the input?
122 if (o_stream == null) {
123 HttpListener listener = context.Listener;
124 bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
125 o_stream = new ResponseStream (sock, context.Response, ign);
130 void OnRead (IAsyncResult ares)
132 // TODO: set a limit on ms length.
133 HttpConnection cnc = (HttpConnection) ares.AsyncState;
136 nread = stream.EndRead (ares);
137 ms.Write (buffer, 0, nread);
147 // SendError (); // Why bother?
152 if (ProcessInput (ms)) {
153 if (!context.HaveError)
154 context.Request.FinishInitialization ();
156 if (context.HaveError) {
162 if (!epl.BindContext (context)) {
163 SendError ("Invalid host", 400);
166 context_bound = true;
169 stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
183 InputState input_state = InputState.RequestLine;
184 LineState line_state = LineState.None;
187 // true -> done processing
188 // false -> need more input
189 bool ProcessInput (MemoryStream ms)
191 byte [] buffer = ms.GetBuffer ();
192 int len = (int) ms.Length;
195 while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
198 if (input_state == InputState.RequestLine)
205 if (input_state == InputState.RequestLine) {
206 context.Request.SetRequestLine (line);
207 input_state = InputState.Headers;
209 context.Request.AddHeader (line);
212 if (context.HaveError)
226 string ReadLine (byte [] buffer, int offset, int len, ref int used)
228 if (current_line == null)
229 current_line = new StringBuilder ();
230 int last = offset + len;
232 for (int i = offset; i < last && line_state != LineState.LF; i++) {
236 line_state = LineState.CR;
237 } else if (b == 10) {
238 line_state = LineState.LF;
240 current_line.Append ((char) b);
244 string result = null;
245 if (line_state == LineState.LF) {
246 line_state = LineState.None;
247 result = current_line.ToString ();
248 current_line.Length = 0;
254 public void SendError (string msg, int status)
256 HttpListenerResponse response = context.Response;
257 response.StatusCode = status;
258 response.ContentType = "text/html";
259 string description = HttpListenerResponse.GetStatusDescription (status);
262 str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
264 str = String.Format ("<h1>{0}</h1>", description);
266 byte [] error = context.Response.ContentEncoding.GetBytes (str);
267 response.Close (error, false);
270 public void SendError ()
272 SendError (context.ErrorMessage, context.ErrorStatus);
277 if (o_stream != null) {
278 Stream st = o_stream;
284 if (chunked && context.Response.ForceCloseChunked == false) {
285 // Don't close. Keep working.
294 s.Shutdown (SocketShutdown.Both);
297 epl.UnbindContext (context);