DemoProcessor.d 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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. string path = req.uri;
  34. if(path.length == plaintextLength) { // plaintext
  35. respondWith("Hello, World!", 200, textHeader);
  36. } else if(path.length == jsonLength) { // json
  37. JSONValue js = JSONValue(["message" : JSONValue("Hello, World!")]);
  38. respondWith(js.toJSON(), 200, jsonHeader);
  39. } else {
  40. version (POSTGRESQL) {
  41. if(path.length == dbLength) {
  42. respondSingleQuery();
  43. } else if(path.length == fortunesLength) {
  44. respondFortunes();
  45. } else {
  46. handleDbUpdate(path);
  47. }
  48. } else {
  49. respondWith404();
  50. }
  51. }
  52. }
  53. private void respondWith404() {
  54. version (POSTGRESQL) {
  55. respondWith("The available paths are: /plaintext, /json, /db, /fortunes," ~
  56. " /queries?queries=number, /updates?queries=number", 404);
  57. } else {
  58. respondWith("The available paths are: /plaintext, /json", 404);
  59. }
  60. }
  61. version (POSTGRESQL) {
  62. private void handleDbUpdate(string url) {
  63. uri.parse(url);
  64. switch(uri.getPath()) {
  65. case "/queries":
  66. UrlEncoded queriesMap = new UrlEncoded();
  67. uri.decodeQueryTo(queriesMap);
  68. int number = 1;
  69. debug {
  70. trace(queriesMap.toString());
  71. if (!queriesMap.containsKey("queries")) {
  72. respondWith404();
  73. return;
  74. }
  75. string v = queriesMap.getValue("queries", 0);
  76. if (!v.empty) {
  77. try {
  78. number = to!int(v);
  79. } catch (Exception ex) {
  80. warning(ex.msg);
  81. }
  82. }
  83. } else {
  84. string v = queriesMap.getValue("queries", 0);
  85. if (!v.empty) {
  86. try {
  87. number = to!int(v);
  88. } catch (Exception ex) {
  89. }
  90. }
  91. }
  92. respondMultipleQuery(number);
  93. break;
  94. case "/updates":
  95. UrlEncoded queriesMap = new UrlEncoded();
  96. uri.decodeQueryTo(queriesMap);
  97. int number = 1;
  98. debug {
  99. if (!queriesMap.containsKey("queries")) {
  100. respondWith404();
  101. return;
  102. }
  103. string v = queriesMap.getValue("queries", 0);
  104. if (!v.empty) {
  105. try {
  106. number = to!int(v);
  107. } catch (Exception ex) {
  108. warning(ex.msg);
  109. }
  110. }
  111. } else {
  112. string v = queriesMap.getValue("queries", 0);
  113. if (!v.empty) {
  114. try {
  115. number = to!int(v);
  116. } catch (Exception ex) {
  117. }
  118. }
  119. }
  120. respondUpdates(number);
  121. break;
  122. default:
  123. respondWith404();
  124. break;
  125. }
  126. }
  127. private void respondSingleQuery() {
  128. int id = uniform(1, 10000);
  129. string query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
  130. ResultSet rs = dbConnection.query(query);
  131. JSONValue js = JSONValue(["id" : JSONValue(id), "randomNumber"
  132. : JSONValue(to!int(rs.front()[0]))]);
  133. respondWith(js.toJSON(), 200, jsonHeader);
  134. }
  135. private void respondMultipleQuery(int queries) {
  136. if (queries < 1)
  137. queries = 1;
  138. else if (queries > 500)
  139. queries = 500;
  140. JSONValue[] arr = new JSONValue[queries];
  141. for (int i = 0; i < queries; i++) {
  142. immutable id = uniform(1, 10000);
  143. immutable query = "SELECT randomNumber FROM world WHERE id = " ~ id.to!string;
  144. ResultSet rs = dbConnection.query(query);
  145. arr[i] = JSONValue(["id" : JSONValue(id), "randomNumber"
  146. : JSONValue(to!int(rs.front()[0]))]);
  147. }
  148. JSONValue js = JSONValue(arr);
  149. respondWith(js.toJSON(), 200, jsonHeader);
  150. }
  151. private void respondFortunes() {
  152. immutable query = "SELECT id, message::text FROM Fortune";
  153. ResultSet rs = dbConnection.query(query);
  154. FortuneModel[] data = rs.map!(f => FortuneModel(f["id"].to!int, f["message"])).array;
  155. data ~= FortuneModel(0, "Additional fortune added at request time.");
  156. data.sort!((a, b) => a.message < b.message);
  157. // trace(data);
  158. respondWith(randerFortunes(data), 200, htmlHeader);
  159. }
  160. static string randerFortunes(FortuneModel[] data) {
  161. Appender!string sb;
  162. sb.put(`<!DOCTYPE html>
  163. <html>
  164. <head>
  165. <title>Fortunes</title>
  166. </head>
  167. <body>
  168. <table>
  169. <tr>
  170. <th>id</th><th>message</th>
  171. </tr>
  172. `);
  173. foreach (FortuneModel f; data) {
  174. string message = replace(f.message, ">", "&gt;");
  175. message = replace(message, "<", "&lt;");
  176. message = replace(message, "\"", "&quot;");
  177. sb.put(format(" <tr>\n <td>%d</td><td>%s</td>\n </tr>\n", f.id, message));
  178. }
  179. sb.put(" </table>\n </body>\n</html>");
  180. return sb.data;
  181. }
  182. private void respondUpdates(int queries) {
  183. if (queries < 1)
  184. queries = 1;
  185. else if (queries > 500)
  186. queries = 500;
  187. JSONValue[] arr = new JSONValue[queries];
  188. for (int i = 0; i < queries; i++) {
  189. immutable id = uniform(1, 10000);
  190. immutable idString = id.to!string;
  191. immutable query = "SELECT randomNumber FROM world WHERE id = " ~ idString;
  192. ResultSet rs = dbConnection.query(query);
  193. int randomNumber = to!int(rs.front()[0]);
  194. debug tracef("id=%d, randomNumber=%d", id, randomNumber);
  195. randomNumber = uniform(1, 10000);
  196. string updateSql = "UPDATE world SET randomNumber = "
  197. ~ randomNumber.to!string ~ " WHERE id = " ~ idString;
  198. int r = dbConnection.execute(updateSql);
  199. // debug tracef("r=%d", r);
  200. arr[i] = JSONValue(["id" : JSONValue(id), "randomNumber" : JSONValue(randomNumber)]);
  201. }
  202. JSONValue js = JSONValue(arr);
  203. respondWith(js.toJSON(), 200, jsonHeader);
  204. }
  205. }
  206. }
  207. struct FortuneModel {
  208. int id;
  209. string message;
  210. }