feat: get printers shared via cups #49
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/cups-get-printers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Review Summary
Good work on implementing the CUPS printer discovery feature. The C++ native module compiles cleanly, passes both clang-tidy and clang-format checks, and the overall architecture (using
Napi::AsyncWorkerfor blocking I/O) is correct.Here are my findings organized by severity:
🔴 Critical Issues
1. Use-after-free race condition in
cups-get-dests.cppIn
CupsGetDestsWorker, the rawhttp_t*pointer is stored fromExternal::Data(). If theCupsConnectionJS object is garbage collected while the worker is executingExecute(), the finalizer will callhttpClose()on the underlying pointer, but the worker thread will still be using it — leading to a use-after-free.The
Externalis only kept alive by the JS-sideCupsConnection.datareference, but the native worker doesn't hold a strong reference to it during execution.Fix: Store a
Napi::ObjectReference(orNapi::Persistent) to the JS object in the worker to keep it alive during execution:🟡 Medium Issues
2. No explicit resource cleanup for
CupsConnectionThe
CupsConnectionclass has nodestroy()orclose()method. The underlyinghttp_t*is only closed when theExternalfinalizer runs, which is non-deterministic. In long-running servers, this could lead to leaked HTTP connections.Suggestion: Add a
close()method:3. Empty stub methods in
CupsPrintergetInfo()andsendJob()are empty bodies. These should either be implemented or marked with// TODO:comments so consumers know they're not yet available.4. Unused exported type
CupsPrinterInfoDataThis branded type is exported but never used anywhere. It appears to be a placeholder for future
getInfo()implementation. Either use it or remove it to avoid confusion.🟢 Minor / Style
5.
CupsPrintercreates a circular referenceCupsPrinterholds a reference toCupsConnection, which in turn would hold references toCupsPrinter(if added to a list). This is handled by the JS GC, but it meansCupsConnectioncannot be collected while anyCupsPrinterreferences exist. Consider whetherCupsPrinterneeds to hold the connection reference at all, or if it could be accessed lazily.6. Buffer size constants in
http-connect-uri.cppThe local variables
schemeLen,usernameLen, etc. are declaredconst autobut are only used in one place. Consider simplifying:7. Missing C++23 requirement in docs
The meson.build upgrades to C++23 for
std::views::enumerate, but the README and AGENTS.md don't mention a minimum C++23 compiler requirement. Consider adding this to the prerequisites.✅ Positive Notes
http-connect-uri.cppandcups-get-dests.cppeach handle a single responsibilityNapi::AsyncWorkerfor blocking libcups callsExternalfinalizers (httpCloseandcupsFreeDests)is_system: truefor system headersunknown & { __brand: "..." }) provides nominal type safety@ -0,0 +10,4 @@Napi::Promise::Deferred deferred;http_t* connection;std::vector<cups_dest_t> dests;Use-after-free: raw http_t* stored from External::Data() without keeping JS object alive during worker execution. If CupsConnection is GC'd mid-execution, httpClose() frees the pointer while worker thread still uses it.
@ -0,0 +28,4 @@auto usernameBuf = std::array<char, usernameLen>();const auto hostLen = HTTP_MAX_HOST;auto hostBuf = std::array<char, hostLen>();const auto resourceLen = HTTP_MAX_URI;Consider simplifying — no need for const auto intermediate variables.
@ -0,0 +2,4 @@export type CupsConnectionData = unknown & { __brand: "CupsConnectionData" };export type CupsPrinterData = unknown & { __brand: "CupsPrinterData" };export type CupsPrinterInfoData = unknown & { __brand: "CupsPrinterInfoData" };Unused exported type. Either use it for getInfo() or remove it.
@ -0,0 +9,4 @@constructor(data: CupsConnectionData) {this.data = data;}Consider adding a close()/destroy() method to explicitly clean up the underlying HTTP connection instead of relying on GC.
@ -0,0 +32,4 @@this.connection = connection;this.name = name;this.isDefault = isDefault;this.data = data;Empty stub method — implement or mark as TODO.
@ -0,0 +34,4 @@this.isDefault = isDefault;this.data = data;}Empty stub method — implement or mark as TODO.