DemoProcessor.d 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. module DemoProcessor;
  2. // import stdx.data.json;
  3. import std.json;
  4. import hunt.database;
  5. import hunt.io;
  6. import http.Common;
  7. import http.Processor;
  8. import http.HttpURI;
  9. import http.UrlEncoded;
  10. import hunt.logging.ConsoleLogger : trace, warning, tracef;
  11. import std.algorithm;
  12. import std.array;
  13. import std.exception;
  14. import std.random;
  15. import std.string;
  16. version (POSTGRESQL) {
  17. __gshared Database dbConnection;
  18. }
  19. enum HttpHeader textHeader = HttpHeader("Content-Type", "text/plain; charset=UTF-8");
  20. enum HttpHeader htmlHeader = HttpHeader("Content-Type", "text/html; charset=UTF-8");
  21. enum HttpHeader jsonHeader = HttpHeader("Content-Type", "application/json; charset=UTF-8");
  22. enum plaintextLength = "/plaintext".length;
  23. enum jsonLength = "/json".length;
  24. enum dbLength = "/db".length;
  25. enum fortunesLength = "/fortunes".length;
  26. class DemoProcessor : HttpProcessor {
  27. version (POSTGRESQL) HttpURI uri;
  28. this(TcpStream client) {
  29. version (POSTGRESQL) uri = new HttpURI();
  30. super(client);
  31. }
  32. override void onComplete(ref HttpRequest req) {
  33. debug {
  34. trace(req.uri());
  35. trace(req.method());
  36. trace(req.headers());
  37. }
  38. string path = req.uri;
  39. // auto uri = new HttpURI(req.uri);
  40. // uri.parse(req.uri);
  41. // if(cmp(path, "/plaintext") == 0) {
  42. // respondWith("Hello, World!", 200, textHeader);
  43. // } else if(cmp(path, "/json") == 0) {
  44. // JSONValue js = JSONValue(["message" : JSONValue("Hello, World!")]);
  45. // respondWith(js.toJSON(), 200, jsonHeader);
  46. // } else {
  47. // respondWith404();
  48. // }
  49. if(path.length == plaintextLength) { // plaintext
  50. respondWith("Hello, World!", 200, textHeader);
  51. } else if(path.length == jsonLength) { // json
  52. JSONValue js = JSONValue(["message" : JSONValue("Hello, World!")]);
  53. respondWith(js.toJSON(), 200, jsonHeader);
  54. } else {
  55. version (POSTGRESQL) {
  56. if(path.length == dbLength) {
  57. respondSingleQuery();
  58. } else if(path.length == fortunesLength) {
  59. respondFortunes();
  60. } else {
  61. handleDbUpdate(path);
  62. }
  63. } else {
  64. respondWith404();
  65. }
  66. }
  67. }
  68. private void respondWith404() {
  69. version (POSTGRESQL) {
  70. respondWith("The available paths are: /plaintext, /json, /db, /fortunes," ~
  71. " /queries?queries=number, /updates?queries=number", 404);
  72. } else {
  73. respondWith("The available paths are: /plaintext, /json", 404);
  74. }
  75. }
  76. version (POSTGRESQL) {
  77. private void handleDbUpdate(string url) {
  78. uri.parse(url);
  79. switch(uri.getPath()) {
  80. case "/queries":
  81. UrlEncoded queriesMap = new UrlEncoded();
  82. uri.decodeQueryTo(queriesMap);
  83. int number = 1;
  84. debug {
  85. trace(queriesMap.toString());
  86. if (!queriesMap.containsKey("queries")) {
  87. respondWith404();
  88. return;
  89. }
  90. string v = queriesMap.getValue("queries", 0);
  91. if (!v.empty) {
  92. try {
  93. number = to!int(v);
  94. } catch (Exception ex) {
  95. warning(ex.msg);
  96. }
  97. }
  98. } else {
  99. string v = queriesMap.getValue("queries", 0);
  100. if (!v.empty) {
  101. try {
  102. number = to!int(v);
  103. } catch (Exception ex) {
  104. }
  105. }
  106. }
  107. respondMultipleQuery(number);
  108. break;
  109. case "/updates":
  110. UrlEncoded queriesMap = new UrlEncoded();
  111. uri.decodeQueryTo(queriesMap);
  112. int number = 1;
  113. debug {
  114. if (!queriesMap.containsKey("queries")) {
  115. respondWith404();
  116. return;
  117. }
  118. string v = queriesMap.getValue("queries", 0);
  119. if (!v.empty) {
  120. try {
  121. number = to!int(v);
  122. } catch (Exception ex) {
  123. warning(ex.msg);
  124. }
  125. }
  126. } else {
  127. string v = queriesMap.getValue("queries", 0);
  128. if (!v.empty) {
  129. try {
  130. number = to!int(v);
  131. } catch (Exception ex) {
  132. }
  133. }
  134. }
  135. respondUpdates(number);
  136. break;
  137. default:
  138. respondWith404();
  139. break;
  140. }
  141. }
  142. private void respondSingleQuery() {
  143. int id = uniform(1, 10001);
  144. string query = "SELECT id, randomNumber FROM world WHERE id = " ~ id.to!string;
  145. ResultSet rs = dbConnection.query(query);
  146. JSONValue js = JSONValue(["id" : JSONValue(id), "randomNumber"
  147. : JSONValue(to!int(rs.front()[0]))]);
  148. respondWith(js.toJSON(), 200, jsonHeader);
  149. }
  150. private void respondMultipleQuery(int queries) {
  151. if (queries < 1)
  152. queries = 1;
  153. else if (queries > 500)
  154. queries = 500;
  155. JSONValue[] arr = new JSONValue[queries];
  156. for (int i = 0; i < queries; i++) {
  157. immutable id = uniform(1, 10001);
  158. immutable query = "SELECT id, randomNumber FROM world WHERE id = " ~ id.to!string;
  159. ResultSet rs = dbConnection.query(query);
  160. arr[i] = JSONValue(["id" : JSONValue(id), "randomNumber"
  161. : JSONValue(to!int(rs.front()[0]))]);
  162. }
  163. JSONValue js = JSONValue(arr);
  164. respondWith(js.toJSON(), 200, jsonHeader);
  165. }
  166. private void respondFortunes() {
  167. immutable query = "SELECT id, message::text FROM Fortune";
  168. ResultSet rs = dbConnection.query(query);
  169. FortuneModel[] data = rs.map!(f => FortuneModel(f["id"].to!int, f["message"])).array;
  170. data ~= FortuneModel(0, "Additional fortune added at request time.");
  171. data.sort!((a, b) => a.message < b.message);
  172. // trace(data);
  173. respondWith(randerFortunes(data), 200, htmlHeader);
  174. }
  175. static string randerFortunes(FortuneModel[] data) {
  176. Appender!string sb;
  177. sb.put(`<!DOCTYPE html>
  178. <html>
  179. <head>
  180. <title>Fortunes</title>
  181. </head>
  182. <body>
  183. <table>
  184. <tr>
  185. <th>id</th><th>message</th>
  186. </tr>
  187. `);
  188. foreach (FortuneModel f; data) {
  189. string message = replace(f.message, ">", "&gt;");
  190. message = replace(message, "<", "&lt;");
  191. message = replace(message, "\"", "&quot;");
  192. sb.put(format(" <tr>\n <td>%d</td><td>%s</td>\n </tr>\n", f.id, message));
  193. }
  194. sb.put(" </table>\n </body>\n</html>");
  195. return sb.data;
  196. }
  197. private void respondUpdates(int queries) {
  198. if (queries < 1)
  199. queries = 1;
  200. else if (queries > 500)
  201. queries = 500;
  202. JSONValue[] arr = new JSONValue[queries];
  203. for (int i = 0; i < queries; i++) {
  204. immutable id = uniform(1, 10001);
  205. immutable idString = id.to!string;
  206. immutable query = "SELECT id, randomNumber FROM world WHERE id = " ~ idString;
  207. ResultSet rs = dbConnection.query(query);
  208. int randomNumber = to!int(rs.front()[0]);
  209. debug tracef("id=%d, randomNumber=%d", id, randomNumber);
  210. randomNumber = uniform(1, 10001);
  211. string updateSql = "UPDATE world SET randomNumber = "
  212. ~ randomNumber.to!string ~ " WHERE id = " ~ idString;
  213. int r = dbConnection.execute(updateSql);
  214. // debug tracef("r=%d", r);
  215. arr[i] = JSONValue(["id" : JSONValue(id), "randomNumber" : JSONValue(randomNumber)]);
  216. }
  217. JSONValue js = JSONValue(arr);
  218. respondWith(js.toJSON(), 200, jsonHeader);
  219. }
  220. }
  221. }
  222. struct FortuneModel {
  223. int id;
  224. string message;
  225. }