Full Code of cloudflare/pingora for AI

main 82342e138c01 cached
380 files
3.0 MB
807.7k tokens
4519 symbols
1 requests
Download .txt
Showing preview only (3,223K chars total). Download the full file or copy to clipboard to get everything.
Repository: cloudflare/pingora
Branch: main
Commit: 82342e138c01
Files: 380
Total size: 3.0 MB

Directory structure:
gitextract__dwmq04u/

├── .bleep
├── .cargo/
│   ├── audit.toml
│   └── config.toml
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── audit.yml
│       ├── build.yml
│       ├── docs.yml
│       ├── mark-stale.yaml
│       └── semgrep.yml
├── .gitignore
├── .rustfmt.toml
├── CHANGELOG.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── README.md
├── cliff.toml
├── clippy.toml
├── docs/
│   ├── README.md
│   ├── quick_start.md
│   └── user_guide/
│       ├── conf.md
│       ├── ctx.md
│       ├── daemon.md
│       ├── error_log.md
│       ├── errors.md
│       ├── failover.md
│       ├── graceful.md
│       ├── index.md
│       ├── internals.md
│       ├── modify_filter.md
│       ├── panic.md
│       ├── peer.md
│       ├── phase.md
│       ├── phase_chart.md
│       ├── pooling.md
│       ├── prom.md
│       ├── rate_limiter.md
│       ├── start_stop.md
│       └── systemd.md
├── pingora/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── app/
│   │   │   ├── echo.rs
│   │   │   ├── mod.rs
│   │   │   └── proxy.rs
│   │   ├── client.rs
│   │   ├── server.rs
│   │   └── service/
│   │       ├── echo.rs
│   │       ├── mod.rs
│   │       └── proxy.rs
│   ├── src/
│   │   └── lib.rs
│   └── tests/
│       └── pingora_conf.yaml
├── pingora-boringssl/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── boring_tokio.rs
│       ├── ext.rs
│       └── lib.rs
├── pingora-cache/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── lru_memory.rs
│   │   ├── lru_serde.rs
│   │   └── simple_lru_memory.rs
│   └── src/
│       ├── cache_control.rs
│       ├── eviction/
│       │   ├── lru.rs
│       │   ├── mod.rs
│       │   └── simple_lru.rs
│       ├── filters.rs
│       ├── hashtable.rs
│       ├── key.rs
│       ├── lib.rs
│       ├── lock.rs
│       ├── max_file_size.rs
│       ├── memory.rs
│       ├── meta.rs
│       ├── predictor.rs
│       ├── put.rs
│       ├── storage.rs
│       ├── trace.rs
│       └── variance.rs
├── pingora-core/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── bootstrap_as_a_service.rs
│   │   ├── client_cert.rs
│   │   ├── keys/
│   │   │   ├── client-ca/
│   │   │   │   ├── cert.pem
│   │   │   │   └── key.pem
│   │   │   ├── clients/
│   │   │   │   ├── cert-1.pem
│   │   │   │   ├── cert-2.pem
│   │   │   │   ├── invalid-cert.pem
│   │   │   │   ├── invalid-key.pem
│   │   │   │   ├── key-1.pem
│   │   │   │   └── key-2.pem
│   │   │   └── server/
│   │   │       ├── cert.pem
│   │   │       └── key.pem
│   │   └── service_dependencies.rs
│   ├── src/
│   │   ├── apps/
│   │   │   ├── http_app.rs
│   │   │   ├── mod.rs
│   │   │   └── prometheus_http_app.rs
│   │   ├── connectors/
│   │   │   ├── http/
│   │   │   │   ├── custom/
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── v1.rs
│   │   │   │   └── v2.rs
│   │   │   ├── l4.rs
│   │   │   ├── mod.rs
│   │   │   ├── offload.rs
│   │   │   └── tls/
│   │   │       ├── boringssl_openssl/
│   │   │       │   └── mod.rs
│   │   │       ├── mod.rs
│   │   │       ├── rustls/
│   │   │       │   └── mod.rs
│   │   │       └── s2n/
│   │   │           └── mod.rs
│   │   ├── lib.rs
│   │   ├── listeners/
│   │   │   ├── connection_filter.rs
│   │   │   ├── l4.rs
│   │   │   ├── mod.rs
│   │   │   └── tls/
│   │   │       ├── boringssl_openssl/
│   │   │       │   └── mod.rs
│   │   │       ├── mod.rs
│   │   │       ├── rustls/
│   │   │       │   └── mod.rs
│   │   │       └── s2n/
│   │   │           └── mod.rs
│   │   ├── modules/
│   │   │   ├── http/
│   │   │   │   ├── compression.rs
│   │   │   │   ├── grpc_web.rs
│   │   │   │   └── mod.rs
│   │   │   └── mod.rs
│   │   ├── protocols/
│   │   │   ├── digest.rs
│   │   │   ├── http/
│   │   │   │   ├── body_buffer.rs
│   │   │   │   ├── bridge/
│   │   │   │   │   ├── grpc_web.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── client.rs
│   │   │   │   ├── compression/
│   │   │   │   │   ├── brotli.rs
│   │   │   │   │   ├── gzip.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── zstd.rs
│   │   │   │   ├── conditional_filter.rs
│   │   │   │   ├── custom/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   ├── date.rs
│   │   │   │   ├── error_resp.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── server.rs
│   │   │   │   ├── subrequest/
│   │   │   │   │   ├── body.rs
│   │   │   │   │   ├── dummy.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   ├── v1/
│   │   │   │   │   ├── body.rs
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── common.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   └── v2/
│   │   │   │       ├── client.rs
│   │   │   │       ├── mod.rs
│   │   │   │       └── server.rs
│   │   │   ├── l4/
│   │   │   │   ├── ext.rs
│   │   │   │   ├── listener.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── socket.rs
│   │   │   │   ├── stream.rs
│   │   │   │   └── virt.rs
│   │   │   ├── mod.rs
│   │   │   ├── raw_connect.rs
│   │   │   ├── tls/
│   │   │   │   ├── boringssl_openssl/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── server.rs
│   │   │   │   │   └── stream.rs
│   │   │   │   ├── digest.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── noop_tls/
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── rustls/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── server.rs
│   │   │   │   │   └── stream.rs
│   │   │   │   └── s2n/
│   │   │   │       ├── client.rs
│   │   │   │       ├── mod.rs
│   │   │   │       ├── server.rs
│   │   │   │       └── stream.rs
│   │   │   └── windows.rs
│   │   ├── server/
│   │   │   ├── bootstrap_services.rs
│   │   │   ├── configuration/
│   │   │   │   └── mod.rs
│   │   │   ├── daemon.rs
│   │   │   ├── mod.rs
│   │   │   └── transfer_fd/
│   │   │       └── mod.rs
│   │   ├── services/
│   │   │   ├── background.rs
│   │   │   ├── listening.rs
│   │   │   └── mod.rs
│   │   ├── tls/
│   │   │   └── mod.rs
│   │   ├── upstreams/
│   │   │   ├── mod.rs
│   │   │   └── peer.rs
│   │   └── utils/
│   │       ├── mod.rs
│   │       └── tls/
│   │           ├── boringssl_openssl.rs
│   │           ├── mod.rs
│   │           ├── rustls.rs
│   │           └── s2n.rs
│   └── tests/
│       ├── certs/
│       │   ├── alt-ca.crt
│       │   ├── alt-server.crt
│       │   ├── ca.crt
│       │   ├── server.crt
│       │   └── server.key
│       ├── keys/
│       │   ├── key.pem
│       │   ├── public.pem
│       │   ├── server.crt
│       │   └── server.csr
│       ├── nginx.conf
│       ├── nginx_proxy.conf
│       ├── pingora_conf.yaml
│       ├── server_phase_fastshutdown.rs
│       ├── server_phase_gracefulshutdown.rs
│       ├── test_basic.rs
│       └── utils/
│           └── mod.rs
├── pingora-error/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── immut_str.rs
│       └── lib.rs
├── pingora-header-serde/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── samples/
│   │   └── test/
│   │       ├── 1
│   │       ├── 2
│   │       ├── 3
│   │       ├── 4
│   │       ├── 5
│   │       ├── 6
│   │       └── 7
│   └── src/
│       ├── dict.rs
│       ├── lib.rs
│       ├── thread_zstd.rs
│       └── trainer.rs
├── pingora-http/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── case_header_name.rs
│       └── lib.rs
├── pingora-ketama/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── memory.rs
│   │   └── simple.rs
│   ├── examples/
│   │   └── health_aware_selector.rs
│   ├── src/
│   │   └── lib.rs
│   ├── test-data/
│   │   ├── README.md
│   │   ├── nginx.conf
│   │   ├── sample-nginx-upstream.csv
│   │   └── trace.sh
│   └── tests/
│       ├── backwards_compat.rs
│       └── old_version/
│           └── mod.rs
├── pingora-limits/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── benchmark.rs
│   └── src/
│       ├── estimator.rs
│       ├── inflight.rs
│       ├── lib.rs
│       └── rate.rs
├── pingora-load-balancing/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── background.rs
│       ├── discovery.rs
│       ├── health_check.rs
│       ├── lib.rs
│       └── selection/
│           ├── algorithms.rs
│           ├── consistent.rs
│           ├── mod.rs
│           └── weighted.rs
├── pingora-lru/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── bench_linked_list.rs
│   │   └── bench_lru.rs
│   └── src/
│       ├── lib.rs
│       └── linked_list.rs
├── pingora-memory-cache/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── lib.rs
│       └── read_through.rs
├── pingora-openssl/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── ext.rs
│       └── lib.rs
├── pingora-pool/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── connection.rs
│       ├── lib.rs
│       └── lru.rs
├── pingora-proxy/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── backoff_retry.rs
│   │   ├── conf.yaml
│   │   ├── connection_filter.rs
│   │   ├── ctx.rs
│   │   ├── gateway.rs
│   │   ├── grpc_web_module.rs
│   │   ├── load_balancer.rs
│   │   ├── modify_response.rs
│   │   ├── multi_lb.rs
│   │   ├── rate_limiter.rs
│   │   ├── use_module.rs
│   │   └── virtual_l4.rs
│   ├── src/
│   │   ├── lib.rs
│   │   ├── proxy_cache.rs
│   │   ├── proxy_common.rs
│   │   ├── proxy_custom.rs
│   │   ├── proxy_h1.rs
│   │   ├── proxy_h2.rs
│   │   ├── proxy_purge.rs
│   │   ├── proxy_trait.rs
│   │   └── subrequest/
│   │       ├── mod.rs
│   │       └── pipe.rs
│   └── tests/
│       ├── headers.dict
│       ├── keys/
│       │   ├── key.pem
│       │   ├── public.pem
│       │   ├── server.crt
│       │   └── server.csr
│       ├── pingora_conf.yaml
│       ├── test_basic.rs
│       ├── test_upstream.rs
│       └── utils/
│           ├── cert.rs
│           ├── conf/
│           │   ├── keys/
│           │   │   ├── README.md
│           │   │   ├── ca1.crt
│           │   │   ├── ca1.key.pem
│           │   │   ├── ca2.crt
│           │   │   ├── ca_chain.cert
│           │   │   ├── ca_chain.srl
│           │   │   ├── cert_chain.crt
│           │   │   ├── curve_test.384.crt
│           │   │   ├── curve_test.384.key.pem
│           │   │   ├── curve_test.521.crt
│           │   │   ├── curve_test.521.key.pem
│           │   │   ├── ex1.crt
│           │   │   ├── ex1.key.b64
│           │   │   ├── intermediate.cnf
│           │   │   ├── intermediate.crt
│           │   │   ├── intermediate.csr
│           │   │   ├── intermediate.key
│           │   │   ├── intermediate.srl
│           │   │   ├── key.pem
│           │   │   ├── leaf.cnf
│           │   │   ├── leaf.crt
│           │   │   ├── leaf.csr
│           │   │   ├── leaf.key
│           │   │   ├── leaf.srl
│           │   │   ├── leaf2.crt
│           │   │   ├── leaf2.csr
│           │   │   ├── leaf2.key
│           │   │   ├── leaf2.srl
│           │   │   ├── public.pem
│           │   │   ├── root.crt
│           │   │   ├── root.key
│           │   │   ├── root.srl
│           │   │   ├── server.crt
│           │   │   ├── server_boringssl_openssl.crt
│           │   │   ├── server_boringssl_openssl.csr
│           │   │   ├── server_rustls.crt
│           │   │   ├── server_s2n.crt
│           │   │   └── v3.ext
│           │   └── origin/
│           │       ├── .gitignore
│           │       ├── conf/
│           │       │   └── nginx.conf
│           │       └── html/
│           │           └── index.html
│           ├── mock_origin.rs
│           ├── mod.rs
│           ├── server_utils.rs
│           └── websocket/
│               ├── mod.rs
│               ├── ws_echo.rs
│               └── ws_echo_raw.rs
├── pingora-runtime/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── hello.rs
│   └── src/
│       └── lib.rs
├── pingora-rustls/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       └── lib.rs
├── pingora-s2n/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       └── lib.rs
├── pingora-timeout/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── benchmark.rs
│   └── src/
│       ├── fast_timeout.rs
│       ├── lib.rs
│       └── timer.rs
└── tinyufo/
    ├── Cargo.toml
    ├── LICENSE
    ├── README.md
    ├── benches/
    │   ├── bench_hit_ratio.rs
    │   ├── bench_memory.rs
    │   └── bench_perf.rs
    └── src/
        ├── buckets.rs
        ├── estimation.rs
        └── lib.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .bleep
================================================
5a1cf681f7e2691687623b60387a88076493015f

================================================
FILE: .cargo/audit.toml
================================================
[advisories]
ignore = [
    # This came from the prometheus crate's protobuf encoder.
    # We don't use the protobuf encoder, only the text one.
    # https://rustsec.org/advisories/RUSTSEC-2024-0437
    "RUSTSEC-2024-0437",
]


================================================
FILE: .cargo/config.toml
================================================
[resolver]
incompatible-rust-versions = "fallback"

================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing

Welcome to Pingora! Before you make a contribution, be it a bug report, documentation improvement,
pull request (PR), etc., please read and follow these guidelines.

## Start with filing an issue

More often than not, **start by filing an issue on GitHub**. If you have a bug report or feature
request, open a GitHub issue. Non-trivial PRs will also require a GitHub issue. The issue provides
us with a space to discuss proposed changes with you and the community.

Having a discussion via GitHub issue upfront is the best way to ensure your contribution lands in
Pingora. We don't want you to spend your time making a PR, only to find that we won't accept it on
a design basis. For example, we may find that your proposed feature works better as a third-party
module built on top of or for use with Pingora and encourage you to pursue that direction instead.

**You do not need to file an issue for small fixes.** What counts as a "small" or trivial fix is a
judgment call, so here's a few examples to clarify:
- fixing a typo
- refactoring a bit of code
- most documentation or comment edits

Still, _sometimes_ we may review your PR and ask you to file an issue if we expect there are larger
design decisions to be made.

## Making a PR

After you've filed an issue, you can make your PR referencing that issue number. Once you open your
PR, it will be labelled _Needs Review_. A maintainer will review your PR as soon as they can. The
reviewer may ask for changes - they will mark the PR as _Changes Requested_ and will give you
details about the requested changes. Feel free to ask lots of questions! The maintainers are there
to help you.

Once we (the maintainers) decide to accept your change, we will label your PR as _Accepted_.
Later (usually within a week or two), we will rebase your commits onto the `main` branch in a
separate PR, batched alongside other _Accepted_ commits and any internal changes. (This process
allows us to sync the state of our internal repository with the public repository.) Once your
change lands in `main`, we will close your PR.

### Caveats

Currently, internal contributions will take priority. Today Pingora is being maintained by
Cloudflare's Content Delivery team, and internal Cloudflare proxy services are a primary user of
Pingora. We value the community's work on Pingora, but the reality is that our team has a limited
amount of resources and time. We can't promise we will review or address all PRs or issues in a
timely manner.

## Conduct

Pingora and Cloudflare OpenSource generally follows the [Contributor Covenant Code of Conduct].
Violating the CoC could result in a warning or a ban to Pingora or any and all repositories in the Cloudflare organization.

[Contributor Covenant Code of Conduct]: https://github.com/cloudflare/.github/blob/26b37ca2ba7ab3d91050ead9f2c0e30674d3b91e/CODE_OF_CONDUCT.md

## Contact

If you have any questions, please reach out to [opensource@cloudflare.com](mailto:opensource@cloudflare.com).


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report
about: Report an issue to help us improve
title: ''
labels: ''
assignees: ''
---

## Describe the bug

A clear and concise description of what the bug is.

## Pingora info

Please include the following information about your environment:

**Pingora version**: release number of commit hash
**Rust version**: i.e. `cargo --version`
**Operating system version**: e.g. Ubuntu 22.04, Debian 12.4

## Steps to reproduce

Please provide step-by-step instructions to reproduce the issue. Include any relevant code
snippets.

## Expected results

What were you expecting to happen?

## Observed results

What actually happened?

## Additional context

What other information would you like to provide? e.g. screenshots, how you're working around the
issue, or other clues you think could be helpful to identify the root cause.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Propose a new feature
title: ''
labels: ''
assignees: ''
---

## What is the problem your feature solves, or the need it fulfills?

A clear and concise description of why this feature should be added. What is the problem? Who is
this for?

## Describe the solution you'd like

What do you propose to resolve the problem or fulfill the need above? How would you like it to
work?

## Describe alternatives you've considered

What other solutions, features, or workarounds have you considered that might also solve the issue?
What are the tradeoffs for these alternatives compared to what you're proposing?

## Additional context

This could include references to documentation or papers, prior art, screenshots, or benchmark
results.


================================================
FILE: .github/workflows/audit.yml
================================================
name: Security Audit

on:
  push:
    branches:
      - master
    paths:
      - "**/Cargo.toml"
  schedule:
    - cron: "0 2 * * *" # run at 2 AM UTC

permissions:
  contents: read

jobs:
  security-audit:
    permissions:
      checks: write # for rustsec/audit-check to create check
      contents: read # for actions/checkout to fetch code
      issues: write # for rustsec/audit-check to create issues
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate Cargo.lock
        # https://github.com/rustsec/audit-check/issues/27
        run: cargo generate-lockfile --ignore-rust-version

      - name: Audit Check
        # https://github.com/rustsec/audit-check/issues/2
        uses: rustsec/audit-check@master
        with:
          token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/build.yml
================================================
on: [push, pull_request]

name: build

jobs:
  pingora:
    strategy:
      fail-fast: false
      matrix:
        # nightly, msrv, and latest stable
        toolchain: [nightly, 1.84.0, 1.91.1]
    runs-on: ubuntu-latest
    # Only run on "pull_request" event for external PRs. This is to avoid
    # duplicate builds for PRs created from internal branches.
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          submodules: "recursive"

      - name: Install build dependencies
        run: |
          sudo apt update
          sudo apt install -y cmake libclang-dev wget gnupg ca-certificates lsb-release --no-install-recommends
          # openresty is used for convenience in tests as a server.
          wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg
          echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list > /dev/null
          sudo apt update
          sudo apt install -y openresty --no-install-recommends

      - name: Install toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.toolchain }}
          components: rustfmt, clippy

      - name: Run cargo fmt
        run: cargo fmt --all -- --check

      - name: Run cargo test
        run: cargo test --verbose --lib --bins --tests --no-fail-fast

      # Need to run doc tests separately.
      # (https://github.com/rust-lang/cargo/issues/6669)
      - name: Run cargo doc test
        run: cargo test --verbose --doc

      - name: Run cargo clippy
        run: |
          [[ ${{ matrix.toolchain }} != 1.91.1 ]] || cargo clippy --all-targets --all -- --allow=unknown-lints --deny=warnings

      - name: Run cargo audit
        run: |
          [[ ${{ matrix.toolchain }} != 1.91.1 ]] || (cargo install --locked cargo-audit && cargo generate-lockfile --ignore-rust-version && cargo audit)

      - name: Run cargo machete
        run: |
          [[ ${{ matrix.toolchain }} != 1.91.1 ]] || (cargo install cargo-machete --version 0.7.0 && cargo machete)


================================================
FILE: .github/workflows/docs.yml
================================================
on:
  push:
    branches:
      - master

name: Docs

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v4
        with:
          submodules: "recursive"

      - name: Install build dependencies
        run: |
          sudo apt update
          sudo apt install -y cmake libclang-dev

      - name: Install stable toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Run cargo doc
        run: cargo doc --no-deps --all-features


================================================
FILE: .github/workflows/mark-stale.yaml
================================================
name: 'Close stale questions'
on:
  schedule:
    - cron: '30 1 * * *'
  workflow_dispatch:

permissions:
  issues: write
  pull-requests: write

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: 'This question has been stale for a week. It will be closed in an additional day if not updated.'
          close-issue-message: 'This issue has been closed because it has been stalled with no activity.'
          days-before-stale: -1
          days-before-issue-stale: 7
          days-before-issue-close: 1
          stale-issue-label: 'stale'
          only-issue-labels: 'question'


================================================
FILE: .github/workflows/semgrep.yml
================================================
on:
  pull_request: {}
  workflow_dispatch: {}
  push: 
    branches:
      - main
      - master
  schedule:
    - cron: '0 0 * * *'
name: Semgrep config
jobs:
  semgrep:
    name: semgrep/ci
    runs-on: ubuntu-latest
    env:
      SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
      SEMGREP_URL: https://cloudflare.semgrep.dev
      SEMGREP_APP_URL: https://cloudflare.semgrep.dev
      SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
    container:
      image: returntocorp/semgrep
    steps:
      - uses: actions/checkout@v4
      - run: semgrep ci


================================================
FILE: .gitignore
================================================
Cargo.lock
/target
**/*.rs.bk
dhat-heap.json
.vscode
.idea
.cover
bleeper.user.toml

================================================
FILE: .rustfmt.toml
================================================
edition = "2021"


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

## [0.8.0](https://github.com/cloudflare/pingora/compare/0.7.0...0.8.0) - 2026-03-02


**🚀 Features**

* Add support for client certificate verification in mTLS configuration.
* Add upstream\_write\_pending\_time to Session for upload diagnostics.
* Pipe subrequests utility: creates a state machine to treat subrequests as a "pipe," enabling direct sending of request body and writing of response tasks, with a handler for error propagation and support for reusing a preset or captured input body for chained subrequests.
* Add the ability to limit the number of times a downstream connection can be reused
* Add a system for specifying and using service-level dependencies
* Add a builder for pingora proxy service, e.g. to specify ServerOptions.

**🐛 Bug Fixes**

* Fix various Windows compiler issues.
* Handle custom ALPNs in s2n impl of ALPN::to\_wire\_protocols() to fix s2n compile issues.
* Fix: don't use “all” permissions for socket.
* Fix a bug with the ketama load balancing where configurations were not persisted after updates.
* Ensure http1 downstream session is not reused on more body bytes than expected.
* Send RST\_STREAM CANCEL on application read timeouts for h2 client.
* Start close-delimited body mode after 101 is received for WebSocket upgrades. `UpgradedBody` is now an explicit HttpTask.
* Avoid close delimit mode on http/1.0 req.
* Reject invalid content-length http/1 requests to eliminate ambiguous request framing.
* Validate invalid content-length on http/1 resp by default, and removes content-length from the response if transfer-encoding is present, per RFC.
* Correct the custom protocol code for shutdown: changed the numeric code passed on shutdown to 0 to indicate an explicit shutdown rather than a transport error.

**⚙️ Miscellaneous Tasks**

* Remove `CacheKey::default` impl, users of caching should implement `cache_key_callback` themselves
* Allow server bootstrapping to take place in the context of services with dependents and dependencies
* Don't consider "bytes=" a valid range header: added an early check for an empty/whitespace-only range-set after the `bytes=` prefix, returning 416 Range Not Satisfiable, consistent with RFC 9110 14.1.2.
* Strip {content, transfer}-encoding from 416s to mirror the behavior for 304 Not Modified responses.
* Disable CONNECT method proxying by default, with an option to enable via server options; unsupported requests will now be automatically rejected.

## [0.7.0](https://github.com/cloudflare/pingora/compare/0.6.0...0.7.0) - 2026-01-30

### Highlights

- Extensible SslDigest to save user-defined TLS context
- Add ConnectionFilter trait for early TCP connection filtering

### 🚀 Features

- Add ConnectionFilter trait for early TCP connection filtering
- Introduce a virtual L4 stream abstraction
- Add support for verify_cert and verify_hostname using rustls
- Exposes the HttpProxy struct to allow external crates to customize the proxy logic.
- Exposes a new_mtls method for creating a HttpProxy with a client_cert_key to enable mtls peers.
- Add SSLKEYLOGFILE support to rustls connector
- Allow spawning background subrequests from main session
- Allow Extensions in cache LockCore and user tracing
- Add body-bytes tracking across H1/H2 and proxy metrics
- Allow setting max_weight on MissFinishType::Appended
- Allow adding SslDigestExtensions on downstream and upstream
- Add Custom session support for encapsulated HTTP

### 🐛 Bug Fixes

- Use write timeout consistently for h2 body writes
- Prevent downstream error prior to header from canceling cache fill
- Fix debug log and new tests
- Fix size calculation for buffer capacity
- Fix cache admission on header only misses
- Fix duplicate zero-size chunk on cache hit
- Fix chunked trailer end parsing
- Lock age timeouts cause lock reacquisition
- Fix transfer fd compile error for non linux os

### Sec

- Removed atty
- Upgrade lru to >= 0.16.3 crate version because of RUSTSEC-2026-0002

### Everything Else

- Add tracing to log reason for not caching an asset on cache put
- Evict when asset count exceeds optional watermark
- Remove trailing comma from Display for HttpPeer
- Make ProxyHTTP::upstream_response_body_filter return an optional duration for rate limiting
- Restore daemonize STDOUT/STDERR when error log file is not specified
- Log task info when upstream header failed to send
- Check cache enablement to determine cache fill
- Update meta when revalidating before lock release
- Add ForceFresh status to cache hit filter
- Pass stale status to cache lock
- Bump max multipart ranges to 200
- Downgrade Expires header warn to debug log
- CI and effective msrv bump to 1.83
- Add default noop custom param to client Session
- Use static str in ErrorSource or ErrorType as_str
- Use bstr for formatting byte strings
- Tweak the implementation of and documentation of `connection_filter` feature
- Set h1.1 when proxying cacheable responses
- Add or remove accept-ranges on range header filter
- Update msrv in github ci, fixup .bleep
- Override request keepalive on process shutdown
- Add shutdown flag to proxy session
- Add ResponseHeader in pingora_http crate's prelude
- Add a configurable upgrade for pingora-ketama that reduces runtime cpu and memory
- Add to cache api spans
- Increase visibility of multirange items
- Use seek_multipart on body readers
- Log read error when reading trailers end
- Re-add the warning about cache-api volatility
- Default to close on downstream response before body finish
- Ensure idle_timeout is polled even if idle_timeout is unset so notify events are registered for h2 idle pool, filter out closed connections when retrieving from h2 in use pool.
- Add simple read test for invalid extra char in header end
- Allow customizing lock status on Custom NoCacheReasons
- Close h1 conn by default if req header unfinished
- Add configurable retries for upgrade sock connect/accept
- Deflake test by increasing write size
- Make the version restrictions on rmp and rmp-serde more strict to prevent forcing consumers to use 2024 edition
- Rewind preread bytes when parsing next H1 response
- Add epoch and epoch_override to CacheMeta

## [0.6.0](https://github.com/cloudflare/pingora/compare/0.5.0...0.6.0) - 2025-08-15

### Highlights
- This release bumps the minimum h2 crate dependency to guard against the [MadeYouReset]((https://blog.cloudflare.com/madeyoureset-an-http-2-vulnerability-thwarted-by-rapid-reset-mitigations/)) H2 attack


### 🚀 Features

- Log runtime names during Server shutdown
- Enabling tracking the execution phase of a server
- Allow using in-memory compression dicts
- Make H2Options configurable at HttpServer, HttpProxy
  Also adds HttpServerOptions to the HttpServer implementation, and
  updates the HttpEchoApp to use HttpServer for easier adhoc testing.

### 🐛 Bug Fixes

- Fix: read body without discard

### Everything Else

- Try loading each LRU shard individually and warn on errors
- Update LRU save to disk to be atomic
- Allow cache to spawn_async_purge
- Pass hit handler in hit filter
- Cache hit filter can mutate cache, allow resetting cache lock
- Persist keepalive_timeout between requests on same stream
- Properly check for H2 io ReadError retry types
- Add cache lock wait timeout for readers
- Fix CacheLock status timeout conditions
- Handle close on partial chunk head
- Allow optional to reset session timeouts
- Clippy fixes for 1.87, add 1.87 to GitHub CI
- Run `range_{header,body}_filter` after disabling cache
- Convert `InterpretCacheControl` members to `Duration`
- Disable downstream ranging on max file size
- Allow explicit infinite keepalive timeout to be respected
  Note that a necessary follow up is to refactor the infinite keepalive
  timeout to only apply to first read between requests on reused conns.
- Add method to disable keepalive if downstream is unfinished
- Discard extra upstream body and disable keepalive
- Explicitly disable keepalive on upstream connection when excess body
  (content-length) is detected.
- Add brief sleep to shutdown signal tests to avoid flake
- Allow override of cache lock timeouts
- Allow arbitrary bytes in CacheKey instead of just Strings
- Corrects out-of-order data return after multiple peek calls with different buffer sizes.
- Mark previously too large chunked assets as cacheable
- Boring/OpenSSL load cert chain from connector options
- Add initial support for multipart range requests
- Adds a callback to HttpHealthCheck for collecting detailed backend summary information
- Multipart range filter state fixes


### Docs

- Explanation of request_body_filter phase



## [0.5.0](https://github.com/cloudflare/pingora/compare/0.4.0...0.5.0) - 2025-05-09

### 🚀 Features

- [Add tweak_new_upstream_tcp_connection hook to invoke logic on new upstream TCP sockets prior to connection](https://github.com/cloudflare/pingora/commit/be4a023d18c2b061f64ad5efd0868f9498199c91)
- [Add ability to configure max retries for upstream proxy failures](https://github.com/cloudflare/pingora/commit/6c5d6021a6e67c971e835bef269655d0db94c2d1)
- [Allow tcp user timeout to be configurable](https://github.com/cloudflare/pingora/commit/e77ca63da58892281f36dcb97c51a8b1e882e2f6)
- [Add peer address to downstream handshake error logs](https://github.com/cloudflare/pingora/commit/3f9e0a2fae8feaea12a1a9687e6b4bf4616f66c5)
- [Allow proxy to set stream level downstream read timeout](https://github.com/cloudflare/pingora/commit/87ae8ce2e7883c0a924a776b193c8a4f858b9349)
- [Improve support for sending custom response headers and bodies for error messages](https://github.com/cloudflare/pingora/commit/a8a6e77eef2c0f4d2a45f00c5b0e316dd373f2f2)
- [Allow configuring multiple listener tasks per endpoint](https://github.com/cloudflare/pingora/commit/69254671148938f6bc467f6decc2fc89ee7f531e)
- [Add get_stale and get_stale_while_update for memory-cache](https://github.com/cloudflare/pingora/commit/bb28044cbe9ac9251940b8a313d970c7d15aaff6)

### 🐛 Bug Fixes

- [Fix deadloop if proxy_handle_upstream exits earlier than proxy_handle_downstream](https://github.com/cloudflare/pingora/commit/bb111aaa92b3753e650957df3a68f56b0cffc65d)
- [Check on h2 stream end if error occurred for forwarding HTTP tasks](https://github.com/cloudflare/pingora/commit/e18f41bb6ddb1d6354e824df3b91d77f3255bea2)
- [Check for content-length underflow on end of stream h2 header](https://github.com/cloudflare/pingora/commit/575d1aafd7c679a50a443701a4c55dcfdbc443b2)
- [Correctly send empty h2 data frames prior to capacity polling](https://github.com/cloudflare/pingora/commit/c54190432a2efea30c5a0187bb7d078d33570a43)
- [Signal that the response is done when body write finishes to avoid h1 downstream/h2 upstream errors](https://github.com/cloudflare/pingora/commit/5750e4279e75b1e764dcfc5530aa7a7cebe3abef)
- [Ignore h2 pipe error when finishing an H2 upstream](https://github.com/cloudflare/pingora/commit/8ad15031291eb5779e0e93e714eb969c4132f632)
- [Add finish_request_body() for HTTP healthchecks so that H2 healthchecks succeed](https://github.com/cloudflare/pingora/commit/67bc7cc170e754d335cc1d6d526f203c4345eceb)
- [Fix Windows compile errors by updating `impl<T> UniqueID` to use correct return type](https://github.com/cloudflare/pingora/commit/1756948df77d257bddf7ab798cc3fddf348a91c8)
- [Fixed compilation errors on Windows](https://github.com/cloudflare/pingora/commit/906cb90864bf6e441727083c9cbd4f6fb289d6f5)
- [Poll for H2 capacity before sending H2 body to propagate backpressure](https://github.com/cloudflare/pingora/commit/b6f24ff3725d9d8b6a740d87cad959d94befbe54)
- [Fix for write_error_response for http2 downstreams to set EOS](https://github.com/cloudflare/pingora/commit/c0fa5065812d87e6e404c5624b26cd99c5194079)
- [Always drain v1 request body before session reuse](https://github.com/cloudflare/pingora/commit/fda3317ec822678564d641e7cf1c9b77ee3759ff)
- [Fixes HTTP1 client reads to properly timeout on initial read](https://github.com/cloudflare/pingora/commit/3c7db34acb0d930ae7043290a88bc56c1cd77e45)
- [Fixes issue where if TLS client never sends any bytes, hangs forever](https://github.com/cloudflare/pingora/commit/d1bf0bcac98f943fd716278d674e7d10dce2223e)

### Everything Else

- [Add builder api for pingora listeners](https://github.com/cloudflare/pingora/commit/3f564af3ae56e898478e13e71d67d095d7f5dbbd)
- [Better handling for h1 requests that contain both transfer-encoding and content-length](https://github.com/cloudflare/pingora/commit/9287b82645be4a52b0b63530ba38aa0c7ddc4b77)
- [Allow setting raw path in request to support non-UTF8 use cases](https://github.com/cloudflare/pingora/commit/e6b823c5d89860bb97713fdf14f197f799aed6af)
- [Allow reusing session on errors prior to proxy upstream](https://github.com/cloudflare/pingora/commit/f8d01278a586c60392b1e3b92e5ed97a415d8fe7)
- [Avoid allocating large buffer in the accept() loop](https://github.com/cloudflare/pingora/commit/ef234f5baa45650be064c7dd34c2f17986361480)
- [Ensure HTTP/1.1 when forcing chunked encoding](https://github.com/cloudflare/pingora/commit/9281cab8eab1b545f15f0e387d2ba4cd2ca27364)
- [Reject if the HTTP header contains duplicated Content-Length values](https://github.com/cloudflare/pingora/commit/eef35768d11305d1293468a6c3ce91a3858dc0fc)
- [proxy_upstream_filter tries to reuse downstream by default](https://github.com/cloudflare/pingora/commit/86293e65b5c7d8a96f3a333a1f191766dc95bee5)
- [Allow building server that avoids std::process::exit during shutdown](https://github.com/cloudflare/pingora/commit/2d977d4eb808d8bcbc0ce87cabac4cf4854dfb80)
- [Update Sentry crate to 0.36](https://github.com/cloudflare/pingora/commit/01a1f9a65c51a4351c29d6961ea3164a6a811958)
- [Update the bounds on `MemoryCache` methods to accept broader key types](https://github.com/cloudflare/pingora/commit/d66923a9a41d00b326cef5dfb57d8c020d6a4abb)
- [Flush already received data if upstream write errors](https://github.com/cloudflare/pingora/commit/aa7c2f1a89a652137a987e5f5dbdab228c2f4d06)
- [Allow modules to receive HttpTask::Done, flush response compression on receiving Done task](https://github.com/cloudflare/pingora/commit/c82fb6ba57b95c256b58095881a33a9bc08f170a)
- API signature changes as part of experimental proxy cache support
- Note MSRV was effectively bumped to 1.82 from 1.72 due to a dependency update, though older compilers may still be able to build by pinning dependencies, e.g. `cargo update -p backtrace --precise 0.3.74`.

## [0.4.0](https://github.com/cloudflare/pingora/compare/0.3.0...0.4.0) - 2024-11-01

### 🚀 Features
- [Add preliminary rustls support](https://github.com/cloudflare/pingora/commit/354a6ee1e99b82e23fc0f27a37d8bf41e62b2dc5)
- [Add experimental support for windows](https://github.com/cloudflare/pingora/commit/4aadba12727afe6178f3b9fc2a3cad2223ac7b2e)
- [Add the option to use no TLS implementation](https://github.com/cloudflare/pingora/commit/d8f3ffae77ddc1edd285ab1d517a1b6748ce3d58)
- [Add support for gRPC-web module to bridge gRPC-web client requests to gRPC server requests](https://github.com/cloudflare/pingora/commit/9917177c646a0ab58197f15ec57a3bcbe1e0a201)
- [Add the support for h2c and http1 to coexist](https://github.com/cloudflare/pingora/commit/792d5fd3c14c1cd588b155ddf09c09a4c125a26b)
- [Add the support for custom L4 connector](https://github.com/cloudflare/pingora/commit/7c122e7f36de5c946ac960a1691c5dd41f26e6e6)
- [Support opaque extension field in Backend](https://github.com/cloudflare/pingora/commit/999e379064d2c1266a267abdf9f4f41b14bffcf5)
- [Add the ability to ignore informational responses when proxying downstream](https://github.com/cloudflare/pingora/commit/be97e35031cf4f5a01191f1848bdf491bd9f0d62)
- [Add un-gzip support and allow decompress by algorithm](https://github.com/cloudflare/pingora/commit/e1c6e57db3e613991eda3160d15f81e0669ea066)
- [Add the ability to observe backend health status](https://github.com/cloudflare/pingora/commit/8a0c73f174a27a87c54426a748c4818b10de9425)
- [Add the support for passing sentry release](https://github.com/cloudflare/pingora/commit/07a970e413009ee62fc4c15e0820ae1aa036af22)
- [Add the support for binding to local port ranges](https://github.com/cloudflare/pingora/commit/d1d7a87b761eeb4f71fcaa3f7c4ae8e32f1d93c8)
- [Support retrieving rx timestamp for TcpStream](https://github.com/cloudflare/pingora/commit/d811795938cee5a6eb7cd46399cef17210a0d0c5)

### 🐛 Bug Fixes
- [Handle bare IPv6 address in raw connect Host](https://github.com/cloudflare/pingora/commit/9f50e6ccb09db2940eec6fc170a1e9e9b14a95d0)
- [Set proper response headers when compression is enabled](https://github.com/cloudflare/pingora/commit/55049c4e7983055551b34feee397c736ffc912bb)
- [Check the current advertised h2 max streams](https://github.com/cloudflare/pingora/commit/7419b1967e7686b00aefb7bcd2a4dfe59b31e639)
- Other bug fixes and improvements


### ⚙️ Changes and Miscellaneous Tasks
- [Make sentry an optional feature](https://github.com/cloudflare/pingora/commit/ab1b717bf587723c1c537d6549a8f8096f0900d4)
- [Make timeouts Sync](https://github.com/cloudflare/pingora/commit/18db42cd2cb892432fd7896f0da7e9d19221214b)
- [Retry all h2 connection when encountering graceful shutdown](https://github.com/cloudflare/pingora/commit/11b5882a422774cffbd14d9a9ea7dfc9dc98b02c)
- [Make l4 module pub to expose Connect](https://github.com/cloudflare/pingora/commit/91702bb0c0c5e1f2d5e2f40a19a3f340bb5a6d82)
- [Auto snake case set-cookie header when downgrade to from h2 to http1.1](https://github.com/cloudflare/pingora/commit/2c6190c634f2a5dd2f00e8597902f2b735a9d84f)
- [shutdown h2 connection gracefully with GOAWAYs](https://github.com/cloudflare/pingora/commit/04d7cfeef6205d2cf33ad5704a363ee107250771)
- Other API signature updates

## [0.3.0](https://github.com/cloudflare/pingora/compare/0.2.0...0.3.0) - 2024-07-12

### 🚀 Features
- Add support for HTTP modules. This feature allows users to import modules written by 3rd parties.
- Add `request_body_filter`. Now request body can be inspected and modified.
- Add H2c support.
- Add TCP fast open support.
- Add support for server side TCP keep-alive.
- Add support to get TCP_INFO.
- Add support to set DSCP.
- Add `or_err()`/`or_err_with` API to convert `Options` to `pingora::Error`.
- Add `or_fail()` API to convert `impl std::error::Error` to `pingora::Error`.
- Add the API to track socket read and write pending time.
- Compression: allow setting level per algorithm.

### 🐛 Bug Fixes
- Fixed a panic when using multiple H2 streams in the same H2 connection to upstreams.
- Pingora now respects the `Connection` header it sends to upstream.
- Accept-Ranges header is now removed when response is compressed.
- Fix ipv6_only socket flag.
- A new H2 connection is opened now if the existing connection returns GOAWAY with graceful shutdown error.
- Fix a FD mismatch error when 0.0.0.0 is used as the upstream IP

### ⚙️ Changes and Miscellaneous Tasks
- Dependency: replace `structopt` with `clap`
- Rework the API of HTTP modules
- Optimize remove_header() API call
- UDS parsing now requires the path to have `unix:` prefix. The support for the path without prefix is deprecated and will be removed on the next release.
- Other minor API changes

## [0.2.0](https://github.com/cloudflare/pingora/compare/0.1.1...0.2.0) - 2024-05-10

### 🚀 Features
- Add support for downstream h2 trailers and add an upstream h2 response trailer filter
- Add the ability to set TCP recv buf size
- Add a convenience function to retrieve Session digest
- Add `body_bytes_read()` method to Session
- Add `cache_not_modified_filter`
- Add `SSLKEYLOG` support for tls upstream
- Add `Service<HttpProxy<T>>` constructor for providing name
- Add `purge_response` callback
- Make `pop_closed` pub, to simplify DIY drains

### 🐛 Bug Fixes
- Fixed gRPC trailer proxying
- Fixed `response_body_filter` `end_of_stream` always being false
- Fixed compile error in Rust <= 1.73
- Fixed non linux build
- Fixed the counting problem of used_weight data field in `LruUnit<T>`
- Fixed `cargo run --example server` missing cert
- Fixed error log string interpolation outside of proper context
- Fixed tinylfu test flake

### ⚙️ Changes and Miscellaneous Tasks
- API change: `Server::run_forever` now takes ownership and ensures exit semantics
- API change: `cleanup()` method of `ServerApp` trait is now async
- Behavior change: Always return `HttpTask::Body` on body done instead of `HttpTask::done`
- Behavior change: HTTP/1 reason phrase is now parsed and proxied
- Updated `h2` dependency for RUSTSEC-2024-0332
- Updated zstd dependencies
- Code optimization and refactor in a few crates
- More examples and docs

## [0.1.1](https://github.com/cloudflare/pingora/compare/0.1.0...0.1.1) - 2024-04-05

### 🚀 Features
- `Server::new` now accepts `Into<Option<T>>`
- Implemented client `HttpSession::get_keepalive_values` for Keep-Alive parsing
- Expose `ListenFds` and `Fds` to fix a voldemort types issue
- Expose config options in `ServerConf`, provide new `Server` constructor
- `upstream_response_filter` now runs on upstream 304 responses during cache revalidation
- Added `server_addr` and `client_addr` APIs to `Session`
- Allow body modification in `response_body_filter`
- Allow configuring grace period and graceful shutdown timeout
- Added TinyUFO sharded skip list storage option

### 🐛 Bug Fixes
- Fixed build failures with the `boringssl` feature
- Fixed compile warnings with nightly Rust
- Fixed an issue where Upgrade request bodies might not be handled correctly
- Fix compilation to only include openssl or boringssl rather than both
- Fix OS read errors so they are reported as `ReadError` rather than `ReadTimeout` when reading http/1.1 response headers

### ⚙️ Miscellaneous Tasks
- Performance improvements in `pingora-ketama`
- Added more TinyUFO benchmarks
- Added tests for `pingora-cache` purge
- Limit buffer size for `InvalidHTTPHeader` error logs
- Example code: improvements in pingora client, new LB cluster example
- Typo fixes and clarifications across comments and docs

## [0.1.0] - 2024-02-28
### Highlights
- First Public Release of Pingora 🎉


================================================
FILE: Cargo.toml
================================================





[workspace]
resolver = "2"
members = [
    "pingora",
    "pingora-core",
    "pingora-pool",
    "pingora-error",
    "pingora-limits",
    "pingora-timeout",
    "pingora-header-serde",
    "pingora-proxy",
    "pingora-cache",
    "pingora-http",
    "pingora-lru",
    "pingora-openssl",
    "pingora-boringssl",
    "pingora-runtime",
    "pingora-rustls",
    "pingora-s2n",
    "pingora-ketama",
    "pingora-load-balancing",
    "pingora-memory-cache",
    "tinyufo",
]

[workspace.dependencies]
bstr = "1.12.0"
tokio = "1"
tokio-stream = { version = "0.1" }
async-trait = "0.1.42"
httparse = "1"
bytes = "1.0"
derivative = "2.2.0"
http = "1"
log = "0.4"
h2 = ">=0.4.11"
once_cell = "1"
lru = "0.16.3"
ahash = ">=0.8.9"

[profile.bench]
debug = true


================================================
FILE: Dockerfile
================================================
FROM debian:latest as builder

ARG BUILDARCH
RUN apt-get -qq update \
    && apt-get -qq install -y --no-install-recommends \
       gcc g++ libfindbin-libs-perl \
       make cmake libclang-dev git \
       wget curl gnupg ca-certificates lsb-release \
    && wget --no-check-certificate -O - https://openresty.org/package/pubkey.gpg | gpg --dearmor -o /usr/share/keyrings/openresty.gpg \
    && if [ "${BUILDARCH}" = "arm64" ]; then URL="http://openresty.org/package/arm64/debian"; else URL="http://openresty.org/package/debian"; fi \
    && echo "deb [arch=$BUILDARCH signed-by=/usr/share/keyrings/openresty.gpg] ${URL} $(lsb_release -sc) openresty" | tee /etc/apt/sources.list.d/openresty.list > /dev/null \
    && apt-get -qq update \
    && apt-get -qq install -y openresty --no-install-recommends

RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

WORKDIR /var/opt/pingora
COPY . .
RUN cargo build


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
# Pingora

![Pingora banner image](./docs/assets/pingora_banner.png)

## What is Pingora
Pingora is a Rust framework to [build fast, reliable and programmable networked systems](https://blog.cloudflare.com/pingora-open-source).

Pingora is battle tested as it has been serving more than 40 million Internet requests per second for [more than a few years](https://blog.cloudflare.com/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet).

## Feature highlights
* Async Rust: fast and reliable
* HTTP 1/2 end to end proxy
* TLS over OpenSSL, BoringSSL, s2n-tls, or rustls(experimental).
* gRPC and websocket proxying
* Graceful reload
* Customizable load balancing and failover strategies
* Support for a variety of observability tools

## Reasons to use Pingora
* **Security** is your top priority: Pingora is a more memory safe alternative for services that are written in C/C++
* Your service is **performance-sensitive**: Pingora is fast and efficient
* Your service requires extensive **customization**: The APIs Pingora proxy framework provides are highly programmable

# Getting started

See our [quick starting guide](./docs/quick_start.md) to see how easy it is to build a load balancer.

Our [user guide](./docs/user_guide/index.md) covers more topics such as how to configure and run Pingora servers, as well as how to build custom HTTP servers and proxy logic on top of Pingora's framework.

API docs are also available for all the crates.

# Notable crates in this workspace
* Pingora: the "public facing" crate to build networked systems and proxies
* Pingora-core: this crate defines the protocols, functionalities and basic traits
* Pingora-proxy: the logic and APIs to build HTTP proxies
* Pingora-error: the common error type used across Pingora crates
* Pingora-http: the HTTP header definitions and APIs
* Pingora-openssl & pingora-boringssl: SSL related extensions and APIs
* Pingora-ketama: the [Ketama](https://github.com/RJ/ketama) consistent algorithm
* Pingora-limits: efficient counting algorithms
* Pingora-load-balancing: load balancing algorithm extensions for pingora-proxy
* Pingora-memory-cache: Async in-memory caching with cache lock to prevent cache stampede
* Pingora-s2n: SSL extensions and APIs related to s2n-tls
* Pingora-timeout: A more efficient async timer system
* TinyUfo: The caching algorithm behind pingora-memory-cache

Note that Pingora proxy integration with caching should be considered experimental, and as such APIs related to caching are currently highly volatile.

# System requirements

## Systems
Linux is our tier 1 environment and main focus.

We will try our best for most code to compile for Unix environments. This is for developers and users to have an easier time developing with Pingora in Unix-like environments like macOS (though some features might be missing)

Windows support is preliminary by community's best effort only.

Both x86_64 and aarch64 architectures will be supported.

## Rust version

Pingora keeps a rolling MSRV (minimum supported Rust version) policy of 6 months. This means we will accept PRs that upgrade the MSRV as long as the new Rust version used is at least 6 months old. However, we generally will not bump the highest MSRV across the workspace without a sufficiently compelling reason.

Our current MSRV is 1.84.

Currently not all crates enforce `rust-version` as it is possible to use some crates on lower versions.

## Build Requirements

Some of the crates in this repository have dependencies on additional tools and
libraries that must be satisfied in order to build them:

* Make sure that [Clang] is installed on your system (for boringssl)
* Make sure that [Perl 5] is installed on your system (for openssl)

[Clang]:https://clang.llvm.org/
[Perl 5]:https://www.perl.org/

# Contributing
Please see our [contribution guidelines](./.github/CONTRIBUTING.md).

# License
This project is Licensed under [Apache License, Version 2.0](./LICENSE).


================================================
FILE: cliff.toml
================================================
# git-cliff ~ default configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.

[changelog]
# changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
  {% if previous.version %}\
    ## [{{ version | trim_start_matches(pat="v") }}](https://github.com/cloudflare/pingora/compare/{{ previous.version }}...{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
  {% else %}\
    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
  {% endif %}\
{% else %}\
    ## [unreleased]
{% endif %}\

### Highlights
  - Human-written change summaries go here

{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group | striptags | trim | upper_first }}
    {% for commit in commits %}
        - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
            {% if commit.breaking %}[**breaking**] {% endif %}\
            {{ commit.message | upper_first }}\
    {% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
"""
# remove the leading and trailing whitespace
trim = true

[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true

# filter out the commits that are not conventional
filter_unconventional = false

# process each line of a commit as an individual commit
split_commits = false

# regex for preprocessing the commit messages
commit_preprocessors = [
  { pattern = '\n\w+(?:\-\w+)*:\s+[^\n]+', replace = "\n" },
  { pattern = '\n+', replace = "\n  " },
  { pattern = '\s+$', replace = "" }
]

# regex for parsing and grouping commits
commit_parsers = [
  { message = "^feat", group = "<!-- 0 -->🚀 Features" },
  { message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
  { message = "^doc", group = "<!-- 3 -->📚 Documentation", skip = true  },
  { message = "^perf", group = "<!-- 4 -->⚡ Performance" },
  { message = "^refactor", group = "<!-- 2 -->🚜 Refactor", skip = true  },
  { message = "^style", group = "<!-- 5 -->🎨 Styling", skip = true  },
  { message = "^test", group = "<!-- 6 -->🧪 Testing", skip = true  },
  { message = "^chore\\(release\\): prepare for", skip = true },
  { message = "^chore\\(deps.*\\)", skip = true },
  { message = "^chore\\(pr\\)", skip = true },
  { message = "^chore\\(pull\\)", skip = true },
  { message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
  { body = ".*security", group = "<!-- 8 -->🛡️ Security" },
  { message = "^revert", group = "<!-- 9 -->◀️ Revert" },
  { message = '\S+(?:\s+\S+){6,}', group = "<!--10--> Everything Else" }
]

# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false

# filter out the commits that are not matched by commit parsers
filter_commits = false
tag_pattern = "[0-9].[0-9].[0-9]"
topo_order = false

================================================
FILE: clippy.toml
================================================
msrv = "1.84"


================================================
FILE: docs/README.md
================================================
# Pingora User Manual

## Quick Start
In this section we show you how to build a bare-bones load balancer.

[Read the quick start here.](quick_start.md)

## User Guide
Covers how to configure and run Pingora servers, as well as how to build custom HTTP server and proxy logic on top of Pingora's framework.

[Read the user guide here.](user_guide/index.md)

## API Reference
TBD


================================================
FILE: docs/quick_start.md
================================================
# Quick Start: load balancer

## Introduction

This quick start shows how to build a bare-bones load balancer using pingora and pingora-proxy.

The goal of the load balancer is for every incoming HTTP request, select one of the two backends: https://1.1.1.1 and https://1.0.0.1 in a round-robin fashion.

## Build a basic load balancer

Create a new cargo project for our load balancer. Let's call it `load_balancer`

```
cargo new load_balancer
```

### Include the Pingora Crate and Basic Dependencies

In your project's `cargo.toml` file add the following to your dependencies
```
async-trait="0.1"
pingora = { version = "0.3", features = [ "lb" ] }
```

### Create a pingora server
First, let's create a pingora server. A pingora `Server` is a process which can host one or many
services. The pingora `Server` takes care of configuration and CLI argument parsing, daemonization,
signal handling, and graceful restart or shutdown.

The preferred usage is to initialize the `Server` in the `main()` function and
use `run_forever()` to spawn all the runtime threads and block the main thread until the server is
ready to exit.


```rust
use async_trait::async_trait;
use pingora::prelude::*;
use std::sync::Arc;

fn main() {
    let mut my_server = Server::new(None).unwrap();
    my_server.bootstrap();
    my_server.run_forever();
}
```

This will compile and run, but it doesn't do anything interesting.

### Create a load balancer proxy
Next let's create a load balancer. Our load balancer holds a static list of upstream IPs. The `pingora-load-balancing` crate already provides the `LoadBalancer` struct with common selection algorithms such as round robin and hashing. So let’s just use it. If the use case requires more sophisticated or customized server selection logic, users can simply implement it themselves in this function.


```rust
pub struct LB(Arc<LoadBalancer<RoundRobin>>);
```

In order to make the server a proxy, we need to implement the `ProxyHttp` trait for it.

Any object that implements the `ProxyHttp` trait essentially defines how a request is handled in
the proxy. The only required method in the `ProxyHttp` trait is `upstream_peer()` which returns
the address where the request should be proxied to.

In the body of the `upstream_peer()`, let's use the `select()` method for the `LoadBalancer` to round-robin across the upstream IPs. In this example we use HTTPS to connect to the backends, so we also need to specify to `use_tls` and set the SNI when constructing our [`Peer`](user_guide/peer.md)) object.

```rust
#[async_trait]
impl ProxyHttp for LB {

    /// For this small example, we don't need context storage
    type CTX = ();
    fn new_ctx(&self) -> () {
        ()
    }

    async fn upstream_peer(&self, _session: &mut Session, _ctx: &mut ()) -> Result<Box<HttpPeer>> {
        let upstream = self.0
            .select(b"", 256) // hash doesn't matter for round robin
            .unwrap();

        println!("upstream peer is: {upstream:?}");

        // Set SNI to one.one.one.one
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}
```

In order for the 1.1.1.1 backends to accept our requests, a host header must be present. Adding this header
can be done by the `upstream_request_filter()` callback which modifies the request header after
the connection to the backends are established and before the request header is sent.

```rust
impl ProxyHttp for LB {
    // ...
    async fn upstream_request_filter(
        &self,
        _session: &mut Session,
        upstream_request: &mut RequestHeader,
        _ctx: &mut Self::CTX,
    ) -> Result<()> {
        upstream_request.insert_header("Host", "one.one.one.one").unwrap();
        Ok(())
    }
}
```


### Create a pingora-proxy service
Next, let's create a proxy service that follows the instructions of the load balancer above.

A pingora `Service` listens to one or multiple (TCP or Unix domain socket) endpoints. When a new connection is established
the `Service` hands the connection over to its "application." `pingora-proxy` is such an application
which proxies the HTTP request to the given backend as configured above.

In the example below, we create a `LB` instance with two backends `1.1.1.1:443` and `1.0.0.1:443`.
We put that `LB` instance to a proxy `Service` via the  `http_proxy_service()` call and then tell our
`Server` to host that proxy `Service`.

```rust
fn main() {
    let mut my_server = Server::new(None).unwrap();
    my_server.bootstrap();

    let upstreams =
        LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();

    let mut lb = http_proxy_service(&my_server.configuration, LB(Arc::new(upstreams)));
        lb.add_tcp("0.0.0.0:6188");

    my_server.add_service(lb);

    my_server.run_forever();
}
```

### Run it

Now that we have added the load balancer to the service, we can run our new 
project with 

```cargo run```

To test it, simply send the server a few requests with the command:
```
curl 127.0.0.1:6188 -svo /dev/null
```

You can also navigate your browser to [http://localhost:6188](http://localhost:6188)

The following output shows that the load balancer is doing its job to balance across the two backends:
```
upstream peer is: Backend { addr: Inet(1.0.0.1:443), weight: 1 }
upstream peer is: Backend { addr: Inet(1.1.1.1:443), weight: 1 }
upstream peer is: Backend { addr: Inet(1.0.0.1:443), weight: 1 }
upstream peer is: Backend { addr: Inet(1.1.1.1:443), weight: 1 }
upstream peer is: Backend { addr: Inet(1.0.0.1:443), weight: 1 }
...
```

Well done! At this point you have a functional load balancer. It is a _very_ 
basic load balancer though, so the next section will walk you through how to
make it more robust with some built-in pingora tooling.

## Add functionality

Pingora provides several helpful features that can be enabled and configured 
with just a few lines of code. These range from simple peer health checks to 
the ability to seamlessly update running binary with zero service interruptions.

### Peer health checks

To make our load balancer more reliable, we would like to add some health checks 
to our upstream peers. That way if there is a peer that has gone down, we can 
quickly stop routing our traffic to that peer.

First let's see how our simple load balancer behaves when one of the peers is
down. To do this, we'll update the list of peers to include a peer that is 
guaranteed to be broken.

```rust
fn main() {
    // ...
    let upstreams =
        LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443", "127.0.0.1:343"]).unwrap();
    // ...
}
```

Now if we run our load balancer again with `cargo run`, and test it with 

```
curl 127.0.0.1:6188 -svo /dev/null
```

We can see that one in every 3 request fails with `502: Bad Gateway`. This is 
because our peer selection is strictly following the `RoundRobin` selection 
pattern we gave it with no consideration to whether that peer is healthy. We can
fix this by adding a basic health check service. 

```rust
fn main() {
    let mut my_server = Server::new(None).unwrap();
    my_server.bootstrap();

    // Note that upstreams needs to be declared as `mut` now
    let mut upstreams =
        LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443", "127.0.0.1:343"]).unwrap();

    let hc = TcpHealthCheck::new();
    upstreams.set_health_check(hc);
    upstreams.health_check_frequency = Some(std::time::Duration::from_secs(1));

    let background = background_service("health check", upstreams);
    let upstreams = background.task();

    // `upstreams` no longer need to be wrapped in an arc
    let mut lb = http_proxy_service(&my_server.configuration, LB(upstreams));
    lb.add_tcp("0.0.0.0:6188");

    my_server.add_service(background);

    my_server.add_service(lb);
    my_server.run_forever();
}
```

Now if we again run and test our load balancer, we see that all requests 
succeed and the broken peer is never used. Based on the configuration we used, 
if that peer were to become healthy again, it would be re-included in the round
robin again in within 1 second.

### Command line options

The pingora `Server` type provides a lot of built-in functionality that we can
take advantage of with single-line change. 

```rust
fn main() {
    let mut my_server = Server::new(Some(Opt::parse_args())).unwrap();
    ...
}
```

With this change, the command-line arguments passed to our load balancer will be 
consumed by Pingora. We can test this by running:

```
cargo run -- -h
```

We should see a help menu with the list of arguments now available to us. We 
will take advantage of those in the next sections to do more with our load 
balancer for free

### Running in the background

Passing the parameter `-d` or `--daemon` will tell the program to run in the background.

```
cargo run -- -d
```

To stop this service, you can send `SIGTERM` signal to it for a graceful shutdown, in which the service will stop accepting new request but try to finish all ongoing requests before exiting.
```
pkill -SIGTERM load_balancer
```
 (`SIGTERM` is the default signal for `pkill`.)

### Configurations
Pingora configuration files help define how to run the service. Here is an 
example config file that defines how many threads the service can have, the 
location of the pid file, the error log file, and the upgrade coordination 
socket (which we will explain later). Copy the contents below and put them into
a file called `conf.yaml` in your `load_balancer` project directory.

```yaml
---
version: 1
threads: 2
pid_file: /tmp/load_balancer.pid
error_log: /tmp/load_balancer_err.log
upgrade_sock: /tmp/load_balancer.sock
```

To use this conf file:
```
RUST_LOG=INFO cargo run -- -c conf.yaml -d
```
`RUST_LOG=INFO` is here so that the service actually populate the error log.

Now you can find the pid of the service.
```
 cat /tmp/load_balancer.pid
```

### Gracefully upgrade the service
(Linux only)

Let's say we changed the code of the load balancer and recompiled the binary. Now we want to upgrade the service running in the background to this newer version.

If we simply stop the old service, then start the new one, some request arriving in between could be lost. Fortunately, Pingora provides a graceful way to upgrade the service.

This is done by, first, send `SIGQUIT` signal to the running server, and then start the new server with the parameter `-u` \ `--upgrade`.

```
pkill -SIGQUIT load_balancer &&\
RUST_LOG=INFO cargo run -- -c conf.yaml -d -u
```

In this process, The old running server will wait and hand over its listening sockets to the new server. Then the old server runs until all its ongoing requests finish.

From a client's perspective, the service is always running because the listening socket is never closed.

## Full examples

The full code for this example is available in this repository under

[pingora-proxy/examples/load_balancer.rs](../pingora-proxy/examples/load_balancer.rs)

Other examples that you may find helpful are also available here

[pingora-proxy/examples/](../pingora-proxy/examples/)
[pingora/examples](../pingora/examples/)

================================================
FILE: docs/user_guide/conf.md
================================================
# Configuration

A Pingora configuration file is a list of Pingora settings in yaml format.

Example
```yaml
---
version: 1
threads: 2
pid_file: /run/pingora.pid
upgrade_sock: /tmp/pingora_upgrade.sock
user: nobody
group: webusers
```
## Settings
| Key      | meaning        | value type |
| ------------- |-------------| ----|
| version | the version of the conf, currently it is a constant `1` | number |
| pid_file | The path to the pid file | string |
| daemon | whether to run the server in the background | bool |
| error_log | the path to error log output file. STDERR is used if not set | string |
| upgrade_sock | the path to the upgrade socket. | string |
| threads | number of threads per service | number |
| user | the user the pingora server should be run under after daemonization | string |
| group | the group the pingora server should be run under after daemonization | string |
| client_bind_to_ipv4 | source IPv4 addresses to bind to when connecting to server | list of string |
| client_bind_to_ipv6 | source IPv6 addresses to bind to when connecting to server| list of string |
| ca_file | The path to the root CA file | string |
| s2n_config_cache_size | The maximum number of unique s2n configs to cache. A value of 0 disables the cache. Default: 10 (s2n-tls only) | number |
| work_stealing | Enable work stealing runtime (default true). See Pingora runtime (WIP) section for more info | bool |
| upstream_keepalive_pool_size | The number of total connections to keep in the connection pool | number |

## Extension
Any unknown settings will be ignored. This allows extending the conf file to add and pass user defined settings. See User defined configuration section.


================================================
FILE: docs/user_guide/ctx.md
================================================
# Sharing state across phases with `CTX`

## Using `CTX`
The custom filters users implement in different phases of the request don't interact with each other directly. In order to share information and state across the filters, users can define a `CTX` struct. Each request owns a single `CTX` object. All the filters are able to read and update members of the `CTX` object. The CTX object will be dropped at the end of the request.

### Example

In the following example, the proxy parses the request header in the `request_filter` phase, it stores the boolean flag so that later in the `upstream_peer` phase the flag is used to decide which server to route traffic to. (Technically, the header can be parsed in `upstream_peer` phase, but we just do it in an earlier phase just for the demonstration.)

```Rust
pub struct MyProxy();

pub struct MyCtx {
    beta_user: bool,
}

fn check_beta_user(req: &pingora_http::RequestHeader) -> bool {
    // some simple logic to check if user is beta
    req.headers.get("beta-flag").is_some()
}

#[async_trait]
impl ProxyHttp for MyProxy {
    type CTX = MyCtx;
    fn new_ctx(&self) -> Self::CTX {
        MyCtx { beta_user: false }
    }

    async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result<bool> {
        ctx.beta_user = check_beta_user(session.req_header());
        Ok(false)
    }

    async fn upstream_peer(
        &self,
        _session: &mut Session,
        ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>> {
        let addr = if ctx.beta_user {
            info!("I'm a beta user");
            ("1.0.0.1", 443)
        } else {
            ("1.1.1.1", 443)
        };

        let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}
```

## Sharing state across requests
Sharing state such as a counter, cache and other info across requests is common. There is nothing special needed for sharing resources and data across requests in Pingora. `Arc`, `static` or any other mechanism can be used.


### Example
Let's modify the example above to track the number of beta visitors as well as the number of total visitors. The counters can either be defined in the `MyProxy` struct itself or defined as a global variable. Because the counters can be concurrently accessed, Mutex is used here.

```Rust
// global counter
static REQ_COUNTER: Mutex<usize> = Mutex::new(0);

pub struct MyProxy {
    // counter for the service
    beta_counter: Mutex<usize>, // AtomicUsize works too
}

pub struct MyCtx {
    beta_user: bool,
}

fn check_beta_user(req: &pingora_http::RequestHeader) -> bool {
    // some simple logic to check if user is beta
    req.headers.get("beta-flag").is_some()
}

#[async_trait]
impl ProxyHttp for MyProxy {
    type CTX = MyCtx;
    fn new_ctx(&self) -> Self::CTX {
        MyCtx { beta_user: false }
    }

    async fn request_filter(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result<bool> {
        ctx.beta_user = check_beta_user(session.req_header());
        Ok(false)
    }

    async fn upstream_peer(
        &self,
        _session: &mut Session,
        ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>> {
        let mut req_counter = REQ_COUNTER.lock().unwrap();
        *req_counter += 1;

        let addr = if ctx.beta_user {
            let mut beta_count = self.beta_counter.lock().unwrap();
            *beta_count += 1;
            info!("I'm a beta user #{beta_count}");
            ("1.0.0.1", 443)
        } else {
            info!("I'm an user #{req_counter}");
            ("1.1.1.1", 443)
        };

        let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}
```

The complete example can be found under [`pingora-proxy/examples/ctx.rs`](../../pingora-proxy/examples/ctx.rs). You can run it using `cargo`:
```
RUST_LOG=INFO cargo run --example ctx
```

================================================
FILE: docs/user_guide/daemon.md
================================================
# Daemonization

When a Pingora server is configured to run as a daemon, after its bootstrapping, it will move itself to the background and optionally change to run under the configured user and group. The `pid_file` option comes handy in this case for the user to track the PID of the daemon in the background.

Daemonization also allows the server to perform privileged actions like loading secrets and then switch to an unprivileged user before accepting any requests from the network.

This process happens in the `run_forever()` call. Because daemonization involves `fork()`, certain things like threads created before this call are likely lost.


================================================
FILE: docs/user_guide/error_log.md
================================================
# Error logging

Pingora libraries are built to expect issues like disconnects, timeouts and invalid inputs from the network. A common way to record these issues are to output them in error log (STDERR or log files).

## Log level guidelines
Pingora adopts the idea behind [log](https://docs.rs/log/latest/log/). There are five log levels:
* `error`: This level should be used when the error stops the request from being handled correctly. For example when the server we try to connect to is offline.
* `warning`: This level should be used when an error occurs but the system recovers from it. For example when the primary DNS timed out but the system is able to query the secondary DNS.
* `info`: Pingora logs when the server is starting up or shutting down.
* `debug`: Internal details. This log level is not compiled in `release` builds.
* `trace`: Fine-grained internal details. This log level is not compiled in `release` builds.

The pingora-proxy crate has a well-defined interface to log errors, so that users don't have to manually log common proxy errors. See its guide for more details.


================================================
FILE: docs/user_guide/errors.md
================================================
# How to return errors

For easy error handling, the `pingora-error` crate exports a custom `Result` type used throughout other Pingora crates.

The `Error` struct used in this `Result`'s error variant is a wrapper around arbitrary error types. It allows the user to tag the source of the underlying error and attach other custom context info.

Users will often need to return errors by propagating an existing error or creating a wholly new one. `pingora-error` makes this easy with its error building functions.

## Examples

For example, one could return an error when an expected header is not present:

```rust
fn validate_req_header(req: &RequestHeader) -> Result<()> {
    // validate that the `host` header exists
    req.headers()
        .get(http::header::HOST)
        .ok_or_else(|| Error::explain(InvalidHTTPHeader, "No host header detected"))
}

impl MyServer {
    pub async fn handle_request_filter(
        &self,
        http_session: &mut Session,
        ctx: &mut CTX,
    ) -> Result<bool> {
        validate_req_header(session.req_header()?).or_err(HTTPStatus(400), "Missing required headers")?;
        Ok(true)
    }
}
```

`validate_req_header` returns an `Error` if the `host` header is not found, using `Error::explain` to create a new `Error` along with an associated type (`InvalidHTTPHeader`) and helpful context that may be logged in an error log.

This error will eventually propagate to the request filter, where it is returned as a new `HTTPStatus` error using `or_err`. (As part of the default pingora-proxy `fail_to_proxy()` phase, not only will this error be logged, but it will result in sending a `400 Bad Request` response downstream.)

Note that the original causing error will be visible in the error logs as well. `or_err` wraps the original causing error in a new one with additional context, but `Error`'s `Display` implementation also prints the chain of causing errors.

## Guidelines

An error has a _type_ (e.g. `ConnectionClosed`), a _source_ (e.g. `Upstream`, `Downstream`, `Internal`), and optionally, a _cause_ (another wrapped error) and a _context_ (arbitrary user-provided string details).

A minimal error can be created using functions like `new_in` / `new_up` / `new_down`, each of which specifies a source and asks the user to provide a type.

Generally speaking:
* To create a new error, without a direct cause but with more context, use `Error::explain`. You can also use `explain_err` on a `Result` to replace the potential error inside it with a new one.
* To wrap a causing error in a new one with more context, use `Error::because`. You can also use `or_err` on a `Result` to replace the potential error inside it by wrapping the original one.

## Retry

Errors can be "retry-able." If the error is retry-able, pingora-proxy will be allowed to retry the upstream request. Some errors are only retry-able on [reused connections](pooling.md), e.g. to handle situations where the remote end has dropped a connection we attempted to reuse.

By default a newly created `Error` either takes on its direct causing error's retry status, or, if left unspecified, is considered not retry-able.


================================================
FILE: docs/user_guide/failover.md
================================================
# Handling failures and failover

Pingora-proxy allows users to define how to handle failures throughout the life of a proxied request.

When a failure happens before the response header is sent downstream, users have a few options:
1. Send an error page downstream and then give up.
2. Retry the same upstream again.
3. Try another upstream if applicable.

Otherwise, once the response header is already sent downstream, there is nothing the proxy can do other than logging an error and then giving up on the request.


## Retry / Failover
In order to implement retry or failover, `fail_to_connect()` / `error_while_proxy()` needs to mark the error as "retry-able." For failover, `fail_to_connect() / error_while_proxy()` also needs to update the `CTX` to tell `upstream_peer()` not to use the same `Peer` again.

### Safety
In general, idempotent HTTP requests, e.g., `GET`, are safe to retry. Other requests, e.g., `POST`, are not safe to retry if the requests have already been sent. When `fail_to_connect()` is called, pingora-proxy guarantees that nothing was sent upstream. Users are not recommended to retry a non-idempotent request after `error_while_proxy()` unless they know the upstream server enough to know whether it is safe.

### Example
In the following example we set a `tries` variable on the `CTX` to track how many connection attempts we've made. When setting our peer in `upstream_peer` we check if `tries` is less than one and connect to 192.0.2.1. On connect failure we increment `tries` in `fail_to_connect` and set `e.set_retry(true)` which tells Pingora this is a retryable error. On retry, we enter `upstream_peer` again and this time connect to 1.1.1.1. If we're unable to connect to 1.1.1.1 we return a 502 since we only set `e.set_retry(true)` in `fail_to_connect` when `tries` is zero.

```Rust
pub struct MyProxy();

pub struct MyCtx {
    tries: usize,
}

#[async_trait]
impl ProxyHttp for MyProxy {
    type CTX = MyCtx;
    fn new_ctx(&self) -> Self::CTX {
        MyCtx { tries: 0 }
    }

    fn fail_to_connect(
        &self,
        _session: &mut Session,
        _peer: &HttpPeer,
        ctx: &mut Self::CTX,
        mut e: Box<Error>,
    ) -> Box<Error> {
        if ctx.tries > 0 {
            return e;
        }
        ctx.tries += 1;
        e.set_retry(true);
        e
    }

    async fn upstream_peer(
        &self,
        _session: &mut Session,
        ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>> {
        let addr = if ctx.tries < 1 {
            ("192.0.2.1", 443)
        } else {
            ("1.1.1.1", 443)
        };

        let mut peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string()));
        peer.options.connection_timeout = Some(Duration::from_millis(100));
        Ok(peer)
    }
}
```


================================================
FILE: docs/user_guide/graceful.md
================================================
# Graceful restart and shutdown

Graceful restart, upgrade, and shutdown mechanisms are very commonly used to avoid errors or downtime when releasing new versions of Pingora servers.

Pingora graceful upgrade mechanism guarantees the following:
* A request is guaranteed to be handled either by the old server instance or the new one. No request will see connection refused when trying to connect to the server endpoints.
* A request that can finish within the grace period is guaranteed not to be terminated.

## How to graceful upgrade
### Step 0
Configure the upgrade socket. The old and new server need to agree on the same path to this socket. See configuration manual for details.

### Step 1
Start the new instance with the `--upgrade` CLI option. The new instance will not try to listen to the service endpoint right away. It will try to acquire the listening socket from the old instance instead.

### Step 2
Send SIGQUIT signal to the old instance. The old instance will start to transfer the listening socket to the new instance.

Once step 2 is successful, the new instance will start to handle new incoming connections right away. Meanwhile, the old instance will enter its graceful shutdown mode. It waits a short period of time (to give the new instance time to initialize and prepare to handle traffic), after which it will not accept any new connections.


================================================
FILE: docs/user_guide/index.md
================================================
# User Guide

In this guide, we will cover the most used features, operations and settings of Pingora.

## Running Pingora servers
* [Start and stop](start_stop.md)
* [Graceful restart and graceful shutdown](graceful.md)
* [Configuration](conf.md)
* [Daemonization](daemon.md)
* [Systemd integration](systemd.md)
* [Handling panics](panic.md)
* [Error logging](error_log.md)
* [Prometheus](prom.md)

## Building HTTP proxies
* [Life of a request: `pingora-proxy` phases and filters](phase.md)
* [`Peer`: how to connect to upstream](peer.md)
* [Sharing state across phases with `CTX`](ctx.md)
* [How to return errors](errors.md)
* [Examples: take control of the request](modify_filter.md)
* [Connection pooling and reuse](pooling.md)
* [Handling failures and failover](failover.md)
* [RateLimiter quickstart](rate_limiter.md)

## Advanced topics (WIP)
* [Pingora internals](internals.md)
* Using BoringSSL
* User defined configuration
* Pingora async runtime and threading model
* Background Service
* Blocking code in async context
* Tracing


================================================
FILE: docs/user_guide/internals.md
================================================
# Pingora Internals

(Special thanks to [James Munns](https://github.com/jamesmunns) for writing this section)


## Starting the `Server`

The pingora system starts by spawning a *server*. The server is responsible for starting *services*, and listening for termination events.

```
                               ┌───────────┐
                    ┌─────────>│  Service  │
                    │          └───────────┘
┌────────┐          │          ┌───────────┐
│ Server │──Spawns──┼─────────>│  Service  │
└────────┘          │          └───────────┘
                    │          ┌───────────┐
                    └─────────>│  Service  │
                               └───────────┘
```

After spawning the *services*, the server continues to listen to a termination event, which it will propagate to the created services.

## Services

*Services* are entities that handle listening to given sockets, and perform the core functionality. A *service* is tied to a particular protocol and set of options.

> NOTE: there are also "background" services, which just do *stuff*, and aren't necessarily listening to a socket. For now we're just talking about listener services.

Each service has its own threadpool/tokio runtime, with a number of threads based on the configured value. Worker threads are not shared cross-service. Service runtime threadpools may be work-stealing (tokio-default), or non-work-stealing (N isolated single threaded runtimes).

```
┌─────────────────────────┐
│ ┌─────────────────────┐ │
│ │┌─────────┬─────────┐│ │
│ ││  Conn   │  Conn   ││ │
│ │├─────────┼─────────┤│ │
│ ││Endpoint │Endpoint ││ │
│ │├─────────┴─────────┤│ │
│ ││     Listeners     ││ │
│ │├─────────┬─────────┤│ │
│ ││ Worker  │ Worker  ││ │
│ ││ Thread  │ Thread  ││ │
│ │├─────────┴─────────┤│ │
│ ││  Tokio Executor   ││ │
│ │└───────────────────┘│ │
│ └─────────────────────┘ │
│ ┌───────┐               │
└─┤Service├───────────────┘
  └───────┘
```

## Service Listeners

At startup, each Service is assigned a set of downstream endpoints that they listen to. A single service may listen to more than one endpoint. The Server also passes along any relevant configuration, including TLS settings if relevant.

These endpoints are converted into listening sockets, called `TransportStack`s. Each `TransportStack` is assigned to an async task within that service's executor.

```
                                 ┌───────────────────┐
                                 │┌─────────────────┐│    ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐  ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
 ┌─────────┐                     ││ TransportStack  ││                                ┌────────────────────┐│
┌┤Listeners├────────┐            ││                 ││    │                       │  ││                    │
│└─────────┘        │            ││ (Listener, TLS  │├──────spawn(run_endpoint())────>│ Service<ServerApp> ││
│┌─────────────────┐│            ││    Acceptor,    ││    │                       │  ││                    │
││    Endpoint     ││            ││   UpgradeFDs)   ││                                └────────────────────┘│
││   addr/ports    ││            │├─────────────────┤│    │                       │  │
││ + TLS Settings  ││            ││ TransportStack  ││                                ┌────────────────────┐│
│├─────────────────┤│            ││                 ││    │                       │  ││                    │
││    Endpoint     ││──build()─> ││ (Listener, TLS  │├──────spawn(run_endpoint())────>│ Service<ServerApp> ││
││   addr/ports    ││            ││    Acceptor,    ││    │                       │  ││                    │
││ + TLS Settings  ││            ││   UpgradeFDs)   ││                                └────────────────────┘│
│├─────────────────┤│            │├─────────────────┤│    │                       │  │
││    Endpoint     ││            ││ TransportStack  ││                                ┌────────────────────┐│
││   addr/ports    ││            ││                 ││    │                       │  ││                    │
││ + TLS Settings  ││            ││ (Listener, TLS  │├──────spawn(run_endpoint())────>│ Service<ServerApp> ││
│└─────────────────┘│            ││    Acceptor,    ││    │                       │  ││                    │
└───────────────────┘            ││   UpgradeFDs)   ││                                └────────────────────┘│
                                 │└─────────────────┘│    │ ┌───────────────┐     │  │ ┌──────────────┐
                                 └───────────────────┘     ─│start_service()│─ ─ ─    ─│ Worker Tasks ├ ─ ─ ┘
                                                            └───────────────┘          └──────────────┘
```

## Downstream connection lifecycle

Each service processes incoming connections by spawning a task-per-connection. These connections are held open
as long as there are new events to be handled.

```
                                  ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐

                                  │  ┌───────────────┐   ┌────────────────┐   ┌─────────────────┐    ┌─────────────┐  │
┌────────────────────┐               │ UninitStream  │   │    Service     │   │       App       │    │  Task Ends  │
│                    │            │  │ ::handshake() │──>│::handle_event()│──>│ ::process_new() │──┬>│             │  │
│ Service<ServerApp> │──spawn()──>   └───────────────┘   └────────────────┘   └─────────────────┘  │ └─────────────┘
│                    │            │                                                    ▲           │                  │
└────────────────────┘                                                                 │         while
                                  │                                                    └─────────reuse                │
                                     ┌───────────────────────────┐
                                  └ ─│  Task on Service Runtime  │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
                                     └───────────────────────────┘
```

## What is a proxy then?

Interestingly, the `pingora` `Server` itself has no particular notion of a Proxy.

Instead, it only thinks in terms of `Service`s, which are expected to contain a particular implementor of the `ServiceApp` trait.

For example, this is how an `HttpProxy` struct, from the `pingora-proxy` crate, "becomes" a `Service` spawned by the `Server`:

```
┌─────────────┐
│  HttpProxy  │
│  (struct)   │
└─────────────┘
       │
   implements   ┌─────────────┐
       │        │HttpServerApp│
       └───────>│   (trait)   │
                └─────────────┘
                       │
                   implements   ┌─────────────┐
                       │        │  ServerApp  │
                       └───────>│   (trait)   │
                                └─────────────┘
                                       │
                                   contained    ┌─────────────────────┐
                                     within     │                     │
                                       └───────>│ Service<ServiceApp> │
                                                │                     │
                                                └─────────────────────┘
```

Different functionalities and helpers are provided at different layers in this representation.

```
┌─────────────┐        ┌──────────────────────────────────────┐
│  HttpProxy  │        │Handles high level Proxying workflow, │
│  (struct)   │─ ─ ─ ─ │   customizable via ProxyHttp trait   │
└──────┬──────┘        └──────────────────────────────────────┘
       │
┌──────▼──────┐        ┌──────────────────────────────────────┐
│HttpServerApp│        │ Handles selection of H1 vs H2 stream │
│   (trait)   │─ ─ ─ ─ │     handling, incl H2 handshake      │
└──────┬──────┘        └──────────────────────────────────────┘
       │
┌──────▼──────┐        ┌──────────────────────────────────────┐
│  ServerApp  │        │ Handles dispatching of App instances │
│   (trait)   │─ ─ ─ ─ │   as individual tasks, per Session   │
└──────┬──────┘        └──────────────────────────────────────┘
       │
┌──────▼──────┐        ┌──────────────────────────────────────┐
│ Service<A>  │        │ Handles dispatching of App instances │
│  (struct)   │─ ─ ─ ─ │  as individual tasks, per Listener   │
└─────────────┘        └──────────────────────────────────────┘
```

The `HttpProxy` struct handles the high level workflow of proxying an HTTP connection

It uses the `ProxyHttp` (note the flipped wording order!) **trait** to allow customization
at each of the following steps (note: taken from [the phase chart](./phase_chart.md) doc):

```mermaid
 graph TD;
    start("new request")-->request_filter;
    request_filter-->upstream_peer;

    upstream_peer-->Connect{{IO: connect to upstream}};

    Connect--connection success-->connected_to_upstream;
    Connect--connection failure-->fail_to_connect;

    connected_to_upstream-->upstream_request_filter;
    upstream_request_filter --> SendReq{{IO: send request to upstream}};
    SendReq-->RecvResp{{IO: read response from upstream}};
    RecvResp-->upstream_response_filter-->response_filter-->upstream_response_body_filter-->response_body_filter-->logging-->endreq("request done");

    fail_to_connect --can retry-->upstream_peer;
    fail_to_connect --can't retry-->fail_to_proxy--send error response-->logging;

    RecvResp--failure-->IOFailure;
    SendReq--failure-->IOFailure;
    error_while_proxy--can retry-->upstream_peer;
    error_while_proxy--can't retry-->fail_to_proxy;

    request_filter --send response-->logging


    Error>any response filter error]-->error_while_proxy
    IOFailure>IO error]-->error_while_proxy

```

## Zooming out

Before we zoom in, it's probably good to zoom out and remind ourselves how
a proxy generally works:

```
┌────────────┐          ┌─────────────┐         ┌────────────┐
│ Downstream │          │    Proxy    │         │  Upstream  │
│   Client   │─────────>│             │────────>│   Server   │
└────────────┘          └─────────────┘         └────────────┘
```

The proxy will be taking connections from the **Downstream** client, and (if
everything goes right), establishing a connection with the appropriate
**Upstream** server. This selected upstream server is referred to as
the **Peer**.

Once the connection is established, the Downstream and Upstream can communicate
bidirectionally.

So far, the discussion of Server, Services, and Listeners have focused on the LEFT
half of this diagram, handling incoming Downstream connections, and getting it TO
the proxy component.

Next, we'll look at the RIGHT half of this diagram, connecting to Upstreams.

## Managing the Upstream

Connections to Upstream Peers are made through `Connector`s. This is not a specific type or trait, but more
of a "style".

Connectors are responsible for a few things:

* Establishing a connection with a Peer
* Maintaining a connection pool with the Peer, allowing for connection reuse across:
    * Multiple requests from a single downstream client
    * Multiple requests from different downstream clients
* Measuring health of connections, for connections like H2, which perform regular pings
* Handling protocols with multiple poolable layers, like H2
* Caching, if relevant to the protocol and enabled
* Compression, if relevant to the protocol and enabled

Now in context, we can see how each end of the Proxy is handled:

```
┌────────────┐          ┌─────────────┐         ┌────────────┐
│ Downstream │       ┌ ─│─   Proxy  ┌ ┼ ─       │  Upstream  │
│   Client   │─────────>│ │           │──┼─────>│   Server   │
└────────────┘       │  └───────────┼─┘         └────────────┘
                      ─ ─ ┘          ─ ─ ┘
                        ▲              ▲
                     ┌──┘              └──┐
                     │                    │
                ┌ ─ ─ ─ ─ ┐         ┌ ─ ─ ─ ─ ─
                 Listeners           Connectors│
                └ ─ ─ ─ ─ ┘         └ ─ ─ ─ ─ ─
```

## What about multiple peers?

`Connectors` only handle the connection to a single peer, so selecting one of potentially multiple Peers
is actually handled one level up, in the `upstream_peer()` method of the `ProxyHttp` trait.


================================================
FILE: docs/user_guide/modify_filter.md
================================================
# Examples: taking control of the request

In this section we will go through how to route, modify or reject requests.

## Routing
Any information from the request can be used to make routing decision. Pingora doesn't impose any constraints on how users could implement their own routing logic.

In the following example, the proxy sends traffic to 1.0.0.1 only when the request path start with `/family/`. All the other requests are routed to 1.1.1.1.

```Rust
pub struct MyGateway;

#[async_trait]
impl ProxyHttp for MyGateway {
    type CTX = ();
    fn new_ctx(&self) -> Self::CTX {}

    async fn upstream_peer(
        &self,
        session: &mut Session,
        _ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>> {
        let addr = if session.req_header().uri.path().starts_with("/family/") {
            ("1.0.0.1", 443)
        } else {
            ("1.1.1.1", 443)
        };

        info!("connecting to {addr:?}");

        let peer = Box::new(HttpPeer::new(addr, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}
```


## Modifying headers

Both request and response headers can be added, removed or modified in their corresponding phases. In the following example, we add logic to the `response_filter` phase to update the `Server` header and remove the `alt-svc` header.

```Rust
#[async_trait]
impl ProxyHttp for MyGateway {
    ...
    async fn response_filter(
        &self,
        _session: &mut Session,
        upstream_response: &mut ResponseHeader,
        _ctx: &mut Self::CTX,
    ) -> Result<()>
    where
        Self::CTX: Send + Sync,
    {
        // replace existing header if any
        upstream_response
            .insert_header("Server", "MyGateway")
            .unwrap();
        // because we don't support h3
        upstream_response.remove_header("alt-svc");

        Ok(())
    }
}
```

## Return Error pages

Sometimes instead of proxying the traffic, under certain conditions, such as authentication failures, you might want the proxy to just return an error page.

```Rust
fn check_login(req: &pingora_http::RequestHeader) -> bool {
    // implement you logic check logic here
    req.headers.get("Authorization").map(|v| v.as_bytes()) == Some(b"password")
}

#[async_trait]
impl ProxyHttp for MyGateway {
    ...
    async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result<bool> {
        if session.req_header().uri.path().starts_with("/login")
            && !check_login(session.req_header())
        {
            let _ = session.respond_error(403).await;
            // true: tell the proxy that the response is already written
            return Ok(true);
        }
        Ok(false)
    }
```
## Logging

Logging logic can be added to the `logging` phase of Pingora. The logging phase runs on every request right before Pingora proxy finish processing it. This phase runs for both successful and failed requests.

In the example below, we add Prometheus metric and access logging to the proxy. In order for the metrics to be scraped, we also start a Prometheus metric server on a different port.


``` Rust
pub struct MyGateway {
    req_metric: prometheus::IntCounter,
}

#[async_trait]
impl ProxyHttp for MyGateway {
    ...
    async fn logging(
        &self,
        session: &mut Session,
        _e: Option<&pingora::Error>,
        ctx: &mut Self::CTX,
    ) {
        let response_code = session
            .response_written()
            .map_or(0, |resp| resp.status.as_u16());
        // access log
        info!(
            "{} response code: {response_code}",
            self.request_summary(session, ctx)
        );

        self.req_metric.inc();
    }

fn main() {
   ...
    let mut prometheus_service_http =
        pingora::services::listening::Service::prometheus_http_service();
    prometheus_service_http.add_tcp("127.0.0.1:6192");
    my_server.add_service(prometheus_service_http);

    my_server.run_forever();
}
```

================================================
FILE: docs/user_guide/panic.md
================================================
# Handling panics

Any panic that happens to particular requests does not affect other ongoing requests or the server's ability to handle other requests. Sockets acquired by the panicking requests are dropped (closed). The panics will be captured by the tokio runtime and then ignored.

In order to monitor the panics, Pingora server has built-in Sentry integration.
```rust
my_server.sentry = Some(
    sentry::ClientOptions{
        dsn: "SENTRY_DSN".into_dsn().unwrap(),
        ..Default::default()
    }
);
```

Even though a panic is not fatal in Pingora, it is still not the preferred way to handle failures like network timeouts. Panics should be reserved for unexpected logic errors.


================================================
FILE: docs/user_guide/peer.md
================================================
# `Peer`: how to connect to upstream

In the `upstream_peer()` phase the user should return a `Peer` object which defines how to connect to a certain upstream.

## `Peer`
A `HttpPeer` defines which upstream to connect to.
| attribute      | meaning        |
| ------------- |-------------|
|address: `SocketAddr`| The IP:Port to connect to |
|scheme: `Scheme`| Http or Https |
|sni: `String`| The SNI to use, Https only |
|proxy: `Option<Proxy>`| The setting to proxy the request through a [CONNECT proxy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT) |
|client_cert_key: `Option<Arc<CertKey>>`| The client certificate to use in mTLS connections to upstream |
|options: `PeerOptions`| See below |


## `PeerOptions`
A `PeerOptions` defines how to connect to the upstream.
| attribute      | meaning        |
| ------------- |-------------|
|bind_to: `Option<InetSocketAddr>`| Which local address to bind to as the client IP |
|connection_timeout: `Option<Duration>`| How long to wait before giving up *establishing* a TCP connection |
|total_connection_timeout: `Option<Duration>`| How long to wait before giving up *establishing* a connection including TLS handshake time |
|read_timeout: `Option<Duration>`| How long to wait before each individual `read()` from upstream. The timer is reset after each `read()` |
|idle_timeout: `Option<Duration>`| How long to wait before closing a idle connection waiting for connection reuse |
|write_timeout: `Option<Duration>`| How long to wait before a `write()` to upstream finishes |
|verify_cert: `bool`| Whether to check if upstream' server cert is valid and validated |
|verify_hostname: `bool`| Whether to check if upstream server cert's CN matches the SNI |
|use_system_certs: `bool`| Whether the system trust store should be loaded and used when verifying certificates. Impacts performance (s2n-tls only) |
|alternative_cn: `Option<String>`| Accept the cert if the CN matches this name |
|alpn: `ALPN`| Which HTTP protocol to advertise during ALPN, http1.1 and/or http2 |
|ca: `Option<Arc<Box<[X509]>>>`| Which Root CA to use to validate the server's cert |
|psk: `Option<Arc<PskConfig>>` | The PSK configuration to use in [PSK-TLS](https://datatracker.ietf.org/doc/html/rfc4279) handshakes (s2n-tls only) |
|s2n_security_policy: `Option<S2NPolicy>` | S2N [Security Policy](https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html) to use. Defaults to `default_tls13` if undefined. (s2n-tls only) |
|max_blinding_delay: `Option<u32>` | S2N-TLS will delay a response up to the [max blinding delay](https://aws.github.io/s2n-tls/usage-guide/ch03-error-handling.html#blinding) (default 30) seconds whenever an error triggered by a peer occurs to mitigate against timing side channels. (s2n-tls only) |
|tcp_keepalive: `Option<TcpKeepalive>`| TCP keepalive settings to upstream |

## Examples
TBD


================================================
FILE: docs/user_guide/phase.md
================================================
# Life of a request: pingora-proxy phases and filters

## Intro
The pingora-proxy HTTP proxy framework supports highly programmable proxy behaviors. This is done by allowing users to inject custom logic into different phases (stages) in the life of a request.

## Life of a proxied HTTP request
1. The life of a proxied HTTP request starts when the proxy reads the request header from the **downstream** (i.e., the client).
2. Then, the proxy connects to the **upstream** (i.e., the remote server). This step is skipped if there is a previously established [connection to reuse](pooling.md).
3. The proxy then sends the request header to the upstream.
4. Once the request header is sent, the proxy enters a duplex mode, which simultaneously proxies:
    a. upstream response (both header and body) to the downstream, and
    b. downstream request body to upstream (if any).
5. Once the entire request/response finishes, the life of the request is ended. All resources are released. The downstream connections and the upstream connections are recycled to be reused if applicable.

## Pingora-proxy phases and filters
Pingora-proxy allows users to insert arbitrary logic into the life of a request.
```mermaid
 graph TD;
    start("new request")-->early_request_filter;
    early_request_filter-->request_filter;
    request_filter-->upstream_peer;

    upstream_peer-->Connect{{IO: connect to upstream}};

    Connect--connection success-->connected_to_upstream;
    Connect--connection failure-->fail_to_connect;

    connected_to_upstream-->upstream_request_filter;
    upstream_request_filter --> request_body_filter;
    request_body_filter --> SendReq{{IO: send request to upstream}};
    SendReq-->RecvResp{{IO: read response from upstream}};
    RecvResp-->upstream_response_filter-->response_filter-->upstream_response_body_filter-->response_body_filter-->logging-->endreq("request done");

    fail_to_connect --can retry-->upstream_peer;
    fail_to_connect --can't retry-->fail_to_proxy--send error response-->logging;

    RecvResp--failure-->IOFailure;
    SendReq--failure-->IOFailure;
    error_while_proxy--can retry-->upstream_peer;
    error_while_proxy--can't retry-->fail_to_proxy;

    request_filter --send response-->logging


    Error>any response filter error]-->error_while_proxy
    IOFailure>IO error]-->error_while_proxy
```

### General filter usage guidelines
* Most filters return a [`pingora_error::Result<_>`](errors.md). When the returned value is `Result::Err`, `fail_to_proxy()` will be called and the request will be terminated.
* Most filters are async functions, which allows other async operations such as IO to be performed within the filters.
* A per-request `CTX` object can be defined to share states across the filters of the same request. All filters have mutable access to this object.
* Most filters are optional.
* The reason both `upstream_response_*_filter()` and `response_*_filter()` exist is for HTTP caching integration reasons (still WIP).


### `early_request_filter()`
This is the first phase of every request.

This function is similar to `request_filter()` but executes before any other logic, including downstream module logic. The main purpose of this function is to provide finer-grained control of the behavior of the modules.

### `request_filter()`
This phase is usually for validating request inputs, rate limiting, and initializing context.

### `request_body_filter()`
This phase is triggered after a request body is ready to send to upstream. It will be called every time a piece of request body is received.

### `proxy_upstream_filter()`
This phase determines if we should continue to the upstream to serve a response. If we short-circuit, a 502 is returned by default, but a different response can be implemented.

This phase returns a boolean determining if we should continue to the upstream or error.

### `upstream_peer()`
This phase decides which upstream to connect to (e.g. with DNS lookup and hashing/round-robin), and how to connect to it.

This phase returns a `Peer` that defines the upstream to connect to. Implementing this phase is **required**.

### `connected_to_upstream()`
This phase is executed when upstream is successfully connected.

Usually this phase is for logging purposes. Connection info such as RTT and upstream TLS ciphers are reported in this phase.

### `fail_to_connect()`
The counterpart of `connected_to_upstream()`. This phase is called if an error is encountered when connecting to upstream.

In this phase users can report the error in Sentry/Prometheus/error log. Users can also decide if the error is retry-able.

If the error is retry-able, `upstream_peer()` will be called again, in which case the user can decide whether to retry the same upstream or failover to a secondary one.

If the error is not retry-able, the request will end.

### `upstream_request_filter()`
This phase is to modify requests before sending to upstream.

### `upstream_response_filter()/upstream_response_body_filter()/upstream_response_trailer_filter()`
This phase is triggered after an upstream response header/body/trailer is received.

This phase is to modify or process response headers, body, or trailers before sending to downstream. Note that this phase is called _prior_ to HTTP caching and therefore any changes made here will affect the response stored in the HTTP cache.

### `response_filter()/response_body_filter()/response_trailer_filter()`
This phase is triggered after a response header/body/trailer is ready to send to downstream.

This phase is to modify them before sending to downstream.

### `error_while_proxy()`
This phase is triggered during proxy errors to upstream, this is after the connection is established.

This phase may decide to retry a request if the connection was re-used and the HTTP method is idempotent.

### `fail_to_proxy()`
This phase is called whenever an error is encounter during any of the phases above.

This phase is usually for error logging and error reporting to downstream.

### `logging()`
This is the last phase that runs after the request is finished (or errors) and before any of its resources are released. Every request will end up in this final phase.

This phase is usually for logging and post request cleanup.

### `request_summary()`
This is not a phase, but a commonly used callback.

Every error that reaches `fail_to_proxy()` will be automatically logged in the error log. `request_summary()` will be called to dump the info regarding the request when logging the error.

This callback returns a string which allows users to customize what info to dump in the error log to help track and debug the failures.

### `suppress_error_log()`
This is also not a phase, but another callback.

`fail_to_proxy()` errors are automatically logged in the error log, but users may not be interested in every error. For example, downstream errors are logged if the client disconnects early, but these errors can become noisy if users are mainly interested in observing upstream issues. This callback can inspect the error and returns true or false. If true, the error will not be written to the log.

### Cache filters

To be documented


================================================
FILE: docs/user_guide/phase_chart.md
================================================
Pingora proxy phases without caching
```mermaid
 graph TD;
    start("new request")-->early_request_filter;
    early_request_filter-->request_filter;
    request_filter-->upstream_peer;

    upstream_peer-->Connect{{IO: connect to upstream}};

    Connect--connection success-->connected_to_upstream;
    Connect--connection failure-->fail_to_connect;

    connected_to_upstream-->upstream_request_filter;
    upstream_request_filter --> request_body_filter;
    request_body_filter --> SendReq{{IO: send request to upstream}};
    SendReq-->RecvResp{{IO: read response from upstream}};
    RecvResp-->upstream_response_filter-->response_filter-->upstream_response_body_filter-->response_body_filter-->logging-->endreq("request done");

    fail_to_connect --can retry-->upstream_peer;
    fail_to_connect --can't retry-->fail_to_proxy--send error response-->logging;

    RecvResp--failure-->IOFailure;
    SendReq--failure-->IOFailure;
    error_while_proxy--can retry-->upstream_peer;
    error_while_proxy--can't retry-->fail_to_proxy;

    request_filter --send response-->logging


    Error>any response filter error]-->error_while_proxy
    IOFailure>IO error]-->error_while_proxy
```

================================================
FILE: docs/user_guide/pooling.md
================================================
# Connection pooling and reuse

When the request to a `Peer` (upstream server) is finished, the connection to that peer is kept alive and added to a connection pool to be _reused_ by subsequent requests. This happens automatically without any special configuration.

Requests that reuse previously established connections avoid the latency and compute cost of setting up a new connection, improving the Pingora server's overall performance and scalability.

## Same `Peer`
Only the connections to the exact same `Peer` can be reused by a request. For correctness and security reasons, two `Peer`s are the same if and only if all the following attributes are the same
* IP:port
* scheme
* SNI
* client cert
* verify cert
* verify hostname
* alternative_cn
* proxy settings

## Disable pooling
To disable connection pooling and reuse to a certain `Peer`, just set the `idle_timeout` to 0 seconds to all requests using that `Peer`.

## Failure
A connection is considered not reusable if errors happen during the request.


================================================
FILE: docs/user_guide/prom.md
================================================
# Prometheus

Pingora has a built-in prometheus HTTP metric server for scraping.

```rust
    ...
    let mut prometheus_service_http = Service::prometheus_http_service();
    prometheus_service_http.add_tcp("0.0.0.0:1234");
    my_server.add_service(prometheus_service_http);
    my_server.run_forever();
```

The simplest way to use it is to have [static metrics](https://docs.rs/prometheus/latest/prometheus/#static-metrics).

```rust
static MY_COUNTER: Lazy<IntGauge> = Lazy::new(|| {
    register_int_gauge!("my_counter", "my counter").unwrap()
});

```

This static metric will automatically appear in the Prometheus metric endpoint.


================================================
FILE: docs/user_guide/rate_limiter.md
================================================
# **RateLimiter quickstart**
Pingora provides a crate `pingora-limits` which provides a simple and easy to use rate limiter for your application. Below is an example of how you can use [`Rate`](https://docs.rs/pingora-limits/latest/pingora_limits/rate/struct.Rate.html) to create an application that uses multiple limiters to restrict the rate at which requests can be made on a per-app basis (determined by a request header).

## Steps
1. Add the following dependencies to your `Cargo.toml`:
   ```toml
   async-trait="0.1"
   pingora = { version = "0.3", features = [ "lb" ] }
   pingora-limits = "0.3.0"
   once_cell = "1.19.0"
   ```
2. Declare a global rate limiter map to store the rate limiter for each client. In this example, we use `appid`.
3. Override the `request_filter` method in the `ProxyHttp` trait to implement rate limiting.
   1. Retrieve the client appid from header.
   2. Retrieve the current window requests from the rate limiter map. If there is no rate limiter for the client, create a new one and insert it into the map.
   3. If the current window requests exceed the limit, return 429 and set RateLimiter associated headers.
   4. If the request is not rate limited, return `Ok(false)` to continue the request.

## Example
```rust
use async_trait::async_trait;
use once_cell::sync::Lazy;
use pingora::prelude::*;
use pingora_limits::rate::Rate;
use std::sync::Arc;
use std::time::Duration;

fn main() {
    let mut server = Server::new(Some(Opt::default())).unwrap();
    server.bootstrap();
    let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();
    // Set health check
    let hc = TcpHealthCheck::new();
    upstreams.set_health_check(hc);
    upstreams.health_check_frequency = Some(Duration::from_secs(1));
    // Set background service
    let background = background_service("health check", upstreams);
    let upstreams = background.task();
    // Set load balancer
    let mut lb = http_proxy_service(&server.configuration, LB(upstreams));
    lb.add_tcp("0.0.0.0:6188");

    // let rate = Rate
    server.add_service(background);
    server.add_service(lb);
    server.run_forever();
}

pub struct LB(Arc<LoadBalancer<RoundRobin>>);

impl LB {
    pub fn get_request_appid(&self, session: &mut Session) -> Option<String> {
        match session
            .req_header()
            .headers
            .get("appid")
            .map(|v| v.to_str())
        {
            None => None,
            Some(v) => match v {
                Ok(v) => Some(v.to_string()),
                Err(_) => None,
            },
        }
    }
}

// Rate limiter
static RATE_LIMITER: Lazy<Rate> = Lazy::new(|| Rate::new(Duration::from_secs(1)));

// max request per second per client
static MAX_REQ_PER_SEC: isize = 1;

#[async_trait]
impl ProxyHttp for LB {
    type CTX = ();

    fn new_ctx(&self) {}

    async fn upstream_peer(
        &self,
        _session: &mut Session,
        _ctx: &mut Self::CTX,
    ) -> Result<Box<HttpPeer>> {
        let upstream = self.0.select(b"", 256).unwrap();
        // Set SNI
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }

    async fn upstream_request_filter(
        &self,
        _session: &mut Session,
        upstream_request: &mut RequestHeader,
        _ctx: &mut Self::CTX,
    ) -> Result<()>
    where
        Self::CTX: Send + Sync,
    {
        upstream_request
            .insert_header("Host", "one.one.one.one")
            .unwrap();
        Ok(())
    }

    async fn request_filter(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result<bool>
    where
        Self::CTX: Send + Sync,
    {
        let appid = match self.get_request_appid(session) {
            None => return Ok(false), // no client appid found, skip rate limiting
            Some(addr) => addr,
        };

        // retrieve the current window requests
        let curr_window_requests = RATE_LIMITER.observe(&appid, 1);
        if curr_window_requests > MAX_REQ_PER_SEC {
            // rate limited, return 429
            let mut header = ResponseHeader::build(429, None).unwrap();
            header
                .insert_header("X-Rate-Limit-Limit", MAX_REQ_PER_SEC.to_string())
                .unwrap();
            header.insert_header("X-Rate-Limit-Remaining", "0").unwrap();
            header.insert_header("X-Rate-Limit-Reset", "1").unwrap();
            session.set_keepalive(None);
            session
                .write_response_header(Box::new(header), true)
                .await?;
            return Ok(true);
        }
        Ok(false)
    }
}
```

## Testing
To use the example above,

1. Run your program with `cargo run`.
2. Verify the program is working with a few executions of ` curl localhost:6188 -H "appid:1" -v`
   - The first request should work and any later requests that arrive within 1s of a previous request should fail with:
     ```
     *   Trying 127.0.0.1:6188...
     * Connected to localhost (127.0.0.1) port 6188 (#0)
     > GET / HTTP/1.1
     > Host: localhost:6188
     > User-Agent: curl/7.88.1
     > Accept: */*
     > appid:1
     >
     < HTTP/1.1 429 Too Many Requests
     < X-Rate-Limit-Limit: 1
     < X-Rate-Limit-Remaining: 0
     < X-Rate-Limit-Reset: 1
     < Date: Sun, 14 Jul 2024 20:29:02 GMT
     < Connection: close
     <
     * Closing connection 0
     ```

## Complete Example
You can run the pre-made example code in the [`pingora-proxy` examples folder](https://github.com/cloudflare/pingora/tree/main/pingora-proxy/examples/rate_limiter.rs) with

```
cargo run --example rate_limiter
```


================================================
FILE: docs/user_guide/start_stop.md
================================================
# Starting and stopping Pingora server

A pingora server is a regular unprivileged multithreaded process.

## Start
By default, the server will run in the foreground.

A Pingora server by default takes the following command-line arguments:

| Argument      | Effect        | default|
| ------------- |-------------| ----|
| -d, --daemon | Daemonize the server | false |
| -t, --test | Test the server conf and then exit (WIP) | false |
| -c, --conf | The path to the configuration file | empty string |
| -u, --upgrade | This server should gracefully upgrade a running server | false |

## Stop
A Pingora server will listen to the following signals.

### SIGINT: fast shutdown
Upon receiving SIGINT (ctrl + c), the server will exit immediately with no delay. All unfinished requests will be interrupted. This behavior is usually less preferred because it could break requests.

### SIGTERM: graceful shutdown
Upon receiving SIGTERM, the server will notify all its services to shutdown, wait for some preconfigured time and then exit. This behavior gives requests a grace period to finish.

### SIGQUIT: graceful upgrade
Similar to SIGTERM, but the server will also transfer all its listening sockets to a new Pingora server so that there is no downtime during the upgrade. See the [graceful upgrade](graceful.md) section for more details.


================================================
FILE: docs/user_guide/systemd.md
================================================
# Systemd integration

A Pingora server doesn't depend on systemd but it can easily be made into a systemd service.

```ini
[Service]
Type=forking
PIDFile=/run/pingora.pid
ExecStart=/bin/pingora -d -c /etc/pingora.conf
ExecReload=kill -QUIT $MAINPID
ExecReload=/bin/pingora -u -d -c /etc/pingora.conf
```

The example systemd setup integrates Pingora's graceful upgrade into systemd. To upgrade the pingora service, simply install a version of the binary and then call `systemctl reload pingora.service`.


================================================
FILE: pingora/Cargo.toml
================================================
[package]
name = "pingora"
version = "0.8.0"
authors = ["Yuchen Wu <yuchen@cloudflare.com>"]
license = "Apache-2.0"
edition = "2021"
repository = "https://github.com/cloudflare/pingora"
description = """
A framework to build fast, reliable and programmable networked systems at Internet scale.
"""
categories = ["asynchronous", "network-programming"]
keywords = ["async", "proxy", "http", "pingora"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "pingora"
path = "src/lib.rs"

[package.metadata.docs.rs]
features = ["document-features"]
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
pingora-core = { version = "0.8.0", path = "../pingora-core", default-features = false }
pingora-http = { version = "0.8.0", path = "../pingora-http" }
pingora-timeout = { version = "0.8.0", path = "../pingora-timeout" }
pingora-load-balancing = { version = "0.8.0", path = "../pingora-load-balancing", optional = true, default-features = false }
pingora-proxy = { version = "0.8.0", path = "../pingora-proxy", optional = true, default-features = false }
pingora-cache = { version = "0.8.0", path = "../pingora-cache", optional = true, default-features = false }

# Only used for documenting features, but doesn't work in any other dependency 
# group :(
document-features = { version = "0.2.10", optional = true }

[dev-dependencies]
clap = { version = "4.5", features = ["derive"] }
tokio = { workspace = true, features = ["rt-multi-thread", "signal"] }
env_logger = "0.11"
reqwest = { version = "0.11", features = ["rustls"], default-features = false }
hyper = "0.14"
async-trait = { workspace = true }
http = { workspace = true }
log = { workspace = true }
prometheus = "0.13"
once_cell = { workspace = true }
bytes = { workspace = true }
regex = "1"

[target.'cfg(unix)'.dev-dependencies]
hyperlocal = "0.8"
jemallocator = "0.5"

[features]
default = []

#! ### Tls
#! Tls is provided by adding one of these features. If no tls-providing feature
#! is added, only unencrypted http. Only one tls-providing feature can be
#! selected at a time

## Use [OpenSSL](https://crates.io/crates/openssl) for tls
##
## Requires native openssl libraries and build tooling
openssl = [
    "pingora-core/openssl",
    "pingora-proxy?/openssl",
    "pingora-cache?/openssl",
    "pingora-load-balancing?/openssl",
    "openssl_derived",
]

## Use [BoringSSL](https://crates.io/crates/boring) for tls 
##
## Requires native boring libraries and build tooling
boringssl = [
    "pingora-core/boringssl",
    "pingora-proxy?/boringssl",
    "pingora-cache?/boringssl",
    "pingora-load-balancing?/boringssl",
    "openssl_derived",
]

## Use  [s2n-tls](https://crates.io/crates/s2n-tls) for tls
##
## Requires native s2n-tls libraries and build tooling
s2n = [
    "pingora-core/s2n",
    "pingora-proxy?/s2n",
    "pingora-cache?/s2n",
    "pingora-load-balancing?/s2n",
    "any_tls",
]

## Use  [rustls](https://crates.io/crates/rustls) for tls 
##
## ⚠️ _Highly Experimental_! ⚠️ Try it, but don't rely on it (yet)
rustls = [
    "pingora-core/rustls",
    "pingora-proxy?/rustls",
    "pingora-cache?/rustls",
    "pingora-load-balancing?/rustls",
    "any_tls",
]

#! ### Pingora extensions

## Include the [proxy](crate::proxy) module
##
## This feature will include and export `pingora_proxy::prelude::*`
proxy = ["pingora-proxy"]

## Include the [lb](crate::lb) (load-balancing) module
##
## This feature will include and export `pingora_load_balancing::prelude::*`
lb = ["pingora-load-balancing", "proxy"]

## Include the [cache](crate::cache) module
##
## This feature will include and export `pingora_cache::prelude::*`
cache = ["pingora-cache"]

## Enable time/scheduling functionality
time = []

## Enable sentry for error notifications
sentry = ["pingora-core/sentry"]

## Enable pre-TLS connection filtering
connection_filter = [
    "pingora-core/connection_filter",
    "pingora-proxy?/connection_filter",
]


# These features are intentionally not documented
openssl_derived = ["any_tls"]
any_tls = []
patched_http1 = ["pingora-core/patched_http1"]
document-features = [
    "dep:document-features",
    "proxy",
    "lb",
    "cache",
    "time",
    "sentry",
    "connection_filter"
]


================================================
FILE: pingora/LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: pingora/examples/app/echo.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use async_trait::async_trait;
use bytes::Bytes;
use http::{Response, StatusCode};
use log::debug;
use once_cell::sync::Lazy;
use pingora_timeout::timeout;
use prometheus::{register_int_counter, IntCounter};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

use pingora::apps::http_app::ServeHttp;
use pingora::apps::ServerApp;
use pingora::protocols::http::ServerSession;
use pingora::protocols::Stream;
use pingora::server::ShutdownWatch;

static REQ_COUNTER: Lazy<IntCounter> =
    Lazy::new(|| register_int_counter!("reg_counter", "Number of requests").unwrap());

#[derive(Clone)]
pub struct EchoApp;

#[async_trait]
impl ServerApp for EchoApp {
    async fn process_new(
        self: &Arc<Self>,
        mut io: Stream,
        _shutdown: &ShutdownWatch,
    ) -> Option<Stream> {
        let mut buf = [0; 1024];
        loop {
            let n = io.read(&mut buf).await.unwrap();
            if n == 0 {
                debug!("session closing");
                return None;
            }
            io.write_all(&buf[0..n]).await.unwrap();
            io.flush().await.unwrap();
        }
    }
}

pub struct HttpEchoApp;

#[async_trait]
impl ServeHttp for HttpEchoApp {
    async fn response(&self, http_stream: &mut ServerSession) -> Response<Vec<u8>> {
        REQ_COUNTER.inc();
        // read timeout of 2s
        let read_timeout = 2000;
        let body = match timeout(
            Duration::from_millis(read_timeout),
            http_stream.read_request_body(),
        )
        .await
        {
            Ok(res) => match res.unwrap() {
                Some(bytes) => bytes,
                None => Bytes::from("no body!"),
            },
            Err(_) => {
                panic!("Timed out after {:?}ms", read_timeout);
            }
        };

        Response::builder()
            .status(StatusCode::OK)
            .header(http::header::CONTENT_TYPE, "text/html")
            .header(http::header::CONTENT_LENGTH, body.len())
            .body(body.to_vec())
            .unwrap()
    }
}


================================================
FILE: pingora/examples/app/mod.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod echo;
pub mod proxy;


================================================
FILE: pingora/examples/app/proxy.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use async_trait::async_trait;
use log::debug;

use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::select;

use pingora::apps::ServerApp;
use pingora::connectors::TransportConnector;
use pingora::protocols::Stream;
use pingora::server::ShutdownWatch;
use pingora::upstreams::peer::BasicPeer;

pub struct ProxyApp {
    client_connector: TransportConnector,
    proxy_to: BasicPeer,
}

enum DuplexEvent {
    DownstreamRead(usize),
    UpstreamRead(usize),
}

impl ProxyApp {
    pub fn new(proxy_to: BasicPeer) -> Self {
        ProxyApp {
            client_connector: TransportConnector::new(None),
            proxy_to,
        }
    }

    async fn duplex(&self, mut server_session: Stream, mut client_session: Stream) {
        let mut upstream_buf = [0; 1024];
        let mut downstream_buf = [0; 1024];
        loop {
            let downstream_read = server_session.read(&mut upstream_buf);
            let upstream_read = client_session.read(&mut downstream_buf);
            let event: DuplexEvent;
            select! {
                n = downstream_read => event
                    = DuplexEvent::DownstreamRead(n.unwrap()),
                n = upstream_read => event
                    = DuplexEvent::UpstreamRead(n.unwrap()),
            }
            match event {
                DuplexEvent::DownstreamRead(0) => {
                    debug!("downstream session closing");
                    return;
                }
                DuplexEvent::UpstreamRead(0) => {
                    debug!("upstream session closing");
                    return;
                }
                DuplexEvent::DownstreamRead(n) => {
                    client_session.write_all(&upstream_buf[0..n]).await.unwrap();
                    client_session.flush().await.unwrap();
                }
                DuplexEvent::UpstreamRead(n) => {
                    server_session
                        .write_all(&downstream_buf[0..n])
                        .await
                        .unwrap();
                    server_session.flush().await.unwrap();
                }
            }
        }
    }
}

#[async_trait]
impl ServerApp for ProxyApp {
    async fn process_new(
        self: &Arc<Self>,
        io: Stream,
        _shutdown: &ShutdownWatch,
    ) -> Option<Stream> {
        let client_session = self.client_connector.new_stream(&self.proxy_to).await;

        match client_session {
            Ok(client_session) => {
                self.duplex(io, client_session).await;
                None
            }
            Err(e) => {
                debug!("Failed to create client session: {}", e);
                None
            }
        }
    }
}


================================================
FILE: pingora/examples/client.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use pingora::{connectors::http::Connector, prelude::*};
use regex::Regex;

#[tokio::main]
async fn main() -> Result<()> {
    let connector = Connector::new(None);

    // create the HTTP session
    let peer_addr = "1.1.1.1:443";
    let mut peer = HttpPeer::new(peer_addr, true, "one.one.one.one".into());
    peer.options.set_http_version(2, 1);
    let (mut http, _reused) = connector.get_http_session(&peer).await?;

    // perform a GET request
    let mut new_request = RequestHeader::build("GET", b"/", None)?;
    new_request.insert_header("Host", "one.one.one.one")?;
    http.write_request_header(Box::new(new_request)).await?;

    // Servers usually don't respond until the full request body is read.
    http.finish_request_body().await?;
    http.read_response_header().await?;

    // display the headers from the response
    if let Some(header) = http.response_header() {
        println!("{header:#?}");
    } else {
        return Error::e_explain(ErrorType::InvalidHTTPHeader, "No response header");
    };

    // collect the response body
    let mut response_body = String::new();
    while let Some(chunk) = http.read_response_body().await? {
        response_body.push_str(&String::from_utf8_lossy(&chunk));
    }

    // verify that the response body is valid HTML by displaying the page <title>
    let re = Regex::new(r"<title>(.*?)</title>")
        .or_err(ErrorType::InternalError, "Failed to compile regex")?;
    if let Some(title) = re
        .captures(&response_body)
        .and_then(|caps| caps.get(1).map(|match_| match_.as_str()))
    {
        println!("Page Title: {title}");
    } else {
        return Error::e_explain(
            ErrorType::new("InvalidHTML"),
            "No <title> found in response body",
        );
    }

    // gracefully release the connection
    connector
        .release_http_session(http, &peer, Some(std::time::Duration::from_secs(5)))
        .await;

    Ok(())
}


================================================
FILE: pingora/examples/server.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;

use pingora::listeners::tls::TlsSettings;
use pingora::protocols::TcpKeepalive;
use pingora::server::configuration::Opt;
use pingora::server::{Server, ShutdownWatch};
use pingora::services::background::{background_service, BackgroundService};
use pingora::services::listening::Service as ListeningService;
use pingora::services::ServiceWithDependents;

use async_trait::async_trait;
use clap::Parser;
use tokio::time::interval;

use std::time::Duration;

mod app;
mod service;

pub struct ExampleBackgroundService;
#[async_trait]
impl BackgroundService for ExampleBackgroundService {
    async fn start(&self, mut shutdown: ShutdownWatch) {
        let mut period = interval(Duration::from_secs(1));
        loop {
            tokio::select! {
                _ = shutdown.changed() => {
                    // shutdown
                    break;
                }
                _ = period.tick() => {
                    // do some work
                    // ...
                }
            }
        }
    }
}
#[cfg(feature = "openssl_derived")]
mod boringssl_openssl {
    use super::*;
    use pingora::tls::pkey::{PKey, Private};
    use pingora::tls::x509::X509;

    pub(super) struct DynamicCert {
        cert: X509,
        key: PKey<Private>,
    }

    impl DynamicCert {
        pub(super) fn new(cert: &str, key: &str) -> Box<Self> {
            let cert_bytes = std::fs::read(cert).unwrap();
            let cert = X509::from_pem(&cert_bytes).unwrap();

            let key_bytes = std::fs::read(key).unwrap();
            let key = PKey::private_key_from_pem(&key_bytes).unwrap();
            Box::new(DynamicCert { cert, key })
        }
    }

    #[async_trait]
    impl pingora::listeners::TlsAccept for DynamicCert {
        async fn certificate_callback(&self, ssl: &mut pingora::tls::ssl::SslRef) {
            use pingora::tls::ext;
            ext::ssl_use_certificate(ssl, &self.cert).unwrap();
            ext::ssl_use_private_key(ssl, &self.key).unwrap();
        }
    }
}

const USAGE: &str = r#"
Usage
port 6142: TCP echo server
nc 127.0.0.1 6142

port 6143: TLS echo server
openssl s_client -connect 127.0.0.1:6143

port 6145: Http echo server
curl http://127.0.0.1:6145 -v -d 'hello'

port 6148: Https echo server
curl https://127.0.0.1:6148 -vk -d 'hello'

port 6141: TCP proxy
curl http://127.0.0.1:6141 -v -H 'host: 1.1.1.1'

port 6144: TLS proxy
curl https://127.0.0.1:6144 -vk -H 'host: one.one.one.one' -o /dev/null

port 6150: metrics endpoint
curl http://127.0.0.1:6150
"#;

pub fn main() {
    env_logger::init();

    print!("{USAGE}");

    let opt = Some(Opt::parse());
    let mut my_server = Server::new(opt).unwrap();
    my_server.bootstrap();

    let cert_path = format!("{}/tests/keys/server.crt", env!("CARGO_MANIFEST_DIR"));
    let key_path = format!("{}/tests/keys/key.pem", env!("CARGO_MANIFEST_DIR"));

    let mut echo_service = service::echo::echo_service();
    echo_service.add_tcp("127.0.0.1:6142");
    echo_service
        .add_tls("0.0.0.0:6143", &cert_path, &key_path)
        .unwrap();

    let mut echo_service_http = service::echo::echo_service_http();

    let mut options = pingora::listeners::TcpSocketOptions::default();
    options.tcp_fastopen = Some(10);
    options.tcp_keepalive = Some(TcpKeepalive {
        idle: Duration::from_secs(60),
        interval: Duration::from_secs(5),
        count: 5,
        #[cfg(target_os = "linux")]
        user_timeout: Duration::from_secs(85),
    });

    echo_service_http.add_tcp_with_settings("0.0.0.0:6145", options);
    echo_service_http.add_uds("/tmp/echo.sock", None);

    let mut tls_settings;

    // NOTE: dynamic certificate callback is only supported with BoringSSL/OpenSSL
    #[cfg(feature = "openssl_derived")]
    {
        use std::ops::DerefMut;

        let dynamic_cert = boringssl_openssl::DynamicCert::new(&cert_path, &key_path);
        tls_settings = TlsSettings::with_callbacks(dynamic_cert).unwrap();
        // by default intermediate supports both TLS 1.2 and 1.3. We force to tls 1.2 just for the demo

        tls_settings
            .deref_mut()
            .deref_mut()
            .set_max_proto_version(Some(pingora::tls::ssl::SslVersion::TLS1_2))
            .unwrap();
    }
    #[cfg(feature = "rustls")]
    {
        tls_settings = TlsSettings::intermediate(&cert_path, &key_path).unwrap();
    }
    #[cfg(feature = "s2n")]
    {
        tls_settings = TlsSettings::intermediate(&cert_path, &key_path).unwrap();
    }
    #[cfg(not(feature = "any_tls"))]
    {
        tls_settings = TlsSettings;
    }

    tls_settings.enable_h2();
    echo_service_http.add_tls_with_settings("0.0.0.0:6148", None, tls_settings);

    let proxy_service = service::proxy::proxy_service(
        "0.0.0.0:6141", // listen
        "1.1.1.1:80",   // proxy to
    );

    let proxy_service_ssl = service::proxy::proxy_service_tls(
        "0.0.0.0:6144",    // listen
        "1.1.1.1:443",     // proxy to
        "one.one.one.one", // SNI
        &cert_path,
        &key_path,
    );

    let mut prometheus_service_http = ListeningService::prometheus_http_service();
    prometheus_service_http.add_tcp("127.0.0.1:6150");

    let background_service = background_service("example", ExampleBackgroundService {});

    let services: Vec<Box<dyn ServiceWithDependents>> = vec![
        Box::new(echo_service),
        Box::new(echo_service_http),
        Box::new(proxy_service),
        Box::new(proxy_service_ssl),
        Box::new(prometheus_service_http),
        Box::new(background_service),
    ];
    my_server.add_services(services);
    my_server.run_forever();
}


================================================
FILE: pingora/examples/service/echo.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::app::echo::{EchoApp, HttpEchoApp};
use pingora::apps::http_app::HttpServer;
use pingora::services::listening::Service;

pub fn echo_service() -> Service<EchoApp> {
    Service::new("Echo Service".to_string(), EchoApp)
}

pub fn echo_service_http() -> Service<HttpServer<HttpEchoApp>> {
    let server = HttpServer::new_app(HttpEchoApp);
    Service::new("Echo Service HTTP".to_string(), server)
}


================================================
FILE: pingora/examples/service/mod.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod echo;
pub mod proxy;


================================================
FILE: pingora/examples/service/proxy.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::app::proxy::ProxyApp;
use pingora_core::listeners::Listeners;
use pingora_core::services::listening::Service;
use pingora_core::upstreams::peer::BasicPeer;

pub fn proxy_service(addr: &str, proxy_addr: &str) -> Service<ProxyApp> {
    let proxy_to = BasicPeer::new(proxy_addr);

    Service::with_listeners(
        "Proxy Service".to_string(),
        Listeners::tcp(addr),
        ProxyApp::new(proxy_to),
    )
}

pub fn proxy_service_tls(
    addr: &str,
    proxy_addr: &str,
    proxy_sni: &str,
    cert_path: &str,
    key_path: &str,
) -> Service<ProxyApp> {
    let mut proxy_to = BasicPeer::new(proxy_addr);
    // set SNI to enable TLS
    proxy_to.sni = proxy_sni.into();
    Service::with_listeners(
        "Proxy Service TLS".to_string(),
        Listeners::tls(addr, cert_path, key_path).unwrap(),
        ProxyApp::new(proxy_to),
    )
}


================================================
FILE: pingora/src/lib.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![warn(clippy::all)]
#![allow(clippy::new_without_default)]
#![allow(clippy::type_complexity)]
#![allow(clippy::match_wild_err_arm)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::upper_case_acronyms)]
// This enables the feature that labels modules that are only available with
// certain pingora features
#![cfg_attr(docsrs, feature(doc_cfg))]

//! # Pingora
//!
//! Pingora is a framework to build fast, reliable and programmable networked systems at Internet scale.
//!
//! # Features
//! - Http 1.x and Http 2
//! - Modern TLS with OpenSSL or BoringSSL (FIPS compatible)
//! - Zero downtime upgrade
//!
//! # Usage
//! This crate provides low level service and protocol implementation and abstraction.
//!
//! If looking to build a (reverse) proxy, see [`pingora-proxy`](https://docs.rs/pingora-proxy) crate.
//!
//! # Feature flags
#![cfg_attr(
    feature = "document-features",
    cfg_attr(doc, doc = ::document_features::document_features!())
)]

pub use pingora_core::*;

/// HTTP header objects that preserve http header cases
pub mod http {
    pub use pingora_http::*;
}

#[cfg(feature = "cache")]
#[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
/// Caching services and tooling
pub mod cache {
    pub use pingora_cache::*;
}

#[cfg(feature = "lb")]
#[cfg_attr(docsrs, doc(cfg(feature = "lb")))]
/// Load balancing recipes
pub mod lb {
    pub use pingora_load_balancing::*;
}

#[cfg(feature = "proxy")]
#[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
/// Proxying recipes
pub mod proxy {
    pub use pingora_proxy::*;
}

#[cfg(feature = "time")]
#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
/// Timeouts and other useful time utilities
pub mod time {
    pub use pingora_timeout::*;
}

/// A useful set of types for getting started
pub mod prelude {
    pub use pingora_core::prelude::*;
    pub use pingora_http::prelude::*;
    pub use pingora_timeout::*;

    #[cfg(feature = "cache")]
    #[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
    pub use pingora_cache::prelude::*;

    #[cfg(feature = "lb")]
    #[cfg_attr(docsrs, doc(cfg(feature = "lb")))]
    pub use pingora_load_balancing::prelude::*;

    #[cfg(feature = "proxy")]
    #[cfg_attr(docsrs, doc(cfg(feature = "proxy")))]
    pub use pingora_proxy::prelude::*;

    #[cfg(feature = "time")]
    #[cfg_attr(docsrs, doc(cfg(feature = "time")))]
    pub use pingora_timeout::*;
}


================================================
FILE: pingora/tests/pingora_conf.yaml
================================================
---
version: 1
client_bind_to_ipv4:
    - 127.0.0.2
ca_file: tests/keys/server.crt

================================================
FILE: pingora-boringssl/Cargo.toml
================================================
[package]
name = "pingora-boringssl"
version = "0.8.0"
authors = ["Yuchen Wu <yuchen@cloudflare.com>"]
license = "Apache-2.0"
edition = "2021"
repository = "https://github.com/cloudflare/pingora"
categories = ["asynchronous", "network-programming"]
keywords = ["async", "tls", "ssl", "pingora"]
description = """
BoringSSL async APIs for Pingora.
"""

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "pingora_boringssl"
path = "src/lib.rs"

[dependencies]
boring = { version = "4.5", features = ["pq-experimental"] }
boring-sys = "4.5"
futures-util = { version = "0.3", default-features = false }
tokio = { workspace = true, features = ["io-util", "net", "macros", "rt-multi-thread"] }
libc = "0.2.70"
foreign-types-shared = { version = "0.3" }


[dev-dependencies]
tokio-test = "0.4"
tokio = { workspace = true, features = ["full"] }

[features]
default = []
pq_use_second_keyshare = []
# waiting for boring-rs release
read_uninit = []


================================================
FILE: pingora-boringssl/LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: pingora-boringssl/src/boring_tokio.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This file reimplements tokio-boring with the [overhauled](https://github.com/sfackler/tokio-openssl/commit/56f6618ab619f3e431fa8feec2d20913bf1473aa)
//! tokio-openssl interface while the tokio APIs from official [boring] crate is not yet caught up to it.

use boring::error::ErrorStack;
use boring::ssl::{self, ErrorCode, ShutdownResult, Ssl, SslRef, SslStream as SslStreamCore};
use futures_util::future;
use std::fmt;
use std::io::{self, Read, Write};
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

struct StreamWrapper<S> {
    stream: S,
    context: usize,
}

impl<S> fmt::Debug for StreamWrapper<S>
where
    S: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.stream, fmt)
    }
}

impl<S> StreamWrapper<S> {
    /// # Safety
    ///
    /// Must be called with `context` set to a valid pointer to a live `Context` object, and the
    /// wrapper must be pinned in memory.
    unsafe fn parts(&mut self) -> (Pin<&mut S>, &mut Context<'_>) {
        debug_assert_ne!(self.context, 0);
        let stream = Pin::new_unchecked(&mut self.stream);
        let context = &mut *(self.context as *mut _);
        (stream, context)
    }
}

impl<S> Read for StreamWrapper<S>
where
    S: AsyncRead,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let (stream, cx) = unsafe { self.parts() };
        let mut buf = ReadBuf::new(buf);
        match stream.poll_read(cx, &mut buf)? {
            Poll::Ready(()) => Ok(buf.filled().len()),
            Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
        }
    }
}

impl<S> Write for StreamWrapper<S>
where
    S: AsyncWrite,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let (stream, cx) = unsafe { self.parts() };
        match stream.poll_write(cx, buf) {
            Poll::Ready(r) => r,
            Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
        }
    }

    fn flush(&mut self) -> io::Result<()> {
        let (stream, cx) = unsafe { self.parts() };
        match stream.poll_flush(cx) {
            Poll::Ready(r) => r,
            Poll::Pending => Err(io::Error::from(io::ErrorKind::WouldBlock)),
        }
    }
}

fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
    match r {
        Ok(v) => Poll::Ready(Ok(v)),
        Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
        Err(e) => Poll::Ready(Err(e)),
    }
}

fn cvt_ossl<T>(r: Result<T, ssl::Error>) -> Poll<Result<T, ssl::Error>> {
    match r {
        Ok(v) => Poll::Ready(Ok(v)),
        Err(e) => match e.code() {
            ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => Poll::Pending,
            _ => Poll::Ready(Err(e)),
        },
    }
}

/// An asynchronous version of [`boring::ssl::SslStream`].
#[derive(Debug)]
pub struct SslStream<S>(SslStreamCore<StreamWrapper<S>>);

impl<S: AsyncRead + AsyncWrite> SslStream<S> {
    /// Like [`SslStream::new`](ssl::SslStream::new).
    pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> {
        SslStreamCore::new(ssl, StreamWrapper { stream, context: 0 }).map(SslStream)
    }

    /// Like [`SslStream::connect`](ssl::SslStream::connect).
    pub fn poll_connect(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Result<(), ssl::Error>> {
        self.with_context(cx, |s| cvt_ossl(s.connect()))
    }

    /// A convenience method wrapping [`poll_connect`](Self::poll_connect).
    pub async fn connect(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
        future::poll_fn(|cx| self.as_mut().poll_connect(cx)).await
    }

    /// Like [`SslStream::accept`](ssl::SslStream::accept).
    pub fn poll_accept(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), ssl::Error>> {
        self.with_context(cx, |s| cvt_ossl(s.accept()))
    }

    /// A convenience method wrapping [`poll_accept`](Self::poll_accept).
    pub async fn accept(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
        future::poll_fn(|cx| self.as_mut().poll_accept(cx)).await
    }

    /// Like [`SslStream::do_handshake`](ssl::SslStream::do_handshake).
    pub fn poll_do_handshake(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Result<(), ssl::Error>> {
        self.with_context(cx, |s| cvt_ossl(s.do_handshake()))
    }

    /// A convenience method wrapping [`poll_do_handshake`](Self::poll_do_handshake).
    pub async fn do_handshake(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
        future::poll_fn(|cx| self.as_mut().poll_do_handshake(cx)).await
    }

    // TODO: early data
}

impl<S> SslStream<S> {
    /// Returns a shared reference to the `Ssl` object associated with this stream.
    pub fn ssl(&self) -> &SslRef {
        self.0.ssl()
    }

    /// Returns a shared reference to the underlying stream.
    pub fn get_ref(&self) -> &S {
        &self.0.get_ref().stream
    }

    /// Returns a mutable reference to the underlying stream.
    pub fn get_mut(&mut self) -> &mut S {
        &mut self.0.get_mut().stream
    }

    /// Returns a pinned mutable reference to the underlying stream.
    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
        unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0.get_mut().stream) }
    }

    fn with_context<F, R>(self: Pin<&mut Self>, ctx: &mut Context<'_>, f: F) -> R
    where
        F: FnOnce(&mut SslStreamCore<StreamWrapper<S>>) -> R,
    {
        let this = unsafe { self.get_unchecked_mut() };
        this.0.get_mut().context = ctx as *mut _ as usize;
        let r = f(&mut this.0);
        this.0.get_mut().context = 0;
        r
    }
}

#[cfg(feature = "read_uninit")]
impl<S> AsyncRead for SslStream<S>
where
    S: AsyncRead + AsyncWrite,
{
    fn poll_read(
        self: Pin<&mut Self>,
        ctx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        self.with_context(ctx, |s| {
            // SAFETY: read_uninit does not de-initialize the buffer.
            match cvt(s.read_uninit(unsafe { buf.unfilled_mut() }))? {
                Poll::Ready(nread) => {
                    unsafe {
                        buf.assume_init(nread);
                    }
                    buf.advance(nread);
                    Poll::Ready(Ok(()))
                }
                Poll::Pending => Poll::Pending,
            }
        })
    }
}

#[cfg(not(feature = "read_uninit"))]
impl<S> AsyncRead for SslStream<S>
where
    S: AsyncRead + AsyncWrite,
{
    fn poll_read(
        self: Pin<&mut Self>,
        ctx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        self.with_context(ctx, |s| {
            // This isn't really "proper", but rust-openssl doesn't currently expose a suitable interface even though
            // OpenSSL itself doesn't require the buffer to be initialized. So this is good enough for now.
            let slice = unsafe {
                let buf = buf.unfilled_mut();
                std::slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<u8>(), buf.len())
            };
            match cvt(s.read(slice))? {
                Poll::Ready(nread) => {
                    unsafe {
                        buf.assume_init(nread);
                    }
                    buf.advance(nread);
                    Poll::Ready(Ok(()))
                }
                Poll::Pending => Poll::Pending,
            }
        })
    }
}

impl<S> AsyncWrite for SslStream<S>
where
    S: AsyncRead + AsyncWrite,
{
    fn poll_write(self: Pin<&mut Self>, ctx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
        self.with_context(ctx, |s| cvt(s.write(buf)))
    }

    fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
        self.with_context(ctx, |s| cvt(s.flush()))
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Result<()>> {
        match self.as_mut().with_context(ctx, |s| s.shutdown()) {
            Ok(ShutdownResult::Sent) | Ok(ShutdownResult::Received) => {}
            Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => {}
            Err(ref e) if e.code() == ErrorCode::WANT_READ || e.code() == ErrorCode::WANT_WRITE => {
                return Poll::Pending;
            }
            Err(e) => {
                return Poll::Ready(Err(e.into_io_error().unwrap_or_else(io::Error::other)));
            }
        }

        self.get_pin_mut().poll_shutdown(ctx)
    }
}

#[tokio::test]
async fn test_google() {
    use boring::ssl;
    use std::net::ToSocketAddrs;
    use std::pin::Pin;
    use tokio::io::{AsyncReadExt, AsyncWriteExt};
    use tokio::net::TcpStream;

    let addr = "8.8.8.8:443".to_socket_addrs().unwrap().next().unwrap();
    let stream = TcpStream::connect(&addr).await.unwrap();

    let ssl_context = ssl::SslContext::builder(ssl::SslMethod::tls())
        .unwrap()
        .build();
    let ssl = ssl::Ssl::new(&ssl_context).unwrap();
    let mut stream = crate::tokio_ssl::SslStream::new(ssl, stream).unwrap();

    Pin::new(&mut stream).connect().await.unwrap();

    stream.write_all(b"GET / HTTP/1.0\r\n\r\n").await.unwrap();

    let mut buf = vec![];
    stream.read_to_end(&mut buf).await.unwrap();
    let response = String::from_utf8_lossy(&buf);
    let response = response.trim_end();

    // any response code is fine
    assert!(response.starts_with("HTTP/1.0 "));
    assert!(response.ends_with("</html>") || response.ends_with("</HTML>"));
}


================================================
FILE: pingora-boringssl/src/ext.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! the extended functionalities that are yet exposed via the [`boring`] APIs

use boring::error::ErrorStack;
use boring::pkey::{HasPrivate, PKeyRef};
use boring::ssl::{Ssl, SslAcceptor, SslRef};
use boring::x509::store::X509StoreRef;
use boring::x509::verify::X509VerifyParamRef;
use boring::x509::X509Ref;
use foreign_types_shared::ForeignTypeRef;
use libc::*;
use std::ffi::CString;

fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
    if r != 1 {
        Err(ErrorStack::get())
    } else {
        Ok(r)
    }
}

/// Add name as an additional reference identifier that can match the peer's certificate
///
/// See [X509_VERIFY_PARAM_set1_host](https://www.openssl.org/docs/man3.1/man3/X509_VERIFY_PARAM_set1_host.html).
pub fn add_host(verify_param: &mut X509VerifyParamRef, host: &str) -> Result<(), ErrorStack> {
    if host.is_empty() {
        return Ok(());
    }
    unsafe {
        cvt(boring_sys::X509_VERIFY_PARAM_add1_host(
            verify_param.as_ptr(),
            host.as_ptr() as *const _,
            host.len(),
        ))
        .map(|_| ())
    }
}

/// Set the verify cert store of `ssl`
///
/// See [SSL_set1_verify_cert_store](https://www.openssl.org/docs/man1.1.1/man3/SSL_set1_verify_cert_store.html).
pub fn ssl_set_verify_cert_store(
    ssl: &mut SslRef,
    cert_store: &X509StoreRef,
) -> Result<(), ErrorStack> {
    unsafe {
        cvt(boring_sys::SSL_set1_verify_cert_store(
            ssl.as_ptr(),
            cert_store.as_ptr(),
        ))?;
    }
    Ok(())
}

/// Load the certificate into `ssl`
///
/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html).
pub fn ssl_use_certificate(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
    unsafe {
        cvt(boring_sys::SSL_use_certificate(ssl.as_ptr(), cert.as_ptr()))?;
    }
    Ok(())
}

/// Load the private key into `ssl`
///
/// See [SSL_use_certificate](https://www.openssl.org/docs/man1.1.1/man3/SSL_use_PrivateKey.html).
pub fn ssl_use_private_key<T>(ssl: &mut SslRef, key: &PKeyRef<T>) -> Result<(), ErrorStack>
where
    T: HasPrivate,
{
    unsafe {
        cvt(boring_sys::SSL_use_PrivateKey(ssl.as_ptr(), key.as_ptr()))?;
    }
    Ok(())
}

/// Add the certificate into the cert chain of `ssl`
///
/// See [SSL_add1_chain_cert](https://www.openssl.org/docs/man1.1.1/man3/SSL_add1_chain_cert.html)
pub fn ssl_add_chain_cert(ssl: &mut SslRef, cert: &X509Ref) -> Result<(), ErrorStack> {
    unsafe {
        cvt(boring_sys::SSL_add1_chain_cert(ssl.as_ptr(), cert.as_ptr()))?;
    }
    Ok(())
}

/// Set renegotiation
///
/// This function is specific to BoringSSL
/// See <https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_set_renegotiate_mode>
pub fn ssl_set_renegotiate_mode_freely(ssl: &mut SslRef) {
    unsafe {
        boring_sys::SSL_set_renegotiate_mode(
            ssl.as_ptr(),
            boring_sys::ssl_renegotiate_mode_t::ssl_renegotiate_freely,
        );
    }
}

/// Set the curves/groups of `ssl`
///
/// See [set_groups_list](https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_curves.html).
pub fn ssl_set_groups_list(ssl: &mut SslRef, groups: &str) -> Result<(), ErrorStack> {
    let groups = CString::new(groups).unwrap();
    unsafe {
        // somehow SSL_set1_groups_list doesn't exist but SSL_set1_curves_list means the same anyways
        cvt(boring_sys::SSL_set1_curves_list(
            ssl.as_ptr(),
            groups.as_ptr(),
        ))?;
    }
    Ok(())
}

/// Set's whether a second keyshare to be sent in client hello when PQ is used.
///
/// Default is true. When `true`, the first PQ (if any) and none-PQ keyshares are sent.
/// When `false`, only the first configured keyshares are sent.
#[cfg(feature = "pq_use_second_keyshare")]
pub fn ssl_use_second_key_share(ssl: &mut SslRef, enabled: bool) {
    unsafe { boring_sys::SSL_use_second_keyshare(ssl.as_ptr(), enabled as _) }
}
#[cfg(not(feature = "pq_use_second_keyshare"))]
pub fn ssl_use_second_key_share(_ssl: &mut SslRef, _enabled: bool) {}

/// Clear the error stack
///
/// SSL calls should check and clear the BoringSSL error stack. But some calls fail to do so.
/// This causes the next unrelated SSL call to fail due to the leftover errors. This function allows
/// the caller to clear the error stack before performing SSL calls to avoid this issue.
pub fn clear_error_stack() {
    let _ = ErrorStack::get();
}

/// Create a new [Ssl] from &[SslAcceptor]
///
/// This function is needed because [Ssl::new()] doesn't take `&SslContextRef` like openssl-rs
pub fn ssl_from_acceptor(acceptor: &SslAcceptor) -> Result<Ssl, ErrorStack> {
    Ssl::new_from_ref(acceptor.context())
}

/// Suspend the TLS handshake when a certificate is needed.
///
/// This function will cause tls handshake to pause and return the error: SSL_ERROR_WANT_X509_LOOKUP.
/// The caller should set the certificate and then call [unblock_ssl_cert()] before continue the
/// handshake on the tls connection.
pub fn suspend_when_need_ssl_cert(ssl: &mut SslRef) {
    unsafe {
        boring_sys::SSL_set_cert_cb(ssl.as_ptr(), Some(raw_cert_block), std::ptr::null_mut());
    }
}

/// Unblock a TLS handshake after the certificate is set.
///
/// The user should continue to call tls handshake after this function is called.
pub fn unblock_ssl_cert(ssl: &mut SslRef) {
    unsafe {
        boring_sys::SSL_set_cert_cb(ssl.as_ptr(), None, std::ptr::null_mut());
    }
}

// Just block the handshake
extern "C" fn raw_cert_block(_ssl: *mut boring_sys::SSL, _arg: *mut c_void) -> c_int {
    -1
}

/// Whether the TLS error is SSL_ERROR_WANT_X509_LOOKUP
pub fn is_suspended_for_cert(error: &boring::ssl::Error) -> bool {
    error.code().as_raw() == boring_sys::SSL_ERROR_WANT_X509_LOOKUP
}

#[allow(clippy::mut_from_ref)]
/// Get a mutable SslRef ouf of SslRef. which is a missing functionality for certain SslStream
/// # Safety
/// the caller needs to make sure that they hold a &mut SslRef
pub unsafe fn ssl_mut(ssl: &SslRef) -> &mut SslRef {
    unsafe { SslRef::from_ptr_mut(ssl.as_ptr()) }
}


================================================
FILE: pingora-boringssl/src/lib.rs
================================================
// Copyright 2026 Cloudflare, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! The BoringSSL API compatibility layer.
//!
//! This crate aims at making [boring] APIs exchangeable with [openssl-rs](https://docs.rs/openssl/latest/openssl/).
//! In other words, this crate and [`pingora-openssl`](https://docs.rs/pingora-openssl) expose identical rust APIs.

#![warn(clippy::all)]

use boring as ssl_lib;
pub use boring_sys as ssl_sys;
pub mod boring_tokio;
pub use boring_tokio as tokio_ssl;
pub mod ext;

// export commonly used libs
pub use ssl_lib::error;
pub use ssl_lib::hash;
pub use ssl_lib::nid;
pub use ssl_lib::pkey;
pub use ssl_lib::ssl;
pub use ssl_lib::x509;


================================================
FILE: pingora-cache/Cargo.toml
================================================
[package]
name = "pingora-cache"
version = "0.8.0"
authors = ["Yuchen Wu <yuchen@cloudflare.com>"]
license = "Apache-2.0"
edition = "2021"
rust-version = "1.84"
repository = "https://github.com/cloudflare/pingora"
categories = ["asynchronous", "network-programming"]
keywords = ["async", "http", "cache"]
description = """
HTTP caching APIs for Pingora proxy.
"""

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "pingora_cache"
path = "src/lib.rs"

[dependencies]
pingora-core = { version = "0.8.0", path = "../pingora-core", default-features = false }
pingora-error = { version = "0.8.0", path = "../pingora-error" }
pingora-header-serde = { version = "0.8.0", path = "../pingora-header-serde" }
pingora-http = { version = "0.8.0", path = "../pingora-http" }
pingora-lru = { version = "0.8.0", path = "../pingora-lru" }
pingora-timeout = { version = "0.8.0", path = "../pingora-timeout" }
bstr = { workspace = true }
http = { workspace = true }
indexmap = "1"
once_cell = { workspace = true }
regex = "1"
blake2 = "0.10"
serde = { version = "1.0", features = ["derive"] }
rmp-serde = "1.3.0"
bytes = { workspace = true }
httpdate = "1.0.2"
log = { workspace = true }
async-trait = { workspace = true }
parking_lot = "0.12"
cf-rustracing = "1.0"
cf-rustracing-jaeger = "1.0"
rmp = "0.8.14"
tokio = { workspace = true }
lru = { workspace = true }
ahash = { workspace = true }
hex = "0.4"
httparse = { workspace = true }
strum = { version = "0.26", features = ["derive"] }
rand = "0.8"

[dev-dependencies]
tokio-test = "0.4"
tokio = { workspace = true, features = ["fs"] }
env_logger = "0.11"
dhat = "0"
futures = "0.3"

[[bench]]
name = "simple_lru_memory"
harness = false

[[bench]]
name = "lru_memory"
harness = false

[[bench]]
name = "lru_serde"
harness = false

[features]
default = []
openssl = ["pingora-core/openssl"]
boringssl = ["pingora-core/boringssl"]
rustls = ["pingora-core/rustls"]
s2n = ["pingora-core/s2n"]


================================================
FILE: pingora-cache/LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes 
Download .txt
gitextract__dwmq04u/

├── .bleep
├── .cargo/
│   ├── audit.toml
│   └── config.toml
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       ├── audit.yml
│       ├── build.yml
│       ├── docs.yml
│       ├── mark-stale.yaml
│       └── semgrep.yml
├── .gitignore
├── .rustfmt.toml
├── CHANGELOG.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── README.md
├── cliff.toml
├── clippy.toml
├── docs/
│   ├── README.md
│   ├── quick_start.md
│   └── user_guide/
│       ├── conf.md
│       ├── ctx.md
│       ├── daemon.md
│       ├── error_log.md
│       ├── errors.md
│       ├── failover.md
│       ├── graceful.md
│       ├── index.md
│       ├── internals.md
│       ├── modify_filter.md
│       ├── panic.md
│       ├── peer.md
│       ├── phase.md
│       ├── phase_chart.md
│       ├── pooling.md
│       ├── prom.md
│       ├── rate_limiter.md
│       ├── start_stop.md
│       └── systemd.md
├── pingora/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── app/
│   │   │   ├── echo.rs
│   │   │   ├── mod.rs
│   │   │   └── proxy.rs
│   │   ├── client.rs
│   │   ├── server.rs
│   │   └── service/
│   │       ├── echo.rs
│   │       ├── mod.rs
│   │       └── proxy.rs
│   ├── src/
│   │   └── lib.rs
│   └── tests/
│       └── pingora_conf.yaml
├── pingora-boringssl/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── boring_tokio.rs
│       ├── ext.rs
│       └── lib.rs
├── pingora-cache/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── lru_memory.rs
│   │   ├── lru_serde.rs
│   │   └── simple_lru_memory.rs
│   └── src/
│       ├── cache_control.rs
│       ├── eviction/
│       │   ├── lru.rs
│       │   ├── mod.rs
│       │   └── simple_lru.rs
│       ├── filters.rs
│       ├── hashtable.rs
│       ├── key.rs
│       ├── lib.rs
│       ├── lock.rs
│       ├── max_file_size.rs
│       ├── memory.rs
│       ├── meta.rs
│       ├── predictor.rs
│       ├── put.rs
│       ├── storage.rs
│       ├── trace.rs
│       └── variance.rs
├── pingora-core/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── bootstrap_as_a_service.rs
│   │   ├── client_cert.rs
│   │   ├── keys/
│   │   │   ├── client-ca/
│   │   │   │   ├── cert.pem
│   │   │   │   └── key.pem
│   │   │   ├── clients/
│   │   │   │   ├── cert-1.pem
│   │   │   │   ├── cert-2.pem
│   │   │   │   ├── invalid-cert.pem
│   │   │   │   ├── invalid-key.pem
│   │   │   │   ├── key-1.pem
│   │   │   │   └── key-2.pem
│   │   │   └── server/
│   │   │       ├── cert.pem
│   │   │       └── key.pem
│   │   └── service_dependencies.rs
│   ├── src/
│   │   ├── apps/
│   │   │   ├── http_app.rs
│   │   │   ├── mod.rs
│   │   │   └── prometheus_http_app.rs
│   │   ├── connectors/
│   │   │   ├── http/
│   │   │   │   ├── custom/
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── v1.rs
│   │   │   │   └── v2.rs
│   │   │   ├── l4.rs
│   │   │   ├── mod.rs
│   │   │   ├── offload.rs
│   │   │   └── tls/
│   │   │       ├── boringssl_openssl/
│   │   │       │   └── mod.rs
│   │   │       ├── mod.rs
│   │   │       ├── rustls/
│   │   │       │   └── mod.rs
│   │   │       └── s2n/
│   │   │           └── mod.rs
│   │   ├── lib.rs
│   │   ├── listeners/
│   │   │   ├── connection_filter.rs
│   │   │   ├── l4.rs
│   │   │   ├── mod.rs
│   │   │   └── tls/
│   │   │       ├── boringssl_openssl/
│   │   │       │   └── mod.rs
│   │   │       ├── mod.rs
│   │   │       ├── rustls/
│   │   │       │   └── mod.rs
│   │   │       └── s2n/
│   │   │           └── mod.rs
│   │   ├── modules/
│   │   │   ├── http/
│   │   │   │   ├── compression.rs
│   │   │   │   ├── grpc_web.rs
│   │   │   │   └── mod.rs
│   │   │   └── mod.rs
│   │   ├── protocols/
│   │   │   ├── digest.rs
│   │   │   ├── http/
│   │   │   │   ├── body_buffer.rs
│   │   │   │   ├── bridge/
│   │   │   │   │   ├── grpc_web.rs
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── client.rs
│   │   │   │   ├── compression/
│   │   │   │   │   ├── brotli.rs
│   │   │   │   │   ├── gzip.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── zstd.rs
│   │   │   │   ├── conditional_filter.rs
│   │   │   │   ├── custom/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   ├── date.rs
│   │   │   │   ├── error_resp.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── server.rs
│   │   │   │   ├── subrequest/
│   │   │   │   │   ├── body.rs
│   │   │   │   │   ├── dummy.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   ├── v1/
│   │   │   │   │   ├── body.rs
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── common.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   └── server.rs
│   │   │   │   └── v2/
│   │   │   │       ├── client.rs
│   │   │   │       ├── mod.rs
│   │   │   │       └── server.rs
│   │   │   ├── l4/
│   │   │   │   ├── ext.rs
│   │   │   │   ├── listener.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── socket.rs
│   │   │   │   ├── stream.rs
│   │   │   │   └── virt.rs
│   │   │   ├── mod.rs
│   │   │   ├── raw_connect.rs
│   │   │   ├── tls/
│   │   │   │   ├── boringssl_openssl/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── server.rs
│   │   │   │   │   └── stream.rs
│   │   │   │   ├── digest.rs
│   │   │   │   ├── mod.rs
│   │   │   │   ├── noop_tls/
│   │   │   │   │   └── mod.rs
│   │   │   │   ├── rustls/
│   │   │   │   │   ├── client.rs
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── server.rs
│   │   │   │   │   └── stream.rs
│   │   │   │   └── s2n/
│   │   │   │       ├── client.rs
│   │   │   │       ├── mod.rs
│   │   │   │       ├── server.rs
│   │   │   │       └── stream.rs
│   │   │   └── windows.rs
│   │   ├── server/
│   │   │   ├── bootstrap_services.rs
│   │   │   ├── configuration/
│   │   │   │   └── mod.rs
│   │   │   ├── daemon.rs
│   │   │   ├── mod.rs
│   │   │   └── transfer_fd/
│   │   │       └── mod.rs
│   │   ├── services/
│   │   │   ├── background.rs
│   │   │   ├── listening.rs
│   │   │   └── mod.rs
│   │   ├── tls/
│   │   │   └── mod.rs
│   │   ├── upstreams/
│   │   │   ├── mod.rs
│   │   │   └── peer.rs
│   │   └── utils/
│   │       ├── mod.rs
│   │       └── tls/
│   │           ├── boringssl_openssl.rs
│   │           ├── mod.rs
│   │           ├── rustls.rs
│   │           └── s2n.rs
│   └── tests/
│       ├── certs/
│       │   ├── alt-ca.crt
│       │   ├── alt-server.crt
│       │   ├── ca.crt
│       │   ├── server.crt
│       │   └── server.key
│       ├── keys/
│       │   ├── key.pem
│       │   ├── public.pem
│       │   ├── server.crt
│       │   └── server.csr
│       ├── nginx.conf
│       ├── nginx_proxy.conf
│       ├── pingora_conf.yaml
│       ├── server_phase_fastshutdown.rs
│       ├── server_phase_gracefulshutdown.rs
│       ├── test_basic.rs
│       └── utils/
│           └── mod.rs
├── pingora-error/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── immut_str.rs
│       └── lib.rs
├── pingora-header-serde/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── samples/
│   │   └── test/
│   │       ├── 1
│   │       ├── 2
│   │       ├── 3
│   │       ├── 4
│   │       ├── 5
│   │       ├── 6
│   │       └── 7
│   └── src/
│       ├── dict.rs
│       ├── lib.rs
│       ├── thread_zstd.rs
│       └── trainer.rs
├── pingora-http/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── case_header_name.rs
│       └── lib.rs
├── pingora-ketama/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── memory.rs
│   │   └── simple.rs
│   ├── examples/
│   │   └── health_aware_selector.rs
│   ├── src/
│   │   └── lib.rs
│   ├── test-data/
│   │   ├── README.md
│   │   ├── nginx.conf
│   │   ├── sample-nginx-upstream.csv
│   │   └── trace.sh
│   └── tests/
│       ├── backwards_compat.rs
│       └── old_version/
│           └── mod.rs
├── pingora-limits/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── benchmark.rs
│   └── src/
│       ├── estimator.rs
│       ├── inflight.rs
│       ├── lib.rs
│       └── rate.rs
├── pingora-load-balancing/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── background.rs
│       ├── discovery.rs
│       ├── health_check.rs
│       ├── lib.rs
│       └── selection/
│           ├── algorithms.rs
│           ├── consistent.rs
│           ├── mod.rs
│           └── weighted.rs
├── pingora-lru/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   ├── bench_linked_list.rs
│   │   └── bench_lru.rs
│   └── src/
│       ├── lib.rs
│       └── linked_list.rs
├── pingora-memory-cache/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── lib.rs
│       └── read_through.rs
├── pingora-openssl/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── ext.rs
│       └── lib.rs
├── pingora-pool/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       ├── connection.rs
│       ├── lib.rs
│       └── lru.rs
├── pingora-proxy/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── examples/
│   │   ├── backoff_retry.rs
│   │   ├── conf.yaml
│   │   ├── connection_filter.rs
│   │   ├── ctx.rs
│   │   ├── gateway.rs
│   │   ├── grpc_web_module.rs
│   │   ├── load_balancer.rs
│   │   ├── modify_response.rs
│   │   ├── multi_lb.rs
│   │   ├── rate_limiter.rs
│   │   ├── use_module.rs
│   │   └── virtual_l4.rs
│   ├── src/
│   │   ├── lib.rs
│   │   ├── proxy_cache.rs
│   │   ├── proxy_common.rs
│   │   ├── proxy_custom.rs
│   │   ├── proxy_h1.rs
│   │   ├── proxy_h2.rs
│   │   ├── proxy_purge.rs
│   │   ├── proxy_trait.rs
│   │   └── subrequest/
│   │       ├── mod.rs
│   │       └── pipe.rs
│   └── tests/
│       ├── headers.dict
│       ├── keys/
│       │   ├── key.pem
│       │   ├── public.pem
│       │   ├── server.crt
│       │   └── server.csr
│       ├── pingora_conf.yaml
│       ├── test_basic.rs
│       ├── test_upstream.rs
│       └── utils/
│           ├── cert.rs
│           ├── conf/
│           │   ├── keys/
│           │   │   ├── README.md
│           │   │   ├── ca1.crt
│           │   │   ├── ca1.key.pem
│           │   │   ├── ca2.crt
│           │   │   ├── ca_chain.cert
│           │   │   ├── ca_chain.srl
│           │   │   ├── cert_chain.crt
│           │   │   ├── curve_test.384.crt
│           │   │   ├── curve_test.384.key.pem
│           │   │   ├── curve_test.521.crt
│           │   │   ├── curve_test.521.key.pem
│           │   │   ├── ex1.crt
│           │   │   ├── ex1.key.b64
│           │   │   ├── intermediate.cnf
│           │   │   ├── intermediate.crt
│           │   │   ├── intermediate.csr
│           │   │   ├── intermediate.key
│           │   │   ├── intermediate.srl
│           │   │   ├── key.pem
│           │   │   ├── leaf.cnf
│           │   │   ├── leaf.crt
│           │   │   ├── leaf.csr
│           │   │   ├── leaf.key
│           │   │   ├── leaf.srl
│           │   │   ├── leaf2.crt
│           │   │   ├── leaf2.csr
│           │   │   ├── leaf2.key
│           │   │   ├── leaf2.srl
│           │   │   ├── public.pem
│           │   │   ├── root.crt
│           │   │   ├── root.key
│           │   │   ├── root.srl
│           │   │   ├── server.crt
│           │   │   ├── server_boringssl_openssl.crt
│           │   │   ├── server_boringssl_openssl.csr
│           │   │   ├── server_rustls.crt
│           │   │   ├── server_s2n.crt
│           │   │   └── v3.ext
│           │   └── origin/
│           │       ├── .gitignore
│           │       ├── conf/
│           │       │   └── nginx.conf
│           │       └── html/
│           │           └── index.html
│           ├── mock_origin.rs
│           ├── mod.rs
│           ├── server_utils.rs
│           └── websocket/
│               ├── mod.rs
│               ├── ws_echo.rs
│               └── ws_echo_raw.rs
├── pingora-runtime/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── hello.rs
│   └── src/
│       └── lib.rs
├── pingora-rustls/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       └── lib.rs
├── pingora-s2n/
│   ├── Cargo.toml
│   ├── LICENSE
│   └── src/
│       └── lib.rs
├── pingora-timeout/
│   ├── Cargo.toml
│   ├── LICENSE
│   ├── benches/
│   │   └── benchmark.rs
│   └── src/
│       ├── fast_timeout.rs
│       ├── lib.rs
│       └── timer.rs
└── tinyufo/
    ├── Cargo.toml
    ├── LICENSE
    ├── README.md
    ├── benches/
    │   ├── bench_hit_ratio.rs
    │   ├── bench_memory.rs
    │   └── bench_perf.rs
    └── src/
        ├── buckets.rs
        ├── estimation.rs
        └── lib.rs
Download .txt
Showing preview only (383K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4519 symbols across 200 files)

FILE: pingora-boringssl/src/boring_tokio.rs
  type StreamWrapper (line 27) | struct StreamWrapper<S> {
  function fmt (line 36) | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
  function parts (line 46) | unsafe fn parts(&mut self) -> (Pin<&mut S>, &mut Context<'_>) {
  method read (line 58) | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
  method write (line 72) | fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
  method flush (line 80) | fn flush(&mut self) -> io::Result<()> {
  function cvt (line 89) | fn cvt<T>(r: io::Result<T>) -> Poll<io::Result<T>> {
  function cvt_ossl (line 97) | fn cvt_ossl<T>(r: Result<T, ssl::Error>) -> Poll<Result<T, ssl::Error>> {
  type SslStream (line 109) | pub struct SslStream<S>(SslStreamCore<StreamWrapper<S>>);
  function new (line 113) | pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> {
  function poll_connect (line 118) | pub fn poll_connect(
  function connect (line 126) | pub async fn connect(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
  function poll_accept (line 131) | pub fn poll_accept(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<R...
  function accept (line 136) | pub async fn accept(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
  function poll_do_handshake (line 141) | pub fn poll_do_handshake(
  function do_handshake (line 149) | pub async fn do_handshake(mut self: Pin<&mut Self>) -> Result<(), ssl::E...
  function ssl (line 158) | pub fn ssl(&self) -> &SslRef {
  function get_ref (line 163) | pub fn get_ref(&self) -> &S {
  function get_mut (line 168) | pub fn get_mut(&mut self) -> &mut S {
  function get_pin_mut (line 173) | pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
  function with_context (line 177) | fn with_context<F, R>(self: Pin<&mut Self>, ctx: &mut Context<'_>, f: F)...
  method poll_read (line 194) | fn poll_read(
  method poll_read (line 220) | fn poll_read(
  method poll_write (line 250) | fn poll_write(self: Pin<&mut Self>, ctx: &mut Context, buf: &[u8]) -> Po...
  method poll_flush (line 254) | fn poll_flush(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io::Resul...
  method poll_shutdown (line 258) | fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<io...
  function test_google (line 275) | async fn test_google() {

FILE: pingora-boringssl/src/ext.rs
  function cvt (line 27) | fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
  function add_host (line 38) | pub fn add_host(verify_param: &mut X509VerifyParamRef, host: &str) -> Re...
  function ssl_set_verify_cert_store (line 55) | pub fn ssl_set_verify_cert_store(
  function ssl_use_certificate (line 71) | pub fn ssl_use_certificate(ssl: &mut SslRef, cert: &X509Ref) -> Result<(...
  function ssl_use_private_key (line 81) | pub fn ssl_use_private_key<T>(ssl: &mut SslRef, key: &PKeyRef<T>) -> Res...
  function ssl_add_chain_cert (line 94) | pub fn ssl_add_chain_cert(ssl: &mut SslRef, cert: &X509Ref) -> Result<()...
  function ssl_set_renegotiate_mode_freely (line 105) | pub fn ssl_set_renegotiate_mode_freely(ssl: &mut SslRef) {
  function ssl_set_groups_list (line 117) | pub fn ssl_set_groups_list(ssl: &mut SslRef, groups: &str) -> Result<(),...
  function ssl_use_second_key_share (line 134) | pub fn ssl_use_second_key_share(ssl: &mut SslRef, enabled: bool) {
  function ssl_use_second_key_share (line 138) | pub fn ssl_use_second_key_share(_ssl: &mut SslRef, _enabled: bool) {}
  function clear_error_stack (line 145) | pub fn clear_error_stack() {
  function ssl_from_acceptor (line 152) | pub fn ssl_from_acceptor(acceptor: &SslAcceptor) -> Result<Ssl, ErrorSta...
  function suspend_when_need_ssl_cert (line 161) | pub fn suspend_when_need_ssl_cert(ssl: &mut SslRef) {
  function unblock_ssl_cert (line 170) | pub fn unblock_ssl_cert(ssl: &mut SslRef) {
  function raw_cert_block (line 177) | extern "C" fn raw_cert_block(_ssl: *mut boring_sys::SSL, _arg: *mut c_vo...
  function is_suspended_for_cert (line 182) | pub fn is_suspended_for_cert(error: &boring::ssl::Error) -> bool {
  function ssl_mut (line 190) | pub unsafe fn ssl_mut(ssl: &SslRef) -> &mut SslRef {

FILE: pingora-cache/benches/lru_memory.rs
  constant ITEMS (line 23) | const ITEMS: usize = 5 * usize::pow(2, 20);
  function main (line 88) | fn main() {

FILE: pingora-cache/benches/lru_serde.rs
  constant ITEMS (line 22) | const ITEMS: usize = 5 * usize::pow(2, 20);
  function main (line 24) | fn main() {

FILE: pingora-cache/benches/simple_lru_memory.rs
  constant ITEMS (line 23) | const ITEMS: usize = 5 * usize::pow(2, 20);
  function main (line 70) | fn main() {

FILE: pingora-cache/src/cache_control.rs
  constant DELTA_SECONDS_OVERFLOW_VALUE (line 46) | pub const DELTA_SECONDS_OVERFLOW_VALUE: u32 = i32::MAX as u32;
  constant DELTA_SECONDS_OVERFLOW_DURATION (line 47) | pub const DELTA_SECONDS_OVERFLOW_DURATION: Duration =
  type DirectiveKey (line 51) | pub type DirectiveKey = String;
  type DirectiveValue (line 55) | pub struct DirectiveValue(pub Vec<u8>);
    method as_ref (line 58) | fn as_ref(&self) -> &[u8] {
    method parse_as_bytes (line 65) | pub fn parse_as_bytes(&self) -> &[u8] {
    method parse_as_str (line 73) | pub fn parse_as_str(&self) -> Result<&str> {
    method parse_as_delta_seconds (line 82) | pub fn parse_as_delta_seconds(&self) -> Result<u32> {
  type DirectiveMap (line 98) | pub type DirectiveMap = IndexMap<DirectiveKey, Option<DirectiveValue>>;
  type CacheControl (line 102) | pub struct CacheControl {
    method from_headers (line 178) | fn from_headers(headers: http::header::GetAll<HeaderValue>) -> Option<...
    method from_headers_named (line 205) | pub fn from_headers_named(header_name: &str, headers: &http::HeaderMap...
    method from_req_headers_named (line 214) | pub fn from_req_headers_named(header_name: &str, req_header: &ReqHeade...
    method from_req_headers (line 219) | pub fn from_req_headers(req_header: &ReqHeader) -> Option<Self> {
    method from_resp_headers_named (line 224) | pub fn from_resp_headers_named(header_name: &str, resp_header: &RespHe...
    method from_resp_headers (line 229) | pub fn from_resp_headers(resp_header: &RespHeader) -> Option<Self> {
    method has_key (line 234) | pub fn has_key(&self, key: &str) -> bool {
    method public (line 239) | pub fn public(&self) -> bool {
    method has_key_without_value (line 244) | fn has_key_without_value(&self, key: &str) -> bool {
    method private (line 254) | pub fn private(&self) -> bool {
    method get_field_names (line 258) | fn get_field_names(&self, key: &str) -> Option<ListValueIter<'_>> {
    method private_field_names (line 264) | pub fn private_field_names(&self) -> Option<ListValueIter<'_>> {
    method no_cache (line 269) | pub fn no_cache(&self) -> bool {
    method no_cache_field_names (line 274) | pub fn no_cache_field_names(&self) -> Option<ListValueIter<'_>> {
    method no_store (line 279) | pub fn no_store(&self) -> bool {
    method parse_delta_seconds (line 283) | fn parse_delta_seconds(&self, key: &str) -> Result<Option<u32>> {
    method max_age (line 292) | pub fn max_age(&self) -> Result<Option<u32>> {
    method s_maxage (line 297) | pub fn s_maxage(&self) -> Result<Option<u32>> {
    method stale_while_revalidate (line 302) | pub fn stale_while_revalidate(&self) -> Result<Option<u32>> {
    method stale_if_error (line 307) | pub fn stale_if_error(&self) -> Result<Option<u32>> {
    method must_revalidate (line 312) | pub fn must_revalidate(&self) -> bool {
    method proxy_revalidate (line 317) | pub fn proxy_revalidate(&self) -> bool {
    method only_if_cached (line 322) | pub fn only_if_cached(&self) -> bool {
  type Cacheable (line 109) | pub enum Cacheable {
  type ListValueIter (line 119) | pub struct ListValueIter<'a>(slice::Split<'a, u8, fn(&u8) -> bool>);
  function from (line 122) | pub fn from(value: &'a DirectiveValue) -> Self {
  function trim_ows (line 129) | fn trim_ows(bytes: &[u8]) -> &[u8] {
  type Item (line 144) | type Item = &'a [u8];
  method next (line 146) | fn next(&mut self) -> Option<Self::Item> {
  type InterpretCacheControl (line 409) | pub trait InterpretCacheControl {
    method is_cacheable (line 328) | fn is_cacheable(&self) -> Cacheable {
    method allow_caching_authorized_req (line 338) | fn allow_caching_authorized_req(&self) -> bool {
    method fresh_duration (line 345) | fn fresh_duration(&self) -> Option<Duration> {
    method serve_stale_while_revalidate_duration (line 359) | fn serve_stale_while_revalidate_duration(&self) -> Option<Duration> {
    method serve_stale_if_error_duration (line 370) | fn serve_stale_if_error_duration(&self) -> Option<Duration> {
    method strip_private_headers (line 380) | fn strip_private_headers(&self, resp_header: &mut ResponseHeader) {
    method is_cacheable (line 415) | fn is_cacheable(&self) -> Cacheable;
    method allow_caching_authorized_req (line 419) | fn allow_caching_authorized_req(&self) -> bool;
    method fresh_duration (line 425) | fn fresh_duration(&self) -> Option<Duration>;
    method serve_stale_while_revalidate_duration (line 435) | fn serve_stale_while_revalidate_duration(&self) -> Option<Duration>;
    method serve_stale_if_error_duration (line 445) | fn serve_stale_if_error_duration(&self) -> Option<Duration>;
    method strip_private_headers (line 449) | fn strip_private_headers(&self, resp_header: &mut ResponseHeader);
  function build_response (line 458) | fn build_response(cc_key: HeaderName, cc_value: &str) -> response::Parts {
  function test_simple_cache_control (line 468) | fn test_simple_cache_control() {
  function test_private_cache_control (line 476) | fn test_private_cache_control() {
  function test_directives_across_header_lines (line 485) | fn test_directives_across_header_lines() {
  function test_recognizes_semicolons_as_delimiters (line 499) | fn test_recognizes_semicolons_as_delimiters() {
  function test_unknown_directives (line 508) | fn test_unknown_directives() {
  function test_case_insensitive_directive_keys (line 529) | fn test_case_insensitive_directive_keys() {
  function test_non_ascii (line 561) | fn test_non_ascii() {
  function test_non_utf8_key (line 585) | fn test_non_utf8_key() {
  function test_non_utf8_value (line 604) | fn test_non_utf8_value() {
  function test_age_overflow (line 637) | fn test_age_overflow() {
  function test_fresh_sec (line 656) | fn test_fresh_sec() {
  function test_cacheability (line 672) | fn test_cacheability() {
  function test_no_cache (line 697) | fn test_no_cache() {
  function test_no_cache_field_names (line 705) | fn test_no_cache_field_names() {
  function test_strip_private_headers (line 746) | fn test_strip_private_headers() {
  function test_stale_while_revalidate (line 761) | fn test_stale_while_revalidate() {
  function test_stale_if_error (line 773) | fn test_stale_if_error() {
  function test_must_revalidate (line 785) | fn test_must_revalidate() {
  function test_proxy_revalidate (line 802) | fn test_proxy_revalidate() {
  function test_s_maxage_stale (line 819) | fn test_s_maxage_stale() {
  function test_authorized_request (line 835) | fn test_authorized_request() {
  function build_request (line 857) | fn build_request(cc_key: HeaderName, cc_value: &str) -> request::Parts {
  function test_request_only_if_cached (line 867) | fn test_request_only_if_cached() {

FILE: pingora-cache/src/eviction/lru.rs
  type Manager (line 40) | pub struct Manager<const N: usize>(Lru<CompactCacheKey, N>);
  type SerdeHelperNode (line 43) | struct SerdeHelperNode(CompactCacheKey, usize);
  function with_capacity (line 49) | pub fn with_capacity(limit: usize, capacity: usize) -> Self {
  function with_capacity_and_watermark (line 57) | pub fn with_capacity_and_watermark(
  function shards (line 66) | pub fn shards(&self) -> usize {
  function shard_weight (line 71) | pub fn shard_weight(&self, shard: usize) -> usize {
  function shard_len (line 76) | pub fn shard_len(&self, shard: usize) -> usize {
  function get_shard_for_key (line 84) | pub fn get_shard_for_key(&self, key: &CompactCacheKey) -> usize {
  function serialize_shard (line 89) | pub fn serialize_shard(&self, shard: usize) -> Result<Vec<u8>> {
  function deserialize_shard (line 117) | pub fn deserialize_shard(&self, buf: &[u8]) -> Result<()> {
  function peek_weight (line 129) | pub fn peek_weight(&self, item: &CompactCacheKey) -> Option<usize> {
  type InsertToManager (line 135) | struct InsertToManager<'a, const N: usize> {
  type Value (line 140) | type Value = ();
  function expecting (line 142) | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Re...
  function visit_seq (line 146) | fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
  function u64key (line 159) | fn u64key(key: &CompactCacheKey) -> u64 {
  constant FILE_NAME (line 166) | const FILE_NAME: &str = "lru.data";
  function err_str_path (line 169) | fn err_str_path(s: &str, path: &Path) -> String {
  method total_size (line 175) | fn total_size(&self) -> usize {
  method total_items (line 178) | fn total_items(&self) -> usize {
  method evicted_size (line 181) | fn evicted_size(&self) -> usize {
  method evicted_items (line 184) | fn evicted_items(&self) -> usize {
  method admit (line 188) | fn admit(
  method increment_weight (line 203) | fn increment_weight(
  method remove (line 218) | fn remove(&self, item: &CompactCacheKey) {
  method access (line 223) | fn access(&self, item: &CompactCacheKey, size: usize, _fresh_until: Syst...
  method peek (line 233) | fn peek(&self, item: &CompactCacheKey) -> bool {
  method save (line 238) | async fn save(&self, dir_path: &str) -> Result<()> {
  method load (line 281) | async fn load(&self, dir_path: &str) -> Result<()> {
  function cleanup_temp_files (line 324) | fn cleanup_temp_files(dir_path: &str) {
  function test_admission (line 391) | fn test_admission() {
  function test_access (line 415) | fn test_access() {
  function test_remove (line 440) | fn test_remove() {
  function test_access_add (line 465) | fn test_access_add() {
  function test_admit_update (line 485) | fn test_admit_update() {
  function test_peek (line 516) | fn test_peek() {
  function test_serde (line 529) | fn test_serde() {
  function test_save_to_disk (line 559) | async fn test_save_to_disk() {
  function test_temp_file_cleanup (line 583) | async fn test_temp_file_cleanup() {

FILE: pingora-cache/src/eviction/mod.rs
  type EvictionManager (line 31) | pub trait EvictionManager: Send + Sync {
    method total_size (line 33) | fn total_size(&self) -> usize;
    method total_items (line 35) | fn total_items(&self) -> usize;
    method evicted_size (line 39) | fn evicted_size(&self) -> usize;
    method evicted_items (line 43) | fn evicted_items(&self) -> usize;
    method admit (line 53) | fn admit(
    method increment_weight (line 69) | fn increment_weight(
    method remove (line 79) | fn remove(&self, item: &CompactCacheKey);
    method access (line 87) | fn access(&self, item: &CompactCacheKey, size: usize, fresh_until: Sys...
    method peek (line 93) | fn peek(&self, item: &CompactCacheKey) -> bool;
    method save (line 101) | async fn save(&self, dir_path: &str) -> Result<()>;
    method load (line 104) | async fn load(&self, dir_path: &str) -> Result<()>;

FILE: pingora-cache/src/eviction/simple_lru.rs
  type Node (line 36) | struct Node {
  type Manager (line 44) | pub struct Manager {
    method new (line 56) | pub fn new(limit: usize) -> Self {
    method new_with_watermark (line 69) | pub fn new_with_watermark(limit: usize, items_watermark: Option<usize>...
    method insert (line 81) | fn insert(&self, hash_key: u64, node: CompactCacheKey, size: usize, re...
    method increase_weight (line 105) | fn increase_weight(&self, key: u64, delta: usize) {
    method over_limits (line 115) | fn over_limits(&self) -> bool {
    method evict (line 123) | fn evict(&self) -> Vec<CompactCacheKey> {
    method serialize (line 151) | fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 169) | fn deserialize(&self, buf: &[u8]) -> Result<()> {
  type InsertToManager (line 180) | struct InsertToManager<'a> {
  type Value (line 185) | type Value = ();
  function expecting (line 187) | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Re...
  function visit_seq (line 191) | fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
  function u64key (line 204) | fn u64key(key: &CompactCacheKey) -> u64 {
  constant FILE_NAME (line 210) | const FILE_NAME: &str = "simple_lru.data";
  method total_size (line 214) | fn total_size(&self) -> usize {
  method total_items (line 217) | fn total_items(&self) -> usize {
  method evicted_size (line 220) | fn evicted_size(&self) -> usize {
  method evicted_items (line 223) | fn evicted_items(&self) -> usize {
  method admit (line 227) | fn admit(
  method increment_weight (line 238) | fn increment_weight(
  method remove (line 249) | fn remove(&self, item: &CompactCacheKey) {
  method access (line 258) | fn access(&self, item: &CompactCacheKey, size: usize, _fresh_until: Syst...
  method peek (line 268) | fn peek(&self, item: &CompactCacheKey) -> bool {
  method save (line 273) | async fn save(&self, dir_path: &str) -> Result<()> {
  method load (line 306) | async fn load(&self, dir_path: &str) -> Result<()> {
  function test_admission (line 330) | fn test_admission() {
  function test_access (line 354) | fn test_access() {
  function test_remove (line 379) | fn test_remove() {
  function test_access_add (line 404) | fn test_access_add() {
  function test_admit_update (line 424) | fn test_admit_update() {
  function test_serde (line 455) | fn test_serde() {
  function test_save_to_disk (line 485) | async fn test_save_to_disk() {
  function test_watermark_eviction (line 515) | fn test_watermark_eviction() {

FILE: pingora-cache/src/filters.rs
  function request_cacheable (line 28) | pub fn request_cacheable(req_header: &ReqHeader) -> bool {
  function resp_cacheable (line 37) | pub fn resp_cacheable(
  function calculate_fresh_until (line 72) | pub fn calculate_fresh_until(
  function calculate_expires_header_time (line 120) | pub fn calculate_expires_header_time(resp_header: &RespHeader) -> Option...
  function calculate_serve_stale_durations (line 145) | pub fn calculate_serve_stale_durations(
  function request_filter (line 180) | pub fn request_filter(req: &mut RequestHeader, meta: Option<&CacheMeta>) {
  function init_log (line 220) | fn init_log() {
  constant DEFAULTS (line 224) | const DEFAULTS: CacheMetaDefaults = CacheMetaDefaults::new(
  constant BYPASS_CACHE_DEFAULTS (line 239) | const BYPASS_CACHE_DEFAULTS: CacheMetaDefaults = CacheMetaDefaults::new(...
  function build_response (line 241) | fn build_response(status: u16, headers: &[(HeaderName, &str)]) -> Respon...
  function resp_cacheable_wrapper (line 249) | fn resp_cacheable_wrapper(
  function test_resp_cacheable (line 267) | fn test_resp_cacheable() {
  function test_resp_uncacheable_directives (line 289) | fn test_resp_uncacheable_directives() {
  function test_resp_cache_authorization (line 306) | fn test_resp_cache_authorization() {
  function test_resp_zero_max_age (line 340) | fn test_resp_zero_max_age() {
  function test_resp_expires (line 352) | fn test_resp_expires() {
  function test_resp_past_expires (line 382) | fn test_resp_past_expires() {
  function test_resp_nonstandard_expires (line 393) | fn test_resp_nonstandard_expires() {
  function test_resp_multiple_expires (line 422) | fn test_resp_multiple_expires() {
  function test_resp_cache_control_with_expires (line 460) | fn test_resp_cache_control_with_expires() {
  function test_resp_stale_while_revalidate (line 480) | fn test_resp_stale_while_revalidate() {
  function test_resp_stale_if_error (line 519) | fn test_resp_stale_if_error() {
  function test_resp_status_cache_defaults (line 575) | fn test_resp_status_cache_defaults() {
  function test_resp_cache_no_cache_fields (line 646) | fn test_resp_cache_no_cache_fields() {

FILE: pingora-cache/src/hashtable.rs
  type ConcurrentHashTable (line 24) | pub struct ConcurrentHashTable<V, const N: usize> {
  function get_shard (line 29) | fn get_shard(key: u128, n_shards: usize) -> usize {
  function new (line 37) | pub fn new() -> Self {
  function get (line 42) | pub fn get(&self, key: u128) -> &RwLock<HashMap<u128, V>> {
  function get_shard_at_idx (line 47) | pub fn get_shard_at_idx(&self, idx: usize) -> Option<&RwLock<HashMap<u12...
  function read (line 52) | pub fn read(&self, key: u128) -> RwLockReadGuard<'_, HashMap<u128, V>> {
  function write (line 56) | pub fn write(&self, key: u128) -> RwLockWriteGuard<'_, HashMap<u128, V>> {
  function for_each (line 61) | pub fn for_each<F>(&self, mut f: F)
  method default (line 80) | fn default() -> Self {
  type LruShard (line 86) | pub struct LruShard<V>(RwLock<LruCache<u128, V>>);
  method default (line 88) | fn default() -> Self {
  type ConcurrentLruCache (line 95) | pub struct ConcurrentLruCache<V, const N: usize> {
  function new (line 103) | pub fn new(shard_capacity: usize) -> Self {
  function get (line 117) | pub fn get(&self, key: u128) -> &RwLock<LruCache<u128, V>> {
  function read (line 122) | pub fn read(&self, key: u128) -> RwLockReadGuard<'_, LruCache<u128, V>> {
  function write (line 126) | pub fn write(&self, key: u128) -> RwLockWriteGuard<'_, LruCache<u128, V>> {

FILE: pingora-cache/src/key.rs
  constant KEY_SIZE (line 23) | const KEY_SIZE: usize = 16;
  type HashBinary (line 26) | pub type HashBinary = [u8; KEY_SIZE];
  function hex2str (line 28) | fn hex2str(hex: &[u8]) -> String {
  function str2hex (line 40) | pub fn str2hex(s: &str) -> Option<HashBinary> {
  type CacheHashKey (line 51) | pub trait CacheHashKey {
    method primary_bin (line 53) | fn primary_bin(&self) -> HashBinary;
    method variance_bin (line 58) | fn variance_bin(&self) -> Option<HashBinary>;
    method combined_bin (line 61) | fn combined_bin(&self) -> HashBinary {
    method user_tag (line 77) | fn user_tag(&self) -> &str;
    method primary (line 80) | fn primary(&self) -> String {
    method variance (line 85) | fn variance(&self) -> Option<String> {
    method combined (line 90) | fn combined(&self) -> String {
    method primary_bin (line 167) | fn primary_bin(&self) -> HashBinary {
    method variance_bin (line 171) | fn variance_bin(&self) -> Option<HashBinary> {
    method user_tag (line 175) | fn user_tag(&self) -> &str {
    method primary_bin (line 256) | fn primary_bin(&self) -> HashBinary {
    method variance_bin (line 264) | fn variance_bin(&self) -> Option<HashBinary> {
    method user_tag (line 268) | fn user_tag(&self) -> &str {
  type CacheKey (line 97) | pub struct CacheKey {
    method set_variance_key (line 116) | pub fn set_variance_key(&mut self, key: HashBinary) {
    method get_variance_key (line 121) | pub fn get_variance_key(&self) -> Option<&HashBinary> {
    method remove_variance_key (line 126) | pub fn remove_variance_key(&mut self) {
    method set_primary_bin_override (line 131) | pub fn set_primary_bin_override(&mut self, key: HashBinary) {
    method primary_key_str (line 136) | pub fn primary_key_str(&self) -> Option<&str> {
    method namespace_str (line 141) | pub fn namespace_str(&self) -> Option<&str> {
    method primary_hasher (line 208) | fn primary_hasher(&self) -> Blake2b128 {
    method new (line 218) | pub fn new<B1, B2, S>(namespace: B1, primary: B2, user_tag: S) -> Self
    method namespace (line 235) | pub fn namespace(&self) -> &[u8] {
    method primary_key (line 240) | pub fn primary_key(&self) -> &[u8] {
    method to_compact (line 245) | pub fn to_compact(&self) -> CompactCacheKey {
  type CompactCacheKey (line 149) | pub struct CompactCacheKey {
  method fmt (line 157) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
  type Blake2b128 (line 189) | pub(crate) type Blake2b128 = Blake2b<blake2::digest::consts::U16>;
  function hash_u8 (line 192) | pub fn hash_u8(key: &str) -> u8 {
  function hash_key (line 200) | pub fn hash_key<K: AsRef<[u8]>>(key: K) -> HashBinary {
  function test_cache_key_hash (line 278) | fn test_cache_key_hash() {
  function test_cache_key_hash_override (line 298) | fn test_cache_key_hash_override() {
  function test_cache_key_vary_hash (line 329) | fn test_cache_key_vary_hash() {
  function test_cache_key_vary_hash_override (line 352) | fn test_cache_key_vary_hash_override() {
  function test_hex_str (line 375) | fn test_hex_str() {
  function test_primary_key_str_valid_utf8 (line 388) | fn test_primary_key_str_valid_utf8() {
  function test_primary_key_str_invalid_utf8 (line 405) | fn test_primary_key_str_invalid_utf8() {

FILE: pingora-cache/src/lib.rs
  type HttpCache (line 62) | pub struct HttpCache {
    method new (line 326) | pub fn new() -> Self {
    method enabled (line 335) | pub fn enabled(&self) -> bool {
    method bypassing (line 340) | pub fn bypassing(&self) -> bool {
    method phase (line 345) | pub fn phase(&self) -> CachePhase {
    method upstream_used (line 352) | pub fn upstream_used(&self) -> bool {
    method storage_type_is (line 362) | pub fn storage_type_is<T: 'static>(&self) -> bool {
    method release_write_lock (line 379) | pub fn release_write_lock(&mut self, reason: NoCacheReason) {
    method disable (line 417) | pub fn disable(&mut self, reason: NoCacheReason) {
    method bypass (line 462) | pub fn bypass(&mut self) {
    method enable (line 485) | pub fn enable(
    method set_cache_lock (line 529) | pub fn set_cache_lock(
    method enable_tracing (line 560) | pub fn enable_tracing(&mut self, parent_span: trace::Span) {
    method get_cache_span (line 567) | pub fn get_cache_span(&self) -> Option<trace::SpanHandle> {
    method get_miss_span (line 574) | pub fn get_miss_span(&self) -> Option<trace::SpanHandle> {
    method get_hit_span (line 581) | pub fn get_hit_span(&self) -> Option<trace::SpanHandle> {
    method inner_enabled_mut (line 589) | fn inner_enabled_mut(&mut self) -> &mut HttpCacheInnerEnabled {
    method inner_enabled (line 594) | fn inner_enabled(&self) -> &HttpCacheInnerEnabled {
    method inner_mut (line 600) | fn inner_mut(&mut self) -> &mut HttpCacheInner {
    method inner (line 605) | fn inner(&self) -> &HttpCacheInner {
    method set_cache_key (line 612) | pub fn set_cache_key(&mut self, key: CacheKey) {
    method cache_key (line 625) | pub fn cache_key(&self) -> &CacheKey {
    method max_file_size_bytes (line 639) | pub fn max_file_size_bytes(&self) -> Option<usize> {
    method set_max_file_size_bytes (line 658) | pub fn set_max_file_size_bytes(&mut self, max_file_size_bytes: usize) {
    method track_body_bytes_for_max_file_size (line 677) | pub fn track_body_bytes_for_max_file_size(&mut self, bytes_len: usize)...
    method exceeded_max_file_size (line 696) | pub fn exceeded_max_file_size(&self) -> bool {
    method cache_found (line 716) | pub fn cache_found(&mut self, meta: CacheMeta, hit_handler: HitHandler...
    method cache_miss (line 773) | pub fn cache_miss(&mut self) {
    method hit_handler (line 794) | pub fn hit_handler(&mut self) -> &mut HitHandler {
    method miss_body_reader (line 809) | pub fn miss_body_reader(&mut self) -> Option<&mut HitHandler> {
    method support_streaming_partial_write (line 828) | pub fn support_streaming_partial_write(&self) -> Option<bool> {
    method finish_hit_handler (line 842) | pub async fn finish_hit_handler(&mut self) -> Result<()> {
    method set_miss_handler (line 874) | pub async fn set_miss_handler(&mut self) -> Result<()> {
    method miss_handler (line 935) | pub fn miss_handler(&mut self) -> Option<&mut MissHandler> {
    method finish_miss_handler (line 950) | pub async fn finish_miss_handler(&mut self) -> Result<()> {
    method set_cache_meta (line 1008) | pub fn set_cache_meta(&mut self, meta: CacheMeta) {
    method revalidate_cache_meta (line 1028) | pub async fn revalidate_cache_meta(&mut self, mut meta: CacheMeta) -> ...
    method revalidate_merge_header (line 1085) | pub fn revalidate_merge_header(&mut self, resp: &RespHeader) -> Respon...
    method revalidate_uncacheable (line 1130) | pub fn revalidate_uncacheable(&mut self, header: ResponseHeader, reaso...
    method set_stale_updating (line 1145) | pub fn set_stale_updating(&mut self) {
    method update_variance (line 1157) | pub fn update_variance(&mut self, variance: Option<HashBinary>) {
    method cache_meta (line 1222) | pub fn cache_meta(&self) -> &CacheMeta {
    method maybe_cache_meta (line 1251) | pub fn maybe_cache_meta(&self) -> Option<&CacheMeta> {
    method maybe_cache_key (line 1268) | pub fn maybe_cache_key(&self) -> Option<&CacheKey> {
    method cache_lookup (line 1282) | pub async fn cache_lookup(&mut self) -> Result<Option<(CacheMeta, HitH...
    method cache_vary_lookup (line 1328) | pub fn cache_vary_lookup(&mut self, variance: HashBinary, meta: &Cache...
    method is_cache_locked (line 1371) | pub fn is_cache_locked(&self) -> bool {
    method is_cache_lock_writer (line 1383) | pub fn is_cache_lock_writer(&self) -> bool {
    method take_write_lock (line 1396) | pub fn take_write_lock(&mut self) -> (WritePermit, &'static CacheKeyLo...
    method set_write_lock (line 1420) | pub fn set_write_lock(&mut self, write_lock: WritePermit) {
    method has_staled_asset (line 1427) | fn has_staled_asset(&self) -> bool {
    method can_serve_stale_error (line 1432) | pub fn can_serve_stale_error(&self) -> bool {
    method can_serve_stale_updating (line 1437) | pub fn can_serve_stale_updating(&self) -> bool {
    method cache_lock_wait (line 1447) | pub async fn cache_lock_wait(&mut self) -> LockStatus {
    method lock_duration (line 1486) | pub fn lock_duration(&self) -> Option<Duration> {
    method lookup_duration (line 1491) | pub fn lookup_duration(&self) -> Option<Duration> {
    method purge (line 1498) | pub async fn purge(&self) -> Result<bool> {
    method spawn_async_purge (line 1515) | pub fn spawn_async_purge(
    method purge_impl (line 1538) | async fn purge_impl(
    method cacheable_prediction (line 1561) | pub fn cacheable_prediction(&self) -> bool {
    method response_became_cacheable (line 1571) | pub fn response_became_cacheable(&self) {
    method response_became_uncacheable (line 1579) | pub fn response_became_uncacheable(&self, reason: NoCacheReason) {
    method tag_as_subrequest (line 1586) | pub fn tag_as_subrequest(&mut self) {
  type CachePhase (line 71) | pub enum CachePhase {
    method as_str (line 99) | pub fn as_str(&self) -> &'static str {
  type NoCacheReason (line 118) | pub enum NoCacheReason {
    method as_str (line 154) | pub fn as_str(&self) -> &'static str {
  type HttpCacheDigest (line 175) | pub struct HttpCacheDigest {
    method add_lookup_duration (line 187) | fn add_lookup_duration(&mut self, extra_lookup_duration: Duration) {
    method add_lock_duration (line 191) | fn add_lock_duration(&mut self, extra_lock_duration: Duration) {
  function add_duration_to_opt (line 182) | fn add_duration_to_opt(target_opt: &mut Option<Duration>, to_add: Durati...
  type RespCacheable (line 200) | pub enum RespCacheable {
    method is_cacheable (line 208) | pub fn is_cacheable(&self) -> bool {
    method unwrap_meta (line 215) | pub fn unwrap_meta(self) -> CacheMeta {
  type ForcedFreshness (line 227) | pub enum ForcedFreshness {
  type HitStatus (line 244) | pub enum HitStatus {
    method as_str (line 267) | pub fn as_str(&self) -> &'static str {
    method is_fresh (line 272) | pub fn is_fresh(&self) -> bool {
    method is_treated_as_miss (line 281) | pub fn is_treated_as_miss(self) -> bool {
  type LockCtx (line 286) | pub struct LockCtx {
  type HttpCacheInnerEnabled (line 293) | struct HttpCacheInnerEnabled {
  type HttpCacheInner (line 305) | struct HttpCacheInner {
  type CacheOptionOverrides (line 318) | pub struct CacheOptionOverrides {

FILE: pingora-cache/src/lock.rs
  type CacheKeyLockImpl (line 25) | pub type CacheKeyLockImpl = dyn CacheKeyLock + Send + Sync;
  type CacheKeyLock (line 27) | pub trait CacheKeyLock {
    method lock (line 35) | fn lock(&self, key: &CacheKey, stale_writer: bool) -> Locked;
    method release (line 41) | fn release(&self, key: &CacheKey, permit: WritePermit, reason: LockSta...
    method trace_lock_wait (line 44) | fn trace_lock_wait(&self, span: &mut Span, _read_lock: &ReadLock, lock...
    method custom_lock_status (line 50) | fn custom_lock_status(&self, _custom_no_cache: &'static str) -> LockSt...
    method lock (line 110) | fn lock(&self, key: &CacheKey, stale_writer: bool) -> Locked {
    method release (line 148) | fn release(&self, key: &CacheKey, mut permit: WritePermit, reason: Loc...
  constant N_SHARDS (line 57) | const N_SHARDS: usize = 16;
  type CacheLock (line 61) | pub struct CacheLock {
    method new_boxed (line 89) | pub fn new_boxed(age_timeout: Duration) -> Box<Self> {
    method new (line 101) | pub fn new(age_timeout_default: Duration) -> Self {
  type Locked (line 69) | pub enum Locked {
    method is_write (line 78) | pub fn is_write(&self) -> bool {
  type LockStatus (line 175) | pub enum LockStatus {
    method from (line 208) | fn from(v: u8) -> Self {
  function from (line 194) | fn from(l: LockStatus) -> u8 {
  type LockCore (line 214) | pub struct LockCore {
    method new_arc (line 225) | pub fn new_arc(timeout: Duration, stale_writer: bool, extensions: Exte...
    method locked (line 236) | pub fn locked(&self) -> bool {
    method unlock (line 240) | pub fn unlock(&self, reason: LockStatus) {
    method lock_status (line 251) | pub fn lock_status(&self) -> LockStatus {
    method stale_writer (line 256) | pub fn stale_writer(&self) -> bool {
    method extensions (line 260) | pub fn extensions(&self) -> &Extensions {
  type ReadLock (line 269) | pub struct ReadLock(Arc<LockCore>);
    method wait (line 273) | pub async fn wait(&self) {
    method locked (line 305) | pub fn locked(&self) -> bool {
    method expired (line 310) | pub fn expired(&self) -> bool {
    method lock_status (line 317) | pub fn lock_status(&self) -> LockStatus {
    method extensions (line 326) | pub fn extensions(&self) -> &Extensions {
  type WritePermit (line 333) | pub struct WritePermit {
    method new (line 340) | pub fn new(
    method stale_writer (line 357) | pub fn stale_writer(&self) -> bool {
    method unlock (line 361) | pub fn unlock(&mut self, reason: LockStatus) {
    method lock_status (line 366) | pub fn lock_status(&self) -> LockStatus {
    method extensions (line 370) | pub fn extensions(&self) -> &Extensions {
  method drop (line 376) | fn drop(&mut self) {
  type LockStub (line 386) | pub struct LockStub(pub Arc<LockCore>);
    method read_lock (line 388) | pub fn read_lock(&self) -> ReadLock {
    method extensions (line 392) | pub fn extensions(&self) -> &Extensions {
  function test_get_release (line 403) | fn test_get_release() {
  function test_lock (line 421) | async fn test_lock() {
  function test_lock_timeout (line 442) | async fn test_lock_timeout() {
  function test_lock_expired_release (line 487) | async fn test_lock_expired_release() {
  function test_lock_expired_no_reader (line 535) | async fn test_lock_expired_no_reader() {
  function test_lock_concurrent (line 559) | async fn test_lock_concurrent() {

FILE: pingora-cache/src/max_file_size.rs
  constant ERR_RESPONSE_TOO_LARGE (line 20) | pub const ERR_RESPONSE_TOO_LARGE: ErrorType = ErrorType::Custom("respons...
  type MaxFileSizeTracker (line 25) | pub(crate) struct MaxFileSizeTracker {
    method new (line 32) | pub fn new(max_size: usize) -> MaxFileSizeTracker {
    method add_body_bytes (line 41) | pub fn add_body_bytes(&mut self, bytes: usize) -> bool {
    method max_file_size_bytes (line 46) | pub fn max_file_size_bytes(&self) -> usize {
    method allow_caching (line 50) | pub fn allow_caching(&self) -> bool {

FILE: pingora-cache/src/memory.rs
  type BinaryMeta (line 36) | type BinaryMeta = (Vec<u8>, Vec<u8>);
  type CacheObject (line 38) | pub(crate) struct CacheObject {
  type TempObject (line 43) | pub(crate) struct TempObject {
    method new (line 51) | fn new(meta: BinaryMeta) -> Self {
    method make_cache_object (line 60) | fn make_cache_object(&self) -> CacheObject {
  type MemCache (line 70) | pub struct MemCache {
    method new (line 78) | pub fn new() -> Self {
  type MemHitHandler (line 87) | pub enum MemHitHandler {
  type PartialState (line 93) | enum PartialState {
  type CompleteHit (line 98) | pub struct CompleteHit {
    method get (line 106) | fn get(&mut self) -> Option<Bytes> {
    method seek (line 117) | fn seek(&mut self, start: usize, end: Option<usize>) -> Result<()> {
  type PartialHit (line 135) | pub struct PartialHit {
    method read (line 142) | async fn read(&mut self) -> Option<Bytes> {
  method read_body (line 177) | async fn read_body(&mut self) -> Result<Option<Bytes>> {
  method finish (line 183) | async fn finish(
  method can_seek (line 192) | fn can_seek(&self) -> bool {
  method seek (line 199) | fn seek(&mut self, start: usize, end: Option<usize>) -> Result<()> {
  method should_count_access (line 209) | fn should_count_access(&self) -> bool {
  method get_eviction_weight (line 217) | fn get_eviction_weight(&self) -> usize {
  method as_any (line 226) | fn as_any(&self) -> &(dyn Any + Send + Sync) {
  method as_any_mut (line 230) | fn as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync) {
  type MemMissHandler (line 235) | pub struct MemMissHandler {
  method write_body (line 249) | async fn write_body(&mut self, data: bytes::Bytes, eof: bool) -> Result<...
  method finish (line 265) | async fn finish(self: Box<Self>) -> Result<MissFinishType> {
  method streaming_write_tag (line 284) | fn streaming_write_tag(&self) -> Option<&[u8]> {
  method drop (line 290) | fn drop(&mut self) {
  function hit_from_temp_obj (line 298) | fn hit_from_temp_obj(temp_obj: &TempObject) -> Result<Option<(CacheMeta,...
  method lookup (line 311) | async fn lookup(
  method lookup_streaming_write (line 342) | async fn lookup_streaming_write(
  method get_miss_handler (line 362) | async fn get_miss_handler(
  method purge (line 388) | async fn purge(
  method update_meta (line 402) | async fn update_meta(
  method support_streaming_partial_write (line 417) | fn support_streaming_partial_write(&self) -> bool {
  method as_any (line 421) | fn as_any(&self) -> &(dyn Any + Send + Sync) {
  function gen_meta (line 432) | fn gen_meta() -> CacheMeta {
  function test_write_then_read (line 447) | async fn test_write_then_read() {
  function test_read_range (line 484) | async fn test_read_range() {
  function test_write_while_read (line 527) | async fn test_write_while_read() {
  function test_purge_partial (line 596) | async fn test_purge_partial() {
  function test_purge_complete (line 623) | async fn test_purge_complete() {

FILE: pingora-cache/src/meta.rs
  type InternalMeta (line 29) | pub(crate) type InternalMeta = internal_meta::InternalMetaLatest;
  type InternalMetaLatest (line 33) | pub(crate) type InternalMetaLatest = InternalMetaV2;
  type InternalMetaV0 (line 36) | pub(crate) struct InternalMetaV0 {
    method serialize (line 46) | fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 50) | fn deserialize(buf: &[u8]) -> Result<Self> {
  type InternalMetaV1 (line 57) | pub(crate) struct InternalMetaV1 {
    constant VERSION (line 68) | pub const VERSION: u8 = 1;
    method serialize (line 71) | pub fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 76) | fn deserialize(buf: &[u8]) -> Result<Self> {
  type InternalMetaV2 (line 83) | pub(crate) struct InternalMetaV2 {
    constant VERSION (line 119) | pub const VERSION: u8 = 2;
    method serialize (line 121) | pub fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 126) | fn deserialize(buf: &[u8]) -> Result<Self> {
    method from (line 133) | fn from(v0: InternalMetaV0) -> Self {
    method from (line 147) | fn from(v1: InternalMetaV1) -> Self {
  method default (line 103) | fn default() -> Self {
  function deserialize (line 161) | pub(crate) fn deserialize(buf: &[u8]) -> Result<InternalMetaLatest> {
  function test_internal_meta_serde_v0 (line 199) | fn test_internal_meta_serde_v0() {
  function test_internal_meta_serde_v1 (line 212) | fn test_internal_meta_serde_v1() {
  function test_internal_meta_serde_v2 (line 226) | fn test_internal_meta_serde_v2() {
  function test_internal_meta_serde_across_versions (line 237) | fn test_internal_meta_serde_across_versions() {
  type InternalMetaV2Base (line 267) | struct InternalMetaV2Base {
    constant VERSION (line 277) | pub const VERSION: u8 = 2;
    method serialize (line 278) | pub fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 282) | fn deserialize(buf: &[u8]) -> Result<Self> {
  type InternalMetaV2BaseWithVariance (line 290) | struct InternalMetaV2BaseWithVariance {
    constant VERSION (line 318) | pub const VERSION: u8 = 2;
    method serialize (line 319) | pub fn serialize(&self) -> Result<Vec<u8>> {
    method deserialize (line 323) | fn deserialize(buf: &[u8]) -> Result<Self> {
  method default (line 303) | fn default() -> Self {
  function test_internal_meta_serde_v2_extend_fields_variance (line 330) | fn test_internal_meta_serde_v2_extend_fields_variance() {
  function test_internal_meta_serde_v2_extend_fields_epoch_override (line 359) | fn test_internal_meta_serde_v2_extend_fields_epoch_override() {
  type CacheMetaInner (line 412) | pub(crate) struct CacheMetaInner {
  type CacheMeta (line 423) | pub struct CacheMeta(pub(crate) Box<CacheMetaInner>);
    method new (line 427) | pub fn new(
    method created (line 450) | pub fn created(&self) -> SystemTime {
    method updated (line 457) | pub fn updated(&self) -> SystemTime {
    method epoch (line 465) | pub fn epoch(&self) -> SystemTime {
    method epoch_override (line 470) | pub fn epoch_override(&self) -> Option<SystemTime> {
    method set_epoch_override (line 478) | pub fn set_epoch_override(&mut self, epoch: SystemTime) {
    method remove_epoch_override (line 483) | pub fn remove_epoch_override(&mut self) {
    method is_fresh (line 488) | pub fn is_fresh(&self, time: SystemTime) -> bool {
    method fresh_sec (line 497) | pub fn fresh_sec(&self) -> u64 {
    method fresh_until (line 510) | pub fn fresh_until(&self) -> SystemTime {
    method age (line 517) | pub fn age(&self) -> Duration {
    method stale_while_revalidate_sec (line 525) | pub fn stale_while_revalidate_sec(&self) -> u32 {
    method stale_if_error_sec (line 530) | pub fn stale_if_error_sec(&self) -> u32 {
    method serve_stale_while_revalidate (line 538) | pub fn serve_stale_while_revalidate(&self, time: SystemTime) -> bool {
    method serve_stale_if_error (line 546) | pub fn serve_stale_if_error(&self, time: SystemTime) -> bool {
    method disable_serve_stale (line 551) | pub fn disable_serve_stale(&mut self) {
    method variance (line 557) | pub fn variance(&self) -> Option<HashBinary> {
    method set_variance_key (line 562) | pub fn set_variance_key(&mut self, variance_key: HashBinary) {
    method set_variance (line 567) | pub fn set_variance(&mut self, variance: HashBinary) {
    method remove_variance (line 572) | pub fn remove_variance(&mut self) {
    method response_header (line 577) | pub fn response_header(&self) -> &ResponseHeader {
    method response_header_mut (line 582) | pub fn response_header_mut(&mut self) -> &mut ResponseHeader {
    method extensions (line 587) | pub fn extensions(&self) -> &Extensions {
    method extensions_mut (line 592) | pub fn extensions_mut(&mut self) -> &mut Extensions {
    method response_header_copy (line 597) | pub fn response_header_copy(&self) -> ResponseHeader {
    method headers (line 602) | pub fn headers(&self) -> &HMap {
    method can_serve_stale (line 606) | fn can_serve_stale(&self, serve_stale_sec: u32, time: SystemTime) -> b...
    method serialize (line 624) | pub fn serialize(&self) -> Result<(Vec<u8>, Vec<u8>)> {
    method deserialize (line 632) | pub fn deserialize(internal: &[u8], header: &[u8]) -> Result<Self> {
  type FreshDurationByStatusFn (line 646) | pub type FreshDurationByStatusFn = fn(StatusCode) -> Option<Duration>;
  type CacheMetaDefaults (line 649) | pub struct CacheMetaDefaults {
    method new (line 659) | pub const fn new(
    method fresh_sec (line 674) | pub fn fresh_sec(&self, resp_status: StatusCode) -> Option<Duration> {
    method serve_stale_while_revalidate_sec (line 684) | pub fn serve_stale_while_revalidate_sec(&self) -> u32 {
    method serve_stale_if_error_sec (line 689) | pub fn serve_stale_if_error_sec(&self) -> u32 {
  function header_serialize (line 710) | pub(crate) fn header_serialize(header: &ResponseHeader) -> Result<Vec<u8...
  function header_deserialize (line 714) | pub(crate) fn header_deserialize<T: AsRef<[u8]>>(buf: T) -> Result<Respo...
  function set_compression_dict_path (line 723) | pub fn set_compression_dict_path(path: &str) -> bool {
  function set_compression_dict_content (line 742) | pub fn set_compression_dict_content(content: Cow<'static, [u8]>) -> bool {
  function test_cache_meta_age_without_override (line 752) | fn test_cache_meta_age_without_override() {
  function test_cache_meta_age_with_epoch_override_past (line 767) | fn test_cache_meta_age_with_epoch_override_past() {
  function test_cache_meta_age_with_epoch_override_future (line 787) | fn test_cache_meta_age_with_epoch_override_future() {
  function test_cache_meta_fresh_sec (line 802) | fn test_cache_meta_fresh_sec() {

FILE: pingora-cache/src/predictor.rs
  type CustomReasonPredicate (line 19) | pub type CustomReasonPredicate = fn(&'static str) -> bool;
  type Predictor (line 30) | pub struct Predictor<const N_SHARDS: usize> {
  type CacheablePredictor (line 41) | pub trait CacheablePredictor {
    method cacheable_prediction (line 43) | fn cacheable_prediction(&self, key: &CacheKey) -> bool;
    method mark_cacheable (line 47) | fn mark_cacheable(&self, key: &CacheKey) -> bool;
    method mark_uncacheable (line 53) | fn mark_uncacheable(&self, key: &CacheKey, reason: NoCacheReason) -> O...
    method cacheable_prediction (line 87) | fn cacheable_prediction(&self, key: &CacheKey) -> bool {
    method mark_cacheable (line 97) | fn mark_cacheable(&self, key: &CacheKey) -> bool {
    method mark_uncacheable (line 115) | fn mark_uncacheable(&self, key: &CacheKey, reason: NoCacheReason) -> O...
  function new (line 72) | pub fn new(
  function test_mark_cacheability (line 160) | fn test_mark_cacheability() {
  function test_custom_skip_predicate (line 182) | fn test_custom_skip_predicate() {
  function test_mark_uncacheable_lru (line 207) | fn test_mark_uncacheable_lru() {

FILE: pingora-cache/src/put.rs
  type CachePut (line 28) | pub trait CachePut {
    method cacheable (line 30) | fn cacheable(&self, response: ResponseHeader) -> RespCacheable {
    method cache_defaults (line 36) | fn cache_defaults() -> &'static CacheMetaDefaults;
    method trace_header (line 39) | fn trace_header(&mut self, _response: &ResponseHeader) {}
    method cache_defaults (line 247) | fn cache_defaults() -> &'static CacheMetaDefaults {
  type CachePutCtx (line 45) | pub struct CachePutCtx<C: CachePut> {
  function new (line 61) | pub fn new(
  function set_max_file_size_bytes (line 82) | pub fn set_max_file_size_bytes(&mut self, max_file_size_bytes: usize) {
  function put_header (line 86) | async fn put_header(&mut self, meta: CacheMeta) -> Result<()> {
  function put_body (line 98) | async fn put_body(&mut self, data: Bytes, eof: bool) -> Result<()> {
  function finish (line 118) | async fn finish(&mut self) -> Result<()> {
  function trace_header (line 153) | fn trace_header(&mut self, header: &ResponseHeader) {
  function do_cache_put (line 168) | async fn do_cache_put(&mut self, data: &[u8]) -> Result<Option<NoCacheRe...
  function cache_put (line 214) | pub async fn cache_put(
  type TestCachePut (line 245) | struct TestCachePut();
  type TestCachePutCtx (line 254) | type TestCachePutCtx = CachePutCtx<TestCachePut>;
  function test_cache_put (line 258) | async fn test_cache_put() {
  function test_cache_put_uncacheable (line 292) | async fn test_cache_put_uncacheable() {
  function test_cache_put_204_invalid_body (line 313) | async fn test_cache_put_204_invalid_body() {
  function test_cache_put_extra_body (line 353) | async fn test_cache_put_extra_body() {
  constant INCOMPLETE_BODY (line 405) | pub const INCOMPLETE_BODY: ErrorType = ErrorType::new("IncompleteHttpBod...
  constant MAX_HEADERS (line 407) | const MAX_HEADERS: usize = 256;
  constant INIT_HEADER_BUF_SIZE (line 408) | const INIT_HEADER_BUF_SIZE: usize = 4096;
  type ParseState (line 411) | enum ParseState {
    method is_done (line 421) | fn is_done(&self) -> bool {
    method read_header (line 424) | fn read_header(&self) -> bool {
    method read_body (line 427) | fn read_body(&self) -> bool {
  type ResponseParse (line 435) | pub(super) struct ResponseParse {
    method new (line 442) | pub fn new() -> Self {
    method inject_data (line 450) | pub fn inject_data(&mut self, data: &[u8]) -> Result<Vec<HttpTask>> {
    method put_data (line 482) | fn put_data(&mut self, data: &[u8]) {
    method parse_header (line 490) | fn parse_header(&mut self) -> Result<Option<ResponseHeader>> {
    method parse_body (line 533) | fn parse_body(&mut self) -> Result<Option<Bytes>> {
    method finish (line 565) | pub fn finish(&mut self) -> Result<()> {
  function body_type (line 577) | fn body_type(resp: &ResponseHeader) -> ParseState {
  function test_basic_response (line 611) | fn test_basic_response() {
  function test_partial_response_headers (line 633) | fn test_partial_response_headers() {
  function test_invalid_headers (line 653) | fn test_invalid_headers() {
  function test_body_content_length (line 666) | fn test_body_content_length() {
  function test_body_chunked (line 695) | fn test_body_chunked() {
  function test_body_content_length_early (line 716) | fn test_body_content_length_early() {
  function test_body_content_length_more_data (line 737) | fn test_body_content_length_more_data() {
  function test_body_chunked_partial_chunk (line 759) | fn test_body_chunked_partial_chunk() {
  function test_no_body_content_length (line 786) | fn test_no_body_content_length() {
  function test_no_body_304_no_content_length (line 802) | fn test_no_body_304_no_content_length() {
  function test_204_with_chunked_body (line 818) | fn test_204_with_chunked_body() {
  function test_204_with_content_length (line 837) | fn test_204_with_content_length() {
  function test_200_with_zero_content_length_more_data (line 856) | fn test_200_with_zero_content_length_more_data() {

FILE: pingora-cache/src/storage.rs
  type PurgeType (line 27) | pub enum PurgeType {
  type Storage (line 36) | pub trait Storage {
    method lookup (line 40) | async fn lookup(
    method lookup_streaming_write (line 58) | async fn lookup_streaming_write(
    method get_miss_handler (line 68) | async fn get_miss_handler(
    method purge (line 78) | async fn purge(
    method update_meta (line 86) | async fn update_meta(
    method support_streaming_partial_write (line 96) | fn support_streaming_partial_write(&self) -> bool {
    method as_any (line 101) | fn as_any(&self) -> &(dyn Any + Send + Sync + 'static);
  type HandleHit (line 106) | pub trait HandleHit {
    method read_body (line 110) | async fn read_body(&mut self) -> Result<Option<bytes::Bytes>>;
    method finish (line 113) | async fn finish(
    method can_seek (line 121) | fn can_seek(&self) -> bool {
    method can_seek_multipart (line 128) | fn can_seek_multipart(&self) -> bool {
    method seek (line 135) | fn seek(&mut self, _start: usize, _end: Option<usize>) -> Result<()> {
    method seek_multipart (line 148) | fn seek_multipart(&mut self, start: usize, end: Option<usize>) -> Resu...
    method should_count_access (line 159) | fn should_count_access(&self) -> bool {
    method get_eviction_weight (line 169) | fn get_eviction_weight(&self) -> usize {
    method as_any (line 174) | fn as_any(&self) -> &(dyn Any + Send + Sync);
    method as_any_mut (line 177) | fn as_any_mut(&mut self) -> &mut (dyn Any + Send + Sync);
  type HitHandler (line 181) | pub type HitHandler = Box<dyn HandleHit + Sync + Send>;
  type MissFinishType (line 184) | pub enum MissFinishType {
  type HandleMiss (line 193) | pub trait HandleMiss {
    method write_body (line 195) | async fn write_body(&mut self, data: bytes::Bytes, eof: bool) -> Resul...
    method finish (line 201) | async fn finish(
    method streaming_write_tag (line 217) | fn streaming_write_tag(&self) -> Option<&[u8]> {
  type MissHandler (line 223) | pub type MissHandler = Box<dyn HandleMiss + Sync + Send>;
  type U64WriteId (line 231) | pub struct U64WriteId([u8; 8]);
    method as_bytes (line 234) | pub fn as_bytes(&self) -> &[u8] {
    method from (line 240) | fn from(value: u64) -> U64WriteId {
    type Error (line 250) | type Error = std::array::TryFromSliceError;
    method try_from (line 252) | fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
  function from (line 245) | fn from(value: U64WriteId) -> u64 {
  type U32WriteId (line 262) | pub struct U32WriteId([u8; 4]);
    method as_bytes (line 265) | pub fn as_bytes(&self) -> &[u8] {
    method from (line 271) | fn from(value: u32) -> U32WriteId {
    type Error (line 281) | type Error = std::array::TryFromSliceError;
    method try_from (line 283) | fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
  function from (line 276) | fn from(value: U32WriteId) -> u32 {

FILE: pingora-cache/src/trace.rs
  type Span (line 24) | pub type Span = cf_rustracing::span::Span<SpanContextState>;
  type SpanHandle (line 25) | pub type SpanHandle = cf_rustracing::span::SpanHandle<SpanContextState>;
  type CacheTraceCTX (line 28) | pub(crate) struct CacheTraceCTX {
    method new (line 59) | pub fn new() -> Self {
    method enable (line 67) | pub fn enable(&mut self, cache_span: Span) {
    method get_cache_span (line 71) | pub fn get_cache_span(&self) -> SpanHandle {
    method child (line 76) | pub fn child(&self, name: &'static str) -> Span {
    method start_miss_span (line 80) | pub fn start_miss_span(&mut self) {
    method get_miss_span (line 84) | pub fn get_miss_span(&self) -> SpanHandle {
    method finish_miss_span (line 88) | pub fn finish_miss_span(&mut self) {
    method start_hit_span (line 92) | pub fn start_hit_span(&mut self, phase: CachePhase, hit_status: HitSta...
    method get_hit_span (line 99) | pub fn get_hit_span(&self) -> SpanHandle {
    method finish_hit_span (line 103) | pub fn finish_hit_span(&mut self) {
    method log_meta_in_hit_span (line 107) | pub fn log_meta_in_hit_span(&mut self, meta: &CacheMeta) {
    method log_meta_in_miss_span (line 111) | pub fn log_meta_in_miss_span(&mut self, meta: &CacheMeta) {
  function tag_span_with_meta (line 36) | pub fn tag_span_with_meta(span: &mut Span, meta: &CacheMeta) {

FILE: pingora-cache/src/variance.rs
  type VarianceBuilder (line 10) | pub struct VarianceBuilder<'a> {
  function new (line 17) | pub fn new() -> Self {
  function add_value (line 25) | pub fn add_value(&mut self, name: &'a str, value: &'a (impl AsRef<[u8]> ...
  function add_owned_value (line 34) | pub fn add_owned_value(&mut self, name: &'a str, value: Vec<u8>) {
  function has_variance (line 39) | pub fn has_variance(&self) -> bool {
  function finalize (line 44) | pub fn finalize(self) -> Option<HashBinary> {
  function test_basic (line 66) | fn test_basic() {
  function test_value_ordering (line 84) | fn test_value_ordering() {
  function test_value_overriding (line 108) | fn test_value_overriding() {

FILE: pingora-core/examples/bootstrap_as_a_service.rs
  type MyService (line 53) | pub struct MyService;
  method start_service (line 57) | async fn start_service(
  method name (line 71) | fn name(&self) -> &str {
  method threads (line 75) | fn threads(&self) -> Option<usize> {
  function main (line 80) | fn main() {

FILE: pingora-core/examples/client_cert.rs
  type MyTlsInfo (line 42) | struct MyTlsInfo {
  type MyApp (line 51) | struct MyApp;
  method response (line 55) | async fn response(&self, session: &mut ServerSession) -> http::Response<...
  type MyTlsCallbacks (line 89) | struct MyTlsCallbacks;
  method handshake_complete_callback (line 94) | async fn handshake_complete_callback(
  function san_to_string (line 132) | fn san_to_string(san: &GeneralName) -> Option<String> {
  function bytes_to_ip_addr (line 149) | fn bytes_to_ip_addr(bytes: &[u8]) -> Option<IpAddr> {
  function main (line 189) | fn main() -> Result<(), Box<dyn std::error::Error>> {
  function main (line 225) | fn main() {

FILE: pingora-core/examples/service_dependencies.rs
  type DatabaseService (line 57) | pub struct DatabaseService {
    method new (line 62) | fn new() -> Self {
    method get_connection_string (line 68) | fn get_connection_string(&self) -> Arc<Mutex<Option<String>>> {
  method start_service (line 75) | async fn start_service(
  method name (line 103) | fn name(&self) -> &str {
  method threads (line 107) | fn threads(&self) -> Option<usize> {
  type CacheService (line 114) | pub struct CacheService;
  method start_service (line 120) | async fn start_service(
  method name (line 137) | fn name(&self) -> &str {
  method threads (line 141) | fn threads(&self) -> Option<usize> {
  type ApiService (line 148) | pub struct ApiService {
    method new (line 153) | fn new(db_connection: Arc<Mutex<Option<String>>>) -> Self {
  method start_service (line 162) | async fn start_service(
  method name (line 187) | fn name(&self) -> &str {
  method threads (line 191) | fn threads(&self) -> Option<usize> {
  function main (line 196) | fn main() {

FILE: pingora-core/src/apps/http_app.rs
  type ServeHttp (line 32) | pub trait ServeHttp {
    method response (line 41) | async fn response(&self, http_session: &mut ServerSession) -> Response...
  method process_new_http (line 50) | async fn process_new_http(
  type HttpServer (line 112) | pub struct HttpServer<SV> {
  function new_app (line 121) | pub fn new_app(app: SV) -> Self {
  function add_module (line 131) | pub fn add_module(&mut self, module: ModuleBuilder) {
  method process_new_http (line 141) | async fn process_new_http(
  method h2_options (line 218) | fn h2_options(&self) -> Option<H2Options> {
  method server_options (line 222) | fn server_options(&self) -> Option<&HttpServerOptions> {

FILE: pingora-core/src/apps/mod.rs
  constant H2_PREFACE (line 33) | const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
  type ServerApp (line 37) | pub trait ServerApp {
    method process_new (line 50) | async fn process_new(
    method cleanup (line 58) | async fn cleanup(&self) {}
    method process_new (line 185) | async fn process_new(
    method cleanup (line 294) | async fn cleanup(&self) {
  type HttpServerOptions (line 63) | pub struct HttpServerOptions {
  type HttpPersistentSettings (line 88) | pub struct HttpPersistentSettings {
    method for_session (line 94) | pub fn for_session(session: &ServerSession) -> Self {
    method apply_to_session (line 101) | pub fn apply_to_session(self, session: &mut ServerSession) {
  type ReusedHttpStream (line 119) | pub struct ReusedHttpStream {
    method new (line 125) | pub fn new(stream: Stream, persistent_settings: Option<HttpPersistentS...
    method consume (line 132) | pub fn consume(self) -> (Stream, Option<HttpPersistentSettings>) {
  type HttpServerApp (line 139) | pub trait HttpServerApp {
    method process_new_http (line 145) | async fn process_new_http(
    method h2_options (line 156) | fn h2_options(&self) -> Option<server::H2Options> {
    method server_options (line 164) | fn server_options(&self) -> Option<&HttpServerOptions> {
    method http_cleanup (line 168) | async fn http_cleanup(&self) {}
    method process_custom_session (line 171) | async fn process_custom_session(

FILE: pingora-core/src/apps/prometheus_http_app.rs
  type PrometheusHttpApp (line 30) | pub struct PrometheusHttpApp;
  method response (line 34) | async fn response(&self, _http_session: &mut ServerSession) -> Response<...
  type PrometheusServer (line 51) | pub type PrometheusServer = HttpServer<PrometheusHttpApp>;
    method new (line 54) | pub fn new() -> Self {

FILE: pingora-core/src/connectors/http/custom/mod.rs
  type Connection (line 26) | pub enum Connection<S: Session> {
  type Connector (line 32) | pub trait Connector: Send + Sync + Unpin + 'static {
    method get_http_session (line 35) | async fn get_http_session<P: Peer + Send + Sync + 'static>(
    method reused_http_session (line 40) | async fn reused_http_session<P: Peer + Send + Sync + 'static>(
    method release_http_session (line 45) | async fn release_http_session<P: Peer + Send + Sync + 'static>(
    type Session (line 56) | type Session = ();
    method get_http_session (line 58) | async fn get_http_session<P: Peer + Send + Sync + 'static>(
    method reused_http_session (line 65) | async fn reused_http_session<P: Peer + Send + Sync + 'static>(
    method release_http_session (line 72) | async fn release_http_session<P: Peer + Send + Sync + 'static>(

FILE: pingora-core/src/connectors/http/mod.rs
  type Connector (line 30) | pub struct Connector<C = ()>
  function new (line 40) | pub fn new(options: Option<ConnectorOptions>) -> Self {
  function new_custom (line 53) | pub fn new_custom(options: Option<ConnectorOptions>, custom: C) -> Self {
  function get_http_session (line 64) | pub async fn get_http_session<P: Peer + Send + Sync + 'static>(
  function release_http_session (line 133) | pub async fn release_http_session<P: Peer + Send + Sync + 'static>(
  function prefer_h1 (line 151) | pub fn prefer_h1(&self, peer: &impl Peer) {
  function get_http (line 176) | async fn get_http(http: &mut Http1Session, expected_status: u16) {
  function test_connect_h2 (line 188) | async fn test_connect_h2() {
  function test_connect_h1 (line 213) | async fn test_connect_h1() {
  function test_connect_h2_fallback_h1_reuse (line 239) | async fn test_connect_h2_fallback_h1_reuse() {
  function test_connect_prefer_h1 (line 274) | async fn test_connect_prefer_h1() {
  type MockConnector (line 302) | struct MockConnector {
    type Session (line 309) | type Session = ();
    method get_http_session (line 311) | async fn get_http_session<P: Peer + Send + Sync + 'static>(
    method reused_http_session (line 323) | async fn reused_http_session<P: Peer + Send + Sync + 'static>(
    method release_http_session (line 336) | async fn release_http_session<P: Peer + Send + Sync + 'static>(
  function get_available_port (line 348) | async fn get_available_port() -> u16 {
  function create_test_connector (line 358) | fn create_test_connector() -> Connector<MockConnector> {
  function create_peer_with_custom_proto (line 377) | fn create_peer_with_custom_proto(port: u16, proto: &[u8]) -> HttpPeer {
  function build_custom_tls_listener (line 387) | async fn build_custom_tls_listener(port: u16, custom_alpn: CustomALPN) -...
  function spawn_test_tls_server (line 408) | fn spawn_test_tls_server(listener: TransportStack) -> JoinHandle<()> {
  function test_custom_client_custom_upstream (line 424) | async fn test_custom_client_custom_upstream() {
  function test_incompatible_custom_client_custom_upstream (line 482) | async fn test_incompatible_custom_client_custom_upstream() {
  function test_custom_client_non_custom_upstream (line 519) | async fn test_custom_client_non_custom_upstream() {
  type NoCertificateVerification (line 566) | pub struct NoCertificateVerification;
  method verify_server_cert (line 569) | fn verify_server_cert(
  method verify_tls12_signature (line 580) | fn verify_tls12_signature(
  method verify_tls13_signature (line 589) | fn verify_tls13_signature(
  method supported_verify_schemes (line 598) | fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
  function apply_no_verify (line 603) | pub fn apply_no_verify(config: &mut rustls::ClientConfig) {

FILE: pingora-core/src/connectors/http/v1.rs
  type Connector (line 22) | pub struct Connector {
    method new (line 27) | pub fn new(options: Option<ConnectorOptions>) -> Self {
    method get_http_session (line 33) | pub async fn get_http_session<P: Peer + Send + Sync + 'static>(
    method reused_http_session (line 42) | pub async fn reused_http_session<P: Peer + Send + Sync + 'static>(
    method release_http_session (line 51) | pub async fn release_http_session<P: Peer + Send + Sync + 'static>(
  function get_http (line 74) | async fn get_http(http: &mut HttpSession, expected_status: u16) {
  function test_connect (line 86) | async fn test_connect() {
  function test_reuse_rejects_fd_mismatch (line 108) | async fn test_reuse_rejects_fd_mismatch() {
  function test_connect_tls (line 165) | async fn test_connect_tls() {

FILE: pingora-core/src/connectors/http/v2.rs
  type Stub (line 35) | struct Stub(SendRequest<Bytes>);
    method new_stream (line 38) | async fn new_stream(&self) -> Result<SendRequest<Bytes>> {
  type ConnectionRefInner (line 47) | pub(crate) struct ConnectionRefInner {
  type ConnectionRef (line 66) | pub struct ConnectionRef(Arc<ConnectionRefInner>);
    method new (line 69) | pub fn new(
    method more_streams_allowed (line 90) | pub fn more_streams_allowed(&self) -> bool {
    method is_idle (line 97) | pub fn is_idle(&self) -> bool {
    method release_stream (line 101) | pub fn release_stream(&self) {
    method id (line 105) | pub fn id(&self) -> UniqueIDType {
    method digest (line 109) | pub fn digest(&self) -> &Digest {
    method digest_mut (line 113) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
    method ping_timedout (line 117) | pub fn ping_timedout(&self) -> bool {
    method is_closed (line 121) | pub fn is_closed(&self) -> bool {
    method is_shutting_down (line 127) | pub fn is_shutting_down(&self) -> bool {
    method spawn_stream (line 132) | pub async fn spawn_stream(&self) -> Result<Option<Http2Session>> {
  type InUsePool (line 166) | pub struct InUsePool {
    method new (line 172) | fn new() -> Self {
    method try_remove_empty_node (line 184) | fn try_remove_empty_node(&self, reuse_hash: u64) {
    method insert (line 193) | pub fn insert(&self, reuse_hash: u64, conn: ConnectionRef) {
    method get (line 217) | pub fn get(&self, reuse_hash: u64) -> Option<ConnectionRef> {
    method release (line 238) | pub fn release(&self, reuse_hash: u64, id: UniqueIDType) -> Option<Con...
  constant DEFAULT_POOL_SIZE (line 257) | const DEFAULT_POOL_SIZE: usize = 128;
  type Connector (line 260) | pub struct Connector {
    method new (line 271) | pub fn new(options: Option<ConnectorOptions>) -> Self {
    method transport (line 283) | pub fn transport(&self) -> &TransportConnector {
    method idle_pool (line 287) | pub fn idle_pool(&self) -> &Arc<ConnectionPool<ConnectionRef>> {
    method in_use_pool (line 291) | pub fn in_use_pool(&self) -> &InUsePool {
    method new_http_session (line 298) | pub async fn new_http_session<P: Peer + Send + Sync + 'static, C: Sess...
    method reused_http_session (line 344) | pub async fn reused_http_session<P: Peer + Send + Sync + 'static>(
    method release_http_session (line 404) | pub fn release_http_session<P: Peer + Send + Sync + 'static>(
    method prefer_h1 (line 450) | pub fn prefer_h1(&self, peer: &impl Peer) {
    method h1_is_preferred (line 454) | pub(crate) fn h1_is_preferred(&self, peer: &impl Peer) -> bool {
  constant H2_WINDOW_SIZE (line 468) | const H2_WINDOW_SIZE: u32 = 1 << 23;
  function handshake (line 470) | pub async fn handshake(
  function test_connect_h2 (line 547) | async fn test_connect_h2() {
  function test_connect_h1 (line 564) | async fn test_connect_h1() {
  function test_connect_h1_plaintext (line 581) | async fn test_connect_h1_plaintext() {
  function test_h2_single_stream (line 598) | async fn test_h2_single_stream() {
  function test_h2_multiple_stream (line 634) | async fn test_h2_multiple_stream() {
  function test_h2_reuse_rejects_fd_mismatch (line 679) | async fn test_h2_reuse_rejects_fd_mismatch() {

FILE: pingora-core/src/connectors/l4.rs
  type Connect (line 36) | pub trait Connect: std::fmt::Debug {
    method connect (line 37) | async fn connect(&self, addr: &SocketAddr) -> Result<Stream>;
  type BindTo (line 42) | pub struct BindTo {
    method set_port_range (line 57) | pub fn set_port_range(&mut self, range: Option<(u16, u16)>) -> Result<...
    method set_fallback (line 76) | pub fn set_fallback(&mut self, fallback: bool) {
    method port_range (line 81) | pub fn port_range(&self) -> Option<(u16, u16)> {
    method will_fallback (line 86) | pub fn will_fallback(&self) -> bool {
  function connect (line 92) | pub(crate) async fn connect<P>(peer: &P, bind_to: Option<BindTo>) -> Res...
  function bind_to_random (line 213) | pub(crate) fn bind_to_random<P: Peer>(
  function proxy_connect (line 261) | async fn proxy_connect<P: Peer>(peer: &P) -> Result<Stream> {
  function proxy_connect (line 302) | async fn proxy_connect<P: Peer>(peer: &P) -> Result<Stream> {
  function wait_for_peer (line 325) | async fn wait_for_peer<P>(peer: &P)
  function test_conn_error_refused (line 347) | async fn test_conn_error_refused() {
  function test_conn_error_no_route (line 356) | async fn test_conn_error_no_route() {
  function test_conn_error_addr_not_avail (line 363) | async fn test_conn_error_addr_not_avail() {
  function test_conn_error_other (line 375) | async fn test_conn_error_other() {
  function test_conn_timeout (line 396) | async fn test_conn_timeout() {
  function test_tweak_hook (line 405) | async fn test_tweak_hook() {
  function test_custom_connect (line 425) | async fn test_custom_connect() {
  function test_connect_proxy_fail (line 449) | async fn test_connect_proxy_fail() {
  function test_connect_proxy_work (line 467) | async fn test_connect_proxy_work() {
  function test_connect_proxy_conn_closed (line 496) | async fn test_connect_proxy_conn_closed() {
  function test_bind_to_port_range_on_connect (line 527) | async fn test_bind_to_port_range_on_connect() {
  function test_bind_to_port_ranges (line 627) | fn test_bind_to_port_ranges() {

FILE: pingora-core/src/connectors/mod.rs
  type ConnectorOptions (line 46) | pub struct ConnectorOptions {
    method from_server_conf (line 86) | pub fn from_server_conf(server_conf: &ServerConf) -> Self {
    method new (line 126) | pub fn new(keepalive_pool_size: usize) -> Self {
  type TransportConnector (line 142) | pub struct TransportConnector {
    method new (line 155) | pub fn new(mut options: Option<ConnectorOptions>) -> Self {
    method new_stream (line 181) | pub async fn new_stream<P: Peer + Send + Sync + 'static>(&self, peer: ...
    method reused_stream (line 202) | pub async fn reused_stream<P: Peer + Send + Sync>(&self, peer: &P) -> ...
    method release_stream (line 258) | pub fn release_stream(
    method get_stream (line 287) | pub async fn get_stream<P: Peer + Send + Sync + 'static>(
    method prefer_h1 (line 301) | pub fn prefer_h1(&self, peer: &impl Peer) {
  constant DEFAULT_POOL_SIZE (line 151) | const DEFAULT_POOL_SIZE: usize = 128;
  function do_connect (line 308) | async fn do_connect<P: Peer + Send + Sync>(
  function do_connect_inner (line 331) | async fn do_connect_inner<P: Peer + Send + Sync>(
  type PreferredHttpVersion (line 346) | struct PreferredHttpVersion {
    method new (line 354) | pub fn new() -> Self {
    method add (line 360) | pub fn add(&self, peer: &impl Peer, version: u8) {
    method get (line 366) | pub fn get(&self, peer: &impl Peer) -> Option<ALPN> {
  function test_reusable_stream (line 379) | fn test_reusable_stream(stream: &mut Stream) -> bool {
  function unique_uds_path (line 409) | pub fn unique_uds_path(test_name: &str) -> String {
  function spawn_mock_uds_server (line 423) | pub fn spawn_mock_uds_server(
  function spawn_mock_uds_server_close_immediate (line 454) | pub fn spawn_mock_uds_server_close_immediate(
  constant BLACK_HOLE (line 492) | const BLACK_HOLE: &str = "192.0.2.1:79";
  function test_connect (line 495) | async fn test_connect() {
  function test_connect_tls (line 507) | async fn test_connect_tls() {
  function test_connect_uds (line 522) | async fn test_connect_uds() {
  function do_test_conn_timeout (line 550) | async fn do_test_conn_timeout(conf: Option<ConnectorOptions>) {
  function test_conn_timeout (line 562) | async fn test_conn_timeout() {
  function test_conn_timeout_with_offload (line 567) | async fn test_conn_timeout_with_offload() {
  function test_connector_bind_to (line 574) | async fn test_connector_bind_to() {
  function get_do_connect_failure_with_peer (line 590) | async fn get_do_connect_failure_with_peer(peer: &BasicPeer) -> (ErrorTyp...
  function test_do_connect_with_total_timeout (line 606) | async fn test_do_connect_with_total_timeout() {
  function test_tls_connect_timeout_supersedes_total (line 615) | async fn test_tls_connect_timeout_supersedes_total() {
  function test_do_connect_without_total_timeout (line 625) | async fn test_do_connect_without_total_timeout() {

FILE: pingora-core/src/connectors/offload.rs
  type OffloadRuntime (line 23) | pub(crate) struct OffloadRuntime {
    method new (line 32) | pub fn new(shards: usize, thread_per_shard: usize) -> Self {
    method init_pools (line 42) | fn init_pools(&self) -> Box<[(Handle, Sender<()>)]> {
    method get_runtime (line 66) | pub fn get_runtime(&self, hash: u64) -> &Handle {

FILE: pingora-core/src/connectors/tls/boringssl_openssl/mod.rs
  type TlsConnector (line 35) | pub type TlsConnector = SslConnector;
  constant CIPHER_LIST (line 37) | const CIPHER_LIST: &str = "AES-128-GCM-SHA256\
  constant SIGALG_LIST (line 56) | const SIGALG_LIST: &str = "ECDSA_SECP256R1_SHA256\
  constant BORINGSSL_CURVE_LIST (line 75) | const BORINGSSL_CURVE_LIST: &[SslCurve] = &[
  function init_ssl_cert_env_vars (line 83) | fn init_ssl_cert_env_vars() {
  type Connector (line 92) | pub struct Connector {
    method new (line 97) | pub fn new(options: Option<ConnectorOptions>) -> Self {
  function connect (line 153) | pub(crate) async fn connect<T, P>(

FILE: pingora-core/src/connectors/tls/mod.rs
  function replace_leftmost_underscore (line 45) | pub fn replace_leftmost_underscore(sni: &str) -> Option<String> {
  function test_replace_leftmost_underscore (line 65) | fn test_replace_leftmost_underscore() {

FILE: pingora-core/src/connectors/tls/rustls/mod.rs
  type Connector (line 41) | pub struct Connector {
    method new (line 49) | pub fn new(config_opt: Option<ConnectorOptions>) -> Self {
  type TlsConnector (line 54) | pub struct TlsConnector {
    method build_connector (line 60) | pub(crate) fn build_connector(options: Option<ConnectorOptions>) -> Re...
  function connect (line 126) | pub async fn connect<T, P>(
  type VerificationMode (line 265) | pub enum VerificationMode {
  type CustomServerCertVerifier (line 274) | pub struct CustomServerCertVerifier {
    method new (line 280) | pub fn new(delegate: Arc<WebPkiServerVerifier>, verification_mode: Ver...
  method verify_server_cert (line 293) | fn verify_server_cert(
  method verify_tls12_signature (line 332) | fn verify_tls12_signature(
  method verify_tls13_signature (line 341) | fn verify_tls13_signature(
  method supported_verify_schemes (line 350) | fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {

FILE: pingora-core/src/connectors/tls/s2n/mod.rs
  constant DEFAULT_CONFIG_CACHE_SIZE (line 40) | const DEFAULT_CONFIG_CACHE_SIZE: NonZero<usize> = NonZero::new(10).unwra...
  type Connector (line 43) | pub struct Connector {
    method new (line 51) | pub fn new(options: Option<ConnectorOptions>) -> Self {
  type TlsConnector (line 67) | pub struct TlsConnector {
    method new (line 73) | pub fn new(options: Option<ConnectorOptions>) -> Self {
    method load_config (line 82) | fn load_config(&self, config_options: S2NConfigOptions) -> Result<Conf...
    method load_config_from_cache (line 97) | fn load_config_from_cache(&self, config_hash: u64) -> Option<Config> {
    method put_config_in_cache (line 106) | fn put_config_in_cache(&self, config_hash: u64, config: Config) {
    method create_config_cache (line 113) | fn create_config_cache(
  function connect (line 130) | pub(crate) async fn connect<T, P>(
  function create_s2n_config (line 173) | fn create_s2n_config(
  type S2NConfigOptions (line 252) | struct S2NConfigOptions {
    method from_peer (line 263) | fn from_peer<P>(peer: &P, alpn_override: Option<ALPN>) -> Self
    method config_hash (line 278) | fn config_hash(&self) -> u64 {
  method hash (line 286) | fn hash<H: Hasher>(&self, state: &mut H) {
  constant CA_CERT_FILE (line 307) | const CA_CERT_FILE: &str = "tests/certs/ca.crt";
  constant ALT_CA_CERT_FILE (line 308) | const ALT_CA_CERT_FILE: &str = "tests/certs/alt-ca.crt";
  constant CERT_FILE (line 310) | const CERT_FILE: &str = "tests/certs/server.crt";
  constant ALT_CERT_FILE (line 311) | const ALT_CERT_FILE: &str = "tests/certs/alt-server.crt";
  constant KEY_FILE (line 313) | const KEY_FILE: &str = "tests/certs/server.key";
  function read_file (line 315) | fn read_file(file: &str) -> Vec<u8> {
  function load_pem_from_file (line 319) | fn load_pem_from_file(file: &str) -> X509Pem {
  function create_config_options (line 323) | fn create_config_options() -> S2NConfigOptions {
  function config_cache_hit_identical (line 339) | fn config_cache_hit_identical() {
  function config_cache_miss_max_blinding_delay_changed (line 351) | fn config_cache_miss_max_blinding_delay_changed() {
  function config_cache_miss_alpn_changed (line 363) | fn config_cache_miss_alpn_changed() {
  function config_cache_miss_verify_cert_changed (line 375) | fn config_cache_miss_verify_cert_changed() {
  function config_cache_miss_verify_hostname_changed (line 387) | fn config_cache_miss_verify_hostname_changed() {
  function config_cache_miss_use_system_certs_changed (line 399) | fn config_cache_miss_use_system_certs_changed() {
  function config_cache_miss_ca_changed (line 411) | fn config_cache_miss_ca_changed() {
  function config_cache_miss_client_cert_key_changed (line 423) | fn config_cache_miss_client_cert_key_changed() {

FILE: pingora-core/src/listeners/connection_filter.rs
  type ConnectionFilter (line 66) | pub trait ConnectionFilter: Debug + Send + Sync {
    method should_accept (line 92) | async fn should_accept(&self, _addr: Option<&SocketAddr>) -> bool {
    method should_accept (line 121) | async fn should_accept(&self, addr_opt: Option<&SocketAddr>) -> bool {
  type AcceptAllFilter (line 102) | pub struct AcceptAllFilter;
  type BlockListFilter (line 115) | struct BlockListFilter {
  function test_accept_all_filter (line 129) | async fn test_accept_all_filter() {
  function test_blocklist_filter (line 136) | async fn test_blocklist_filter() {

FILE: pingora-core/src/listeners/l4.rs
  constant TCP_LISTENER_MAX_TRY (line 48) | const TCP_LISTENER_MAX_TRY: usize = 30;
  constant TCP_LISTENER_TRY_STEP (line 49) | const TCP_LISTENER_TRY_STEP: Duration = Duration::from_secs(1);
  constant LISTENER_BACKLOG (line 51) | const LISTENER_BACKLOG: u32 = 65535;
  type ServerAddress (line 55) | pub enum ServerAddress {
    method as_ref (line 62) | fn as_ref(&self) -> &str {
    method tcp_sock_opts (line 72) | fn tcp_sock_opts(&self) -> Option<&TcpSocketOptions> {
  type TcpSocketOptions (line 84) | pub struct TcpSocketOptions {
  function set_perms (line 125) | pub(super) fn set_perms(path: &str, perms: Option<Permissions>) -> Resul...
  function set_backlog (line 133) | pub(super) fn set_backlog(l: StdUnixListener, backlog: u32) -> Result<Un...
  function bind (line 143) | pub(super) fn bind(addr: &str, perms: Option<Permissions>) -> Result<Lis...
  function apply_tcp_socket_options (line 169) | fn apply_tcp_socket_options(sock: &TcpSocket, opt: Option<&TcpSocketOpti...
  function from_raw_fd (line 204) | fn from_raw_fd(address: &ServerAddress, fd: i32) -> Result<Listener> {
  function bind_tcp (line 229) | async fn bind_tcp(addr: &str, opt: Option<TcpSocketOptions>) -> Result<L...
  function bind (line 277) | async fn bind(addr: &ServerAddress) -> Result<Listener> {
  type ListenerEndpoint (line 286) | pub struct ListenerEndpoint {
    method builder (line 381) | pub fn builder() -> ListenerEndpointBuilder {
    method as_str (line 385) | pub fn as_str(&self) -> &str {
    method apply_stream_settings (line 389) | fn apply_stream_settings(&self, stream: &mut Stream) -> Result<()> {
    method accept (line 419) | pub async fn accept(&self) -> Result<Stream> {
  type ListenerEndpointBuilder (line 294) | pub struct ListenerEndpointBuilder {
    method new (line 301) | pub fn new() -> ListenerEndpointBuilder {
    method listen_addr (line 309) | pub fn listen_addr(&mut self, addr: ServerAddress) -> &mut Self {
    method connection_filter (line 315) | pub fn connection_filter(&mut self, filter: Arc<dyn ConnectionFilter>)...
    method listen (line 321) | pub async fn listen(self, fds: Option<ListenFds>) -> Result<ListenerEn...
    method listen (line 359) | pub async fn listen(self) -> Result<ListenerEndpoint> {
  function test_listen_tcp (line 472) | async fn test_listen_tcp() {
  function test_listen_tcp_ipv6_only (line 495) | async fn test_listen_tcp_ipv6_only() {
  function test_listen_uds (line 526) | async fn test_listen_uds() {
  function test_tcp_so_reuseport (line 546) | async fn test_tcp_so_reuseport() {
  function test_tcp_so_reuseport_false (line 570) | async fn test_tcp_so_reuseport_false() {
  function test_connection_filter_accept (line 606) | async fn test_connection_filter_accept() {
  function test_connection_filter_blocks_all (line 669) | async fn test_connection_filter_blocks_all() {

FILE: pingora-core/src/listeners/mod.rs
  type AcceptAllFilter (line 56) | pub struct AcceptAllFilter;
  type ConnectionFilter (line 59) | pub trait ConnectionFilter: std::fmt::Debug + Send + Sync {
    method should_accept (line 60) | fn should_accept(&self, _addr: &std::net::SocketAddr) -> bool {
    method should_accept (line 67) | fn should_accept(&self, _addr: &std::net::SocketAddr) -> bool {
  type TlsAccept (line 95) | pub trait TlsAccept {
    method certificate_callback (line 101) | async fn certificate_callback(&self, _ssl: &mut TlsRef) -> () {
    method handshake_complete_callback (line 111) | async fn handshake_complete_callback(
  type TlsAcceptCallbacks (line 119) | pub type TlsAcceptCallbacks = Box<dyn TlsAccept + Send + Sync>;
  type TransportStackBuilder (line 121) | struct TransportStackBuilder {
    method build (line 129) | pub async fn build(
  type TransportStack (line 156) | pub(crate) struct TransportStack {
    method as_str (line 162) | pub fn as_str(&self) -> &str {
    method accept (line 166) | pub async fn accept(&self) -> Result<UninitializedStream> {
    method cleanup (line 174) | pub fn cleanup(&mut self) {
  type UninitializedStream (line 179) | pub(crate) struct UninitializedStream {
    method handshake (line 185) | pub async fn handshake(mut self) -> Result<Stream> {
    method peer_addr (line 196) | pub fn peer_addr(&self) -> Option<SocketAddr> {
  type Listeners (line 204) | pub struct Listeners {
    method new (line 212) | pub fn new() -> Self {
    method tcp (line 220) | pub fn tcp(addr: &str) -> Self {
    method uds (line 228) | pub fn uds(addr: &str, perm: Option<Permissions>) -> Self {
    method tls (line 238) | pub fn tls(addr: &str, cert_path: &str, key_path: &str) -> Result<Self> {
    method add_tcp (line 245) | pub fn add_tcp(&mut self, addr: &str) {
    method add_tcp_with_settings (line 250) | pub fn add_tcp_with_settings(&mut self, addr: &str, sock_opt: TcpSocke...
    method add_uds (line 256) | pub fn add_uds(&mut self, addr: &str, perm: Option<Permissions>) {
    method add_tls (line 262) | pub fn add_tls(&mut self, addr: &str, cert_path: &str, key_path: &str)...
    method add_tls_with_settings (line 269) | pub fn add_tls_with_settings(
    method add_address (line 279) | pub fn add_address(&mut self, addr: ServerAddress) {
    method set_connection_filter (line 285) | pub fn set_connection_filter(&mut self, filter: Arc<dyn ConnectionFilt...
    method add_endpoint (line 298) | pub fn add_endpoint(&mut self, l4: ServerAddress, tls: Option<TlsSetti...
    method build (line 307) | pub(crate) async fn build(
    method cleanup (line 327) | pub(crate) fn cleanup(&self) {
  function test_listen_tcp (line 343) | async fn test_listen_tcp() {
  function test_listen_tls (line 375) | async fn test_listen_tls() {
  function test_connection_filter_inheritance (line 417) | fn test_connection_filter_inheritance() {

FILE: pingora-core/src/listeners/tls/boringssl_openssl/mod.rs
  constant TLS_CONF_ERR (line 31) | pub const TLS_CONF_ERR: ErrorType = ErrorType::Custom("TLSConfigError");
  type Acceptor (line 33) | pub(crate) struct Acceptor {
    method tls_handshake (line 141) | pub async fn tls_handshake<S: IO>(&self, stream: S) -> Result<SslStrea...
  type TlsSettings (line 39) | pub struct TlsSettings {
    method from (line 45) | fn from(settings: SslAcceptorBuilder) -> Self {
    method intermediate (line 71) | pub fn intermediate(cert_path: &str, key_path: &str) -> Result<Self> {
    method with_callbacks (line 92) | pub fn with_callbacks(callbacks: TlsAcceptCallbacks) -> Result<Self> {
    method enable_h2 (line 105) | pub fn enable_h2(&mut self) {
    method set_alpn (line 110) | pub fn set_alpn(&mut self, alpn: ALPN) {
    method build (line 132) | pub(crate) fn build(self) -> Acceptor {
  type Target (line 54) | type Target = SslAcceptorBuilder;
  method deref (line 56) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 62) | fn deref_mut(&mut self) -> &mut Self::Target {
  function valid_alpn (line 156) | pub(super) fn valid_alpn(alpn_in: &[u8]) -> bool {
  function select_protocol (line 169) | pub(super) fn select_protocol<'a>(
  function prefer_h2 (line 187) | pub fn prefer_h2<'a>(_ssl: &mut SslRef, alpn_in: &'a [u8]) -> Result<&'a...
  function h1_only (line 197) | pub fn h1_only<'a>(_ssl: &mut SslRef, alpn_in: &'a [u8]) -> Result<&'a [...
  function h2_only (line 207) | pub fn h2_only<'a>(_ssl: &mut SslRef, alpn_in: &'a [u8]) -> Result<&'a [...

FILE: pingora-core/src/listeners/tls/rustls/mod.rs
  type TlsSettings (line 30) | pub struct TlsSettings {
    method build (line 50) | pub fn build(self) -> Acceptor {
    method enable_h2 (line 85) | pub fn enable_h2(&mut self) {
    method set_alpn (line 89) | pub fn set_alpn(&mut self, alpn: ALPN) {
    method set_client_cert_verifier (line 94) | pub fn set_client_cert_verifier(&mut self, verifier: Arc<dyn ClientCer...
    method intermediate (line 98) | pub fn intermediate(cert_path: &str, key_path: &str) -> Result<Self>
    method with_callbacks (line 110) | pub fn with_callbacks() -> Result<Self>
  type Acceptor (line 37) | pub struct Acceptor {
    method tls_handshake (line 123) | pub async fn tls_handshake<S: IO>(&self, stream: S) -> Result<TlsStrea...

FILE: pingora-core/src/listeners/tls/s2n/mod.rs
  type TlsSettings (line 29) | pub struct TlsSettings {
    method build (line 46) | pub fn build(self) -> Acceptor {
    method enable_h2 (line 104) | pub fn enable_h2(&mut self) {
    method set_alpn (line 108) | fn set_alpn(&mut self, alpn: ALPN) {
    method set_ca (line 113) | pub fn set_ca(&mut self, ca: CaType) {
    method set_psk_config (line 119) | pub fn set_psk_config(&mut self, psk_config: PskConfig) {
    method set_policy (line 126) | pub fn set_policy(&mut self, policy: S2NPolicy) {
    method set_cert (line 131) | pub fn set_cert(&mut self, cert_path: &str, key_path: &str) {
    method set_client_auth_required (line 137) | pub fn set_client_auth_required(&mut self, required: bool) {
    method set_verify_client_hostname (line 142) | pub fn set_verify_client_hostname(&mut self, verify: bool) {
    method set_max_blinding_delay (line 149) | pub fn set_max_blinding_delay(&mut self, delay: u32) {
    method intermediate (line 153) | pub fn intermediate(cert_path: &str, key_path: &str) -> Result<Self>
    method new (line 170) | pub fn new() -> Self {
  type Acceptor (line 41) | pub struct Acceptor {
    method tls_handshake (line 186) | pub async fn tls_handshake<S: IO>(&self, stream: S) -> Result<TlsStrea...

FILE: pingora-core/src/modules/http/compression.rs
  type ResponseCompression (line 22) | pub struct ResponseCompression(ResponseCompressionCtx);
  type Target (line 25) | type Target = ResponseCompressionCtx;
  method deref (line 27) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 33) | fn deref_mut(&mut self) -> &mut Self::Target {
  method as_any (line 40) | fn as_any(&self) -> &dyn std::any::Any {
  method as_any_mut (line 43) | fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
  method request_header_filter (line 47) | async fn request_header_filter(&mut self, req: &mut RequestHeader) -> Re...
  method response_header_filter (line 52) | async fn response_header_filter(
  method response_body_filter (line 61) | fn response_body_filter(
  method response_done_filter (line 76) | fn response_done_filter(&mut self) -> Result<Option<Bytes>> {
  type ResponseCompressionBuilder (line 87) | pub struct ResponseCompressionBuilder {
    method enable (line 93) | pub fn enable(level: u32) -> ModuleBuilder {
  method init (line 99) | fn init(&self) -> Module {
  method order (line 105) | fn order(&self) -> i16 {

FILE: pingora-core/src/modules/http/grpc_web.rs
  type GrpcWebBridge (line 22) | pub struct GrpcWebBridge(GrpcWebCtx);
  type Target (line 25) | type Target = GrpcWebCtx;
  method deref (line 27) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 33) | fn deref_mut(&mut self) -> &mut Self::Target {
  method as_any (line 40) | fn as_any(&self) -> &dyn std::any::Any {
  method as_any_mut (line 44) | fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
  method request_header_filter (line 48) | async fn request_header_filter(&mut self, req: &mut RequestHeader) -> Re...
  method response_header_filter (line 53) | async fn response_header_filter(
  method response_trailer_filter (line 62) | fn response_trailer_filter(
  type GrpcWeb (line 74) | pub struct GrpcWeb;
  method init (line 77) | fn init(&self) -> Module {

FILE: pingora-core/src/modules/http/mod.rs
  type HttpModule (line 39) | pub trait HttpModule {
    method request_header_filter (line 40) | async fn request_header_filter(&mut self, _req: &mut RequestHeader) ->...
    method request_body_filter (line 44) | async fn request_body_filter(
    method response_header_filter (line 52) | async fn response_header_filter(
    method response_body_filter (line 60) | fn response_body_filter(
    method response_trailer_filter (line 68) | fn response_trailer_filter(
    method response_done_filter (line 75) | fn response_done_filter(&mut self) -> Result<Option<Bytes>> {
    method as_any (line 79) | fn as_any(&self) -> &dyn Any;
    method as_any_mut (line 80) | fn as_any_mut(&mut self) -> &mut dyn Any;
    method as_any (line 292) | fn as_any(&self) -> &dyn Any {
    method as_any_mut (line 295) | fn as_any_mut(&mut self) -> &mut dyn Any {
    method request_header_filter (line 298) | async fn request_header_filter(&mut self, req: &mut RequestHeader) -> ...
    method as_any (line 316) | fn as_any(&self) -> &dyn Any {
    method as_any_mut (line 319) | fn as_any_mut(&mut self) -> &mut dyn Any {
    method request_header_filter (line 322) | async fn request_header_filter(&mut self, req: &mut RequestHeader) -> ...
  type Module (line 83) | pub type Module = Box<dyn HttpModule + 'static + Send + Sync>;
  type HttpModuleBuilder (line 86) | pub trait HttpModuleBuilder {
    method order (line 91) | fn order(&self) -> i16 {
    method init (line 96) | fn init(&self) -> Module;
    method order (line 304) | fn order(&self) -> i16 {
    method init (line 308) | fn init(&self) -> Module {
    method order (line 334) | fn order(&self) -> i16 {
    method init (line 338) | fn init(&self) -> Module {
  type ModuleBuilder (line 99) | pub type ModuleBuilder = Box<dyn HttpModuleBuilder + 'static + Send + Sy...
  type HttpModules (line 102) | pub struct HttpModules {
    method new (line 109) | pub fn new() -> Self {
    method add_module (line 121) | pub fn add_module(&mut self, builder: ModuleBuilder) {
    method build_ctx (line 134) | pub fn build_ctx(&self) -> HttpModuleCtx {
  type HttpModuleCtx (line 161) | pub struct HttpModuleCtx {
    method empty (line 172) | pub fn empty() -> Self {
    method get (line 180) | pub fn get<T: 'static>(&self) -> Option<&T> {
    method get_mut (line 191) | pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
    method request_header_filter (line 202) | pub async fn request_header_filter(&mut self, req: &mut RequestHeader)...
    method request_body_filter (line 210) | pub async fn request_body_filter(
    method response_header_filter (line 222) | pub async fn response_header_filter(
    method response_body_filter (line 234) | pub fn response_body_filter(
    method response_trailer_filter (line 253) | pub fn response_trailer_filter(
    method response_done_filter (line 274) | pub fn response_done_filter(&mut self) -> Result<Option<Bytes>> {
  type MyModule (line 289) | struct MyModule;
  type MyModuleBuilder (line 302) | struct MyModuleBuilder;
  type MyOtherModule (line 313) | struct MyOtherModule;
  type MyOtherModuleBuilder (line 332) | struct MyOtherModuleBuilder;
  function test_module_get (line 344) | fn test_module_get() {
  function test_module_filter (line 358) | async fn test_module_filter() {

FILE: pingora-core/src/protocols/digest.rs
  type Digest (line 29) | pub struct Digest {
  type ProtoDigest (line 41) | pub trait ProtoDigest {
    method get_digest (line 42) | fn get_digest(&self) -> Option<&Digest> {
  type TimingDigest (line 49) | pub struct TimingDigest {
  method default (line 55) | fn default() -> Self {
  type SocketDigest (line 64) | pub struct SocketDigest {
    method from_raw_fd (line 79) | pub fn from_raw_fd(raw_fd: std::os::unix::io::RawFd) -> SocketDigest {
    method from_raw_socket (line 89) | pub fn from_raw_socket(raw_sock: std::os::windows::io::RawSocket) -> S...
    method peer_addr (line 99) | pub fn peer_addr(&self) -> Option<&SocketAddr> {
    method peer_addr (line 106) | pub fn peer_addr(&self) -> Option<&SocketAddr> {
    method local_addr (line 113) | pub fn local_addr(&self) -> Option<&SocketAddr> {
    method local_addr (line 120) | pub fn local_addr(&self) -> Option<&SocketAddr> {
    method is_inet (line 126) | fn is_inet(&self) -> bool {
    method tcp_info (line 131) | pub fn tcp_info(&self) -> Option<TCP_INFO> {
    method tcp_info (line 140) | pub fn tcp_info(&self) -> Option<TCP_INFO> {
    method get_recv_buf (line 149) | pub fn get_recv_buf(&self) -> Option<usize> {
    method get_recv_buf (line 158) | pub fn get_recv_buf(&self) -> Option<usize> {
    method get_snd_buf (line 167) | pub fn get_snd_buf(&self) -> Option<usize> {
    method get_snd_buf (line 176) | pub fn get_snd_buf(&self) -> Option<usize> {
    method original_dst (line 185) | pub fn original_dst(&self) -> Option<&SocketAddr> {
    method original_dst (line 197) | pub fn original_dst(&self) -> Option<&SocketAddr> {
  type GetTimingDigest (line 210) | pub trait GetTimingDigest {
    method get_timing_digest (line 212) | fn get_timing_digest(&self) -> Vec<Option<TimingDigest>>;
    method get_read_pending_time (line 213) | fn get_read_pending_time(&self) -> Duration {
    method get_write_pending_time (line 216) | fn get_write_pending_time(&self) -> Duration {
  type GetProxyDigest (line 222) | pub trait GetProxyDigest {
    method get_proxy_digest (line 223) | fn get_proxy_digest(&self) -> Option<Arc<ProxyDigest>>;
    method set_proxy_digest (line 224) | fn set_proxy_digest(&mut self, _digest: ProxyDigest) {}
  type GetSocketDigest (line 228) | pub trait GetSocketDigest {
    method get_socket_digest (line 229) | fn get_socket_digest(&self) -> Option<Arc<SocketDigest>>;
    method set_socket_digest (line 230) | fn set_socket_digest(&mut self, _socket_digest: SocketDigest) {}

FILE: pingora-core/src/protocols/http/body_buffer.rs
  type FixedBuffer (line 19) | pub struct FixedBuffer {
    method new (line 26) | pub fn new(capacity: usize) -> Self {
    method write_to_buffer (line 35) | pub fn write_to_buffer(&mut self, data: &Bytes) {
    method clear (line 43) | pub fn clear(&mut self) {
    method is_empty (line 47) | pub fn is_empty(&self) -> bool {
    method is_truncated (line 50) | pub fn is_truncated(&self) -> bool {
    method get_buffer (line 53) | pub fn get_buffer(&self) -> Option<Bytes> {

FILE: pingora-core/src/protocols/http/bridge/grpc_web.rs
  type GrpcWebCtx (line 27) | pub enum GrpcWebCtx {
    method init (line 40) | pub fn init(&mut self) {
    method request_header_filter (line 46) | pub fn request_header_filter(&mut self, req: &mut RequestHeader) {
    method response_header_filter (line 88) | pub fn response_header_filter(&mut self, resp: &mut ResponseHeader) {
    method response_trailer_filter (line 127) | pub fn response_trailer_filter(
  constant GRPC (line 36) | const GRPC: &str = "application/grpc";
  constant GRPC_WEB (line 37) | const GRPC_WEB: &str = "application/grpc-web";
  function non_grpc_web_request_ignored (line 190) | fn non_grpc_web_request_ignored() {
  function grpc_web_request_module_disabled_ignored (line 210) | fn grpc_web_request_module_disabled_ignored() {
  function grpc_web_request_upgrade (line 230) | fn grpc_web_request_upgrade() {
  function non_grpc_response_ignored (line 253) | fn non_grpc_response_ignored() {
  function grpc_response_module_disabled_ignored (line 271) | fn grpc_response_module_disabled_ignored() {
  function grpc_response_upgrade (line 287) | fn grpc_response_upgrade() {
  function grpc_response_informational_proxied (line 309) | fn grpc_response_informational_proxied() {
  function grpc_response_trailer_headers_convert_to_byte_buf (line 319) | fn grpc_response_trailer_headers_convert_to_byte_buf() {

FILE: pingora-core/src/protocols/http/client.rs
  type HttpSession (line 25) | pub enum HttpSession<S = ()> {
  function as_http1 (line 32) | pub fn as_http1(&self) -> Option<&Http1Session> {
  function as_http2 (line 40) | pub fn as_http2(&self) -> Option<&Http2Session> {
  function as_custom (line 48) | pub fn as_custom(&self) -> Option<&S> {
  function as_custom_mut (line 56) | pub fn as_custom_mut(&mut self) -> Option<&mut S> {
  function write_request_header (line 67) | pub async fn write_request_header(&mut self, req: Box<RequestHeader>) ->...
  function write_request_body (line 79) | pub async fn write_request_body(&mut self, data: Bytes, end: bool) -> Re...
  function finish_request_body (line 92) | pub async fn finish_request_body(&mut self) -> Result<()> {
  function set_read_timeout (line 106) | pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
  function set_write_timeout (line 117) | pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
  function read_response_header (line 128) | pub async fn read_response_header(&mut self) -> Result<()> {
  function read_response_body (line 142) | pub async fn read_response_body(&mut self) -> Result<Option<Bytes>> {
  function response_done (line 151) | pub fn response_done(&mut self) -> bool {
  function shutdown (line 162) | pub async fn shutdown(&mut self) {
  function response_header (line 173) | pub fn response_header(&self) -> Option<&ResponseHeader> {
  function digest (line 185) | pub fn digest(&self) -> Option<&Digest> {
  function digest_mut (line 196) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
  function server_addr (line 205) | pub fn server_addr(&self) -> Option<&SocketAddr> {
  function client_addr (line 214) | pub fn client_addr(&self) -> Option<&SocketAddr> {
  function stream (line 224) | pub fn stream(&self) -> Option<&Stream> {

FILE: pingora-core/src/protocols/http/compression/brotli.rs
  type Decompressor (line 24) | pub struct Decompressor {
    method new (line 32) | pub fn new() -> Self {
  method encode (line 44) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 73) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  type Compressor (line 78) | pub struct Compressor {
    method new (line 86) | pub fn new(level: u32) -> Self {
  method encode (line 98) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 122) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  function decompress_brotli_data (line 132) | fn decompress_brotli_data() {
  function compress_brotli_data (line 148) | fn compress_brotli_data() {

FILE: pingora-core/src/protocols/http/compression/gzip.rs
  type Decompressor (line 23) | pub struct Decompressor {
    method new (line 31) | pub fn new() -> Self {
  method encode (line 42) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 70) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  type Compressor (line 75) | pub struct Compressor {
    method new (line 84) | pub fn new(level: u32) -> Compressor {
  method encode (line 96) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 113) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  type Target (line 120) | type Target = GzDecoder<Vec<u8>>;
  method deref (line 122) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 128) | fn deref_mut(&mut self) -> &mut Self::Target {
  type Target (line 134) | type Target = GzEncoder<Vec<u8>>;
  method deref (line 136) | fn deref(&self) -> &Self::Target {
  method deref_mut (line 142) | fn deref_mut(&mut self) -> &mut Self::Target {
  function gzip_data (line 152) | fn gzip_data() {
  function gunzip_data (line 169) | fn gunzip_data() {

FILE: pingora-core/src/protocols/http/compression/mod.rs
  constant COMPRESSION_ERROR (line 38) | pub const COMPRESSION_ERROR: ErrorType = ErrorType::new("CompressionErro...
  type Encode (line 42) | pub trait Encode {
    method encode (line 46) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes>;
    method stat (line 49) | fn stat(&self) -> (&'static str, usize, usize, Duration);
  type ResponseCompressionCtx (line 68) | pub struct ResponseCompressionCtx(CtxInner);
    method new (line 96) | pub fn new(compression_level: u32, decompress_enable: bool, preserve_e...
    method is_enabled (line 108) | pub fn is_enabled(&self) -> bool {
    method get_info (line 121) | pub fn get_info(&self) -> Option<(&'static str, usize, usize, Duration...
    method adjust_level (line 131) | pub fn adjust_level(&mut self, new_level: u32) {
    method adjust_algorithm_level (line 146) | pub fn adjust_algorithm_level(&mut self, algorithm: Algorithm, new_lev...
    method adjust_decompression (line 161) | pub fn adjust_decompression(&mut self, enabled: bool) {
    method adjust_algorithm_decompression (line 175) | pub fn adjust_algorithm_decompression(&mut self, algorithm: Algorithm,...
    method adjust_preserve_etag (line 189) | pub fn adjust_preserve_etag(&mut self, enabled: bool) {
    method adjust_algorithm_preserve_etag (line 201) | pub fn adjust_algorithm_preserve_etag(&mut self, algorithm: Algorithm,...
    method set_dictionary (line 213) | pub fn set_dictionary(&mut self, dictionary_bytes: Bytes, dictionary_h...
    method has_dictionary (line 226) | pub fn has_dictionary(&self) -> bool {
    method clear_dictionary (line 236) | pub fn clear_dictionary(&mut self) {
    method request_filter (line 246) | pub fn request_filter(&mut self, req: &RequestHeader) {
    method response_header_filter (line 262) | pub fn response_header_filter(&mut self, resp: &mut ResponseHeader, en...
    method response_body_filter (line 334) | pub fn response_body_filter(&mut self, data: Option<&Bytes>, end: bool...
    method response_filter (line 360) | pub fn response_filter(&mut self, t: &mut HttpTask) {
  type DictionaryData (line 72) | pub struct DictionaryData {
  type CtxInner (line 77) | enum CtxInner {
  type Algorithm (line 386) | pub enum Algorithm {
    method as_str (line 399) | pub fn as_str(&self) -> &'static str {
    method compressor (line 411) | pub fn compressor(&self, level: u32) -> Option<Box<dyn Encode + Send +...
    method maybe_compressor_with_dictionary (line 424) | pub fn maybe_compressor_with_dictionary(
    method decompressor (line 448) | pub fn decompressor(&self, enabled: bool) -> Option<Box<dyn Encode + S...
    method index (line 460) | pub fn index(&self) -> usize {
    method from (line 466) | fn from(s: &str) -> Self {
  type Action (line 489) | enum Action {
  function parse_accept_encoding (line 496) | fn parse_accept_encoding(accept_encoding: Option<&http::HeaderValue>, li...
  function test_accept_encoding_req_header (line 531) | fn test_accept_encoding_req_header() {
  function depends_on_accept_encoding (line 561) | fn depends_on_accept_encoding(
  function test_decide_on_accept_encoding (line 574) | fn test_decide_on_accept_encoding() {
  function decide_action (line 602) | fn decide_action(resp: &ResponseHeader, accept_encoding: &[Algorithm]) -...
  function test_decide_action (line 643) | fn test_decide_action() {
  function compressible (line 747) | fn compressible(resp: &ResponseHeader) -> bool {
  function add_vary_header (line 784) | fn add_vary_header(resp: &mut ResponseHeader, value: &http::header::Head...
  function test_add_vary_header (line 819) | fn test_add_vary_header() {
  function adjust_response_header (line 870) | fn adjust_response_header(resp: &mut ResponseHeader, action: &Action, pr...
  function test_adjust_response_header (line 934) | fn test_adjust_response_header() {
  constant TEST_DICTIONARY (line 1013) | const TEST_DICTIONARY: &[u8] = b"The quick brown fox jumps over the lazy...
  function test_dictionary_hash (line 1017) | fn test_dictionary_hash() -> [u8; 32] {
  function set_and_clear_dictionary (line 1026) | fn set_and_clear_dictionary() {
  function dcz_compression_with_dictionary (line 1038) | fn dcz_compression_with_dictionary() {
  function dcz_without_dictionary_no_compression (line 1068) | fn dcz_without_dictionary_no_compression() {
  function dcz_no_fallback_without_dictionary (line 1086) | fn dcz_no_fallback_without_dictionary() {
  function maybe_compressor_with_dictionary_dcz_only (line 1105) | fn maybe_compressor_with_dictionary_dcz_only() {
  function dcz_full_flow (line 1131) | fn dcz_full_flow() {

FILE: pingora-core/src/protocols/http/compression/zstd.rs
  constant DCZ_MAGIC (line 24) | pub const DCZ_MAGIC: [u8; 8] = [0x5e, 0x2a, 0x4d, 0x18, 0x20, 0x00, 0x00...
  constant DCZ_HEADER_SIZE (line 27) | pub const DCZ_HEADER_SIZE: usize = 40;
  type Compressor (line 29) | pub struct Compressor {
    method new (line 37) | pub fn new(level: u32) -> Self {
  method encode (line 50) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 74) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  type DictionaryCompressor (line 81) | pub struct DictionaryCompressor {
    method new (line 91) | pub fn new(level: u32, dictionary: &[u8], dictionary_hash: [u8; 32]) -...
    method build_header (line 107) | fn build_header(&self) -> [u8; DCZ_HEADER_SIZE] {
  method encode (line 116) | fn encode(&mut self, input: &[u8], end: bool) -> Result<Bytes> {
  method stat (line 147) | fn stat(&self) -> (&'static str, usize, usize, Duration) {
  function compress_zstd_data (line 157) | fn compress_zstd_data() {
  constant TEST_DICTIONARY (line 176) | const TEST_DICTIONARY: &[u8] = b"The quick brown fox jumps over the lazy...
  function test_dictionary_hash (line 185) | fn test_dictionary_hash() -> [u8; 32] {
  function compress_dcz_prepends_header (line 203) | fn compress_dcz_prepends_header() {
  function compress_dcz_header_written_once (line 220) | fn compress_dcz_header_written_once() {
  function compress_dcz_stats (line 235) | fn compress_dcz_stats() {
  function compress_dcz_empty_input (line 250) | fn compress_dcz_empty_input() {
  function compress_dcz_streaming (line 260) | fn compress_dcz_streaming() {
  function compress_dcz_achieves_compression (line 281) | fn compress_dcz_achieves_compression() {
  function compress_dcz_roundtrip (line 295) | fn compress_dcz_roundtrip() {

FILE: pingora-core/src/protocols/http/conditional_filter.rs
  function not_modified_filter (line 25) | pub fn not_modified_filter(req: &RequestHeader, resp: &ResponseHeader) -...
  function trim_ascii_start (line 75) | fn trim_ascii_start(mut bytes: &[u8]) -> &[u8] {
  function weak_validate_etag (line 91) | pub fn weak_validate_etag(input_etag_header: &[u8], target_etag: &[u8]) ...
  function req_header_as_http_date (line 160) | pub fn req_header_as_http_date<H>(req: &RequestHeader, header_name: H) -...
  function resp_header_as_http_date (line 171) | pub fn resp_header_as_http_date<H>(
  function parse_bytes_as_http_date (line 184) | fn parse_bytes_as_http_date(bytes: &[u8]) -> Result<HttpDate> {
  function to_304 (line 194) | pub fn to_304(resp: &mut ResponseHeader) {
  function test_if_modified_since (line 217) | fn test_if_modified_since() {
  function test_weak_validate_etag (line 249) | fn test_weak_validate_etag() {
  function test_weak_validate_etag_unquoted (line 297) | fn test_weak_validate_etag_unquoted() {

FILE: pingora-core/src/protocols/http/custom/client.rs
  type Session (line 30) | pub trait Session: Send + Sync + Unpin + 'static {
    method write_request_header (line 31) | async fn write_request_header(&mut self, req: Box<RequestHeader>, end:...
    method write_request_body (line 33) | async fn write_request_body(&mut self, data: Bytes, end: bool) -> Resu...
    method finish_request_body (line 35) | async fn finish_request_body(&mut self) -> Result<()>;
    method set_read_timeout (line 37) | fn set_read_timeout(&mut self, timeout: Option<Duration>);
    method set_write_timeout (line 39) | fn set_write_timeout(&mut self, timeout: Option<Duration>);
    method read_response_header (line 41) | async fn read_response_header(&mut self) -> Result<()>;
    method read_response_body (line 43) | async fn read_response_body(&mut self) -> Result<Option<Bytes>>;
    method response_finished (line 45) | fn response_finished(&self) -> bool;
    method shutdown (line 47) | async fn shutdown(&mut self, code: u32, ctx: &str);
    method response_header (line 49) | fn response_header(&self) -> Option<&ResponseHeader>;
    method was_upgraded (line 51) | fn was_upgraded(&self) -> bool;
    method digest (line 53) | fn digest(&self) -> Option<&Digest>;
    method digest_mut (line 55) | fn digest_mut(&mut self) -> Option<&mut Digest>;
    method server_addr (line 57) | fn server_addr(&self) -> Option<&SocketAddr>;
    method client_addr (line 59) | fn client_addr(&self) -> Option<&SocketAddr>;
    method read_trailers (line 61) | async fn read_trailers(&mut self) -> Result<Option<HeaderMap>>;
    method fd (line 63) | fn fd(&self) -> UniqueIDType;
    method check_response_end_or_error (line 65) | async fn check_response_end_or_error(&mut self, headers: bool) -> Resu...
    method take_request_body_writer (line 67) | fn take_request_body_writer(&mut self) -> Option<Box<dyn BodyWrite>>;
    method finish_custom (line 69) | async fn finish_custom(&mut self) -> Result<()>;
    method take_custom_message_reader (line 71) | fn take_custom_message_reader(
    method drain_custom_messages (line 75) | async fn drain_custom_messages(&mut self) -> Result<()>;
    method take_custom_message_writer (line 77) | fn take_custom_message_writer(&mut self) -> Option<Box<dyn CustomMessa...
    method write_request_header (line 83) | async fn write_request_header(&mut self, _req: Box<RequestHeader>, _en...
    method write_request_body (line 87) | async fn write_request_body(&mut self, _data: Bytes, _end: bool) -> Re...
    method finish_request_body (line 91) | async fn finish_request_body(&mut self) -> Result<()> {
    method set_read_timeout (line 95) | fn set_read_timeout(&mut self, _timeout: Option<Duration>) {
    method set_write_timeout (line 99) | fn set_write_timeout(&mut self, _timeout: Option<Duration>) {
    method read_response_header (line 103) | async fn read_response_header(&mut self) -> Result<()> {
    method read_response_body (line 107) | async fn read_response_body(&mut self) -> Result<Option<Bytes>> {
    method response_finished (line 111) | fn response_finished(&self) -> bool {
    method shutdown (line 115) | async fn shutdown(&mut self, _code: u32, _ctx: &str) {
    method response_header (line 119) | fn response_header(&self) -> Option<&ResponseHeader> {
    method was_upgraded (line 123) | fn was_upgraded(&self) -> bool {
    method digest (line 127) | fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 131) | fn digest_mut(&mut self) -> Option<&mut Digest> {
    method server_addr (line 135) | fn server_addr(&self) -> Option<&SocketAddr> {
    method client_addr (line 139) | fn client_addr(&self) -> Option<&SocketAddr> {
    method finish_custom (line 143) | async fn finish_custom(&mut self) -> Result<()> {
    method read_trailers (line 147) | async fn read_trailers(&mut self) -> Result<Option<HeaderMap>> {
    method fd (line 151) | fn fd(&self) -> UniqueIDType {
    method check_response_end_or_error (line 155) | async fn check_response_end_or_error(&mut self, _headers: bool) -> Res...
    method take_custom_message_reader (line 159) | fn take_custom_message_reader(
    method drain_custom_messages (line 165) | async fn drain_custom_messages(&mut self) -> Result<()> {
    method take_custom_message_writer (line 169) | fn take_custom_message_writer(&mut self) -> Option<Box<dyn CustomMessa...
    method take_request_body_writer (line 173) | fn take_request_body_writer(&mut self) -> Option<Box<dyn BodyWrite>> {

FILE: pingora-core/src/protocols/http/custom/mod.rs
  constant CUSTOM_MESSAGE_QUEUE_SIZE (line 27) | pub const CUSTOM_MESSAGE_QUEUE_SIZE: usize = 128;
  function is_informational_except_101 (line 29) | pub fn is_informational_except_101<T: PartialOrd<u32>>(code: T) -> bool {
  type CustomMessageWrite (line 37) | pub trait CustomMessageWrite: Send + Sync + Unpin + 'static {
    method set_write_timeout (line 38) | fn set_write_timeout(&mut self, timeout: Option<Duration>);
    method write_custom_message (line 39) | async fn write_custom_message(&mut self, msg: Bytes) -> Result<()>;
    method finish_custom (line 40) | async fn finish_custom(&mut self) -> Result<()>;
    method set_write_timeout (line 46) | fn set_write_timeout(&mut self, _timeout: Option<Duration>) {}
    method write_custom_message (line 48) | async fn write_custom_message(&mut self, msg: Bytes) -> Result<()> {
    method finish_custom (line 53) | async fn finish_custom(&mut self) -> Result<()> {
  type BodyWrite (line 60) | pub trait BodyWrite: Send + Sync + Unpin + 'static {
    method write_all_buf (line 61) | async fn write_all_buf(&mut self, data: &mut Bytes) -> Result<()>;
    method finish (line 62) | async fn finish(&mut self) -> Result<()>;
    method cleanup (line 63) | async fn cleanup(&mut self) -> Result<()>;
    method upgrade_body_writer (line 64) | fn upgrade_body_writer(&mut self);
  function drain_custom_messages (line 67) | pub async fn drain_custom_messages(

FILE: pingora-core/src/protocols/http/custom/server.rs
  type Session (line 30) | pub trait Session: Send + Sync + Unpin + 'static {
    method req_header (line 31) | fn req_header(&self) -> &RequestHeader;
    method req_header_mut (line 33) | fn req_header_mut(&mut self) -> &mut RequestHeader;
    method read_body_bytes (line 35) | async fn read_body_bytes(&mut self) -> Result<Option<Bytes>>;
    method drain_request_body (line 37) | async fn drain_request_body(&mut self) -> Result<()>;
    method write_response_header (line 39) | async fn write_response_header(&mut self, resp: Box<ResponseHeader>, e...
    method write_response_header_ref (line 41) | async fn write_response_header_ref(&mut self, resp: &ResponseHeader, e...
    method write_body (line 43) | async fn write_body(&mut self, data: Bytes, end: bool) -> Result<()>;
    method write_trailers (line 45) | async fn write_trailers(&mut self, trailers: HeaderMap) -> Result<()>;
    method response_duplex_vec (line 47) | async fn response_duplex_vec(&mut self, tasks: Vec<HttpTask>) -> Resul...
    method set_read_timeout (line 49) | fn set_read_timeout(&mut self, timeout: Option<Duration>);
    method get_read_timeout (line 51) | fn get_read_timeout(&self) -> Option<Duration>;
    method set_write_timeout (line 53) | fn set_write_timeout(&mut self, timeout: Option<Duration>);
    method get_write_timeout (line 55) | fn get_write_timeout(&self) -> Option<Duration>;
    method set_total_drain_timeout (line 57) | fn set_total_drain_timeout(&mut self, timeout: Option<Duration>);
    method get_total_drain_timeout (line 59) | fn get_total_drain_timeout(&self) -> Option<Duration>;
    method request_summary (line 61) | fn request_summary(&self) -> String;
    method response_written (line 63) | fn response_written(&self) -> Option<&ResponseHeader>;
    method shutdown (line 65) | async fn shutdown(&mut self, code: u32, ctx: &str);
    method is_body_done (line 67) | fn is_body_done(&mut self) -> bool;
    method finish (line 69) | async fn finish(&mut self) -> Result<()>;
    method is_body_empty (line 71) | fn is_body_empty(&mut self) -> bool;
    method read_body_or_idle (line 73) | async fn read_body_or_idle(&mut self, no_body_expected: bool) -> Resul...
    method body_bytes_sent (line 75) | fn body_bytes_sent(&self) -> usize;
    method body_bytes_read (line 77) | fn body_bytes_read(&self) -> usize;
    method digest (line 79) | fn digest(&self) -> Option<&Digest>;
    method digest_mut (line 81) | fn digest_mut(&mut self) -> Option<&mut Digest>;
    method client_addr (line 83) | fn client_addr(&self) -> Option<&SocketAddr>;
    method server_addr (line 85) | fn server_addr(&self) -> Option<&SocketAddr>;
    method pseudo_raw_h1_request_header (line 87) | fn pseudo_raw_h1_request_header(&self) -> Bytes;
    method enable_retry_buffering (line 89) | fn enable_retry_buffering(&mut self);
    method retry_buffer_truncated (line 91) | fn retry_buffer_truncated(&self) -> bool;
    method get_retry_buffer (line 93) | fn get_retry_buffer(&self) -> Option<Bytes>;
    method finish_custom (line 95) | async fn finish_custom(&mut self) -> Result<()>;
    method take_custom_message_reader (line 97) | fn take_custom_message_reader(
    method restore_custom_message_reader (line 101) | fn restore_custom_message_reader(
    method take_custom_message_writer (line 106) | fn take_custom_message_writer(&mut self) -> Option<Box<dyn CustomMessa...
    method restore_custom_message_writer (line 108) | fn restore_custom_message_writer(&mut self, writer: Box<dyn CustomMess...
    method is_upgrade_req (line 113) | fn is_upgrade_req(&self) -> bool {
    method was_upgraded (line 120) | fn was_upgraded(&self) -> bool {
    method req_header (line 128) | fn req_header(&self) -> &RequestHeader {
    method req_header_mut (line 132) | fn req_header_mut(&mut self) -> &mut RequestHeader {
    method read_body_bytes (line 136) | async fn read_body_bytes(&mut self) -> Result<Option<Bytes>> {
    method drain_request_body (line 140) | async fn drain_request_body(&mut self) -> Result<()> {
    method write_response_header (line 144) | async fn write_response_header(
    method write_response_header_ref (line 152) | async fn write_response_header_ref(
    method write_body (line 160) | async fn write_body(&mut self, _data: Bytes, _end: bool) -> Result<()> {
    method write_trailers (line 164) | async fn write_trailers(&mut self, _trailers: HeaderMap) -> Result<()> {
    method response_duplex_vec (line 168) | async fn response_duplex_vec(&mut self, _tasks: Vec<HttpTask>) -> Resu...
    method set_read_timeout (line 172) | fn set_read_timeout(&mut self, _timeout: Option<Duration>) {
    method get_read_timeout (line 176) | fn get_read_timeout(&self) -> Option<Duration> {
    method set_write_timeout (line 180) | fn set_write_timeout(&mut self, _timeout: Option<Duration>) {
    method get_write_timeout (line 184) | fn get_write_timeout(&self) -> Option<Duration> {
    method set_total_drain_timeout (line 188) | fn set_total_drain_timeout(&mut self, _timeout: Option<Duration>) {
    method get_total_drain_timeout (line 192) | fn get_total_drain_timeout(&self) -> Option<Duration> {
    method request_summary (line 196) | fn request_summary(&self) -> String {
    method response_written (line 200) | fn response_written(&self) -> Option<&ResponseHeader> {
    method shutdown (line 204) | async fn shutdown(&mut self, _code: u32, _ctx: &str) {
    method is_body_done (line 208) | fn is_body_done(&mut self) -> bool {
    method finish (line 212) | async fn finish(&mut self) -> Result<()> {
    method is_body_empty (line 216) | fn is_body_empty(&mut self) -> bool {
    method read_body_or_idle (line 220) | async fn read_body_or_idle(&mut self, _no_body_expected: bool) -> Resu...
    method body_bytes_sent (line 224) | fn body_bytes_sent(&self) -> usize {
    method body_bytes_read (line 228) | fn body_bytes_read(&self) -> usize {
    method digest (line 232) | fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 236) | fn digest_mut(&mut self) -> Option<&mut Digest> {
    method client_addr (line 240) | fn client_addr(&self) -> Option<&SocketAddr> {
    method server_addr (line 244) | fn server_addr(&self) -> Option<&SocketAddr> {
    method pseudo_raw_h1_request_header (line 248) | fn pseudo_raw_h1_request_header(&self) -> Bytes {
    method enable_retry_buffering (line 252) | fn enable_retry_buffering(&mut self) {
    method retry_buffer_truncated (line 256) | fn retry_buffer_truncated(&self) -> bool {
    method get_retry_buffer (line 260) | fn get_retry_buffer(&self) -> Option<Bytes> {
    method finish_custom (line 264) | async fn finish_custom(&mut self) -> Result<()> {
    method take_custom_message_reader (line 268) | fn take_custom_message_reader(
    method restore_custom_message_reader (line 274) | fn restore_custom_message_reader(
    method take_custom_message_writer (line 281) | fn take_custom_message_writer(&mut self) -> Option<Box<dyn CustomMessa...
    method restore_custom_message_writer (line 285) | fn restore_custom_message_writer(
    method is_upgrade_req (line 292) | fn is_upgrade_req(&self) -> bool {
    method was_upgraded (line 296) | fn was_upgraded(&self) -> bool {

FILE: pingora-core/src/protocols/http/date.rs
  function to_date_string (line 20) | fn to_date_string(epoch_sec: i64) -> String {
  type CacheableDate (line 25) | struct CacheableDate {
    method new (line 31) | pub fn new() -> Self {
    method update (line 41) | pub fn update(&mut self, d_now: Duration) {
    method get_date (line 48) | pub fn get_date(&mut self) -> HeaderValue {
  function get_cached_date (line 62) | pub fn get_cached_date() -> HeaderValue {
  function now_date_header (line 70) | fn now_date_header() -> HeaderValue {
  function test_date_string (line 81) | fn test_date_string() {
  function test_date_cached (line 87) | fn test_date_cached() {

FILE: pingora-core/src/protocols/http/error_resp.rs
  function gen_error_response (line 26) | pub fn gen_error_response(code: u16) -> ResponseHeader {

FILE: pingora-core/src/protocols/http/mod.rs
  constant SERVER_NAME (line 33) | pub const SERVER_NAME: &[u8; 7] = b"Pingora";
  type HttpTask (line 37) | pub enum HttpTask {
    method is_end (line 54) | pub fn is_end(&self) -> bool {
    method type_str (line 66) | pub fn type_str(&self) -> &'static str {

FILE: pingora-core/src/protocols/http/server.rs
  type Session (line 33) | pub enum Session {
    method new_http1 (line 42) | pub fn new_http1(stream: Stream) -> Self {
    method new_http2 (line 47) | pub fn new_http2(session: SessionV2) -> Self {
    method new_subrequest (line 52) | pub fn new_subrequest(session: SessionSubrequest) -> Self {
    method new_custom (line 57) | pub fn new_custom(session: Box<dyn SessionCustom>) -> Self {
    method is_http2 (line 62) | pub fn is_http2(&self) -> bool {
    method is_subrequest (line 67) | pub fn is_subrequest(&self) -> bool {
    method is_custom (line 72) | pub fn is_custom(&self) -> bool {
    method read_request (line 81) | pub async fn read_request(&mut self) -> Result<bool> {
    method req_header (line 100) | pub fn req_header(&self) -> &RequestHeader {
    method req_header_mut (line 112) | pub fn req_header_mut(&mut self) -> &mut RequestHeader {
    method get_header (line 125) | pub fn get_header<K: AsHeaderName>(&self, key: K) -> Option<&HeaderVal...
    method get_header_bytes (line 131) | pub fn get_header_bytes<K: AsHeaderName>(&self, key: K) -> &[u8] {
    method read_request_body (line 136) | pub async fn read_request_body(&mut self) -> Result<Option<Bytes>> {
    method drain_request_body (line 149) | pub async fn drain_request_body(&mut self) -> Result<()> {
    method write_response_header (line 161) | pub async fn write_response_header(&mut self, resp: Box<ResponseHeader...
    method write_response_header_ref (line 177) | pub async fn write_response_header_ref(&mut self, resp: &ResponseHeade...
    method write_response_body (line 193) | pub async fn write_response_body(&mut self, data: Bytes, end: bool) ->...
    method write_response_trailers (line 220) | pub async fn write_response_trailers(&mut self, trailers: HeaderMap) -...
    method finish (line 233) | pub async fn finish(self) -> Result<Option<Stream>> {
    method on_proxy_failure (line 260) | pub fn on_proxy_failure(&mut self, e: Box<Error>) {
    method response_duplex_vec (line 270) | pub async fn response_duplex_vec(&mut self, tasks: Vec<HttpTask>) -> R...
    method set_keepalive (line 281) | pub fn set_keepalive(&mut self, duration: Option<u64>) {
    method get_keepalive (line 292) | pub fn get_keepalive(&self) -> Option<u64> {
    method set_keepalive_reuses_remaining (line 303) | pub fn set_keepalive_reuses_remaining(&mut self, reuses: Option<u32>) {
    method get_keepalive_reuses_remaining (line 312) | pub fn get_keepalive_reuses_remaining(&self) -> Option<u32> {
    method set_read_timeout (line 324) | pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
    method get_read_timeout (line 334) | pub fn get_read_timeout(&self) -> Option<Duration> {
    method set_write_timeout (line 346) | pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
    method get_write_timeout (line 356) | pub fn get_write_timeout(&self) -> Option<Duration> {
    method set_total_drain_timeout (line 371) | pub fn set_total_drain_timeout(&mut self, timeout: Option<Duration>) {
    method get_total_drain_timeout (line 381) | pub fn get_total_drain_timeout(&self) -> Option<Duration> {
    method set_min_send_rate (line 400) | pub fn set_min_send_rate(&mut self, rate: Option<usize>) {
    method set_ignore_info_resp (line 417) | pub fn set_ignore_info_resp(&mut self, ignore: bool) {
    method set_close_on_response_before_downstream_finish (line 430) | pub fn set_close_on_response_before_downstream_finish(&mut self, close...
    method request_summary (line 441) | pub fn request_summary(&self) -> String {
    method response_written (line 452) | pub fn response_written(&self) -> Option<&ResponseHeader> {
    method shutdown (line 465) | pub async fn shutdown(&mut self) {
    method to_h1_raw (line 474) | pub fn to_h1_raw(&self) -> Bytes {
    method is_body_done (line 484) | pub fn is_body_done(&mut self) -> bool {
    method finish_body (line 498) | pub async fn finish_body(&mut self) -> Result<()> {
    method generate_error (line 507) | pub fn generate_error(error: u16) -> ResponseHeader {
    method respond_error (line 517) | pub async fn respond_error(&mut self, error: u16) -> Result<()> {
    method respond_error_with_body (line 522) | pub async fn respond_error_with_body(&mut self, error: u16, body: Byte...
    method write_error_response (line 532) | pub async fn write_error_response(&mut self, resp: ResponseHeader, bod...
    method is_body_empty (line 564) | pub fn is_body_empty(&mut self) -> bool {
    method retry_buffer_truncated (line 573) | pub fn retry_buffer_truncated(&self) -> bool {
    method enable_retry_buffering (line 582) | pub fn enable_retry_buffering(&mut self) {
    method get_retry_buffer (line 591) | pub fn get_retry_buffer(&self) -> Option<Bytes> {
    method read_body_or_idle (line 602) | pub async fn read_body_or_idle(&mut self, no_body_expected: bool) -> R...
    method as_http1 (line 611) | pub fn as_http1(&self) -> Option<&SessionV1> {
    method as_http2 (line 620) | pub fn as_http2(&self) -> Option<&SessionV2> {
    method as_subrequest (line 629) | pub fn as_subrequest(&self) -> Option<&SessionSubrequest> {
    method as_subrequest_mut (line 638) | pub fn as_subrequest_mut(&mut self) -> Option<&mut SessionSubrequest> {
    method as_custom (line 647) | pub fn as_custom(&self) -> Option<&dyn SessionCustom> {
    method as_custom_mut (line 656) | pub fn as_custom_mut(&mut self) -> Option<&mut Box<dyn SessionCustom>> {
    method write_continue_response (line 666) | pub async fn write_continue_response(&mut self) -> Result<()> {
    method is_upgrade_req (line 686) | pub fn is_upgrade_req(&self) -> bool {
    method was_upgraded (line 696) | pub fn was_upgraded(&self) -> bool {
    method body_bytes_sent (line 706) | pub fn body_bytes_sent(&self) -> usize {
    method body_bytes_read (line 716) | pub fn body_bytes_read(&self) -> usize {
    method digest (line 726) | pub fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 738) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
    method client_addr (line 748) | pub fn client_addr(&self) -> Option<&SocketAddr> {
    method server_addr (line 758) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method stream (line 769) | pub fn stream(&self) -> Option<&Stream> {

FILE: pingora-core/src/protocols/http/subrequest/body.rs
  constant PREMATURE_BODY_END (line 35) | pub const PREMATURE_BODY_END: ErrorType = ErrorType::new("PrematureBodyE...
  type ParseState (line 38) | pub enum ParseState {
  type PS (line 46) | type PS = ParseState;
  type BodyReader (line 48) | pub struct BodyReader {
    method new (line 54) | pub fn new(notify_wants_body: Option<oneshot::Sender<()>>) -> Self {
    method need_init (line 62) | pub fn need_init(&self) -> bool {
    method init_content_length (line 66) | pub fn init_content_length(&mut self, cl: usize) {
    method init_close_delimited (line 75) | pub fn init_close_delimited(&mut self) {
    method convert_to_close_delimited (line 81) | pub fn convert_to_close_delimited(&mut self) {
    method body_done (line 91) | pub fn body_done(&self) -> bool {
    method body_empty (line 95) | pub fn body_empty(&self) -> bool {
    method read_body (line 99) | pub async fn read_body(&mut self, rx: &mut mpsc::Receiver<HttpTask>) -...
    method do_read_body (line 109) | pub async fn do_read_body(
    method do_read_body_until_closed (line 169) | pub async fn do_read_body_until_closed(
  type BodyMode (line 209) | pub enum BodyMode {
  type BM (line 216) | type BM = BodyMode;
  type BodyWriter (line 218) | pub struct BodyWriter {
    method new (line 223) | pub fn new() -> Self {
    method init_close_delimited (line 229) | pub fn init_close_delimited(&mut self) {
    method init_content_length (line 233) | pub fn init_content_length(&mut self, cl: usize) {
    method write_body (line 237) | pub async fn write_body(
    method finished (line 251) | pub fn finished(&self) -> bool {
    method do_write_body (line 259) | async fn do_write_body(
    method do_write_until_close_body (line 289) | async fn do_write_until_close_body(
    method finish (line 309) | pub async fn finish(&mut self, sender: &mut mpsc::Sender<HttpTask>) ->...
    method do_finish_body (line 318) | async fn do_finish_body(&mut self, tx: &mut mpsc::Sender<HttpTask>) ->...
    method do_finish_until_close_body (line 338) | async fn do_finish_until_close_body(
    method write_trailers (line 355) | pub async fn write_trailers(
  function init_log (line 373) | fn init_log() {
  constant TASK_BUFFER_SIZE (line 377) | const TASK_BUFFER_SIZE: usize = 4;
  function read_with_body_content_length (line 380) | async fn read_with_body_content_length() {
  function read_with_body_content_length_2 (line 397) | async fn read_with_body_content_length_2() {
  function read_with_body_content_length_empty_task (line 422) | async fn read_with_body_content_length_empty_task() {
  function read_with_body_content_length_less (line 456) | async fn read_with_body_content_length_less() {
  function read_with_body_content_length_more (line 478) | async fn read_with_body_content_length_more() {
  function read_with_body_until_close (line 503) | async fn read_with_body_until_close() {
  function write_body_cl (line 533) | async fn write_body_cl() {
  function write_body_until_close (line 576) | async fn write_body_until_close() {

FILE: pingora-core/src/protocols/http/subrequest/dummy.rs
  type DummyIO (line 30) | pub(crate) struct DummyIO(Cursor<Vec<u8>>);
    method new (line 33) | pub fn new(read_bytes: &[u8]) -> Self {
    method shutdown (line 100) | async fn shutdown(&mut self) -> () {}
  method poll_read (line 39) | fn poll_read(
  method poll_write (line 54) | fn poll_write(
  method poll_flush (line 62) | fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Resul...
  method poll_shutdown (line 65) | fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Re...
  method id (line 71) | fn id(&self) -> UniqueIDType {
  method get_timing_digest (line 79) | fn get_timing_digest(&self) -> Vec<Option<TimingDigest>> {
  method get_proxy_digest (line 85) | fn get_proxy_digest(&self) -> Option<Arc<ProxyDigest>> {
  method get_socket_digest (line 91) | fn get_socket_digest(&self) -> Option<Arc<SocketDigest>> {
  function test_dummy_io (line 104) | async fn test_dummy_io() {

FILE: pingora-core/src/protocols/http/subrequest/server.rs
  type HttpSession (line 57) | pub struct HttpSession {
    method new_from_session (line 108) | pub fn new_from_session(session: &GenericHttpSession) -> (Self, Subreq...
    method read_request (line 147) | pub async fn read_request(&mut self) -> Result<Option<usize>> {
    method validate_request (line 166) | pub fn validate_request(&self) -> Result<()> {
    method req_header (line 173) | pub fn req_header(&self) -> &RequestHeader {
    method req_header_mut (line 180) | pub fn req_header_mut(&mut self) -> &mut RequestHeader {
    method get_header (line 187) | pub fn get_header(&self, name: impl AsHeaderName) -> Option<&HeaderVal...
    method get_method (line 192) | pub(super) fn get_method(&self) -> Option<&http::Method> {
    method get_path (line 198) | pub(super) fn get_path(&self) -> &[u8] {
    method get_host (line 203) | pub(super) fn get_host(&self) -> &[u8] {
    method request_summary (line 208) | pub fn request_summary(&self) -> String {
    method is_upgrade_req (line 218) | pub fn is_upgrade_req(&self) -> bool {
    method get_header_bytes (line 223) | pub fn get_header_bytes(&self, name: impl AsHeaderName) -> &[u8] {
    method read_body_bytes (line 228) | pub async fn read_body_bytes(&mut self) -> Result<Option<Bytes>> {
    method do_read_body (line 238) | async fn do_read_body(&mut self) -> Result<Option<Bytes>> {
    method read_body (line 246) | async fn read_body(&mut self) -> Result<Option<Bytes>> {
    method do_drain_request_body (line 259) | async fn do_drain_request_body(&mut self) -> Result<()> {
    method drain_request_body (line 270) | pub async fn drain_request_body(&mut self) -> Result<()> {
    method is_body_done (line 287) | pub fn is_body_done(&mut self) -> bool {
    method is_body_empty (line 296) | pub fn is_body_empty(&mut self) -> bool {
    method write_response_header (line 303) | pub async fn write_response_header(&mut self, header: Box<ResponseHead...
    method response_written (line 371) | pub fn response_written(&self) -> Option<&ResponseHeader> {
    method is_upgrade (line 378) | pub fn is_upgrade(&self, header: &ResponseHeader) -> Option<bool> {
    method was_upgraded (line 388) | pub fn was_upgraded(&self) -> bool {
    method init_body_writer (line 392) | fn init_body_writer(&mut self, header: &ResponseHeader) {
    method write_response_header_ref (line 431) | pub async fn write_response_header_ref(&mut self, resp: &ResponseHeade...
    method do_write_body (line 435) | async fn do_write_body(&mut self, buf: Bytes) -> Result<Option<usize>> {
    method write_body (line 450) | pub async fn write_body(&mut self, buf: Bytes) -> Result<Option<usize>> {
    method maybe_force_close_body_reader (line 461) | fn maybe_force_close_body_reader(&mut self) {
    method finish (line 472) | pub async fn finish(&mut self) -> Result<Option<usize>> {
    method on_proxy_failure (line 490) | pub fn on_proxy_failure(&mut self, e: Box<Error>) {
    method body_bytes_sent (line 498) | pub fn body_bytes_sent(&self) -> usize {
    method body_bytes_read (line 503) | pub fn body_bytes_read(&self) -> usize {
    method is_chunked_encoding (line 507) | fn is_chunked_encoding(&self) -> bool {
    method clear_request_body_headers (line 515) | pub fn clear_request_body_headers(&mut self) {
    method init_body_reader (line 526) | fn init_body_reader(&mut self) {
    method retry_buffer_truncated (line 560) | pub fn retry_buffer_truncated(&self) -> bool {
    method enable_retry_buffering (line 566) | pub fn enable_retry_buffering(&mut self) {
    method get_retry_buffer (line 572) | pub fn get_retry_buffer(&self) -> Option<Bytes> {
    method idle (line 583) | pub async fn idle(&mut self) -> Result<HttpTask> {
    method read_body_or_idle (line 605) | pub async fn read_body_or_idle(&mut self, no_body_expected: bool) -> R...
    method get_headers_raw_bytes (line 618) | pub fn get_headers_raw_bytes(&self) -> Bytes {
    method shutdown (line 624) | pub fn shutdown(&mut self) {
    method set_read_timeout (line 631) | pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
    method get_read_timeout (line 636) | pub fn get_read_timeout(&self) -> Option<Duration> {
    method set_write_timeout (line 642) | pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
    method get_write_timeout (line 647) | pub fn get_write_timeout(&self) -> Option<Duration> {
    method set_total_drain_timeout (line 653) | pub fn set_total_drain_timeout(&mut self, timeout: Option<Duration>) {
    method get_total_drain_timeout (line 658) | pub fn get_total_drain_timeout(&self) -> Option<Duration> {
    method digest (line 663) | pub fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 668) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
    method client_addr (line 673) | pub fn client_addr(&self) -> Option<&SocketAddr> {
    method server_addr (line 680) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method write_continue_response (line 687) | pub async fn write_continue_response(&mut self) -> Result<()> {
    method write_non_empty_body (line 698) | async fn write_non_empty_body(&mut self, data: Option<Bytes>, upgraded...
    method response_duplex (line 716) | async fn response_duplex(&mut self, task: HttpTask) -> Result<bool> {
    method response_duplex_vec (line 747) | pub async fn response_duplex_vec(&mut self, mut tasks: Vec<HttpTask>) ...
    method write_trailers (line 795) | pub async fn write_trailers(&mut self, trailers: Option<Box<HeaderMap>...
  type SubrequestHandle (line 82) | pub struct SubrequestHandle {
    method drain_tasks (line 95) | pub fn drain_tasks(mut self) -> tokio::task::JoinHandle<()> {
  function init_log (line 816) | fn init_log() {
  function session_from_input (line 820) | async fn session_from_input(input: &[u8]) -> (HttpSession, SubrequestHan...
  function build_upgrade_req (line 829) | async fn build_upgrade_req(upgrade: &str, conn: &str) -> (HttpSession, S...
  function build_req (line 834) | async fn build_req() -> (HttpSession, SubrequestHandle) {
  function read_basic (line 840) | async fn read_basic() {
  function read_upgrade_req (line 850) | async fn read_upgrade_req() {
  function read_upgrade_req_with_1xx_response (line 880) | async fn read_upgrade_req_with_1xx_response() {
  function write (line 894) | async fn write() {
  function write_informational (line 913) | async fn write_informational() {
  function write_101_switching_protocol (line 943) | async fn write_101_switching_protocol() {
  function write_body_cl (line 989) | async fn write_body_cl() {
  function write_body_until_close (line 1013) | async fn write_body_until_close() {
  function read_with_illegal (line 1033) | async fn read_with_illegal() {
  function test_write_body_write_timeout (line 1056) | async fn test_write_body_write_timeout() {
  function test_write_continue_resp (line 1082) | async fn test_write_continue_resp() {
  function session_from_input_no_validate (line 1094) | async fn session_from_input_no_validate(input: &[u8]) -> (HttpSession, S...
  function validate_request_rejects_invalid_content_length (line 1111) | async fn validate_request_rejects_invalid_content_length(#[case] invalid...
  function validate_request_accepts_valid_content_length (line 1133) | async fn validate_request_accepts_valid_content_length(#[case] valid_val...
  function validate_request_accepts_no_content_length (line 1145) | async fn validate_request_accepts_no_content_length() {
  constant POST_CL_UPGRADE_REQ (line 1153) | const POST_CL_UPGRADE_REQ: &[u8] = b"POST / HTTP/1.1\r\nHost: pingora.or...
  constant POST_CHUNKED_UPGRADE_REQ (line 1154) | const POST_CHUNKED_UPGRADE_REQ: &[u8] = b"POST / HTTP/1.1\r\nHost: pingo...
  constant POST_BODY_DATA (line 1155) | const POST_BODY_DATA: &[u8] = b"abcdefghij";
  function build_upgrade_req_with_body (line 1157) | async fn build_upgrade_req_with_body(header: &[u8]) -> (HttpSession, Sub...
  function read_upgrade_req_with_body (line 1170) | async fn read_upgrade_req_with_body(#[case] header: &[u8]) {

FILE: pingora-core/src/protocols/http/v1/body.rs
  constant BODY_BUFFER_SIZE (line 29) | const BODY_BUFFER_SIZE: usize = 1024 * 64;
  constant PARTIAL_CHUNK_HEAD_LIMIT (line 31) | const PARTIAL_CHUNK_HEAD_LIMIT: usize = 1024 * 8;
  constant TRAILER_SIZE_LIMIT (line 36) | const TRAILER_SIZE_LIMIT: usize = 1024 * 64;
  constant LAST_CHUNK (line 38) | const LAST_CHUNK: &[u8; 5] = &[b'0', CR, LF, CR, LF];
  constant CR (line 39) | const CR: u8 = b'\r';
  constant LF (line 40) | const LF: u8 = b'\n';
  constant CRLF (line 41) | const CRLF: &[u8; 2] = &[CR, LF];
  constant TRAILERS_END (line 43) | const TRAILERS_END: &[u8; 4] = &[CR, LF, CR, LF];
  constant INVALID_CHUNK (line 45) | pub const INVALID_CHUNK: ErrorType = ErrorType::new("InvalidChunk");
  constant INVALID_TRAILER_END (line 46) | pub const INVALID_TRAILER_END: ErrorType = ErrorType::new("InvalidTraile...
  constant PREMATURE_BODY_END (line 47) | pub const PREMATURE_BODY_END: ErrorType = ErrorType::new("PrematureBodyE...
  type ParseState (line 50) | pub enum ParseState {
    method finish (line 71) | pub fn finish(&self, additional_bytes: usize) -> Self {
    method done (line 81) | pub fn done(&self, additional_bytes: usize) -> Self {
    method read_final_chunk (line 91) | pub fn read_final_chunk(&self, remaining_buf_size: usize) -> Self {
    method partial_chunk (line 109) | pub fn partial_chunk(&self, bytes_read: usize, bytes_to_read: usize) -...
    method multi_chunk (line 117) | pub fn multi_chunk(&self, bytes_read: usize, buf_start_index: usize) -...
    method partial_chunk_head (line 127) | pub fn partial_chunk_head(&self, head_end: usize, head_size: usize) ->...
    method new_buf (line 136) | pub fn new_buf(&self, buf_end: usize) -> Self {
  type PS (line 68) | type PS = ParseState;
  type BodyReader (line 145) | pub struct BodyReader {
    method new (line 155) | pub fn new(upstream: bool) -> Self {
    method need_init (line 166) | pub fn need_init(&self) -> bool {
    method reinit (line 170) | pub fn reinit(&mut self) {
    method prepare_buf (line 174) | fn prepare_buf(&mut self, buf_to_rewind: &[u8]) {
    method init_chunked (line 190) | pub fn init_chunked(&mut self, buf_to_rewind: &[u8]) {
    method init_content_length (line 195) | pub fn init_content_length(&mut self, cl: usize, buf_to_rewind: &[u8]) {
    method init_close_delimited (line 213) | pub fn init_close_delimited(&mut self, buf_to_rewind: &[u8]) {
    method convert_to_close_delimited (line 222) | pub fn convert_to_close_delimited(&mut self) {
    method get_body (line 238) | pub fn get_body(&self, buf_ref: &BufRef) -> &[u8] {
    method get_body_overread (line 244) | pub fn get_body_overread(&self) -> Option<&[u8]> {
    method has_bytes_overread (line 248) | pub fn has_bytes_overread(&self) -> bool {
    method body_done (line 252) | pub fn body_done(&self) -> bool {
    method body_empty (line 256) | pub fn body_empty(&self) -> bool {
    method finish_body_buf (line 260) | fn finish_body_buf(&mut self, end_of_body: usize, total_read: usize) {
    method read_body (line 268) | pub async fn read_body<S>(&mut self, stream: &mut S) -> Result<Option<...
    method do_read_body (line 283) | pub async fn do_read_body<S>(&mut self, stream: &mut S) -> Result<Opti...
    method do_read_body_until_closed (line 337) | pub async fn do_read_body_until_closed<S>(&mut self, stream: &mut S) -...
    method do_read_chunked_body (line 365) | pub async fn do_read_chunked_body<S>(&mut self, stream: &mut S) -> Res...
    method parse_chunked_buf (line 514) | fn parse_chunked_buf(
    method do_read_chunked_body_final (line 635) | pub async fn do_read_chunked_body_final<S>(&mut self, stream: &mut S) ...
    method parse_trailers_end (line 746) | fn parse_trailers_end(
    method validate_crlf (line 824) | fn validate_crlf(
  type TrailersEndParseState (line 892) | pub enum TrailersEndParseState {
  type BodyMode (line 898) | pub enum BodyMode {
  type BM (line 906) | type BM = BodyMode;
  type BodyWriter (line 908) | pub struct BodyWriter {
    method new (line 913) | pub fn new() -> Self {
    method init_chunked (line 919) | pub fn init_chunked(&mut self) {
    method init_close_delimited (line 923) | pub fn init_close_delimited(&mut self) {
    method init_content_length (line 927) | pub fn init_content_length(&mut self, cl: usize) {
    method convert_to_close_delimited (line 931) | pub fn convert_to_close_delimited(&mut self) {
    method write_body (line 949) | pub async fn write_body<S>(&mut self, stream: &mut S, buf: &[u8]) -> R...
    method finished (line 963) | pub fn finished(&self) -> bool {
    method is_close_delimited (line 971) | pub fn is_close_delimited(&self) -> bool {
    method do_write_body (line 975) | async fn do_write_body<S>(&mut self, stream: &mut S, buf: &[u8]) -> Re...
    method do_write_chunked_body (line 1007) | async fn do_write_chunked_body<S>(
    method do_write_until_close_body (line 1033) | async fn do_write_until_close_body<S>(
    method finish (line 1057) | pub async fn finish<S>(&mut self, stream: &mut S) -> Result<Option<usi...
    method do_finish_body (line 1070) | fn do_finish_body<S>(&mut self, _stream: S) -> Result<Option<usize>> {
    method do_finish_chunked_body (line 1086) | async fn do_finish_chunked_body<S>(&mut self, stream: &mut S) -> Resul...
    method do_finish_until_close_body (line 1103) | fn do_finish_until_close_body<S>(&mut self, _stream: &mut S) -> Result...
  function init_log (line 1119) | fn init_log() {
  function read_with_body_content_length (line 1124) | async fn read_with_body_content_length() {
  function read_with_body_content_length_2 (line 1138) | async fn read_with_body_content_length_2() {
  function read_with_body_content_length_less (line 1157) | async fn read_with_body_content_length_less() {
  function read_with_body_content_length_more (line 1175) | async fn read_with_body_content_length_more() {
  function read_with_body_content_length_overread (line 1198) | async fn read_with_body_content_length_overread() {
  function read_with_body_content_length_rewind (line 1217) | async fn read_with_body_content_length_rewind() {
  function read_with_body_http10 (line 1236) | async fn read_with_body_http10() {
  function read_with_body_http10_rewind (line 1254) | async fn read_with_body_http10_rewind() {
  function read_with_body_zero_chunk (line 1277) | async fn read_with_body_zero_chunk() {
  function read_with_body_zero_chunk_malformed (line 1290) | async fn read_with_body_zero_chunk_malformed() {
  function read_with_body_zero_chunk_split (line 1308) | async fn read_with_body_zero_chunk_split() {
  function read_with_body_zero_chunk_split_head (line 1325) | async fn read_with_body_zero_chunk_split_head() {
  function read_with_body_zero_chunk_split_head_2 (line 1350) | async fn read_with_body_zero_chunk_split_head_2() {
  function read_with_body_zero_chunk_split_head_3 (line 1375) | async fn read_with_body_zero_chunk_split_head_3() {
  function read_with_body_chunk_ext (line 1406) | async fn read_with_body_chunk_ext() {
  function read_with_body_chunk_ext_oversize (line 1419) | async fn read_with_body_chunk_ext_oversize() {
  function read_with_body_1_chunk (line 1445) | async fn read_with_body_1_chunk() {
  function read_with_body_1_chunk_malformed (line 1463) | async fn read_with_body_1_chunk_malformed() {
  function read_with_body_1_chunk_partial_end (line 1478) | async fn read_with_body_1_chunk_partial_end() {
  function read_with_body_1_chunk_partial_end_1 (line 1499) | async fn read_with_body_1_chunk_partial_end_1() {
  function read_with_body_1_chunk_partial_end_2 (line 1528) | async fn read_with_body_1_chunk_partial_end_2() {
  function read_with_body_1_chunk_incomplete (line 1557) | async fn read_with_body_1_chunk_incomplete() {
  function read_with_body_1_chunk_partial_end_malformed (line 1573) | async fn read_with_body_1_chunk_partial_end_malformed() {
  function read_with_body_1_chunk_rewind (line 1591) | async fn read_with_body_1_chunk_rewind() {
  function read_with_body_multi_chunk (line 1614) | async fn read_with_body_multi_chunk() {
  function read_with_body_multi_chunk_malformed (line 1636) | async fn read_with_body_multi_chunk_malformed() {
  function read_with_body_partial_chunk (line 1665) | async fn read_with_body_partial_chunk() {
  function read_with_body_partial_chunk_end (line 1687) | async fn read_with_body_partial_chunk_end() {
  function read_with_body_partial_head_chunk (line 1710) | async fn read_with_body_partial_head_chunk() {
  function read_with_body_partial_head_terminal_crlf (line 1731) | async fn read_with_body_partial_head_terminal_crlf() {
  function read_with_body_partial_head_terminal_crlf_2 (line 1765) | async fn read_with_body_partial_head_terminal_crlf_2() {
  function read_with_body_partial_head_terminal_crlf_3 (line 1795) | async fn read_with_body_partial_head_terminal_crlf_3() {
  function read_with_body_partial_head_terminal_crlf_malformed (line 1840) | async fn read_with_body_partial_head_terminal_crlf_malformed() {
  function read_with_body_partial_head_terminal_crlf_overread (line 1873) | async fn read_with_body_partial_head_terminal_crlf_overread() {
  function read_with_body_multi_chunk_overread (line 1907) | async fn read_with_body_multi_chunk_overread() {
  function read_with_body_partial_head_chunk_incomplete (line 1929) | async fn read_with_body_partial_head_chunk_incomplete() {
  function read_with_body_trailers (line 1944) | async fn read_with_body_trailers() {
  function read_with_body_trailers_2 (line 2001) | async fn read_with_body_trailers_2() {
  function read_with_body_trailers_3 (line 2031) | async fn read_with_body_trailers_3() {
  function read_with_body_trailers_4 (line 2070) | async fn read_with_body_trailers_4() {
  function read_with_body_trailers_malformed (line 2112) | async fn read_with_body_trailers_malformed() {
  function read_with_body_trailers_malformed_2 (line 2139) | async fn read_with_body_trailers_malformed_2() {
  function read_with_body_trailers_malformed_3 (line 2170) | async fn read_with_body_trailers_malformed_3() {
  function read_with_body_trailers_overflow (line 2202) | async fn read_with_body_trailers_overflow() {
  function write_body_cl (line 2260) | async fn write_body_cl() {
  function write_body_chunked (line 2287) | async fn write_body_chunked() {
  function write_body_http10 (line 2322) | async fn write_body_http10() {

FILE: pingora-core/src/protocols/http/v1/client.rs
  type HttpSession (line 35) | pub struct HttpSession {
    method new (line 73) | pub fn new(stream: Stream) -> Self {
    method new_with_options (line 104) | pub fn new_with_options<P: crate::upstreams::peer::Peer>(stream: Strea...
    method write_request_header (line 117) | pub async fn write_request_header(&mut self, req: Box<RequestHeader>) ...
    method do_write_body (line 150) | async fn do_write_body(&mut self, buf: &[u8]) -> Result<Option<usize>> {
    method write_body (line 165) | pub async fn write_body(&mut self, buf: &[u8]) -> Result<Option<usize>> {
    method maybe_force_close_body_reader (line 176) | fn maybe_force_close_body_reader(&mut self) {
    method finish_body (line 185) | pub async fn finish_body(&mut self) -> Result<Option<usize>> {
    method validate_response (line 198) | fn validate_response(&self) -> Result<()> {
    method read_response (line 219) | pub async fn read_response(&mut self) -> Result<usize> {
    method read_resp_header_parts (line 396) | pub async fn read_resp_header_parts(&mut self) -> Result<Box<ResponseH...
    method resp_header (line 403) | pub fn resp_header(&self) -> Option<&ResponseHeader> {
    method get_header (line 411) | pub fn get_header(&self, name: impl AsHeaderName) -> Option<&HeaderVal...
    method get_header_bytes (line 418) | pub fn get_header_bytes(&self, name: impl AsHeaderName) -> &[u8] {
    method get_status (line 423) | pub fn get_status(&self) -> Option<StatusCode> {
    method do_read_body (line 427) | async fn do_read_body(&mut self) -> Result<Option<BufRef>> {
    method read_body_ref (line 437) | pub async fn read_body_ref(&mut self) -> Result<Option<&[u8]>> {
    method read_body_bytes (line 456) | pub async fn read_body_bytes(&mut self) -> Result<Option<Bytes>> {
    method body_bytes_received (line 462) | pub fn body_bytes_received(&self) -> usize {
    method is_body_done (line 467) | pub fn is_body_done(&mut self) -> bool {
    method set_allow_h1_response_invalid_content_length (line 472) | pub fn set_allow_h1_response_invalid_content_length(&mut self, allow: ...
    method get_headers_raw (line 476) | pub(super) fn get_headers_raw(&self) -> &[u8] {
    method get_headers_raw_bytes (line 482) | pub fn get_headers_raw_bytes(&self) -> Bytes {
    method set_keepalive (line 486) | fn set_keepalive(&mut self, seconds: Option<u64>) {
    method respect_keepalive (line 504) | pub fn respect_keepalive(&mut self) {
    method will_keepalive (line 554) | pub fn will_keepalive(&self) -> bool {
    method is_connection_keepalive (line 558) | fn is_connection_keepalive(&self) -> Option<bool> {
    method get_keepalive_values (line 575) | fn get_keepalive_values(&self) -> (Option<u64>, Option<usize>) {
    method shutdown (line 601) | pub async fn shutdown(&mut self) {
    method reuse (line 610) | pub async fn reuse(mut self) -> Option<Stream> {
    method init_body_reader (line 623) | fn init_body_reader(&mut self) {
    method is_upgrade_req (line 669) | pub fn is_upgrade_req(&self) -> bool {
    method is_upgrade (line 679) | fn is_upgrade(&self, header: &ResponseHeader) -> Option<bool> {
    method was_upgraded (line 691) | pub fn was_upgraded(&self) -> bool {
    method maybe_upgrade_body_writer (line 697) | pub fn maybe_upgrade_body_writer(&mut self) {
    method get_content_length (line 704) | fn get_content_length(&self) -> Result<Option<usize>> {
    method is_chunked_encoding (line 711) | fn is_chunked_encoding(&self) -> bool {
    method init_req_body_writer (line 717) | fn init_req_body_writer(&mut self, header: &RequestHeader) {
    method init_body_writer_comm (line 721) | fn init_body_writer_comm(&mut self, headers: &HMap) {
    method should_read_resp_header (line 744) | fn should_read_resp_header(&self) -> bool {
    method read_response_task (line 753) | pub async fn read_response_task(&mut self) -> Result<HttpTask> {
    method digest (line 789) | pub fn digest(&self) -> &Digest {
    method digest_mut (line 794) | pub fn digest_mut(&mut self) -> &mut Digest {
    method server_addr (line 799) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method client_addr (line 807) | pub fn client_addr(&self) -> Option<&SocketAddr> {
    method stream (line 815) | pub fn stream(&self) -> &Stream {
    method into_inner (line 822) | pub fn into_inner(self) -> Stream {
  function parse_resp_buffer (line 828) | fn parse_resp_buffer<'buf>(
  function http_req_header_to_wire (line 849) | pub fn http_req_header_to_wire(req: &RequestHeader) -> Option<BytesMut> {
  method id (line 878) | fn id(&self) -> UniqueIDType {
  function init_log (line 892) | fn init_log() {
  function read_basic_response (line 897) | async fn read_basic_response() {
  function read_response_custom_reason (line 908) | async fn read_response_custom_reason() {
  function read_response_default (line 922) | async fn read_response_default() {
  function body_bytes_received_content_length (line 947) | async fn body_bytes_received_content_length() {
  function body_bytes_received_chunked (line 965) | async fn body_bytes_received_chunked() {
  function h1_body_bytes_received_http10_until_close (line 984) | async fn h1_body_bytes_received_http10_until_close() {
  function h1_body_bytes_received_chunked_multi (line 1002) | async fn h1_body_bytes_received_chunked_multi() {
  function h1_body_bytes_received_preread_in_header_buf (line 1021) | async fn h1_body_bytes_received_preread_in_header_buf() {
  function h1_body_bytes_received_overread_content_length (line 1038) | async fn h1_body_bytes_received_overread_content_length() {
  function h1_body_bytes_received_after_100_continue (line 1058) | async fn h1_body_bytes_received_after_100_continue() {
  function read_response_overread (line 1093) | async fn read_response_overread() {
  function read_resp_header_with_space (line 1117) | async fn read_resp_header_with_space() {
  function read_resp_header_with_utf8 (line 1130) | async fn read_resp_header_with_utf8() {
  function read_timeout (line 1143) | async fn read_timeout() {
  function read_2_buf (line 1157) | async fn read_2_buf() {
  function read_invalid (line 1178) | async fn read_invalid() {
  function write (line 1188) | async fn write() {
  function validate_response_rejects_invalid_content_length (line 1209) | async fn validate_response_rejects_invalid_content_length(#[case] invali...
  function allow_invalid_content_length_close_delimited_when_configured (line 1224) | async fn allow_invalid_content_length_close_delimited_when_configured() {
  function validate_response_accepts_valid_content_length (line 1259) | async fn validate_response_accepts_valid_content_length(#[case] valid_va...
  function validate_response_accepts_no_content_length (line 1272) | async fn validate_response_accepts_no_content_length() {
  function response_transfer_encoding_and_content_length_handling (line 1292) | async fn response_transfer_encoding_and_content_length_handling(
  function write_timeout (line 1335) | async fn write_timeout() {
  function write_body_timeout (line 1353) | async fn write_body_timeout() {
  function write_invalid_path (line 1377) | async fn write_invalid_path() {
  function read_informational (line 1391) | async fn read_informational() {
  function read_informational_combined_with_final (line 1423) | async fn read_informational_combined_with_final() {
  function read_informational_multiple_combined_with_final (line 1466) | async fn read_informational_multiple_combined_with_final() {
  function read_informational_then_keepalive_response (line 1510) | async fn read_informational_then_keepalive_response() {
  function init_body_for_upgraded_req (line 1578) | async fn init_body_for_upgraded_req() {
  function init_preread_body_for_upgraded_req (line 1643) | async fn init_preread_body_for_upgraded_req() {
  function read_body_eos_after_upgrade (line 1706) | async fn read_body_eos_after_upgrade() {
  function read_switching_protocol (line 1783) | async fn read_switching_protocol() {
  function read_obsolete_multiline_headers (line 1851) | async fn read_obsolete_multiline_headers() {
  function read_headers_skip_invalid_line (line 1879) | async fn read_headers_skip_invalid_line() {
  function read_keepalive_headers (line 1891) | async fn read_keepalive_headers() {
  function test_http10_response_with_transfer_encoding_disables_keepalive (line 2042) | async fn test_http10_response_with_transfer_encoding_disables_keepalive() {
  function test_http11_response_with_transfer_encoding_allows_keepalive (line 2063) | async fn test_http11_response_with_transfer_encoding_allows_keepalive() {
  function test_response_multiple_transfer_encoding_headers (line 2082) | async fn test_response_multiple_transfer_encoding_headers() {
  function test_response_multiple_te_headers_chunked_not_last (line 2108) | async fn test_response_multiple_te_headers_chunked_not_last() {
  function test_is_chunked_encoding_before_response (line 2127) | fn test_is_chunked_encoding_before_response() {
  function write_request_body_implicit_zero_content_length (line 2137) | async fn write_request_body_implicit_zero_content_length() {
  function write_request_body_with_content_length (line 2156) | async fn write_request_body_with_content_length() {
  function close_delimited_response_explicitly_disables_keepalive (line 2183) | async fn close_delimited_response_explicitly_disables_keepalive() {
  function test_request_to_wire (line 2235) | fn test_request_to_wire() {

FILE: pingora-core/src/protocols/http/v1/common.rs
  constant MAX_HEADERS (line 27) | pub(super) const MAX_HEADERS: usize = 256;
  constant INIT_HEADER_BUF_SIZE (line 29) | pub(super) const INIT_HEADER_BUF_SIZE: usize = 4096;
  constant MAX_HEADER_SIZE (line 30) | pub(super) const MAX_HEADER_SIZE: usize = 1048575;
  constant BODY_BUF_LIMIT (line 32) | pub(crate) const BODY_BUF_LIMIT: usize = 1024 * 64;
  constant CRLF (line 34) | pub const CRLF: &[u8; 2] = b"\r\n";
  constant HEADER_KV_DELIMITER (line 35) | pub const HEADER_KV_DELIMITER: &[u8; 2] = b": ";
  type HeaderParseState (line 37) | pub(super) enum HeaderParseState {
  type KeepaliveStatus (line 44) | pub(super) enum KeepaliveStatus {
  type ConnectionValue (line 50) | struct ConnectionValue {
    method new (line 57) | fn new() -> Self {
    method close (line 65) | fn close(mut self) -> Self {
    method upgrade (line 69) | fn upgrade(mut self) -> Self {
    method keep_alive (line 73) | fn keep_alive(mut self) -> Self {
  function parse_connection_header (line 79) | fn parse_connection_header(value: &[u8]) -> ConnectionValue {
  function init_body_writer_comm (line 123) | pub(crate) fn init_body_writer_comm(body_writer: &mut BodyWriter, header...
  function find_last_te_token (line 145) | fn find_last_te_token(bytes: &[u8]) -> &[u8] {
  function is_chunked_encoding_from_headers (line 156) | pub(crate) fn is_chunked_encoding_from_headers(headers: &HMap) -> bool {
  function is_upgrade_req (line 178) | pub fn is_upgrade_req(req: &RequestHeader) -> bool {
  function is_expect_continue_req (line 182) | pub fn is_expect_continue_req(req: &RequestHeader) -> bool {
  function is_upgrade_resp (line 194) | pub fn is_upgrade_resp(header: &ResponseHeader) -> bool {
  function header_value_content_length (line 199) | pub fn header_value_content_length(
  function buf_to_content_length (line 209) | pub(super) fn buf_to_content_length(header_value: Option<&[u8]>) -> Resu...
  function is_buf_keepalive (line 245) | pub(super) fn is_buf_keepalive(header_value: Option<&HeaderValue>) -> Op...
  function populate_headers (line 259) | pub(super) fn populate_headers(
  function check_dup_content_length (line 285) | pub(super) fn check_dup_content_length(headers: &HMap) -> Result<()> {
  function test_check_dup_content_length (line 315) | fn test_check_dup_content_length() {
  function test_is_upgrade_resp (line 331) | fn test_is_upgrade_resp() {
  function test_is_chunked_encoding_from_headers_empty (line 351) | fn test_is_chunked_encoding_from_headers_empty() {
  function test_is_chunked_encoding_single_header (line 366) | fn test_is_chunked_encoding_single_header(#[case] value: &str, #[case] e...
  function test_is_chunked_encoding_multiple_headers (line 382) | fn test_is_chunked_encoding_multiple_headers(#[case] values: &[&str], #[...

FILE: pingora-core/src/protocols/http/v1/server.rs
  type HttpSession (line 40) | pub struct HttpSession {
    method new (line 95) | pub fn new(underlying_stream: Stream) -> Self {
    method read_request (line 135) | pub async fn read_request(&mut self) -> Result<Option<usize>> {
    method validate_request (line 328) | pub fn validate_request(&self) -> Result<()> {
    method req_header (line 359) | pub fn req_header(&self) -> &RequestHeader {
    method req_header_mut (line 368) | pub fn req_header_mut(&mut self) -> &mut RequestHeader {
    method get_header (line 377) | pub fn get_header(&self, name: impl AsHeaderName) -> Option<&HeaderVal...
    method get_method (line 384) | pub(crate) fn get_method(&self) -> Option<&http::Method> {
    method get_path (line 390) | pub(crate) fn get_path(&self) -> &[u8] {
    method get_host (line 395) | pub(crate) fn get_host(&self) -> &[u8] {
    method request_summary (line 403) | pub fn request_summary(&self) -> String {
    method is_upgrade_req (line 413) | pub fn is_upgrade_req(&self) -> bool {
    method get_header_bytes (line 421) | pub fn get_header_bytes(&self, name: impl AsHeaderName) -> &[u8] {
    method read_body_bytes (line 426) | pub async fn read_body_bytes(&mut self) -> Result<Option<Bytes>> {
    method do_read_body (line 438) | async fn do_read_body(&mut self) -> Result<Option<BufRef>> {
    method read_body (line 446) | async fn read_body(&mut self) -> Result<Option<BufRef>> {
    method do_drain_request_body (line 456) | async fn do_drain_request_body(&mut self) -> Result<()> {
    method drain_request_body (line 467) | pub async fn drain_request_body(&mut self) -> Result<()> {
    method is_body_done (line 481) | pub fn is_body_done(&mut self) -> bool {
    method is_body_empty (line 490) | pub fn is_body_empty(&mut self) -> bool {
    method write_response_header (line 497) | pub async fn write_response_header(&mut self, mut header: Box<Response...
    method response_written (line 611) | pub fn response_written(&self) -> Option<&ResponseHeader> {
    method is_upgrade (line 618) | pub fn is_upgrade(&self, header: &ResponseHeader) -> Option<bool> {
    method was_upgraded (line 630) | pub fn was_upgraded(&self) -> bool {
    method set_keepalive (line 634) | fn set_keepalive(&mut self, seconds: Option<u64>) {
    method get_keepalive_timeout (line 649) | pub fn get_keepalive_timeout(&self) -> Option<u64> {
    method set_keepalive_reuses_remaining (line 657) | pub fn set_keepalive_reuses_remaining(&mut self, remaining: Option<u32...
    method get_keepalive_reuses_remaining (line 661) | pub fn get_keepalive_reuses_remaining(&self) -> Option<u32> {
    method will_keepalive (line 666) | pub fn will_keepalive(&self) -> bool {
    method get_keepalive_values (line 674) | fn get_keepalive_values(&self) -> (Option<u64>, Option<usize>) {
    method ignore_info_resp (line 679) | fn ignore_info_resp(&self, status: u16) -> bool {
    method is_expect_continue_req (line 684) | fn is_expect_continue_req(&self) -> bool {
    method is_connection_keepalive (line 691) | fn is_connection_keepalive(&self) -> Option<bool> {
    method write_timeout (line 696) | fn write_timeout(&self, buf_len: usize) -> Option<Duration> {
    method respect_keepalive (line 710) | pub fn respect_keepalive(&mut self) {
    method init_body_writer (line 729) | fn init_body_writer(&mut self, header: &ResponseHeader) {
    method write_response_header_ref (line 754) | pub async fn write_response_header_ref(&mut self, resp: &ResponseHeade...
    method do_write_body (line 758) | async fn do_write_body(&mut self, buf: &[u8]) -> Result<Option<usize>> {
    method write_body (line 773) | pub async fn write_body(&mut self, buf: &[u8]) -> Result<Option<usize>> {
    method do_write_body_buf (line 784) | async fn do_write_body_buf(&mut self) -> Result<Option<usize>> {
    method write_body_buf (line 805) | async fn write_body_buf(&mut self) -> Result<Option<usize>> {
    method maybe_force_close_body_reader (line 815) | fn maybe_force_close_body_reader(&mut self) {
    method finish_body (line 826) | pub async fn finish_body(&mut self) -> Result<Option<usize>> {
    method body_bytes_sent (line 842) | pub fn body_bytes_sent(&self) -> usize {
    method body_bytes_read (line 847) | pub fn body_bytes_read(&self) -> usize {
    method is_chunked_encoding (line 851) | fn is_chunked_encoding(&self) -> bool {
    method get_content_length (line 855) | fn get_content_length(&self) -> Result<Option<usize>> {
    method init_body_reader (line 862) | fn init_body_reader(&mut self) {
    method retry_buffer_truncated (line 899) | pub fn retry_buffer_truncated(&self) -> bool {
    method enable_retry_buffering (line 905) | pub fn enable_retry_buffering(&mut self) {
    method get_retry_buffer (line 911) | pub fn get_retry_buffer(&self) -> Option<Bytes> {
    method get_body (line 921) | fn get_body(&self, buf_ref: &BufRef) -> &[u8] {
    method idle (line 927) | pub async fn idle(&mut self) -> Result<usize> {
    method read_body_or_idle (line 940) | pub async fn read_body_or_idle(&mut self, no_body_expected: bool) -> R...
    method get_headers_raw_bytes (line 962) | pub fn get_headers_raw_bytes(&self) -> Bytes {
    method shutdown (line 968) | pub async fn shutdown(&mut self) {
    method set_server_keepalive (line 977) | pub fn set_server_keepalive(&mut self, keepalive: Option<u64>) {
    method set_read_timeout (line 988) | pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
    method get_read_timeout (line 993) | pub fn get_read_timeout(&self) -> Option<Duration> {
    method set_write_timeout (line 1000) | pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
    method get_write_timeout (line 1005) | pub fn get_write_timeout(&self) -> Option<Duration> {
    method set_total_drain_timeout (line 1017) | pub fn set_total_drain_timeout(&mut self, timeout: Option<Duration>) {
    method get_total_drain_timeout (line 1022) | pub fn get_total_drain_timeout(&self) -> Option<Duration> {
    method set_min_send_rate (line 1034) | pub fn set_min_send_rate(&mut self, min_send_rate: Option<usize>) {
    method set_ignore_info_resp (line 1046) | pub fn set_ignore_info_resp(&mut self, ignore: bool) {
    method set_close_on_response_before_downstream_finish (line 1054) | pub fn set_close_on_response_before_downstream_finish(&mut self, close...
    method digest (line 1059) | pub fn digest(&self) -> &Digest {
    method digest_mut (line 1064) | pub fn digest_mut(&mut self) -> &mut Digest {
    method client_addr (line 1069) | pub fn client_addr(&self) -> Option<&SocketAddr> {
    method server_addr (line 1077) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method reuse (line 1093) | pub async fn reuse(mut self) -> Result<Option<Stream>> {
    method write_continue_response (line 1115) | pub async fn write_continue_response(&mut self) -> Result<()> {
    method write_non_empty_body (line 1126) | async fn write_non_empty_body(&mut self, data: Option<Bytes>, upgraded...
    method response_duplex (line 1148) | async fn response_duplex(&mut self, task: HttpTask) -> Result<bool> {
    method buffer_body_data (line 1175) | fn buffer_body_data(&mut self, data: Option<Bytes>, upgraded: bool) {
    method response_duplex_vec (line 1193) | pub async fn response_duplex_vec(&mut self, mut tasks: Vec<HttpTask>) ...
    method stream (line 1239) | pub fn stream(&self) -> &Stream {
    method into_inner (line 1246) | pub fn into_inner(self) -> Stream {
  constant URI_ESC_CHARSET (line 1257) | const URI_ESC_CHARSET: &AsciiSet = &CONTROLS.add(b' ').add(b'<').add(b'>...
  function escape_illegal_request_line (line 1259) | fn escape_illegal_request_line(buf: &BytesMut) -> Option<BytesMut> {
  function parse_req_buffer (line 1291) | fn parse_req_buffer<'buf>(
  function http_resp_header_to_buf (line 1320) | fn http_resp_header_to_buf(
  function init_log (line 1361) | fn init_log() {
  function read_basic (line 1366) | async fn read_basic() {
  function read_invalid_path (line 1378) | async fn read_invalid_path() {
  function read_2_buf (line 1390) | async fn read_2_buf() {
  function read_with_body_content_length (line 1411) | async fn read_with_body_content_length() {
  function read_with_body_timeout (line 1431) | async fn read_with_body_timeout() {
  function read_with_body_content_length_single_read (line 1451) | async fn read_with_body_content_length_single_read() {
  function read_with_body_http10 (line 1466) | async fn read_with_body_http10() {
  function read_with_body_http10_single_read (line 1487) | async fn read_with_body_http10_single_read() {
  function read_http11_default_no_body (line 1503) | async fn read_http11_default_no_body() {
  function read_http10_with_content_length (line 1517) | async fn read_http10_with_content_length() {
  function read_with_body_chunked_0_incomplete (line 1536) | async fn read_with_body_chunked_0_incomplete() {
  function read_with_body_chunked_0_extra (line 1557) | async fn read_with_body_chunked_0_extra() {
  function read_with_body_chunked_single_read (line 1582) | async fn read_with_body_chunked_single_read() {
  function read_with_body_chunked_single_read_extra (line 1608) | async fn read_with_body_chunked_single_read_extra() {
  function transfer_encoding_and_content_length_disallowed (line 1644) | async fn transfer_encoding_and_content_length_disallowed(
  function validate_request_rejects_invalid_content_length (line 1691) | async fn validate_request_rejects_invalid_content_length(#[case] invalid...
  function validate_request_accepts_valid_content_length (line 1710) | async fn validate_request_accepts_valid_content_length(#[case] valid_val...
  function validate_request_accepts_no_content_length (line 1723) | async fn validate_request_accepts_no_content_length() {
  function read_invalid (line 1734) | async fn read_invalid() {
  function read_invalid_header_end (line 1744) | async fn read_invalid_header_end() {
  function build_upgrade_req (line 1752) | async fn build_upgrade_req(upgrade: &str, conn: &str) -> HttpSession {
  function read_upgrade_req (line 1761) | async fn read_upgrade_req() {
  constant POST_CL_UPGRADE_REQ (line 1800) | const POST_CL_UPGRADE_REQ: &[u8] = b"POST / HTTP/1.1\r\nHost: pingora.or...
  constant POST_BODY_DATA (line 1801) | const POST_BODY_DATA: &[u8] = b"abcdefghij";
  constant POST_CHUNKED_UPGRADE_REQ (line 1802) | const POST_CHUNKED_UPGRADE_REQ: &[u8] = b"POST / HTTP/1.1\r\nHost: pingo...
  constant POST_BODY_DATA_CHUNKED (line 1803) | const POST_BODY_DATA_CHUNKED: &[u8] = b"3\r\nabc\r\n7\r\ndefghij\r\n0\r\...
  function read_upgrade_req_with_body (line 1809) | async fn read_upgrade_req_with_body(
  function read_upgrade_req_with_body_extra (line 1860) | async fn read_upgrade_req_with_body_extra(
  function read_upgrade_req_with_preread_body (line 1911) | async fn read_upgrade_req_with_preread_body(
  function read_upgrade_req_with_preread_body_after_101 (line 1961) | async fn read_upgrade_req_with_preread_body_after_101(
  function read_upgrade_req_with_1xx_response (line 1997) | async fn read_upgrade_req_with_1xx_response() {
  function test_upgrade_without_content_length_with_ws_data (line 2031) | async fn test_upgrade_without_content_length_with_ws_data() {
  function set_server_keepalive (line 2071) | async fn set_server_keepalive() {
  function write (line 2113) | async fn write() {
  function write_custom_reason (line 2129) | async fn write_custom_reason() {
  function write_informational (line 2146) | async fn write_informational() {
  function write_informational_ignored (line 2167) | async fn write_informational_ignored() {
  function write_informational_100_not_ignored_if_expect_continue (line 2190) | async fn write_informational_100_not_ignored_if_expect_continue() {
  function write_informational_1xx_ignored_if_expect_continue (line 2214) | async fn write_informational_1xx_ignored_if_expect_continue() {
  function write_101_switching_protocol (line 2238) | async fn write_101_switching_protocol() {
  function write_body_cl (line 2271) | async fn write_body_cl() {
  function write_body_http10 (line 2300) | async fn write_body_http10() {
  function write_body_chunk (line 2325) | async fn write_body_chunk() {
  function read_with_illegal (line 2358) | async fn read_with_illegal() {
  function escape_illegal (line 2378) | fn escape_illegal() {
  function test_write_body_buf (line 2407) | async fn test_write_body_buf() {
  function test_write_body_buf_write_timeout (line 2426) | async fn test_write_body_buf_write_timeout() {
  function test_write_continue_resp (line 2452) | async fn test_write_continue_resp() {
  function test_get_write_timeout (line 2462) | fn test_get_write_timeout() {
  function test_get_write_timeout_none (line 2471) | fn test_get_write_timeout_none() {
  function test_get_write_timeout_min_send_rate_zero (line 2477) | fn test_get_write_timeout_min_send_rate_zero() {
  function test_get_write_timeout_min_send_rate_overrides_write_timeout (line 2488) | fn test_get_write_timeout_min_send_rate_overrides_write_timeout() {
  function test_get_write_timeout_min_send_rate_max_zero_buf (line 2499) | fn test_get_write_timeout_min_send_rate_max_zero_buf() {
  function test_te_and_cl_disables_keepalive (line 2508) | async fn test_te_and_cl_disables_keepalive() {
  function test_http10_request_with_transfer_encoding_rejected (line 2542) | async fn test_http10_request_with_transfer_encoding_rejected() {
  function test_http10_request_without_transfer_encoding_accepted (line 2564) | async fn test_http10_request_without_transfer_encoding_accepted() {
  function test_http11_request_with_transfer_encoding_accepted (line 2581) | async fn test_http11_request_with_transfer_encoding_accepted() {
  function test_request_multiple_transfer_encoding_headers (line 2601) | async fn test_request_multiple_transfer_encoding_headers() {
  function test_request_multiple_te_headers_chunked_not_last (line 2627) | async fn test_request_multiple_te_headers_chunked_not_last() {
  function test_no_more_reuses_explicitly_disables_reuse (line 2645) | async fn test_no_more_reuses_explicitly_disables_reuse() {
  function test_close_delimited_response_explicitly_disables_reuse (line 2678) | async fn test_close_delimited_response_explicitly_disables_reuse() {
  function init_log (line 2712) | fn init_log() {
  function test_response_to_wire (line 2717) | fn test_response_to_wire() {
  constant TEST_MAX_WAIT_FOR_READ (line 2746) | const TEST_MAX_WAIT_FOR_READ: Duration = Duration::from_secs(3);
  constant TEST_FOREVER_DURATION (line 2749) | const TEST_FOREVER_DURATION: Duration = Duration::from_secs(600);
  constant TEST_READ_TIMEOUT (line 2752) | const TEST_READ_TIMEOUT: Duration = Duration::from_secs(1);
  type ReadBlockedForeverError (line 2755) | struct ReadBlockedForeverError;
  function mocked_blocking_headers_forever_stream (line 2758) | fn mocked_blocking_headers_forever_stream() -> Box<Mock> {
  function mocked_blocking_body_forever_stream (line 2762) | fn mocked_blocking_body_forever_stream() -> Box<Mock> {
  function test_read_with_tokio_timeout (line 2776) | async fn test_read_with_tokio_timeout<F, T>(
  function test_read_http_request_headers_timeout_for_read_request (line 2787) | async fn test_read_http_request_headers_timeout_for_read_request() {
  function test_read_http_body_timeout_for_read_body_bytes (line 2803) | async fn test_read_http_body_timeout_for_read_body_bytes() {
  function init_log (line 2827) | fn init_log() {
  function test_reuse_with_preread_body_overread (line 2844) | async fn test_reuse_with_preread_body_overread(
  function test_reuse_with_chunked_body_overread (line 2901) | async fn test_reuse_with_chunked_body_overread(#[case] read_body: bool) {

FILE: pingora-core/src/protocols/http/v2/client.rs
  constant PING_TIMEDOUT (line 38) | pub const PING_TIMEDOUT: ErrorType = ErrorType::new("PingTimedout");
  type Http2Session (line 40) | pub struct Http2Session {
    method new (line 69) | pub(crate) fn new(send_req: SendRequest<Bytes>, conn: ConnectionRef) -...
    method sanitize_request_header (line 85) | fn sanitize_request_header(req: &mut RequestHeader) -> Result<()> {
    method write_request_header (line 112) | pub fn write_request_header(&mut self, mut req: Box<RequestHeader>, en...
    method write_request_body (line 135) | pub async fn write_request_body(&mut self, data: Bytes, end: bool) -> ...
    method finish_request_body (line 154) | pub fn finish_request_body(&mut self) -> Result<()> {
    method read_response_header (line 174) | pub async fn read_response_header(&mut self) -> Result<()> {
    method poll_read_response_header (line 201) | pub fn poll_read_response_header(
    method read_response_body (line 232) | pub async fn read_response_body(&mut self) -> Result<Option<Bytes>> {
    method poll_read_response_body (line 269) | pub fn poll_read_response_body(
    method response_finished (line 293) | pub fn response_finished(&self) -> bool {
    method check_response_end_or_error (line 303) | pub fn check_response_end_or_error(&mut self) -> Result<bool> {
    method read_trailers (line 339) | pub async fn read_trailers(&mut self) -> Result<Option<HeaderMap>> {
    method request_header (line 379) | pub fn request_header(&self) -> Option<&RequestHeader> {
    method response_header (line 384) | pub fn response_header(&self) -> Option<&ResponseHeader> {
    method shutdown (line 389) | pub fn shutdown(&mut self) {
    method conn (line 400) | pub(crate) fn conn(&self) -> ConnectionRef {
    method ping_timedout (line 407) | pub(crate) fn ping_timedout(&self) -> bool {
    method digest (line 415) | pub fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 422) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
    method server_addr (line 427) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method client_addr (line 436) | pub fn client_addr(&self) -> Option<&SocketAddr> {
    method fd (line 445) | pub fn fd(&self) -> UniqueIDType {
    method body_bytes_received (line 450) | pub fn body_bytes_received(&self) -> usize {
    method take_request_body_writer (line 455) | pub fn take_request_body_writer(&mut self) -> Option<SendStream<Bytes>> {
    method handle_err (line 459) | fn handle_err(&self, mut e: Box<Error>) -> Box<Error> {
  method drop (line 63) | fn drop(&mut self) {
  function handle_read_header_error (line 490) | fn handle_read_header_error(e: h2::Error) -> Box<Error> {
  function drive_connection (line 538) | pub async fn drive_connection<S>(
  constant PING_TIMEOUT (line 587) | const PING_TIMEOUT: Duration = Duration::from_secs(5);
  function do_ping_pong (line 589) | async fn do_ping_pong(
  function h2_body_bytes_received_multi_frames (line 637) | async fn h2_body_bytes_received_multi_frames() {

FILE: pingora-core/src/protocols/http/v2/mod.rs
  function reserve_and_send (line 28) | async fn reserve_and_send(
  function write_body (line 52) | pub async fn write_body(
  function test_client_write_timeout (line 107) | async fn test_client_write_timeout() {
  function test_server_write_timeout (line 187) | async fn test_server_write_timeout() {

FILE: pingora-core/src/protocols/http/v2/server.rs
  constant BODY_BUF_LIMIT (line 39) | const BODY_BUF_LIMIT: usize = 1024 * 64;
  type H2Connection (line 41) | type H2Connection<S> = server::Connection<S, Bytes>;
  function handshake (line 49) | pub async fn handshake(io: Stream, options: Option<H2Options>) -> Result...
  type Idle (line 72) | pub struct Idle<'a>(&'a mut HttpSession);
  type Output (line 75) | type Output = Result<h2::Reason>;
  method poll (line 77) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
  type HttpSession (line 88) | pub struct HttpSession {
    method from_h2_conn (line 128) | pub async fn from_h2_conn(
    method req_header (line 161) | pub fn req_header(&self) -> &RequestHeader {
    method req_header_mut (line 169) | pub fn req_header_mut(&mut self) -> &mut RequestHeader {
    method read_body_bytes (line 174) | pub async fn read_body_bytes(&mut self) -> Result<Option<Bytes>> {
    method poll_read_body_bytes (line 194) | pub fn poll_read_body_bytes(
    method do_drain_request_body (line 214) | async fn do_drain_request_body(&mut self) -> Result<()> {
    method drain_request_body (line 226) | pub async fn drain_request_body(&mut self) -> Result<()> {
    method set_write_timeout (line 244) | pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
    method get_write_timeout (line 249) | pub fn get_write_timeout(&self) -> Option<Duration> {
    method set_total_drain_timeout (line 255) | pub fn set_total_drain_timeout(&mut self, timeout: Option<Duration>) {
    method get_total_drain_timeout (line 260) | pub fn get_total_drain_timeout(&self) -> Option<Duration> {
    method write_response_header (line 271) | pub fn write_response_header(
    method write_body (line 318) | pub async fn write_body(&mut self, data: Bytes, end: bool) -> Result<(...
    method do_write_body (line 331) | async fn do_write_body(&mut self, data: Bytes, end: bool) -> Result<()> {
    method write_trailers (line 354) | pub fn write_trailers(&mut self, trailers: HeaderMap) -> Result<()> {
    method write_response_header_ref (line 375) | pub fn write_response_header_ref(&mut self, header: &ResponseHeader, e...
    method finish (line 386) | pub fn finish(&mut self) -> Result<()> {
    method response_duplex_vec (line 405) | pub async fn response_duplex_vec(&mut self, tasks: Vec<HttpTask>) -> R...
    method request_summary (line 454) | pub fn request_summary(&self) -> String {
    method response_written (line 474) | pub fn response_written(&self) -> Option<&ResponseHeader> {
    method shutdown (line 481) | pub fn shutdown(&mut self) {
    method take_response_body_writer (line 488) | pub fn take_response_body_writer(&mut self) -> Option<SendStream<Bytes...
    method pseudo_raw_h1_request_header (line 494) | pub fn pseudo_raw_h1_request_header(&self) -> Bytes {
    method is_body_done (line 500) | pub fn is_body_done(&self) -> bool {
    method is_body_empty (line 507) | pub fn is_body_empty(&self) -> bool {
    method retry_buffer_truncated (line 517) | pub fn retry_buffer_truncated(&self) -> bool {
    method enable_retry_buffering (line 523) | pub fn enable_retry_buffering(&mut self) {
    method get_retry_buffer (line 529) | pub fn get_retry_buffer(&self) -> Option<Bytes> {
    method idle (line 543) | pub fn idle(&mut self) -> Idle<'_> {
    method read_body_or_idle (line 549) | pub async fn read_body_or_idle(&mut self, no_body_expected: bool) -> R...
    method body_bytes_sent (line 562) | pub fn body_bytes_sent(&self) -> usize {
    method body_bytes_read (line 567) | pub fn body_bytes_read(&self) -> usize {
    method digest (line 572) | pub fn digest(&self) -> Option<&Digest> {
    method digest_mut (line 577) | pub fn digest_mut(&mut self) -> Option<&mut Digest> {
    method server_addr (line 582) | pub fn server_addr(&self) -> Option<&SocketAddr> {
    method client_addr (line 587) | pub fn client_addr(&self) -> Option<&SocketAddr> {
  function test_server_handshake_accept_request (line 599) | async fn test_server_handshake_accept_request() {
  function test_req_content_length_eq_0_and_no_header_eos (line 695) | async fn test_req_content_length_eq_0_and_no_header_eos() {
  function test_req_header_no_eos_empty_data_with_eos (line 768) | async fn test_req_header_no_eos_empty_data_with_eos() {

FILE: pingora-core/src/protocols/l4/ext.rs
  type TCP_INFO (line 41) | pub struct TCP_INFO {
    method new (line 102) | pub unsafe fn new() -> Self {
    method len (line 108) | pub fn len() -> socklen_t {
    method len (line 114) | pub fn len() -> usize {
  function set_opt (line 120) | fn set_opt<T: Copy>(sock: c_int, opt: c_int, val: c_int, payload: T) -> ...
  function get_opt (line 135) | fn get_opt<T>(
  function get_opt_sized (line 150) | fn get_opt_sized<T>(sock: c_int, opt: c_int, val: c_int) -> io::Result<T> {
  function cvt_linux_error (line 165) | fn cvt_linux_error(t: i32) -> io::Result<i32> {
  function ip_bind_addr_no_port (line 174) | fn ip_bind_addr_no_port(fd: RawFd, val: bool) -> io::Result<()> {
  function ip_bind_addr_no_port (line 184) | fn ip_bind_addr_no_port(_fd: RawFd, _val: bool) -> io::Result<()> {
  function ip_local_port_range (line 192) | fn ip_local_port_range(fd: RawFd, low: u16, high: u16) -> io::Result<()> {
  function ip_local_port_range (line 204) | fn ip_local_port_range(_fd: RawFd, _low: u16, _high: u16) -> io::Result<...
  function ip_local_port_range (line 209) | fn ip_local_port_range(_fd: RawSocket, _low: u16, _high: u16) -> io::Res...
  function set_so_keepalive (line 214) | fn set_so_keepalive(fd: RawFd, val: bool) -> io::Result<()> {
  function set_so_keepalive_idle (line 219) | fn set_so_keepalive_idle(fd: RawFd, val: Duration) -> io::Result<()> {
  function set_so_keepalive_user_timeout (line 229) | fn set_so_keepalive_user_timeout(fd: RawFd, val: Duration) -> io::Result...
  function set_so_keepalive_interval (line 239) | fn set_so_keepalive_interval(fd: RawFd, val: Duration) -> io::Result<()> {
  function set_so_keepalive_count (line 249) | fn set_so_keepalive_count(fd: RawFd, val: usize) -> io::Result<()> {
  function set_keepalive (line 254) | fn set_keepalive(fd: RawFd, ka: &TcpKeepalive) -> io::Result<()> {
  function set_keepalive (line 263) | fn set_keepalive(_fd: RawFd, _ka: &TcpKeepalive) -> io::Result<()> {
  function set_keepalive (line 268) | fn set_keepalive(_sock: RawSocket, _ka: &TcpKeepalive) -> io::Result<()> {
  function get_tcp_info (line 274) | pub fn get_tcp_info(fd: RawFd) -> io::Result<TCP_INFO> {
  function get_tcp_info (line 279) | pub fn get_tcp_info(_fd: RawFd) -> io::Result<TCP_INFO> {
  function get_tcp_info (line 284) | pub fn get_tcp_info(_fd: RawSocket) -> io::Result<TCP_INFO> {
  function set_recv_buf (line 290) | pub fn set_recv_buf(fd: RawFd, val: usize) -> Result<()> {
  function set_recv_buf (line 296) | pub fn set_recv_buf(_fd: RawFd, _: usize) -> Result<()> {
  function set_recv_buf (line 301) | pub fn set_recv_buf(_sock: RawSocket, _: usize) -> Result<()> {
  function set_snd_buf (line 307) | pub fn set_snd_buf(fd: RawFd, val: usize) -> Result<()> {
  function set_snd_buf (line 313) | pub fn set_snd_buf(_fd: RawFd, _: usize) -> Result<()> {
  function set_snd_buf (line 318) | pub fn set_snd_buf(_sock: RawSocket, _: usize) -> Result<()> {
  function get_recv_buf (line 323) | pub fn get_recv_buf(fd: RawFd) -> io::Result<usize> {
  function get_recv_buf (line 328) | pub fn get_recv_buf(_fd: RawFd) -> io::Result<usize> {
  function get_recv_buf (line 333) | pub fn get_recv_buf(_sock: RawSocket) -> io::Result<usize> {
  function get_snd_buf (line 338) | pub fn get_snd_buf(fd: RawFd) -> io::Result<usize> {
  function get_snd_buf (line 343) | pub fn get_snd_buf(_fd: RawFd) -> io::Result<usize> {
  function get_snd_buf (line 348) | pub fn get_snd_buf(_sock: RawSocket) -> io::Result<usize> {
  function set_tcp_fastopen_connect (line 354) | pub fn set_tcp_fastopen_connect(fd: RawFd) -> Result<()> {
  function set_tcp_fastopen_connect (line 365) | pub fn set_tcp_fastopen_connect(_fd: RawFd) -> Result<()> {
  function set_tcp_fastopen_connect (line 370) | pub fn set_tcp_fastopen_connect(_sock: RawSocket) -> Result<()> {
  function set_tcp_fastopen_backlog (line 376) | pub fn set_tcp_fastopen_backlog(fd: RawFd, backlog: usize) -> Result<()> {
  function set_tcp_fastopen_backlog (line 382) | pub fn set_tcp_fastopen_backlog(_fd: RawFd, _backlog: usize) -> Result<(...
  function set_tcp_fastopen_backlog (line 387) | pub fn set_tcp_fastopen_backlog(_sock: RawSocket, _backlog: usize) -> Re...
  function set_dscp (line 392) | pub fn set_dscp(fd: RawFd, value: u8) -> Result<()> {
  function set_dscp (line 412) | pub fn set_dscp(_fd: RawFd, _value: u8) -> Result<()> {
  function set_dscp (line 417) | pub fn set_dscp(_sock: RawSocket, _value: u8) -> Result<()> {
  function get_socket_cookie (line 422) | pub fn get_socket_cookie(fd: RawFd) -> io::Result<u64> {
  function get_socket_cookie (line 427) | pub fn get_socket_cookie(_fd: RawFd) -> io::Result<u64> {
  function get_original_dest (line 432) | pub fn get_original_dest(fd: RawFd) -> Result<Option<SocketAddr>> {
  function get_original_dest (line 467) | pub fn get_original_dest(_fd: RawFd) -> Result<Option<SocketAddr>> {
  function get_original_dest (line 472) | pub fn get_original_dest(_sock: RawSocket) -> Result<Option<SocketAddr>> {
  function connect_with (line 485) | pub(crate) async fn connect_with<F: FnOnce(&TcpSocket) -> Result<()> + C...
  function inner_connect_with (line 509) | async fn inner_connect_with<F: FnOnce(&TcpSocket) -> Result<()>>(
  function connect (line 564) | pub async fn connect(addr: &SocketAddr, bind_to: Option<&BindTo>) -> Res...
  function connect_uds (line 570) | pub async fn connect_uds(path: &std::path::Path) -> Result<UnixStream> {
  function wrap_os_connect_error (line 576) | fn wrap_os_connect_error(e: std::io::Error, context: String) -> Box<Erro...
  type TcpKeepalive (line 595) | pub struct TcpKeepalive {
    method fmt (line 613) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    method fmt (line 621) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  function set_tcp_keepalive (line 627) | pub fn set_tcp_keepalive(stream: &TcpStream, ka: &TcpKeepalive) -> Resul...
  function test_set_recv_buf (line 641) | fn test_set_recv_buf() {
  function test_set_fast_open (line 659) | async fn test_set_fast_open() {

FILE: pingora-core/src/protocols/l4/listener.rs
  type Listener (line 31) | pub enum Listener {
    method from (line 38) | fn from(s: TcpListener) -> Self {
    method from (line 45) | fn from(s: UnixListener) -> Self {
    method accept (line 71) | pub async fn accept(&self) -> io::Result<Stream> {
  method as_raw_fd (line 52) | fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
  method as_raw_socket (line 62) | fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {

FILE: pingora-core/src/protocols/l4/socket.rs
  type SocketAddr (line 32) | pub enum SocketAddr {
    method as_inet (line 40) | pub fn as_inet(&self) -> Option<&StdSockAddr> {
    method as_unix (line 50) | pub fn as_unix(&self) -> Option<&StdUnixSockAddr> {
    method set_port (line 59) | pub fn set_port(&mut self, port: u16) {
    method from_sockaddr_storage (line 66) | fn from_sockaddr_storage(sock: &SockaddrStorage) -> Option<SocketAddr> {
    method from_raw_fd (line 87) | pub fn from_raw_fd(fd: std::os::unix::io::RawFd, peer_addr: bool) -> O...
    method from_raw_socket (line 102) | pub fn from_raw_socket(
    method fmt (line 118) | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    type Err (line 201) | type Err = Box<Error>;
    method from_str (line 205) | fn from_str(s: &str) -> Result<Self, Self::Err> {
    method from_str (line 227) | fn from_str(s: &str) -> Result<Self, Self::Err> {
    type Iter (line 234) | type Iter = std::iter::Once<StdSockAddr>;
    method to_socket_addrs (line 237) | fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
    method from (line 249) | fn from(sockaddr: StdSockAddr) -> Self {
    method from (line 256) | fn from(sockaddr: StdUnixSockAddr) -> Self {
    type Error (line 266) | type Error = String;
    method try_from (line 268) | fn try_from(value: TokioUnixSockAddr) -> Result<Self, Self::Error> {
  method hash (line 134) | fn hash<H: Hasher>(&self, state: &mut H) {
  method eq (line 154) | fn eq(&self, other: &Self) -> bool {
  method partial_cmp (line 168) | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
  method cmp (line 174) | fn cmp(&self, other: &Self) -> Ordering {
  function parse_ip (line 283) | fn parse_ip() {
  function parse_uds (line 290) | fn parse_uds() {
  function parse_uds_with_prefix (line 297) | fn parse_uds_with_prefix() {

FILE: pingora-core/src/protocols/l4/stream.rs
  type RawStream (line 49) | enum RawStream {
  method poll_read (line 57) | fn poll_read(
  method poll_write (line 75) | fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Pol...
  method poll_flush (line 87) | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result...
  method poll_shutdown (line 99) | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Res...
  method poll_write_vectored (line 111) | fn poll_write_vectored(
  method is_write_vectored (line 127) | fn is_write_vectored(&self) -> bool {
  method as_raw_fd (line 139) | fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
  method as_raw_socket (line 150) | fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
  type RawStreamWrapper (line 160) | struct RawStreamWrapper {
    method new (line 175) | pub fn new(stream: RawStream) -> Self {
    method enable_rx_ts (line 187) | pub fn enable_rx_ts(&mut self, enable_rx_ts: bool) {
  method poll_read (line 194) | fn poll_read(
  method poll_read (line 212) | fn poll_read(
  method poll_write (line 286) | fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Pol...
  method poll_flush (line 298) | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result...
  method poll_shutdown (line 310) | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Res...
  method poll_write_vectored (line 322) | fn poll_write_vectored(
  method is_write_vectored (line 338) | fn is_write_vectored(&self) -> bool {
  method as_raw_fd (line 345) | fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
  method as_raw_socket (line 352) | fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
  constant BUF_READ_SIZE (line 359) | const BUF_READ_SIZE: usize = 64 * 1024;
  constant BUF_WRITE_SIZE (line 364) | const BUF_WRITE_SIZE: usize = 1460;
  type Stream (line 371) | pub struct Stream {
    method stream (line 390) | fn stream(&self) -> &BufStream<RawStreamWrapper> {
    method stream_mut (line 394) | fn stream_mut(&mut self) -> &mut BufStream<RawStreamWrapper> {
    method set_nodelay (line 399) | pub fn set_nodelay(&mut self) -> Result<()> {
    method set_keepalive (line 415) | pub fn set_keepalive(&mut self, ka: &TcpKeepalive) -> Result<()> {
    method set_rx_timestamp (line 431) | pub fn set_rx_timestamp(&mut self) -> Result<()> {
    method set_rx_timestamp (line 446) | pub fn set_rx_timestamp(&mut self) -> io::Result<()> {
    method rewind (line 451) | pub(crate) fn rewind(&mut self, data: &[u8]) {
    method se
Condensed preview — 380 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,285K chars).
[
  {
    "path": ".bleep",
    "chars": 40,
    "preview": "5a1cf681f7e2691687623b60387a88076493015f"
  },
  {
    "path": ".cargo/audit.toml",
    "chars": 228,
    "preview": "[advisories]\nignore = [\n    # This came from the prometheus crate's protobuf encoder.\n    # We don't use the protobuf en"
  },
  {
    "path": ".cargo/config.toml",
    "chars": 50,
    "preview": "[resolver]\nincompatible-rust-versions = \"fallback\""
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 2997,
    "preview": "# Contributing\n\nWelcome to Pingora! Before you make a contribution, be it a bug report, documentation improvement,\npull "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 840,
    "preview": "---\nname: Bug Report\nabout: Report an issue to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n## Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 765,
    "preview": "---\nname: Feature request\nabout: Propose a new feature\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n## What is the problem yo"
  },
  {
    "path": ".github/workflows/audit.yml",
    "chars": 818,
    "preview": "name: Security Audit\n\non:\n  push:\n    branches:\n      - master\n    paths:\n      - \"**/Cargo.toml\"\n  schedule:\n    - cron"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 2338,
    "preview": "on: [push, pull_request]\n\nname: build\n\njobs:\n  pingora:\n    strategy:\n      fail-fast: false\n      matrix:\n        # nig"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 512,
    "preview": "on:\n  push:\n    branches:\n      - master\n\nname: Docs\n\njobs:\n  docs:\n    runs-on: ubuntu-latest\n    steps:\n      - name: "
  },
  {
    "path": ".github/workflows/mark-stale.yaml",
    "chars": 662,
    "preview": "name: 'Close stale questions'\non:\n  schedule:\n    - cron: '30 1 * * *'\n  workflow_dispatch:\n\npermissions:\n  issues: writ"
  },
  {
    "path": ".github/workflows/semgrep.yml",
    "chars": 591,
    "preview": "on:\n  pull_request: {}\n  workflow_dispatch: {}\n  push: \n    branches:\n      - main\n      - master\n  schedule:\n    - cron"
  },
  {
    "path": ".gitignore",
    "chars": 83,
    "preview": "Cargo.lock\n/target\n**/*.rs.bk\ndhat-heap.json\n.vscode\n.idea\n.cover\nbleeper.user.toml"
  },
  {
    "path": ".rustfmt.toml",
    "chars": 17,
    "preview": "edition = \"2021\"\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 22186,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [0.8.0](https://github.com/cloudfl"
  },
  {
    "path": "Cargo.toml",
    "chars": 763,
    "preview": "\n\n\n\n\n[workspace]\nresolver = \"2\"\nmembers = [\n    \"pingora\",\n    \"pingora-core\",\n    \"pingora-pool\",\n    \"pingora-error\",\n"
  },
  {
    "path": "Dockerfile",
    "chars": 941,
    "preview": "FROM debian:latest as builder\n\nARG BUILDARCH\nRUN apt-get -qq update \\\n    && apt-get -qq install -y --no-install-recomme"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 3967,
    "preview": "# Pingora\n\n![Pingora banner image](./docs/assets/pingora_banner.png)\n\n## What is Pingora\nPingora is a Rust framework to "
  },
  {
    "path": "cliff.toml",
    "chars": 3155,
    "preview": "# git-cliff ~ default configuration file\n# https://git-cliff.org/docs/configuration\n#\n# Lines starting with \"#\" are comm"
  },
  {
    "path": "clippy.toml",
    "chars": 14,
    "preview": "msrv = \"1.84\"\n"
  },
  {
    "path": "docs/README.md",
    "chars": 379,
    "preview": "# Pingora User Manual\n\n## Quick Start\nIn this section we show you how to build a bare-bones load balancer.\n\n[Read the qu"
  },
  {
    "path": "docs/quick_start.md",
    "chars": 11150,
    "preview": "# Quick Start: load balancer\n\n## Introduction\n\nThis quick start shows how to build a bare-bones load balancer using ping"
  },
  {
    "path": "docs/user_guide/conf.md",
    "chars": 1694,
    "preview": "# Configuration\n\nA Pingora configuration file is a list of Pingora settings in yaml format.\n\nExample\n```yaml\n---\nversion"
  },
  {
    "path": "docs/user_guide/ctx.md",
    "chars": 3910,
    "preview": "# Sharing state across phases with `CTX`\n\n## Using `CTX`\nThe custom filters users implement in different phases of the r"
  },
  {
    "path": "docs/user_guide/daemon.md",
    "chars": 651,
    "preview": "# Daemonization\n\nWhen a Pingora server is configured to run as a daemon, after its bootstrapping, it will move itself to"
  },
  {
    "path": "docs/user_guide/error_log.md",
    "chars": 1098,
    "preview": "# Error logging\n\nPingora libraries are built to expect issues like disconnects, timeouts and invalid inputs from the net"
  },
  {
    "path": "docs/user_guide/errors.md",
    "chars": 3152,
    "preview": "# How to return errors\n\nFor easy error handling, the `pingora-error` crate exports a custom `Result` type used throughou"
  },
  {
    "path": "docs/user_guide/failover.md",
    "chars": 2789,
    "preview": "# Handling failures and failover\n\nPingora-proxy allows users to define how to handle failures throughout the life of a p"
  },
  {
    "path": "docs/user_guide/graceful.md",
    "chars": 1372,
    "preview": "# Graceful restart and shutdown\n\nGraceful restart, upgrade, and shutdown mechanisms are very commonly used to avoid erro"
  },
  {
    "path": "docs/user_guide/index.md",
    "chars": 1042,
    "preview": "# User Guide\n\nIn this guide, we will cover the most used features, operations and settings of Pingora.\n\n## Running Pingo"
  },
  {
    "path": "docs/user_guide/internals.md",
    "chars": 12297,
    "preview": "# Pingora Internals\n\n(Special thanks to [James Munns](https://github.com/jamesmunns) for writing this section)\n\n\n## Star"
  },
  {
    "path": "docs/user_guide/modify_filter.md",
    "chars": 3954,
    "preview": "# Examples: taking control of the request\n\nIn this section we will go through how to route, modify or reject requests.\n\n"
  },
  {
    "path": "docs/user_guide/panic.md",
    "chars": 693,
    "preview": "# Handling panics\n\nAny panic that happens to particular requests does not affect other ongoing requests or the server's "
  },
  {
    "path": "docs/user_guide/peer.md",
    "chars": 2875,
    "preview": "# `Peer`: how to connect to upstream\n\nIn the `upstream_peer()` phase the user should return a `Peer` object which define"
  },
  {
    "path": "docs/user_guide/phase.md",
    "chars": 7183,
    "preview": "# Life of a request: pingora-proxy phases and filters\n\n## Intro\nThe pingora-proxy HTTP proxy framework supports highly p"
  },
  {
    "path": "docs/user_guide/phase_chart.md",
    "chars": 1193,
    "preview": "Pingora proxy phases without caching\n```mermaid\n graph TD;\n    start(\"new request\")-->early_request_filter;\n    early_re"
  },
  {
    "path": "docs/user_guide/pooling.md",
    "chars": 1018,
    "preview": "# Connection pooling and reuse\n\nWhen the request to a `Peer` (upstream server) is finished, the connection to that peer "
  },
  {
    "path": "docs/user_guide/prom.md",
    "chars": 640,
    "preview": "# Prometheus\n\nPingora has a built-in prometheus HTTP metric server for scraping.\n\n```rust\n    ...\n    let mut prometheus"
  },
  {
    "path": "docs/user_guide/rate_limiter.md",
    "chars": 5644,
    "preview": "# **RateLimiter quickstart**\nPingora provides a crate `pingora-limits` which provides a simple and easy to use rate limi"
  },
  {
    "path": "docs/user_guide/start_stop.md",
    "chars": 1339,
    "preview": "# Starting and stopping Pingora server\n\nA pingora server is a regular unprivileged multithreaded process.\n\n## Start\nBy d"
  },
  {
    "path": "docs/user_guide/systemd.md",
    "chars": 505,
    "preview": "# Systemd integration\n\nA Pingora server doesn't depend on systemd but it can easily be made into a systemd service.\n\n```"
  },
  {
    "path": "pingora/Cargo.toml",
    "chars": 4249,
    "preview": "[package]\nname = \"pingora\"\nversion = \"0.8.0\"\nauthors = [\"Yuchen Wu <yuchen@cloudflare.com>\"]\nlicense = \"Apache-2.0\"\nedit"
  },
  {
    "path": "pingora/LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "pingora/examples/app/echo.rs",
    "chars": 2663,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/app/mod.rs",
    "chars": 619,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/app/proxy.rs",
    "chars": 3301,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/client.rs",
    "chars": 2535,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/server.rs",
    "chars": 6299,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/service/echo.rs",
    "chars": 998,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/service/mod.rs",
    "chars": 619,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/examples/service/proxy.rs",
    "chars": 1457,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/src/lib.rs",
    "chars": 2966,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora/tests/pingora_conf.yaml",
    "chars": 82,
    "preview": "---\nversion: 1\nclient_bind_to_ipv4:\n    - 127.0.0.2\nca_file: tests/keys/server.crt"
  },
  {
    "path": "pingora-boringssl/Cargo.toml",
    "chars": 999,
    "preview": "[package]\nname = \"pingora-boringssl\"\nversion = \"0.8.0\"\nauthors = [\"Yuchen Wu <yuchen@cloudflare.com>\"]\nlicense = \"Apache"
  },
  {
    "path": "pingora-boringssl/LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "pingora-boringssl/src/boring_tokio.rs",
    "chars": 10149,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-boringssl/src/ext.rs",
    "chars": 6666,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-boringssl/src/lib.rs",
    "chars": 1185,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/Cargo.toml",
    "chars": 1987,
    "preview": "[package]\nname = \"pingora-cache\"\nversion = \"0.8.0\"\nauthors = [\"Yuchen Wu <yuchen@cloudflare.com>\"]\nlicense = \"Apache-2.0"
  },
  {
    "path": "pingora-cache/LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "pingora-cache/benches/lru_memory.rs",
    "chars": 6134,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/benches/lru_serde.rs",
    "chars": 1656,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/benches/simple_lru_memory.rs",
    "chars": 4964,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/cache_control.rs",
    "chars": 33067,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/eviction/lru.rs",
    "chars": 21408,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/eviction/mod.rs",
    "chars": 4205,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/eviction/simple_lru.rs",
    "chars": 18405,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/filters.rs",
    "chars": 22679,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/hashtable.rs",
    "chars": 3727,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/key.rs",
    "chars": 13335,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/lib.rs",
    "chars": 64945,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/lock.rs",
    "chars": 20144,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/max_file_size.rs",
    "chars": 1629,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/memory.rs",
    "chars": 20294,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/meta.rs",
    "chars": 30569,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/predictor.rs",
    "chars": 9383,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/put.rs",
    "chars": 31840,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/storage.rs",
    "chars": 10043,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/trace.rs",
    "chars": 3487,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-cache/src/variance.rs",
    "chars": 4032,
    "preview": "use std::{borrow::Cow, collections::BTreeMap};\n\nuse blake2::Digest;\n\nuse crate::key::{Blake2b128, HashBinary};\n\n/// A bu"
  },
  {
    "path": "pingora-core/Cargo.toml",
    "chars": 3576,
    "preview": "[package]\nname = \"pingora-core\"\nversion = \"0.8.0\"\nauthors = [\"Yuchen Wu <yuchen@cloudflare.com>\"]\nlicense = \"Apache-2.0\""
  },
  {
    "path": "pingora-core/LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "pingora-core/examples/bootstrap_as_a_service.rs",
    "chars": 3695,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/examples/client_cert.rs",
    "chars": 8079,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/examples/keys/client-ca/cert.pem",
    "chars": 859,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICTjCCAfWgAwIBAgIULuUoq/di4EKmLyN0YwAkd6MQjv4wCgYIKoZIzj0EAwIw\ndTELMAkGA1UEBhMCVVMxEzARBgN"
  },
  {
    "path": "pingora-core/examples/keys/client-ca/key.pem",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIJOxEQowpYL5VLNf+qaCEBhic8e26UyR0ku65Sk6gjMIoAoGCCqGSM49\nAwEHoUQDQgAEcXMTgEeM1Az4"
  },
  {
    "path": "pingora-core/examples/keys/clients/cert-1.pem",
    "chars": 948,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICjjCCAjWgAwIBAgIUYUSqEzxm/oebfxxQmZEesZL2WFAwCgYIKoZIzj0EAwIw\ndTELMAkGA1UEBhMCVVMxEzARBgN"
  },
  {
    "path": "pingora-core/examples/keys/clients/cert-2.pem",
    "chars": 1042,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIC0zCCAnmgAwIBAgIUVQlGCD9Zryvkh9G8GZXFBa2L9kQwCgYIKoZIzj0EAwIw\ndTELMAkGA1UEBhMCVVMxEzARBgN"
  },
  {
    "path": "pingora-core/examples/keys/clients/invalid-cert.pem",
    "chars": 948,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICjzCCAjWgAwIBAgIUHYIVFYFooGVi2bNlk5R6GsbDKqUwCgYIKoZIzj0EAwIw\ndTELMAkGA1UEBhMCVVMxEzARBgN"
  },
  {
    "path": "pingora-core/examples/keys/clients/invalid-key.pem",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFyLneOGHgjTBS8I2GB8kF0LHgDS/eTJBSDNS4PAkJ0JoAoGCCqGSM49\nAwEHoUQDQgAExiqaTJFA7Db6"
  },
  {
    "path": "pingora-core/examples/keys/clients/key-1.pem",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIFNioASifzPy0Fcp+qmMoMUhFOJGLki20ygISqZb+HY1oAoGCCqGSM49\nAwEHoUQDQgAEw3uoQcKZhOCb"
  },
  {
    "path": "pingora-core/examples/keys/clients/key-2.pem",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICd8DwjvpvE6nIKKKH2smrnLBM5zQyIkAKwBCiiRZGGsoAoGCCqGSM49\nAwEHoUQDQgAEtiddK6uUbeE4"
  },
  {
    "path": "pingora-core/examples/keys/server/cert.pem",
    "chars": 871,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICVzCCAf6gAwIBAgIUYGbx/r4kY40a+zNq7IW/1lsvzk0wCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVVMxEzARBgN"
  },
  {
    "path": "pingora-core/examples/keys/server/key.pem",
    "chars": 241,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTAnVhDuKvV5epzX4\nuuC8kEZL2vUPI49gUmS5kM+j5VW"
  },
  {
    "path": "pingora-core/examples/service_dependencies.rs",
    "chars": 7221,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/apps/http_app.rs",
    "chars": 7625,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/apps/mod.rs",
    "chars": 11414,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/apps/prometheus_http_app.rs",
    "chars": 2231,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/http/custom/mod.rs",
    "chars": 2253,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/http/mod.rs",
    "chars": 23523,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/http/v1.rs",
    "chars": 6145,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/http/v2.rs",
    "chars": 26979,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/l4.rs",
    "chars": 23279,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/mod.rs",
    "chars": 23805,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/offload.rs",
    "chars": 2982,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/tls/boringssl_openssl/mod.rs",
    "chars": 10007,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/tls/mod.rs",
    "chars": 2996,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/tls/rustls/mod.rs",
    "chars": 12611,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/connectors/tls/s2n/mod.rs",
    "chars": 14492,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/lib.rs",
    "chars": 4321,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/connection_filter.rs",
    "chars": 4757,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/l4.rs",
    "chars": 25661,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/mod.rs",
    "chars": 14860,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/tls/boringssl_openssl/mod.rs",
    "chars": 7767,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/tls/mod.rs",
    "chars": 878,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/tls/rustls/mod.rs",
    "chars": 4410,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/listeners/tls/s2n/mod.rs",
    "chars": 6163,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/modules/http/compression.rs",
    "chars": 3074,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/modules/http/grpc_web.rs",
    "chars": 2100,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/modules/http/mod.rs",
    "chars": 11832,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/modules/mod.rs",
    "chars": 667,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/digest.rs",
    "chars": 6477,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/body_buffer.rs",
    "chars": 1945,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/bridge/grpc_web.rs",
    "chars": 12216,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/bridge/mod.rs",
    "chars": 608,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/client.rs",
    "chars": 8043,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/compression/brotli.rs",
    "chars": 5303,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/compression/gzip.rs",
    "chars": 5762,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/compression/mod.rs",
    "chars": 44540,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/compression/zstd.rs",
    "chars": 11498,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/conditional_filter.rs",
    "chars": 13857,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/custom/client.rs",
    "chars": 5522,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/custom/mod.rs",
    "chars": 2736,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/custom/server.rs",
    "chars": 9049,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/date.rs",
    "chars": 2528,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/error_resp.rs",
    "chars": 1601,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/mod.rs",
    "chars": 2506,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/server.rs",
    "chars": 29154,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/subrequest/body.rs",
    "chars": 22012,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/subrequest/dummy.rs",
    "chars": 3262,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/subrequest/mod.rs",
    "chars": 59,
    "preview": "pub(crate) mod body;\npub(crate) mod dummy;\npub mod server;\n"
  },
  {
    "path": "pingora-core/src/protocols/http/subrequest/server.rs",
    "chars": 48100,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v1/body.rs",
    "chars": 102303,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v1/client.rs",
    "chars": 85045,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v1/common.rs",
    "chars": 13008,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v1/mod.rs",
    "chars": 688,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v1/server.rs",
    "chars": 118209,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v2/client.rs",
    "chars": 25789,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v2/mod.rs",
    "chars": 8810,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/http/v2/server.rs",
    "chars": 30985,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/ext.rs",
    "chars": 20926,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/listener.rs",
    "chars": 3334,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/mod.rs",
    "chars": 712,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/socket.rs",
    "chars": 9471,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/stream.rs",
    "chars": 36747,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/l4/virt.rs",
    "chars": 4682,
    "preview": "//! Provides [`VirtualSocketStream`].\n\nuse std::{\n    pin::Pin,\n    task::{Context, Poll},\n};\n\nuse tokio::io::{AsyncRead"
  },
  {
    "path": "pingora-core/src/protocols/mod.rs",
    "chars": 11221,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/raw_connect.rs",
    "chars": 12095,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/boringssl_openssl/client.rs",
    "chars": 4614,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/boringssl_openssl/mod.rs",
    "chars": 878,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/boringssl_openssl/server.rs",
    "chars": 8622,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/boringssl_openssl/stream.rs",
    "chars": 6171,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/digest.rs",
    "chars": 2420,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/mod.rs",
    "chars": 7476,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/noop_tls/mod.rs",
    "chars": 5051,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/rustls/client.rs",
    "chars": 1470,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/rustls/mod.rs",
    "chars": 745,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/rustls/server.rs",
    "chars": 3766,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/rustls/stream.rs",
    "chars": 11862,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/s2n/client.rs",
    "chars": 1855,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/s2n/mod.rs",
    "chars": 2916,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/s2n/server.rs",
    "chars": 1693,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/tls/s2n/stream.rs",
    "chars": 9746,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/protocols/windows.rs",
    "chars": 4383,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/server/bootstrap_services.rs",
    "chars": 5700,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/server/configuration/mod.rs",
    "chars": 13153,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/server/daemon.rs",
    "chars": 3877,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/server/mod.rs",
    "chars": 29274,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/server/transfer_fd/mod.rs",
    "chars": 16563,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/services/background.rs",
    "chars": 3883,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/services/listening.rs",
    "chars": 11764,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/services/mod.rs",
    "chars": 24150,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/tls/mod.rs",
    "chars": 24812,
    "preview": "// Copyright 2024 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/upstreams/mod.rs",
    "chars": 653,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/upstreams/peer.rs",
    "chars": 25962,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/utils/mod.rs",
    "chars": 3242,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/utils/tls/boringssl_openssl.rs",
    "chars": 4413,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/utils/tls/mod.rs",
    "chars": 878,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/utils/tls/rustls.rs",
    "chars": 7406,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/src/utils/tls/s2n.rs",
    "chars": 6989,
    "preview": "// Copyright 2026 Cloudflare, Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "pingora-core/tests/certs/alt-ca.crt",
    "chars": 2074,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFzzCCA7egAwIBAgIUdmTkBmGw2cEQiP+uCa1TuTBp1aYwDQYJKoZIhvcNAQEL\nBQAwdzELMAkGA1UEBhMCVVMxCzA"
  },
  {
    "path": "pingora-core/tests/certs/alt-server.crt",
    "chars": 1688,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEsTCCApmgAwIBAgIUcEJNjZRw1qumRzsbm9HSdXyCojkwDQYJKoZIhvcNAQEL\nBQAwdzELMAkGA1UEBhMCVVMxCzA"
  },
  {
    "path": "pingora-core/tests/certs/ca.crt",
    "chars": 1939,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIFazCCA1OgAwIBAgIUcvULnZbENoxhjvv5TARdWr24pXcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzA"
  },
  {
    "path": "pingora-core/tests/certs/server.crt",
    "chars": 1570,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEWjCCAkKgAwIBAgIUcEJNjZRw1qumRzsbm9HSdXyCojcwDQYJKoZIhvcNAQEL\nBQAwRTELMAkGA1UEBhMCQVUxEzA"
  },
  {
    "path": "pingora-core/tests/certs/server.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyUoXpICzkly7i\nNIJYRi310ZbDufM+HuGEhh0TR/j"
  },
  {
    "path": "pingora-core/tests/keys/key.pem",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIN5lAOvtlKwtc/LR8/U77dohJmZS30OuezU9gL6vmm6DoAoGCCqGSM49\nAwEHoUQDQgAE2f/1Fm1HjySd"
  },
  {
    "path": "pingora-core/tests/keys/public.pem",
    "chars": 178,
    "preview": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2f/1Fm1HjySdokPq2T0F1xxol9nS\nEYQ+foFINeaWYk+FxMGpriJTBb8A"
  },
  {
    "path": "pingora-core/tests/keys/server.crt",
    "chars": 741,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIB9zCCAZ2gAwIBAgIUMI7aLvTxyRFCHhw57hGt4U6yupcwCgYIKoZIzj0EAwIw\nZDELMAkGA1UEBhMCVVMxCzAJBgN"
  },
  {
    "path": "pingora-core/tests/keys/server.csr",
    "chars": 477,
    "preview": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBJzCBzgIBADBsMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW\nMBQGA1UEBwwNU2FuIEZ"
  },
  {
    "path": "pingora-core/tests/nginx.conf",
    "chars": 2148,
    "preview": "\n#user  nobody;\nworker_processes  1;\n\nerror_log  /dev/stdout;\n#error_log  logs/error.log  notice;\n#error_log  logs/error"
  }
]

// ... and 180 more files (download for full content)

About this extraction

This page contains the full source code of the cloudflare/pingora GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 380 files (3.0 MB), approximately 807.7k tokens, and a symbol index with 4519 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!