├── .github └── workflows │ ├── benches.yml │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── .dockerignore ├── Cargo.toml ├── NOTES.md ├── README.md ├── basic.rs ├── basic_no_args.rs ├── logs │ ├── Darwin_v10cpu_benchmark_logs.txt │ ├── Darwin_v10cpu_benchmark_noargs_logs.txt │ ├── Linux_v4cpu_benchmark_logs.txt │ └── Linux_v4cpu_benchmark_noargs_logs.txt ├── logs_concurrent │ ├── Darwin_v10cpu_benchmark_logs.txt │ ├── Darwin_v10cpu_benchmark_noargs_logs.txt │ ├── Linux_v4cpu_benchmark_logs.txt │ └── Linux_v4cpu_benchmark_noargs_logs.txt ├── run.rs ├── run_concurrent.rs └── utils.rs ├── docker ├── Dockerfile ├── Dockerfile.brave ├── Dockerfile.headless_shell ├── Dockerfile.headless_shell_playwright ├── Dockerfile.lightpanda ├── Dockerfile.playwright └── Dockerfile.xvfb ├── headless_browser ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src │ └── main.rs └── tests │ └── cdp.rs ├── headless_browser_lib ├── Cargo.lock ├── Cargo.toml ├── LOAD_BALANCER_DOCS.md ├── README.md └── src │ ├── conf.rs │ ├── lib.rs │ ├── modify.rs │ ├── proxy.rs │ └── render_conf.rs ├── local.conf └── scripts ├── build-base.sh ├── build-headless-shell.sh ├── build-image.sh ├── build-unpatched.sh ├── build.sh ├── cleanup.sh ├── docker-entrypoint-xvfb.sh └── docker-entrypoint.sh /.github/workflows/benches.yml: -------------------------------------------------------------------------------- 1 | name: Rust Benches 2 | on: 3 | pull_request: 4 | branches: [main] 5 | 6 | env: 7 | CARGO_TERM_COLOR: always 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/cache@v4 15 | id: cache 16 | with: 17 | path: | 18 | ~/.cargo/bin/ 19 | ~/.cargo/registry/index/ 20 | ~/.cargo/registry/cache/ 21 | ~/.cargo/git/db/ 22 | target/ 23 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 24 | 25 | - name: Install Dependencies 26 | run: | 27 | sudo apt-get install -y --no-install-recommends curl unzip ca-certificates git openssh-client && sudo apt-get update 28 | 29 | - name: Install Rust 30 | run: | 31 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 32 | source $HOME/.cargo/env 33 | rustup update stable 34 | 35 | - name: Build 36 | run: cargo build --verbose --release 37 | 38 | - name: Download and Set Up Headless-Shell 39 | run: | 40 | sudo mkdir -p /out/latest/headless-shell 41 | sudo curl -SL https://storage.googleapis.com/chrome-for-testing-public/133.0.6943.126/linux64/chrome-headless-shell-linux64.zip -o chrome-headless-shell-linux64.zip 42 | sudo unzip chrome-headless-shell-linux64.zip -d /out/latest/headless-shell 43 | sudo chmod +x /out/latest/headless-shell/chrome-headless-shell-linux64/chrome-headless-shell 44 | sudo chmod -R 777 /out 45 | npx playwright install-deps chromium 46 | 47 | - name: Run Benches 48 | run: | 49 | cargo bench 50 | env: 51 | HEADLESS: true 52 | CHROME_PATH: /out/latest/headless-shell/chrome-headless-shell-linux64/chrome-headless-shell 53 | RUST_LOG: info,error,warn 54 | TEST_NO_ARGS: false 55 | SAMPLES: 10 56 | 57 | - name: Commit and Push Changes 58 | run: | 59 | git config --global user.name "GitHub Actions" 60 | git config --global user.email "actions@github.com" 61 | git add benches/logs/ 62 | git add benches/logs_concurrent/ 63 | git commit -m "Update benchmark logs" || echo "No changes to commit" 64 | git push 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/cache@v4 17 | id: cache 18 | with: 19 | path: | 20 | ~/.cargo/bin/ 21 | ~/.cargo/registry/index/ 22 | ~/.cargo/registry/cache/ 23 | ~/.cargo/git/db/ 24 | target/ 25 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 26 | 27 | - name: Install Dependencies 28 | run: | 29 | sudo apt-get install -y --no-install-recommends curl unzip ca-certificates git openssh-client && sudo apt-get update 30 | 31 | - name: Install Rust 32 | run: | 33 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 34 | source $HOME/.cargo/env 35 | rustup update stable 36 | 37 | - name: Build 38 | run: cargo build --verbose --release 39 | 40 | - name: Download and Set Up Headless-Shell 41 | run: | 42 | sudo mkdir -p /out/latest/headless-shell 43 | sudo curl -SL https://storage.googleapis.com/chrome-for-testing-public/133.0.6943.126/linux64/chrome-headless-shell-linux64.zip -o chrome-headless-shell-linux64.zip 44 | sudo unzip chrome-headless-shell-linux64.zip -d /out/latest/headless-shell 45 | sudo chmod +x /out/latest/headless-shell/chrome-headless-shell-linux64/chrome-headless-shell 46 | sudo chmod -R 777 /out 47 | npx playwright install-deps chromium 48 | 49 | - name: Run Tests 50 | run: | 51 | cargo test --package headless_browser --test cdp -- --nocapture 52 | env: 53 | HEADLESS: true 54 | CHROME_PATH: /out/latest/headless-shell/chrome-headless-shell-linux64/chrome-headless-shell 55 | RUST_LOG: info,error,warn 56 | TEST_NO_ARGS: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Added by cargo 4 | 5 | /target 6 | 7 | deploy.sh 8 | 9 | cloudbuild.yaml 10 | 11 | # chrome cache 12 | ~ 13 | 14 | .env 15 | 16 | docker-headless-shell 17 | chrome-headless-shell 18 | out 19 | lightpanda-x86_64-linux 20 | lightpanda-aarch64-macos -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "headless_browser", 4 | "headless_browser_lib", 5 | "benches" 6 | ] 7 | resolver = "2" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Spider 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # headless-browser 2 | 3 | The `headless-browser` crate offers a scalable solution for managing headless Chrome instances with integrated proxy and server support. This system is designed to optimize resource utilization and enhance performance for large-scale web automation tasks. 4 | 5 | ## Installation 6 | 7 | Make sure you have [Rust](https://www.rust-lang.org/learn/get-started) installed before proceeding. 8 | 9 | ```sh 10 | cargo install headless_browser 11 | ``` 12 | 13 | ## Usage 14 | 15 | 1. Launches the most recent browser instance, supporting remote proxy connections and a server for `json/version` rewrites to facilitate networking. 16 | 2. Allows manual and automatic spawning and termination of multiple headless instances, including error handling functionalities. 17 | 3. Provides access to Chrome DevTools Protocol (CDP) WebSocket connections and status, caching values for improved performance. 18 | 19 | By default, the instance binds Chrome to `0.0.0.0` when initialized via the API. 20 | 21 | Use the `REMOTE_ADDRESS` environment variable to specify the desired address for the Chrome instance, whether local or networked. 22 | 23 | The application passes load balancer health checks on port `6000`, providing the status of the Chrome container. 24 | 25 | To run Chrome on a load balancer, a companion application is required, which is a primary function of the server. 26 | 27 | The default port configuration includes `9223` for Chrome and `9222` for the TCP proxy, in response to `0.0.0.0` not being exposed in recent versions like `HeadlessChrome/131.0.6778.139` and newer. 28 | 29 | For web scraping, we suggest using our [Headless Shell Dockerfile](./docker/Dockerfile.headless_shell_playwright) or downloading the [chrome-headless-shell](https://storage.googleapis.com/chrome-for-testing-public/134.0.6998.23/linux64/chrome-headless-shell-linux64.zip). The headless-shell offers significantly faster performance compared to the standard headless mode. Additionally, our Docker image is remarkably compact for the chrome-headless-shell, thanks to a multi-stage build process that installs only the essential components, resulting in a size of just 300 MB compared to Playwright's traditional 1.2 GB default. 30 | 31 | --- 32 | 33 | ## API 34 | 35 | 1. POST: `fork` to start a new chrome instance or use `fork/$port` with the port to startup the instance ex: `curl --location --request POST 'http://localhost:6000/fork/9223'`. 36 | 2. POST: `shutdown/$PID` to shutdown the instance. ex: `curl --location --request POST 'http://localhost:6000/shutdown/77057'` 37 | 3. POST: `/json/version` get the json info of the chrome instance to connect to web sockets ex: `curl --location --request POST 'http://localhost:6000/json/version'`. 38 | 39 | ### Curl Examples 40 | 41 | `fork` 42 | 43 | ```sh 44 | curl --location --request POST 'http://localhost:6000/fork' 45 | ``` 46 | 47 | `shutdown` 48 | 49 | ```sh 50 | curl --location --request POST 'http://localhost:6000/shutdown' 51 | ``` 52 | 53 | `/json/version` 54 | 55 | ```sh 56 | curl --location --request GET 'http://localhost:6000/json/version' \ 57 | --header 'Content-Type: application/json' 58 | 59 | # example output 60 | { 61 | "Browser": "HeadlessChrome/131.0.6778.139", 62 | "Protocol-Version": "1.3", 63 | "User-Agent": "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/131.0.6778.139 Safari/537.36", 64 | "V8-Version": "13.1.201.16", 65 | "WebKit-Version": "537.36 (@c35bbcbd7c2775a12a3f320e05ac0022939b1a8a)", 66 | "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/43e14f5a-6877-4e2f-846e-ab5801f1b6fc" 67 | } 68 | ``` 69 | 70 | ## Args 71 | 72 | 1. The first arg is the chrome application location example linux `'/opt/google/chrome/chrome'`. 73 | 2. The second arg is the chrome address `127.0.0.1`. 74 | 3. The third arg you can pass in `init` to auto start chrome on `9222`. 75 | 76 | Example to start chrome (all params are optional): 77 | 78 | ```sh 79 | headless_browser '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' 127.0.0.1 init 80 | # Chrome PID: 87659 81 | # Chrome server at localhost:6000 82 | # DevTools listening on ws://127.0.0.1:9222/devtools/browser/c789f9e0-7f65-495d-baee-243eb454ea15 83 | ``` 84 | 85 | ### Docker 86 | 87 | You can build this image using the following: 88 | 89 | 1. Dockerfile (Alpine) 90 | 1. Dockerfile.playwright (Playwright Custom Chrome) 91 | 1. Dockerfile.headless_shell (Manual) 92 | 1. Dockerfile.brave 93 | 1. Dockerfile.headless_shell_playwright (Playwright Headless Shell) 94 | 1. Dockerfile.xvfb (Virtual Display) 95 | 1. Dockerfile.lightpanda 96 | 97 | You need to set the env variable passed in as an arg `HOSTNAME_OVERRIDE` to override the docker container and set it to `host.docker.internal`. 98 | 99 | #### Manual (WIP) 100 | 101 | Use the following to build with docker. 102 | Run the command `./build.sh` to build chrome on the machine with docker. 103 | The build scripts are originally from [docker-headless-shell](https://github.com/chromedp/docker-headless-shell). 104 | 105 | ### ENV Variables 106 | 107 | ```sh 108 | # the chrome path on the OS ex: CHROME_PATH=./chrome-headless-shell/mac_arm-132.0.6834.159/chrome-headless-shell-mac-arm64/chrome-headless-shell 109 | CHROME_PATH= 110 | # the remote address of the chrome intance 111 | REMOTE_ADDRESS= 112 | # use brave browser as default. Set the value to true. 113 | BRAVE_ENABLED= 114 | ``` 115 | 116 | ## Library 117 | 118 | You can use the [lib](https://docs.rs/headless_browser_lib/latest/headless_browser_lib/) with `cargo add headless_browser_lib` control the startup and shutdown manually. Below is an example of using the [spider_chrome](https://github.com/spider-rs/spider/tree/main/spider_chrome) project to run CDP commands concurrently fast. 119 | 120 | ```rust 121 | futures-util = "0.3" 122 | tokio = { version = "1", features = ["full"] } 123 | spider_chrome = "2" 124 | headless_browser_lib = "0.1" 125 | ``` 126 | 127 | ```rust 128 | /// spider_chrome is mapped to chromiumoxide since it was forked and kept the API the same. 129 | use chromiumoxide::{browser::Browser, error::CdpError}; 130 | use futures_util::stream::StreamExt; 131 | 132 | #[tokio::main] 133 | async fn main() -> Result<(), Box> { 134 | tokio::spawn(headless_browser_lib::run_main()); // spawn main server, proxy, and headless. 135 | tokio::time::sleep(std::time::Duration::from_millis(200)).await; // give a slight delay for now until we use a oneshot. 136 | let start = std::time::Instant::now(); 137 | 138 | let (browser, mut handler) = 139 | // port 6000 - main server entry for routing correctly across networks. 140 | Browser::connect_with_config("http://127.0.0.1:6000/json/version", Default::default()) 141 | .await?; 142 | 143 | let handle = tokio::task::spawn(async move { 144 | while let Some(h) = handler.next().await { 145 | if h.is_err() { 146 | break; 147 | } 148 | } 149 | }); 150 | 151 | let navigate_page = async |u: &str| -> Result { 152 | let page = browser.new_page(u).await?; 153 | let html = page.wait_for_navigation().await?.content().await?; 154 | Ok::(html) 155 | }; 156 | 157 | let (spider_html, example_html) = tokio::join!( 158 | navigate_page("https://spider.cloud"), 159 | navigate_page("https://example.com") 160 | ); 161 | 162 | browser.close().await?; 163 | let _ = handle.await; 164 | 165 | let spider_html = spider_html?; 166 | let example_html = example_html?; 167 | 168 | let elasped = start.elapsed(); 169 | 170 | browser.close().await?; 171 | let _ = handle.await; 172 | 173 | println!("===\n{}\n{}\n===", "spider.cloud", spider_html); 174 | println!("===\n{}\n{}\n===", "example.com", example_html); 175 | println!("Time took: {:?}", elasped); 176 | 177 | Ok(()) 178 | } 179 | ``` 180 | 181 | ## Testing and Benchmarks 182 | 183 | View the [benches](./benches/README.md) to see the performance between browsers for headless. 184 | 185 | ## Flags 186 | 187 | Use the flag `physical_gpu` to enable physical gpu rendering. 188 | 189 | ## License 190 | 191 | MIT 192 | -------------------------------------------------------------------------------- /benches/.dockerignore: -------------------------------------------------------------------------------- 1 | logs 2 | logs_concurrent -------------------------------------------------------------------------------- /benches/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "benches" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [dependencies] 8 | headless_browser_lib = { path = "../headless_browser_lib", features = ["testing"] } 9 | futures-util = "0.3" 10 | spider_chrome = "2" 11 | tokio = { version = "1", features = ["full"] } 12 | chrono = "0.4" 13 | sys-info = "0.9" 14 | 15 | [[bench]] 16 | name = "basic" 17 | path = "basic.rs" 18 | harness = false 19 | 20 | [[bench]] 21 | name = "basic_no_args" 22 | path = "basic_no_args.rs" 23 | harness = false -------------------------------------------------------------------------------- /benches/NOTES.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | Notes that can help with setting up the browser of choice. -------------------------------------------------------------------------------- /benches/README.md: -------------------------------------------------------------------------------- 1 | # headless-browser-benches 2 | 3 | Benches for the headless browsers. The benches may have multiple sections as command line args influence the performance. 4 | 5 | ## Tests 6 | 7 | View the benchmarks for each browser below. 8 | 9 | ### Google Chrome 10 | 11 | example with Google Chrome ( reliable and fast ). 12 | 13 | ```sh 14 | HEADLESS=true CHROME_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" cargo test --package headless_browser --test cdp -- --nocapture 15 | # NewPage(https://example.com): 172.977417ms 16 | # WaitForNavigationAndContent(https://example.com): 834.417µs 17 | # NewPage(https://spider.cloud): 510.849ms 18 | # WaitForNavigationAndContent(https://spider.cloud): 24.371167ms 19 | # Time took: 943.520375ms 20 | ``` 21 | 22 | ### Chrome Headless Shell 23 | 24 | example with Google Chrome headless-shell: (4x faster). 25 | 26 | ```sh 27 | HEADLESS=true CHROME_PATH=./chrome-headless-shell/chromium_headless_shell-1155/chrome-mac/headless_shell cargo test --package headless_browser --test cdp -- --nocapture 28 | # NewPage(https://example.com): 51.755125ms 29 | # WaitForNavigationAndContent(https://example.com): 840.084µs 30 | # NewPage(https://spider.cloud): 221.127334ms 31 | # WaitForNavigationAndContent(https://spider.cloud): 22.51475ms 32 | # Time took: 270.031125ms 33 | ``` 34 | 35 | ### Brave Browser 36 | 37 | example with brave(10x-20x slower). 38 | 39 | ```sh 40 | HEADLESS=true CHROME_PATH="/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" cargo test --package headless_browser --test cdp -- --nocapture 41 | # tracing_subscriber - init success 42 | # NewPage(https://example.com): 330.503792ms 43 | # WaitForNavigationAndContent(https://example.com): 3.170792ms 44 | # NewPage(https://spider.cloud): 572.666833ms 45 | # WaitForNavigationAndContent(https://spider.cloud): 20.741291ms 46 | # Time took: 5.972425416s 47 | ``` 48 | 49 | ## Running 50 | 51 | When you run the benches make sure to pass in the CHROME_PATH env. 52 | 53 | ```sh 54 | HEADLESS=true CHROME_PATH=./chrome-headless-shell/chromium_headless_shell-1155/chrome-mac/headless_shell cargo bench 55 | ``` 56 | 57 | ## Benchmarks 58 | 59 | View the [benchmarks](./logs/) to see the runs with the machine used and history of the args for the performance. 60 | The `Argless` has the headless instance launched with minimal args required to run. 61 | 62 | ### Mac 63 | 64 | Sync 10x 65 | 66 | * [Darwin_v10cpu](./logs/Darwin_v10cpu_benchmark_logs.txt) 67 | * [Darwin_v10cpu Argless](./logs/Darwin_v10cpu_benchmark_noargs_logs.txt) 68 | 69 | Concurrent 10x 70 | 71 | * [Darwin_v10cpu](./logs_concurrent/Darwin_v10cpu_benchmark_logs.txt) 72 | * [Darwin_v10cpu Argless](./logs_concurrent/Darwin_v10cpu_benchmark_noargs_logs.txt) 73 | 74 | ### Linux 75 | 76 | Sync 10x 77 | 78 | * [Linux_v4cpu](./logs/Linux_v4cpu_benchmark_logs.txt) 79 | * [Linux_v4cpu Argless](./logs/Linux_v4cpu_benchmark_noargs_logs.txt) 80 | 81 | Concurrent 10x 82 | 83 | * [Linux_v4cpu](./logs_concurrent/Linux_v4cpu_benchmark_logs.txt) 84 | * [Linux_v4cpu Argless](./logs_concurrent/Linux_v4cpu_benchmark_noargs_logs.txt) 85 | -------------------------------------------------------------------------------- /benches/basic.rs: -------------------------------------------------------------------------------- 1 | mod run; 2 | mod run_concurrent; 3 | mod utils; 4 | 5 | use std::env::set_var; 6 | 7 | const LOG_FILE_NAME: &str = "benchmark_logs.txt"; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | set_var("CHROME_INIT", "ignore"); // Ignore the auto start. 12 | set_var("HOSTNAME", "localhost"); 13 | let samples = std::env::var("SAMPLES") 14 | .unwrap_or("10".into()) 15 | .parse::() 16 | .unwrap_or(10); 17 | 18 | headless_browser_lib::fork(Some(*headless_browser_lib::conf::DEFAULT_PORT)); 19 | let task = tokio::spawn(headless_browser_lib::run_main()); 20 | tokio::time::sleep(std::time::Duration::from_millis(1000)).await; // Wait for the server to load. 21 | run::run(LOG_FILE_NAME, samples).await; 22 | run_concurrent::run(LOG_FILE_NAME, samples).await; 23 | headless_browser_lib::shutdown_instances().await; 24 | task.abort(); 25 | } 26 | -------------------------------------------------------------------------------- /benches/basic_no_args.rs: -------------------------------------------------------------------------------- 1 | mod run; 2 | mod run_concurrent; 3 | mod utils; 4 | 5 | use std::env::set_var; 6 | 7 | const LOG_FILE_NAME: &str = "benchmark_noargs_logs.txt"; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | set_var("CHROME_INIT", "ignore"); // Ignore the auto start. 12 | set_var("TEST_NO_ARGS", "true"); 13 | set_var("HOSTNAME", "localhost"); 14 | let samples = std::env::var("SAMPLES") 15 | .unwrap_or("10".into()) 16 | .parse::() 17 | .unwrap_or(10); 18 | 19 | headless_browser_lib::fork(Some(*headless_browser_lib::conf::DEFAULT_PORT)); 20 | let task = tokio::spawn(headless_browser_lib::run_main()); 21 | tokio::time::sleep(std::time::Duration::from_millis(1000)).await; // Wait for the server to load. 22 | run::run(LOG_FILE_NAME, samples).await; 23 | run_concurrent::run(LOG_FILE_NAME, samples).await; 24 | headless_browser_lib::shutdown_instances().await; 25 | task.abort(); 26 | } 27 | -------------------------------------------------------------------------------- /benches/logs/Darwin_v10cpu_benchmark_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: headless_shell 3 | CHROME_ARGS: (92)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 4 | MACHINE: Darwin/v10cpu 5 | DATE: 2025-02-25 09:49:12 6 | Average Duration: 154.52907ms 7 | 8 | - 10 SAMPLES 9 | CHROME_PATH: headless_shell 10 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 11 | MACHINE: Darwin/v10cpu 12 | DATE: 2025-02-25 23:33:27 13 | Total Duration: 1.609840333s 14 | Average Duration: 160.96892ms 15 | 16 | - 20 SAMPLES 17 | CHROME_PATH: headless_shell 18 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 19 | MACHINE: Darwin/v10cpu 20 | DATE: 2025-02-25 23:33:45 21 | Total Duration: 2.596999167s 22 | Average Duration: 129.831708ms 23 | 24 | - 50 SAMPLES 25 | CHROME_PATH: headless_shell 26 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 27 | MACHINE: Darwin/v10cpu 28 | DATE: 2025-02-25 23:34:05 29 | Total Duration: 6.547482333s 30 | Average Duration: 130.93284ms 31 | 32 | - 20 SAMPLES 33 | CHROME_PATH: headless_shell 34 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 35 | MACHINE: Darwin/v10cpu 36 | DATE: 2025-02-26 05:28:32 37 | Total Duration: 2.915269541s 38 | Average Duration: 145.745568ms 39 | 40 | - 20 SAMPLES 41 | CHROME_PATH: headless_shell 42 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 43 | MACHINE: Darwin/v10cpu 44 | DATE: 2025-02-26 05:33:18 45 | Total Duration: 2.969043375s 46 | Average Duration: 148.430283ms 47 | 48 | - 10 SAMPLES 49 | CHROME_PATH: headless_shell 50 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 51 | MACHINE: Darwin/v10cpu 52 | DATE: 2025-02-26 05:34:10 53 | Total Duration: 1.328313334s 54 | Average Duration: 132.799387ms 55 | 56 | - 10 SAMPLES 57 | CHROME_PATH: headless_shell 58 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 59 | MACHINE: Darwin/v10cpu 60 | DATE: 2025-02-27 21:54:40 61 | Total Duration: 1.451455792s 62 | Average Duration: 145.12042ms 63 | 64 | -------------------------------------------------------------------------------- /benches/logs/Darwin_v10cpu_benchmark_noargs_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: headless_shell 3 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 4 | MACHINE: Darwin/v10cpu 5 | DATE: 2025-02-25 09:49:33 6 | Average Duration: 170.837966ms 7 | 8 | - 10 SAMPLES 9 | CHROME_PATH: headless_shell 10 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 11 | MACHINE: Darwin/v10cpu 12 | DATE: 2025-02-25 12:37:16 13 | Total Duration: 1.45234475s 14 | Average Duration: 145.212333ms 15 | 16 | - 10 SAMPLES 17 | CHROME_PATH: headless_shell 18 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 19 | MACHINE: Darwin/v10cpu 20 | DATE: 2025-02-25 13:16:24 21 | Total Duration: 1.372718833s 22 | Average Duration: 137.257075ms 23 | 24 | - 5 SAMPLES 25 | CHROME_PATH: headless_shell 26 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 27 | MACHINE: Darwin/v10cpu 28 | DATE: 2025-02-25 13:29:55 29 | Total Duration: 834.222292ms 30 | Average Duration: 166.822649ms 31 | 32 | - 2 SAMPLES 33 | CHROME_PATH: headless_shell 34 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 35 | MACHINE: Darwin/v10cpu 36 | DATE: 2025-02-25 13:32:14 37 | Total Duration: 410.434625ms 38 | Average Duration: 205.164521ms 39 | 40 | - 4 SAMPLES 41 | CHROME_PATH: headless_shell 42 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 43 | MACHINE: Darwin/v10cpu 44 | DATE: 2025-02-25 13:32:57 45 | Total Duration: 655.835292ms 46 | Average Duration: 163.909187ms 47 | 48 | - 20 SAMPLES 49 | CHROME_PATH: headless_shell 50 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 51 | MACHINE: Darwin/v10cpu 52 | DATE: 2025-02-25 13:33:44 53 | Total Duration: 2.676222959s 54 | Average Duration: 133.7862ms 55 | 56 | - 15 SAMPLES 57 | CHROME_PATH: headless_shell 58 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 59 | MACHINE: Darwin/v10cpu 60 | DATE: 2025-02-25 13:35:43 61 | Total Duration: 2.397807791s 62 | Average Duration: 159.828138ms 63 | 64 | - 50 SAMPLES 65 | CHROME_PATH: headless_shell 66 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 67 | MACHINE: Darwin/v10cpu 68 | DATE: 2025-02-25 13:36:48 69 | Total Duration: 6.484034583s 70 | Average Duration: 129.662054ms 71 | 72 | - 50 SAMPLES 73 | CHROME_PATH: headless_shell 74 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 75 | MACHINE: Darwin/v10cpu 76 | DATE: 2025-02-25 13:39:26 77 | Total Duration: 6.713125291s 78 | Average Duration: 134.24218ms 79 | 80 | - 20 SAMPLES 81 | CHROME_PATH: headless_shell 82 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 83 | MACHINE: Darwin/v10cpu 84 | DATE: 2025-02-25 14:51:21 85 | Total Duration: 2.614171791s 86 | Average Duration: 130.690579ms 87 | 88 | - 20 SAMPLES 89 | CHROME_PATH: headless_shell 90 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 91 | MACHINE: Darwin/v10cpu 92 | DATE: 2025-02-25 14:58:31 93 | Total Duration: 2.55098625s 94 | Average Duration: 127.531591ms 95 | 96 | - 20 SAMPLES 97 | CHROME_PATH: headless_shell 98 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 99 | MACHINE: Darwin/v10cpu 100 | DATE: 2025-02-25 14:59:52 101 | Total Duration: 2.762563667s 102 | Average Duration: 138.107618ms 103 | 104 | - 20 SAMPLES 105 | CHROME_PATH: headless_shell 106 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 107 | MACHINE: Darwin/v10cpu 108 | DATE: 2025-02-25 15:02:15 109 | Total Duration: 2.666630334s 110 | Average Duration: 133.312329ms 111 | 112 | - 50 SAMPLES 113 | CHROME_PATH: headless_shell 114 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 115 | MACHINE: Darwin/v10cpu 116 | DATE: 2025-02-25 15:03:19 117 | Total Duration: 6.518802417s 118 | Average Duration: 130.358351ms 119 | 120 | - 50 SAMPLES 121 | CHROME_PATH: headless_shell 122 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 123 | MACHINE: Darwin/v10cpu 124 | DATE: 2025-02-25 15:04:39 125 | Total Duration: 6.622225458s 126 | Average Duration: 132.424491ms 127 | 128 | - 25 SAMPLES 129 | CHROME_PATH: headless_shell 130 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 131 | MACHINE: Darwin/v10cpu 132 | DATE: 2025-02-25 15:06:23 133 | Total Duration: 3.417184042s 134 | Average Duration: 136.668676ms 135 | 136 | - 15 SAMPLES 137 | CHROME_PATH: headless_shell 138 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 139 | MACHINE: Darwin/v10cpu 140 | DATE: 2025-02-25 15:07:04 141 | Total Duration: 2.123479708s 142 | Average Duration: 141.54543ms 143 | 144 | - 6 SAMPLES 145 | CHROME_PATH: headless_shell 146 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 147 | MACHINE: Darwin/v10cpu 148 | DATE: 2025-02-25 15:07:24 149 | Total Duration: 865.587958ms 150 | Average Duration: 144.236333ms 151 | 152 | - 10 SAMPLES 153 | CHROME_PATH: headless_shell 154 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 155 | MACHINE: Darwin/v10cpu 156 | DATE: 2025-02-25 23:33:30 157 | Total Duration: 1.406989291s 158 | Average Duration: 140.677729ms 159 | 160 | - 20 SAMPLES 161 | CHROME_PATH: headless_shell 162 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 163 | MACHINE: Darwin/v10cpu 164 | DATE: 2025-02-25 23:33:50 165 | Total Duration: 2.552595791s 166 | Average Duration: 127.609654ms 167 | 168 | - 50 SAMPLES 169 | CHROME_PATH: headless_shell 170 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 171 | MACHINE: Darwin/v10cpu 172 | DATE: 2025-02-25 23:34:17 173 | Total Duration: 6.259639875s 174 | Average Duration: 125.172952ms 175 | 176 | - 20 SAMPLES 177 | CHROME_PATH: headless_shell 178 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 179 | MACHINE: Darwin/v10cpu 180 | DATE: 2025-02-26 05:28:37 181 | Total Duration: 2.627886125s 182 | Average Duration: 131.371589ms 183 | 184 | - 20 SAMPLES 185 | CHROME_PATH: headless_shell 186 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 187 | MACHINE: Darwin/v10cpu 188 | DATE: 2025-02-26 05:33:23 189 | Total Duration: 2.608605958s 190 | Average Duration: 130.414008ms 191 | 192 | - 10 SAMPLES 193 | CHROME_PATH: headless_shell 194 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 195 | MACHINE: Darwin/v10cpu 196 | DATE: 2025-02-26 05:34:13 197 | Total Duration: 1.338665375s 198 | Average Duration: 133.848741ms 199 | 200 | - 10 SAMPLES 201 | CHROME_PATH: headless_shell 202 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 203 | MACHINE: Darwin/v10cpu 204 | DATE: 2025-02-27 21:54:43 205 | Total Duration: 1.592259375s 206 | Average Duration: 159.205304ms 207 | 208 | -------------------------------------------------------------------------------- /benches/logs/Linux_v4cpu_benchmark_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: chrome-headless-shell 3 | CHROME_ARGS: (92)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 4 | MACHINE: Linux/v4cpu 5 | DATE: 2025-02-25 15:24:44 6 | Average Duration: 233.486896ms 7 | 8 | -------------------------------------------------------------------------------- /benches/logs/Linux_v4cpu_benchmark_noargs_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: chrome-headless-shell 3 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 4 | MACHINE: Linux/v4cpu 5 | DATE: 2025-02-25 15:24:48 6 | Average Duration: 353.392512ms 7 | 8 | -------------------------------------------------------------------------------- /benches/logs_concurrent/Darwin_v10cpu_benchmark_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: headless_shell 3 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 4 | MACHINE: Darwin/v10cpu 5 | DATE: 2025-02-25 23:33:28 6 | Total Duration: 583.946ms 7 | Average Duration: 373.669783ms 8 | 9 | - 20 SAMPLES 10 | CHROME_PATH: headless_shell 11 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 12 | MACHINE: Darwin/v10cpu 13 | DATE: 2025-02-25 23:33:46 14 | Total Duration: 1.142564416s 15 | Average Duration: 698.08477ms 16 | 17 | - 50 SAMPLES 18 | CHROME_PATH: headless_shell 19 | CHROME_ARGS: (97)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 20 | MACHINE: Darwin/v10cpu 21 | DATE: 2025-02-25 23:34:09 22 | Total Duration: 4.158599583s 23 | Average Duration: 3.755785809s 24 | 25 | - 20 SAMPLES 26 | CHROME_PATH: headless_shell 27 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 28 | MACHINE: Darwin/v10cpu 29 | DATE: 2025-02-26 05:28:33 30 | Total Duration: 1.189235583s 31 | Average Duration: 753.532166ms 32 | 33 | - 20 SAMPLES 34 | CHROME_PATH: headless_shell 35 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 36 | MACHINE: Darwin/v10cpu 37 | DATE: 2025-02-26 05:33:19 38 | Total Duration: 1.187148708s 39 | Average Duration: 751.300475ms 40 | 41 | - 10 SAMPLES 42 | CHROME_PATH: headless_shell 43 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 44 | MACHINE: Darwin/v10cpu 45 | DATE: 2025-02-26 05:34:10 46 | Total Duration: 558.842458ms 47 | Average Duration: 356.873224ms 48 | 49 | - 10 SAMPLES 50 | CHROME_PATH: headless_shell 51 | CHROME_ARGS: (99)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle,--no-first-run,--no-sandbox,--disable-setuid-sandbox,--no-zygote,--hide-scrollbars,--user-data-dir=~/.config/google-chrome,--allow-running-insecure-content,--autoplay-policy=user-gesture-required,--ignore-certificate-errors,--no-default-browser-check,--disable-dev-shm-usage,--disable-threaded-scrolling,--disable-cookie-encryption,--disable-demo-mode,--disable-dinosaur-easter-egg,--disable-fetching-hints-at-navigation-start,--disable-site-isolation-trials,--disable-web-security,--disable-threaded-animation,--disable-sync,--disable-print-preview,--disable-search-engine-choice-screen,--disable-partial-raster,--disable-in-process-stack-traces,--use-angle=swiftshader,--disable-low-res-tiling,--disable-speech-api,--disable-oobe-chromevox-hint-timer-for-testing,--disable-smooth-scrolling,--disable-default-apps,--disable-prompt-on-repost,--disable-domain-reliability,--enable-dom-distiller,--enable-distillability-service,--disable-component-update,--disable-background-timer-throttling,--disable-breakpad,--disable-crash-reporter,--disable-software-rasterizer,--disable-asynchronous-spellchecking,--disable-extensions,--disable-html5-camera,--noerrdialogs,--disable-popup-blocking,--disable-hang-monitor,--disable-checker-imaging,--enable-surface-synchronization,--disable-image-animation-resync,--disable-client-side-phishing-detection,--disable-component-extensions-with-background-pages,--run-all-compositor-stages-before-draw,--disable-background-networking,--disable-renderer-backgrounding,--disable-field-trial-config,--disable-back-forward-cache,--disable-backgrounding-occluded-windows,--log-level=3,--enable-logging=stderr,--font-render-hinting=none,--block-new-web-contents,--no-subproc-heap-profiling,--no-pre-read-main-dll,--disable-stack-profiler,--disable-libassistant-logfile,--crash-on-hang-threads,--restore-last-session,--ip-protection-proxy-opt-out,--unsafely-disable-devtools-self-xss-warning,--enable-features=PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess,--metrics-recording-only,--use-mock-keychain,--force-color-profile=srgb,--disable-infobars,--mute-audio,--disable-datasaver-prompt,--no-service-autorun,--password-store=basic,--export-tagged-pdf,--no-pings,--rusty-png,--disable-histogram-customizer,--window-size=800,600,--disable-vulkan-fallback-to-gl-for-testing,--disable-vulkan-surface,--disable-webrtc,--disable-oopr-debug-crash-dump,--disable-pnacl-crash-throttling,--disable-renderer-accessibility,--renderer-process-limit=50,--disable-pushstate-throttle,--disable-blink-features=AutomationControlled,--disable-ipc-flooding-protection,--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate") 52 | MACHINE: Darwin/v10cpu 53 | DATE: 2025-02-27 21:54:41 54 | Total Duration: 643.615208ms 55 | Average Duration: 409.253508ms 56 | 57 | -------------------------------------------------------------------------------- /benches/logs_concurrent/Darwin_v10cpu_benchmark_noargs_logs.txt: -------------------------------------------------------------------------------- 1 | - 10 SAMPLES 2 | CHROME_PATH: headless_shell 3 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 4 | MACHINE: Darwin/v10cpu 5 | DATE: 2025-02-25 23:33:31 6 | Total Duration: 623.24175ms 7 | Average Duration: 387.092666ms 8 | 9 | - 20 SAMPLES 10 | CHROME_PATH: headless_shell 11 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 12 | MACHINE: Darwin/v10cpu 13 | DATE: 2025-02-25 23:33:51 14 | Total Duration: 1.150761333s 15 | Average Duration: 695.616285ms 16 | 17 | - 50 SAMPLES 18 | CHROME_PATH: headless_shell 19 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 20 | MACHINE: Darwin/v10cpu 21 | DATE: 2025-02-25 23:34:21 22 | Total Duration: 4.252429458s 23 | Average Duration: 3.912185341s 24 | 25 | - 20 SAMPLES 26 | CHROME_PATH: headless_shell 27 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 28 | MACHINE: Darwin/v10cpu 29 | DATE: 2025-02-26 05:28:38 30 | Total Duration: 1.195677s 31 | Average Duration: 761.019604ms 32 | 33 | - 20 SAMPLES 34 | CHROME_PATH: headless_shell 35 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 36 | MACHINE: Darwin/v10cpu 37 | DATE: 2025-02-26 05:33:24 38 | Total Duration: 1.208197125s 39 | Average Duration: 760.903531ms 40 | 41 | - 10 SAMPLES 42 | CHROME_PATH: headless_shell 43 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 44 | MACHINE: Darwin/v10cpu 45 | DATE: 2025-02-26 05:34:13 46 | Total Duration: 576.73775ms 47 | Average Duration: 368.845141ms 48 | 49 | - 10 SAMPLES 50 | CHROME_PATH: headless_shell 51 | CHROME_ARGS: (6)("--remote-debugging-address=0.0.0.0,--remote-debugging-port=9223,--headless,--disable-gpu,--disable-gpu-sandbox,--use-gl=angle") 52 | MACHINE: Darwin/v10cpu 53 | DATE: 2025-02-27 21:54:44 54 | Total Duration: 596.303208ms 55 | Average Duration: 361.702ms 56 | 57 | -------------------------------------------------------------------------------- /benches/logs_concurrent/Linux_v4cpu_benchmark_logs.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spider-rs/headless-browser/1b9c51bbd3009378c29289ba682c6cac3b96cc4a/benches/logs_concurrent/Linux_v4cpu_benchmark_logs.txt -------------------------------------------------------------------------------- /benches/logs_concurrent/Linux_v4cpu_benchmark_noargs_logs.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spider-rs/headless-browser/1b9c51bbd3009378c29289ba682c6cac3b96cc4a/benches/logs_concurrent/Linux_v4cpu_benchmark_noargs_logs.txt -------------------------------------------------------------------------------- /benches/run.rs: -------------------------------------------------------------------------------- 1 | use super::utils::{ensure_log_directory_exists, get_last_benchmark, navigate_extract_and_close}; 2 | use std::ops::Div; 3 | use std::{ 4 | env, 5 | fs::OpenOptions, 6 | io::{self, Write}, 7 | path::Path, 8 | time::{Duration, Instant}, 9 | }; 10 | 11 | const LOG_DIR: &str = "logs"; 12 | 13 | /// Run the benchmarks to the env BENCH_URL. 14 | pub async fn run(log_file_name: &str, samples: u32) { 15 | ensure_log_directory_exists(LOG_DIR).expect("Failed to create log directory"); 16 | let query = env::var("BENCH_URL").unwrap_or_else(|_| "http://spider.cloud".into()); 17 | let mut total_duration = Duration::new(0, 0); 18 | let current_time = Instant::now(); 19 | 20 | for i in 0..samples { 21 | println!("Running sample {} of {}", i + 1, samples); 22 | 23 | let start_time = Instant::now(); 24 | let result = navigate_extract_and_close(&query).await; 25 | let duration = start_time.elapsed(); 26 | 27 | if let Err(e) = result { 28 | eprintln!("Error running test {}: {:?}", i + 1, e); 29 | } else { 30 | println!("Sample {} took: {:?}", i + 1, duration); 31 | } 32 | 33 | total_duration += duration; 34 | } 35 | 36 | let average_duration = total_duration.div(samples); 37 | let total_time = current_time.elapsed(); 38 | 39 | println!( 40 | "Finished average time: {:?} - total time: {:?}", 41 | average_duration, total_time 42 | ); 43 | 44 | log_performance(total_time, average_duration, &query, log_file_name, samples) 45 | .expect("Failed to log performance"); 46 | } 47 | 48 | /// Log the performance to file. 49 | fn log_performance( 50 | total_duration: Duration, 51 | current_avg: Duration, 52 | query: &str, 53 | log_file_name: &str, 54 | samples: u32, 55 | ) -> io::Result<()> { 56 | let os_type = sys_info::os_type().unwrap_or_default(); 57 | let cpu_count = sys_info::cpu_num().unwrap_or_default().to_string(); 58 | let sanitized_os = os_type.replace(|c: char| !c.is_alphanumeric(), "_"); 59 | 60 | // Construct the log file path with the machine information 61 | let log_file_name = format!("{}_v{}cpu_{}", sanitized_os, cpu_count, log_file_name); 62 | let log_file_path = format!("{}/{}", LOG_DIR, log_file_name); 63 | 64 | if let Ok(mut log_file) = OpenOptions::new() 65 | .read(true) 66 | .write(true) 67 | .append(true) 68 | .create(true) 69 | .open(log_file_path) 70 | { 71 | let chrome_args = if env::var("TEST_NO_ARGS").unwrap_or_default() == "true" { 72 | format!( 73 | "({})({:?})", 74 | headless_browser_lib::conf::CHROME_ARGS_TEST.len(), 75 | headless_browser_lib::conf::CHROME_ARGS_TEST.join(",") 76 | ) 77 | } else { 78 | format!( 79 | "({})({:?})", 80 | headless_browser_lib::conf::CHROME_ARGS.len(), 81 | headless_browser_lib::conf::CHROME_ARGS.join(",") 82 | ) 83 | }; 84 | 85 | let chrome_path = headless_browser_lib::conf::CHROME_PATH 86 | .trim_end_matches('/') 87 | .to_string(); 88 | let chrome_path = Path::new(&chrome_path) 89 | .file_name() 90 | .and_then(|s| s.to_str()) 91 | .unwrap_or_default(); 92 | 93 | let last_benchmark = get_last_benchmark(&log_file)?; 94 | 95 | if let Some(last_avg) = last_benchmark { 96 | match current_avg.cmp(&last_avg) { 97 | std::cmp::Ordering::Greater => { 98 | println!("Performance degraded. Previous average: {:?}", last_avg) 99 | } 100 | std::cmp::Ordering::Less => { 101 | println!("Performance improved. Previous average: {:?}", last_avg) 102 | } 103 | std::cmp::Ordering::Equal => println!("Performance unchanged."), 104 | } 105 | } 106 | 107 | writeln!( 108 | log_file, 109 | "<{query}> - {samples} SAMPLES\nCHROME_PATH: {}\nCHROME_ARGS: {}\nMACHINE: {}\nDATE: {}\nTotal Duration: {:?}\nAverage Duration: {:?}\n", 110 | chrome_path, 111 | chrome_args, 112 | format!("{}/v{}cpu", os_type, cpu_count), 113 | chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), 114 | total_duration, 115 | current_avg 116 | )?; 117 | } 118 | Ok(()) 119 | } 120 | -------------------------------------------------------------------------------- /benches/run_concurrent.rs: -------------------------------------------------------------------------------- 1 | use super::utils::{ensure_log_directory_exists, get_last_benchmark, navigate_extract_and_close}; 2 | use std::ops::Div; 3 | use std::sync::Arc; 4 | use std::{ 5 | env, 6 | fs::OpenOptions, 7 | io::{self, Write}, 8 | path::Path, 9 | time::{Duration, Instant}, 10 | }; 11 | use tokio::task::JoinSet; 12 | 13 | const LOG_DIR: &str = "logs_concurrent"; 14 | 15 | /// Run the benchmarks concurrently to the env BENCH_URL. 16 | pub async fn run(log_file_name: &str, samples: u32) { 17 | ensure_log_directory_exists(LOG_DIR).expect("Failed to create log directory"); 18 | let query = env::var("BENCH_URL").unwrap_or_else(|_| "http://spider.cloud".into()); 19 | let q1 = query.clone(); 20 | let mut total_duration = Duration::new(0, 0); 21 | let mut set = JoinSet::new(); 22 | let query = Arc::new(query); 23 | let current_time = Instant::now(); 24 | 25 | for i in 0..samples { 26 | println!("Running sample {} of {}", i + 1, samples); 27 | let query = query.clone(); 28 | 29 | set.spawn(async move { 30 | let start_time = Instant::now(); 31 | let result = navigate_extract_and_close(&query).await; 32 | let duration = start_time.elapsed(); 33 | if let Err(e) = result { 34 | eprintln!("Error running concurrent test {}: {:?}", i + 1, e); 35 | } else { 36 | println!("Sample concurrent {} took: {:?}", i + 1, duration); 37 | } 38 | duration 39 | }); 40 | } 41 | 42 | while let Some(res) = set.join_next().await { 43 | total_duration += res.unwrap_or_default(); 44 | } 45 | 46 | let average_duration = total_duration.div(samples); 47 | let total_time = current_time.elapsed(); 48 | 49 | println!( 50 | "Finished concurrent average time: {:?} - total time: {:?}", 51 | average_duration, total_time 52 | ); 53 | 54 | log_performance(total_time, average_duration, &q1, log_file_name, samples) 55 | .expect("Failed to log performance"); 56 | } 57 | 58 | /// Log the performance to file. 59 | fn log_performance( 60 | total_duration: Duration, 61 | current_avg: Duration, 62 | query: &str, 63 | log_file_name: &str, 64 | samples: u32, 65 | ) -> io::Result<()> { 66 | let os_type = sys_info::os_type().unwrap_or_default(); 67 | let cpu_count = sys_info::cpu_num().unwrap_or_default().to_string(); 68 | let sanitized_os = os_type.replace(|c: char| !c.is_alphanumeric(), "_"); 69 | 70 | // Construct the log file path with the machine information 71 | let log_file_name = format!("{}_v{}cpu_{}", sanitized_os, cpu_count, log_file_name); 72 | let log_file_path = format!("{}/{}", LOG_DIR, log_file_name); 73 | 74 | if let Ok(mut log_file) = OpenOptions::new() 75 | .read(true) 76 | .write(true) 77 | .append(true) 78 | .create(true) 79 | .open(log_file_path) 80 | { 81 | let chrome_args = if env::var("TEST_NO_ARGS").unwrap_or_default() == "true" { 82 | format!( 83 | "({})({:?})", 84 | headless_browser_lib::conf::CHROME_ARGS_TEST.len(), 85 | headless_browser_lib::conf::CHROME_ARGS_TEST.join(",") 86 | ) 87 | } else { 88 | format!( 89 | "({})({:?})", 90 | headless_browser_lib::conf::CHROME_ARGS.len(), 91 | headless_browser_lib::conf::CHROME_ARGS.join(",") 92 | ) 93 | }; 94 | 95 | let chrome_path = headless_browser_lib::conf::CHROME_PATH 96 | .trim_end_matches('/') 97 | .to_string(); 98 | let chrome_path = Path::new(&chrome_path) 99 | .file_name() 100 | .and_then(|s| s.to_str()) 101 | .unwrap_or_default(); 102 | 103 | let last_benchmark = get_last_benchmark(&log_file)?; 104 | 105 | if let Some(last_avg) = last_benchmark { 106 | match current_avg.cmp(&last_avg) { 107 | std::cmp::Ordering::Greater => { 108 | println!("Performance degraded. Previous average: {:?}", last_avg) 109 | } 110 | std::cmp::Ordering::Less => { 111 | println!("Performance improved. Previous average: {:?}", last_avg) 112 | } 113 | std::cmp::Ordering::Equal => println!("Performance unchanged."), 114 | } 115 | } 116 | 117 | writeln!( 118 | log_file, 119 | "<{query}> - {samples} SAMPLES\nCHROME_PATH: {}\nCHROME_ARGS: {}\nMACHINE: {}\nDATE: {}\nTotal Duration: {:?}\nAverage Duration: {:?}\n", 120 | chrome_path, 121 | chrome_args, 122 | format!("{}/v{}cpu", os_type, cpu_count), 123 | chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), 124 | total_duration, 125 | current_avg 126 | )?; 127 | } 128 | Ok(()) 129 | } 130 | -------------------------------------------------------------------------------- /benches/utils.rs: -------------------------------------------------------------------------------- 1 | use chromiumoxide::browser::Browser; 2 | use futures_util::stream::StreamExt; 3 | use std::{ 4 | fs::{self, File}, 5 | io::{self, BufRead}, 6 | path::Path, 7 | time::Duration, 8 | }; 9 | 10 | /// Ensure the dir always exist. 11 | pub fn ensure_log_directory_exists(dir: &str) -> io::Result<()> { 12 | if !Path::new(dir).exists() { 13 | fs::create_dir_all(dir)?; 14 | } 15 | Ok(()) 16 | } 17 | 18 | /// Get the last benchmark results duration. 19 | pub fn get_last_benchmark(log_file: &File) -> io::Result> { 20 | let mut lines = io::BufReader::new(log_file).lines(); 21 | let mut last_line = None; 22 | 23 | while let Some(line) = lines.next() { 24 | let next_line = line?; 25 | if !next_line.is_empty() { 26 | last_line = Some(next_line); 27 | } 28 | } 29 | 30 | if let Some(last_line) = last_line { 31 | if let Some(duration_str) = last_line.split(',').next() { 32 | if let Some(duration_value) = duration_str.split(':').nth(1) { 33 | return Ok(Some(parse_duration(duration_value.trim())?)); 34 | } 35 | } 36 | } 37 | Ok(None) 38 | } 39 | 40 | /// Parse the duration without the ms. 41 | pub fn parse_duration(s: &str) -> io::Result { 42 | if let Some(stripped) = s.strip_suffix("ms") { 43 | stripped 44 | .parse::() 45 | .map(|millis| Duration::from_millis(millis as u64)) 46 | .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid duration format")) 47 | } else { 48 | Err(io::Error::new( 49 | io::ErrorKind::InvalidData, 50 | "Invalid duration format", 51 | )) 52 | } 53 | } 54 | 55 | /// Navigate, get the HTML, and close the page. 56 | pub async fn navigate_extract_and_close(u: &str) -> Result<(), Box> { 57 | let (browser, mut handler) = 58 | Browser::connect_with_config("http://127.0.0.1:6000/json/version", Default::default()) 59 | .await?; 60 | 61 | let handle = tokio::task::spawn(async move { 62 | while let Some(h) = handler.next().await { 63 | if h.is_err() { 64 | break; 65 | } 66 | } 67 | }); 68 | 69 | let page = browser.new_page(u).await?; 70 | page.wait_for_navigation().await?.content().await?; 71 | handle.abort(); // Abort the handle to drop the connection. 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:alpine3.21 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | RUN apk upgrade --update-cache --available && \ 6 | apk add --no-cache gcc make g++ cmake musl-dev perl libressl-dev 7 | 8 | COPY ../headless_browser/ ./headless_browser 9 | COPY ../headless_browser_lib/ ./headless_browser_lib 10 | COPY ../benches/ ./benches 11 | COPY ../Cargo.* . 12 | 13 | RUN rustup update stable 14 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 15 | 16 | FROM alpine:3.21 17 | 18 | # Installs latest Chromium package. 19 | RUN apk upgrade --no-cache --available \ 20 | && apk add --no-cache \ 21 | chromium-swiftshader \ 22 | ttf-freefont \ 23 | font-noto-emoji \ 24 | && apk add --no-cache \ 25 | --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ 26 | font-wqy-zenhei 27 | 28 | COPY ../local.conf /etc/fonts/local.conf 29 | 30 | # Add Chrome as a user 31 | RUN mkdir -p /usr/src/app \ 32 | && adduser -D chrome \ 33 | && chown -R chrome:chrome /usr/src/app 34 | # Run Chrome as non-privileged 35 | USER chrome 36 | WORKDIR /usr/src/app 37 | 38 | ENV CHROME_BIN=/usr/bin/chromium-browser \ 39 | CHROME_PATH=/usr/lib/chromium/ 40 | 41 | EXPOSE 9222 6000 42 | 43 | USER root 44 | 45 | COPY --from=rustbuilder /usr/local/cargo/bin/headless_browser /usr/local/bin/headless_browser 46 | COPY ../scripts/docker-entrypoint.sh / 47 | 48 | RUN apk add --no-cache tini curl sudo nss dbus freetype harfbuzz ca-certificates libxcomposite libxrandr \ 49 | libxdamage libxext libxshmfence mesa-gl udev 50 | 51 | RUN chmod +x /docker-entrypoint.sh 52 | 53 | USER chrome 54 | 55 | ENV REMOTE_ADDRESS=0.0.0.0 56 | ENV LAUNCH=init 57 | ENV DEFAULT_PORT=9223 58 | ENV DEFAULT_PORT_SERVER=6000 59 | # ENV HOSTNAME_OVERRIDE=127.0.0.1 60 | 61 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 62 | -------------------------------------------------------------------------------- /docker/Dockerfile.brave: -------------------------------------------------------------------------------- 1 | FROM ubuntu:25.04 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | # Get Ubuntu packages 6 | RUN apt-get update && apt-get install -y \ 7 | build-essential \ 8 | curl 9 | 10 | COPY ../headless_browser/ ./headless_browser 11 | COPY ../headless_browser_lib/ ./headless_browser_lib 12 | COPY ../benches/ ./benches 13 | COPY ../Cargo.* . 14 | 15 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 16 | ENV PATH="/root/.cargo/bin:${PATH}" 17 | RUN rustup update stable 18 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 19 | 20 | FROM ubuntu:25.04 21 | 22 | ARG VERSION=latest 23 | 24 | COPY ../local.conf /etc/fonts/local.conf 25 | 26 | RUN apt-get update -y 27 | RUN apt-get install -y apt-utils software-properties-common apt-transport-https 28 | RUN apt-get upgrade -y 29 | 30 | RUN apt-get update && apt-get install -y \ 31 | build-essential \ 32 | tini curl sudo 33 | 34 | RUN curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg 35 | RUN echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list 36 | RUN apt-get update -y 37 | RUN apt-get install -y brave-browser 38 | 39 | # Add Chrome as a user 40 | RUN mkdir -p /usr/src/app \ 41 | && useradd -m chrome \ 42 | && chown -R chrome:chrome /usr/src/app 43 | 44 | EXPOSE 9222 6000 9223 45 | 46 | USER root 47 | 48 | COPY --from=rustbuilder /root/.cargo/bin/headless_browser /usr/local/bin/headless_browser 49 | COPY ../scripts/docker-entrypoint.sh / 50 | 51 | RUN chmod +x /docker-entrypoint.sh 52 | 53 | ENV REMOTE_ADDRESS=0.0.0.0 54 | ENV LAUNCH=init 55 | ENV DEFAULT_PORT=9223 56 | ENV DEFAULT_PORT_SERVER=6000 57 | ENV DEFAULT_LAUNCH_NAME=brave-browser 58 | 59 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 60 | -------------------------------------------------------------------------------- /docker/Dockerfile.headless_shell: -------------------------------------------------------------------------------- 1 | FROM rust:alpine3.21 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | RUN apk upgrade --update-cache --available && \ 6 | apk add --no-cache gcc make g++ cmake musl-dev perl libressl-dev 7 | 8 | 9 | COPY ../headless_browser/ ./headless_browser 10 | COPY ../headless_browser_lib/ ./headless_browser_lib 11 | COPY ../benches/ ./benches 12 | COPY ../Cargo.* . 13 | 14 | RUN rustup update stable 15 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 16 | 17 | FROM alpine:3.21 18 | 19 | ARG VERSION=latest 20 | 21 | COPY ../out/$VERSION/headless-shell/ /headless-shell/ 22 | COPY ../local.conf /etc/fonts/local.conf 23 | 24 | # Add Chrome as a user 25 | RUN mkdir -p /usr/src/app \ 26 | && adduser -D chrome \ 27 | && chown -R chrome:chrome /usr/src/app 28 | # Run Chrome as non-privileged 29 | USER chrome 30 | WORKDIR /usr/src/app 31 | 32 | ENV CHROME_BIN=/usr/bin/chromium-browser \ 33 | CHROME_PATH=/headless-shell:$PATH 34 | 35 | EXPOSE 9222 6000 9223 36 | 37 | USER root 38 | 39 | COPY --from=rustbuilder /usr/local/cargo/bin/headless_browser /usr/local/bin/headless_browser 40 | COPY ../scripts/docker-entrypoint.sh / 41 | 42 | RUN apk add --no-cache tini curl sudo nss dbus freetype harfbuzz ca-certificates libxcomposite libxrandr \ 43 | libxdamage libxext libxshmfence mesa-gl udev gcompat at-spi2-core gpm xkeyboard-config libxkbcommon libxrandr alsa-lib 44 | 45 | RUN chmod +x /docker-entrypoint.sh 46 | 47 | USER chrome 48 | 49 | ENV REMOTE_ADDRESS=0.0.0.0 50 | ENV LAUNCH=init 51 | ENV DEFAULT_PORT=9223 52 | ENV DEFAULT_PORT_SERVER=6000 53 | # ENV HOSTNAME_OVERRIDE=127.0.0.1 54 | ENV DEFAULT_LAUNCH_NAME=/headless-shell/headless-shell 55 | 56 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 57 | -------------------------------------------------------------------------------- /docker/Dockerfile.headless_shell_playwright: -------------------------------------------------------------------------------- 1 | FROM ubuntu:25.04 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | # Get Ubuntu packages 6 | RUN apt-get update && apt-get install -y \ 7 | build-essential \ 8 | bash curl \ 9 | && apt-get update 10 | 11 | COPY ../headless_browser/ ./headless_browser 12 | COPY ../headless_browser_lib/ ./headless_browser_lib 13 | COPY ../benches/ ./benches 14 | COPY ../Cargo.* . 15 | 16 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 17 | ENV PATH="/root/.cargo/bin:${PATH}" 18 | RUN rustup update stable 19 | 20 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 21 | 22 | FROM ubuntu:25.04 AS chromebuilder 23 | 24 | ARG VERSION=latest 25 | 26 | COPY ../scripts/build-unpatched.sh . 27 | COPY ../local.conf /etc/fonts/local.conf 28 | 29 | RUN apt-get update && apt-get install -y \ 30 | build-essential \ 31 | tini curl npm 32 | 33 | RUN ./build-unpatched.sh 34 | 35 | FROM ubuntu:25.04 36 | 37 | ARG VERSION=latest 38 | 39 | COPY ../local.conf /etc/fonts/local.conf 40 | 41 | # https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/nativeDeps.ts#L37 42 | # https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/chameleon/updatable/apt-clone/raspberrypi/var/lib/apt-clone/installed.pkgs;l=472?q=libwebp6&sq= 43 | RUN apt-get update && apt-get install --no-install-recommends -y \ 44 | libasound2t64 tini curl ca-certificates libfreetype6 \ 45 | libnspr4 libnss3 libexpat1 libgbm1 libfontconfig1 glib2.0 libatk1.0-0 \ 46 | fonts-liberation fonts-noto-color-emoji libatspi2.0-0 \ 47 | libc6 libcairo2 libcups2 libdbus-1-3 \ 48 | libgcc1 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libpango-1.0-0 \ 49 | libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libevdev2 \ 50 | libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ 51 | fonts-liberation libatk-bridge2.0-0 libdrm2 libjpeg-turbo8 fonts-unifont libwebpdemux2 \ 52 | fonts-ipafont-gothic xfonts-cyrillic xfonts-scalable libxml2 libepoxy0 bzip2 libatomic1 libgl1 libgles2 libopengl0 53 | 54 | # Add Chrome as a user 55 | RUN mkdir -p /usr/src/app \ 56 | && useradd -m chrome \ 57 | && chown -R chrome:chrome /usr/src/app 58 | 59 | EXPOSE 9222 6000 9223 60 | 61 | USER root 62 | 63 | COPY --from=rustbuilder /root/.cargo/bin/headless_browser /usr/local/bin/headless_browser 64 | COPY --from=chromebuilder /out/latest/headless-shell /out/latest/headless-shell/ 65 | COPY ../scripts/docker-entrypoint.sh / 66 | 67 | RUN chmod +x /docker-entrypoint.sh 68 | 69 | ENV REMOTE_ADDRESS=0.0.0.0 70 | ENV LAUNCH=init 71 | ENV DEFAULT_PORT=9223 72 | ENV DEFAULT_PORT_SERVER=6000 73 | ENV DEFAULT_LAUNCH_NAME=/out/latest/headless-shell/headless-shell 74 | # ENV HOSTNAME_OVERRIDE=127.0.0.1 75 | 76 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 77 | -------------------------------------------------------------------------------- /docker/Dockerfile.lightpanda: -------------------------------------------------------------------------------- 1 | FROM ubuntu:25.04 AS pandabuilder 2 | 3 | ARG ZIG=0.13.0 4 | ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U 5 | ARG OS=linux 6 | ARG ARCH=x86_64 7 | ARG V8=11.1.134 8 | ARG ZIG_V8=v0.1.11 9 | 10 | RUN apt-get update -yq && \ 11 | apt-get install -yq xz-utils \ 12 | python3 ca-certificates git \ 13 | pkg-config libglib2.0-dev \ 14 | gperf libexpat1-dev \ 15 | cmake clang \ 16 | curl git 17 | 18 | # install minisig 19 | RUN curl -L -O https://github.com/jedisct1/minisign/releases/download/0.11/minisign-0.11-linux.tar.gz && \ 20 | tar xvzf minisign-0.11-linux.tar.gz 21 | 22 | # install zig 23 | RUN curl -O https://ziglang.org/download/${ZIG}/zig-linux-x86_64-${ZIG}.tar.xz && \ 24 | curl -O https://ziglang.org/download/${ZIG}/zig-linux-x86_64-${ZIG}.tar.xz.minisig 25 | 26 | RUN minisign-linux/x86_64/minisign -Vm zig-linux-x86_64-${ZIG}.tar.xz -P ${ZIG_MINISIG} 27 | 28 | # clean minisg 29 | RUN rm -fr minisign-0.11-linux.tar.gz minisign-linux 30 | 31 | # install zig 32 | RUN tar xvf zig-linux-x86_64-${ZIG}.tar.xz && \ 33 | mv zig-linux-x86_64-${ZIG} /usr/local/lib && \ 34 | ln -s /usr/local/lib/zig-linux-x86_64-${ZIG}/zig /usr/local/bin/zig 35 | 36 | # clean up zig install 37 | RUN rm -fr zig-linux-x86_64-${ZIG}.tar.xz zig-linux-x86_64-${ZIG}.tar.xz.minisig 38 | 39 | # force use of http instead of ssh with github 40 | RUN cat < /root/.gitconfig 41 | [url "https://github.com/"] 42 | insteadOf="git@github.com:" 43 | EOF 44 | 45 | # clone lightpanda 46 | RUN git clone git@github.com:lightpanda-io/browser.git 47 | 48 | WORKDIR /browser 49 | 50 | # install deps 51 | RUN git submodule init && \ 52 | git submodule update --recursive 53 | 54 | RUN cd vendor/zig-js-runtime && \ 55 | git submodule init && \ 56 | git submodule update --recursive 57 | 58 | RUN make install-libiconv && \ 59 | make install-netsurf && \ 60 | make install-mimalloc 61 | 62 | # download and install v8 63 | RUN curl -L -o libc_v8.a https://github.com/lightpanda-io/zig-v8-fork/releases/download/${ZIG_V8}/libc_v8_${V8}_${OS}_${ARCH}.a && \ 64 | mkdir -p vendor/zig-js-runtime/vendor/v8/${ARCH}-${OS}/release && \ 65 | mv libc_v8.a vendor/zig-js-runtime/vendor/v8/${ARCH}-${OS}/release/libc_v8.a 66 | 67 | # build release 68 | RUN make build 69 | 70 | FROM ubuntu:25.04 AS rustbuilder 71 | 72 | WORKDIR /app 73 | 74 | # Get Ubuntu packages 75 | RUN apt-get update && apt-get install -y \ 76 | build-essential \ 77 | curl 78 | 79 | COPY ../headless_browser/ ./headless_browser 80 | COPY ../headless_browser_lib/ ./headless_browser_lib 81 | COPY ../benches/ ./benches 82 | COPY ../Cargo.* . 83 | 84 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 85 | ENV PATH="/root/.cargo/bin:${PATH}" 86 | RUN rustup update stable 87 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 88 | 89 | FROM ubuntu:25.04 90 | 91 | ARG VERSION=latest 92 | 93 | COPY local.conf /etc/fonts/local.conf 94 | 95 | RUN apt-get update && apt-get install -y \ 96 | build-essential \ 97 | tini curl sudo 98 | 99 | # Add Chrome as a user 100 | RUN mkdir -p /usr/src/app \ 101 | && useradd -m chrome \ 102 | && chown -R chrome:chrome /usr/src/app 103 | 104 | EXPOSE 9222 6000 9223 105 | 106 | # USER root 107 | 108 | COPY --from=rustbuilder /root/.cargo/bin/headless_browser /usr/local/bin/headless_browser 109 | COPY --from=pandabuilder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 110 | COPY --from=pandabuilder /browser/zig-out/bin/lightpanda /bin/lightpanda 111 | COPY ../scripts/docker-entrypoint.sh / 112 | 113 | RUN chmod +x /docker-entrypoint.sh 114 | 115 | ENV REMOTE_ADDRESS=0.0.0.0 116 | ENV LAUNCH=init 117 | ENV DEFAULT_PORT=9223 118 | ENV DEFAULT_PORT_SERVER=6000 119 | # ENV HOSTNAME_OVERRIDE=127.0.0.1 120 | ENV DEFAULT_LAUNCH_NAME=/bin/lightpanda 121 | 122 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 123 | -------------------------------------------------------------------------------- /docker/Dockerfile.playwright: -------------------------------------------------------------------------------- 1 | FROM rust:alpine3.21 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | RUN apk upgrade --update-cache --available && \ 6 | apk add gcc cmake make g++ 7 | 8 | COPY ../headless_browser/ ./headless_browser 9 | COPY ../headless_browser_lib/ ./headless_browser_lib 10 | COPY ../benches/ ./benches 11 | COPY ../Cargo.* . 12 | 13 | RUN rustup update stable 14 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 15 | 16 | FROM alpine:3.21 17 | 18 | # Installs latest Chromium package. 19 | RUN apk upgrade --no-cache --available \ 20 | && apk add --no-cache \ 21 | chromium-swiftshader \ 22 | ttf-freefont \ 23 | font-noto-emoji \ 24 | && apk add --no-cache \ 25 | --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ 26 | font-wqy-zenhei 27 | 28 | COPY ../local.conf /etc/fonts/local.conf 29 | 30 | # Add Chrome as a user 31 | RUN mkdir -p /usr/src/app \ 32 | && adduser -D chrome \ 33 | && chown -R chrome:chrome /usr/src/app 34 | 35 | EXPOSE 9222 6000 36 | 37 | USER root 38 | 39 | RUN apk add --no-cache tini curl sudo nodejs npm yarn \ 40 | && npm install -g playwright \ 41 | && npx playwright install chromium 42 | 43 | # FROM zenika/alpine-chrome 44 | 45 | # EXPOSE 9222 6000 46 | 47 | # USER root 48 | 49 | # RUN apk add --no-cache tini curl sudo nodejs npm yarn \ 50 | # && npm install -g playwright \ 51 | # && npx playwright install chromium 52 | 53 | COPY --from=rustbuilder /usr/local/cargo/bin/headless_browser /usr/local/bin/headless_browser 54 | 55 | RUN PLAYWRIGHT_CHROMIUM_PATH=$(find /root/.cache/ms-playwright | grep "chrome-linux/chrome$") && \ 56 | cp $PLAYWRIGHT_CHROMIUM_PATH /chromium_path 57 | 58 | COPY ../scripts/docker-entrypoint.sh / 59 | 60 | RUN apk add --no-cache tini curl sudo 61 | RUN chmod +x /docker-entrypoint.sh 62 | 63 | USER chrome 64 | 65 | ENV REMOTE_ADDRESS=0.0.0.0 66 | ENV LAUNCH=init 67 | ENV DEFAULT_PORT=9223 68 | ENV DEFAULT_PORT_SERVER=6000 69 | 70 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] -------------------------------------------------------------------------------- /docker/Dockerfile.xvfb: -------------------------------------------------------------------------------- 1 | FROM rust:alpine3.21 AS rustbuilder 2 | 3 | WORKDIR /app 4 | 5 | RUN apk upgrade --update-cache --available && \ 6 | apk add gcc cmake make g++ musl-dev 7 | 8 | COPY ../headless_browser/ ./headless_browser 9 | COPY ../headless_browser_lib/ ./headless_browser_lib 10 | COPY ../benches/ ./benches 11 | COPY ../Cargo.* . 12 | 13 | RUN rustup update stable 14 | RUN RUST_LOG=error cargo install --no-default-features --path headless_browser 15 | 16 | FROM alpine:3.21 17 | 18 | # Installs latest Chromium package. 19 | RUN apk upgrade --no-cache --available \ 20 | && apk add --no-cache \ 21 | chromium-swiftshader \ 22 | ttf-freefont \ 23 | font-noto-emoji \ 24 | && apk add --no-cache \ 25 | --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ 26 | font-wqy-zenhei 27 | 28 | COPY ../local.conf /etc/fonts/local.conf 29 | 30 | # Add Chrome as a user 31 | RUN mkdir -p /usr/src/app \ 32 | && adduser -D chrome \ 33 | && chown -R chrome:chrome /usr/src/app 34 | 35 | EXPOSE 9222 6000 36 | 37 | USER root 38 | 39 | COPY --from=rustbuilder /usr/local/cargo/bin/headless_browser /usr/local/bin/headless_browser 40 | COPY ../docker-entrypoint-xvfb.sh / 41 | 42 | RUN apk add --no-cache tini curl sudo xvfb dbus 43 | 44 | RUN chmod +x /docker-entrypoint-xvfb.sh 45 | 46 | USER chrome 47 | 48 | ENV DISPLAY=:0 49 | ENV REMOTE_ADDRESS=0.0.0.0 50 | ENV LAUNCH=init 51 | ENV DEFAULT_PORT=9224 52 | ENV DEFAULT_PORT_SERVER=6001 53 | 54 | ENTRYPOINT ["tini", "--", "/docker-entrypoint-xvfb.sh"] -------------------------------------------------------------------------------- /headless_browser/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "allocator-api2" 34 | version = "0.2.21" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 37 | 38 | [[package]] 39 | name = "async-trait" 40 | version = "0.1.86" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" 43 | dependencies = [ 44 | "proc-macro2", 45 | "quote", 46 | "syn", 47 | ] 48 | 49 | [[package]] 50 | name = "autocfg" 51 | version = "1.4.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 54 | 55 | [[package]] 56 | name = "backtrace" 57 | version = "0.3.74" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 60 | dependencies = [ 61 | "addr2line", 62 | "cfg-if", 63 | "libc", 64 | "miniz_oxide", 65 | "object", 66 | "rustc-demangle", 67 | "windows-targets", 68 | ] 69 | 70 | [[package]] 71 | name = "bitflags" 72 | version = "2.8.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 75 | 76 | [[package]] 77 | name = "bumpalo" 78 | version = "3.17.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 81 | 82 | [[package]] 83 | name = "bytes" 84 | version = "1.10.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" 87 | 88 | [[package]] 89 | name = "cached" 90 | version = "0.54.0" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" 93 | dependencies = [ 94 | "ahash", 95 | "async-trait", 96 | "cached_proc_macro", 97 | "cached_proc_macro_types", 98 | "futures", 99 | "hashbrown", 100 | "once_cell", 101 | "thiserror", 102 | "tokio", 103 | "web-time", 104 | ] 105 | 106 | [[package]] 107 | name = "cached_proc_macro" 108 | version = "0.23.0" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" 111 | dependencies = [ 112 | "darling", 113 | "proc-macro2", 114 | "quote", 115 | "syn", 116 | ] 117 | 118 | [[package]] 119 | name = "cached_proc_macro_types" 120 | version = "0.1.1" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" 123 | 124 | [[package]] 125 | name = "cc" 126 | version = "1.2.15" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" 129 | dependencies = [ 130 | "shlex", 131 | ] 132 | 133 | [[package]] 134 | name = "cfg-if" 135 | version = "1.0.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 138 | 139 | [[package]] 140 | name = "headless_browser" 141 | version = "0.2.33" 142 | dependencies = [ 143 | "cached", 144 | "http-body-util", 145 | "hyper", 146 | "hyper-util", 147 | "lazy_static", 148 | "openssl", 149 | "openssl-sys", 150 | "tikv-jemallocator", 151 | "tokio", 152 | "tracing", 153 | "tracing-subscriber", 154 | ] 155 | 156 | [[package]] 157 | name = "darling" 158 | version = "0.20.10" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" 161 | dependencies = [ 162 | "darling_core", 163 | "darling_macro", 164 | ] 165 | 166 | [[package]] 167 | name = "darling_core" 168 | version = "0.20.10" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" 171 | dependencies = [ 172 | "fnv", 173 | "ident_case", 174 | "proc-macro2", 175 | "quote", 176 | "strsim", 177 | "syn", 178 | ] 179 | 180 | [[package]] 181 | name = "darling_macro" 182 | version = "0.20.10" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" 185 | dependencies = [ 186 | "darling_core", 187 | "quote", 188 | "syn", 189 | ] 190 | 191 | [[package]] 192 | name = "fnv" 193 | version = "1.0.7" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 196 | 197 | [[package]] 198 | name = "foreign-types" 199 | version = "0.3.2" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 202 | dependencies = [ 203 | "foreign-types-shared", 204 | ] 205 | 206 | [[package]] 207 | name = "foreign-types-shared" 208 | version = "0.1.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 211 | 212 | [[package]] 213 | name = "futures" 214 | version = "0.3.31" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 217 | dependencies = [ 218 | "futures-channel", 219 | "futures-core", 220 | "futures-io", 221 | "futures-sink", 222 | "futures-task", 223 | "futures-util", 224 | ] 225 | 226 | [[package]] 227 | name = "futures-channel" 228 | version = "0.3.31" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 231 | dependencies = [ 232 | "futures-core", 233 | "futures-sink", 234 | ] 235 | 236 | [[package]] 237 | name = "futures-core" 238 | version = "0.3.31" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 241 | 242 | [[package]] 243 | name = "futures-io" 244 | version = "0.3.31" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 247 | 248 | [[package]] 249 | name = "futures-sink" 250 | version = "0.3.31" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 253 | 254 | [[package]] 255 | name = "futures-task" 256 | version = "0.3.31" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 259 | 260 | [[package]] 261 | name = "futures-util" 262 | version = "0.3.31" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 265 | dependencies = [ 266 | "futures-core", 267 | "futures-sink", 268 | "futures-task", 269 | "pin-project-lite", 270 | "pin-utils", 271 | ] 272 | 273 | [[package]] 274 | name = "gimli" 275 | version = "0.31.1" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 278 | 279 | [[package]] 280 | name = "hashbrown" 281 | version = "0.14.5" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 284 | dependencies = [ 285 | "ahash", 286 | "allocator-api2", 287 | ] 288 | 289 | [[package]] 290 | name = "http" 291 | version = "1.2.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 294 | dependencies = [ 295 | "bytes", 296 | "fnv", 297 | "itoa", 298 | ] 299 | 300 | [[package]] 301 | name = "http-body" 302 | version = "1.0.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 305 | dependencies = [ 306 | "bytes", 307 | "http", 308 | ] 309 | 310 | [[package]] 311 | name = "http-body-util" 312 | version = "0.1.2" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 315 | dependencies = [ 316 | "bytes", 317 | "futures-util", 318 | "http", 319 | "http-body", 320 | "pin-project-lite", 321 | ] 322 | 323 | [[package]] 324 | name = "httparse" 325 | version = "1.10.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" 328 | 329 | [[package]] 330 | name = "httpdate" 331 | version = "1.0.3" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 334 | 335 | [[package]] 336 | name = "hyper" 337 | version = "1.6.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" 340 | dependencies = [ 341 | "bytes", 342 | "futures-channel", 343 | "futures-util", 344 | "http", 345 | "http-body", 346 | "httparse", 347 | "httpdate", 348 | "itoa", 349 | "pin-project-lite", 350 | "smallvec", 351 | "tokio", 352 | "want", 353 | ] 354 | 355 | [[package]] 356 | name = "hyper-util" 357 | version = "0.1.10" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" 360 | dependencies = [ 361 | "bytes", 362 | "futures-util", 363 | "http", 364 | "http-body", 365 | "hyper", 366 | "pin-project-lite", 367 | "tokio", 368 | ] 369 | 370 | [[package]] 371 | name = "ident_case" 372 | version = "1.0.1" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 375 | 376 | [[package]] 377 | name = "itoa" 378 | version = "1.0.14" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 381 | 382 | [[package]] 383 | name = "js-sys" 384 | version = "0.3.77" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 387 | dependencies = [ 388 | "once_cell", 389 | "wasm-bindgen", 390 | ] 391 | 392 | [[package]] 393 | name = "lazy_static" 394 | version = "1.5.0" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 397 | 398 | [[package]] 399 | name = "libc" 400 | version = "0.2.170" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" 403 | 404 | [[package]] 405 | name = "lock_api" 406 | version = "0.4.12" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 409 | dependencies = [ 410 | "autocfg", 411 | "scopeguard", 412 | ] 413 | 414 | [[package]] 415 | name = "log" 416 | version = "0.4.26" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 419 | 420 | [[package]] 421 | name = "memchr" 422 | version = "2.7.4" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 425 | 426 | [[package]] 427 | name = "miniz_oxide" 428 | version = "0.8.5" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 431 | dependencies = [ 432 | "adler2", 433 | ] 434 | 435 | [[package]] 436 | name = "mio" 437 | version = "1.0.3" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 440 | dependencies = [ 441 | "libc", 442 | "wasi", 443 | "windows-sys", 444 | ] 445 | 446 | [[package]] 447 | name = "nu-ansi-term" 448 | version = "0.46.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 451 | dependencies = [ 452 | "overload", 453 | "winapi", 454 | ] 455 | 456 | [[package]] 457 | name = "object" 458 | version = "0.36.7" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 461 | dependencies = [ 462 | "memchr", 463 | ] 464 | 465 | [[package]] 466 | name = "once_cell" 467 | version = "1.20.3" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 470 | 471 | [[package]] 472 | name = "openssl" 473 | version = "0.10.71" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" 476 | dependencies = [ 477 | "bitflags", 478 | "cfg-if", 479 | "foreign-types", 480 | "libc", 481 | "once_cell", 482 | "openssl-macros", 483 | "openssl-sys", 484 | ] 485 | 486 | [[package]] 487 | name = "openssl-macros" 488 | version = "0.1.1" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 491 | dependencies = [ 492 | "proc-macro2", 493 | "quote", 494 | "syn", 495 | ] 496 | 497 | [[package]] 498 | name = "openssl-src" 499 | version = "300.4.2+3.4.1" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "168ce4e058f975fe43e89d9ccf78ca668601887ae736090aacc23ae353c298e2" 502 | dependencies = [ 503 | "cc", 504 | ] 505 | 506 | [[package]] 507 | name = "openssl-sys" 508 | version = "0.9.106" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" 511 | dependencies = [ 512 | "cc", 513 | "libc", 514 | "openssl-src", 515 | "pkg-config", 516 | "vcpkg", 517 | ] 518 | 519 | [[package]] 520 | name = "overload" 521 | version = "0.1.1" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 524 | 525 | [[package]] 526 | name = "parking_lot" 527 | version = "0.12.3" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 530 | dependencies = [ 531 | "lock_api", 532 | "parking_lot_core", 533 | ] 534 | 535 | [[package]] 536 | name = "parking_lot_core" 537 | version = "0.9.10" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 540 | dependencies = [ 541 | "cfg-if", 542 | "libc", 543 | "redox_syscall", 544 | "smallvec", 545 | "windows-targets", 546 | ] 547 | 548 | [[package]] 549 | name = "pin-project-lite" 550 | version = "0.2.16" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 553 | 554 | [[package]] 555 | name = "pin-utils" 556 | version = "0.1.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 559 | 560 | [[package]] 561 | name = "pkg-config" 562 | version = "0.3.31" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 565 | 566 | [[package]] 567 | name = "proc-macro2" 568 | version = "1.0.93" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 571 | dependencies = [ 572 | "unicode-ident", 573 | ] 574 | 575 | [[package]] 576 | name = "quote" 577 | version = "1.0.38" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 580 | dependencies = [ 581 | "proc-macro2", 582 | ] 583 | 584 | [[package]] 585 | name = "redox_syscall" 586 | version = "0.5.9" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" 589 | dependencies = [ 590 | "bitflags", 591 | ] 592 | 593 | [[package]] 594 | name = "rustc-demangle" 595 | version = "0.1.24" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 598 | 599 | [[package]] 600 | name = "scopeguard" 601 | version = "1.2.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 604 | 605 | [[package]] 606 | name = "sharded-slab" 607 | version = "0.1.7" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 610 | dependencies = [ 611 | "lazy_static", 612 | ] 613 | 614 | [[package]] 615 | name = "shlex" 616 | version = "1.3.0" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 619 | 620 | [[package]] 621 | name = "signal-hook-registry" 622 | version = "1.4.2" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 625 | dependencies = [ 626 | "libc", 627 | ] 628 | 629 | [[package]] 630 | name = "smallvec" 631 | version = "1.14.0" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 634 | 635 | [[package]] 636 | name = "socket2" 637 | version = "0.5.8" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 640 | dependencies = [ 641 | "libc", 642 | "windows-sys", 643 | ] 644 | 645 | [[package]] 646 | name = "strsim" 647 | version = "0.11.1" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 650 | 651 | [[package]] 652 | name = "syn" 653 | version = "2.0.98" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 656 | dependencies = [ 657 | "proc-macro2", 658 | "quote", 659 | "unicode-ident", 660 | ] 661 | 662 | [[package]] 663 | name = "thiserror" 664 | version = "1.0.69" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 667 | dependencies = [ 668 | "thiserror-impl", 669 | ] 670 | 671 | [[package]] 672 | name = "thiserror-impl" 673 | version = "1.0.69" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 676 | dependencies = [ 677 | "proc-macro2", 678 | "quote", 679 | "syn", 680 | ] 681 | 682 | [[package]] 683 | name = "thread_local" 684 | version = "1.1.8" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 687 | dependencies = [ 688 | "cfg-if", 689 | "once_cell", 690 | ] 691 | 692 | [[package]] 693 | name = "tikv-jemalloc-sys" 694 | version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" 697 | dependencies = [ 698 | "cc", 699 | "libc", 700 | ] 701 | 702 | [[package]] 703 | name = "tikv-jemallocator" 704 | version = "0.6.0" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" 707 | dependencies = [ 708 | "libc", 709 | "tikv-jemalloc-sys", 710 | ] 711 | 712 | [[package]] 713 | name = "tokio" 714 | version = "1.43.0" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" 717 | dependencies = [ 718 | "backtrace", 719 | "bytes", 720 | "libc", 721 | "mio", 722 | "parking_lot", 723 | "pin-project-lite", 724 | "signal-hook-registry", 725 | "socket2", 726 | "tokio-macros", 727 | "windows-sys", 728 | ] 729 | 730 | [[package]] 731 | name = "tokio-macros" 732 | version = "2.5.0" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 735 | dependencies = [ 736 | "proc-macro2", 737 | "quote", 738 | "syn", 739 | ] 740 | 741 | [[package]] 742 | name = "tracing" 743 | version = "0.1.41" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 746 | dependencies = [ 747 | "pin-project-lite", 748 | "tracing-attributes", 749 | "tracing-core", 750 | ] 751 | 752 | [[package]] 753 | name = "tracing-attributes" 754 | version = "0.1.28" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 757 | dependencies = [ 758 | "proc-macro2", 759 | "quote", 760 | "syn", 761 | ] 762 | 763 | [[package]] 764 | name = "tracing-core" 765 | version = "0.1.33" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 768 | dependencies = [ 769 | "once_cell", 770 | "valuable", 771 | ] 772 | 773 | [[package]] 774 | name = "tracing-log" 775 | version = "0.2.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 778 | dependencies = [ 779 | "log", 780 | "once_cell", 781 | "tracing-core", 782 | ] 783 | 784 | [[package]] 785 | name = "tracing-subscriber" 786 | version = "0.3.19" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 789 | dependencies = [ 790 | "nu-ansi-term", 791 | "sharded-slab", 792 | "smallvec", 793 | "thread_local", 794 | "tracing-core", 795 | "tracing-log", 796 | ] 797 | 798 | [[package]] 799 | name = "try-lock" 800 | version = "0.2.5" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 803 | 804 | [[package]] 805 | name = "unicode-ident" 806 | version = "1.0.17" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 809 | 810 | [[package]] 811 | name = "valuable" 812 | version = "0.1.1" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 815 | 816 | [[package]] 817 | name = "vcpkg" 818 | version = "0.2.15" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 821 | 822 | [[package]] 823 | name = "version_check" 824 | version = "0.9.5" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 827 | 828 | [[package]] 829 | name = "want" 830 | version = "0.3.1" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 833 | dependencies = [ 834 | "try-lock", 835 | ] 836 | 837 | [[package]] 838 | name = "wasi" 839 | version = "0.11.0+wasi-snapshot-preview1" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 842 | 843 | [[package]] 844 | name = "wasm-bindgen" 845 | version = "0.2.100" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 848 | dependencies = [ 849 | "cfg-if", 850 | "once_cell", 851 | "wasm-bindgen-macro", 852 | ] 853 | 854 | [[package]] 855 | name = "wasm-bindgen-backend" 856 | version = "0.2.100" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 859 | dependencies = [ 860 | "bumpalo", 861 | "log", 862 | "proc-macro2", 863 | "quote", 864 | "syn", 865 | "wasm-bindgen-shared", 866 | ] 867 | 868 | [[package]] 869 | name = "wasm-bindgen-macro" 870 | version = "0.2.100" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 873 | dependencies = [ 874 | "quote", 875 | "wasm-bindgen-macro-support", 876 | ] 877 | 878 | [[package]] 879 | name = "wasm-bindgen-macro-support" 880 | version = "0.2.100" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 883 | dependencies = [ 884 | "proc-macro2", 885 | "quote", 886 | "syn", 887 | "wasm-bindgen-backend", 888 | "wasm-bindgen-shared", 889 | ] 890 | 891 | [[package]] 892 | name = "wasm-bindgen-shared" 893 | version = "0.2.100" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 896 | dependencies = [ 897 | "unicode-ident", 898 | ] 899 | 900 | [[package]] 901 | name = "web-time" 902 | version = "1.1.0" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 905 | dependencies = [ 906 | "js-sys", 907 | "wasm-bindgen", 908 | ] 909 | 910 | [[package]] 911 | name = "winapi" 912 | version = "0.3.9" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 915 | dependencies = [ 916 | "winapi-i686-pc-windows-gnu", 917 | "winapi-x86_64-pc-windows-gnu", 918 | ] 919 | 920 | [[package]] 921 | name = "winapi-i686-pc-windows-gnu" 922 | version = "0.4.0" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 925 | 926 | [[package]] 927 | name = "winapi-x86_64-pc-windows-gnu" 928 | version = "0.4.0" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 931 | 932 | [[package]] 933 | name = "windows-sys" 934 | version = "0.52.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 937 | dependencies = [ 938 | "windows-targets", 939 | ] 940 | 941 | [[package]] 942 | name = "windows-targets" 943 | version = "0.52.6" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 946 | dependencies = [ 947 | "windows_aarch64_gnullvm", 948 | "windows_aarch64_msvc", 949 | "windows_i686_gnu", 950 | "windows_i686_gnullvm", 951 | "windows_i686_msvc", 952 | "windows_x86_64_gnu", 953 | "windows_x86_64_gnullvm", 954 | "windows_x86_64_msvc", 955 | ] 956 | 957 | [[package]] 958 | name = "windows_aarch64_gnullvm" 959 | version = "0.52.6" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 962 | 963 | [[package]] 964 | name = "windows_aarch64_msvc" 965 | version = "0.52.6" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 968 | 969 | [[package]] 970 | name = "windows_i686_gnu" 971 | version = "0.52.6" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 974 | 975 | [[package]] 976 | name = "windows_i686_gnullvm" 977 | version = "0.52.6" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 980 | 981 | [[package]] 982 | name = "windows_i686_msvc" 983 | version = "0.52.6" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 986 | 987 | [[package]] 988 | name = "windows_x86_64_gnu" 989 | version = "0.52.6" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 992 | 993 | [[package]] 994 | name = "windows_x86_64_gnullvm" 995 | version = "0.52.6" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 998 | 999 | [[package]] 1000 | name = "windows_x86_64_msvc" 1001 | version = "0.52.6" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1004 | 1005 | [[package]] 1006 | name = "zerocopy" 1007 | version = "0.7.35" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1010 | dependencies = [ 1011 | "zerocopy-derive", 1012 | ] 1013 | 1014 | [[package]] 1015 | name = "zerocopy-derive" 1016 | version = "0.7.35" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1019 | dependencies = [ 1020 | "proc-macro2", 1021 | "quote", 1022 | "syn", 1023 | ] 1024 | -------------------------------------------------------------------------------- /headless_browser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "headless_browser" 3 | version = "0.1.23" 4 | edition = "2021" 5 | authors = [ 6 | "j-mendez " 7 | ] 8 | description = "A scalable application for managing headless Chrome instances with proxy and server capabilities." 9 | license = "MIT" 10 | repository = "https://github.com/spider-rs/chrome-server" 11 | documentation = "https://docs.rs/headless_browser" 12 | categories = ["web-programming", "command-line-utilities"] 13 | keywords = ["chrome", "proxy"] 14 | 15 | [dependencies] 16 | headless_browser_lib = { version = "0", path = "../headless_browser_lib" } 17 | tokio = { version = "1", features = ["rt-multi-thread", "signal", "macros", "net", "io-util"] } 18 | tracing = "0.1" 19 | tracing-subscriber = "0.3" 20 | 21 | [target.'cfg(not(target_env = "msvc"))'.dependencies] 22 | tikv-jemallocator = { version = "0.6", features = ["background_threads", "background_threads_runtime_support"], optional = true } 23 | 24 | [dev-dependencies] 25 | futures-util = "0.3" 26 | spider_chrome = "2" 27 | tokio = { version = "1", features = ["rt-multi-thread", "signal", "macros", "net", "io-util"] } 28 | 29 | [features] 30 | default = ["jemalloc"] 31 | jemalloc = ["dep:tikv-jemallocator"] 32 | physical_gpu = ["headless_browser_lib/physical_gpu"] -------------------------------------------------------------------------------- /headless_browser/README.md: -------------------------------------------------------------------------------- 1 | 2 | # headless-browser 3 | 4 | The `headless-browser` application serves as the primary entry point for managing headless Chrome instances with integrated proxy and server capabilities. This solution is designed to scale efficiently, providing developers with a robust tool for web automation and testing. 5 | 6 | ## Installation 7 | 8 | To install the `headless-browser`, ensure that you have Rust installed, and then run: 9 | 10 | ```sh 11 | cargo install headless_browser 12 | ``` 13 | 14 | ## Usage 15 | 16 | Once installed, you can start the `headless-browser` application by executing the following command: 17 | 18 | ```sh 19 | headless_browser 20 | ``` 21 | 22 | This command initializes the environment and sets up the headless Chrome instances with the configured proxy and server settings. 23 | 24 | ## Testing 25 | 26 | The application is tested using the highly optimized concurrent control mechanisms provided by the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) through the [spider_chrome](https://github.com/spider-rs/spider/tree/main/spider_chrome) crate. This ensures robust testing capabilities for a variety of web interaction scenarios. 27 | 28 | Run the following command to execute tests and capture outputs: 29 | 30 | ```sh 31 | RUST_LOG=error,info cargo test --package headless_browser --test cdp -- --nocapture 32 | ``` 33 | 34 | Ensure you have the appropriate logging and test configurations set up to get detailed output from your test executions. 35 | 36 | --- 37 | 38 | This documentation provides a comprehensive overview for users looking to get started with the `headless-browser` tool, detailing installation, execution, and testing processes. -------------------------------------------------------------------------------- /headless_browser/src/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] 2 | use tikv_jemallocator::Jemalloc; 3 | #[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))] 4 | #[global_allocator] 5 | static GLOBAL: Jemalloc = Jemalloc; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<(), Box> { 9 | tracing_subscriber::fmt::init(); 10 | headless_browser_lib::run_main().await 11 | } 12 | -------------------------------------------------------------------------------- /headless_browser/tests/cdp.rs: -------------------------------------------------------------------------------- 1 | use chromiumoxide::{browser::Browser, error::CdpError}; 2 | use futures_util::stream::StreamExt; 3 | use std::{ 4 | env::set_var, 5 | time::{Duration, Instant}, 6 | }; 7 | 8 | #[tokio::test] 9 | /// Test the basic crawl with all of the major chromium based libs. 10 | async fn basic() -> Result<(), Box> { 11 | set_var("CHROME_INIT", "ignore"); // ignore the auto start 12 | tracing_subscriber::fmt::init(); 13 | headless_browser_lib::fork(Some(*headless_browser_lib::conf::DEFAULT_PORT)); 14 | let task = tokio::spawn(headless_browser_lib::run_main()); 15 | tokio::time::sleep(Duration::from_millis(100)).await; // give a slight delay for now until we use a oneshot. 16 | 17 | let start = Instant::now(); 18 | 19 | let (browser, mut handler) = 20 | Browser::connect_with_config("http://127.0.0.1:6000/json/version", Default::default()) 21 | .await?; 22 | 23 | let handle = tokio::task::spawn(async move { 24 | while let Some(h) = handler.next().await { 25 | if h.is_err() { 26 | break; 27 | } 28 | } 29 | }); 30 | 31 | let navigate_page = async |u: &str| -> Result { 32 | let start = Instant::now(); 33 | let page = browser.new_page(u).await?; 34 | println!("NewPage({u}): {:?}", start.elapsed()); 35 | let start = Instant::now(); 36 | let html = page.wait_for_navigation().await?.content().await?; 37 | println!("WaitForNavigationAndContent({u}): {:?}", start.elapsed()); 38 | Ok::(html) 39 | }; 40 | 41 | let (spider_html, example_html) = tokio::join!( 42 | navigate_page("https://spider.cloud"), 43 | navigate_page("https://example.com") 44 | ); 45 | 46 | let elasped = start.elapsed(); 47 | 48 | browser.close().await?; 49 | let _ = handle.await; 50 | 51 | let spider_html = spider_html?; 52 | let example_html = example_html?; 53 | 54 | assert_eq!(elasped.as_secs() <= 15, true); 55 | assert_eq!(spider_html.is_empty(), false); 56 | assert_eq!(spider_html.len() >= 1000, true); 57 | 58 | assert_eq!(example_html.is_empty(), false); 59 | assert_eq!(example_html.len() >= 400, true); 60 | 61 | println!("Time took: {:?}", elasped); 62 | task.abort(); 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /headless_browser_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "headless_browser_lib" 3 | version = "0.1.23" 4 | edition = "2021" 5 | authors = [ 6 | "j-mendez " 7 | ] 8 | description = "A library providing a Chrome proxy API for managing Chrome instances in cloud environments." 9 | license = "MIT" 10 | repository = "https://github.com/spider-rs/headless-browser" 11 | documentation = "https://docs.rs/headless_browser_lib" 12 | categories = ["web-programming", "command-line-utilities"] 13 | keywords = ["chrome", "proxy"] 14 | 15 | [dependencies] 16 | hyper = { version = "1", features = ["client", "http1", "server"] } 17 | tokio = { version = "1", features = ["rt-multi-thread", "signal", "macros", "net", "io-util"] } 18 | http-body-util = "0.1" 19 | hyper-util = { version = "0.1", features = ["tokio"] } 20 | lazy_static = "1" 21 | openssl = { version = "0.10", features = ["vendored"] } 22 | openssl-sys = { version = "0.9", features = ["vendored"] } 23 | cached = { version = "0", features = ["async_tokio_rt_multi_thread"]} 24 | tracing = "0.1" 25 | rand = "0.9" 26 | num_cpus = "1" 27 | sysinfo = "0.33" 28 | dashmap = "6" 29 | 30 | [features] 31 | testing = [] 32 | physical_gpu = [] -------------------------------------------------------------------------------- /headless_browser_lib/LOAD_BALANCER_DOCS.md: -------------------------------------------------------------------------------- 1 | # Atomic Load Balancer Forwarder for Chrome Instances 2 | 3 | ## Overview 4 | 5 | This document outlines the architecture and design of a library that acts as a load balancer (LB) and forwarder to manage multiple headless Chrome instances on remote servers. This system addresses the security limitations posed by Chrome's restriction on remote connections and optimizes resource utilization across machines. 6 | 7 | ## Objectives 8 | 9 | - Enable secure forwarding of requests to internal Chrome instances on remote servers. 10 | - Improve resource utilization by distributing the load across multiple machines and CPU cores. 11 | - Implement an efficient load balancing strategy using a round-robin approach to spawn multiple Chrome instances. 12 | 13 | ## Architecture 14 | 15 | Below is a high level design for the LB. 16 | 17 | ### Forwarding 18 | 19 | The primary function of the library is to forward all incoming requests to the appropriate Chrome instance. This allows for centralized management and control over the Chrome connections, enhancing security and flexibility. 20 | 21 | ### Performance 22 | 23 | Chrome's multi-threading capabilities include certain limitations, particularly when scaling CPU usage beyond 100-200% on a single core, leading to performance degradation. To mitigate this, the load balancer is designed to handle workloads across multiple machines. 24 | 25 | - The system will utilize a round-robin strategy to spawn and manage multiple Chrome instances, maximizing CPU core usage. 26 | - Efficient management of Chrome instances ensures optimal resource allocation and performance. 27 | 28 | ## Design 29 | 30 | The following steps outline the core design principles and implementation strategy: 31 | 32 | 1. **Atomic Tracker for Instance Management:** 33 | - Use the `/json/version` endpoint to maintain an atomic tracker that manages the spawning of new Chrome instances. 34 | - Track the last Process ID (PID) in an atomic manner to ensure that each instance is correctly managed and tracked. 35 | 36 | 2. **Proxy Forwarder Logic:** 37 | - Identify the current target instance and route the connection to the corresponding node using the atomic tracker from `/json/version`. 38 | - Decrement the atomic tracker upon request completion to maintain an accurate count of active instances. 39 | 40 | 3. **Index and Tracker Synchronization:** 41 | - Synchronize the proxy forwarder with the atomic tracker and current index values based on `/json/version`. 42 | - Ensure consistency between the tracker and active instances. 43 | 44 | 4. **Connection Limit Management:** 45 | - Implement a maximum connection limit for atomic trackers to facilitate the spawning of new Chrome instances. 46 | - Prioritize reusing available slots before initiating additional instances on other machines. 47 | 48 | 5. **PID Tracking:** 49 | - Use `/json/version` trackers to maintain a record of active PIDs, enabling efficient cleanup and management of instance positions. 50 | 51 | 6. **Modify modify_json_output** 52 | - Make the changes for `modify_json_output` so it returns the proper port binding. 53 | 54 | This architecture and design aim to optimize performance, security, and resource management for handling multiple Chrome instances, ensuring scalable and efficient operations. -------------------------------------------------------------------------------- /headless_browser_lib/README.md: -------------------------------------------------------------------------------- 1 | # headless_browser_lib 2 | 3 | The headless browser library. 4 | 5 | `cargo add headless_browser_lib` 6 | 7 | ## Features 8 | 9 | If you know you have a physical gpu use the flag `--physical_gpu`. -------------------------------------------------------------------------------- /headless_browser_lib/src/conf.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicBool, AtomicU64}; 2 | 3 | /// The performance arg count. 4 | #[cfg(not(feature = "physical_gpu"))] 5 | pub(crate) const PERF_ARGS: usize = 99; 6 | 7 | /// The performance arg count. 8 | #[cfg(feature = "physical_gpu")] 9 | pub(crate) const PERF_ARGS: usize = 97; 10 | 11 | lazy_static::lazy_static! { 12 | /// The chrome args to use test ( basic without anything used for testing ). 13 | pub static ref CHROME_ARGS_TEST: [&'static str; 6] = { 14 | let headless = std::env::args() 15 | .nth(6) 16 | .unwrap_or("true".into()); 17 | 18 | let headless = if headless != "false" { 19 | match std::env::var("HEADLESS") { 20 | Ok(h) => { 21 | if h == "false" { 22 | "" 23 | } else if h == "new" { 24 | "--headless=new" 25 | }else { 26 | "--headless" 27 | } 28 | } 29 | _ => "--headless" 30 | } 31 | } else { 32 | "" 33 | }; 34 | 35 | let port = if DEFAULT_PORT.eq(&9223) { 36 | "--remote-debugging-port=9223" 37 | } else if DEFAULT_PORT.eq(&9224) { 38 | "--remote-debugging-port=9224" 39 | } else { 40 | "--remote-debugging-port=9222" 41 | }; 42 | 43 | let use_gl = match std::env::var("CHROME_GL") { 44 | Ok(h) => { 45 | if h == "angle" { 46 | "--use-gl=angle" 47 | } else { 48 | "--use-gl=swiftshader" 49 | } 50 | } 51 | _ => "--use-gl=angle" 52 | }; 53 | 54 | let gpu = std::env::var("ENABLE_GPU").unwrap_or_default() == "true"; 55 | 56 | let gpu_enabled = if gpu { "--enable-gpu" } else { "--disable-gpu" }; 57 | let gpu_enabled_sandboxed = if gpu { "--enable-gpu-sandbox" } else { "--disable-gpu-sandbox" }; 58 | 59 | [ 60 | // *SPECIAL* 61 | "--remote-debugging-address=0.0.0.0", 62 | port, 63 | // *SPECIAL* 64 | headless, 65 | gpu_enabled, 66 | gpu_enabled_sandboxed, 67 | use_gl, 68 | ] 69 | }; 70 | } 71 | 72 | lazy_static::lazy_static! { 73 | /// Is the instance healthy? 74 | pub static ref IS_HEALTHY: AtomicBool = AtomicBool::new(true); 75 | pub static ref CHROME_INSTANCES: dashmap::DashSet = dashmap::DashSet::new(); 76 | pub static ref DEFAULT_PORT: u32 = { 77 | let default_port = std::env::args() 78 | .nth(4) 79 | .unwrap_or("9223".into()) 80 | .parse::() 81 | .unwrap_or_default(); 82 | 83 | let default_port = if default_port == 0 { 84 | 9223 85 | } else { 86 | default_port 87 | }; 88 | 89 | default_port 90 | }; 91 | pub static ref DEFAULT_PORT_SERVER: u16 = { 92 | let default_port = std::env::args() 93 | .nth(5) 94 | .unwrap_or("6000".into()) 95 | .parse::() 96 | .unwrap_or_default(); 97 | let default_port = if default_port == 0 { 98 | 6000 99 | } else { 100 | default_port 101 | }; 102 | 103 | default_port 104 | }; 105 | /// Is a brave instance? 106 | pub(crate) static ref BRAVE_INSTANCE: bool = { 107 | CHROME_PATH.ends_with("Brave Browser") 108 | || CHROME_PATH.ends_with("brave-browser") 109 | }; 110 | /// Is a lightpanda instance? 111 | pub(crate) static ref LIGHT_PANDA: bool = { 112 | CHROME_PATH.ends_with("lightpanda-aarch64-macos") 113 | || CHROME_PATH.ends_with("lightpanda-x86_64-linux") 114 | }; 115 | /// The light panda args to use. 116 | pub static ref LIGHTPANDA_ARGS: [&'static str; 2] = { 117 | let port = if DEFAULT_PORT.eq(&9223) { 118 | "--port=9223" 119 | } else if DEFAULT_PORT.eq(&9224) { 120 | "--port=9224" 121 | } else { 122 | "--port=9222" 123 | }; 124 | 125 | [ 126 | "--host=0.0.0.0", 127 | port, 128 | ] 129 | }; 130 | /// Return base target and replacement. Target port is the port for chrome. 131 | pub(crate) static ref TARGET_REPLACEMENT: (&'static [u8; 5], &'static[u8; 5]) = { 132 | if *DEFAULT_PORT == 9223 { 133 | let target_port = b":9223"; 134 | let proxy_port = b":9222"; 135 | 136 | (target_port, proxy_port) 137 | } else { 138 | // we need to allow dynamic ports instead of defaulting to standard and xfvb offport. 139 | let target_port = b":9224"; 140 | let proxy_port = b":9223"; 141 | 142 | (target_port, proxy_port) 143 | } 144 | }; 145 | /// The hostname of the machine to replace 127.0.0.1 when making request to /json/version on port 6000. 146 | pub(crate) static ref HOST_NAME: String = { 147 | let mut hostname = String::new(); 148 | 149 | if let Ok(name) = std::env::var("HOSTNAME_OVERRIDE") { 150 | if !name.is_empty() { 151 | hostname = name; 152 | } 153 | } 154 | 155 | if hostname.is_empty() { 156 | if let Ok(name) = std::env::var("HOSTNAME") { 157 | if !name.is_empty() { 158 | hostname = name; 159 | } 160 | } 161 | } 162 | 163 | hostname 164 | }; 165 | /// The main endpoint for entry. 166 | pub(crate) static ref ENDPOINT_BASE: String = { 167 | format!("http://127.0.0.1:{}", *DEFAULT_PORT) 168 | }; 169 | /// The main endpoint json/version. 170 | pub(crate) static ref ENDPOINT: String = { 171 | format!("http://127.0.0.1:{}/json/version", *DEFAULT_PORT) 172 | }; 173 | /// The chrome launch path. 174 | pub static ref CHROME_PATH: String = { 175 | // cargo bench will always pass in the first arg 176 | let default_path = std::env::args().nth(1).unwrap_or_default(); 177 | let trimmed_path = default_path.trim(); 178 | 179 | // handle testing and default to OS 180 | if default_path.is_empty() || trimmed_path == "--nocapture" || trimmed_path == "--bench" { 181 | let chrome_path = match std::env::var("CHROME_PATH") { 182 | Ok(p) => p, 183 | _ => Default::default() 184 | }; 185 | 186 | if chrome_path.is_empty() { 187 | get_default_chrome_bin().to_string() 188 | } else { 189 | chrome_path 190 | } 191 | } else { 192 | default_path 193 | } 194 | }; 195 | /// The chrome address. 196 | pub(crate) static ref CHROME_ADDRESS: String = { 197 | let mut host_address = std::env::args().nth(2).unwrap_or("127.0.0.1".to_string()).to_string(); 198 | 199 | if host_address.is_empty() { 200 | host_address = String::from("127.0.0.1").into() 201 | } 202 | 203 | host_address 204 | }; 205 | pub(crate) static ref CACHEABLE: AtomicBool = { 206 | AtomicBool::new(true) 207 | }; 208 | /// The last cache date period. 209 | pub(crate) static ref LAST_CACHE: AtomicU64 = { 210 | AtomicU64::new(0) 211 | }; 212 | /// Debug the json version endpoint. 213 | pub(crate) static ref DEBUG_JSON: bool = std::env::var("DEBUG_JSON").unwrap_or_default() == "true"; 214 | /// Test headless without args. 215 | pub(crate) static ref TEST_NO_ARGS: bool = std::env::var("TEST_NO_ARGS").unwrap_or_default() == "true"; 216 | /// Entry port to the proxy. 217 | pub(crate) static ref ENTRY: &'static str = { 218 | if crate::TARGET_REPLACEMENT.0 == b":9223" { 219 | "0.0.0.0:9222" 220 | } else { 221 | "0.0.0.0:9223" 222 | } 223 | }; 224 | /// Target chrome server. 225 | pub(crate) static ref TARGET: &'static str = { 226 | if crate::TARGET_REPLACEMENT.1 == b":9222" { 227 | "0.0.0.0:9223" 228 | } else { 229 | "0.0.0.0:9224" 230 | } 231 | }; 232 | /// The buffer size. 233 | pub(crate) static ref BUFFER_SIZE: usize = { 234 | let buffer_size = std::env::var("BUFFER_SIZE") 235 | .ok() 236 | .and_then(|s| s.parse().ok()) 237 | .unwrap_or(131072); // Default to 128kb 238 | buffer_size 239 | }; 240 | /// 10 sec cache 241 | pub(crate) static ref TEN_SECONDS: std::time::Duration = { 242 | std::time::Duration::from_secs(10) 243 | }; 244 | } 245 | 246 | #[cfg(not(feature = "physical_gpu"))] 247 | lazy_static::lazy_static! { 248 | /// The chrome args to use. 249 | pub static ref CHROME_ARGS: [&'static str; PERF_ARGS] = { 250 | let headless = std::env::args() 251 | .nth(6) 252 | .unwrap_or("true".into()); 253 | 254 | let headless = match (headless != "false", std::env::var("HEADLESS").as_deref()) { 255 | (false, _) | (true, Ok("false")) => "--test-type=gpu", 256 | (_, Ok("new")) => "--headless=new", 257 | _ => "--headless", 258 | }; 259 | 260 | let port = if DEFAULT_PORT.eq(&9223) { 261 | "--remote-debugging-port=9223" 262 | } else if DEFAULT_PORT.eq(&9224) { 263 | "--remote-debugging-port=9224" 264 | } else { 265 | "--remote-debugging-port=9222" 266 | }; 267 | let gpu = std::env::var("ENABLE_GPU").unwrap_or_default() == "true"; 268 | let gpu_enabled = if gpu { "--enable-gpu" } else { "--disable-gpu" }; 269 | let gpu_enabled_sandboxed = if gpu { "--enable-gpu-sandbox" } else { "--disable-gpu-sandbox" }; 270 | 271 | let use_gl = match std::env::var("CHROME_GL") { 272 | Ok(h) => { 273 | if h == "angle" { 274 | "--use-gl=angle" 275 | } else { 276 | "--use-gl=swiftshader" 277 | } 278 | } 279 | _ => "--use-gl=swiftshader" 280 | }; 281 | 282 | 283 | [ 284 | // *SPECIAL* 285 | "--remote-debugging-address=0.0.0.0", 286 | port, 287 | // *SPECIAL* 288 | headless, 289 | gpu_enabled, 290 | gpu_enabled_sandboxed, 291 | use_gl, 292 | "--no-zygote", 293 | "--user-data-dir=~/.config/google-chrome", 294 | "--ignore-certificate-errors", 295 | "--no-default-browser-check", 296 | "--no-first-run", 297 | "--no-sandbox", 298 | "--enable-webgl", 299 | "--enable-webgl2-compute-context", 300 | "--enable-webgl-draft-extensions", 301 | "--enable-unsafe-webgpu", 302 | "--enable-web-bluetooth", 303 | "--enable-dom-distiller", 304 | "--enable-distillability-service", 305 | "--enable-surface-synchronization", 306 | "--enable-logging=stderr", 307 | "--enable-async-dns", 308 | "--disable-setuid-sandbox", 309 | "--disable-dev-shm-usage", // required or else container will crash not enough memory 310 | "--disable-threaded-scrolling", 311 | "--disable-cookie-encryption", 312 | "--disable-demo-mode", 313 | "--disable-dinosaur-easter-egg", 314 | "--disable-fetching-hints-at-navigation-start", 315 | "--disable-site-isolation-trials", 316 | "--disable-threaded-animation", 317 | "--disable-sync", 318 | "--disable-print-preview", 319 | "--disable-search-engine-choice-screen", 320 | "--disable-in-process-stack-traces", 321 | "--disable-low-res-tiling", 322 | "--disable-oobe-chromevox-hint-timer-for-testing", 323 | "--disable-smooth-scrolling", 324 | "--disable-prompt-on-repost", 325 | "--disable-domain-reliability", 326 | "--disable-gesture-typing", 327 | "--disable-background-timer-throttling", 328 | "--disable-breakpad", 329 | "--disable-crash-reporter", 330 | "--disable-asynchronous-spellchecking", 331 | "--disable-html5-camera", 332 | "--disable-hang-monitor", 333 | "--disable-checker-imaging", 334 | "--disable-image-animation-resync", 335 | "--disable-client-side-phishing-detection", 336 | "--disable-component-extensions-with-background-pages", 337 | "--disable-background-networking", 338 | "--disable-renderer-backgrounding", 339 | "--disable-field-trial-config", 340 | "--disable-back-forward-cache", 341 | "--disable-backgrounding-occluded-windows", 342 | "--disable-stack-profiler", 343 | "--disable-libassistant-logfile", 344 | "--disable-datasaver-prompt", 345 | "--disable-histogram-customizer", 346 | "--disable-vulkan-fallback-to-gl-for-testing", 347 | "--disable-vulkan-surface", 348 | "--disable-webrtc", 349 | "--disable-oopr-debug-crash-dump", 350 | "--disable-pnacl-crash-throttling", 351 | "--disable-renderer-accessibility", 352 | "--disable-pushstate-throttle", 353 | "--disable-blink-features=AutomationControlled", 354 | "--disable-ipc-flooding-protection", // we do not need to throttle navigation for https://github.com/spider-rs/spider/commit/9ff5bbd7a2656b8edb84b62843b72ae9d09af079#diff-75ce697faf0d37c3dff4a3a19e7524798b3cb5487f8f54beb5d04c4d48e34234R446. 355 | "--noerrdialogs", 356 | "--hide-scrollbars", 357 | "--allow-running-insecure-content", 358 | "--autoplay-policy=user-gesture-required", 359 | "--run-all-compositor-stages-before-draw", 360 | "--log-level=3", 361 | "--font-render-hinting=none", 362 | "--block-new-web-contents", 363 | "--no-subproc-heap-profiling", 364 | "--use-fake-device-for-media-stream", 365 | "--use-fake-ui-for-media-stream", 366 | "--no-pre-read-main-dll", 367 | "--ip-protection-proxy-opt-out", 368 | "--unsafely-disable-devtools-self-xss-warning", 369 | "--metrics-recording-only", 370 | "--use-mock-keychain", 371 | "--force-color-profile=srgb", 372 | "--disable-infobars", 373 | "--mute-audio", 374 | "--no-service-autorun", 375 | "--password-store=basic", 376 | "--export-tagged-pdf", 377 | "--no-pings", 378 | "--rusty-png", 379 | "--window-size=800,600", 380 | &crate::render_conf::RENDER_PROCESS_LIMIT, 381 | // --deterministic-mode 10-20% drop in perf 382 | // "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4", 383 | "--enable-features=Vulkan,PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess", 384 | "--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate", 385 | // put these args on the same command for now to prevent empty args cross-platform execution. The args will be one less on gpu enabled builds. 386 | "--enable-unsafe-swiftshader", 387 | "--use-angle=swiftshader" 388 | ] 389 | }; 390 | } 391 | 392 | #[cfg(feature = "physical_gpu")] 393 | lazy_static::lazy_static! { 394 | /// The chrome args to use. 395 | pub static ref CHROME_ARGS: [&'static str; PERF_ARGS] = { 396 | let headless = std::env::args() 397 | .nth(6) 398 | .unwrap_or("true".into()); 399 | 400 | let headless = match (headless != "false", std::env::var("HEADLESS").as_deref()) { 401 | (false, _) | (true, Ok("false")) => "--test-type=gpu", 402 | (_, Ok("new")) => "--headless=new", 403 | _ => "--headless", 404 | }; 405 | 406 | let port = if DEFAULT_PORT.eq(&9223) { 407 | "--remote-debugging-port=9223" 408 | } else if DEFAULT_PORT.eq(&9224) { 409 | "--remote-debugging-port=9224" 410 | } else { 411 | "--remote-debugging-port=9222" 412 | }; 413 | let use_gl = "--use-gl=angle"; 414 | 415 | [ 416 | // *SPECIAL* 417 | "--remote-debugging-address=0.0.0.0", 418 | port, 419 | // *SPECIAL* 420 | headless, 421 | "--enable-gpu", 422 | "--enable-gpu-sandbox", 423 | "--enable-webgl", 424 | "--enable-webgl2-compute-context", 425 | "--enable-webgl-draft-extensions", 426 | "--enable-unsafe-webgpu", 427 | use_gl, 428 | "--no-first-run", 429 | "--no-sandbox", 430 | "--disable-setuid-sandbox", 431 | "--no-zygote", 432 | "--enable-async-dns", 433 | "--hide-scrollbars", 434 | "--user-data-dir=~/.config/google-chrome", 435 | "--allow-running-insecure-content", 436 | "--autoplay-policy=user-gesture-required", 437 | "--ignore-certificate-errors", 438 | "--no-default-browser-check", 439 | "--disable-dev-shm-usage", // required or else container will crash not enough memory 440 | "--disable-threaded-scrolling", 441 | "--disable-cookie-encryption", 442 | "--disable-demo-mode", 443 | "--disable-dinosaur-easter-egg", 444 | "--disable-fetching-hints-at-navigation-start", 445 | "--disable-site-isolation-trials", 446 | "--disable-threaded-animation", 447 | "--disable-sync", 448 | "--disable-print-preview", 449 | "--disable-search-engine-choice-screen", 450 | "--disable-in-process-stack-traces", 451 | "--disable-low-res-tiling", 452 | "--disable-oobe-chromevox-hint-timer-for-testing", 453 | "--disable-smooth-scrolling", 454 | "--disable-prompt-on-repost", 455 | "--disable-domain-reliability", 456 | "--enable-web-bluetooth", 457 | "--enable-dom-distiller", 458 | "--enable-distillability-service", 459 | "--enable-surface-synchronization", 460 | "--disable-gesture-typing", 461 | "--disable-background-timer-throttling", 462 | "--disable-breakpad", 463 | "--disable-crash-reporter", 464 | "--disable-asynchronous-spellchecking", 465 | "--disable-html5-camera", 466 | "--noerrdialogs", 467 | "--disable-hang-monitor", 468 | "--disable-checker-imaging", 469 | "--disable-image-animation-resync", 470 | "--disable-client-side-phishing-detection", 471 | "--disable-component-extensions-with-background-pages", 472 | "--run-all-compositor-stages-before-draw", 473 | "--disable-background-networking", 474 | "--disable-renderer-backgrounding", 475 | "--disable-field-trial-config", 476 | "--disable-back-forward-cache", 477 | "--disable-backgrounding-occluded-windows", 478 | "--log-level=3", 479 | "--enable-logging=stderr", 480 | "--font-render-hinting=none", 481 | "--block-new-web-contents", 482 | "--no-subproc-heap-profiling", 483 | "--use-fake-device-for-media-stream", 484 | "--use-fake-ui-for-media-stream", 485 | "--no-pre-read-main-dll", 486 | "--disable-stack-profiler", 487 | "--disable-libassistant-logfile", 488 | "--ip-protection-proxy-opt-out", 489 | "--unsafely-disable-devtools-self-xss-warning", 490 | "--enable-features=Vulkan,PdfOopif,SharedArrayBuffer,NetworkService,NetworkServiceInProcess", 491 | "--metrics-recording-only", 492 | "--use-mock-keychain", 493 | "--force-color-profile=srgb", 494 | "--disable-infobars", 495 | "--mute-audio", 496 | "--disable-datasaver-prompt", 497 | "--no-service-autorun", 498 | "--password-store=basic", 499 | "--export-tagged-pdf", 500 | "--no-pings", 501 | "--rusty-png", 502 | "--disable-histogram-customizer", 503 | "--window-size=800,600", 504 | "--disable-vulkan-fallback-to-gl-for-testing", 505 | "--disable-vulkan-surface", 506 | "--disable-webrtc", 507 | "--disable-oopr-debug-crash-dump", 508 | "--disable-pnacl-crash-throttling", 509 | "--disable-renderer-accessibility", 510 | &crate::render_conf::RENDER_PROCESS_LIMIT, 511 | "--disable-pushstate-throttle", 512 | "--disable-blink-features=AutomationControlled", 513 | "--disable-ipc-flooding-protection", // we do not need to throttle navigation for https://github.com/spider-rs/spider/commit/9ff5bbd7a2656b8edb84b62843b72ae9d09af079#diff-75ce697faf0d37c3dff4a3a19e7524798b3cb5487f8f54beb5d04c4d48e34234R446. 514 | // --deterministic-mode 10-20% drop in perf 515 | // "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4", 516 | "--disable-features=PaintHolding,HttpsUpgrades,DeferRendererTasksAfterInput,LensOverlay,ThirdPartyStoragePartitioning,IsolateSandboxedIframes,ProcessPerSiteUpToMainFrameThreshold,site-per-process,WebUIJSErrorReportingExtended,DIPS,InterestFeedContentSuggestions,PrivacySandboxSettings4,AutofillServerCommunication,CalculateNativeWinOcclusion,OptimizationHints,AudioServiceOutOfProcess,IsolateOrigins,ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate", 517 | ] 518 | }; 519 | } 520 | 521 | /// Get the default chrome bin location per OS. 522 | fn get_default_chrome_bin() -> &'static str { 523 | let brave = match std::env::var("BRAVE_ENABLED") { 524 | Ok(v) => v == "true", 525 | _ => false, 526 | }; 527 | 528 | if cfg!(target_os = "windows") { 529 | if brave { 530 | "brave-browser.exe" 531 | } else { 532 | "chrome.exe" 533 | } 534 | } else if cfg!(target_os = "macos") { 535 | if brave { 536 | "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" 537 | } else { 538 | "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" 539 | } 540 | } else if cfg!(target_os = "linux") { 541 | if brave { 542 | "brave-browser" 543 | } else { 544 | "chromium" 545 | } 546 | } else { 547 | if brave { 548 | "brave" 549 | } else { 550 | "chrome" 551 | } 552 | } 553 | } 554 | -------------------------------------------------------------------------------- /headless_browser_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use cached::proc_macro::once; 2 | 3 | /// Chrome configuration. 4 | pub mod conf; 5 | /// Chrome json modifiers. 6 | mod modify; 7 | /// Proxy forwarder TCP to chrome instances. 8 | pub mod proxy; 9 | /// Chrome renderer configuration. 10 | mod render_conf; 11 | 12 | use conf::{ 13 | CACHEABLE, CHROME_ADDRESS, CHROME_ARGS, CHROME_INSTANCES, CHROME_PATH, DEBUG_JSON, 14 | DEFAULT_PORT, DEFAULT_PORT_SERVER, ENDPOINT, HOST_NAME, IS_HEALTHY, LAST_CACHE, 15 | LIGHTPANDA_ARGS, LIGHT_PANDA, TARGET_REPLACEMENT, 16 | }; 17 | use core::sync::atomic::Ordering; 18 | use http_body_util::Full; 19 | use hyper::{ 20 | body::{Bytes, Incoming}, 21 | server::conn::http1, 22 | service::service_fn, 23 | Method, Request, Response, StatusCode, 24 | }; 25 | use hyper_util::rt::TokioIo; 26 | use std::convert::Infallible; 27 | use std::net::SocketAddr; 28 | use std::process::Command; 29 | use tokio::{ 30 | net::{TcpListener, TcpStream}, 31 | signal, 32 | }; 33 | 34 | use std::time::Duration; 35 | use tokio::time::{sleep, timeout}; 36 | 37 | /// Empty default response without a 'webSocketDebuggerUrl'. 38 | const EMPTY_RESPONSE: Bytes = Bytes::from_static( 39 | br#"{ 40 | "Browser": "", 41 | "Protocol-Version": "", 42 | "User-Agent": "", 43 | "V8-Version": "", 44 | "WebKit-Version": "", 45 | "webSocketDebuggerUrl": "" 46 | }"#, 47 | ); 48 | 49 | /// Attempt the connection. 50 | async fn connect_with_retries(address: &str) -> Option { 51 | let mut attempts = 0; 52 | let mut connection_failed = false; 53 | 54 | loop { 55 | // 15s is the default restart timeout chrome flag. 56 | match timeout(Duration::from_secs(15), TcpStream::connect(address)).await { 57 | Ok(Ok(stream)) => return Some(stream), 58 | Ok(Err(e)) => { 59 | attempts += 1; 60 | // connection refused means the instance is not alive. 61 | if !e.kind().eq(&std::io::ErrorKind::ConnectionRefused) { 62 | tracing::warn!("Failed to connect: {}. Attempt {} of 20", e, attempts); 63 | } else { 64 | if !connection_failed { 65 | connection_failed = true; 66 | } 67 | // empty prevent connections retrying 68 | if attempts >= 10 && CHROME_INSTANCES.is_empty() { 69 | tracing::warn!("ConnectionRefused: {}. Attempt {} of 8", e, attempts); 70 | return None; 71 | } 72 | } 73 | } 74 | Err(_) => { 75 | attempts += 1; 76 | tracing::error!("Connection attempt timed out. Attempt {} of 20", attempts); 77 | } 78 | } 79 | 80 | if attempts >= 20 { 81 | return None; 82 | } 83 | 84 | let rng = rand::random_range(if connection_failed { 85 | 80..=150 86 | } else { 87 | 150..=250 88 | }); 89 | 90 | sleep(Duration::from_millis(rng)).await; 91 | } 92 | } 93 | 94 | /// Shutdown the chrome instance by process id. 95 | #[cfg(target_os = "windows")] 96 | pub fn shutdown(pid: &u32) { 97 | let _ = Command::new("taskkill") 98 | .args(["/PID", &pid.to_string(), "/F"]) 99 | .spawn(); 100 | } 101 | 102 | /// Shutdown the chrome instance by process id. 103 | #[cfg(not(target_os = "windows"))] 104 | pub fn shutdown(pid: &u32) { 105 | let _ = Command::new("kill").args(["-9", &pid.to_string()]).spawn(); 106 | } 107 | 108 | #[cfg(test)] 109 | /// Arguments to test headless without any extra args. Only applies during 'cargo test'. 110 | pub fn get_chrome_args_test() -> [&'static str; 6] { 111 | *crate::conf::CHROME_ARGS_TEST 112 | } 113 | 114 | #[cfg(not(test))] 115 | /// Arguments to test headless without any extra args. Only applies during 'cargo test'. 116 | pub fn get_chrome_args_test() -> [&'static str; crate::conf::PERF_ARGS] { 117 | *crate::conf::CHROME_ARGS 118 | } 119 | 120 | /// Fork a chrome process. 121 | pub fn fork(port: Option) -> String { 122 | let id = if !*LIGHT_PANDA { 123 | let mut command = Command::new(&*CHROME_PATH); 124 | 125 | let cmd = if *crate::conf::TEST_NO_ARGS { 126 | let mut chrome_args = get_chrome_args_test().map(|e| e.to_string()); 127 | if !CHROME_ADDRESS.is_empty() { 128 | chrome_args[0] = 129 | format!("--remote-debugging-address={}", &CHROME_ADDRESS.to_string()); 130 | } 131 | if let Some(port) = port { 132 | chrome_args[1] = format!("--remote-debugging-port={}", &port.to_string()); 133 | } 134 | command.args(&chrome_args) 135 | } else { 136 | let mut chrome_args = CHROME_ARGS.map(|e| e.to_string()); 137 | 138 | if !CHROME_ADDRESS.is_empty() { 139 | chrome_args[0] = 140 | format!("--remote-debugging-address={}", &CHROME_ADDRESS.to_string()); 141 | } 142 | 143 | if let Some(port) = port { 144 | chrome_args[1] = format!("--remote-debugging-port={}", &port.to_string()); 145 | } 146 | 147 | command.args(&chrome_args) 148 | }; 149 | 150 | let id = match cmd.spawn() { 151 | Ok(child) => { 152 | let cid = child.id(); 153 | tracing::info!("Chrome PID: {}", cid); 154 | cid 155 | } 156 | Err(e) => { 157 | tracing::error!("{} command didn't start {:?}", &*CHROME_PATH, e); 158 | 0 159 | } 160 | }; 161 | 162 | id 163 | } else { 164 | let panda_args = LIGHTPANDA_ARGS.map(|e| e.to_string()); 165 | let mut command = Command::new(&*CHROME_PATH); 166 | 167 | let host = panda_args[0].replace("--host=", ""); 168 | let port = panda_args[1].replace("--port=", ""); 169 | 170 | let id = if let Ok(child) = command 171 | .args(["--port", &port]) 172 | .args(["--host", &host]) 173 | .spawn() 174 | { 175 | let cid = child.id(); 176 | 177 | tracing::info!("Chrome PID: {}", cid); 178 | 179 | cid 180 | } else { 181 | tracing::error!("chrome command didn't start"); 182 | 0 183 | }; 184 | 185 | id 186 | }; 187 | 188 | CHROME_INSTANCES.insert(id.into()); 189 | 190 | id.to_string() 191 | } 192 | 193 | /// Get json endpoint for chrome instance proxying. 194 | async fn version_handler_bytes_base(endpoint_path: Option<&str>) -> Option { 195 | use http_body_util::BodyExt; 196 | 197 | let url = endpoint_path 198 | .unwrap_or(&ENDPOINT.as_str()) 199 | .parse::() 200 | .expect("valid chrome endpoint"); 201 | 202 | let req = Request::builder() 203 | .method(Method::GET) 204 | .uri("/json/version") 205 | .header( 206 | hyper::header::HOST, 207 | url.authority() 208 | .map_or_else(|| "localhost".to_string(), |f| f.as_str().to_string()), 209 | ) 210 | .header(hyper::header::CONTENT_TYPE, "application/json") 211 | .body(http_body_util::Empty::::new()) 212 | .expect("Failed to build the request"); 213 | 214 | let host = url.host().expect("uri has no host"); 215 | let port = url.port_u16().unwrap_or(80); 216 | 217 | let address = format!("{}:{}", host, port); 218 | 219 | let resp = if let Some(stream) = connect_with_retries(&address).await { 220 | let io = TokioIo::new(stream); 221 | 222 | if let Ok((mut client, conn)) = hyper::client::conn::http1::handshake(io).await { 223 | tokio::task::spawn(async move { 224 | if let Err(err) = conn.await { 225 | tracing::error!("Connection failed: {:?}", err); 226 | } 227 | }); 228 | 229 | match client.send_request(req).await { 230 | Ok(mut resp) => { 231 | IS_HEALTHY.store(true, Ordering::Relaxed); 232 | 233 | let mut bytes_mut = vec![]; 234 | 235 | while let Some(next) = resp.frame().await { 236 | if let Ok(frame) = next { 237 | if let Some(chunk) = frame.data_ref() { 238 | bytes_mut.extend(chunk); 239 | } 240 | } 241 | } 242 | 243 | if !HOST_NAME.is_empty() { 244 | let body = modify::modify_json_output(bytes_mut.into()); 245 | Some(body) 246 | } else { 247 | Some(bytes_mut.into()) 248 | } 249 | } 250 | _ => { 251 | IS_HEALTHY.store(false, Ordering::Relaxed); 252 | None 253 | } 254 | } 255 | } else { 256 | None 257 | } 258 | } else { 259 | None 260 | }; 261 | 262 | resp 263 | } 264 | 265 | /// Get json endpoint for chrome instance proxying. 266 | #[once(option = true, sync_writes = true, time = 10)] 267 | async fn version_handler_bytes(endpoint_path: Option<&str>) -> Option { 268 | version_handler_bytes_base(endpoint_path).await 269 | } 270 | 271 | /// Health check handler 272 | async fn health_check_handler() -> Result>, Infallible> { 273 | if IS_HEALTHY.load(Ordering::Relaxed) { 274 | Ok(Response::new(Full::new(Bytes::from("healthy")))) 275 | } else { 276 | let mut response = Response::new(Full::new(Bytes::from("unhealthy"))); 277 | *response.status_mut() = StatusCode::SERVICE_UNAVAILABLE; 278 | 279 | Ok(response) 280 | } 281 | } 282 | 283 | /// Fork handler. 284 | async fn fork_handler(port: Option) -> Result>, Infallible> { 285 | let pid = fork(port); 286 | let pid = format!("Forked process with pid: {}", pid); 287 | 288 | Ok(Response::new(Full::new(Bytes::from(pid)))) 289 | } 290 | 291 | /// Json version handler. 292 | async fn json_version_handler( 293 | endpoint_path: Option<&str>, 294 | ) -> Result>, Infallible> { 295 | let mut attempts = 0; 296 | let mut body: Option = None; 297 | let mut checked_empty = false; 298 | 299 | // check if the instances are alive. 300 | while attempts < 10 && body.is_none() && !CHROME_INSTANCES.is_empty() { 301 | body = if CACHEABLE.load(Ordering::Relaxed) { 302 | version_handler_bytes(endpoint_path).await 303 | } else { 304 | version_handler_bytes_base(endpoint_path).await 305 | }; 306 | 307 | if body.is_none() { 308 | // check the first instance. 309 | if !checked_empty { 310 | checked_empty = true; 311 | if CHROME_INSTANCES.is_empty() { 312 | break; 313 | } 314 | } 315 | attempts += 1; 316 | 317 | let rng = rand::random_range(if checked_empty { 10..=50 } else { 20..=80 }); 318 | 319 | tokio::time::sleep(Duration::from_millis(rng)).await; 320 | } 321 | } 322 | 323 | let empty = body.is_none(); 324 | let body = body.unwrap_or_else(|| EMPTY_RESPONSE); 325 | 326 | if *DEBUG_JSON { 327 | tracing::info!("{:?}", body); 328 | } 329 | 330 | let mut resp = Response::new(Full::new(body)); 331 | 332 | resp.headers_mut().insert( 333 | hyper::header::CONTENT_TYPE, 334 | hyper::header::HeaderValue::from_static("application/json"), // body has to be json or parse will fail. 335 | ); 336 | 337 | if empty { 338 | *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; 339 | } 340 | 341 | Ok(resp) 342 | } 343 | 344 | /// Shutdown all the chrome instances launched. 345 | pub async fn shutdown_instances() { 346 | for pid in CHROME_INSTANCES.iter() { 347 | shutdown(&pid); 348 | } 349 | CHROME_INSTANCES.clear(); 350 | CACHEABLE.store(false, std::sync::atomic::Ordering::Relaxed); 351 | } 352 | 353 | /// Shutdown handler. 354 | async fn shutdown_handler() -> Result>, Infallible> { 355 | shutdown_instances().await; 356 | 357 | Ok(Response::new(Full::new(Bytes::from( 358 | "Shutdown successful.", 359 | )))) 360 | } 361 | 362 | /// Request handler. 363 | async fn request_handler(req: Request) -> Result>, Infallible> { 364 | match (req.method(), req.uri().path()) { 365 | (&Method::GET, "/health") => health_check_handler().await, 366 | (&Method::GET, "/") => health_check_handler().await, 367 | (&Method::POST, "/fork") => fork_handler(None).await, 368 | (&Method::POST, path) if path.starts_with("/fork/") => { 369 | if let Some(port) = path.split('/').nth(2) { 370 | if let Ok(port) = port.parse::() { 371 | fork_handler(Some(port)).await 372 | } else { 373 | let message = Response::new(Full::new(Bytes::from("Invalid port argument"))); 374 | 375 | Ok(message) 376 | } 377 | } else { 378 | let message = Response::new(Full::new(Bytes::from("Invalid path"))); 379 | 380 | Ok(message) 381 | } 382 | } 383 | // we only care about the main /json/version for 9223 for the proxy forwarder. 384 | (&Method::GET, "/json/version") => json_version_handler(None).await, 385 | (&Method::POST, "/shutdown") => shutdown_handler().await, 386 | _ => { 387 | let mut resp = Response::new(Full::new(Bytes::from("Not Found"))); 388 | 389 | *resp.status_mut() = StatusCode::NOT_FOUND; 390 | 391 | Ok(resp) 392 | } 393 | } 394 | } 395 | 396 | /// Launch chrome, start the server, and proxy for management. 397 | pub async fn run_main() -> Result<(), Box> { 398 | let auto_start = std::env::args().nth(3).unwrap_or_else(|| { 399 | if std::env::var("CHROME_INIT").unwrap_or("true".into()) == "true" { 400 | "init".into() 401 | } else { 402 | "ignore".into() 403 | } 404 | }); 405 | 406 | if auto_start == "init" { 407 | fork(Some(*DEFAULT_PORT)); 408 | } 409 | 410 | let addr = SocketAddr::new( 411 | std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), 412 | *DEFAULT_PORT_SERVER, 413 | ); 414 | 415 | let listener = TcpListener::bind(addr).await.expect("connection"); 416 | 417 | let make_svc = async move { 418 | let builder_options = std::sync::Arc::new( 419 | http1::Builder::new() 420 | .preserve_header_case(true) 421 | .title_case_headers(true) 422 | .header_read_timeout(None) 423 | .half_close(true) 424 | .auto_date_header(false) 425 | .to_owned(), 426 | ); 427 | 428 | loop { 429 | if let Ok((tcp, _)) = listener.accept().await { 430 | let builder_options = builder_options.clone(); 431 | 432 | tokio::task::spawn(async move { 433 | let io = TokioIo::new(tcp); 434 | if let Err(err) = builder_options 435 | .serve_connection(io, service_fn(request_handler)) 436 | .await 437 | { 438 | eprintln!("Error serving connection: {:?}", err); 439 | } 440 | }); 441 | } 442 | } 443 | }; 444 | 445 | println!( 446 | "Chrome server running on {}:{}", 447 | if CHROME_ADDRESS.is_empty() { 448 | "localhost" 449 | } else { 450 | &CHROME_ADDRESS 451 | }, 452 | DEFAULT_PORT_SERVER.to_string() 453 | ); 454 | 455 | tokio::select! { 456 | _ = make_svc => Ok(()), 457 | _ = crate::proxy::proxy::run_proxy() => Ok(()), 458 | _ = signal::ctrl_c() => Ok(()), 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /headless_browser_lib/src/modify.rs: -------------------------------------------------------------------------------- 1 | use hyper::body::Bytes; 2 | 3 | /// modify the json output for the bytes hosting. The headless instance cannot accept external request so we use the proxy. 4 | pub(crate) fn modify_json_output(body_bytes: Bytes) -> Bytes { 5 | let buffer = body_bytes.as_ref(); 6 | let target_host = b"127.0.0.1"; 7 | let replacement_host = crate::HOST_NAME.as_bytes(); 8 | 9 | let (target_port, replacement_port) = *crate::TARGET_REPLACEMENT; 10 | 11 | // Estimate a suitable capacity 12 | let mut modified_buffer = 13 | Vec::with_capacity(buffer.len() + (replacement_host.len() - target_host.len())); 14 | 15 | let mut start = 0; 16 | 17 | // Replace occurrences of the target host 18 | while let Some(pos) = buffer[start..] 19 | .windows(target_host.len()) 20 | .position(|window| window == target_host) 21 | { 22 | modified_buffer.extend_from_slice(&buffer[start..start + pos]); 23 | modified_buffer.extend_from_slice(replacement_host); 24 | start += pos + target_host.len(); 25 | } 26 | modified_buffer.extend_from_slice(&buffer[start..]); 27 | 28 | // Now handle the port replacement 29 | let mut final_buffer = 30 | Vec::with_capacity(modified_buffer.len() + (replacement_port.len() - target_port.len())); 31 | start = 0; 32 | 33 | while let Some(pos) = modified_buffer[start..] 34 | .windows(target_port.len()) 35 | .position(|window| window == target_port) 36 | { 37 | final_buffer.extend_from_slice(&modified_buffer[start..start + pos]); 38 | final_buffer.extend_from_slice(replacement_port); 39 | start += pos + target_port.len(); 40 | } 41 | final_buffer.extend_from_slice(&modified_buffer[start..]); 42 | 43 | final_buffer.into() 44 | } 45 | -------------------------------------------------------------------------------- /headless_browser_lib/src/proxy.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod proxy { 2 | use crate::conf::{BUFFER_SIZE, ENTRY, TARGET, TEN_SECONDS}; 3 | use crate::{connect_with_retries, fork, shutdown_instances, CACHEABLE, LAST_CACHE}; 4 | use std::{io::ErrorKind, time::Instant}; 5 | use tokio::{ 6 | io::{AsyncReadExt, AsyncWriteExt}, 7 | net::{TcpListener, TcpStream}, 8 | }; 9 | 10 | /// Run the proxy forwarder for chrome. This allows connecting to chrome outside of the network. 11 | pub async fn run_proxy() -> std::io::Result<()> { 12 | let listener = TcpListener::bind(*ENTRY).await?; 13 | println!("Proxy Listening on {}", *ENTRY); 14 | let base_time = Instant::now(); 15 | 16 | loop { 17 | let (mut client_stream, client_addr) = listener.accept().await?; 18 | tracing::info!("Accepted connection from {}", client_addr); 19 | 20 | tokio::spawn(async move { 21 | let mut should_retry = false; 22 | 23 | if let Err(err) = handle_connection(&mut client_stream).await { 24 | if err.kind() == ErrorKind::NotConnected || err.kind() == ErrorKind::Other { 25 | should_retry = true; 26 | tracing::error!("Error handling connection: {}. Restarting Chrome.", err); 27 | // send a signal instead or channel to prevent race 28 | // todo: we need to swap to a new port and use a LB to track the drain to reset the ports used. 29 | shutdown_instances().await; 30 | fork(Some(*crate::DEFAULT_PORT)); 31 | CACHEABLE.store(false, std::sync::atomic::Ordering::Relaxed); 32 | LAST_CACHE.store( 33 | base_time.elapsed().as_secs().try_into().unwrap_or_default(), 34 | std::sync::atomic::Ordering::Relaxed, 35 | ); 36 | } else { 37 | // ignore connection resets by peer 38 | if err.kind() != ErrorKind::ConnectionReset { 39 | tracing::error!("Error handling connection: {}", err); 40 | } 41 | } 42 | } else if !CACHEABLE.load(std::sync::atomic::Ordering::Relaxed) { 43 | let elasped = LAST_CACHE.load(std::sync::atomic::Ordering::Relaxed); 44 | 45 | if elasped > 0 { 46 | let elapsed_since_base = base_time.elapsed(); 47 | let total_elapsed = 48 | tokio::time::Duration::from_secs(elasped) + elapsed_since_base; 49 | 50 | if total_elapsed >= *TEN_SECONDS { 51 | CACHEABLE.store(true, std::sync::atomic::Ordering::Relaxed); 52 | } 53 | } 54 | } 55 | 56 | if should_retry { 57 | tokio::task::yield_now().await; 58 | let _ = handle_connection(&mut client_stream).await; 59 | } 60 | }); 61 | } 62 | } 63 | 64 | /// Handle the proxy connection. 65 | async fn handle_connection(client_stream: &mut TcpStream) -> std::io::Result<()> { 66 | let server_stream: Option = connect_with_retries(*TARGET).await; 67 | 68 | if let Some(mut server_stream) = server_stream { 69 | let buffer_size = *BUFFER_SIZE; 70 | let mut buf1 = vec![0u8; buffer_size]; 71 | let mut buf2 = vec![0u8; buffer_size]; 72 | 73 | loop { 74 | tokio::select! { 75 | a = server_stream.read(&mut buf1) => { 76 | let size = match a { 77 | Ok(p) => p, 78 | Err(_) => break, 79 | }; 80 | if size == 0 { 81 | break; 82 | } 83 | if let Err(_) = client_stream.write_all(&buf1[..size]).await { 84 | break; 85 | } 86 | }, 87 | b = client_stream.read(&mut buf2) => { 88 | let size = match b { 89 | Ok(p) => p, 90 | Err(_) => break, 91 | }; 92 | if size == 0 { 93 | break; 94 | } 95 | if let Err(_) = server_stream.write_all(&buf2[..size]).await { 96 | break; 97 | } 98 | }, 99 | else => { 100 | break; 101 | } 102 | } 103 | } 104 | 105 | Ok(()) 106 | } else { 107 | Err(std::io::Error::new( 108 | std::io::ErrorKind::Other, 109 | "Failed to connect after several attempts", 110 | )) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /headless_browser_lib/src/render_conf.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::sync::LazyLock; 3 | use sysinfo::System; 4 | 5 | /// Calculate the render process limits. 6 | fn calculate_max_renderer_process_hosts() -> u64 { 7 | const ESTIMATED_WEB_CONTENTS_MEMORY_USAGE_MB: u64 = if cfg!(target_pointer_width = "64") { 8 | 85 9 | } else { 10 | 60 11 | }; 12 | 13 | const MIN_RENDERER_PROCESS_COUNT: u64 = 3; 14 | 15 | let max_renderer_process_count_platform: u64 = get_platform_max_renderer_process_count(); 16 | 17 | let sys = System::new_all(); 18 | let total_memory_mb = sys.total_memory() / 1024; // Convert KB to MB 19 | 20 | let mut max_count = total_memory_mb / 2; 21 | 22 | max_count /= ESTIMATED_WEB_CONTENTS_MEMORY_USAGE_MB; 23 | max_count = cmp::max(max_count, MIN_RENDERER_PROCESS_COUNT); 24 | max_count = cmp::min(max_count, max_renderer_process_count_platform); 25 | 26 | max_count 27 | } 28 | 29 | /// The platform max render count. 30 | fn get_platform_max_renderer_process_count() -> u64 { 31 | let platform_limit = get_platform_process_limit(); 32 | if platform_limit != u64::MAX { 33 | platform_limit / 2 34 | } else { 35 | 82 36 | } 37 | } 38 | 39 | /// Platform render process limit. 40 | fn get_platform_process_limit() -> u64 { 41 | (num_cpus::get() * 10).try_into().unwrap_or(82) 42 | } 43 | 44 | /// The renderer process limit. 45 | pub(crate) static RENDER_PROCESS_LIMIT: LazyLock = LazyLock::new(|| { 46 | format!( 47 | "--renderer-process-limit={}", 48 | calculate_max_renderer_process_hosts() 49 | ) 50 | }); 51 | -------------------------------------------------------------------------------- /local.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sans-serif 7 | 8 | Main sans-serif font name goes here 9 | Noto Color Emoji 10 | Noto Emoji 11 | 12 | 13 | 14 | 15 | serif 16 | 17 | Main serif font name goes here 18 | Noto Color Emoji 19 | Noto Emoji 20 | 21 | 22 | 23 | 24 | monospace 25 | 26 | Main monospace font name goes here 27 | Noto Color Emoji 28 | Noto Emoji 29 | 30 | 31 | -------------------------------------------------------------------------------- /scripts/build-base.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | OUT=$SRC/out 6 | SRCDIR= 7 | ATTEMPTS=10 8 | JOBS=$((`nproc` + 2)) 9 | JOBFAIL=30 10 | DRYRUN= 11 | UPDATE= 12 | CHANNELS=() 13 | TARGETS=() 14 | PUSH= 15 | IMAGE=docker.io/spiderrust/headless-shell 16 | URL='https://hub.docker.com/layers/chromedp/headless-shell/%s/images/sha256-%s?context=explore' 17 | 18 | OPTIND=1 19 | while getopts "o:s:a:j:k:nuc:t:pi:l:" opt; do 20 | case "$opt" in 21 | o) OUT=$OPTARG ;; 22 | s) SRCDIR=$OPTARG ;; 23 | a) ATTEMPTS=$OPTARG ;; 24 | j) JOBS=$OPTARG ;; 25 | k) JOBFAIL=$OPTARG ;; 26 | n) DRYRUN=-n ;; 27 | u) UPDATE=-u ;; 28 | c) CHANNELS+=($OPTARG) ;; 29 | t) TARGETS+=($OPTARG) ;; 30 | p) PUSH=-p ;; 31 | i) IMAGE=$OPTARG ;; 32 | l) URL=$OPTARG ;; 33 | esac 34 | done 35 | 36 | # determine source dir 37 | if [ -z "$SRCDIR" ]; then 38 | if [ -d /media/src ]; then 39 | SRCDIR=/media/src 40 | else 41 | SRCDIR=$OUT 42 | fi 43 | fi 44 | 45 | set -e 46 | 47 | # determine channels 48 | if [ ${#CHANNELS[@]} -eq 0 ]; then 49 | CHANNELS=(stable beta dev) 50 | fi 51 | 52 | # determine targets 53 | # if [ ${#TARGETS[@]} -eq 0 ]; then 54 | # TARGETS=(amd64 arm64) 55 | # fi 56 | 57 | if [ ${#TARGETS[@]} -eq 0 ]; then 58 | TARGETS=(arm64) 59 | fi 60 | 61 | echo "------------------------------------------------------------" 62 | echo "STARTING ($(date))" 63 | 64 | # determine versions 65 | declare -A VERSIONS 66 | for CHANNEL in ${CHANNELS[@]}; do 67 | VERSIONS[$CHANNEL]=$(verhist -platform win64 -channel "$CHANNEL" -latest) 68 | done 69 | 70 | # order channels low -> high 71 | CHANNELS_ORDER=$( 72 | for i in ${!VERSIONS[@]}; do 73 | echo "${VERSIONS[$i]}:::$i" 74 | done | sort -V | awk -F::: '{print $2}' |xargs 75 | ) 76 | 77 | # join_by ',' ${A[@]} ${B[@]} 78 | join_by() { 79 | local d=${1-} f=${2-} 80 | if shift 2; then 81 | printf %s "$f" "${@/#/$d}" 82 | fi 83 | } 84 | 85 | channels() { 86 | local s=() 87 | for CHANNEL in $CHANNELS_ORDER; do 88 | s+=("$CHANNEL:${VERSIONS[$CHANNEL]}") 89 | done 90 | join_by ' ' ${s[@]} 91 | } 92 | 93 | # display builds 94 | echo "BUILDING: $(channels) [${TARGETS[@]}]" 95 | 96 | echo -e "\n\nCLEANUP ($(date))" 97 | $SRC/cleanup.sh \ 98 | -o "$OUT" \ 99 | -i "$IMAGE" \ 100 | -c $(join_by ' -c ' ${CHANNELS[@]}) \ 101 | -v $(join_by ' -v ' ${VERSIONS[@]}) 102 | echo "ENDED CLEANUP ($(date))" 103 | 104 | # build 105 | for CHANNEL in $CHANNELS_ORDER; do 106 | VERSION=${VERSIONS[$CHANNEL]} 107 | 108 | # skip build if archive already exists 109 | if [[ -f $OUT/headless-shell-$VERSION-amd64.tar.bz2 && -f $OUT/headless-shell-$VERSION-arm64.tar.bz2 ]]; then 110 | echo -e "\n\nSKIPPING BUILD FOR CHANNEL $CHANNEL $VERSION ($(date))" 111 | continue 112 | fi 113 | 114 | # build 115 | echo -e "\n\nSTARTING BUILD FOR CHANNEL $CHANNEL $VERSION ($(date))" 116 | RET=1 117 | $SRC/build-headless-shell.sh \ 118 | -o $OUT \ 119 | -s $SRCDIR \ 120 | -c $CHANNEL \ 121 | -a $ATTEMPTS \ 122 | -j $JOBS \ 123 | -k $JOBFAIL \ 124 | $DRYRUN \ 125 | $UPDATE \ 126 | -t $(join_by ' -t ' ${TARGETS[@]}) \ 127 | -v $VERSION \ 128 | && RET=$? 129 | if [ $RET -ne 0 ]; then 130 | echo "COULD NOT BUILD $CHANNEL $VERSION ($(date))" 131 | fi 132 | echo "ENDED BUILD FOR $CHANNEL $VERSION ($(date))" 133 | done 134 | 135 | # build images 136 | for CHANNEL in $CHANNELS_ORDER; do 137 | VERSION=${VERSIONS[$CHANNEL]} 138 | TAGS=($CHANNEL) 139 | if [ "$CHANNEL" = "stable" ]; then 140 | TAGS+=(latest) 141 | fi 142 | echo -e "\n\nSTARTING IMAGE BUILD FOR CHANNEL $CHANNEL $VERSION ($(date))" 143 | $SRC/build-image.sh \ 144 | -o $OUT \ 145 | -t $(join_by ' -t ' ${TARGETS[@]}) \ 146 | -g $(join_by ' -g ' ${TAGS[@]}) \ 147 | -v "$VERSION" \ 148 | -i "$IMAGE" \ 149 | $PUSH 150 | echo "ENDED IMAGE BUILD FOR CHANNEL $CHANNEL $VERSION ($(date))" 151 | done 152 | 153 | echo "DONE ($(date))" -------------------------------------------------------------------------------- /scripts/build-headless-shell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | OUT= 6 | SRCDIR= 7 | CHANNEL=stable 8 | ATTEMPTS=10 9 | JOBS=$((`nproc` + 2)) 10 | JOBFAIL=30 11 | DRYRUN= 12 | TTL=64800 13 | UPDATE=0 14 | TARGETS=() 15 | VERSION= 16 | 17 | OPTIND=1 18 | while getopts "o:s:c:a:j:k:nl:ut:v:" opt; do 19 | case "$opt" in 20 | o) OUT=$OPTARG ;; 21 | s) SRCDIR=$OPTARG ;; 22 | c) CHANNEL=$OPTARG ;; 23 | a) ATTEMPTS=$OPTARG ;; 24 | j) JOBS=$OPTARG ;; 25 | k) JOBFAIL=$OPTARG ;; 26 | n) DRYRUN=-n ;; 27 | l) TTL=$OPTARG ;; 28 | u) UPDATE=1 ;; 29 | t) TARGETS+=($OPTARG) ;; 30 | v) VERSION=$OPTARG ;; 31 | esac 32 | done 33 | 34 | set -e 35 | 36 | # determine targets 37 | if [ ${#TARGETS[@]} -eq 0 ]; then 38 | TARGETS=(arm64) 39 | fi 40 | 41 | # determine version 42 | if [ -z "$VERSION" ]; then 43 | VERSION=$(verhist -platform win64 -channel $CHANNEL -latest) 44 | fi 45 | 46 | # determine out dir 47 | if [ -z "$OUT" ]; then 48 | OUT=$(realpath "$SRC/out") 49 | fi 50 | 51 | # determine source dir 52 | if [ -z "$SRCDIR" ]; then 53 | if [ -d /media/src ]; then 54 | SRCDIR=/media/src 55 | else 56 | SRCDIR=$OUT 57 | fi 58 | fi 59 | 60 | # check source directory exists 61 | if [ ! -d "$SRCDIR" ]; then 62 | echo "ERROR: $SRCDIR does not exist!" 63 | exit 1 64 | fi 65 | 66 | # create out dir 67 | mkdir -p $OUT 68 | 69 | # determine last update state 70 | LAST=0 71 | if [ -e $OUT/last ]; then 72 | LAST=$(cat $OUT/last) 73 | fi 74 | if [ "$((`date +%s` - $LAST))" -gt $TTL ]; then 75 | UPDATE=1 76 | fi 77 | 78 | echo "BUILD: $VERSION [${TARGETS[@]}] (u:$UPDATE j:$JOBS a:$ATTEMPTS)" 79 | echo "SOURCE: $SRCDIR/chromium/src" 80 | 81 | TMPDIR=$(mktemp -d -p /tmp headless-shell-$VERSION.XXXXX) 82 | echo "TMPDIR: $TMPDIR" 83 | 84 | # grab depot_tools 85 | if [ ! -d $OUT/depot_tools ]; then 86 | echo -e "\n\nRETRIEVING depot_tools ($(date))" 87 | (set -x; 88 | git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $OUT/depot_tools 89 | ) 90 | fi 91 | 92 | # update to latest depot_tools 93 | if [ "$UPDATE" -eq "1" ]; then 94 | echo -e "\n\nUPDATING $OUT/depot_tools ($(date))" 95 | (set -x; 96 | git -C $OUT/depot_tools reset --hard 97 | git -C $OUT/depot_tools checkout main 98 | git -C $OUT/depot_tools pull 99 | ) 100 | fi 101 | 102 | # add depot_tools to path 103 | export PATH=$OUT/depot_tools:$PATH 104 | 105 | CHROMESRC=$SRCDIR/chromium/src 106 | 107 | # retrieve chromium source tree 108 | if [ ! -d $CHROMESRC ]; then 109 | echo -e "\n\nRETRIEVING chromium -> $CHROMESRC ($(date))" 110 | pushd $SRCDIR &> /dev/null 111 | (set -x; 112 | fetch --nohooks chromium 113 | gclient runhooks 114 | ) 115 | popd &> /dev/null 116 | fi 117 | 118 | useragent_files() { 119 | find $CHROMESRC/headless -type f -iname \*.cc -print0 \ 120 | |xargs -r0 grep -EHi '"(Headless)?Chrome"' \ 121 | |awk -F: '{print $1}' \ 122 | |sed -e "s%^$CHROMESRC/%%" \ 123 | |sort \ 124 | |uniq 125 | } 126 | 127 | # update chromium source tree 128 | if [ "$UPDATE" -eq "1" ]; then 129 | echo -e "\n\nREBASING ($(date))" 130 | USERAGENT_FILES=$(useragent_files) 131 | (set -x; 132 | git -C $CHROMESRC checkout $USERAGENT_FILES 133 | git -C $CHROMESRC checkout main 134 | git -C $CHROMESRC rebase-update 135 | ) 136 | date +%s > $OUT/last 137 | echo "LAST: $(cat $OUT/last) ($(date))" 138 | fi 139 | 140 | # determine sync status 141 | SYNC=$UPDATE 142 | if [ "$VERSION" != "$(git name-rev --tags --name-only $(git rev-parse HEAD))" ]; then 143 | SYNC=1 144 | fi 145 | 146 | if [ "$SYNC" -eq "1" ]; then 147 | echo -e "\n\nRESETTING $VERSION ($(date))" 148 | # files in headless that contain the HeadlessChrome user-agent string 149 | USERAGENT_FILES=$(useragent_files) 150 | (set -x; 151 | git -C $CHROMESRC checkout $USERAGENT_FILES 152 | git -C $CHROMESRC checkout $VERSION 153 | ) 154 | pushd $CHROMESRC &> /dev/null 155 | (set -x; 156 | gclient sync \ 157 | --with_branch_heads \ 158 | --with_tags \ 159 | --delete_unversioned_trees \ 160 | --reset 161 | ./build/linux/sysroot_scripts/install-sysroot.py --arch=arm64 162 | ) 163 | # alter the user agent string 164 | for f in $(useragent_files); do 165 | perl -pi -e 's/"HeadlessChrome"/"Chrome"/' $f 166 | done 167 | popd &> /dev/null 168 | fi 169 | 170 | # build targets 171 | for TARGET in ${TARGETS[@]}; do 172 | NAME=headless-shell-$CHANNEL-$TARGET 173 | PROJECT=$CHROMESRC/out/$NAME 174 | mkdir -p $PROJECT 175 | 176 | # generate build files 177 | echo -e "\n\nGENERATING $TARGET $VERSION -> $PROJECT ($(date))" 178 | 179 | EXTRA= 180 | if [ "$TARGET" = "arm64" ]; then 181 | EXTRA="target_cpu = \"arm64\"" 182 | fi 183 | echo "import(\"//build/args/headless.gn\") 184 | is_debug = false 185 | is_official_build = true 186 | symbol_level = 0 187 | blink_symbol_level = 0 188 | headless_use_prefs = true 189 | chrome_pgo_phase = 0 190 | $EXTRA 191 | " > $PROJECT/args.gn 192 | 193 | pushd $CHROMESRC &> /dev/null 194 | (set -x; 195 | gn gen ./out/$NAME 196 | ) 197 | popd &> /dev/null 198 | 199 | # build 200 | RET=1 201 | for i in $(seq 1 $ATTEMPTS); do 202 | echo -e "\n\nSTARTING BUILD ATTEMPT $i FOR $TARGET $VERSION ($(date))" 203 | 204 | RET=1 205 | $OUT/depot_tools/ninja \ 206 | -j $JOBS \ 207 | -k $JOBFAIL \ 208 | $DRYRUN \ 209 | -C $PROJECT \ 210 | headless_shell && RET=$? 211 | 212 | if [ $RET -eq 0 ]; then 213 | echo "COMPLETED BUILD ATTEMPT $i FOR $TARGET $VERSION ($(date))" 214 | break 215 | fi 216 | echo "BUILD ATTEMPT $i FOR $TARGET $VERSION FAILED ($(date))" 217 | done 218 | 219 | if [ $RET -ne 0 ]; then 220 | echo -e "\n\nERROR: COULD NOT COMPLETE BUILD FOR $TARGET $VERSION, BUILD ATTEMPTS HAVE BEEN EXHAUSTED ($(date))" 221 | exit 1 222 | fi 223 | 224 | # build stamp 225 | echo $VERSION > $PROJECT/.stamp 226 | done 227 | 228 | # package 229 | for TARGET in ${TARGETS[@]}; do 230 | PROJECT=$CHROMESRC/out/headless-shell-$CHANNEL-$TARGET 231 | WORKDIR=$TMPDIR/headless-shell 232 | 233 | # strip 234 | STRIP=strip 235 | if [ "$TARGET" = "arm64" ]; then 236 | STRIP=aarch64-linux-gnu-strip 237 | fi 238 | 239 | # copy files 240 | mkdir -p $WORKDIR 241 | (set -x; 242 | cp -a $PROJECT/headless_shell $WORKDIR/headless-shell 243 | cp -a $PROJECT/{.stamp,libEGL.so,libGLESv2.so,libvk_swiftshader.so,libvulkan.so.1,vk_swiftshader_icd.json} $WORKDIR 244 | $STRIP $WORKDIR/headless-shell $WORKDIR/*.so{,.1} 245 | chmod -x $WORKDIR/*.so{,.1} 246 | du -s $WORKDIR/* 247 | file $WORKDIR/headless-shell 248 | ) 249 | 250 | if [ "$TARGET" = "amd64" ]; then 251 | # verify headless-shell runs and reports correct version 252 | $WORKDIR/headless-shell --remote-debugging-port=5000 &> /dev/null & PID=$! 253 | sleep 1 254 | UA=$(curl --silent --connect-timeout 5 http://localhost:5000/json/version|jq -r '.Browser') 255 | kill -s SIGTERM $PID 256 | set +e 257 | wait $PID 2>/dev/null 258 | set -e 259 | if [ "$UA" != "Chrome/$VERSION" ]; then 260 | echo -e "\n\nERROR: HEADLESS-SHELL REPORTED VERSION '$UA', NOT 'Chrome/$VERSION'! ($(date))" 261 | exit 1 262 | else 263 | echo -e "\n\nHEADLESS SHELL REPORTED VERSION '$UA' ($(date))" 264 | fi 265 | fi 266 | 267 | ARCHIVE=$OUT/headless-shell-$VERSION-$TARGET.tar.bz2 268 | 269 | echo -e "\n\nPACKAGING $ARCHIVE ($(date))" 270 | (set -x; 271 | rm -f $ARCHIVE 272 | tar -C $TMPDIR -cjf $ARCHIVE headless-shell 273 | du -s $ARCHIVE 274 | ) 275 | done -------------------------------------------------------------------------------- /scripts/build-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | OUT=$SRC/out 6 | TARGETS=() 7 | TAGS=() 8 | VERSION= 9 | IMAGE=docker.io/spiderrust/headless-shell 10 | 11 | OPTIND=1 12 | while getopts "o:t:g:v:i:" opt; do 13 | case "$opt" in 14 | o) OUT=$OPTARG ;; 15 | t) TARGETS+=($OPTARG) ;; 16 | g) TAGS+=($OPTARG) ;; 17 | v) VERSION=$OPTARG ;; 18 | i) IMAGE=$OPTARG ;; 19 | esac 20 | done 21 | 22 | set -e 23 | 24 | # check out dir 25 | if [ ! -d $OUT ]; then 26 | echo "$OUT does not exist!" 27 | exit 1 28 | fi 29 | 30 | # determine version 31 | if [ -z "$VERSION" ]; then 32 | VERSION=$(ls $OUT/*.bz2 | sort -r -V | head -1 | sed -e 's/.*headless-shell-\([0-9\.]\+\).*/\1/') 33 | fi 34 | 35 | # determine targets 36 | if [ ${#TARGETS[@]} -eq 0 ]; then 37 | TARGETS=($(ls $OUT/*-${VERSION}-*.bz2 | sed -e 's/.*headless-shell-[0-9\.]\+-\([a-z0-9]\+\).*/\1/' | xargs)) 38 | fi 39 | 40 | # join_by ',' ${A[@]} ${B[@]} 41 | join_by() { 42 | local d=${1-} f=${2-} 43 | if shift 2; then 44 | printf %s "$f" "${@/#/$d}" 45 | fi 46 | } 47 | 48 | echo "VERSION: $VERSION [${TARGETS[@]}]" 49 | echo "IMAGE: $IMAGE [tags: $(join_by ' ' $VERSION ${TAGS[@]})]" 50 | 51 | IMAGES=() 52 | for TARGET in ${TARGETS[@]}; do 53 | TAG=$VERSION-$TARGET 54 | NAME=$IMAGE:$TAG 55 | IMAGES+=($NAME) 56 | 57 | if [ -n "$(docker images -q $NAME)" ]; then 58 | echo -e "\n\nSKIPPING BUILD FOR $NAME ($(date))" 59 | continue 60 | fi 61 | 62 | echo -e "\n\nBUILDING $NAME ($(date))" 63 | ARCHIVE=$OUT/headless-shell-$VERSION-$TARGET.tar.bz2 64 | if [ ! -f $ARCHIVE ]; then 65 | echo "ERROR: $ARCHIVE is missing!" 66 | exit 1 67 | fi 68 | 69 | # Make sure to extract the archive if necessary and adjust context as needed 70 | rm -rf $OUT/$VERSION-$TARGET 71 | mkdir -p $OUT/$VERSION-$TARGET 72 | tar -C $OUT/$VERSION-$TARGET -jxf $ARCHIVE 73 | 74 | (set -x; 75 | docker build \ 76 | --platform linux/$TARGET \ 77 | --build-arg VERSION="$VERSION-$TARGET" \ 78 | --tag $NAME \ 79 | $OUT/$VERSION-$TARGET 80 | ) 81 | done -------------------------------------------------------------------------------- /scripts/build-unpatched.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LATEST_PATH="./out/latest/" 4 | 5 | # we can also pin to 132.0.6834.159 as a working version 6 | # npx @puppeteer/browsers install chrome-headless-shell@stable 7 | PLAYWRIGHT_BROWSERS_PATH=./chrome-headless-shell npx playwright install chromium --only-shell --with-deps 8 | 9 | mkdir -p "$LATEST_PATH" 10 | 11 | # move contents to out 12 | mv ./chrome-headless-shell/*/* "$LATEST_PATH" 13 | 14 | # strip away name 15 | for dir in "$LATEST_PATH"/chrome-headless-shell-*; do 16 | # Check if it is actually a directory 17 | if [[ -d "$dir" ]]; then 18 | # Extract the base name without the "-$arc" part 19 | new_name="$LATEST_PATH/chrome-headless-shell" 20 | echo "Renaming directory $dir to $new_name" 21 | mv "$dir" "$new_name" 22 | 23 | # Check if the binary exists in the newly renamed directory 24 | binary_path="$new_name/headless_shell" 25 | new_binary_name="$new_name/headless-shell" 26 | 27 | if [[ -f "$binary_path" ]]; then 28 | echo "Renaming binary $binary_path to $new_binary_name" 29 | mv "$binary_path" "$new_binary_name" 30 | else 31 | echo "Binary $binary_path not found." 32 | fi 33 | 34 | break 35 | fi 36 | done 37 | 38 | # Remove the now-empty chrome-headless-shell directory 39 | rm -R ./chrome-headless-shell 40 | 41 | cd $LATEST_PATH 42 | 43 | mv chrome-headless-shell ../ 44 | 45 | cd ../ 46 | 47 | mv chrome-headless-shell headless-shell 48 | mv headless-shell ./latest 49 | 50 | cd latest 51 | 52 | mv chrome-linux headless-shell 53 | 54 | cd headless-shell 55 | 56 | mv headless_shell headless-shell 57 | 58 | cd .. 59 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run the chrome build script 4 | # you also need to install some other deps like go and etc. Follow https://github.com/chromedp/docker-headless-shell 5 | 6 | # one liner to install on AWS arm 7 | # sudo yum update -y && sudo yum install -y wget tar && wget https://dl.google.com/go/go1.23.5.linux-arm64.tar.gz && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.23.5.linux-arm64.tar.gz && rm go1.23.5.linux-arm64.tar.gz && echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc && source ~/.bashrc && go version 8 | 9 | # Get the verhist module 10 | go install github.com/chromedp/verhist/cmd/verhist@latest 11 | 12 | # Verify installation 13 | if go list -m github.com/chromedp/verhist@latest &> /dev/null; then 14 | echo "verhist module installed successfully." 15 | else 16 | echo "Failed to install verhist module." 17 | fi 18 | 19 | OUT_DIR="out" 20 | 21 | [ -d "$OUT_DIR" ] || (echo "Creating directory '$OUT_DIR'..." && mkdir "$OUT_DIR") 22 | 23 | ./build-base.sh -------------------------------------------------------------------------------- /scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC=$(realpath $(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)) 4 | 5 | OUT=$SRC/out 6 | IMAGE=docker.io/spiderrust/headless-shell 7 | CHANNELS=() 8 | VERSIONS=() 9 | MTIME=90 10 | 11 | OPTIND=1 12 | while getopts "o:i:c:v:m:" opt; do 13 | case "$opt" in 14 | o) OUT=$OPTARG ;; 15 | i) IMAGE=$OPTARG ;; 16 | c) CHANNELS+=($OPTARG) ;; 17 | v) VERSIONS+=($OPTARG) ;; 18 | m) MTIME=$OPTARG ;; 19 | esac 20 | done 21 | 22 | set -e 23 | 24 | if [ ${#CHANNELS[@]} -eq 0 ]; then 25 | CHANNELS=(stable) 26 | fi 27 | 28 | if [ ${#VERSIONS[@]} -eq 0 ]; then 29 | for CHANNEL in ${CHANNELS[@]}; do 30 | VERSIONS+=($(verhist -platform win64 -channel "$CHANNEL" -latest)) 31 | done 32 | fi 33 | 34 | # join_by ',' ${A[@]} ${B[@]} 35 | join_by() { 36 | local d=${1-} f=${2-} 37 | if shift 2; then 38 | printf %s "$f" "${@/#/$d}" 39 | fi 40 | } 41 | 42 | echo -e "KEEP: $(join_by ', ' latest ${CHANNELS[@]} ${VERSIONS[@]})" 43 | 44 | # cleanup old directories and files 45 | if [ -d $OUT ]; then 46 | REGEX=".*($(join_by '|' ${VERSIONS[@]})).*" 47 | (set -x; 48 | find $OUT \ 49 | -mindepth 1 \ 50 | -maxdepth 1 \ 51 | -regextype posix-extended \ 52 | \( \ 53 | -type d \ 54 | -regex '.*/[0-9]+(\.[0-9]+){3}-(amd64|arm64)$' \ 55 | -or \ 56 | -type f \ 57 | -regex '.*/headless-shell-[0-9]+(\.[0-9]+){3}-(amd64|arm64)\.tar\.bz2$' \ 58 | \) \ 59 | -mtime $MTIME \ 60 | -not \ 61 | -regex "$REGEX" \ 62 | -exec echo REMOVING {} \; \ 63 | -exec rm -rf {} \; 64 | ) 65 | fi 66 | 67 | # # remove containers 68 | # CONTAINERS=$( 69 | # podman container ls \ 70 | # --filter=ancestor=$IMAGE \ 71 | # --filter=status=exited \ 72 | # --filter=status=created \ 73 | # --quiet 74 | # ) 75 | # if [ ! -z "$CONTAINERS" ]; then 76 | # (set -x; 77 | # podman container rm --force $CONTAINERS 78 | # ) 79 | # fi 80 | 81 | # # remove images 82 | # IMAGES=$( 83 | # podman images \ 84 | # --noheading \ 85 | # --filter=reference=$IMAGE \ 86 | # --filter=reference=localhost/$(basename $IMAGE) \ 87 | # |grep -Ev "($(join_by '|' latest ${CHANNELS[@]} ${VERSIONS[@]}))" \ 88 | # |awk '{print $3}' 89 | # ) 90 | # if [ ! -z "$IMAGES" ]; then 91 | # (set -x; 92 | # podman rmi --force $IMAGES 93 | # ) 94 | # fi -------------------------------------------------------------------------------- /scripts/docker-entrypoint-xvfb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | REMOTE_ADDRESS="${REMOTE_ADDRESS:-127.0.0.1}"; 6 | LAUNCH="${LAUNCH:-init}"; 7 | DEFAULT_PORT="${DEFAULT_PORT:-9224}"; 8 | DEFAULT_PORT_SERVER="${DEFAULT_PORT_SERVER:-6000}"; 9 | DEFAULT_LAUNCH_NAME="${DEFAULT_LAUNCH_NAME:-chromium-browser}"; 10 | 11 | echo "Starting Xvfb" 12 | 13 | Xvfb :0 -screen 0 1024x768x16 -nolisten tcp & 14 | sleep 1 15 | 16 | exec headless_browser $DEFAULT_LAUNCH_NAME $REMOTE_ADDRESS $LAUNCH $DEFAULT_PORT $DEFAULT_PORT_SERVER "false" -------------------------------------------------------------------------------- /scripts/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | REMOTE_ADDRESS="${REMOTE_ADDRESS:-127.0.0.1}"; 6 | LAUNCH="${LAUNCH:-init}"; 7 | DEFAULT_PORT="${DEFAULT_PORT:-9223}"; 8 | DEFAULT_PORT_SERVER="${DEFAULT_PORT_SERVER:-6000}"; 9 | DEFAULT_LAUNCH_NAME="${DEFAULT_LAUNCH_NAME:-chromium-browser}"; 10 | 11 | exec headless_browser $DEFAULT_LAUNCH_NAME $REMOTE_ADDRESS $LAUNCH $DEFAULT_PORT $DEFAULT_PORT_SERVER "true" --------------------------------------------------------------------------------