Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pubnub/request_handlers/async_aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ async def async_request(self, options_func, cancellation_event):
uuid=uuid,
auth_key=auth_key,
client_request=None,
client_response=response
client_response=response,
http_version=f"HTTP/{response.version.major}.{response.version.minor}" if response.version else None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Interesting, that aiohttp dopes't support HTTP/2 at all.

Copy link
Copy Markdown
Contributor Author

@jguz-pubnub jguz-pubnub May 27, 2026

Choose a reason for hiding this comment

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

Indeed, but there's nothing we can do here, and we need to ensure that our API is transport-resilient. I would keep setting a http_version field for consistency, even though it will always be HTTP/1.1. What's your take on that?

)

# if body is not None and len(body) > 0 and not options.non_json_response:
Expand Down Expand Up @@ -172,7 +173,7 @@ async def async_request(self, options_func, cancellation_event):
else:
data = "N/A"

logger.debug(data)
logger.debug("[%s %s] %s" % (options.operation_type, response_info.http_version, data))

if response.status not in (200, 307, 204):

Expand Down
8 changes: 5 additions & 3 deletions pubnub/request_handlers/async_httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def __init__(self, pubnub):
async def create_session(self):
self._session = httpx.AsyncClient(
timeout=httpx.Timeout(self.pubnub.config.connect_timeout),
transport=self._connector
transport=self._connector,
http2=True
)

async def close_session(self):
Expand Down Expand Up @@ -193,7 +194,8 @@ async def async_request(self, options_func, cancellation_event):
uuid=uuid,
auth_key=auth_key,
client_request=None,
client_response=response
client_response=response,
http_version=response.http_version
)

# if body is not None and len(body) > 0 and not options.non_json_response:
Expand Down Expand Up @@ -224,7 +226,7 @@ async def async_request(self, options_func, cancellation_event):
else:
data = "N/A"

logger.debug(data)
logger.debug("[%s %s] %s" % (options.operation_type, response_info.http_version, data))

if response.status_code not in (200, 307, 204):

Expand Down
15 changes: 7 additions & 8 deletions pubnub/request_handlers/httpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _ensure_session(self):
with self._session_lock:
if self._session is None or self._session.is_closed:
logger.debug("Creating new HTTP session")
self._session = httpx.Client()
self._session = httpx.Client(http2=True)
return self._session

def close(self):
Expand Down Expand Up @@ -318,7 +318,8 @@ def _build_envelope(self, p_options, e_options):
origin=res.url.host,
uuid=uuid,
auth_key=auth_key,
client_request=res.request
client_request=res.request,
http_version=res.http_version
)

if res.status_code not in [200, 204, 307]:
Expand Down Expand Up @@ -433,15 +434,13 @@ def _invoke_request(self, p_options, e_options, base_origin):

try:
res = session.request(**args)
# Safely access response text - read content first for streaming responses

try:
logger.debug("GOT %s" % res.text)
logger.debug("[%s %s] %s" % (e_options.operation_type, res.http_version, res.text))
except httpx.ResponseNotRead:
# For streaming responses, we need to read first
logger.debug("GOT %s" % res.content.decode('utf-8', errors='ignore'))
logger.debug("[%s %s] %s" % (e_options.operation_type, res.http_version, res.content.decode('utf-8', errors='ignore')))
except Exception as e:
# Fallback logging in case of any response reading issues
logger.debug("GOT response (content access failed: %s)" % str(e))
logger.debug("[%s %s] (content access failed: %s)" % (e_options.operation_type, res.http_version, str(e)))

except httpx.ConnectError as e:
if use_watchdog and self._watchdog.triggered:
Expand Down
13 changes: 11 additions & 2 deletions pubnub/request_handlers/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ def _build_envelope(self, p_options, e_options):
origin=url.hostname,
uuid=uuid,
auth_key=auth_key,
client_request=res.request
client_request=res.request,
http_version=(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as aiohttp this one native request based on urllib3 also doesn't support HTTP/2 - they fundraising for it on their webpage.

Copy link
Copy Markdown
Contributor Author

@jguz-pubnub jguz-pubnub May 27, 2026

Choose a reason for hiding this comment

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

Indeed, but there's nothing we can do here, and we need to ensure that our API is transport-resilient. I would keep setting a http_version field for consistency, even though it will always be HTTP/1.1. What's your take on that?

f"HTTP/{res.raw.version // 10}.{res.raw.version % 10}"
if res.raw and res.raw.version else None
)
)

if not res.ok:
Expand Down Expand Up @@ -268,7 +272,12 @@ def _invoke_request(self, p_options, e_options, base_origin):

try:
res = self.session.request(**args)
logger.debug("GOT %s" % res.text)
http_ver = (
f"HTTP/{res.raw.version // 10}.{res.raw.version % 10}"
if res.raw and res.raw.version else "unknown"
)
logger.debug("[%s %s] %s" % (e_options.operation_type, http_ver, res.text))

except requests.exceptions.ConnectionError as e:
raise PubNubException(
pn_error=PNERR_CONNECTION_ERROR,
Expand Down
4 changes: 3 additions & 1 deletion pubnub/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,16 @@ def __init__(self, headers, pn_config):


class ResponseInfo(object):
def __init__(self, status_code, tls_enabled, origin, uuid, auth_key, client_request, client_response=None):
def __init__(self, status_code, tls_enabled, origin, uuid, auth_key, client_request,
client_response=None, http_version=None):
self.status_code = status_code
self.tls_enabled = tls_enabled
self.origin = origin
self.uuid = uuid
self.auth_key = auth_key
self.client_request = client_request
self.client_response = client_response
self.http_version = http_version


class Envelope(object):
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ flake8>=7.1.2
pytest>=8.3.5
pytest-asyncio>=1.0.0
httpx>=0.28
h2>=4.1
h2>=4.3
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is only development dependencies, but there is actual package dependencies which needs to be updated as well in setup.py.

requests>=2.32.2
aiohttp>=3.10.11
cbor2>=5.6
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
install_requires=[
'pycryptodomex>=3.3',
'httpx>=0.28,<1.0',
'h2>=4.1',
'h2>=4.3',
'requests>=2.32.2',
'aiohttp>3.10.11',
'cbor2>=5.6'
Expand Down
Loading