1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
//! # html2pdf-api
//!
//! Thread-safe browser pool for headless Chrome automation with web framework integration.
//!
//! This crate provides a production-ready browser pool that manages headless Chrome/Chromium
//! instances with automatic lifecycle management, health monitoring, and graceful shutdown.
//!
//! ## Features
//!
//! - **Connection Pooling**: Reuses browser instances to avoid expensive startup costs
//! - **Health Monitoring**: Background thread continuously checks browser health
//! - **TTL Management**: Automatically retires old browsers and creates replacements
//! - **Race-Free Design**: Careful lock ordering prevents deadlocks
//! - **Graceful Shutdown**: Clean termination of all background tasks
//! - **RAII Pattern**: Automatic return of browsers to pool via Drop
//! - **Web Framework Integration**: Optional support for Actix-web, Rocket, and Axum
//!
//! ## Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────┐
//! │ Your Web Application │
//! │ (Actix-web / Rocket / Axum) │
//! └─────────────────┬───────────────────────────┘
//! │
//! ▼
//! ┌─────────────────────────────────────────────┐
//! │ BrowserPool │
//! │ ┌─────────────────────────────────────────┐ │
//! │ │ Available Pool (idle browsers) │ │
//! │ │ [Browser1] [Browser2] [Browser3] │ │
//! │ └─────────────────────────────────────────┘ │
//! │ ┌─────────────────────────────────────────┐ │
//! │ │ Active Tracking (in-use browsers) │ │
//! │ │ {id → Browser} │ │
//! │ └─────────────────────────────────────────┘ │
//! │ ┌─────────────────────────────────────────┐ │
//! │ │ Keep-Alive Thread │ │
//! │ │ (health checks + TTL enforcement) │ │
//! │ └─────────────────────────────────────────┘ │
//! └─────────────────────────────────────────────┘
//! │
//! ▼
//! ┌─────────────────────────────────────────────┐
//! │ Headless Chrome Browsers │
//! │ (managed by headless_chrome crate) │
//! └─────────────────────────────────────────────┘
//! ```
//!
//! ## Quick Start
//!
//! ```rust,ignore
//! use html2pdf_api::prelude::*;
//! use std::time::Duration;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Create pool with custom configuration
//! let pool = BrowserPool::builder()
//! .config(
//! BrowserPoolConfigBuilder::new()
//! .max_pool_size(5)
//! .warmup_count(3)
//! .browser_ttl(Duration::from_secs(3600))
//! .build()?
//! )
//! .factory(Box::new(ChromeBrowserFactory::with_defaults()))
//! .build()?;
//!
//! // Warmup the pool (recommended for production)
//! pool.warmup().await?;
//!
//! // Use browsers
//! {
//! let browser = pool.get()?;
//! let tab = browser.new_tab()?;
//! tab.navigate_to("https://example.com")?;
//! // ... generate PDF, take screenshot, etc.
//! } // Browser automatically returned to pool
//!
//! // Graceful shutdown
//! pool.shutdown_async().await;
//!
//! Ok(())
//! }
//! ```
//!
//! ## Environment Configuration
//!
//! When the `env-config` feature is enabled, you can initialize the pool
//! from environment variables (loaded from `app.env` file or system environment):
//!
//! ```rust,no_run
//! use html2pdf_api::init_browser_pool;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let pool = init_browser_pool().await?;
//! // pool is Arc<BrowserPool>, ready for web handlers
//! Ok(())
//! }
//! ```
//!
//! ### Environment File
//!
//! Create an `app.env` file in your project root (not `.env` for better
//! cross-platform visibility):
//!
//! ```text
//! BROWSER_POOL_SIZE=5
//! BROWSER_WARMUP_COUNT=3
//! BROWSER_TTL_SECONDS=3600
//! ```
//!
//! ### Environment Variables
//!
//! | Variable | Type | Default | Description |
//! |----------|------|---------|-------------|
//! | `BROWSER_POOL_SIZE` | usize | 5 | Maximum browsers in pool |
//! | `BROWSER_WARMUP_COUNT` | usize | 3 | Browsers to pre-create |
//! | `BROWSER_TTL_SECONDS` | u64 | 3600 | Browser lifetime (seconds) |
//! | `BROWSER_WARMUP_TIMEOUT_SECONDS` | u64 | 60 | Warmup timeout |
//! | `BROWSER_PING_INTERVAL_SECONDS` | u64 | 15 | Health check interval |
//! | `BROWSER_MAX_PING_FAILURES` | u32 | 3 | Failures before removal |
//! | `CHROME_PATH` | String | auto | Custom Chrome binary path |
//!
//! ## Feature Flags
//!
//! | Feature | Description |
//! |---------|-------------|
//! | `env-config` | Enable environment-based configuration |
//! | `actix-integration` | Actix-web framework integration |
//! | `rocket-integration` | Rocket framework integration |
//! | `axum-integration` | Axum framework integration |
//! | `test-utils` | Enable mock factory for testing |
//!
//! ## Web Framework Integration
//!
//! ### Actix-web
//!
//! ```rust,ignore
//! use actix_web::{web, App, HttpServer};
//! use html2pdf_api::prelude::*;
//!
//! async fn generate_pdf(
//! pool: web::Data<Arc<BrowserPool>>,
//! ) -> impl Responder {
//! let pool = pool.lock().unwrap();
//! let browser = pool.get()?;
//! // ... use browser
//! }
//! ```
//!
//! ### Rocket
//!
//! ```rust,ignore
//! use rocket::State;
//! use html2pdf_api::prelude::*;
//!
//! #[get("/pdf")]
//! async fn generate_pdf(
//! pool: &State<Arc<BrowserPool>>,
//! ) -> Result<Vec<u8>, Status> {
//! let pool = pool.lock().unwrap();
//! let browser = pool.get()?;
//! // ... use browser
//! }
//! ```
//!
//! ### Axum
//!
//! ```rust,ignore
//! use axum::{Extension, response::IntoResponse};
//! use html2pdf_api::prelude::*;
//!
//! async fn generate_pdf(
//! Extension(pool): Extension<Arc<BrowserPool>>,
//! ) -> impl IntoResponse {
//! let pool = pool.lock().unwrap();
//! let browser = pool.get()?;
//! // ... use browser
//! }
//! ```
//!
//! ## Error Handling
//!
//! All fallible operations return [`Result<T, BrowserPoolError>`](Result).
//! The error type provides context about what went wrong:
//!
//! ```rust,ignore
//! use html2pdf_api::{BrowserPool, BrowserPoolError};
//!
//! match pool.get() {
//! Ok(browser) => {
//! // Use browser
//! }
//! Err(BrowserPoolError::ShuttingDown) => {
//! // Pool is shutting down, handle gracefully
//! }
//! Err(BrowserPoolError::BrowserCreation(msg)) => {
//! // Chrome failed to launch
//! eprintln!("Browser creation failed: {}", msg);
//! }
//! Err(e) => {
//! // Other errors
//! eprintln!("Pool error: {}", e);
//! }
//! }
//! ```
//!
//! ## Testing
//!
//! For testing without Chrome, enable the `test-utils` feature and use
//! [`MockBrowserFactory`](factory::mock::MockBrowserFactory):
//!
//! ```rust,ignore
//! use html2pdf_api::factory::mock::MockBrowserFactory;
//!
//! let factory = MockBrowserFactory::always_fails("Test error");
//! let pool = BrowserPool::builder()
//! .factory(Box::new(factory))
//! .enable_keep_alive(false)
//! .build()?;
//! ```
// ============================================================================
// Modules
// ============================================================================
// Internal modules (not publicly exposed)
pub
// ============================================================================
// Feature-gated modules
// ============================================================================
/// Web framework integrations.
///
/// This module provides optional integrations with popular Rust web frameworks.
/// Enable the corresponding feature flag to use them:
///
/// - `actix-integration` for Actix-web
/// - `rocket-integration` for Rocket
/// - `axum-integration` for Axum
/// Core PDF generation service.
///
/// Provides framework-agnostic types and functions for PDF generation.
/// Used by the framework integrations.
// ============================================================================
// Re-exports (Public API)
// ============================================================================
// Core types
pub use ;
pub use ;
pub use ;
pub use BrowserHandle;
pub use ;
pub use PoolStats;
pub use Healthcheck;
// Feature-gated re-exports
pub use ;
pub use init_browser_pool;
// ============================================================================
// Convenience type aliases
// ============================================================================
/// Shared browser pool type for web frameworks.
///
/// This is the recommended type for sharing a pool across web handlers.
/// `BrowserPool` uses fine-grained internal locks, so no outer `Mutex` is needed.
///
/// # Example
///
/// ```rust,ignore
/// use html2pdf_api::SharedBrowserPool;
///
/// let pool: SharedBrowserPool = browser_pool.into_shared();
/// ```
pub type SharedBrowserPool = Arc;