Processor.d 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /// An example "HTTP server" with poor usability but sensible performance
  2. ///
  3. module http.Processor;
  4. import std.conv;
  5. import std.array, std.exception, std.format, std.algorithm.mutation, std.socket;
  6. import core.stdc.stdlib;
  7. import core.thread, core.atomic;
  8. import http.Parser;
  9. import hunt.collection.ByteBuffer;
  10. import http.Common;
  11. import hunt.logging;
  12. import hunt.io;
  13. import hunt.util.DateTime;
  14. private alias Parser = HttpParser!HttpProcessor;
  15. struct HttpRequest {
  16. private Parser* parser;
  17. HttpHeader[] headers(bool canCopy=false)() @property {
  18. return parser.headers!canCopy();
  19. }
  20. HttpMethod method() @property {
  21. return parser.method();
  22. }
  23. string uri(bool canCopy=false)() @property {
  24. return parser.uri!(canCopy)();
  25. }
  26. }
  27. version(NO_HTTPPARSER) {
  28. enum string ResponseData = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\nConnection: Keep-Alive\r\nContent-Type: text/plain\r\nServer: Hunt/1.0\r\nDate: Wed, 17 Apr 2013 12:00:00 GMT\r\n\r\nHello, World!";
  29. }
  30. abstract class HttpProcessor {
  31. package:
  32. Appender!(char[]) outBuf;
  33. HttpHeader[] headers; // buffer for headers
  34. Parser parser;
  35. HttpRequest request;
  36. bool serving;
  37. public:
  38. TcpStream client;
  39. this(TcpStream sock) {
  40. serving = true;
  41. client = sock;
  42. headers = new HttpHeader[1];
  43. parser = httpParser(this);
  44. request.parser = &parser;
  45. }
  46. void run() {
  47. client.onReceived((ByteBuffer buffer) {
  48. version(NO_HTTPPARSER) {
  49. client.write(cast(ubyte[])ResponseData);
  50. } else {
  51. try {
  52. parser.execute(cast(ubyte[]) buffer.getRemaining());
  53. } catch(Exception ex) {
  54. respondWith(ex.msg, 500);
  55. }
  56. }
  57. })
  58. .onClosed(() {
  59. // notifyClientClosed();
  60. })
  61. .onError((string msg) {
  62. debug warning("Error: ", msg);
  63. })
  64. .start();
  65. }
  66. protected void notifyClientClosed() {
  67. debug tracef("The connection[%s] is closed", client.remoteAddress());
  68. }
  69. void respondWith(string _body, uint status, HttpHeader[] headers...) {
  70. return respondWith(cast(const(ubyte)[]) _body, status, headers);
  71. }
  72. void respondWith(const(ubyte)[] _body, uint status, HttpHeader[] headers...) {
  73. outBuf.clear();
  74. formattedWrite(outBuf, "HTTP/1.1 %s OK\r\n", status);
  75. outBuf.put("Server: Hunt/1.0\r\n");
  76. formattedWrite(outBuf, "Date: %s\r\n", DateTimeHelper.getDateAsGMT());
  77. if (!parser.shouldKeepAlive)
  78. outBuf.put("Connection: close\r\n");
  79. foreach (ref hdr; headers) {
  80. outBuf.put(hdr.name);
  81. outBuf.put(": ");
  82. outBuf.put(hdr.value);
  83. outBuf.put("\r\n");
  84. }
  85. formattedWrite(outBuf, "Content-Length: %d\r\n\r\n", _body.length);
  86. outBuf.put(cast(string) _body);
  87. client.write(cast(ubyte[]) outBuf.data); // TODO: short-writes are quite possible
  88. }
  89. void onChunk(ref HttpRequest req, const(ubyte)[] chunk) {
  90. // TODO: Tasks pending completion - 5/16/2019, 5:40:18 PM
  91. //
  92. }
  93. void onComplete(ref HttpRequest req);
  94. final int onBody(Parser* parser, const(ubyte)[] chunk) {
  95. onChunk(request, chunk);
  96. return 0;
  97. }
  98. final int onMessageComplete() {
  99. try {
  100. onComplete(request);
  101. } catch(Exception ex) {
  102. respondWith(ex.msg, 500);
  103. }
  104. if (!parser.shouldKeepAlive)
  105. serving = false;
  106. return 0;
  107. }
  108. }