HttpConnection.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. //
  2. // System.Net.HttpConnection
  3. //
  4. // Author:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. //
  7. // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
  8. //
  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:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  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.
  27. //
  28. #if NET_2_0
  29. using System.IO;
  30. using System.Net.Sockets;
  31. using System.Text;
  32. namespace System.Net {
  33. sealed class HttpConnection
  34. {
  35. const int BufferSize = 8192;
  36. Socket sock;
  37. NetworkStream stream;
  38. EndPointListener epl;
  39. MemoryStream ms;
  40. byte [] buffer;
  41. HttpListenerContext context;
  42. bool secure;
  43. StringBuilder current_line;
  44. ListenerPrefix prefix;
  45. RequestStream i_stream;
  46. ResponseStream o_stream;
  47. bool chunked;
  48. int chunked_uses;
  49. bool context_bound;
  50. public HttpConnection (Socket sock, EndPointListener epl, bool secure)
  51. {
  52. this.sock = sock;
  53. stream = new NetworkStream (sock, false);
  54. this.epl = epl;
  55. this.secure = secure;
  56. Init ();
  57. }
  58. void Init ()
  59. {
  60. context_bound = false;
  61. i_stream = null;
  62. o_stream = null;
  63. prefix = null;
  64. chunked = false;
  65. ms = new MemoryStream ();
  66. position = 0;
  67. input_state = InputState.RequestLine;
  68. line_state = LineState.None;
  69. context = new HttpListenerContext (this);
  70. }
  71. public int ChunkedUses {
  72. get { return chunked_uses; }
  73. }
  74. public IPEndPoint LocalEndPoint {
  75. get { return (IPEndPoint) sock.LocalEndPoint; }
  76. }
  77. public IPEndPoint RemoteEndPoint {
  78. get { return (IPEndPoint) sock.RemoteEndPoint; }
  79. }
  80. public bool IsSecure {
  81. get { return secure; }
  82. }
  83. public ListenerPrefix Prefix {
  84. get { return prefix; }
  85. set { prefix = value; }
  86. }
  87. public void BeginReadRequest ()
  88. {
  89. if (buffer == null)
  90. buffer = new byte [BufferSize];
  91. stream.BeginRead (buffer, 0, BufferSize, OnRead, this);
  92. }
  93. public RequestStream GetRequestStream (bool chunked)
  94. {
  95. if (i_stream == null) {
  96. byte [] buffer = ms.GetBuffer ();
  97. int length = (int) ms.Length;
  98. ms = null;
  99. if (chunked) {
  100. this.chunked = true;
  101. context.Response.SendChunked = true;
  102. i_stream = new ChunkedInputStream (context, sock, buffer, position, length);
  103. } else {
  104. i_stream = new RequestStream (sock, buffer, position, length);
  105. }
  106. }
  107. return i_stream;
  108. }
  109. public ResponseStream GetResponseStream ()
  110. {
  111. // TODO: can we get this stream before reading the input?
  112. if (o_stream == null) {
  113. HttpListener listener = context.Listener;
  114. bool ign = (listener == null) ? true : listener.IgnoreWriteExceptions;
  115. o_stream = new ResponseStream (sock, context.Response, ign);
  116. }
  117. return o_stream;
  118. }
  119. void OnRead (IAsyncResult ares)
  120. {
  121. // TODO: set a limit on ms length.
  122. HttpConnection cnc = (HttpConnection) ares.AsyncState;
  123. int nread = -1;
  124. try {
  125. nread = stream.EndRead (ares);
  126. ms.Write (buffer, 0, nread);
  127. } catch {
  128. if (ms.Length > 0)
  129. SendError ();
  130. sock.Close ();
  131. return;
  132. }
  133. if (nread == 0) {
  134. //if (ms.Length > 0)
  135. // SendError (); // Why bother?
  136. sock.Close ();
  137. return;
  138. }
  139. if (ProcessInput (ms)) {
  140. if (!context.HaveError)
  141. context.Request.FinishInitialization ();
  142. if (context.HaveError) {
  143. SendError ();
  144. Close ();
  145. return;
  146. }
  147. if (!epl.BindContext (context)) {
  148. SendError ("Invalid host", 400);
  149. Close ();
  150. }
  151. context_bound = true;
  152. return;
  153. }
  154. stream.BeginRead (buffer, 0, BufferSize, OnRead, cnc);
  155. }
  156. enum InputState {
  157. RequestLine,
  158. Headers
  159. }
  160. enum LineState {
  161. None,
  162. CR,
  163. LF
  164. }
  165. InputState input_state = InputState.RequestLine;
  166. LineState line_state = LineState.None;
  167. int position;
  168. // true -> done processing
  169. // false -> need more input
  170. bool ProcessInput (MemoryStream ms)
  171. {
  172. byte [] buffer = ms.GetBuffer ();
  173. int len = (int) ms.Length;
  174. int used = 0;
  175. string line;
  176. while ((line = ReadLine (buffer, position, len - position, ref used)) != null) {
  177. position += used;
  178. if (line == "") {
  179. if (input_state == InputState.RequestLine)
  180. continue;
  181. current_line = null;
  182. ms = null;
  183. return true;
  184. }
  185. if (input_state == InputState.RequestLine) {
  186. context.Request.SetRequestLine (line);
  187. input_state = InputState.Headers;
  188. } else {
  189. context.Request.AddHeader (line);
  190. }
  191. if (context.HaveError)
  192. return true;
  193. if (position >= len)
  194. break;
  195. }
  196. if (used == len) {
  197. ms.SetLength (0);
  198. position = 0;
  199. }
  200. return false;
  201. }
  202. string ReadLine (byte [] buffer, int offset, int len, ref int used)
  203. {
  204. if (current_line == null)
  205. current_line = new StringBuilder ();
  206. int last = offset + len;
  207. used = 0;
  208. for (int i = offset; i < last && line_state != LineState.LF; i++) {
  209. used++;
  210. byte b = buffer [i];
  211. if (b == 13) {
  212. line_state = LineState.CR;
  213. } else if (b == 10) {
  214. line_state = LineState.LF;
  215. } else {
  216. current_line.Append ((char) b);
  217. }
  218. }
  219. string result = null;
  220. if (line_state == LineState.LF) {
  221. line_state = LineState.None;
  222. result = current_line.ToString ();
  223. current_line.Length = 0;
  224. }
  225. return result;
  226. }
  227. public void SendError (string msg, int status)
  228. {
  229. HttpListenerResponse response = context.Response;
  230. response.StatusCode = status;
  231. response.ContentType = "text/html";
  232. string description = HttpListenerResponse.GetStatusDescription (status);
  233. string str;
  234. if (msg != null)
  235. str = String.Format ("<h1>{0} ({1})</h1>", description, msg);
  236. else
  237. str = String.Format ("<h1>{0}</h1>", description);
  238. byte [] error = context.Response.ContentEncoding.GetBytes (str);
  239. response.Close (error, false);
  240. }
  241. public void SendError ()
  242. {
  243. SendError (context.ErrorMessage, context.ErrorStatus);
  244. }
  245. public void Close ()
  246. {
  247. if (o_stream != null) {
  248. Stream st = o_stream;
  249. st.Close ();
  250. o_stream = null;
  251. }
  252. if (sock != null) {
  253. if (chunked && context.Response.ForceCloseChunked == false) {
  254. // Don't close. Keep working.
  255. chunked_uses++;
  256. Init ();
  257. BeginReadRequest ();
  258. return;
  259. }
  260. Socket s = sock;
  261. sock = null;
  262. s.Shutdown (SocketShutdown.Both);
  263. s.Close ();
  264. if (context_bound)
  265. epl.UnbindContext (context);
  266. }
  267. }
  268. }
  269. }
  270. #endif