File: //opt/cpanel/ea-ruby27/src/passenger-release-6.0.27/doc/AiInstructions.md
This is codebase of the Passenger application server.
## Architecture summary
### Multi-process
- Web server (Apache or Nginx, both with the Passenger module loaded). Only simple I/O processing and lifecycle management of the Passenger Watchdog Agent.
- Passenger Watchdog Agent: spawns and supervises Passenger Core Agent and a bunch of other periodic helper tasks.
- Passenger Core Agent. Most logic happens here. Main request processing (Controller), spawns (SpawningKit) and supervises (ApplicationPool) application processes, keeps track of various statistics. Has an API server (separate socket) for management operations (used by e.g. `passenger-status`).
### HTTP request routing path
1. Requests are commonly first received by the web server, which forwards request to Passenger Core. Core's Controller subsystem then processes it.
2. Requests may also be directly received by Passenger Core (Passenger Standalone + "builtin" engine).
3. Passenger Core's Controller subsystem uses ApplicationPool to determine whether to spawn an application process or which process to route request to. Connects to the app's socket or reuses keepalive socket.
4. Response from app forwarded back to the connecting client (could be a real client or the web server).
### Applcation processes lifecycle management and request routing
- Main logic in ApplicationPool (src/agent/Core/ApplicationPool). Keeps track of which application Processes exist. Contains logic for routing a request to best Process.
- Pool has many Groups. Group has many Processes (for application processes). Process has many Sockets (of which one is the "main socket" for receiving requests).
- Spawning logic outsourced to SpawningKit.
### Application spawning
- Main logic in SpawningKit (src/agent/Core/SpawningKit)
- Creates subprocess, sets up appropriate environment:
- Basic things like working directory and UID.
- But also a temporary directory for subprocesses to communicate its spawning progress with the parent process.
- Once generic subprocess environment setup is done, exec()s a language-specific "loader" which handles further language-specific spawning. For example for Ruby apps, rack-preloader.rb and rack-loader.rb are applicable.
- Two spawning modes:
- "direct" is a normal execution of the app without fancy tricks. Uses "loaders".
- "smart" uses preforking. Preforking server is started through a "preloader". SpawningKit communicates with preforking server to perform further actual spawning.
## C++ tech stack
- Agent written in C++
- C++11
- Boost. But prefer C++11
### HTTP and non-blocking I/O handling
- ServerKit (src/cxx_supportlib/ServerKit)
- libev
- Some parts use libuv. Integrates with libev via SafeLibev.h
## Important directories
- src: most source code
- src/agent: various agents
- src/agent/Core
- src/agent/Watchdog
- src/nginx_module
- src/apache2_module
- src/cxx_supportlib: C++ support library
- src/helper-scripts: various helper scripts, including SpawningKit language-specific (pre)loaders.
## Build system
- Building agents: `drake -j4 nginx` (for concurrent building) or `rake nginx`
- Building C++ test suite: `drake -j4 test:cxx:build` (for concurrent building) or `rake test:cxx:build`
## Special files
*.cxxcodebuilder: a Ruby DSL for generating C/C++ code: https://github.com/phusion/cxxcodebuilder/blob/master/README.md
Similar in philosophy as Rails's Jbuilder.
Convention: AutoGeneratedFoo.c.cxxcodebuilder generates AutoGeneratedFoo.c
## Config handling
Apache/Nginx:
- Config flows from the web server to the Core to the app.
- There are three levels of configs: global, per-application, per-request. They're centrally registered:
- src/ruby_supportlib/phusion_passenger/apache2/config_options.rb
- src/ruby_supportlib/phusion_passenger/nginx/config_options.rb
- Module C/C++ code for managing these configs are autogenerated by build system from config_options.rb.
Passenger Standalone:
- Config flows from the Passenger Standalone CLI tool, either to Nginx ("nginx" engine mode) or directly to Core ("builtin" engine mode).
- Config options are centrally registrered in src/ruby_supportlib/phusion_passenger/standalone/config_options_list.rb.
- Nginx engine: config to pass from CLI to Nginx, are registered in the Nginx config templates in resources/templates/standalone.
- Builtin engine: config to pass from CLI to Core (via Watchdog) through src/ruby_supportlib/phusion_passenger/standalone/start_command/builtin_engine.rb build_daemon_controller_options.
In the Core Controller there are also three levels of configs: global (via ConfigKit), per-application and per-request. Per-application and per-request configs are fetched from HTTP headers. Per-application configs are passed to ApplicationPool options. Per-request configs are sometimes (but usually not) passed to ApplicationPool.
Keep config option naming between the different integration modes consistent, yet adhering to naming conventions for that integration mode. Example: Apache=PassengerStartTimeout, Nginx=passenger_start_timeout, Standalone=start_timeout (config file) or --start-timeout (CLI arg).
## Coding guidelines
### General
- Make the smallest changes to existing interfaces as possible. Be averse to rewriting things.
- Keep code readable and focused by splitting smaller or side concerns into functions.
- Unless the code is trivial and small, prefer not to duplicate code: prefer splitting to common functions.
- When appropriate, consult design aspects documentation in doc/DesignAspects.
### For C++
- Don't introduce direct libev/libuv dependency in components that don't already use libev/libuv/ServerKit.
- Prefer using internal utility library.
- Prefer oxt/system_calls.hpp wrappers (e.g. oxt::open) over direct syscalls. If no wrapper available, loop until no EINTR.
- Consult Utils.h (general utils), IOUtils.h (I/O), FileTools/*.h (file operations).
- Prefer FileDescriptor or safelyClose() over close().
- Mind security: see TempFileHandling.md; prefer safeReadFile() for reading files.
- Before modifying .cpp files, research corresponding header files.
- Be careful about object lifetimes. For example, if an object must live longer than the scope, consider storing it as a class field.
#### When working on a ServerKit server (including Core Controller)
- Working objects whose lifecycle are scoped by the request, are to be stored in the corresponding Request struct.
- When adding stuff to the Request struct, mind proper initialization and deinitialization. Update the initializeRequest and deinitializeRequest methods.
- When performing an async operation that's not cancellable, use refRequest to keep the Request object alive until the async callback is called. In the callback, check for req->ended().