feat(http): add Lua script handler - #856
Conversation
There was a problem hiding this comment.
Pull request overview
Adds optional Lua scripting support to libhv’s HTTP server so requests can be handled by .lua scripts via a generic HttpScriptHandler (backed by HttpLuaHandler), including directory-to-route script mapping through HttpService::Script.
Changes:
- Introduces
HttpScriptHandler/HttpLuaHandlerto execute Lua scripts with a minimalctxAPI and basichvhost helpers. - Adds
HttpService::Script()for mapping URL prefixes to a script directory with per-script handler caching. - Updates build/test infrastructure (Makefile + CMake + unittest runner), adds a new unit test, example script, and CN documentation.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| unittest/http_lua_handler_test.cpp | Adds unit tests covering Lua handler responses, method dispatch preference, script-dir mapping, and traversal blocking. |
| unittest/CMakeLists.txt | Builds the new Lua unittest when WITH_LUA is enabled. |
| scripts/unittest.sh | Executes the Lua unittest when present. |
| Makefile.vars | Adds Lua detection/link flags for Makefile builds. |
| Makefile.in | Includes Makefile.vars and wires WITH_LUA flags into compile/link. |
| Makefile | Installs Lua handler headers when enabled and builds the new unittest binary under WITH_LUA. |
| http/server/HttpService.h | Adds HttpService::Script() API under WITH_LUA; simplifies http_handler copy/assign. |
| http/server/HttpService.cpp | Implements HttpService::Script() with per-script handler caching. |
| http/server/HttpScriptHandler.h | Declares generic script handler API and options. |
| http/server/HttpScriptHandler.cpp | Implements script type dispatch (currently .lua → HttpLuaHandler). |
| http/server/HttpLuaHandler.h | Declares Lua-backed handler with reload-on-change and error tracking. |
| http/server/HttpLuaHandler.cpp | Implements Lua VM lifecycle, ctx bindings, JSON conversion, and method-specific dispatch. |
| hconfig.h.in | Adds WITH_LUA config define for generated config headers. |
| examples/scripts/hello.lua | Provides a sample Lua script demonstrating get/post/handle handlers. |
| examples/http_server_test.cpp | Demonstrates registering Lua script routes and script directory mapping under WITH_LUA. |
| docs/PLAN.md | Notes “http lua handler” as a planned/available capability. |
| docs/cn/README.md | Links to the new CN documentation page for the Lua handler. |
| docs/cn/HttpLuaHandler.md | Documents build, usage, directory mapping, exposed APIs, and hot reload behavior. |
| config.mk | Adds WITH_LUA default and updates config date. |
| config.ini | Adds WITH_LUA option documentation/config entry. |
| CMakeLists.txt | Adds WITH_LUA option and links Lua when enabled. |
| .gitignore | Ignores agent-related directories/files. |
Comments suppressed due to low confidence (1)
http/server/HttpScriptHandler.cpp:50
- Lazy initialization of
state_->lua_handleris not thread-safe. Usestd::call_once(together with thestd::once_flaginState) to ensure only one thread initializes the Lua handler.
if (!state_->lua_handler) {
HttpLuaHandlerOptions lua_options;
lua_options.reload_on_change = options_.reload_on_change;
state_->lua_handler = std::make_shared<HttpLuaHandler>(filepath_.c_str(), lua_options);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #include "HttpScriptHandler.h" | ||
| #include "hpath.h" | ||
| #include "hstring.h" | ||
| #include <string.h> | ||
| #endif |
| struct HttpScriptHandler::State { | ||
| HttpLuaHandlerPtr lua_handler; | ||
| }; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
Makefile.vars:13
- Lua autodetection via pkg-config currently checks only for a "lua" package, and the fallback prefix probe won’t match common layouts like /usr/include/lua5.4/lua.h (e.g., Debian/Ubuntu). This can leave LUA_CFLAGS empty and break WITH_LUA builds even when liblua5.x-dev is installed. Consider probing common pkg-config names and include layouts.
LUA_PKG_CONFIG ?= $(shell if command -v $(PKG_CONFIG) >/dev/null 2>&1 && $(PKG_CONFIG) --exists lua; then echo lua; fi)
LUA_PREFIX ?= $(shell for dir in /opt/homebrew/opt/lua /usr/local/opt/lua /usr; do if [ -f "$$dir/include/lua/lua.h" ] || [ -f "$$dir/include/lua.h" ]; then echo $$dir; break; fi; done)
LUA_INCLUDE_DIR ?= $(shell if [ -n "$(LUA_PREFIX)" ]; then for dir in "$(LUA_PREFIX)/include/lua" "$(LUA_PREFIX)/include/lua5.5" "$(LUA_PREFIX)/include/lua5.4" "$(LUA_PREFIX)/include/lua5.3" "$(LUA_PREFIX)/include"; do if [ -f "$$dir/lua.h" ]; then echo $$dir; break; fi; done; fi)
CMakeLists.txt:194
- When WITH_LUA is ON, CMake currently defines WITH_LUA even if find_package(Lua) fails, and then falls back to linking "lua" without adding include directories. This can lead to confusing build failures at compile time (missing lua.h) instead of a clear configure-time error. Prefer making Lua a REQUIRED dependency when WITH_LUA is enabled.
if(WITH_LUA)
add_definitions(-DWITH_LUA)
find_package(Lua)
if(LUA_FOUND)
include_directories(${LUA_INCLUDE_DIR})
set(LIBS ${LIBS} ${LUA_LIBRARIES})
else()
set(LIBS ${LIBS} lua)
endif()
endif()
http/server/HttpService.cpp:164
- HttpService::Script caches a new HttpScriptHandler for every distinct URL-derived script path, even when the script file doesn’t exist. A client can hit many random URLs under the mapped prefix and grow the in-memory scripts map without bound, and missing scripts will later return 500 from the handler. It’s safer to verify the target script exists before inserting into the cache and return 404 for missing scripts.
std::string script = HPath::join(root, name);
if (HPath::suffixname(script).empty()) {
script += ".lua";
}
http/server/HttpLuaHandler.cpp:386
- On Lua runtime errors (lua_pcall failure), the handler writes the raw Lua error string into the HTTP response body. This can expose internal details (file paths, stack traces, host error strings) to remote clients. Consider returning a generic message to the client while keeping the detailed error in logs/lastError().
setErrorLocked(error);
hloge("call lua script %s failed: %s", filepath_.c_str(), error.c_str());
ctx->response->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
ctx->response->String(error);
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
http/server/HttpLuaHandler.cpp:418
- When initial load/reload fails, the handler returns last_error_ directly in the response body. This can leak filesystem and host error details to clients. Consider returning a generic 500 message and keep last_error_ for logs/diagnostics via lastError().
if (!reloadIfNeeded()) {
std::lock_guard<std::mutex> lock(mutex_);
ctx->response->status_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
ctx->response->String(last_error_);
return HTTP_STATUS_INTERNAL_SERVER_ERROR;
Summary
Tests