test_http.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #include <atomic>
  2. #include <nlohmann_json/json.hpp>
  3. #include "kbf.h"
  4. #include "kbf/wifi.h"
  5. #include "kbf/http/server.h"
  6. #include "kbf/http/client.h"
  7. #include <unity.h>
  8. using namespace kbf;
  9. using namespace std;
  10. using nlohmann::json;
  11. std::atomic<bool> asyncFinished = {false};
  12. std::atomic<int> state;
  13. TEST_CASE("HTTP GET, POST, 404, 405", "[kbf_http]") {
  14. wifi::start();
  15. auto server = http::Server();
  16. TEST_ASSERT_FALSE(server.isRunning())
  17. http::Response (*handleGet)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  18. TEST_ASSERT_EQUAL(http::GET, request.method);
  19. return http::Response("OK");
  20. }};
  21. server.route({http::GET, "/get-only", handleGet, nullptr});
  22. http::Response (*handlePost)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  23. TEST_ASSERT_EQUAL(http::POST, request.method);
  24. return http::Response("OK");
  25. }};
  26. server.route({http::POST, "/post-only", handlePost, nullptr});
  27. http::Response (*handleGetAndPost)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  28. if (request.method == http::GET) {
  29. return http::Response("GET");
  30. } else if (request.method == http::POST) {
  31. TEST_ASSERT_EQUAL_STRING("application/json", request.headers.at("Content-Type").c_str());
  32. auto requestJson = json::parse(request.body);
  33. string response = requestJson["foo"];
  34. return http::Response(response);
  35. } else {
  36. TEST_FAIL();
  37. return http::Response("fail"); // unreachable but the compiler moans otherwise
  38. }
  39. }};
  40. server.route({http::GET, "/get-and-post", handleGetAndPost, nullptr});
  41. server.route({http::POST, "/get-and-post", handleGetAndPost, nullptr});
  42. server.start();
  43. TEST_ASSERT_TRUE(server.isRunning())
  44. auto client = http::Client();
  45. auto response = client.get("http://localhost/get-only");
  46. TEST_ASSERT_EQUAL(200, response->status);
  47. TEST_ASSERT_EQUAL_STRING("OK", response->body.c_str());
  48. response = client.get("http://localhost/non-existent");
  49. TEST_ASSERT_EQUAL(404, response->status);
  50. TEST_ASSERT_EQUAL_STRING("This URI does not exist", response->body.c_str());
  51. response = client.get("http://localhost/post-only");
  52. TEST_ASSERT_EQUAL(405, response->status);
  53. TEST_ASSERT_EQUAL_STRING("Request method for this URI is not handled by server", response->body.c_str());
  54. response = client.get("http://localhost/get-and-post");
  55. TEST_ASSERT_EQUAL(200, response->status);
  56. TEST_ASSERT_EQUAL_STRING("GET", response->body.c_str());
  57. json data = {{"foo", "bar"}};
  58. response = client.post("http://localhost/get-and-post", data);
  59. TEST_ASSERT_EQUAL_STRING("bar", response->body.c_str());
  60. server.stop();
  61. wifi::stop();
  62. }
  63. TEST_CASE("HTTP custom headers", "[kbf_http]") {
  64. wifi::start();
  65. auto server = http::Server();
  66. http::Response (*handleContentTypeTest)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  67. auto response = http::Response("OK");
  68. if (!request.query.empty() && request.query.at("type") == "txt") {
  69. response.contentType = "text/plain";
  70. }
  71. return response;
  72. }};
  73. server.route({http::GET, "/content-type-test", handleContentTypeTest, nullptr});
  74. http::Response (*handleHeaderTest)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  75. auto response = http::Response("OK");
  76. response.headers["X-Secret"] = request.query.at("secret");
  77. return response;
  78. }};
  79. server.route({http::GET, "/custom-header-test", handleHeaderTest, nullptr});
  80. server.start();
  81. auto client = http::Client();
  82. auto response = client.get("http://localhost/content-type-test");
  83. TEST_ASSERT_EQUAL_STRING("text/html", response->headers.at("Content-Type").c_str());
  84. TEST_ASSERT_EQUAL_STRING("OK", response->body.data());
  85. response = client.get("http://localhost/content-type-test?type=txt");
  86. TEST_ASSERT_EQUAL_STRING("text/plain", response->headers.at("Content-Type").c_str());
  87. TEST_ASSERT_EQUAL_STRING("OK", response->body.data());
  88. response = client.get("http://localhost/custom-header-test?secret=THISis1337");
  89. TEST_ASSERT_EQUAL_STRING("THISis1337", response->headers.at("X-Secret").c_str());
  90. TEST_ASSERT_EQUAL_STRING("OK", response->body.data());
  91. server.stop();
  92. wifi::stop();
  93. }
  94. TEST_CASE("HTTP JSON request / response", "[kbf_http]") {
  95. wifi::start();
  96. static const string testKey = "key";
  97. static const string testValue = "value";
  98. auto server = http::Server();
  99. http::Response (*handleJson)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  100. TEST_ASSERT_EQUAL(http::POST, request.method);
  101. TEST_ASSERT_EQUAL_STRING("application/json", request.headers.at("Content-Type").c_str());
  102. auto requestJson = request.json();
  103. TEST_ASSERT_NOT_NULL(requestJson)
  104. auto responseJson = json({{testKey, requestJson.find(testKey)->get<string>()}});
  105. return http::Response(responseJson);
  106. }};
  107. http::Response (*handleNotJson)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  108. auto requestJson = request.json();
  109. TEST_ASSERT_NULL(requestJson)
  110. return http::Response("OK");
  111. }};
  112. server.route({http::POST, "/json", handleJson, nullptr});
  113. server.route({http::POST, "/not-json", handleNotJson, nullptr});
  114. server.start();
  115. auto client = http::Client();
  116. auto response = client.post("http://localhost/json", {{testKey, testValue}});
  117. TEST_ASSERT_EQUAL_STRING("application/json", response->headers.at("Content-Type").c_str());
  118. auto responseJson = response->json();
  119. TEST_ASSERT_NOT_NULL(responseJson)
  120. TEST_ASSERT_EQUAL_STRING(testValue.c_str(), responseJson.find(testKey)->get<string>().c_str());
  121. // TODO enable after implementing support for posting data types other than JSON
  122. // response = client.post("http://localhost/not-json", "");
  123. // TEST_ASSERT_NULL(response->json());
  124. server.stop();
  125. wifi::stop();
  126. }
  127. TEST_CASE("HTTP timeout", "[kbf_http]") {
  128. wifi::start();
  129. auto server = http::Server();
  130. http::Response (*handler)(const http::Request &, void *) = {[](const http::Request &request, void *) {
  131. kbf::sleep(stoi(request.query.at("sleep")));
  132. return http::Response("OK");
  133. }};
  134. server.route({http::GET, "/", handler, nullptr});
  135. server.start();
  136. state = 0;
  137. auto client = http::Client(1000);
  138. client.onError = {[](http::Client &) {
  139. TEST_ASSERT_EQUAL(0, state++);
  140. }};
  141. TEST_ASSERT_EQUAL_STRING("OK", client.get("http://localhost/?sleep=200")->body.c_str());
  142. TEST_ASSERT_EQUAL(0, state);
  143. TEST_ASSERT_NULL(client.get("http://localhost/?sleep=1200"))
  144. TEST_ASSERT_EQUAL(1, state);
  145. wifi::stop();
  146. }
  147. TEST_CASE("HTTP CORS", "[notimplemented]") {
  148. TEST_FAIL_MESSAGE("not yet implemented");
  149. }
  150. TEST_CASE("HTTP SPIFFS static route", "[notimplemented]") {
  151. TEST_FAIL_MESSAGE("not yet implemented");
  152. }