123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- #include <string>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <unistd.h>
- #include <cstring>
- #include <thread>
- #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<std::string, std::string> _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<std::string, std::string> headers, form, args;
- };
- struct Response {
- std::string status, msg, data;
- std::map<std::string, std::string> 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<std::string> &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<std::string> &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<std::string> 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<std::string> 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<std::string> 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<json::dict_type>();
- if (dict.find("seq") == dict.end() || dict.find("isBlack") == dict.end()) {
- resp = Fail("seq, isBlack need");
- } else {
- std::vector<std::string> plates;
- std::string msg, seq = dict["seq"].value<std::string>();
- if (managerGetCarDeviceWoBList(seq, dict["isBlack"].value<bool>(), 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<json::dict_type>();
- 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<std::string>(), msg;
- bool isBlack = dict["isBlack"].value<bool>(), errHpn = false;
- auto platesJson = dict["plates"].value<json::list_type>();
- std::vector<Plate> plates(platesJson.size());
- for (int i = 0; i < platesJson.size(); ++i) {
- auto dr = platesJson[i].value<json::dict_type>();
- 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<std::string>(), .plate = dr["plate"].value<std::string>(),
- .timestamp = dr["timestamp"].value<json::int_type>()
- };
- }
- }
- 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<json::dict_type>();
- 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<std::string>(), msg;
- bool isBlack = dict["isBlack"].value<bool>();
- auto cidsJson = dict["cids"].value<json::list_type>();
- std::vector<int> cids(cidsJson.size());
- for (int i = 0; i < cidsJson.size(); ++i) cids[i] = (int)cidsJson[i].value<json::int_type>();
- 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 ~");
- }
- }
|