PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Hosts/Silverlight/Chiron/HttpSocket.cs

http://github.com/IronLanguages/main
C# | 303 lines | 221 code | 56 blank | 26 comment | 64 complexity | 77f3300bde5fa5066a7f169451422766 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Globalization;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Net.Sockets;
  20. using System.Text;
  21. namespace Chiron {
  22. class HttpSocket {
  23. Socket _socket;
  24. int _statusCode, _bytesSent;
  25. string _message;
  26. // logging
  27. public int StatusCode { get { return _statusCode; } set { _statusCode = value; } }
  28. public int BytesSent { get { return _bytesSent; } set { _bytesSent = value; } }
  29. public string Message { get { return _message; } set { _message = value; } }
  30. public HttpSocket(Socket socket) {
  31. _socket = socket;
  32. }
  33. public void Close() {
  34. try {
  35. if (_socket != null) {
  36. _socket.Shutdown(SocketShutdown.Both);
  37. _socket.Close();
  38. }
  39. }
  40. catch {} finally {
  41. _socket = null;
  42. }
  43. }
  44. #region Reading Request
  45. public bool TryReadRequest(out HttpRequestData r) {
  46. r = new HttpRequestData();
  47. // wait for some request bytes
  48. if (WaitForRequestBytes() == 0) return false;
  49. // reader header bytes until \r\n\r\n
  50. const int MaxHeaderBytes = 32000;
  51. const byte CR = ((byte)'\r');
  52. const byte LF = ((byte)'\n');
  53. byte[] hb = null;
  54. int hbLen = -1;
  55. byte[] body = null;
  56. int bodyLen = 0;
  57. do {
  58. byte[] newBytes = ReadRequestBytes(MaxHeaderBytes);
  59. if (newBytes == null) return false;
  60. if (hb == null) {
  61. hb = newBytes;
  62. }
  63. else {
  64. int len = hb.Length + newBytes.Length;
  65. if (len > MaxHeaderBytes) return false;
  66. byte[] bytes = new byte[len];
  67. Buffer.BlockCopy(hb, 0, bytes, 0, hb.Length);
  68. Buffer.BlockCopy(newBytes, 0, bytes, hb.Length, newBytes.Length);
  69. hb = bytes;
  70. }
  71. // look for \r\n\r\n in hb
  72. int l = hb.Length;
  73. for (int i = 0; i < l - 1; i++) {
  74. if (i < l - 3 && hb[i] == CR && hb[i + 1] == LF && hb[i + 2] == CR && hb[i + 3] == LF) {
  75. hbLen = i + 3;
  76. break;
  77. }
  78. else if (hb[i] == LF && hb[i + 1] == LF) {
  79. hbLen = i + 1;
  80. break;
  81. }
  82. }
  83. }
  84. while (hbLen < 0);
  85. // store the initial body chunk
  86. if (hbLen < hb.Length) {
  87. bodyLen = hb.Length - hbLen;
  88. body = new byte[bodyLen];
  89. Buffer.BlockCopy(hb, hbLen, body, 0, bodyLen);
  90. }
  91. // convert headers to strings
  92. string[] headers = Encoding.UTF8.GetString(hb, 0, hbLen).Replace("\r\n", "\n").Split('\n');
  93. // parse request line
  94. string[] firstLine = headers[0].Split(' ');
  95. if (firstLine.Length < 2) return false;
  96. r.Method = firstLine[0].Trim();
  97. r.Uri = firstLine[1].Trim();
  98. // parse headers
  99. int contentLength = -1;
  100. for (int i = 1; i < headers.Length; i++) {
  101. string h = headers[i];
  102. int j = h.IndexOf(':');
  103. if (j > 0) {
  104. string k = h.Substring(0, j).Trim();
  105. string v = h.Substring(j + 1).Trim();
  106. r.Headers.Add(new KeyValuePair<string, string>(k, v));
  107. if (string.Compare(k, "content-length", StringComparison.OrdinalIgnoreCase) == 0) {
  108. if (!int.TryParse(v, out contentLength)) contentLength = -1;
  109. }
  110. }
  111. }
  112. // store the body from the first chunk
  113. if (bodyLen > 0) r.Body.Add(body);
  114. // read remaining body, if any
  115. if (contentLength > 0 && bodyLen < contentLength) {
  116. // 100 response to POST
  117. if (r.Method == "POST") WriteResponse(100, null, null, true);
  118. while (bodyLen < contentLength) {
  119. byte[] bytes = ReadRequestBytes(contentLength - bodyLen);
  120. if (bytes == null || bytes.Length == 0) {
  121. return false;
  122. }
  123. bodyLen += bytes.Length;
  124. r.Body.Add(bytes);
  125. }
  126. }
  127. return true;
  128. }
  129. int WaitForRequestBytes() {
  130. int availBytes = 0;
  131. try {
  132. if (_socket.Available == 0) {
  133. // poll until there is data
  134. _socket.Poll(100000 /* 100ms */, SelectMode.SelectRead);
  135. if (_socket.Available == 0 && _socket.Connected) {
  136. _socket.Poll(30000000 /* 30sec */, SelectMode.SelectRead);
  137. }
  138. }
  139. availBytes = _socket.Available;
  140. }
  141. catch {
  142. }
  143. return availBytes;
  144. }
  145. byte[] ReadRequestBytes(int maxBytes) {
  146. try {
  147. if (WaitForRequestBytes() == 0)
  148. return null;
  149. int numBytes = _socket.Available;
  150. if (numBytes > maxBytes)
  151. numBytes = maxBytes;
  152. int numReceived = 0;
  153. byte[] buffer = new byte[numBytes];
  154. if (numBytes > 0) {
  155. numReceived = _socket.Receive(buffer, 0, numBytes, SocketFlags.None);
  156. }
  157. if (numReceived < numBytes) {
  158. byte[] tempBuffer = new byte[numReceived];
  159. if (numReceived > 0) {
  160. Buffer.BlockCopy(buffer, 0, tempBuffer, 0, numReceived);
  161. }
  162. buffer = tempBuffer;
  163. }
  164. return buffer;
  165. }
  166. catch {
  167. return null;
  168. }
  169. }
  170. #endregion
  171. #region Writing Response
  172. public void WriteResponse(int statusCode, String headers, byte[] body, bool keepAlive) {
  173. try {
  174. Socket s = _socket;
  175. if (s != null) {
  176. int bodyLength = (body != null) ? body.Length : 0;
  177. byte[] headerBytes = GenerateResponseHeaders(statusCode, headers, bodyLength, keepAlive);
  178. s.Send(headerBytes);
  179. if (bodyLength > 0) _socket.Send(body);
  180. BytesSent += headerBytes.Length + bodyLength;
  181. }
  182. }
  183. catch (SocketException) {
  184. }
  185. finally {
  186. StatusCode = statusCode;
  187. if (!keepAlive) Close();
  188. }
  189. }
  190. public void WriteTextResponse(int statusCode, string textType, string body, bool keepAlive) {
  191. byte[] bodyBytes = string.IsNullOrEmpty(body) ? null : Encoding.UTF8.GetBytes(body);
  192. WriteResponse(statusCode,
  193. string.Format("Content-type: text/{0}; charset=utf-8\r\n", textType),
  194. bodyBytes, keepAlive);
  195. }
  196. public void WriteBinaryResponse(int statusCode, string mimeType, byte[] body, bool keepAlive) {
  197. WriteResponse(statusCode,
  198. string.Format("Content-Type: {0}\r\n", mimeType),
  199. body, keepAlive);
  200. }
  201. public void WriteErrorResponse(int statusCode, string message) {
  202. string html = HtmlFormatter.GenerateErrorBody(statusCode, GetStatusCodeText(statusCode), message);
  203. WriteTextResponse(statusCode, "html", html, false);
  204. Message = message;
  205. }
  206. public void WriteErrorResponse(int statusCode) {
  207. WriteErrorResponse(statusCode, null);
  208. }
  209. public static string GetMimeType(string filename) {
  210. string mime;
  211. return Chiron.MimeMap.TryGetValue(Path.GetExtension(filename).ToLowerInvariant(), out mime) ? mime : null;
  212. }
  213. #endregion
  214. #region Helpers
  215. static Dictionary<int, string> StatusCodes;
  216. static HttpSocket() {
  217. StatusCodes = new Dictionary<int, string>();
  218. StatusCodes[100] = "Continue";
  219. StatusCodes[200] = "OK";
  220. StatusCodes[302] = "Found";
  221. StatusCodes[400] = "Bad Request";
  222. StatusCodes[403] = "Forbidden";
  223. StatusCodes[404] = "Not Found";
  224. StatusCodes[405] = "Method Not Allowed";
  225. StatusCodes[500] = "Server Error";
  226. }
  227. static byte[] GenerateResponseHeaders(int statusCode, string extraHeaders, int contentLength, bool keepAlive) {
  228. StringBuilder sb = new StringBuilder();
  229. Version ver = typeof(Chiron).Assembly.GetName().Version;
  230. sb.AppendFormat("HTTP/1.1 {0} {1}\r\n", statusCode, GetStatusCodeText(statusCode));
  231. sb.AppendFormat("Date: {0}\r\n", DateTime.Now.ToUniversalTime().ToString("R", DateTimeFormatInfo.InvariantInfo));
  232. sb.AppendFormat("Server: Chiron/{0}.{1}\r\n", ver.Major, ver.MajorRevision);
  233. if (contentLength >= 0) sb.AppendFormat("Content-Length: {0}\r\n", contentLength);
  234. if (extraHeaders != null) sb.Append(extraHeaders);
  235. if (!keepAlive) sb.Append("Connection: Close\r\n");
  236. sb.Append("\r\n");
  237. return Encoding.UTF8.GetBytes(sb.ToString());
  238. }
  239. static string GetStatusCodeText(int code) {
  240. string statusText;
  241. if (!StatusCodes.TryGetValue(code, out statusText))
  242. statusText = "Unexpected";
  243. return statusText;
  244. }
  245. #endregion
  246. }
  247. }