httpparser_ut.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #include "httpparser.h"
  2. #include <library/cpp/testing/unittest/registar.h>
  3. #define ENUM_OUT(arg) \
  4. case type ::arg: { \
  5. out << #arg; \
  6. return; \
  7. }
  8. template <>
  9. void Out<THttpParserBase::States>(IOutputStream& out, THttpParserBase::States st) {
  10. using type = THttpParserBase::States;
  11. switch (st) {
  12. ENUM_OUT(hp_error)
  13. ENUM_OUT(hp_eof)
  14. ENUM_OUT(hp_in_header)
  15. ENUM_OUT(hp_read_alive)
  16. ENUM_OUT(hp_read_closed)
  17. ENUM_OUT(hp_begin_chunk_header)
  18. ENUM_OUT(hp_chunk_header)
  19. ENUM_OUT(hp_read_chunk)
  20. }
  21. }
  22. namespace {
  23. class TSomethingLikeFakeCheck;
  24. using TTestHttpParser = THttpParser<TSomethingLikeFakeCheck>;
  25. class TSomethingLikeFakeCheck {
  26. TString Body_;
  27. public:
  28. const TString& Body() const {
  29. return Body_;
  30. }
  31. // other functions are not really called by THttpParser
  32. void CheckDocPart(const void* buf, size_t len, THttpHeader* /* header */) {
  33. TString s(static_cast<const char*>(buf), len);
  34. Cout << "State = " << static_cast<TTestHttpParser*>(this)->GetState() << ", CheckDocPart(" << s.Quote() << ")\n";
  35. Body_ += s;
  36. }
  37. };
  38. }
  39. Y_UNIT_TEST_SUITE(TestHttpParser) {
  40. Y_UNIT_TEST(TestTrivialRequest) {
  41. const TString blob{
  42. "GET /search?q=hi HTTP/1.1\r\n"
  43. "Host: www.google.ru:8080 \r\n"
  44. "\r\n"};
  45. THttpHeader hdr;
  46. THttpParser<> parser;
  47. parser.Init(&hdr);
  48. parser.Parse((void*)blob.data(), blob.size());
  49. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_error); // can't parse request as response
  50. }
  51. // XXX: `entity_size` is i32 and `content_length` is i64!
  52. Y_UNIT_TEST(TestTrivialResponse) {
  53. const TString blob{
  54. "HTTP/1.1 200 Ok\r\n"
  55. "Content-Length: 2\r\n"
  56. "\r\n"
  57. "OK"};
  58. THttpHeader hdr;
  59. TTestHttpParser parser;
  60. parser.Init(&hdr);
  61. parser.Parse((void*)blob.data(), blob.size());
  62. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof);
  63. UNIT_ASSERT_EQUAL(parser.Body(), "OK");
  64. UNIT_ASSERT_EQUAL(hdr.header_size, strlen(
  65. "HTTP/1.1 200 Ok\r\n"
  66. "Content-Length: 2\r\n"
  67. "\r\n"));
  68. UNIT_ASSERT_EQUAL(hdr.entity_size, strlen("OK"));
  69. }
  70. // XXX: `entity_size` is off by one in TE:chunked case.
  71. Y_UNIT_TEST(TestChunkedResponse) {
  72. const TString blob{
  73. "HTTP/1.1 200 OK\r\n"
  74. "Transfer-Encoding: chunked\r\n"
  75. "\r\n"
  76. "2\r\n"
  77. "Ok\r\n"
  78. "8\r\n"
  79. "AllRight\r\n"
  80. "0\r\n"
  81. "\r\n"};
  82. THttpHeader hdr;
  83. TTestHttpParser parser;
  84. parser.Init(&hdr);
  85. parser.Parse((void*)blob.data(), blob.size());
  86. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof);
  87. UNIT_ASSERT_EQUAL(parser.Body(), "OkAllRight");
  88. UNIT_ASSERT_EQUAL(hdr.header_size, strlen(
  89. "HTTP/1.1 200 OK\r\n"
  90. "Transfer-Encoding: chunked\r\n"
  91. "\r\n"));
  92. const int off_by_one_err = -1; // XXX: it really looks so
  93. UNIT_ASSERT_EQUAL(hdr.entity_size + off_by_one_err, strlen(
  94. "2\r\n"
  95. "Ok\r\n"
  96. "8\r\n"
  97. "AllRight\r\n"
  98. "0\r\n"
  99. "\r\n"));
  100. }
  101. static const TString PipelineClenBlob_{
  102. "HTTP/1.1 200 Ok\r\n"
  103. "Content-Length: 4\r\n"
  104. "\r\n"
  105. "OK\r\n"
  106. "HTTP/1.1 200 Zz\r\n"
  107. "Content-Length: 4\r\n"
  108. "\r\n"
  109. "ZZ\r\n"};
  110. void AssertPipelineClen(TTestHttpParser & parser, const THttpHeader& hdr) {
  111. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof);
  112. UNIT_ASSERT_EQUAL(4, hdr.content_length);
  113. UNIT_ASSERT_EQUAL(hdr.header_size, strlen(
  114. "HTTP/1.1 200 Ok\r\n"
  115. "Content-Length: 4\r\n"
  116. "\r\n"));
  117. }
  118. Y_UNIT_TEST(TestPipelineClenByteByByte) {
  119. const TString& blob = PipelineClenBlob_;
  120. THttpHeader hdr;
  121. TTestHttpParser parser;
  122. parser.Init(&hdr);
  123. for (size_t i = 0; i < blob.size(); ++i) {
  124. const TStringBuf d{blob, i, 1};
  125. parser.Parse((void*)d.data(), d.size());
  126. Cout << TString(d).Quote() << " -> " << parser.GetState() << Endl;
  127. }
  128. AssertPipelineClen(parser, hdr);
  129. UNIT_ASSERT_EQUAL(parser.Body(), "OK\r\n");
  130. UNIT_ASSERT_EQUAL(hdr.entity_size, hdr.content_length);
  131. }
  132. // XXX: Content-Length is ignored, Body() looks unexpected!
  133. Y_UNIT_TEST(TestPipelineClenOneChunk) {
  134. const TString& blob = PipelineClenBlob_;
  135. THttpHeader hdr;
  136. TTestHttpParser parser;
  137. parser.Init(&hdr);
  138. parser.Parse((void*)blob.data(), blob.size());
  139. AssertPipelineClen(parser, hdr);
  140. UNIT_ASSERT_EQUAL(parser.Body(),
  141. "OK\r\n"
  142. "HTTP/1.1 200 Zz\r\n"
  143. "Content-Length: 4\r\n"
  144. "\r\n"
  145. "ZZ\r\n");
  146. UNIT_ASSERT_EQUAL(hdr.entity_size, strlen(
  147. "OK\r\n"
  148. "HTTP/1.1 200 Zz\r\n"
  149. "Content-Length: 4\r\n"
  150. "\r\n"
  151. "ZZ\r\n"));
  152. }
  153. static const TString PipelineChunkedBlob_{
  154. "HTTP/1.1 200 OK\r\n"
  155. "Transfer-Encoding: chunked\r\n"
  156. "\r\n"
  157. "2\r\n"
  158. "Ok\r\n"
  159. "8\r\n"
  160. "AllRight\r\n"
  161. "0\r\n"
  162. "\r\n"
  163. "HTTP/1.1 200 OK\r\n"
  164. "Transfer-Encoding: chunked\r\n"
  165. "\r\n"
  166. "2\r\n"
  167. "Yo\r\n"
  168. "8\r\n"
  169. "uWin!Iam\r\n"
  170. "0\r\n"
  171. "\r\n"};
  172. void AssertPipelineChunked(TTestHttpParser & parser, const THttpHeader& hdr) {
  173. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof);
  174. UNIT_ASSERT_EQUAL(parser.Body(), "OkAllRight");
  175. UNIT_ASSERT_EQUAL(-1, hdr.content_length);
  176. UNIT_ASSERT_EQUAL(hdr.header_size, strlen(
  177. "HTTP/1.1 200 OK\r\n"
  178. "Transfer-Encoding: chunked\r\n"
  179. "\r\n"));
  180. const int off_by_one_err = -1;
  181. UNIT_ASSERT_EQUAL(hdr.entity_size + off_by_one_err, strlen(
  182. "2\r\n"
  183. "Ok\r\n"
  184. "8\r\n"
  185. "AllRight\r\n"
  186. "0\r\n"
  187. "\r\n"));
  188. }
  189. Y_UNIT_TEST(TestPipelineChunkedByteByByte) {
  190. const TString& blob = PipelineChunkedBlob_;
  191. THttpHeader hdr;
  192. TTestHttpParser parser;
  193. parser.Init(&hdr);
  194. for (size_t i = 0; i < blob.size(); ++i) {
  195. const TStringBuf d{blob, i, 1};
  196. parser.Parse((void*)d.data(), d.size());
  197. Cout << TString(d).Quote() << " -> " << parser.GetState() << Endl;
  198. if (blob.size() / 2 - 1 <= i) // last \n sets EOF
  199. UNIT_ASSERT_EQUAL(parser.GetState(), parser.hp_eof);
  200. }
  201. AssertPipelineChunked(parser, hdr);
  202. }
  203. Y_UNIT_TEST(TestPipelineChunkedOneChunk) {
  204. const TString& blob = PipelineChunkedBlob_;
  205. THttpHeader hdr;
  206. TTestHttpParser parser;
  207. parser.Init(&hdr);
  208. parser.Parse((void*)blob.data(), blob.size());
  209. AssertPipelineChunked(parser, hdr);
  210. }
  211. }