HttpServer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //==========================================================================
  2. // File: HttpServer.cs
  3. //
  4. // Summary: Implements an HttpServer to be used by the HttpServerChannel class
  5. //
  6. //
  7. // Classes: internal sealed HttpServer
  8. // internal sealed ReqMessageParser
  9. // private RequestArguments
  10. //
  11. // By :
  12. // Ahmad Tantawy [email protected]
  13. // Ahmad Kadry [email protected]
  14. // Hussein Mehanna [email protected]
  15. //
  16. //==========================================================================
  17. //
  18. // Permission is hereby granted, free of charge, to any person obtaining
  19. // a copy of this software and associated documentation files (the
  20. // "Software"), to deal in the Software without restriction, including
  21. // without limitation the rights to use, copy, modify, merge, publish,
  22. // distribute, sublicense, and/or sell copies of the Software, and to
  23. // permit persons to whom the Software is furnished to do so, subject to
  24. // the following conditions:
  25. //
  26. // The above copyright notice and this permission notice shall be
  27. // included in all copies or substantial portions of the Software.
  28. //
  29. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  30. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  31. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  32. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  33. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  34. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  35. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  36. //
  37. using System;
  38. using System.Net.Sockets;
  39. using System.Text;
  40. using System.Text.RegularExpressions;
  41. using System.Collections;
  42. using System.Runtime.Remoting.Channels;
  43. using System.IO;
  44. using System.Net;
  45. using System.Runtime.Remoting.Messaging;
  46. namespace System.Runtime.Remoting.Channels.Http
  47. {
  48. internal class RequestArguments
  49. {
  50. static int count;
  51. Socket socket;
  52. public RequestArguments (Socket socket, HttpServerTransportSink sink)
  53. {
  54. Id = count++;
  55. NetworkStream ns = new NetworkStream (socket);
  56. this.Stream = ns;
  57. this.socket = socket;
  58. Sink = sink;
  59. }
  60. public void Process ()
  61. {
  62. HttpServer.ProcessRequest (this);
  63. }
  64. public IPAddress ClientAddress
  65. {
  66. get {
  67. IPEndPoint ep = socket.RemoteEndPoint as IPEndPoint;
  68. if (ep != null) return ep.Address;
  69. else return null;
  70. }
  71. }
  72. public void Close()
  73. {
  74. Stream.Close();
  75. socket.Close();
  76. }
  77. public int Id;
  78. public Stream Stream;
  79. public HttpServerTransportSink Sink;
  80. }
  81. internal sealed class HttpServer
  82. {
  83. static HttpServer ()
  84. {
  85. Array.Sort (knownHeaders);
  86. }
  87. public static void ProcessRequest (RequestArguments reqArg)
  88. {
  89. try {
  90. //Step (1) Start Reciceve the header
  91. ArrayList Headers = RecieveHeader (reqArg);
  92. //Step (2) Start Parse the header
  93. IDictionary HeaderFields = new Hashtable();
  94. IDictionary CustomHeaders = new Hashtable();
  95. if (!ParseHeader (reqArg, Headers, HeaderFields, CustomHeaders))
  96. return;
  97. //Step (3)
  98. if (!CheckRequest (reqArg, HeaderFields, CustomHeaders))
  99. return;
  100. //Step (4) Recieve the entity body
  101. byte[] buffer;
  102. object len = HeaderFields["content-length"];
  103. if (len != null)
  104. {
  105. buffer = new byte [(int)len];
  106. if (!RecieveEntityBody (reqArg, buffer))
  107. return;
  108. }
  109. else
  110. buffer = new byte [0];
  111. //Step (5)
  112. SendRequestForChannel (reqArg, HeaderFields, CustomHeaders, buffer);
  113. } finally {
  114. reqArg.Close ();
  115. }
  116. }
  117. private static ArrayList RecieveHeader (RequestArguments reqArg)
  118. {
  119. bool bLastLine = false;
  120. bool bEndOfLine = false;
  121. byte[] buffer = new byte[1024];
  122. ArrayList Headers = new ArrayList();
  123. Stream ist = reqArg.Stream;
  124. int index =0;
  125. while (!bLastLine)
  126. {
  127. //recieve line by line
  128. index = 0;
  129. bEndOfLine = false;
  130. //Step (1) is it an empty line?
  131. ist.Read (buffer, index, 1);
  132. if(buffer[index++]==13)
  133. {
  134. ist.Read (buffer, index, 1);
  135. bLastLine=true;
  136. bEndOfLine = true;
  137. }
  138. //Step (2) recieve line bytes
  139. while (!bEndOfLine)
  140. {
  141. ist.Read (buffer, index, 1);
  142. if(buffer [index++]==13)
  143. {
  144. bEndOfLine = true;
  145. ist.Read (buffer,index,1);
  146. }
  147. }
  148. //Step (3) convert bytes to a string
  149. if (bLastLine)
  150. continue;
  151. Headers.Add (Encoding.ASCII.GetString (buffer,0,index));
  152. }//end while loop
  153. return Headers;
  154. }
  155. private static bool ParseHeader (RequestArguments reqArg, ArrayList Headers, IDictionary HeaderFields, IDictionary CustomHeaders)
  156. {
  157. // The first "header" is the method
  158. string[] met = ((string) Headers [0]).Split (' ');
  159. HeaderFields.Add ("method", met[0]);
  160. if (met.Length >= 1)
  161. HeaderFields.Add ("request-url", met [1]);
  162. if (met.Length >= 2)
  163. HeaderFields.Add ("http-version", met [2]);
  164. for (int i = 1; i < Headers.Count; i++)
  165. {
  166. string header = (string) Headers [i];
  167. int p = header.IndexOf (':');
  168. string id;
  169. object val;
  170. if (p == -1) {
  171. id = header.Trim ().ToLower ();
  172. val = "";
  173. } else {
  174. id = header.Substring (0, p).Trim ().ToLower ();
  175. val = header.Substring (p + 1).Trim ();
  176. }
  177. if (Array.BinarySearch (knownHeaders, id) >= 0) {
  178. if (id == "content-length")
  179. val = int.Parse ((string)val);
  180. HeaderFields [id] = val;
  181. }
  182. else {
  183. CustomHeaders [id] = val;
  184. }
  185. }
  186. return true;
  187. }
  188. private static bool CheckRequest (RequestArguments reqArg, IDictionary HeaderFields, IDictionary CustomHeaders)
  189. {
  190. string temp;
  191. if (HeaderFields["expect"] as string == "100-continue")
  192. SendResponse (reqArg, 100, null, null);
  193. //Check the method
  194. temp = HeaderFields["method"].ToString();
  195. if (temp != "POST")
  196. return true;
  197. //Check for the content-length field
  198. if (HeaderFields["content-length"]==null)
  199. {
  200. SendResponse (reqArg, 411, null, null);
  201. return false;
  202. }
  203. return true;
  204. }
  205. private static bool RecieveEntityBody (RequestArguments reqArg, byte[] buffer)
  206. {
  207. try
  208. {
  209. int nr = 0;
  210. while (nr < buffer.Length)
  211. nr += reqArg.Stream.Read (buffer, nr, buffer.Length - nr);
  212. }
  213. catch (SocketException e)
  214. {
  215. switch(e.ErrorCode)
  216. {
  217. case 10060 : //TimeOut
  218. SendResponse (reqArg, 408, null, null);
  219. break;
  220. default :
  221. throw e;
  222. }
  223. return false;
  224. }//end catch
  225. return true;
  226. }
  227. private static bool SendRequestForChannel (RequestArguments reqArg, IDictionary HeaderFields, IDictionary CustomHeaders, byte[] buffer)
  228. {
  229. TransportHeaders THeaders = new TransportHeaders();
  230. Stream stream = new MemoryStream(buffer);
  231. if(stream.Position !=0)
  232. stream.Seek(0,SeekOrigin.Begin);
  233. THeaders[CommonTransportKeys.RequestUri] = FixURI((string)HeaderFields["request-url"]);
  234. THeaders[CommonTransportKeys.ContentType]= HeaderFields["content-type"];
  235. THeaders[CommonTransportKeys.RequestVerb]= HeaderFields["method"];
  236. THeaders[CommonTransportKeys.HttpVersion] = HeaderFields["http-version"];
  237. THeaders[CommonTransportKeys.UserAgent] = HeaderFields["user-agent"];
  238. THeaders[CommonTransportKeys.Host] = HeaderFields["host"];
  239. THeaders[CommonTransportKeys.SoapAction] = HeaderFields["soapaction"];
  240. THeaders[CommonTransportKeys.IPAddress] = reqArg.ClientAddress;
  241. THeaders[CommonTransportKeys.ConnectionId] = reqArg.Id;
  242. foreach(DictionaryEntry DictEntry in CustomHeaders)
  243. {
  244. THeaders[DictEntry.Key.ToString()] = DictEntry.Value.ToString();
  245. }
  246. reqArg.Sink.ServiceRequest (reqArg, stream, THeaders);
  247. return true;
  248. }
  249. private static string FixURI(string RequestURI)
  250. {
  251. if(RequestURI.IndexOf ( '.' ) == -1)
  252. return RequestURI;
  253. else
  254. return RequestURI.Substring(1);
  255. }
  256. public static void SendResponse (RequestArguments reqArg, int httpStatusCode, ITransportHeaders headers, Stream responseStream)
  257. {
  258. byte [] headersBuffer = null;
  259. byte [] entityBuffer = null;
  260. StringBuilder responseStr;
  261. String reason = null;
  262. if (headers != null && headers[CommonTransportKeys.HttpStatusCode] != null) {
  263. // The formatter can override the result code
  264. httpStatusCode = int.Parse ((string)headers [CommonTransportKeys.HttpStatusCode]);
  265. reason = (string) headers [CommonTransportKeys.HttpReasonPhrase];
  266. }
  267. if (reason == null)
  268. reason = GetReasonPhrase (httpStatusCode);
  269. //Response Line
  270. responseStr = new StringBuilder ("HTTP/1.0 " + httpStatusCode + " " + reason + "\r\n" );
  271. if (headers != null)
  272. {
  273. foreach (DictionaryEntry entry in headers)
  274. {
  275. string key = entry.Key.ToString();
  276. if (key != CommonTransportKeys.HttpStatusCode && key != CommonTransportKeys.HttpReasonPhrase)
  277. responseStr.Append(key + ": " + entry.Value.ToString() + "\r\n");
  278. }
  279. }
  280. responseStr.Append("Server: Mono Remoting, Mono CLR " + System.Environment.Version.ToString() + "\r\n");
  281. if(responseStream != null && responseStream.Length!=0)
  282. {
  283. responseStr.Append("Content-Length: "+responseStream.Length.ToString()+"\r\n");
  284. entityBuffer = new byte[responseStream.Length];
  285. responseStream.Seek(0 , SeekOrigin.Begin);
  286. responseStream.Read(entityBuffer,0,entityBuffer.Length);
  287. }
  288. else
  289. responseStr.Append("Content-Length: 0\r\n");
  290. responseStr.Append("X-Powered-By: Mono\r\n");
  291. responseStr.Append("Connection: close\r\n");
  292. responseStr.Append("\r\n");
  293. headersBuffer = Encoding.ASCII.GetBytes (responseStr.ToString());
  294. //send headersBuffer
  295. reqArg.Stream.Write (headersBuffer, 0, headersBuffer.Length);
  296. if (entityBuffer != null)
  297. reqArg.Stream.Write (entityBuffer, 0, entityBuffer.Length);
  298. }
  299. internal static string GetReasonPhrase (int HttpStatusCode)
  300. {
  301. switch (HttpStatusCode)
  302. {
  303. case 100 : return "Continue" ;
  304. case 101 :return "Switching Protocols";
  305. case 200 :return "OK";
  306. case 201 :return "Created";
  307. case 202 :return "Accepted";
  308. case 203 :return "Non-Authoritative Information";
  309. case 204 :return "No Content";
  310. case 205 :return "Reset Content";
  311. case 206 :return "Partial Content";
  312. case 300 :return "Multiple Choices";
  313. case 301 :return "Moved Permanently";
  314. case 302 :return "Found";
  315. case 303 :return "See Other";
  316. case 304 :return "Not Modified";
  317. case 305 :return "Use Proxy";
  318. case 307 :return "Temporary Redirect";
  319. case 400 :return "Bad Request";
  320. case 401 :return "Unauthorized";
  321. case 402 :return "Payment Required";
  322. case 403 :return "Forbidden";
  323. case 404 :return "Not Found";
  324. case 405 :return "Method Not Allowed";
  325. case 406 :return "Not Acceptable";
  326. case 407 :return "Proxy Authentication Required";
  327. case 408 :return "Request Time-out";
  328. case 409 :return "Conflict";
  329. case 410 :return "Gone";
  330. case 411 :return "Length Required";
  331. case 412 :return "Precondition Failed";
  332. case 413 :return "Request Entity Too Large";
  333. case 414 :return "Request-URI Too Large";
  334. case 415 :return "Unsupported Media Type";
  335. case 416 :return "Requested range not satisfiable";
  336. case 417 :return "Expectation Failed";
  337. case 500 :return "Internal Server Error";
  338. case 501 :return "Not Implemented";
  339. case 502 :return "Bad Gateway";
  340. case 503 :return "Service Unavailable";
  341. case 504 :return "Gateway Time-out";
  342. case 505 :return "HTTP Version not supported";
  343. default: return "";
  344. }
  345. }
  346. static string[] knownHeaders = new string [] {
  347. "accept",
  348. "accept-charset",
  349. "accept-encoding",
  350. "authorization",
  351. "accept-language",
  352. "from",
  353. "host",
  354. "if-modified-since",
  355. "proxy-authorization",
  356. "range",
  357. "user-agent",
  358. "expect",
  359. "connection",
  360. "allow",
  361. "content-encoding",
  362. "content-language",
  363. "content-length",
  364. "content-range",
  365. "content-type",
  366. "content-version",
  367. "derived-from",
  368. "expires",
  369. "last-modified",
  370. "link",
  371. "title",
  372. "transfere-encoding",
  373. "url-header",
  374. "extension-header"
  375. };
  376. }
  377. }