Processor.d 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /// An example "HTTP server" with poor usability but sensible performance
  2. ///
  3. module http.Processor;
  4. import std.array, std.exception, std.format, std.algorithm.mutation, std.socket;
  5. import core.stdc.stdlib;
  6. import core.thread, core.atomic;
  7. import http.Parser;
  8. import hunt.util.DateTime;
  9. import hunt.logging;
  10. import hunt.io;
  11. struct HttpHeader {
  12. string name, value;
  13. }
  14. struct HttpRequest {
  15. HttpHeader[] headers;
  16. HttpMethod method;
  17. string uri;
  18. }
  19. abstract class HttpProcessor {
  20. private:
  21. enum State {
  22. url,
  23. field,
  24. value,
  25. done
  26. }
  27. ubyte[] buffer;
  28. Appender!(char[]) outBuf;
  29. HttpHeader[] headers; // buffer for headers
  30. size_t header; // current header
  31. string url; // url
  32. alias Parser = HttpParser!HttpProcessor;
  33. Parser parser;
  34. ScratchPad pad;
  35. HttpRequest request;
  36. State state;
  37. bool serving;
  38. public:
  39. TcpStream client;
  40. this(TcpStream sock) {
  41. serving = true;
  42. client = sock;
  43. buffer = new ubyte[2048];
  44. headers = new HttpHeader[1];
  45. pad = ScratchPad(16 * 1024);
  46. parser = httpParser(this, HttpParserType.request);
  47. }
  48. void run() {
  49. client.onDataReceived((const ubyte[] data) {
  50. parser.execute(data);
  51. })
  52. .onClosed(() {
  53. // notifyClientClosed();
  54. })
  55. .onError((string msg) {
  56. debug warning("Error: ", msg);
  57. })
  58. .start();
  59. }
  60. protected void notifyClientClosed() {
  61. debug tracef("The connection[%s] is closed", client.remoteAddress());
  62. }
  63. void respondWith(string _body, uint status, HttpHeader[] headers...) {
  64. return respondWith(cast(const(ubyte)[]) _body, status, headers);
  65. }
  66. void respondWith(const(ubyte)[] _body, uint status, HttpHeader[] headers...) {
  67. formattedWrite(outBuf, "HTTP/1.1 %s OK\r\n", status);
  68. outBuf.put("Server: Hunt/1.0\r\n");
  69. formattedWrite(outBuf, "Date: %s\r\n", DateTimeHelper.getDateAsGMT());
  70. if (!parser.shouldKeepAlive)
  71. outBuf.put("Connection: close\r\n");
  72. foreach (ref hdr; headers) {
  73. outBuf.put(hdr.name);
  74. outBuf.put(": ");
  75. outBuf.put(hdr.value);
  76. outBuf.put("\r\n");
  77. }
  78. formattedWrite(outBuf, "Content-Length: %d\r\n\r\n", _body.length);
  79. outBuf.put(cast(string) _body);
  80. client.write(cast(ubyte[]) outBuf.data); // TODO: short-writes are quite possible
  81. }
  82. void onStart(HttpRequest req) {
  83. }
  84. void onChunk(HttpRequest req, const(ubyte)[] chunk) {
  85. }
  86. void onComplete(HttpRequest req);
  87. final int onMessageBegin(Parser* parser) {
  88. outBuf.clear();
  89. header = 0;
  90. pad.reset();
  91. state = State.url;
  92. return 0;
  93. }
  94. final int onUrl(Parser* parser, const(ubyte)[] chunk) {
  95. pad.put(chunk);
  96. return 0;
  97. }
  98. final int onBody(Parser* parser, const(ubyte)[] chunk) {
  99. onChunk(request, chunk);
  100. return 0;
  101. }
  102. final int onHeaderField(Parser* parser, const(ubyte)[] chunk) {
  103. final switch (state) {
  104. case State.url:
  105. url = pad.sliceStr;
  106. break;
  107. case State.value:
  108. headers[header].value = pad.sliceStr;
  109. header += 1;
  110. if (headers.length <= header)
  111. headers.length += 1;
  112. break;
  113. case State.field:
  114. case State.done:
  115. break;
  116. }
  117. state = State.field;
  118. pad.put(chunk);
  119. return 0;
  120. }
  121. final int onHeaderValue(Parser* parser, const(ubyte)[] chunk) {
  122. if (state == State.field) {
  123. headers[header].name = pad.sliceStr;
  124. }
  125. pad.put(chunk);
  126. state = State.value;
  127. return 0;
  128. }
  129. final int onHeadersComplete(Parser* parser) {
  130. headers[header].value = pad.sliceStr;
  131. header += 1;
  132. request = HttpRequest(headers[0 .. header], parser.method, url);
  133. onStart(request);
  134. state = State.done;
  135. return 0;
  136. }
  137. final int onMessageComplete(Parser* parser) {
  138. import std.stdio;
  139. if (state == State.done) {
  140. try {
  141. onComplete(request);
  142. } catch(Exception ex) {
  143. respondWith(ex.msg, 500);
  144. }
  145. }
  146. if (!parser.shouldKeepAlive)
  147. serving = false;
  148. return 0;
  149. }
  150. }
  151. // ==================================== IMPLEMENTATION DETAILS ==============================================
  152. private:
  153. struct ScratchPad {
  154. ubyte* ptr;
  155. size_t capacity;
  156. size_t last, current;
  157. this(size_t size) {
  158. ptr = cast(ubyte*) malloc(size);
  159. capacity = size;
  160. }
  161. void put(const(ubyte)[] slice) {
  162. enforce(current + slice.length <= capacity, "HTTP headers too long");
  163. ptr[current .. current + slice.length] = slice[];
  164. current += slice.length;
  165. }
  166. const(ubyte)[] slice() {
  167. auto data = ptr[last .. current];
  168. last = current;
  169. return data;
  170. }
  171. string sliceStr() {
  172. return cast(string) slice;
  173. }
  174. void reset() {
  175. current = 0;
  176. last = 0;
  177. }
  178. @disable this(this);
  179. ~this() {
  180. free(ptr);
  181. ptr = null;
  182. }
  183. }