LJHttpProcessor.cs 12 KB

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