Skip to content

refactor: consolidate HTTP client factories with Content-Type headers#203

Open
deanq wants to merge 8 commits intomainfrom
deanq/ae-2106-consolidated-http
Open

refactor: consolidate HTTP client factories with Content-Type headers#203
deanq wants to merge 8 commits intomainfrom
deanq/ae-2106-consolidated-http

Conversation

@deanq
Copy link
Member

@deanq deanq commented Feb 14, 2026

Summary

Consolidates HTTP client creation into centralized factories and ensures consistent Content-Type headers across all HTTP operations.

Changes

  1. Add Content-Type headers to existing factories (3891b3a)

    • httpx and requests factories now include Content-Type: application/json
    • Ensures consistency with aiohttp usage
  2. Add centralized aiohttp session factory (2c85b2b)

    • New get_authenticated_aiohttp_session() function
    • Consistent User-Agent, Authorization, Content-Type headers
    • 300s default timeout for GraphQL operations
    • TCPConnector with ThreadedResolver
    • API key override support for worker propagation
  3. Refactor RunPod API clients (aae56bd)

    • RunpodGraphQLClient and RunpodRestClient use centralized factory
    • Removes ~18 lines of duplicated configuration
    • No behavior changes
  4. Refactor app.py tarball operations (ad5b9cb)

    • download_tarball() and upload_build() use centralized factory
    • Proper session management with context managers
    • Content-Type override for tarball uploads
  5. Add comprehensive test coverage (65401be)

    • 110 lines of new tests
    • Content-Type header validation
    • API key override functionality
    • Timeout configuration tests
    • All 26 tests passing

Benefits

  • Consistency: All HTTP clients use the same header configuration
  • Maintainability: Single source of truth for HTTP client creation
  • Testability: Centralized factories are easier to test and mock
  • Code reduction: Net -16 lines of code (removed duplication)
  • Type safety: All factories properly typed

Test Plan

  • All existing tests pass (983 tests)
  • New tests for HTTP factories (26 tests in test_http.py)
  • Code coverage maintained at 69.25%
  • Linting and formatting checks pass
  • No behavior changes to existing functionality

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes HTTP client/session creation and standardizes default headers (notably Content-Type: application/json) across httpx, requests, and aiohttp, then refactors RunPod API clients and tarball operations to use those factories.

Changes:

  • Add default Content-Type: application/json headers to existing httpx and requests factories.
  • Introduce a centralized aiohttp session factory with consistent headers and timeout/connector configuration.
  • Refactor RunPod API clients and app tarball download/upload flows to use the factories, plus add unit tests covering the factories.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/runpod_flash/core/utils/http.py Adds default JSON Content-Type to factories and introduces get_authenticated_aiohttp_session() for centralized async session configuration.
src/runpod_flash/core/api/runpod.py Refactors GraphQL/REST clients to use the centralized aiohttp session factory.
src/runpod_flash/core/resources/app.py Refactors tarball download/upload to use the centralized requests session factory and context management.
tests/unit/core/utils/test_http.py Adds tests validating headers, override precedence, and timeout behavior for the factories.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@deanq deanq changed the title Consolidate HTTP client factories with Content-Type headers refactor: consolidate HTTP client factories with Content-Type headers Feb 14, 2026
@deanq deanq force-pushed the deanq/ae-2106-consolidated-http branch 2 times, most recently from 496b030 to c4e269f Compare February 26, 2026 02:10
@deanq deanq force-pushed the deanq/ae-2106-consolidated-http branch from dbcf3ab to 9227a33 Compare February 27, 2026 06:23
deanq added a commit that referenced this pull request Feb 27, 2026
- Move raise_for_status() inside session scope with try/finally in app.py
- Add Content-Type header documentation to httpx and requests docstrings
- Add use_threaded_resolver param to aiohttp factory (REST client passes False)
- Add tests for use_threaded_resolver parameter behavior
@runpod-Henrik
Copy link

QA Report

Status: FAIL
PR: #203 — refactor: consolidate HTTP client factories with Content-Type headers
Agent: flash-qa (PR mode)

Targeted Test Results

Area Tests Passed Failed Skipped
HTTP factories (test_http.py) 28 28 0 0
LB stub (test_load_balancer_sls_stub.py) 23 23 0 0
LB handler (test_lb_handler.py) 25 25 0 0
Resource provisioner (test_resource_provisioner.py) 23 22 1 0
Deployment (test_deployment.py) 23 22 1 0
Serverless resources (test_serverless.py) 56 56 0 0

New Failures Introduced by This PR

1. test_create_resource_skips_api_key_when_not_set (tests/unit/runtime/test_resource_provisioner.py:278)

  • Root cause: The PR changed os.getenv("RUNPOD_API_KEY") to get_api_key() in resource_provisioner.py:73. The test clears os.environ with patch.dict(os.environ, {...}, clear=True), but get_api_key() also reads from ~/.config/runpod/credentials.toml. On any machine with saved credentials, the test fails because get_api_key() returns the stored key.
  • Fix: The test needs to also mock get_api_key to return None, e.g., @patch("runpod_flash.runtime.resource_provisioner.get_api_key", return_value=None).

2. test_deploy_fails_when_api_key_missing_for_remote_calls (tests/unit/cli/utils/test_deployment.py:410)

  • Root cause: Same issue. The PR changed os.getenv("RUNPOD_API_KEY") to get_api_key() in deployment.py:207. The test clears env vars but get_api_key() finds a key in the credentials file, so the expected ValueError is never raised. The test then proceeds and fails with FileNotFoundError on a missing .flash/ directory.
  • Fix: Mock get_api_key to return None in the test.

Coverage Impact

Module Coverage
core/utils/http.py 100%
core/api/runpod.py 34.07% (unchanged, low but pre-existing)

Full Suite Results

Suite Passed Failed Notes
Parallel (-m "not serial") 1261 4 2 new failures (above), 2 pre-existing
Serial (-m "serial") 24 10 All pre-existing failures

Pre-existing failures (not caused by this PR):

  • test_remote_concurrency.py (10 serial) — pre-existing on main
  • test_class_execution_integration.py (2) — _constructor_args AttributeError, pre-existing on main

PR Diff Analysis

  • Content-Type headers correct — All three factories (httpx, requests, aiohttp) correctly default to application/json; tarball upload correctly overrides to TARBALL_CONTENT_TYPE
  • All HTTP usages migrated — No remaining direct aiohttp.ClientSession(), requests.Session(), or httpx.AsyncClient() construction outside factories (only dependency_resolver.py codegen template, which is expected)
  • Error handling preserved — raise_for_status() maintained in all call sites
  • api_key_context module fully removed — No remaining imports or references
  • Tests not updated for get_api_key() behavior change — Two tests assume clearing env vars is sufficient, but get_api_key() also reads ~/.config/runpod/credentials.toml

Potential Risk: Auth Headers on Pre-Signed URLs

The tarball download_tarball() and upload_build() methods in app.py now use get_authenticated_requests_session(), which adds Authorization: Bearer <key> to all requests. Previously, these methods only sent User-Agent (and Content-Type for upload). If the upload/download URLs are pre-signed S3 URLs, some S3 configurations may reject requests that include extraneous Authorization headers alongside pre-signed query parameters. This should be verified against the actual RunPod storage backend.

Recommendation

BLOCK — Two tests fail on any machine with stored RunPod credentials (~/.config/runpod/credentials.toml). The fix is straightforward: mock get_api_key in the two failing tests. The pre-signed URL auth header concern should also be verified before merge.


Generated by flash-qa agent

Add "Content-Type: application/json" header to centralized HTTP client
factories for consistency with aiohttp usage and to prevent issues with
JSON endpoints.

- get_authenticated_httpx_client: Add Content-Type header
- get_authenticated_requests_session: Add Content-Type header
Add get_authenticated_aiohttp_session() to provide consistent HTTP client
creation across all client types (httpx, requests, aiohttp).

Features:
- User-Agent header with flash version
- Authorization header with API key support
- Content-Type: application/json
- Configurable timeout (default 300s for GraphQL)
- TCPConnector with ThreadedResolver for DNS
- API key override for worker-to-mothership propagation

This enables consolidation of aiohttp session creation scattered across
RunpodGraphQLClient and RunpodRestClient.
Refactor RunpodGraphQLClient and RunpodRestClient to use the new
get_authenticated_aiohttp_session() factory instead of creating
aiohttp.ClientSession directly.

Benefits:
- Removes ~18 lines of duplicated configuration
- Consistent headers across all HTTP clients
- Centralized timeout and connector configuration
- Easier to test and maintain

No behavior changes - same headers, timeout, and connector settings.
Refactor download_tarball() and upload_build() to use
get_authenticated_requests_session() instead of direct requests.get/put.

Benefits:
- Automatic User-Agent header inclusion
- Consistent with other HTTP operations
- Proper session management with context manager
- Centralized configuration

Changes:
- download_tarball: Use session factory for presigned URL download
- upload_build: Use session factory with Content-Type override for tarball upload
Add tests for Content-Type headers and API key override functionality
in existing factories. Add complete test suite for new aiohttp factory.

New tests:
- Content-Type header presence (httpx, requests, aiohttp)
- API key override parameter (httpx, requests, aiohttp)
- aiohttp session creation with User-Agent
- aiohttp API key inclusion/exclusion
- aiohttp custom/default timeouts (300s)

All 26 tests passing.
- Move raise_for_status() inside session scope with try/finally in app.py
- Add Content-Type header documentation to httpx and requests docstrings
- Add use_threaded_resolver param to aiohttp factory (REST client passes False)
- Add tests for use_threaded_resolver parameter behavior
…olution

Replace raw os.getenv("RUNPOD_API_KEY") with get_api_key() from
core.credentials in CLI-context code. This supports credentials.toml
fallback from 'flash login' in addition to environment variables.
Remove dead code from the abandoned per-request API key propagation
concept. The LB handler no longer extracts inbound Bearer tokens, and
stubs rely on the standard credentials module for API key resolution.

- Delete runtime/api_key_context.py (contextvars-based propagation)
- Remove extract_api_key_middleware from lb_handler.py
- Remove context var usage from load_balancer_sls.py
@deanq deanq force-pushed the deanq/ae-2106-consolidated-http branch from 7dc5ff2 to fc2b70a Compare February 28, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants