HttpHandler.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. using BeetleX;
  2. using BeetleX.Buffers;
  3. using BeetleX.EventArgs;
  4. using SpanJson;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace PlatformBenchmarks
  10. {
  11. public partial class HttpHandler : ServerHandlerBase
  12. {
  13. private static AsciiString _line = new AsciiString("\r\n");
  14. private static AsciiString _2line = new AsciiString("\r\n\r\n");
  15. private static AsciiString _httpsuccess = new AsciiString("HTTP/1.1 200 OK\r\n");
  16. private static readonly AsciiString _headerServer = "Server: TFB\r\n";
  17. private static readonly AsciiString _headerContentLength = "Content-Length: ";
  18. private static readonly AsciiString _headerContentLengthZero = "Content-Length: 0\r\n";
  19. private static readonly AsciiString _headerContentTypeText = "Content-Type: text/plain\r\n";
  20. private static readonly AsciiString _headerContentTypeHtml = "Content-Type: text/html; charset=UTF-8\r\n";
  21. private static readonly AsciiString _headerContentTypeJson = "Content-Type: application/json\r\n";
  22. private static readonly AsciiString _textResultHeader = _httpsuccess + _headerServer.ToString() + _headerContentTypeText.ToString();
  23. private static readonly AsciiString _jsonResultHeader = _httpsuccess + _headerServer.ToString() + _headerContentTypeJson.ToString();
  24. private static readonly AsciiString _htmlResultHeader = _httpsuccess + _headerServer.ToString() + _headerContentTypeHtml.ToString();
  25. private static readonly AsciiString _path_Json = "/json";
  26. private static readonly AsciiString _path_Db = "/db";
  27. private static readonly AsciiString _path_Queries = "/queries";
  28. private static readonly AsciiString _path_Plaintext = "/plaintext";
  29. private static readonly AsciiString _path_Updates = "/updates";
  30. private static readonly AsciiString _path_Fortunes = "/fortunes";
  31. private static readonly AsciiString _result_plaintext = "Hello, World!";
  32. private static readonly AsciiString _cached_worlds = "/cached-worlds";
  33. private static byte _Space = 32;
  34. public const int _LengthSize = 6;
  35. private static byte _question = 63;
  36. public HttpHandler()
  37. {
  38. RequestDispatchs = new BeetleX.Dispatchs.DispatchCenter<HttpToken>(OnRequest, Math.Min(Environment.ProcessorCount, 16));
  39. }
  40. private BeetleX.Dispatchs.DispatchCenter<HttpToken> RequestDispatchs;
  41. public Task Default(PipeStream stream, HttpToken token, ISession session)
  42. {
  43. stream.Write("<b> beetlex server</b><hr/>");
  44. stream.Write("path not found!");
  45. OnCompleted(stream, session, token);
  46. return Task.CompletedTask;
  47. }
  48. public override void Connected(IServer server, ConnectedEventArgs e)
  49. {
  50. base.Connected(server, e);
  51. e.Session.Socket.NoDelay = true;
  52. var token = new HttpToken();
  53. token.ThreadDispatcher = RequestDispatchs.Next();
  54. token.Session = e.Session;
  55. token.Db = new RawDb(new ConcurrentRandom(), Npgsql.NpgsqlFactory.Instance);
  56. e.Session.Tag = token;
  57. }
  58. private int AnalysisUrl(ReadOnlySpan<byte> url)
  59. {
  60. for (int i = 0; i < url.Length; i++)
  61. {
  62. if (url[i] == _question)
  63. return i;
  64. }
  65. return -1;
  66. }
  67. private void OnRequest(HttpToken token)
  68. {
  69. if (token.Requests.TryDequeue(out RequestData result))
  70. {
  71. OnStartRequest(result, token.Session, token, token.Session.Stream.ToPipeStream());
  72. }
  73. }
  74. public override void SessionReceive(IServer server, SessionReceiveEventArgs e)
  75. {
  76. base.SessionReceive(server, e);
  77. PipeStream pipeStream = e.Session.Stream.ToPipeStream();
  78. HttpToken token = (HttpToken)e.Session.Tag;
  79. var result = pipeStream.IndexOfLine();
  80. while (result.End != null)
  81. {
  82. if (result.Length == 2)
  83. {
  84. pipeStream.ReadFree(result.Length);
  85. OnStartRequest(token.CurrentRequest, e.Session, token, pipeStream);
  86. }
  87. else
  88. {
  89. if (token.CurrentRequest == null)
  90. {
  91. var request = new RequestData();
  92. byte[] buffer = null;
  93. buffer = new byte[result.Length];
  94. pipeStream.Read(buffer, 0, result.Length);
  95. request.Data = new ArraySegment<byte>(buffer, 0, result.Length);
  96. AnalysisAction(request);
  97. if (request.Action == ActionType.Plaintext)
  98. {
  99. token.CurrentRequest = request;
  100. }
  101. else
  102. {
  103. token.CurrentRequest = request;
  104. pipeStream.ReadFree((int)pipeStream.Length);
  105. OnStartRequest(request, e.Session, token, pipeStream);
  106. return;
  107. }
  108. }
  109. else
  110. {
  111. pipeStream.ReadFree(result.Length);
  112. }
  113. }
  114. if (pipeStream.Length > 0)
  115. result = pipeStream.IndexOfLine();
  116. else
  117. break;
  118. }
  119. }
  120. private void AnalysisAction(RequestData requestData)
  121. {
  122. var line = _line.AsSpan();
  123. int len = requestData.Data.Count;
  124. var receiveData = requestData.GetSpan();
  125. ReadOnlySpan<byte> http = line;
  126. ReadOnlySpan<byte> method = line;
  127. ReadOnlySpan<byte> url = line;
  128. int offset2 = 0;
  129. int count = 0;
  130. for (int i = 0; i < len; i++)
  131. {
  132. if (receiveData[i] == line[0])
  133. {
  134. http = receiveData.Slice(offset2, i - offset2);
  135. break;
  136. }
  137. else
  138. {
  139. if (receiveData[i] == _Space)
  140. {
  141. if (count != 0)
  142. {
  143. url = receiveData.Slice(offset2, i - offset2);
  144. offset2 = i + 1;
  145. }
  146. else
  147. {
  148. method = receiveData.Slice(offset2, i - offset2);
  149. offset2 = i + 1;
  150. count++;
  151. }
  152. }
  153. }
  154. }
  155. int queryIndex = AnalysisUrl(url);
  156. ReadOnlySpan<byte> baseUrl = default;
  157. ReadOnlySpan<byte> queryString = default;
  158. if (queryIndex > 0)
  159. {
  160. baseUrl = url.Slice(0, queryIndex);
  161. queryString = url.Slice(queryIndex + 1, url.Length - queryIndex - 1);
  162. requestData.QueryString = Encoding.ASCII.GetString(queryString);
  163. }
  164. else
  165. {
  166. baseUrl = url;
  167. }
  168. if (baseUrl.Length == _path_Plaintext.Length && baseUrl.StartsWith(_path_Plaintext))
  169. {
  170. requestData.Action = ActionType.Plaintext;
  171. }
  172. else if (baseUrl.Length == _path_Json.Length && baseUrl.StartsWith(_path_Json))
  173. {
  174. requestData.Action = ActionType.Json;
  175. }
  176. else if (baseUrl.Length == _path_Db.Length && baseUrl.StartsWith(_path_Db))
  177. {
  178. requestData.Action = ActionType.Db;
  179. }
  180. else if (baseUrl.Length == _path_Queries.Length && baseUrl.StartsWith(_path_Queries))
  181. {
  182. requestData.Action = ActionType.Queries;
  183. }
  184. else if (baseUrl.Length == _cached_worlds.Length && baseUrl.StartsWith(_cached_worlds))
  185. {
  186. requestData.Action = ActionType.Caching;
  187. }
  188. else if (baseUrl.Length == _path_Updates.Length && baseUrl.StartsWith(_path_Updates))
  189. {
  190. requestData.Action = ActionType.Updates;
  191. }
  192. else if (baseUrl.Length == _path_Fortunes.Length && baseUrl.StartsWith(_path_Fortunes))
  193. {
  194. requestData.Action = ActionType.Fortunes;
  195. }
  196. else
  197. {
  198. requestData.Action = ActionType.Other;
  199. }
  200. }
  201. public virtual async Task OnStartRequest(RequestData data, ISession session, HttpToken token, PipeStream stream)
  202. {
  203. OnWriteHeader(stream, token, data.Action);
  204. ActionType type = data.Action;
  205. if (type == ActionType.Plaintext)
  206. {
  207. await Plaintext(stream, token, session);
  208. }
  209. else if (type == ActionType.Json)
  210. {
  211. await Json(stream, token, session);
  212. }
  213. else if (type == ActionType.Db)
  214. {
  215. await db(stream, token, session);
  216. }
  217. else if (type == ActionType.Queries)
  218. {
  219. await queries(data.QueryString, stream, token, session);
  220. }
  221. else if (type == ActionType.Caching)
  222. {
  223. await caching(data.QueryString, stream, token, session);
  224. }
  225. else if (type == ActionType.Updates)
  226. {
  227. await updates(data.QueryString, stream, token, session);
  228. }
  229. else if (type == ActionType.Fortunes)
  230. {
  231. await fortunes(stream, token, session);
  232. }
  233. else
  234. {
  235. await Default(stream, token, session);
  236. }
  237. }
  238. private void OnWriteHeader(PipeStream stream, HttpToken token, ActionType type)
  239. {
  240. switch (type)
  241. {
  242. case ActionType.Caching:
  243. case ActionType.Json:
  244. case ActionType.Queries:
  245. case ActionType.Db:
  246. case ActionType.Updates:
  247. stream.Write(_jsonResultHeader.Data, 0, _jsonResultHeader.Length);
  248. break;
  249. case ActionType.Plaintext:
  250. stream.Write(_textResultHeader.Data, 0, _textResultHeader.Length);
  251. break;
  252. default:
  253. stream.Write(_htmlResultHeader.Data, 0, _htmlResultHeader.Length);
  254. break;
  255. }
  256. ArraySegment<byte> date = GMTDate.Default.DATE;
  257. stream.Write(date.Array, date.Offset, date.Count);
  258. OnWriteContentLength(stream, token);
  259. }
  260. private void OnWriteContentLength(PipeStream stream, HttpToken token)
  261. {
  262. var action = token.CurrentRequest.Action;
  263. if (action == ActionType.Json)
  264. {
  265. stream.Write(_jsonPreamble.Data, 0, _jsonPreamble.Length);
  266. stream.Write(_2line, 0, 4);
  267. }
  268. else if (action == ActionType.Plaintext)
  269. {
  270. stream.Write(_plaintextPreamble.Data, 0, _plaintextPreamble.Length);
  271. stream.Write(_2line, 0, 4);
  272. }
  273. else
  274. {
  275. stream.Write(_headerContentLength.Data, 0, _headerContentLength.Length);
  276. token.ContentLength = stream.Allocate(HttpHandler._LengthSize);
  277. stream.Write(_2line, 0, 4);
  278. token.ContentPostion = stream.CacheLength;
  279. }
  280. }
  281. private void OnCompleted(PipeStream stream, ISession session, HttpToken token)
  282. {
  283. var type = token.CurrentRequest.Action;
  284. if (type != ActionType.Plaintext && type != ActionType.Json)
  285. {
  286. token.FullLength((stream.CacheLength - token.ContentPostion).ToString());
  287. }
  288. if (token.Requests.IsEmpty && stream.Length == 0)
  289. session.Stream.Flush();
  290. token.CurrentRequest = null;
  291. }
  292. }
  293. }