LJHttpProcessor.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Web;
  10. namespace LJLib.HttpServer
  11. {
  12. public class LJHttpProcessor
  13. {
  14. private TcpClient socket;
  15. private LJHttpServer srv;
  16. private Stream inputStream;
  17. private StreamWriter outputStream;
  18. public string http_method;
  19. public string http_url;
  20. public string http_protocol_versionstring;
  21. private string querystr; //GET方式请求,路径后接的"?key=value……"
  22. private Dictionary<string, string> httpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  23. /// <summary>
  24. /// 显式参数列表
  25. /// </summary>
  26. public Dictionary<string, string> GETqueryParams()
  27. {
  28. var rslt = new Dictionary<string, string>();
  29. if (!string.IsNullOrEmpty(querystr))
  30. {
  31. var kvs = querystr.Split('&');
  32. foreach (var kv in kvs)
  33. {
  34. var index = kv.IndexOf('=');
  35. if (index > 0)
  36. {
  37. var key = kv.Substring(0, index);
  38. var value = kv.Substring(index + 1);
  39. rslt[key] = value;
  40. }
  41. }
  42. }
  43. return rslt;
  44. }
  45. /// <summary>
  46. /// 显式参数列表
  47. /// </summary>
  48. public Dictionary<string, string> POSTqueryParams(StreamReader inputData)
  49. {
  50. var rslt = new Dictionary<string, string>();
  51. if (inputData != null)
  52. {
  53. var rawData = inputData.ReadToEnd();
  54. if (!string.IsNullOrEmpty(rawData))
  55. {
  56. var kvs = rawData.Split('&');
  57. foreach (var kv in kvs)
  58. {
  59. var index = kv.IndexOf('=');
  60. if (index > 0)
  61. {
  62. var key = kv.Substring(0, index);
  63. var value = HttpUtility.UrlDecode(kv.Substring(index + 1));
  64. rslt[key] = value;
  65. }
  66. }
  67. }
  68. }
  69. return rslt;
  70. }
  71. private static int MAX_POST_SIZE = 10*1024*1024; // 10MB
  72. public LJHttpProcessor(TcpClient s, LJHttpServer srv)
  73. {
  74. socket = s;
  75. this.srv = srv;
  76. }
  77. private string streamReadLine(Stream inputStream)
  78. {
  79. string data = "";
  80. while (true)
  81. {
  82. var next_char = inputStream.ReadByte();
  83. if (next_char == '\n')
  84. {
  85. break;
  86. }
  87. if (next_char == '\r')
  88. {
  89. continue;
  90. }
  91. if (next_char == -1)
  92. {
  93. Thread.Sleep(10);
  94. continue;
  95. }
  96. ;
  97. data += Convert.ToChar(next_char);
  98. }
  99. return data;
  100. }
  101. public void Process()
  102. {
  103. // we can't use a StreamReader for input, because it buffers up extra data on us inside it's
  104. // "processed" view of the world, and we want the data raw after the headers
  105. inputStream = new BufferedStream(socket.GetStream());
  106. // we probably shouldn't be using a streamwriter for all output from handlers either
  107. outputStream = new StreamWriter(new BufferedStream(socket.GetStream()));
  108. try
  109. {
  110. ParseRequest();
  111. ReadHeaders();
  112. if (http_method.Equals("GET"))
  113. {
  114. HandleGetRequest();
  115. }
  116. else if (http_method.Equals("POST"))
  117. {
  118. HandlePostRequest();
  119. }
  120. }
  121. catch (Exception e)
  122. {
  123. Console.WriteLine("Exception: " + e);
  124. WriteError("{\"ErrMsg\":\"" + e + "\"}");
  125. }
  126. outputStream.Flush();
  127. inputStream = null;
  128. outputStream = null; // bs = null;
  129. socket.Close();
  130. }
  131. private void ParseRequest()
  132. {
  133. String request = streamReadLine(inputStream);
  134. string[] tokens = request.Split(' ');
  135. if (tokens.Length != 3)
  136. {
  137. throw new Exception("invalid http request line");
  138. }
  139. http_method = tokens[0].ToUpper();
  140. http_url = tokens[1];
  141. if (http_url.IndexOf('?') > 0)
  142. {
  143. var qsIndex = http_url.IndexOf('?');
  144. querystr = http_url.Substring(qsIndex + 1);
  145. http_url = http_url.Substring(0, qsIndex);
  146. }
  147. http_protocol_versionstring = tokens[2];
  148. Console.WriteLine("starting: " + request);
  149. }
  150. private void ReadHeaders()
  151. {
  152. Console.WriteLine("readHeaders()");
  153. String line;
  154. while ((line = streamReadLine(inputStream)) != null)
  155. {
  156. if (line.Equals(""))
  157. {
  158. Console.WriteLine("got headers");
  159. return;
  160. }
  161. int separator = line.IndexOf(':');
  162. if (separator == -1)
  163. {
  164. throw new Exception("invalid http header line: " + line);
  165. }
  166. String name = line.Substring(0, separator);
  167. int pos = separator + 1;
  168. while ((pos < line.Length) && (line[pos] == ' '))
  169. {
  170. pos++; // strip any spaces
  171. }
  172. string value = line.Substring(pos, line.Length - pos);
  173. Console.WriteLine("header: {0}:{1}", name, value);
  174. httpHeaders[name] = value;
  175. }
  176. }
  177. private void HandleGetRequest()
  178. {
  179. srv.HandleGetRequest(this);
  180. }
  181. private const int BUF_SIZE = 4096;
  182. private void HandlePostRequest()
  183. {
  184. // this post data processing just reads everything into a memory stream.
  185. // this is fine for smallish things, but for large stuff we should really
  186. // hand an input stream to the request processor. However, the input stream
  187. // we hand him needs to let him see the "end of the stream" at this content
  188. // length, because otherwise he won't know when he's seen it all!
  189. Console.WriteLine("get post data start");
  190. int content_len = 0;
  191. using (var ms = new MemoryStream())
  192. {
  193. if (httpHeaders.ContainsKey("Content-Length"))
  194. {
  195. content_len = Convert.ToInt32(httpHeaders["Content-Length"]);
  196. if (content_len > MAX_POST_SIZE)
  197. {
  198. throw new Exception(
  199. String.Format("POST Content-Length({0}) too big for this simple server",
  200. content_len));
  201. }
  202. byte[] buf = new byte[BUF_SIZE];
  203. int to_read = content_len;
  204. while (to_read > 0)
  205. {
  206. Console.WriteLine("starting Read, to_read={0}", to_read);
  207. int numread = inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read));
  208. Console.WriteLine("read finished, numread={0}", numread);
  209. if (numread == 0)
  210. {
  211. if (to_read == 0)
  212. {
  213. break;
  214. }
  215. else
  216. {
  217. throw new Exception("client disconnected during post");
  218. }
  219. }
  220. to_read -= numread;
  221. ms.Write(buf, 0, numread);
  222. }
  223. ms.Seek(0, SeekOrigin.Begin);
  224. }
  225. Console.WriteLine("get post data end");
  226. srv.HandlePostRequest(this, new StreamReader(ms));
  227. }
  228. }
  229. public void WriteResponse(byte[] content,string contentType)
  230. {
  231. if (content == null)
  232. {
  233. WriteError("data not found");
  234. return;
  235. }
  236. if (string.IsNullOrEmpty(contentType))
  237. {
  238. contentType = "text/plain; charset=utf-8";
  239. }
  240. WriteResponseHeaders(new[]
  241. {
  242. "HTTP/1.0 200 OK",
  243. "Content-Type: " + contentType,
  244. "Content-Length: " + content.Length,
  245. "Connection: close"
  246. });
  247. using (var byteStream = new BufferedStream(socket.GetStream()))
  248. {
  249. byteStream.Write(content, 0, content.Length);
  250. byteStream.Flush();
  251. }
  252. }
  253. public void WriteResponse(string content, string contentType)
  254. {
  255. if (string.IsNullOrEmpty(contentType))
  256. {
  257. contentType = "text/plain; charset=utf-8";
  258. }
  259. WriteResponseHeaders(new[]
  260. {
  261. "HTTP/1.0 200 OK",
  262. "Content-Type: " + contentType,
  263. "Connection: close"
  264. });
  265. if (content != null)
  266. {
  267. outputStream.WriteLine(content);
  268. outputStream.Flush();
  269. }
  270. }
  271. public void WriteResponse(string content)
  272. {
  273. WriteResponse(content, null);
  274. }
  275. public void WriteFailure()
  276. {
  277. WriteResponseHeaders(new[]
  278. {
  279. "HTTP/1.0 404 File not found",
  280. "Connection: close"
  281. });
  282. }
  283. public void WriteError(string message)
  284. {
  285. WriteResponse(message);
  286. }
  287. private void WriteResponseHeaders(string[] headers)
  288. {
  289. if (headers == null || headers.Length == 0)
  290. {
  291. throw new Exception("response headers can not be empty");
  292. }
  293. foreach (var header in headers)
  294. {
  295. outputStream.WriteLine(header);
  296. }
  297. outputStream.WriteLine("");
  298. outputStream.Flush();
  299. }
  300. public void WriteResponse(string[] headers, string content)
  301. {
  302. WriteResponseHeaders(headers);
  303. if (content != null)
  304. {
  305. outputStream.WriteLine(content);
  306. outputStream.Flush();
  307. }
  308. }
  309. public void WriteResponse(string[] headers, byte[] content)
  310. {
  311. WriteResponseHeaders(headers);
  312. if (content != null)
  313. {
  314. using (var byteStream = new BufferedStream(socket.GetStream()))
  315. {
  316. byteStream.Write(content, 0, content.Length);
  317. byteStream.Flush();
  318. }
  319. }
  320. }
  321. }
  322. }