handler.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #ifndef HV_HTTPD_HANDLER_H
  2. #define HV_HTTPD_HANDLER_H
  3. #include "HttpMessage.h"
  4. #include "HttpResponseWriter.h"
  5. #include "htime.h"
  6. #include "EventLoop.h" // import setTimeout, setInterval
  7. class Handler {
  8. public:
  9. // preprocessor => api_handlers => postprocessor
  10. static int preprocessor(HttpRequest* req, HttpResponse* resp) {
  11. // printf("%s:%d\n", req->client_addr.ip.c_str(), req->client_addr.port);
  12. // printf("%s\n", req->Dump(true, true).c_str());
  13. // if (req->content_type != APPLICATION_JSON) {
  14. // return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  15. // }
  16. // 解析body字符串到对应结构体(json、form、kv)
  17. req->ParseBody();
  18. // 响应格式默认为application/json
  19. resp->content_type = APPLICATION_JSON;
  20. // cors
  21. resp->headers["Access-Control-Allow-Origin"] = "*";
  22. if (req->method == HTTP_OPTIONS) {
  23. resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*");
  24. resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH");
  25. resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type");
  26. return HTTP_STATUS_NO_CONTENT;
  27. }
  28. #if 0
  29. // 前处理中我们可以做一些公共逻辑,如请求统计、请求拦截、API鉴权等,
  30. // 下面是一段简单的Token头校验代码
  31. // authentication sample code
  32. if (strcmp(req->path.c_str(), "/login") != 0) {
  33. string token = req->GetHeader("token");
  34. if (token.empty()) {
  35. response_status(resp, 10011, "Miss token");
  36. // 返回错误码将直接发送响应,不再走后续的处理
  37. return HTTP_STATUS_UNAUTHORIZED;
  38. }
  39. else if (strcmp(token.c_str(), "abcdefg") != 0) {
  40. response_status(resp, 10012, "Token wrong");
  41. return HTTP_STATUS_UNAUTHORIZED;
  42. }
  43. return 0;
  44. }
  45. #endif
  46. // 返回0表示继续走后面的处理流程
  47. return 0;
  48. }
  49. static int postprocessor(HttpRequest* req, HttpResponse* resp) {
  50. // 后处理中我们可以做一些后处理公共逻辑,如响应状态码统计、响应数据加密等
  51. // printf("%s\n", resp->Dump(true, true).c_str());
  52. return 0;
  53. }
  54. static int sleep(HttpRequest* req, HttpResponse* resp) {
  55. resp->Set("start_ms", gettimeofday_ms());
  56. std::string strTime = req->GetParam("t");
  57. if (!strTime.empty()) {
  58. int ms = atoi(strTime.c_str());
  59. if (ms > 0) {
  60. // 该操作会阻塞当前IO线程,仅用做测试接口,实际应用中请勿在回调中做耗时操作
  61. hv_delay(ms);
  62. }
  63. }
  64. resp->Set("end_ms", gettimeofday_ms());
  65. response_status(resp, 0, "OK");
  66. return 200;
  67. }
  68. static void setTimeout(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
  69. writer->response->Set("start_ms", gettimeofday_ms());
  70. std::string strTime = req->GetParam("t");
  71. if (!strTime.empty()) {
  72. int ms = atoi(strTime.c_str());
  73. if (ms > 0) {
  74. // 在回调线程中可直接使用setTimeout/setInterval定时器接口
  75. hv::setTimeout(ms, [writer](hv::TimerID timerID){
  76. writer->Begin();
  77. HttpResponse* resp = writer->response.get();
  78. resp->Set("end_ms", gettimeofday_ms());
  79. response_status(resp, 0, "OK");
  80. writer->End();
  81. });
  82. }
  83. }
  84. }
  85. static int query(HttpRequest* req, HttpResponse* resp) {
  86. // scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
  87. // ?query => HttpRequest::query_params
  88. // URL里携带的请求参数已被解析到query_params数据结构里,可通过GetParam("key")获取
  89. for (auto& param : req->query_params) {
  90. resp->Set(param.first.c_str(), param.second);
  91. }
  92. response_status(resp, 0, "OK");
  93. return 200;
  94. }
  95. static int kv(HttpRequest* req, HttpResponse* resp) {
  96. if (req->content_type != APPLICATION_URLENCODED) {
  97. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  98. }
  99. // 设置响应格式为application/x-www-form-urlencoded
  100. resp->content_type = APPLICATION_URLENCODED;
  101. resp->kv = req->kv;
  102. resp->kv["int"] = hv::to_string(123);
  103. resp->kv["float"] = hv::to_string(3.14);
  104. resp->kv["string"] = "hello";
  105. return 200;
  106. }
  107. static int json(HttpRequest* req, HttpResponse* resp) {
  108. if (req->content_type != APPLICATION_JSON) {
  109. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  110. }
  111. // 设置响应格式为application/json
  112. resp->content_type = APPLICATION_JSON;
  113. resp->json = req->json;
  114. resp->json["int"] = 123;
  115. resp->json["float"] = 3.14;
  116. resp->json["string"] = "hello";
  117. return 200;
  118. }
  119. static int form(HttpRequest* req, HttpResponse* resp) {
  120. if (req->content_type != MULTIPART_FORM_DATA) {
  121. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  122. }
  123. // 设置响应格式为multipart/form-data
  124. resp->content_type = MULTIPART_FORM_DATA;
  125. resp->form = req->form;
  126. resp->form["int"] = 123;
  127. resp->form["float"] = 3.14;
  128. resp->form["string"] = "hello";
  129. // 使用formdata格式传输文件
  130. // resp->form["file"] = FormData(NULL, "test.jpg");
  131. // resp->UploadFormFile("file", "test.jpg");
  132. return 200;
  133. }
  134. // 通过 Get/Set 接口可以 获取/设置 key:value 到对应数据结构体 json/form/kv
  135. static int test(HttpRequest* req, HttpResponse* resp) {
  136. // bool b = req->Get<bool>("bool");
  137. // int64_t n = req->Get<int64_t>("int");
  138. // double f = req->Get<double>("float");
  139. bool b = req->GetBool("bool");
  140. int64_t n = req->GetInt("int");
  141. double f = req->GetFloat("float");
  142. string str = req->GetString("string");
  143. resp->content_type = req->content_type;
  144. resp->Set("bool", b);
  145. resp->Set("int", n);
  146. resp->Set("float", f);
  147. resp->Set("string", str);
  148. response_status(resp, 0, "OK");
  149. return 200;
  150. }
  151. static int grpc(HttpRequest* req, HttpResponse* resp) {
  152. if (req->content_type != APPLICATION_GRPC) {
  153. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  154. }
  155. // 需引入protobuf库序列化/反序列化body
  156. // parse protobuf
  157. // ParseFromString(req->body);
  158. // resp->content_type = APPLICATION_GRPC;
  159. // serailize protobuf
  160. // resp->body = SerializeAsString(xxx);
  161. response_status(resp, 0, "OK");
  162. return 200;
  163. }
  164. static int restful(HttpRequest* req, HttpResponse* resp) {
  165. // RESTful /:field/ => HttpRequest::query_params
  166. // path=/group/:group_name/user/:user_id
  167. // restful风格URL里的参数已被解析到query_params数据结构里,可通过GetParam("key")获取
  168. std::string group_name = req->GetParam("group_name");
  169. std::string user_id = req->GetParam("user_id");
  170. resp->Set("group_name", group_name);
  171. resp->Set("user_id", user_id);
  172. response_status(resp, 0, "OK");
  173. return 200;
  174. }
  175. // 登录示例:校验用户名,密码,返回一个token
  176. static int login(HttpRequest* req, HttpResponse* resp) {
  177. string username = req->GetString("username");
  178. string password = req->GetString("password");
  179. if (username.empty() || password.empty()) {
  180. response_status(resp, 10001, "Miss username or password");
  181. return HTTP_STATUS_BAD_REQUEST;
  182. }
  183. else if (strcmp(username.c_str(), "admin") != 0) {
  184. response_status(resp, 10002, "Username not exist");
  185. return HTTP_STATUS_BAD_REQUEST;
  186. }
  187. else if (strcmp(password.c_str(), "123456") != 0) {
  188. response_status(resp, 10003, "Password wrong");
  189. return HTTP_STATUS_BAD_REQUEST;
  190. }
  191. else {
  192. resp->Set("token", "abcdefg");
  193. response_status(resp, 0, "OK");
  194. return HTTP_STATUS_OK;
  195. }
  196. }
  197. // 上传文件示例
  198. static int upload(HttpRequest* req, HttpResponse* resp) {
  199. // return resp->SaveFormFile("file", "html/uploads/test.jpg");
  200. if (req->content_type != MULTIPART_FORM_DATA) {
  201. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  202. }
  203. const FormData& file = req->form["file"];
  204. if (file.content.empty()) {
  205. return response_status(resp, HTTP_STATUS_BAD_REQUEST);
  206. }
  207. string filepath("html/uploads/");
  208. filepath += file.filename;
  209. FILE* fp = fopen(filepath.c_str(), "wb");
  210. if (fp) {
  211. fwrite(file.content.data(), 1, file.content.size(), fp);
  212. fclose(fp);
  213. }
  214. response_status(resp, 0, "OK");
  215. return 200;
  216. }
  217. private:
  218. // 统一的响应格式
  219. static int response_status(HttpResponse* resp, int code = 200, const char* message = NULL) {
  220. resp->Set("code", code);
  221. if (message == NULL) message = http_status_str((enum http_status)code);
  222. resp->Set("message", message);
  223. resp->DumpBody();
  224. return code;
  225. }
  226. };
  227. #endif // HV_HTTPD_HANDLER_H