test.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include <gtest/gtest.h>
  2. #include <httplib.h>
  3. #include <future>
  4. #include <iostream>
  5. #define SERVER_CERT_FILE "./cert.pem"
  6. #define SERVER_PRIVATE_KEY_FILE "./key.pem"
  7. #ifdef _WIN32
  8. #include <process.h>
  9. #define msleep(n) ::Sleep(n)
  10. #else
  11. #define msleep(n) ::usleep(n * 1000)
  12. #endif
  13. using namespace std;
  14. using namespace httplib;
  15. const char* HOST = "localhost";
  16. const int PORT = 1234;
  17. #ifdef _WIN32
  18. TEST(StartupTest, WSAStartup)
  19. {
  20. WSADATA wsaData;
  21. int ret = WSAStartup(0x0002, &wsaData);
  22. ASSERT_EQ(0, ret);
  23. }
  24. #endif
  25. TEST(SplitTest, ParseQueryString)
  26. {
  27. string s = "key1=val1&key2=val2&key3=val3";
  28. map<string, string> dic;
  29. detail::split(s.c_str(), s.c_str() + s.size(), '&', [&](const char* b, const char* e) {
  30. string key, val;
  31. detail::split(b, e, '=', [&](const char* b, const char* e) {
  32. if (key.empty()) {
  33. key.assign(b, e);
  34. } else {
  35. val.assign(b, e);
  36. }
  37. });
  38. dic[key] = val;
  39. });
  40. EXPECT_EQ("val1", dic["key1"]);
  41. EXPECT_EQ("val2", dic["key2"]);
  42. EXPECT_EQ("val3", dic["key3"]);
  43. }
  44. TEST(ParseQueryTest, ParseQueryString)
  45. {
  46. string s = "key1=val1&key2=val2&key3=val3";
  47. map<string, string> dic;
  48. detail::parse_query_text(s, dic);
  49. EXPECT_EQ("val1", dic["key1"]);
  50. EXPECT_EQ("val2", dic["key2"]);
  51. EXPECT_EQ("val3", dic["key3"]);
  52. }
  53. TEST(SocketTest, OpenClose)
  54. {
  55. socket_t sock = detail::create_server_socket(HOST, PORT, 0);
  56. ASSERT_NE(-1, sock);
  57. auto ret = detail::close_socket(sock);
  58. EXPECT_EQ(0, ret);
  59. }
  60. TEST(SocketTest, OpenCloseWithAI_PASSIVE)
  61. {
  62. socket_t sock = detail::create_server_socket(nullptr, PORT, AI_PASSIVE);
  63. ASSERT_NE(-1, sock);
  64. auto ret = detail::close_socket(sock);
  65. EXPECT_EQ(0, ret);
  66. }
  67. TEST(GetHeaderValueTest, DefaultValue)
  68. {
  69. MultiMap map = {{"Dummy","Dummy"}};
  70. auto val = detail::get_header_value(map, "Content-Type", "text/plain");
  71. ASSERT_STREQ("text/plain", val);
  72. }
  73. TEST(GetHeaderValueTest, DefaultValueInt)
  74. {
  75. MultiMap map = {{"Dummy","Dummy"}};
  76. auto val = detail::get_header_value_int(map, "Content-Length", 100);
  77. EXPECT_EQ(100, val);
  78. }
  79. TEST(GetHeaderValueTest, RegularValue)
  80. {
  81. MultiMap map = {{"Content-Type", "text/html"}, {"Dummy", "Dummy"}};
  82. auto val = detail::get_header_value(map, "Content-Type", "text/plain");
  83. ASSERT_STREQ("text/html", val);
  84. }
  85. TEST(GetHeaderValueTest, RegularValueInt)
  86. {
  87. MultiMap map = {{"Content-Length", "100"}, {"Dummy", "Dummy"}};
  88. auto val = detail::get_header_value_int(map, "Content-Length", 0);
  89. EXPECT_EQ(100, val);
  90. }
  91. class ServerTest : public ::testing::Test {
  92. protected:
  93. ServerTest()
  94. : cli_(HOST, PORT)
  95. #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  96. , svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
  97. #endif
  98. , up_(false) {}
  99. virtual void SetUp() {
  100. svr_.set_base_dir("./www");
  101. svr_.get("/hi", [&](const Request& req, Response& res) {
  102. res.set_content("Hello World!", "text/plain");
  103. })
  104. .get("/", [&](const Request& req, Response& res) {
  105. res.set_redirect("/hi");
  106. })
  107. .post("/person", [&](const Request& req, Response& res) {
  108. if (req.has_param("name") && req.has_param("note")) {
  109. persons_[req.params.at("name")] = req.params.at("note");
  110. } else {
  111. res.status = 400;
  112. }
  113. })
  114. .get("/person/(.*)", [&](const Request& req, Response& res) {
  115. string name = req.matches[1];
  116. if (persons_.find(name) != persons_.end()) {
  117. auto note = persons_[name];
  118. res.set_content(note, "text/plain");
  119. } else {
  120. res.status = 404;
  121. }
  122. })
  123. .get("/stop", [&](const Request& req, Response& res) {
  124. svr_.stop();
  125. });
  126. persons_["john"] = "programmer";
  127. f_ = async([&](){
  128. up_ = true;
  129. svr_.listen(HOST, PORT);
  130. });
  131. while (!up_) {
  132. msleep(1);
  133. }
  134. }
  135. virtual void TearDown() {
  136. //svr_.stop(); // NOTE: This causes dead lock on Windows.
  137. cli_.get("/stop");
  138. f_.get();
  139. }
  140. map<string, string> persons_;
  141. #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  142. SSLClient cli_;
  143. SSLServer svr_;
  144. #else
  145. Client cli_;
  146. Server svr_;
  147. #endif
  148. future<void> f_;
  149. bool up_;
  150. };
  151. TEST_F(ServerTest, GetMethod200)
  152. {
  153. auto res = cli_.get("/hi");
  154. ASSERT_TRUE(res != nullptr);
  155. EXPECT_EQ(200, res->status);
  156. EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
  157. EXPECT_EQ("Hello World!", res->body);
  158. }
  159. TEST_F(ServerTest, GetMethod302)
  160. {
  161. auto res = cli_.get("/");
  162. ASSERT_TRUE(res != nullptr);
  163. EXPECT_EQ(302, res->status);
  164. EXPECT_EQ("/hi", res->get_header_value("Location"));
  165. }
  166. TEST_F(ServerTest, GetMethod404)
  167. {
  168. auto res = cli_.get("/invalid");
  169. ASSERT_TRUE(res != nullptr);
  170. EXPECT_EQ(404, res->status);
  171. }
  172. TEST_F(ServerTest, HeadMethod200)
  173. {
  174. auto res = cli_.head("/hi");
  175. ASSERT_TRUE(res != nullptr);
  176. EXPECT_EQ(200, res->status);
  177. EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
  178. EXPECT_EQ("", res->body);
  179. }
  180. TEST_F(ServerTest, HeadMethod404)
  181. {
  182. auto res = cli_.head("/invalid");
  183. ASSERT_TRUE(res != nullptr);
  184. EXPECT_EQ(404, res->status);
  185. EXPECT_EQ("", res->body);
  186. }
  187. TEST_F(ServerTest, GetMethodPersonJohn)
  188. {
  189. auto res = cli_.get("/person/john");
  190. ASSERT_TRUE(res != nullptr);
  191. EXPECT_EQ(200, res->status);
  192. EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
  193. EXPECT_EQ("programmer", res->body);
  194. }
  195. TEST_F(ServerTest, PostMethod1)
  196. {
  197. auto res = cli_.get("/person/john1");
  198. ASSERT_TRUE(res != nullptr);
  199. ASSERT_EQ(404, res->status);
  200. res = cli_.post("/person", "name=john1&note=coder", "application/x-www-form-urlencoded");
  201. ASSERT_TRUE(res != nullptr);
  202. ASSERT_EQ(200, res->status);
  203. res = cli_.get("/person/john1");
  204. ASSERT_TRUE(res != nullptr);
  205. ASSERT_EQ(200, res->status);
  206. ASSERT_EQ("text/plain", res->get_header_value("Content-Type"));
  207. ASSERT_EQ("coder", res->body);
  208. }
  209. TEST_F(ServerTest, PostMethod2)
  210. {
  211. auto res = cli_.get("/person/john2");
  212. ASSERT_TRUE(res != nullptr);
  213. ASSERT_EQ(404, res->status);
  214. Map params;
  215. params["name"] = "john2";
  216. params["note"] = "coder";
  217. res = cli_.post("/person", params);
  218. ASSERT_TRUE(res != nullptr);
  219. ASSERT_EQ(200, res->status);
  220. res = cli_.get("/person/john2");
  221. ASSERT_TRUE(res != nullptr);
  222. ASSERT_EQ(200, res->status);
  223. ASSERT_EQ("text/plain", res->get_header_value("Content-Type"));
  224. ASSERT_EQ("coder", res->body);
  225. }
  226. TEST_F(ServerTest, GetMethodDir)
  227. {
  228. auto res = cli_.get("/dir/");
  229. ASSERT_TRUE(res != nullptr);
  230. EXPECT_EQ(200, res->status);
  231. EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
  232. auto body = R"(<html>
  233. <head>
  234. </head>
  235. <body>
  236. <a href="/dir/test.html">Test</a>
  237. <a href="/hi">hi</a>
  238. </body>
  239. </html>
  240. )";
  241. EXPECT_EQ(body, res->body);
  242. }
  243. TEST_F(ServerTest, GetMethodDirTest)
  244. {
  245. auto res = cli_.get("/dir/test.html");
  246. ASSERT_TRUE(res != nullptr);
  247. EXPECT_EQ(200, res->status);
  248. EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
  249. EXPECT_EQ("test.html", res->body);
  250. }
  251. TEST_F(ServerTest, GetMethodDirTestWithDoubleDots)
  252. {
  253. auto res = cli_.get("/dir/../dir/test.html");
  254. ASSERT_TRUE(res != nullptr);
  255. EXPECT_EQ(200, res->status);
  256. EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
  257. EXPECT_EQ("test.html", res->body);
  258. }
  259. TEST_F(ServerTest, GetMethodInvalidPath)
  260. {
  261. auto res = cli_.get("/dir/../test.html");
  262. ASSERT_TRUE(res != nullptr);
  263. EXPECT_EQ(404, res->status);
  264. }
  265. TEST_F(ServerTest, GetMethodOutOfBaseDir)
  266. {
  267. auto res = cli_.get("/../www/dir/test.html");
  268. ASSERT_TRUE(res != nullptr);
  269. EXPECT_EQ(404, res->status);
  270. }
  271. TEST_F(ServerTest, GetMethodOutOfBaseDir2)
  272. {
  273. auto res = cli_.get("/dir/../../www/dir/test.html");
  274. ASSERT_TRUE(res != nullptr);
  275. EXPECT_EQ(404, res->status);
  276. }
  277. TEST_F(ServerTest, InvalidBaseDir)
  278. {
  279. EXPECT_EQ(false, svr_.set_base_dir("invalid_dir"));
  280. EXPECT_EQ(true, svr_.set_base_dir("."));
  281. }
  282. class ServerTestWithAI_PASSIVE : public ::testing::Test {
  283. protected:
  284. ServerTestWithAI_PASSIVE()
  285. : cli_(HOST, PORT)
  286. #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  287. , svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
  288. #endif
  289. , up_(false) {}
  290. virtual void SetUp() {
  291. svr_.get("/hi", [&](const Request& req, Response& res) {
  292. res.set_content("Hello World!", "text/plain");
  293. });
  294. svr_.get("/stop", [&](const Request& req, Response& res) {
  295. svr_.stop();
  296. });
  297. f_ = async([&](){
  298. up_ = true;
  299. svr_.listen(nullptr, PORT, AI_PASSIVE);
  300. });
  301. while (!up_) {
  302. msleep(1);
  303. }
  304. }
  305. virtual void TearDown() {
  306. //svr_.stop(); // NOTE: This causes dead lock on Windows.
  307. cli_.get("/stop");
  308. f_.get();
  309. }
  310. #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  311. SSLClient cli_;
  312. SSLServer svr_;
  313. #else
  314. Client cli_;
  315. Server svr_;
  316. #endif
  317. future<void> f_;
  318. bool up_;
  319. };
  320. TEST_F(ServerTestWithAI_PASSIVE, GetMethod200)
  321. {
  322. auto res = cli_.get("/hi");
  323. ASSERT_TRUE(res != nullptr);
  324. EXPECT_EQ(200, res->status);
  325. EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
  326. EXPECT_EQ("Hello World!", res->body);
  327. }
  328. #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
  329. TEST(SSLClientTest, ServerNameIndication)
  330. {
  331. SSLClient cli("httpbin.org", 443);
  332. auto res = cli.get("/get");
  333. ASSERT_TRUE(res != nullptr);
  334. ASSERT_EQ(200, res->status);
  335. }
  336. #endif
  337. #ifdef _WIN32
  338. TEST(CleanupTest, WSACleanup)
  339. {
  340. int ret = WSACleanup();
  341. ASSERT_EQ(0, ret);
  342. }
  343. #endif
  344. // vim: et ts=4 sw=4 cin cino={1s ff=unix