#include #include #include #include #include #include #include "utils.h" #include "json/parser.h" #include "manager.h" #include "http.h" // declare of scoped vars and funcs namespace hm { int gServerId = 0; std::map _gDefaultHeader = { // NOLINT {"Origin", "HM-Tech CPP"}, {"Server", "TinWeb"}, {"Cross-Origin-Resource-Policy", "*/*"}, {"Content-Type", "application/json"}, {"Content-Length", "2"}, }; struct Request { std::string method, uri, ver = "HTTP/1.1", unknown; json::Value json; std::map headers, form, args; }; struct Response { std::string status, msg, data; std::map headers; std::string body_data() const; // NOLINT void response(const int &client) const; }; Response Success(const std::string &data); Response Fail(const std::string &msg); std::string vec2str(const std::vector &vec); void RequestHandler(const int &client); Response httpGetFaceDevices(Request &request, const int &client); Response httpGetFaceDeviceUsers(Request &request, const int &client); Response httpInsertFaceDeviceUser(Request &request, const int &client); Response httpRemoveFaceDeviceUser(Request &request, const int &client); Response httpGetCarDevices(Request &request, const int &client); Response httpGetCarDeviceWoBList(Request &request, const int &client); Response httpInsertCarDeviceWoBList(Request &request, const int &client); Response httpRemoveCarDeviceWoBList(Request &request, const int &client); } // implement of scoped funcs namespace hm { std::string Response::body_data() const { /* 28: {"status":,"msg":"","data":} * */ size_t size = status.size() + msg.size() + data.size() + 28; std::string res(size, 0); sprintf( res.data(), R"({"status":%s,"msg":"%s","data":%s})", status.c_str(), msg.c_str(), data.c_str() ); return res; } void Response::response(const int &client) const { std::string res("HTTP/1.1 200 OK\r\n"); for (auto &it : headers) res.append(it.first + ": " + it.second + "\r\n"); res.append("\r\n" + body_data()); send(client, res.c_str(), res.size(), 0); } Response Success(const std::string &data) { Response resp = {.status="true", .msg="success", .data=data, .headers=_gDefaultHeader}; resp.headers["Content-Length"] = std::to_string(resp.body_data().size()); return resp; } Response Fail(const std::string &msg) { Response resp = {.status="false", .msg=msg, .data="null", .headers=_gDefaultHeader}; resp.headers["Content-Length"] = std::to_string(resp.body_data().size()); return resp; } std::string vec2str(const std::vector &vec) { std::string res; for (auto &it : vec) res.append(',' + it); if (res.empty()) res = "[]"; else res[0] = '[', res.push_back(']'); return res; } void RequestHandler(const int &client) { size_t size = 1024, read; char charBuf[size]; std::string strBuf; read = recv(client, charBuf, size, 0); if (read <= 0) { Fail("Unrecognized Request").response(client); close(client); return; } strBuf.append(charBuf, read); Request request; // request line size_t pos = strBuf.find(' '); request.method = strBuf.substr(0, pos); strBuf.erase(0, pos + 1); pos = strBuf.find(' '); request.uri = strBuf.substr(0, pos); if (request.uri.find('?') != std::string::npos) { request.uri = request.uri.substr(0, request.uri.find('?')); // TODO: parse url args } strBuf.erase(0, pos + 1); pos = strBuf.find("\r\n"); request.ver = strBuf.substr(0, pos); strBuf.erase(0, pos + 2); // header pos = strBuf.find("\r\n\r\n"); if (pos == std::string::npos) { while (true) { read = recv(client, charBuf, size, 0); strBuf.append(charBuf, read); pos = strBuf.find("\r\n\r\n"); if (pos != std::string::npos) break; } } std::string header = strBuf.substr(0, pos + 2), key; strBuf.erase(0, pos + 4); while (true) { pos = header.find(':'); if (pos == std::string::npos) break; key = header.substr(0, pos); header.erase(0, pos + 2); pos = header.find('\r'); request.headers[key] = header.substr(0, pos); header.erase(0, pos + 2); } // body if (request.headers.find("Content-Length") != request.headers.end()) { size_t rest = stoi(request.headers["Content-Length"]) - strBuf.size(); while (rest > 0) { read = recv(client, charBuf, size, 0); if (read <= 0) { Fail("Error Content Body").response(client); close(client); return; } rest -= read; strBuf.append(charBuf, read); } } if (request.headers.find("Content-Type") != request.headers.end()) { if (startsWith(request.headers["Content-Type"], "multipart/form-data")) { std::string bond = request.headers["Content-Type"].substr(30); while (true) { if (strBuf.find(bond) == std::string::npos) break; strBuf.erase(0, strBuf.find("name=") + 6); key = strBuf.substr(0, strBuf.find('"')); strBuf.erase(0, strBuf.find("\r\n\r\n") + 4); pos = strBuf.find(bond); request.form[key] = strBuf.substr(0, pos - 4); strBuf.erase(0, pos + bond.size()); } strBuf.clear(); } else if (request.headers["Content-Type"] == "application/json") { try { json::Parser parser; request.json = parser.parse(strBuf); strBuf.clear(); } catch (std::exception &error) { Fail(error.what()).response(client); close(client); return; } } else { Fail("Unrecognized Content Type").response(client); close(client); return; } } else { if (!strBuf.empty()) { Fail("Content-Type is needed if content is not null").response(client); strBuf.clear(); close(client); return; } } Response resp; if (request.uri == "/getDevices") resp = httpGetFaceDevices(request, client); else if (request.uri == "/getDeviceUsers") resp = httpGetFaceDeviceUsers(request, client); else if (request.uri == "/addDeviceUser") resp = httpInsertFaceDeviceUser(request, client); else if (request.uri == "/delDeviceUser") resp = httpRemoveFaceDeviceUser(request, client); else if (request.uri == "/getCarDevices") resp = httpGetCarDevices(request, client); else if (request.uri == "/getCarDeviceWoBList") resp = httpGetCarDeviceWoBList(request, client); else if (request.uri == "/addCarDeviceWoBList") resp = httpInsertCarDeviceWoBList(request, client); else if (request.uri == "/delCarDeviceWoBList") resp = httpRemoveCarDeviceWoBList(request, client); else { resp = Fail("API Not Found"); resp.response(client); } Log( Info, "method: [%s], uri: [%s], status: %s", request.method.c_str(), request.uri.c_str(), resp.status.c_str() ); close(client); } Response httpGetFaceDevices(Request &request, const int &client) { Response resp; // GET/POST std::string msg; std::vector devices; if (managerGetFaceDevices(devices, msg)) resp = Success(vec2str(devices)); else resp = Fail(msg); resp.response(client); return resp; } Response httpGetFaceDeviceUsers(Request &request, const int &client) { Response resp; // GET/POST if (request.form.find("seq") == request.form.end()) resp = Fail("seq need"); else { std::vector users; std::string msg; if (managerGetFaceDeviceUsers(request.form["seq"], users, msg)) resp = Success(vec2str(users)); else resp = Fail(msg); } resp.response(client); return resp; } Response httpInsertFaceDeviceUser(Request &request, const int &client) { Response resp; // POST if (request.method == "GET") resp = Fail("POST method only"); else if ( request.form.find("seq") == request.form.end() || request.form.find("uid") == request.form.end() || request.form.find("name") == request.form.end() || request.form.find("password") == request.form.end() || request.form.find("timestamp") == request.form.end() || request.form.find("face") == request.form.end() ) { resp = Fail("seq, uid, name, password, timestamp, face[file] need"); } else { try { User user = { .uid = request.form["uid"], .name = request.form["name"], .password = request.form["password"], .face = request.form["face"], .timestamp = std::stol(request.form["timestamp"]) }; if (user.face.size() > 200 * 1024) resp = Fail("image is too large"); else { std::string msg; if (managerInsertFaceDeviceUser(request.form["seq"], user, msg)) { resp = Success("null"); } else resp = Fail(msg); } } catch (...) { resp = Fail("param type error"); } } resp.response(client); return resp; } Response httpRemoveFaceDeviceUser(Request &request, const int &client) { Response resp; // POST if (request.method == "GET") resp = Fail("POST method only"); else if ( request.form.find("seq") == request.form.end() || request.form.find("uid") == request.form.end() ) { resp = Fail("seq, uid, name, year, month, day, face[file] need"); } else { std::string msg; if (managerRemoveFaceDeviceUser(request.form["seq"], request.form["uid"], msg)) { resp = Success("null"); } else resp = Fail(msg); } resp.response(client); return resp; } Response httpGetCarDevices(Request &request, const int &client) { Response resp; // GET/POST std::string msg; std::vector devices; if (managerGetCarDevices(devices, msg)) resp = Success(vec2str(devices)); else resp = Fail(msg); resp.response(client); return resp; } Response httpGetCarDeviceWoBList(Request &request, const int &client) { Response resp; // GET/POST try { auto dict = request.json.value(); if (dict.find("seq") == dict.end() || dict.find("isBlack") == dict.end()) { resp = Fail("seq, isBlack need"); } else { std::vector plates; std::string msg, seq = dict["seq"].value(); if (managerGetCarDeviceWoBList(seq, dict["isBlack"].value(), plates, msg)) resp = Success(vec2str(plates)); else resp = Fail(msg); } } catch (std::exception &error) { resp = Fail(error.what()); } resp.response(client); return resp; } Response httpInsertCarDeviceWoBList(Request &request, const int &client) { Response resp; // POST if (request.method == "GET") resp = Fail("POST method only"); try { auto dict = request.json.value(); if ( dict.find("seq") == dict.end() || dict.find("isBlack") == dict.end() || dict.find("plates") == dict.end() ) { resp = Fail("seq, isBlack, plates need in the first layer"); } else { std::string seq = dict["seq"].value(), msg; bool isBlack = dict["isBlack"].value(), errHpn = false; auto platesJson = dict["plates"].value(); std::vector plates(platesJson.size()); for (int i = 0; i < platesJson.size(); ++i) { auto dr = platesJson[i].value(); if ( dr.find("name") == dr.end() || dr.find("plate") == dr.end() || dr.find("timestamp") == dr.end() ) { resp = Fail("name, plate, timestamp need in plates list"); errHpn = true; break; } else { plates[i] = { .name = dr["name"].value(), .plate = dr["plate"].value(), .timestamp = dr["timestamp"].value() }; } } if (!errHpn) { if (managerInsertCarDeviceWoBList(seq, isBlack, plates, msg)) { resp = Success(msg); } else resp = Fail(msg); } } } catch (std::exception &error) { resp = Fail(error.what()); } resp.response(client); return resp; } Response httpRemoveCarDeviceWoBList(Request &request, const int &client) { Response resp; // POST if (request.method == "GET") resp = Fail("POST method only"); try { auto dict = request.json.value(); if ( dict.find("seq") == dict.end() || dict.find("isBlack") == dict.end() || dict.find("cids") == dict.end() ) { resp = Fail("seq, isBlack, cids need"); } else { std::string seq = dict["seq"].value(), msg; bool isBlack = dict["isBlack"].value(); auto cidsJson = dict["cids"].value(); std::vector cids(cidsJson.size()); for (int i = 0; i < cidsJson.size(); ++i) cids[i] = (int)cidsJson[i].value(); if (managerRemoveCarDeviceWoBList(seq, isBlack, cids, msg)) { resp = Success(msg); } else resp = Fail(msg); } } catch (std::exception &error) { resp = Fail(error.what()); } resp.response(client); return resp; } } // implement of export funcs namespace hm { [[noreturn]] void StartHttpServer() { gServerId = socket(AF_INET, SOCK_STREAM, 0); if (gServerId == -1) { LogM(Error, "can't start http server: 1"); throw "can't start http server: 1"; // NOLINT } sockaddr_in serverAddress{}; serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = INADDR_ANY; serverAddress.sin_port = htons(gConfServerPort); if (bind(gServerId, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == -1) { LogM(Error, "can't listen at port<%d>", gConfServerPort); throw "can't listen at port"; // NOLINT } if (listen(gServerId, 5) == -1) { LogM(Error, "can't start http server: 2"); close(gServerId); throw "can't start http server: 2"; // NOLINT } Log(Info, "server running on: http://0.0.0.0:%d/", gConfServerPort); while (true) { sockaddr_in addr{}; socklen_t size = sizeof addr; int client = accept(gServerId, (struct sockaddr *) &addr, &size); if (client == -1) { Log(Error, "got an unacceptable connection"); continue; } std::thread thread(RequestHandler, client); thread.detach(); } } void StopHttpServer() { close(gServerId); Log(Info, "http server stopped ~"); } }