Skip to main content

HTTP delegate base class with retry support

Project description

csrd-delegate

HTTP client delegate base class with retry support for FastAPI microservices.

Package: csrd.delegate · Import: from csrd.delegate import BaseDelegate

What's included

  • BaseDelegate — async HTTP client with header forwarding, retry via tenacity, and lifecycle (close() / async with)
  • Response parsing via csrd.models.model_parser
  • Configurable retry profiles (conservative, aggressive, resilient)
  • httpx-specific response types (ResponseHandler, ResponseHandlerMap)

Installation

uv pip install "csrd-delegate @ git+ssh://git@github.com/csrd-api/fastapi-common.git#subdirectory=packages/delegate"

Dependencies

  • csrd-models, csrd-context (Tier 2)

Consumer Feature Matrix (BaseDelegate)

Capability area Common expectation Actual support How to use / notes
HTTP methods Standard verb helpers exist ✅ Built-in get, post, put, patch, delete, head, options
Retry support Automatic retries for transient failures ✅ Built-in + configurable Set default in constructor (retry_profile / retry_*) or per call
Retry presets Named retry policies ✅ Built-in conservative, aggressive, resilient
Auth forwarding Bearer token from incoming request is forwarded ✅ Built-in Uses request context headers; can be filtered explicitly
Header filtering Hop-by-hop headers are removed safely ✅ Built-in RFC-hop headers filtered by default; body requests additionally filter content-length
Response parsing Auto-parse JSON to typed model ✅ Built-in + configurable Use response_model and/or model_handler
Status handling Non-2xx statuses raise exceptions ✅ Built-in + overridable Default raises HTTPException; override specific statuses with response_handlers
Per-status custom behavior Custom handling for 404/409/etc ✅ Built-in Pass response_handlers={404: ...} style map
Client lifecycle Safe async with usage and close management ✅ Built-in Delegate-owned client closes automatically; injected client is not closed
Per-call transport overrides Override timeout/headers/auth per request ✅ Built-in timeout, headers, auth, follow_redirects, etc. on each call
Circuit breaker Open/half-open breaker semantics ❌ Not built-in Add externally (middleware/proxy/custom wrapper)
Rate limiting Outbound request throttling ❌ Not built-in Add externally (gateway/proxy/custom transport)
Service discovery Dynamic upstream resolution/registry ❌ Not built-in Provide concrete service_host at wiring time

Inter-Service Auth Propagation

In multi-service clusters, delegates automatically forward authentication headers from the inbound request to upstream service calls. This enables transparent claim propagation through delegate chains.

How It Works

  1. Client → Service A: Client sends JWT via Authorization: Bearer <token> header
  2. Service A extracts and verifies claims using VerifiedTokenDep (or similar)
  3. Service A → Service B (via BaseDelegate): Delegate automatically includes Authorization header from inbound request
  4. Service B receives and verifies same JWT from Service A's outbound call
  5. Service B → Service C (if needed): Claim propagation continues transitively

Example: Multi-Hop Orchestration

# quote-service orchestrates inventory + pricing calls
class QuoteDelegate(BaseDelegate):
    def __init__(self) -> None:
        super().__init__(settings.quote_service_url, retry_profile="conservative")

    async def compose_quote(self, item_id: str, quantity: int):
        # All three calls automatically include Authorization header
        inventory_data = await self.get(f"/api/inventory/{item_id}")
        pricing_data = await self.get(f"/api/pricing/{item_id}")
        return {
            "item_id": item_id,
            "quantity": quantity,
            "stock_available": inventory_data["available_qty"],
            "price": pricing_data["base_price"],
        }

# On inbound request with JWT:
@router.post("/api/quotes")
async def create_quote(
    item_id: str,
    quantity: int,
    verified: VerifiedTokenDep,  # JWT verified here
    quote_delegate: QuoteDelegate,
) -> dict:
    # QuoteDelegate.compose_quote() calls use verified user's JWT
    return await quote_delegate.compose_quote(item_id, quantity)

Header Filtering Behavior

  • Hop-by-hop headers (RFC 7230): Automatically filtered (connection, keep-alive, etc.)
  • Authorization header: Always forwarded unless explicitly filtered
  • Content-Length: Re-computed by httpx for each request
  • Custom headers: Passed through by default; filter via header_filter_list if needed

Pattern Constraints

  1. No auth bypass: Each hop validates the JWT independently
  2. Claim consistency: All services see the same sub, roles, etc.
  3. No credential injection: Delegates don't add credentials; they forward inbound claims only
  4. No header injection: Custom headers must be passed explicitly; inbound headers only forwarded if present

When to Use Manual Header Control

Override the automatic forwarding only in specific cases:

class InternalDelegate(BaseDelegate):
    def __init__(self) -> None:
        super().__init__(
            settings.internal_service_url,
            header_filter_list=["authorization", "x-tenant-id"],  # Don't forward these
            ignore_incoming_headers=False,  # Still use get_headers() for others
        )

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

csrd_delegate-0.3.38.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

csrd_delegate-0.3.38-py3-none-any.whl (8.3 kB view details)

Uploaded Python 3

File details

Details for the file csrd_delegate-0.3.38.tar.gz.

File metadata

  • Download URL: csrd_delegate-0.3.38.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for csrd_delegate-0.3.38.tar.gz
Algorithm Hash digest
SHA256 db8c52d0d5e3ef77ae4d015e4177f2bca3a30b0866929ea50d62203ec511e55b
MD5 4b35f925220f6967cf1f5233487d4ece
BLAKE2b-256 d062128274fe26b2b78930baf75fefb6f668c51d20f298eb9cff9bb12dc88f43

See more details on using hashes here.

Provenance

The following attestation bundles were made for csrd_delegate-0.3.38.tar.gz:

Publisher: release.yml on csrd-api/fastapi-common

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file csrd_delegate-0.3.38-py3-none-any.whl.

File metadata

  • Download URL: csrd_delegate-0.3.38-py3-none-any.whl
  • Upload date:
  • Size: 8.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for csrd_delegate-0.3.38-py3-none-any.whl
Algorithm Hash digest
SHA256 0115af57d875517a9783bf64a5b9f1e46ff91b2b358fe9d56327fcc1ce1e8322
MD5 c9c781716ffd7f54cee5dabaf09b58c2
BLAKE2b-256 8bdd14348221c263a793d7965584d051d59e5acb403f3e4287a8bcbb364e4e8f

See more details on using hashes here.

Provenance

The following attestation bundles were made for csrd_delegate-0.3.38-py3-none-any.whl:

Publisher: release.yml on csrd-api/fastapi-common

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page