// // Created by xajhuang on 2022/11/4. // #ifdef HTTPSERVER_ON #include #include "http_svr.h" #include "user_errno.h" #include "config.h" #include "common.h" #include "uthash/uthash.h" #include "zlog_module.h" #include "s2j/cJSON.h" #include "proto.h" typedef struct { HTTP_METHOD httpMethod; char methodName[16]; } HTTP_METHOD_INFO; #define GENERATE_HTTP_ENUM_ARRAY(ENUM, desc) {ENUM, desc}, static HTTP_METHOD_INFO g_httpMethod[] = { DEF_HTTP_METHOD(GENERATE_HTTP_ENUM_ARRAY) {-1, "UNKNOWN"} }; typedef struct { char routeName[MAX_URI]; char *pMethod; HTTP_ROUTE_PRIORITY priority; HTTP_REQUEST_CB pCallbackHandler; void *pUserData; UT_hash_handle hh; } HTTP_ROUTE_TABLE, *PHTTP_ROUTE_TABLE; static uv_rwlock_t g_uvRouteLock; static PHTTP_ROUTE_TABLE g_pHttpRoute = NULL; static void rsp_err_msg(struct mg_connection *c, int httpCode, int errCode) { cJSON *pRspMsg = cJSON_CreateObject(); cJSON_AddNumberToObject(pRspMsg, "status", errCode); cJSON_AddStringToObject(pRspMsg, "message", getErrorEnumDesc(errCode)); const char *pStrPro = proto_create_new(pRspMsg, httpCode); mg_http_reply(c, httpCode, "Content-Type: application/json\r\n", "%s\n", pStrPro ? pStrPro : ""); LOG_MOD(debug, ZLOG_MOD_HTTPD, "Response(%d): %s\n", httpCode, pStrPro); if (pStrPro) { free((void *)pStrPro); } } static void on_mg_event_cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *)ev_data; PHTTP_ROUTE_TABLE pItem, pTmp; int matched = FALSE; LOG_MOD(trace, ZLOG_MOD_HTTPD, "Request: %s\n", hm->uri.ptr); HASH_ITER(hh, g_pHttpRoute, pItem, pTmp) { if (mg_http_match_uri(hm, pItem->routeName)) { struct mg_str m = mg_str(pItem->pMethod); if (mg_strcmp(m, hm->method) == 0) { PHTTP_RSP_CTX pCtx = (PHTTP_RSP_CTX)malloc(sizeof(HTTP_RSP_CTX)); if (pCtx) { pItem->pCallbackHandler(hm, pItem->pUserData, pCtx); if (pCtx->errCode == ERR_SUCCESS && pCtx->pRspData) { mg_http_reply(c, pCtx->httpCode, pCtx->pRspHeads, "%s\n", pCtx->pRspData); LOG_MOD(debug, ZLOG_MOD_HTTPD, "Response:(%d) %s\n", pCtx->httpCode, (char *)pCtx->pRspData); } else { rsp_err_msg(c, pCtx->httpCode, pCtx->errCode); } if (pCtx->pRspData) { free(pCtx->pRspData); } } else { rsp_err_msg(c, 500, ERR_MALLOC_MEMORY); } free(pCtx); } else { rsp_err_msg(c, 405, ERR_HTTP_UNSUP_METHOD); } matched = TRUE; } } if (!matched) { rsp_err_msg(c, 404, ERR_HTTP_UNSUP_PAGE); } } } static void httpLoopCb(void *UNUSED(pArg)) { struct mg_mgr mgr; char svrUrl[MAX_URI]; memset(svrUrl, 0, MAX_URI); mg_log_set(MG_LL_DEBUG); mg_mgr_init(&mgr); // Init manager mg_http_listen(&mgr, config_get_http_server_addr(), on_mg_event_cb, &mgr); // Setup listener for (;;) { mg_mgr_poll(&mgr, 1000); // Event loop } mg_mgr_free(&mgr); } static int route_sort(PHTTP_ROUTE_TABLE a, PHTTP_ROUTE_TABLE b) { return ((int)b->priority - (int)a->priority); } int http_add_route(const char *path, HTTP_METHOD method, HTTP_ROUTE_PRIORITY priority, HTTP_REQUEST_CB cb, void *pUserData) { unsigned int nSize = sizeof(HTTP_ROUTE_TABLE); PHTTP_ROUTE_TABLE pTbl; if (path == NULL || strlen(path) == 0) { return -ERR_INPUT_PARAMS; } else if (method >= ARRAY_SIZE(g_httpMethod) || method < 0) { return -ERR_INPUT_PARAMS; } else if (cb == NULL) { return -ERR_INPUT_PARAMS; } pTbl = (PHTTP_ROUTE_TABLE)malloc(nSize); if (pTbl == NULL) { LOG_MOD(error, ZLOG_MOD_HTTPD, "Malloc memory error size %u\n", nSize); return -ERR_MALLOC_MEMORY; } memset(pTbl, 0, nSize); pTbl->pMethod = g_httpMethod[method].methodName; pTbl->priority = (priority < 0) ? PRI_LOWEASE : (priority > PRI_HIGHEST ? PRI_HIGHEST : priority); pTbl->pCallbackHandler = cb; pTbl->pUserData = pUserData; strncpy(pTbl->routeName, path, MAX_URI); uv_rwlock_wrlock(&g_uvRouteLock); HASH_ADD_STR(g_pHttpRoute, routeName, pTbl); HASH_SORT(g_pHttpRoute, route_sort); uv_rwlock_wrunlock(&g_uvRouteLock); return -ERR_SUCCESS; } int http_svr_init() { static uv_thread_t uvThread; uv_rwlock_init(&g_uvRouteLock); uv_thread_create(&uvThread, httpLoopCb, NULL); return ERR_SUCCESS; } int http_svr_uinit() { PHTTP_ROUTE_TABLE pItem, pTmp; HASH_ITER(hh, g_pHttpRoute, pItem, pTmp) { uv_rwlock_wrlock(&g_uvRouteLock); HASH_DEL(g_pHttpRoute, pItem); uv_rwlock_wrunlock(&g_uvRouteLock); free(pItem); } return ERR_SUCCESS; } #endif