Parser.d 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /// Minimalistic low-overhead wrapper for nodejs/http-parser
  2. /// Used for benchmarks with simple server
  3. module http.Parser;
  4. import http.Common;
  5. import hunt.logging.ConsoleLogger;
  6. import std.conv;
  7. import std.range.primitives;
  8. import core.stdc.string;
  9. /* contains name and value of a header (name == NULL if is a continuing line
  10. * of a multiline header */
  11. struct phr_header {
  12. const char *name;
  13. size_t name_len;
  14. const char *value;
  15. size_t value_len;
  16. }
  17. /* returns number of bytes consumed if successful, -2 if request is partial,
  18. * -1 if failed */
  19. extern (C) pure @nogc nothrow int phr_parse_request(const char *buf, size_t len, const char **method,
  20. size_t *method_len, const char **path, size_t *path_len,
  21. int *minor_version, phr_header *headers, size_t *num_headers, size_t last_len);
  22. /* ditto */
  23. extern (C) pure @nogc nothrow int phr_parse_response(const char *_buf, size_t len, int *minor_version,
  24. int *status, const char **msg, size_t *msg_len,
  25. phr_header *headers, size_t *num_headers, size_t last_len);
  26. /* ditto */
  27. extern (C) pure @nogc nothrow int phr_parse_headers(const char *buf, size_t len,
  28. phr_header *headers, size_t *num_headers, size_t last_len);
  29. /* should be zero-filled before start */
  30. struct phr_chunked_decoder {
  31. size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
  32. char consume_trailer; /* if trailing headers should be consumed */
  33. char _hex_count;
  34. char _state;
  35. }
  36. /* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
  37. * encoding headers. When the function returns without an error, bufsz is
  38. * updated to the length of the decoded data available. Applications should
  39. * repeatedly call the function while it returns -2 (incomplete) every time
  40. * supplying newly arrived data. If the end of the chunked-encoded data is
  41. * found, the function returns a non-negative number indicating the number of
  42. * octets left undecoded at the tail of the supplied buffer. Returns -1 on
  43. * error.
  44. */
  45. extern (C) pure @nogc nothrow ptrdiff_t phr_decode_chunked(phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
  46. /* returns if the chunked decoder is in middle of chunked data */
  47. extern (C) pure @nogc nothrow int phr_decode_chunked_is_in_data(phr_chunked_decoder *decoder);
  48. // =========== Public interface starts here =============
  49. public:
  50. class HttpException : Exception {
  51. HttpError error;
  52. pure @nogc nothrow this(HttpError error, string file = __FILE__,
  53. size_t line = __LINE__, Throwable nextInChain = null) {
  54. this.error = error;
  55. super("Http exception", file, line, nextInChain);
  56. }
  57. }
  58. struct HttpParser(Interceptor) {
  59. private {
  60. Interceptor interceptor;
  61. Throwable failure;
  62. phr_header[50] _headers;
  63. char *_method;
  64. char *path;
  65. int minor_version;
  66. size_t buflen = 0, prevbuflen = 0, method_len, path_len, num_headers;
  67. }
  68. alias interceptor this;
  69. this(Interceptor interceptor) {
  70. this.interceptor = interceptor;
  71. }
  72. @property bool status() pure @safe nothrow {
  73. return failure is null;
  74. }
  75. string uri(bool canCopy=false)() {
  76. static if(canCopy) {
  77. return cast(string)path[0..path_len].dup;
  78. } else {
  79. return cast(string)path[0..path_len];
  80. }
  81. }
  82. @property HttpMethod method() {
  83. string s = cast(string)_method[0..method_len];
  84. return to!HttpMethod(s);
  85. }
  86. HttpHeader[] headers(bool canCopy=false)() {
  87. HttpHeader[] hs = new HttpHeader[num_headers];
  88. for(int i; i<num_headers; i++) {
  89. phr_header* h = &_headers[i];
  90. static if(canCopy) {
  91. hs[i].name = cast(string)h.name[0..h.name_len].idup;
  92. hs[i].value = cast(string)h.value[0..h.value_len].idup;
  93. } else {
  94. hs[i].name = cast(string)h.name[0..h.name_len];
  95. hs[i].value = cast(string)h.value[0..h.value_len];
  96. }
  97. }
  98. return hs;
  99. }
  100. @property bool shouldKeepAlive() pure nothrow {
  101. return true;
  102. }
  103. @property ushort httpMajor() @safe pure nothrow {
  104. return 1;
  105. }
  106. @property ushort httpMinor() @safe pure nothrow {
  107. return cast(ushort)minor_version;
  108. }
  109. void execute(const(char)[] str) {
  110. execute(cast(const(ubyte)[]) str);
  111. }
  112. void execute(const(ubyte)[] chunk) {
  113. failure = null;
  114. num_headers = cast(int)_headers.length;
  115. int pret = phr_parse_request(cast(const char*)chunk.ptr, cast(int)chunk.length,
  116. &_method, &method_len,
  117. &path, &path_len,
  118. &minor_version,
  119. _headers.ptr, &num_headers,
  120. 0);
  121. debug {
  122. infof("buffer: %d bytes, request: %d bytes", chunk.length, pret);
  123. } else {
  124. if(pret < chunk.length)
  125. infof("buffer: %d bytes, request: %d bytes", chunk.length, pret);
  126. }
  127. if(pret > 0) {
  128. /* successfully parsed the request */
  129. onMessageComplete();
  130. } else if (pret == -1) {
  131. num_headers = 0;
  132. failure = new HttpException(HttpError.UNKNOWN);
  133. throw failure;
  134. }
  135. }
  136. void onMessageComplete() {
  137. // interceptor.onHeadersComplete();
  138. debug {
  139. tracef("method is %s", _method[0..method_len]);
  140. tracef("path is %s", path[0..path_len]);
  141. tracef("HTTP version is 1.%d", minor_version);
  142. foreach(ref phr_header h; _headers[0..num_headers]) {
  143. tracef("Header: %s = %s", h.name[0..h.name_len], h.value[0..h.value_len]);
  144. }
  145. }
  146. interceptor.onMessageComplete();
  147. }
  148. }
  149. auto httpParser(Interceptor)(Interceptor interceptor) {
  150. return HttpParser!Interceptor(interceptor);
  151. }