5.8 KiB
Dreamwidth Plack Implementation
This document describes the Plack/Starman web server implementation for Dreamwidth, including architecture, middleware, routing, and testing.
Overview
Dreamwidth runs under both Apache/mod_perl and Plack/Starman. The DW::Request abstraction layer lets most application code work under either server. The Plack stack handles routing, BML rendering, journal pages, static assets, authentication, and sysban enforcement.
Running the Server
# Inside the devcontainer
perl bin/starman --port 8080 # single worker (default)
perl bin/starman --port 8080 --workers 4 # multi-worker
The entry point is app.psgi. The LJ_IS_DEV_SERVER environment variable enables dev mode (hot-reloading, ?as= impersonation, auto-verified accounts). See the safety warning in cgi-bin/ljlib.pl.
Middleware Stack
Applied in order by app.psgi via Plack::Builder. Order matters.
| Order | Middleware | File | Purpose |
|---|---|---|---|
| 1 | Plack::Middleware::Options |
(CPAN) | Handles OPTIONS requests; rejects disallowed HTTP methods |
| 2 | DW::RequestWrapper |
Plack/Middleware/DW/RequestWrapper.pm |
Creates DW::Request::Plack, calls LJ::start_request()/end_request(), registers standard resources |
| 3 | DW::Redirects |
Plack/Middleware/DW/Redirects.pm |
Canonical domain redirects and redirect.dat entries |
| 4 | DW::Dev |
Plack/Middleware/DW/Dev.pm |
Dev-only: hot-reloads changed Perl modules |
| 5 | DW::XForwardedFor |
Plack/Middleware/DW/XForwardedFor.pm |
Extracts real client IP from X-Forwarded-For when $TRUST_X_HEADERS is set |
| 6 | DW::ConcatRes |
Plack/Middleware/DW/ConcatRes.pm |
Concatenated CSS/JS combo handler (/stc/??a.css,b.css) |
| 7 | Plack::Middleware::Static |
(CPAN) | Serves static files from htdocs/ directories (/img/, /stc/, /js/) |
| 8 | DW::UniqCookie |
Plack/Middleware/DW/UniqCookie.pm |
Ensures unique tracking cookie is set |
| 9 | DW::Auth |
Plack/Middleware/DW/Auth.pm |
Resolves session cookies, sets remote user. Dev: ?as=username impersonation |
| 10 | DW::Sysban |
Plack/Middleware/DW/Sysban.pm |
Checks IP/uniq/tempbans, returns 403 for banned requests |
Request Routing
The $app handler in app.psgi dispatches requests through three systems in order:
-
DW::Routing — Modern controller-based routes (
DW::Controller::*). Handles/api/v\d+/endpoints and all routes registered viaDW::Routing->register_*. -
DW::Controller::Journal — Path-based journal URLs (
/~user/...,/users/user/...). Extracts the journal username and delegates toLJ::make_journal(). -
DW::BML — Legacy BML page fallback. Resolves URI to a
.bmlfile inhtdocs/, renders it via the BML engine (shared withApache::BML).
If none of these handle the request, a 404 is returned.
Key Modules
DW::Request::Plack (cgi-bin/DW/Request/Plack.pm)
Implements the DW::Request interface over Plack::Request/Plack::Response. Provides method(), uri(), path(), host(), header_in(), header_out(), status(), print(), redirect(), res(), and cookie management. uri() returns path-only (not full URL) to match Apache behavior.
DW::BML (cgi-bin/DW/BML.pm)
Plack-compatible BML renderer. Reuses the core BML engine from Apache::BML (%Apache::BML::FileConfig) while replacing the Apache-specific request handling. Includes path traversal protection and _config.bml access blocking.
DW::Controller::Journal (cgi-bin/DW/Controller/Journal.pm)
Shared journal rendering controller. Validates usernames via LJ::canonical_username(), delegates access control and rendering to LJ::make_journal(). Handles entry date validation against URL parameters.
DW::Controller::Userpic (cgi-bin/DW/Controller/Userpic.pm)
Serves userpic images. Route regex ensures numeric IDs only. Works under both Apache and Plack.
Testing
Test Files
| Test | What it covers |
|---|---|
t/plack-app.t |
Module loading, app.psgi compilation, request/response object methods, API route detection |
t/plack-middleware.t |
Middleware module loading, instantiation, inheritance |
t/plack-auth.t |
Auth middleware: session resolution, ?as= impersonation, anonymous handling |
t/plack-sysban.t |
Sysban middleware: IP bans, uniq bans, tempbans, noanon_ip |
t/plack-integration.t |
Full middleware stack: homepage rendering, API endpoints, redirects, method filtering |
t/plack-static.t |
Static file serving from htdocs directories |
t/plack-bml.t |
BML page resolution and rendering |
t/plack-controller.t |
Journal controller routing and rendering |
Running Tests
# All Plack tests
prove t/plack-*.t
# Individual test
prove -v t/plack-auth.t
Tests use mocking (LJ::Session, LJ::load_user, DW::Routing::call, etc.) to run without a full database. Most tests load the real app.psgi and exercise the full middleware stack via Plack::Test.
Security Notes
LJ_IS_DEV_SERVERmust never be set in production. It enables?as=user impersonation, auto-verified accounts, and skips domain validation. See comment incgi-bin/ljlib.pl.- Auth middleware always marks auth resolution (calls
set_remoteeven for anonymous) to preventLJ::get_remote()from re-entering session resolution. - Sysban middleware checks
LJ::get_remote_ip()plus all X-Forwarded-For IPs, matching the Apache@req_hostspattern. - XForwardedFor only processes proxy headers when
$LJ::TRUST_X_HEADERSis configured.
Adding New Middleware
- Create module in
cgi-bin/Plack/Middleware/DW/, inheriting fromPlack::Middleware - Add
enableline inapp.psgiat the appropriate position in the stack - Add tests in
t/plack-*.t - Run
perl extlib/bin/tidyall <file>andprove t/plack-*.t