Skip to content

Commit

Permalink
respect httpie config to generate path for "quic.json" cache layer
Browse files Browse the repository at this point in the history
  • Loading branch information
Ousret committed Apr 3, 2024
1 parent bfd7f37 commit 0ffa142
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 14 deletions.
5 changes: 4 additions & 1 deletion httpie/client.py
Expand Up @@ -2,6 +2,7 @@
import json
import sys
import typing
from pathlib import Path
from random import randint
from time import monotonic
from typing import Any, Dict, Callable, Iterable
Expand Down Expand Up @@ -86,6 +87,7 @@ def collect_messages(
disable_ipv6=args.ipv4,
disable_ipv4=args.ipv6,
source_address=source_address,
quic_cache=env.config.quic_file,
)

parsed_url = parse_url(args.url)
Expand Down Expand Up @@ -205,9 +207,10 @@ def build_requests_session(
disable_ipv4: bool = False,
disable_ipv6: bool = False,
source_address: typing.Tuple[str, int] = None,
quic_cache: typing.Optional[Path] = None,
) -> niquests.Session:
requests_session = niquests.Session()
requests_session.quic_cache_layer = QuicCapabilityCache()
requests_session.quic_cache_layer = QuicCapabilityCache(quic_cache)

if resolver:
resolver_rebuilt = []
Expand Down
6 changes: 5 additions & 1 deletion httpie/config.py
Expand Up @@ -149,7 +149,7 @@ def __init__(self, directory: Union[str, Path] = DEFAULT_CONFIG_DIR):
def default_options(self) -> list:
return self['default_options']

def _configured_path(self, config_option: str, default: str) -> None:
def _configured_path(self, config_option: str, default: str) -> Path:
return Path(
self.get(config_option, self.directory / default)
).expanduser().resolve()
Expand All @@ -162,6 +162,10 @@ def plugins_dir(self) -> Path:
def version_info_file(self) -> Path:
return self._configured_path('version_info_file', 'version_info.json')

@property
def quic_file(self) -> Path:
return self._configured_path('quic_file', 'quic.json')

@property
def developer_mode(self) -> bool:
"""This is a special setting for the development environment. It is
Expand Down
16 changes: 6 additions & 10 deletions httpie/ssl_.py
@@ -1,11 +1,10 @@
import ssl
import typing
from pathlib import Path
from typing import NamedTuple, Optional, Tuple, MutableMapping
import json
import os.path
from os import makedirs

from httpie.config import DEFAULT_CONFIG_DIR
from httpie.adapters import HTTPAdapter
# noinspection PyPackageRequirements
from urllib3.util.ssl_ import (
Expand Down Expand Up @@ -38,21 +37,18 @@ class QuicCapabilityCache(
See https://urllib3future.readthedocs.io/en/latest/advanced-usage.html#remembering-http-3-over-quic-support for
the implementation guide."""

__file__ = os.path.join(DEFAULT_CONFIG_DIR, "quic.json")

def __init__(self):
def __init__(self, path: Path):
self._path = path
self._cache = {}
if not os.path.exists(DEFAULT_CONFIG_DIR):
makedirs(DEFAULT_CONFIG_DIR, exist_ok=True)
if os.path.exists(QuicCapabilityCache.__file__):
with open(QuicCapabilityCache.__file__, "r") as fp:
if os.path.exists(path):
with open(path, "r") as fp:
try:
self._cache = json.load(fp)
except json.JSONDecodeError: # if the file is corrupted (invalid json) then, ignore it.
pass

def save(self):
with open(QuicCapabilityCache.__file__, "w+") as fp:
with open(self._path, "w") as fp:
json.dump(self._cache, fp)

def __contains__(self, item: Tuple[str, int]):
Expand Down
44 changes: 43 additions & 1 deletion tests/test_h2n3.py
@@ -1,4 +1,7 @@
from .utils import HTTP_OK, http
import pytest
import json

from .utils import HTTP_OK, http, PersistentMockEnvironment


def test_should_not_do_http1_by_default(remote_httpbin_secure):
Expand Down Expand Up @@ -33,3 +36,42 @@ def test_force_http3(remote_httpbin_secure):

assert "HTTP/3" in r
assert HTTP_OK in r


@pytest.fixture
def with_quic_cache_persistent(tmp_path):
env = PersistentMockEnvironment()
env.config['quic_file'] = tmp_path / 'quic.json'
yield env
env.cleanup(force=True)


def test_ensure_quic_cache(remote_httpbin_secure, with_quic_cache_persistent):
"""
This test aim to verify that the QuicCapabilityCache work as intended.
"""
r = http(
"--verify=no",
remote_httpbin_secure + '/get',
env=with_quic_cache_persistent
)

assert "HTTP/2" in r
assert HTTP_OK in r

r = http(
"--verify=no",
remote_httpbin_secure + '/get',
env=with_quic_cache_persistent
)

assert "HTTP/3" in r
assert HTTP_OK in r

tmp_path = with_quic_cache_persistent.config['quic_file']

with open(tmp_path, "r") as fp:
cache = json.load(fp)

assert len(cache) == 1
assert "pie.dev" in list(cache.keys())[0]
6 changes: 5 additions & 1 deletion tests/test_meta.py
Expand Up @@ -11,7 +11,11 @@ def test_meta_elapsed_time(httpbin):


def test_meta_extended_tls(remote_httpbin_secure):
r = http('--verify=no', '--meta', remote_httpbin_secure + '/get')
# using --verify=no may cause the certificate information not to display with Python < 3.10
# it is guaranteed to be there when using HTTP/3 over QUIC. That's why we set the '--http3' flag.
# it's a known CPython limitation with getpeercert(binary_form=False).
r = http('--verify=no', '--http3', '--meta', remote_httpbin_secure + '/get')

assert 'Connected to' in r
assert 'Connection secured using' in r
assert 'Server certificate' in r
Expand Down

0 comments on commit 0ffa142

Please sign in to comment.