http_ut.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. #include "http.h"
  2. #include "http_ex.h"
  3. #include <library/cpp/testing/unittest/registar.h>
  4. #include <library/cpp/testing/unittest/tests_data.h>
  5. #include <util/generic/cast.h>
  6. #include <util/stream/output.h>
  7. #include <util/stream/zlib.h>
  8. #include <util/system/datetime.h>
  9. #include <util/system/mutex.h>
  10. #include <util/random/random.h>
  11. Y_UNIT_TEST_SUITE(THttpServerTest) {
  12. class TEchoServer: public THttpServer::ICallBack {
  13. class TRequest: public THttpClientRequestEx {
  14. public:
  15. inline TRequest(TEchoServer* parent)
  16. : Parent_(parent)
  17. {
  18. }
  19. bool Reply(void* /*tsr*/) override {
  20. if (!ProcessHeaders()) {
  21. return true;
  22. }
  23. Output() << "HTTP/1.1 200 Ok\r\n\r\n";
  24. if (Buf.Size()) {
  25. Output().Write(Buf.AsCharPtr(), Buf.Size());
  26. } else {
  27. Output() << Parent_->Res_;
  28. }
  29. Output().Finish();
  30. return true;
  31. }
  32. private:
  33. TEchoServer* Parent_ = nullptr;
  34. };
  35. public:
  36. inline TEchoServer(const TString& res)
  37. : Res_(res)
  38. {
  39. }
  40. TClientRequest* CreateClient() override {
  41. return new TRequest(this);
  42. }
  43. private:
  44. TString Res_;
  45. };
  46. class TSleepingServer: public THttpServer::ICallBack {
  47. class TReplier: public TRequestReplier {
  48. public:
  49. inline TReplier(TSleepingServer* server)
  50. : Server(server)
  51. {
  52. }
  53. bool BeforeParseRequestOk(void*) override {
  54. if (Server->Ttl && (TInstant::Now() - CreateTime > TDuration::MilliSeconds(Server->Ttl))) {
  55. Output().Write("HTTP/1.0 503 Created\nX-Server: sleeping server\n\nTTL Exceed");
  56. return false;
  57. } else {
  58. return true;
  59. }
  60. }
  61. bool DoReply(const TReplyParams& params) override {
  62. ++Server->Replies;
  63. with_lock (Server->Lock) {
  64. params.Output.Write("HTTP/1.0 201 Created\nX-Server: sleeping server\n\nZoooo");
  65. params.Output.Finish();
  66. }
  67. return true;
  68. }
  69. using TClientRequest::Output;
  70. private:
  71. TSleepingServer* Server = nullptr;
  72. TInstant CreateTime = TInstant::Now();
  73. };
  74. public:
  75. TSleepingServer(size_t ttl = 0)
  76. : Ttl(ttl) {}
  77. TClientRequest* CreateClient() override {
  78. return new TReplier(this);
  79. }
  80. void OnMaxConn() override {
  81. ++MaxConns;
  82. }
  83. public:
  84. TMutex Lock;
  85. std::atomic<size_t> Replies;
  86. std::atomic<size_t> MaxConns;
  87. size_t Ttl;
  88. };
  89. static const TString CrLf = "\r\n";
  90. struct TTestRequest {
  91. TTestRequest(ui16 port, TString content = TString())
  92. : Port(port)
  93. , Content(std::move(content))
  94. {
  95. }
  96. void CheckContinue(TSocketInput& si) {
  97. if (Expect100Continue) {
  98. TStringStream ss;
  99. TString firstLine;
  100. si.ReadLine(firstLine);
  101. for (;;) {
  102. TString buf;
  103. si.ReadLine(buf);
  104. if (buf.size() == 0) {
  105. break;
  106. }
  107. ss << buf << CrLf;
  108. }
  109. UNIT_ASSERT_EQUAL(firstLine, "HTTP/1.1 100 Continue");
  110. }
  111. }
  112. TString Execute() {
  113. TSocket* s = nullptr;
  114. THolder<TSocket> singleReqSocket;
  115. if (KeepAliveConnection) {
  116. if (!KeepAlivedSocket) {
  117. KeepAlivedSocket = MakeHolder<TSocket>(TNetworkAddress("localhost", Port), TDuration::Seconds(10));
  118. }
  119. s = KeepAlivedSocket.Get();
  120. } else {
  121. TNetworkAddress addr("localhost", Port);
  122. singleReqSocket.Reset(new TSocket(addr, TDuration::Seconds(10)));
  123. s = singleReqSocket.Get();
  124. }
  125. bool isPost = Type == "POST";
  126. TSocketInput si(*s);
  127. if (UseHttpOutput) {
  128. TSocketOutput so(*s);
  129. THttpOutput output(&so);
  130. output.EnableKeepAlive(KeepAliveConnection);
  131. output.EnableCompression(EnableResponseEncoding);
  132. TStringStream r;
  133. r << Type << " / HTTP/1.1" << CrLf;
  134. r << "Host: localhost:" + ToString(Port) << CrLf;
  135. if (isPost) {
  136. if (ContentEncoding.size()) {
  137. r << "Content-Encoding: " << ContentEncoding << CrLf;
  138. } else {
  139. r << "Transfer-Encoding: chunked" << CrLf;
  140. }
  141. if (Expect100Continue) {
  142. r << "Expect: 100-continue" << CrLf;
  143. }
  144. }
  145. r << CrLf;
  146. if (isPost) {
  147. output.Write(r.Str());
  148. output.Flush();
  149. CheckContinue(si);
  150. output.Write(Content);
  151. output.Finish();
  152. } else {
  153. output.Write(r.Str());
  154. output.Finish();
  155. }
  156. } else {
  157. TStringStream r;
  158. r << Type << " / HTTP/1.1" << CrLf;
  159. r << "Host: localhost:" + ToString(Port) << CrLf;
  160. if (KeepAliveConnection) {
  161. r << "Connection: Keep-Alive" << CrLf;
  162. } else {
  163. r << "Connection: Close" << CrLf;
  164. }
  165. if (EnableResponseEncoding) {
  166. r << "Accept-Encoding: gzip, deflate, x-gzip, x-deflate, y-lzo, y-lzf, y-lzq, y-bzip2, y-lzma" << CrLf;
  167. }
  168. if (isPost && Expect100Continue) {
  169. r << "Expect: 100-continue" << CrLf;
  170. }
  171. if (isPost && ContentEncoding.size() && Content.size()) {
  172. r << "Content-Encoding: " << ContentEncoding << CrLf;
  173. TStringStream compressedContent;
  174. {
  175. TZLibCompress zlib(&compressedContent);
  176. zlib.Write(Content.data(), Content.size());
  177. zlib.Flush();
  178. zlib.Finish();
  179. }
  180. r << "Content-Length: " << compressedContent.Size() << CrLf;
  181. r << CrLf;
  182. s->Send(r.Data(), r.Size());
  183. CheckContinue(si);
  184. Hdr = r.Str();
  185. TString tosend = compressedContent.Str();
  186. s->Send(tosend.data(), tosend.size());
  187. } else {
  188. if (isPost) {
  189. r << "Content-Length: " << Content.size() << CrLf;
  190. r << CrLf;
  191. s->Send(r.Data(), r.Size());
  192. CheckContinue(si);
  193. Hdr = r.Str();
  194. s->Send(Content.data(), Content.size());
  195. } else {
  196. r << CrLf;
  197. Hdr = r.Str();
  198. s->Send(r.Data(), r.Size());
  199. }
  200. }
  201. }
  202. THttpInput input(&si);
  203. TStringStream ss;
  204. TransferData(&input, &ss);
  205. return ss.Str();
  206. }
  207. TString GetDescription() const {
  208. if (UseHttpOutput) {
  209. TStringStream ss;
  210. ss << (KeepAliveConnection ? "keep-alive " : "") << Type;
  211. if (ContentEncoding.size()) {
  212. ss << " with encoding=" << ContentEncoding;
  213. }
  214. return ss.Str();
  215. } else {
  216. return Hdr;
  217. }
  218. }
  219. ui16 Port = 0;
  220. bool UseHttpOutput = true;
  221. TString Type = "GET";
  222. TString ContentEncoding;
  223. TString Content;
  224. bool KeepAliveConnection = false;
  225. THolder<TSocket> KeepAlivedSocket;
  226. bool EnableResponseEncoding = false;
  227. TString Hdr;
  228. bool Expect100Continue = false;
  229. };
  230. class TFailingMtpQueue: public TSimpleThreadPool {
  231. private:
  232. bool FailOnAdd_ = false;
  233. public:
  234. void SetFailOnAdd(bool fail = true) {
  235. FailOnAdd_ = fail;
  236. }
  237. [[nodiscard]] bool Add(IObjectInQueue* pObj) override {
  238. if (FailOnAdd_) {
  239. return false;
  240. }
  241. return TSimpleThreadPool::Add(pObj);
  242. }
  243. TFailingMtpQueue() = default;
  244. TFailingMtpQueue(IThreadFactory* pool)
  245. : TSimpleThreadPool(pool)
  246. {
  247. }
  248. };
  249. TString TestData(size_t size = 5 * 4096) {
  250. TString res;
  251. for (size_t i = 0; i < size; ++i) {
  252. res += (char)i;
  253. }
  254. return res;
  255. }
  256. Y_UNIT_TEST(TestEchoServer) {
  257. TString res = TestData();
  258. TPortManager pm;
  259. const ui16 port = pm.GetPort();
  260. const bool trueFalse[] = {true, false};
  261. TEchoServer serverImpl(res);
  262. THttpServer server(&serverImpl, THttpServer::TOptions(port).EnableKeepAlive(true).EnableCompression(true));
  263. for (int i = 0; i < 2; ++i) {
  264. UNIT_ASSERT(server.Start());
  265. TTestRequest r(port);
  266. r.Content = res;
  267. for (bool keepAlive : trueFalse) {
  268. r.KeepAliveConnection = keepAlive;
  269. // THttpOutput use chunked stream, else use Content-Length
  270. for (bool useHttpOutput : trueFalse) {
  271. r.UseHttpOutput = useHttpOutput;
  272. for (bool enableResponseEncoding : trueFalse) {
  273. r.EnableResponseEncoding = enableResponseEncoding;
  274. const TString reqTypes[] = {"GET", "POST"};
  275. for (const TString& reqType : reqTypes) {
  276. r.Type = reqType;
  277. const TString encoders[] = {"", "deflate"};
  278. for (const TString& encoder : encoders) {
  279. r.ContentEncoding = encoder;
  280. for (bool expect100Continue : trueFalse) {
  281. r.Expect100Continue = expect100Continue;
  282. TString resp = r.Execute();
  283. UNIT_ASSERT_C(resp == res, "diff echo response for request:\n" + r.GetDescription());
  284. }
  285. }
  286. }
  287. }
  288. }
  289. }
  290. server.Stop();
  291. }
  292. }
  293. Y_UNIT_TEST(TestReusePortEnabled) {
  294. TString res = TestData();
  295. TPortManager pm;
  296. const ui16 port = pm.GetPort();
  297. TEchoServer serverImpl(res);
  298. TVector<THolder<THttpServer>> servers;
  299. for (ui32 i = 0; i < 10; i++) {
  300. servers.push_back(MakeHolder<THttpServer>(&serverImpl, THttpServer::TOptions(port).EnableReusePort(true)));
  301. }
  302. for (ui32 testRun = 0; testRun < 3; testRun++) {
  303. for (auto& server : servers) {
  304. // start servers one at a time and check at least one of them is replying
  305. UNIT_ASSERT(server->Start());
  306. TTestRequest r(port, res);
  307. UNIT_ASSERT_C(r.Execute() == res, "diff echo response for request:\n" + r.GetDescription());
  308. }
  309. for (auto& server : servers) {
  310. // ping servers and stop them one at a time
  311. // at the last iteration only one server is still working and then gets stopped as well
  312. TTestRequest r(port, res);
  313. UNIT_ASSERT_C(r.Execute() == res, "diff echo response for request:\n" + r.GetDescription());
  314. server->Stop();
  315. }
  316. }
  317. }
  318. Y_UNIT_TEST(TestReusePortDisabled) {
  319. // check that with the ReusePort option disabled it's impossible to start two servers on the same port
  320. // check that ReusePort option is disabled by default (don't set it explicitly in the test)
  321. TPortManager pm;
  322. const ui16 port = pm.GetPort();
  323. TEchoServer serverImpl(TString{});
  324. THttpServer server1(&serverImpl, THttpServer::TOptions(port));
  325. THttpServer server2(&serverImpl, THttpServer::TOptions(port));
  326. UNIT_ASSERT(true == server1.Start());
  327. UNIT_ASSERT(false == server2.Start());
  328. server1.Stop();
  329. // Stop() is a sync call, port should be free by now
  330. UNIT_ASSERT(true == server2.Start());
  331. UNIT_ASSERT(false == server1.Start());
  332. }
  333. Y_UNIT_TEST(TestFailServer) {
  334. /**
  335. * Emulate request processing failures
  336. * Data should be large enough not to fit into socket buffer
  337. **/
  338. TString res = TestData(10 * 1024 * 1024);
  339. TPortManager portManager;
  340. const ui16 port = portManager.GetPort();
  341. TEchoServer serverImpl(res);
  342. THttpServer::TOptions options(port);
  343. options.EnableKeepAlive(true);
  344. options.EnableCompression(true);
  345. using TFailingServerMtpQueue = TThreadPoolBinder<TFailingMtpQueue, THttpServer::ICallBack>;
  346. THttpServer::TMtpQueueRef mainWorkers = new TFailingServerMtpQueue(&serverImpl, SystemThreadFactory());
  347. THttpServer::TMtpQueueRef failWorkers = new TThreadPool(SystemThreadFactory());
  348. THttpServer server(&serverImpl, mainWorkers, failWorkers, options);
  349. UNIT_ASSERT(server.Start());
  350. for (size_t i = 0; i < 3; ++i) {
  351. // should fail on 2nd request
  352. static_cast<TFailingMtpQueue*>(mainWorkers.Get())->SetFailOnAdd(i == 1);
  353. TTestRequest r(port);
  354. r.Content = res;
  355. r.Type = "POST";
  356. TString resp = r.Execute();
  357. if (i == 1) {
  358. UNIT_ASSERT(resp.Contains("Service Unavailable"));
  359. } else {
  360. UNIT_ASSERT_C(resp == res, "diff echo response for request:\n" + r.GetDescription());
  361. }
  362. }
  363. server.Stop();
  364. }
  365. class TReleaseConnectionServer: public THttpServer::ICallBack {
  366. class TRequest: public THttpClientRequestEx {
  367. public:
  368. bool Reply(void* /*tsr*/) override {
  369. Output() << "HTTP/1.1 200 Ok\r\n\r\n";
  370. Output() << "reply";
  371. Output().Finish();
  372. ReleaseConnection();
  373. throw yexception() << "some error";
  374. return true;
  375. }
  376. };
  377. public:
  378. TClientRequest* CreateClient() override {
  379. return new TRequest();
  380. }
  381. void OnException() override {
  382. ExceptionMessage = CurrentExceptionMessage();
  383. }
  384. TString ExceptionMessage;
  385. };
  386. class TResetConnectionServer: public THttpServer::ICallBack {
  387. class TRequest: public TClientRequest {
  388. public:
  389. bool Reply(void* /*tsr*/) override {
  390. Output() << "HTTP/1.1";
  391. ResetConnection();
  392. return true;
  393. }
  394. };
  395. public:
  396. TClientRequest* CreateClient() override {
  397. return new TRequest();
  398. }
  399. void OnException() override {
  400. ExceptionMessage = CurrentExceptionMessage();
  401. }
  402. TString ExceptionMessage;
  403. };
  404. class TListenerSockAddrReplyServer: public THttpServer::ICallBack {
  405. class TRequest: public TClientRequest {
  406. public:
  407. bool Reply(void* /*tsr*/) override {
  408. Output() << "HTTP/1.1 200 Ok\r\n\r\n";
  409. Output() << PrintHostAndPort(*GetListenerSockAddrRef());
  410. Output().Finish();
  411. return true;
  412. }
  413. };
  414. public:
  415. TClientRequest* CreateClient() override {
  416. return new TRequest();
  417. }
  418. };
  419. Y_UNIT_TEST(TTestResetConnection) {
  420. TPortManager pm;
  421. const ui16 port = pm.GetPort();
  422. TResetConnectionServer serverImpl;
  423. THttpServer server(&serverImpl, THttpServer::TOptions(port));
  424. UNIT_ASSERT(server.Start());
  425. TTestRequest r(port, "request");
  426. UNIT_ASSERT_EXCEPTION_CONTAINS(r.Execute(), TSystemError, "Connection reset by peer");
  427. server.Stop();
  428. }
  429. Y_UNIT_TEST(TTestReleaseConnection) {
  430. TPortManager pm;
  431. const ui16 port = pm.GetPort();
  432. TReleaseConnectionServer serverImpl;
  433. THttpServer server(&serverImpl, THttpServer::TOptions(port).EnableKeepAlive(true));
  434. UNIT_ASSERT(server.Start());
  435. TTestRequest r(port, "request");
  436. r.KeepAliveConnection = true;
  437. UNIT_ASSERT_C(r.Execute() == "reply", "diff echo response for request:\n" + r.GetDescription());
  438. server.Stop();
  439. UNIT_ASSERT_STRINGS_EQUAL(serverImpl.ExceptionMessage, "(yexception) some error");
  440. }
  441. THttpInput SendRequest(TSocket& socket, ui16 port) {
  442. TSocketInput si(socket);
  443. TSocketOutput so(socket);
  444. THttpOutput out(&so);
  445. out.EnableKeepAlive(true);
  446. out << "GET / HTTP/1.1" << CrLf;
  447. out << "Host: localhost:" + ToString(port) << CrLf;
  448. out << CrLf;
  449. out.Flush();
  450. THttpInput input(&si);
  451. input.ReadAll();
  452. return input;
  453. }
  454. THttpInput SendRequestWithBody(TSocket& socket, ui16 port, TString body) {
  455. TSocketInput si(socket);
  456. TSocketOutput so(socket);
  457. THttpOutput out(&so);
  458. out << "POST / HTTP/1.1" << CrLf;
  459. out << "Host: localhost:" + ToString(port) << CrLf;
  460. out << "Content-Length: " + ToString(body.size()) << CrLf;
  461. out << CrLf;
  462. out << body;
  463. out.Flush();
  464. THttpInput input(&si);
  465. input.ReadAll();
  466. return input;
  467. }
  468. Y_UNIT_TEST(TTestExpirationTimeout) {
  469. TPortManager pm;
  470. const ui16 port = pm.GetPort();
  471. TEchoServer serverImpl("test_data");
  472. THttpServer::TOptions options(port);
  473. options.nThreads = 1;
  474. options.MaxQueueSize = 0;
  475. options.MaxConnections = 0;
  476. options.KeepAliveEnabled = true;
  477. options.ExpirationTimeout = TDuration::Seconds(1);
  478. options.PollTimeout = TDuration::MilliSeconds(100);
  479. THttpServer server(&serverImpl, options);
  480. UNIT_ASSERT(server.Start());
  481. TSocket socket(TNetworkAddress("localhost", port), TDuration::Seconds(10));
  482. SendRequest(socket, port);
  483. SendRequest(socket, port);
  484. Sleep(TDuration::Seconds(5));
  485. UNIT_ASSERT_EXCEPTION(SendRequest(socket, port), THttpReadException);
  486. server.Stop();
  487. }
  488. Y_UNIT_TEST(TTestContentLengthTooLarge) {
  489. TPortManager pm;
  490. const ui16 port = pm.GetPort();
  491. TEchoServer serverImpl("test_data");
  492. THttpServer::TOptions options(port);
  493. options.nThreads = 1;
  494. options.MaxQueueSize = 0;
  495. options.MaxInputContentLength = 2_KB;
  496. options.MaxConnections = 0;
  497. options.KeepAliveEnabled = false;
  498. options.ExpirationTimeout = TDuration::Seconds(1);
  499. options.PollTimeout = TDuration::MilliSeconds(100);
  500. THttpServer server(&serverImpl, options);
  501. UNIT_ASSERT(server.Start());
  502. TSocket socket(TNetworkAddress("localhost", port), TDuration::Seconds(5));
  503. UNIT_ASSERT_STRING_CONTAINS(SendRequestWithBody(socket, port, TString(1_KB, 'a')).FirstLine(), "HTTP/1.1 200 Ok");
  504. TSocket socket2(TNetworkAddress("localhost", port), TDuration::Seconds(5));
  505. UNIT_ASSERT_STRING_CONTAINS(SendRequestWithBody(socket2, port, TString(10_KB, 'a')).FirstLine(), "HTTP/1.1 413 Payload Too Large");
  506. server.Stop();
  507. }
  508. Y_UNIT_TEST(TTestNullInRequest) {
  509. TPortManager pm;
  510. const ui16 port = pm.GetPort();
  511. TEchoServer serverImpl("test_data");
  512. THttpServer::TOptions options(port);
  513. options.nThreads = 1;
  514. options.MaxQueueSize = 0;
  515. options.MaxConnections = 0;
  516. options.KeepAliveEnabled = false;
  517. options.ExpirationTimeout = TDuration::Seconds(1);
  518. options.PollTimeout = TDuration::MilliSeconds(100);
  519. THttpServer server(&serverImpl, options);
  520. UNIT_ASSERT(server.Start());
  521. TSocket socket(TNetworkAddress("localhost", port), TDuration::Seconds(5));
  522. TSocketInput si(socket);
  523. TSocketOutput so(socket);
  524. THttpOutput out(&so);
  525. out << "GET \0/ggg HTTP/1.1" << CrLf;
  526. out << "Host: localhost:" + ToString(port) << CrLf;
  527. out << CrLf;
  528. out.Flush();
  529. THttpInput input(&si);
  530. input.ReadAll();
  531. UNIT_ASSERT_STRING_CONTAINS(input.FirstLine(), "HTTP/1.1 4");
  532. server.Stop();
  533. }
  534. Y_UNIT_TEST(TTestCloseConnectionOnRequestLimit) {
  535. TPortManager pm;
  536. const ui16 port = pm.GetPort();
  537. TEchoServer serverImpl("test_data");
  538. THttpServer server(&serverImpl, THttpServer::TOptions(port).EnableKeepAlive(true).SetMaxRequestsPerConnection(2));
  539. UNIT_ASSERT(server.Start());
  540. TSocket socket(TNetworkAddress("localhost", port), TDuration::Seconds(10));
  541. UNIT_ASSERT(SendRequest(socket, port).IsKeepAlive());
  542. UNIT_ASSERT(!SendRequest(socket, port).IsKeepAlive());
  543. UNIT_ASSERT_EXCEPTION(SendRequest(socket, port), THttpReadException);
  544. server.Stop();
  545. }
  546. Y_UNIT_TEST(TTestListenerSockAddrConnection) {
  547. TPortManager pm;
  548. const ui16 port1 = pm.GetPort();
  549. const ui16 port2 = pm.GetPort();
  550. TListenerSockAddrReplyServer serverImpl;
  551. THttpServer server(&serverImpl, THttpServer::TOptions().EnableKeepAlive(true).AddBindAddress("127.0.0.1", port1).AddBindAddress("127.0.0.1", port2));
  552. UNIT_ASSERT(server.Start());
  553. TTestRequest r1(port1);
  554. r1.KeepAliveConnection = true;
  555. TString resp = r1.Execute();
  556. UNIT_ASSERT(resp == TString::Join("127.0.0.1", ":", ToString(port1)));
  557. TTestRequest r2(port2);
  558. r2.KeepAliveConnection = true;
  559. resp = r2.Execute();
  560. UNIT_ASSERT(resp == TString::Join("127.0.0.1", ":", ToString(port2)));
  561. server.Stop();
  562. }
  563. Y_UNIT_TEST(TestSocketsLeak) {
  564. TPortManager portManager;
  565. TString res = TestData(25);
  566. const bool trueFalse[] = {true, false};
  567. for (bool rejectExcessConnections : trueFalse) {
  568. for (bool keepAlive : trueFalse) {
  569. const ui16 port = portManager.GetPort();
  570. TSleepingServer server;
  571. THttpServer::TOptions options(port);
  572. options.nThreads = 1;
  573. options.MaxConnections = 1;
  574. options.MaxQueueSize = 10;
  575. options.MaxFQueueSize = 2;
  576. options.nFThreads = 2;
  577. options.KeepAliveEnabled = true;
  578. options.RejectExcessConnections = rejectExcessConnections;
  579. THttpServer srv(&server, options);
  580. UNIT_ASSERT(srv.Start());
  581. UNIT_ASSERT(server.Lock.TryAcquire());
  582. std::atomic<size_t> threadsFinished = 0;
  583. TVector<THolder<IThreadFactory::IThread>> threads;
  584. auto func = [port, keepAlive, &threadsFinished]() {
  585. try {
  586. TTestRequest r(port);
  587. r.KeepAliveConnection = keepAlive;
  588. r.Execute();
  589. } catch (...) {
  590. }
  591. ++threadsFinished;
  592. };
  593. threads.push_back(SystemThreadFactory()->Run(func));
  594. while (server.Replies.load() != 1) { //wait while we have one connection inside server
  595. Sleep(TDuration::MilliSeconds(1));
  596. }
  597. for (size_t i = 1; i < 3; ++i) {
  598. threads.push_back(SystemThreadFactory()->Run(func));
  599. //in case of rejectExcessConnections next requests will fail, otherwise will stuck inside server queue
  600. while ((rejectExcessConnections ? threadsFinished.load() : srv.GetRequestQueueSize()) != i) {
  601. Sleep(TDuration::MilliSeconds(1));
  602. }
  603. }
  604. server.Lock.Release();
  605. for (auto&& thread : threads) {
  606. thread->Join();
  607. }
  608. TStringStream opts;
  609. opts << " [" << rejectExcessConnections << ", " << keepAlive << "] ";
  610. UNIT_ASSERT_EQUAL_C(server.MaxConns, 2, opts.Str() + "we should get MaxConn notification 2 times, got " + ToString(server.MaxConns.load()));
  611. if (rejectExcessConnections) {
  612. UNIT_ASSERT_EQUAL_C(server.Replies, 1, opts.Str() + "only one request should have been processed, got " + ToString(server.Replies.load()));
  613. } else {
  614. UNIT_ASSERT_VALUES_EQUAL(server.Replies.load(), 3);
  615. }
  616. }
  617. }
  618. }
  619. class TShooter {
  620. public:
  621. struct TCounters {
  622. public:
  623. TCounters() = default;
  624. TCounters(const TCounters& other)
  625. : Fail(other.Fail.load())
  626. , Success(other.Success.load())
  627. {
  628. }
  629. public:
  630. std::atomic<size_t> Fail = 0;
  631. std::atomic<size_t> Success = 0;
  632. };
  633. public:
  634. TShooter(size_t threadCount, ui16 port)
  635. : Counters_(threadCount)
  636. {
  637. for (size_t i = 0; i < threadCount; ++i) {
  638. auto func = [i, port, this] () {
  639. for (;;) {
  640. try {
  641. TTestRequest r(port);
  642. r.KeepAliveConnection = true;
  643. for (size_t j = 0; j < 100; ++j) {
  644. if (Stopped_.load()) {
  645. return;
  646. }
  647. r.Execute();
  648. Sleep(TDuration::MilliSeconds(1) * RandomNumber<float>());
  649. Counters_[i].Success++;
  650. }
  651. } catch (TSystemError& e) {
  652. UNIT_ASSERT_C(e.Status() == ECONNRESET || e.Status() == ECONNREFUSED, CurrentExceptionMessage());
  653. Counters_[i].Fail++;
  654. } catch (THttpReadException&) {
  655. Counters_[i].Fail++;
  656. } catch (...) {
  657. UNIT_ASSERT_C(false, CurrentExceptionMessage());
  658. }
  659. }
  660. };
  661. Threads_.push_back(SystemThreadFactory()->Run(func));
  662. }
  663. }
  664. void Stop() {
  665. Stopped_.store(true);
  666. for (auto& thread : Threads_) {
  667. thread->Join();
  668. }
  669. }
  670. void WaitProgress() const {
  671. auto snapshot = Counters_;
  672. for (;;) {
  673. size_t haveProgress = 0;
  674. for (size_t i = 0; i < Counters_.size(); ++i) {
  675. haveProgress += (Counters_[i].Fail.load() + Counters_[i].Success.load()) > (snapshot[i].Fail + snapshot[i].Success);
  676. }
  677. if (haveProgress == Counters_.size()) {
  678. return;
  679. }
  680. Sleep(TDuration::MilliSeconds(1));
  681. }
  682. }
  683. const auto& GetCounters() const {
  684. return Counters_;
  685. }
  686. ~TShooter() {
  687. Stop();
  688. }
  689. private:
  690. TVector<THolder<IThreadFactory::IThread>> Threads_;
  691. std::atomic<bool> Stopped_ = false;
  692. TVector<TCounters> Counters_;
  693. };
  694. struct TTestConfig {
  695. bool OneShot = false;
  696. ui32 ListenerThreads = 1;
  697. };
  698. TVector<TTestConfig> testConfigs = {
  699. {.OneShot = false, .ListenerThreads = 1},
  700. {.OneShot = true, .ListenerThreads = 1},
  701. {.OneShot = true, .ListenerThreads = 4},
  702. {.OneShot = true, .ListenerThreads = 63},
  703. };
  704. THttpServer::TOptions ApplyConfig(const THttpServer::TOptions& opts, const TTestConfig& cfg) {
  705. THttpServer::TOptions res = opts;
  706. res.OneShotPoll = cfg.OneShot;
  707. res.nListenerThreads = cfg.ListenerThreads;
  708. return res;
  709. }
  710. Y_UNIT_TEST(TestStartStop) {
  711. TPortManager pm;
  712. const ui16 port = pm.GetPort();
  713. const size_t threadCount = 5;
  714. TShooter shooter(threadCount, port);
  715. TString res = TestData();
  716. for (const auto& cfg : testConfigs) {
  717. TEchoServer serverImpl(res);
  718. THttpServer server(&serverImpl, ApplyConfig(THttpServer::TOptions(port).EnableKeepAlive(true), cfg));
  719. for (size_t i = 0; i < 100; ++i) {
  720. UNIT_ASSERT(server.Start());
  721. shooter.WaitProgress();
  722. {
  723. auto before = shooter.GetCounters();
  724. shooter.WaitProgress();
  725. auto after = shooter.GetCounters();
  726. for (size_t i = 0; i < before.size(); ++i) {
  727. UNIT_ASSERT(before[i].Success < after[i].Success);
  728. UNIT_ASSERT(before[i].Fail == after[i].Fail);
  729. }
  730. }
  731. server.Stop();
  732. shooter.WaitProgress();
  733. {
  734. auto before = shooter.GetCounters();
  735. shooter.WaitProgress();
  736. auto after = shooter.GetCounters();
  737. for (size_t i = 0; i < before.size(); ++i) {
  738. UNIT_ASSERT(before[i].Success == after[i].Success);
  739. UNIT_ASSERT(before[i].Fail < after[i].Fail);
  740. }
  741. }
  742. }
  743. }
  744. }
  745. Y_UNIT_TEST(TestMaxConnections) {
  746. class TMaxConnServer
  747. : public TEchoServer
  748. {
  749. public:
  750. using TEchoServer::TEchoServer;
  751. void OnMaxConn() override {
  752. ++MaxConns;
  753. }
  754. public:
  755. std::atomic<size_t> MaxConns = 0;
  756. };
  757. TPortManager pm;
  758. const ui16 port = pm.GetPort();
  759. const size_t maxConnections = 5;
  760. TString res = TestData();
  761. for (const auto& cfg : testConfigs) {
  762. TMaxConnServer serverImpl(res);
  763. THttpServer server(&serverImpl, ApplyConfig(THttpServer::TOptions(port).EnableKeepAlive(true).SetMaxConnections(maxConnections), cfg));
  764. UNIT_ASSERT(server.Start());
  765. TShooter shooter(maxConnections + 1, port);
  766. for (size_t i = 0; i < 100; ++i) {
  767. const size_t prev = serverImpl.MaxConns.load();
  768. while (serverImpl.MaxConns.load() < prev + 100) {
  769. Sleep(TDuration::MilliSeconds(1));
  770. }
  771. }
  772. shooter.Stop();
  773. server.Stop();
  774. for (const auto& c : shooter.GetCounters()) {
  775. UNIT_ASSERT(c.Success > 0);
  776. UNIT_ASSERT(c.Fail > 0);
  777. UNIT_ASSERT(c.Success > c.Fail);
  778. }
  779. }
  780. }
  781. Y_UNIT_TEST(StartFail) {
  782. TString res = TestData();
  783. TEchoServer serverImpl(res);
  784. {
  785. THttpServer server(&serverImpl, THttpServer::TOptions(1));
  786. UNIT_ASSERT(!server.GetErrorCode());
  787. UNIT_ASSERT(!server.Start());
  788. UNIT_ASSERT(server.GetErrorCode());
  789. }
  790. {
  791. TPortManager pm;
  792. const ui16 port = pm.GetPort();
  793. THttpServer server1(&serverImpl, THttpServer::TOptions(port));
  794. UNIT_ASSERT(server1.Start());
  795. UNIT_ASSERT(!server1.GetErrorCode());
  796. THttpServer server2(&serverImpl, THttpServer::TOptions(port));
  797. UNIT_ASSERT(!server2.Start());
  798. UNIT_ASSERT(server2.GetErrorCode());
  799. }
  800. }
  801. inline TString ToString(const THashSet<TString>& hs) {
  802. TString res = "";
  803. for (auto s : hs) {
  804. if (res) {
  805. res.append(",");
  806. }
  807. res.append("\"").append(s).append("\"");
  808. }
  809. return res;
  810. }
  811. Y_UNIT_TEST(TestTTLExceed) {
  812. // Checks that one of request returns "TTL Exceed"
  813. // First request waits for server.Lock.Release() for one threaded TSleepingServer
  814. // So second request in queue should fail with TTL Exceed, because fist one lock thread pool for (ttl + 1) ms
  815. TPortManager portManager;
  816. const ui16 port = portManager.GetPort();
  817. TString res = TestData(25);
  818. const size_t ttl = 10;
  819. TSleepingServer server{ttl};
  820. THttpServer::TOptions options(port);
  821. options.nThreads = 1;
  822. options.MaxConnections = 2;
  823. THttpServer srv(&server, options);
  824. UNIT_ASSERT(srv.Start());
  825. UNIT_ASSERT(server.Lock.TryAcquire());
  826. THashSet<TString> results;
  827. TMutex resultLock;
  828. auto func = [port, &resultLock, &results]() {
  829. try {
  830. TTestRequest r(port);
  831. TString result = r.Execute();
  832. with_lock(resultLock) {
  833. results.insert(result);
  834. }
  835. } catch (...) {
  836. }
  837. };
  838. auto t1 = SystemThreadFactory()->Run(func);
  839. auto t2 = SystemThreadFactory()->Run(func);
  840. Sleep(TDuration::MilliSeconds(ttl + 1));
  841. server.Lock.Release();
  842. t1->Join();
  843. t2->Join();
  844. UNIT_ASSERT_EQUAL_C(results, (THashSet<TString>({"Zoooo", "TTL Exceed"})), "Results is {" + ToString(results) + "}");
  845. }
  846. }