CDP browser pool

The app/pdfrender/cdp package manages the headless browser fleet.

Pool (pool.go)

Built on jolestar/go-commons-pool. Borrowing runs a callback with a page provider; on success the object is returned to the pool, on error it is invalidated (destroyed). Key settings: BlockWhenExhausted, TestOnBorrow (dead browsers are validated out on borrow), a custom force-eviction policy, and the evictor. Defaults: pool size 1, borrow timeout 1m, repay timeout 5s.

Stats() exposes active/idle/destroyed/invalidated counts to /_status.

Factory (factory.go) — two modes

The factory implements the pool’s object lifecycle (MakeObject/DestroyObject/ValidateObject/ActivateObject/PassivateObject) by delegating to a page-wrapper factory chosen by --factory-type:

  • runner — each pooled object is a whole browser process. Make builds and runs a runner, connects DevTools, and wraps the page; Destroy always stops the browser. Works on Chromium and Firefox.
  • page — one browser process is launched at construction; each pooled object is a new tab. Chromium only.

ValidateObject recycles a page after --factory-max-usage-count renders or if its navigation history can’t be read (dead browser). ActivateObject/ PassivateObject toggle lifecycle-event subscriptions and bump the usage count.

Runner & process management (runner.go)

The runner launches the browser with a hardened headless flag set, in its own process group (Setpgid). It scans stdout for the DevTools listening on … line to discover the debugging URL.

Teardown is the subtle part: because Chromium’s helper processes (GPU, renderers, crash handler) are not children of the main process, Stop SIGTERMs the main process, then polls /proc for any live group member and escalates to SIGKILL across the whole group after --browser-process-group-wait-timeout. A cancelled context escalates immediately. The per-instance temp profile dir is then removed. This is what prevents leaked browser processes.

Render (render.go)

RenderPDF wraps the whole operation in the render timeout, registers the document with the internal server, builds printToPDF args, and borrows a browser with retries. printPDF sets up the event waiter, navigates, waits, and prints.

Extending

  • New factory mode: add a FactoryType, a page-wrapper factory, and a case in NewFactory.
  • New wait signal beyond lifecycle events: add a branch in initEventWaiter (like the callback path).