Skip to content

WIP: pass server_name to on_connect handler#105

Merged
ybrs merged 7 commits intomainfrom
sni-servername-from-tls
Oct 23, 2025
Merged

WIP: pass server_name to on_connect handler#105
ybrs merged 7 commits intomainfrom
sni-servername-from-tls

Conversation

@ybrs
Copy link
Copy Markdown
Owner

@ybrs ybrs commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Connection callbacks now accept an optional server_name (e.g., from TLS SNI) and propagate it through the connect flow to user callbacks, enabling hostname-aware handling while remaining backward-compatible.
  • Tests

    • Updated tests to exercise and validate the optional server_name parameter in connect workflows.
  • Chores

    • Dependency bumped to a minor release with an alternate reference retained in comments for integration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 9, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Threads an optional TLS SNI server_name from pgwire through the Rust core into Python: adds server_name to WorkerMessage::Connect, forwards client.sni_server_name() into PythonWorker::on_connect and Python handlers, updates Python connect signatures and tests, and bumps pgwire in Cargo.toml.

Changes

Cohort / File(s) Summary of changes
Dependency updates
Cargo.toml
Bump pgwire from 0.33.0 to 0.34.0; retain a commented alternative git dependency line referencing branch expose-sni.
Python connect API threading
pysrc/riffq/connection.py
Add optional server_name: str = None parameter to BaseConnection.handle_connect and RiffqServer.handle_connect; forward server_name through connection handling calls.
Rust connect message & callback threading
src/lib.rs
Add server_name: Option<String> to WorkerMessage::Connect; extend PythonWorker::on_connect to accept server_name: Option<&str>; pass server_name (from client.sni_server_name()) into worker messages and include it as a Python kwarg; minor test module visibility change.
Tests updated
tests/test_on_connect.py, tests/test_on_connect_custom_error.py
Local test handle_connect functions now accept a keyword-only server_name=None parameter so handlers can inspect SNI when provided while remaining compatible.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Pgwire as pgwire
  participant Core as RustCore
  participant Py as PythonWorker
  participant CB as on_connect_cb

  Client->>Pgwire: STARTUP / TLS handshake
  Note over Pgwire: extract optional SNI -> server_name
  Pgwire->>Core: connection established (ip, port, server_name?)
  Core->>Py: WorkerMessage::Connect { conn_id, ip, port, server_name, responder }
  Py->>CB: on_connect(conn_id, ip, port, server_name=... or None)
  CB-->>Py: BoolCallbackResult
  Py-->>Core: responder response
  Core-->>Pgwire: finalize connect (allow/deny)
  Pgwire-->>Client: continue or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

codex

Poem

I hop through wires with twitching nose,
SNI in paw where the handshake goes 🐇,
From pgwire burrow to Python gate,
I carry the name to help decide fate,
A tiny hop — the connection grows.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "WIP: pass server_name to on_connect handler" directly corresponds to the primary objective of the changeset. Across all modified files—Rust code extracting SNI server names, Python connection handling accepting and forwarding the parameter, and test functions updated to support the new parameter—the core functionality being implemented is exactly what the title describes: propagating server_name to the on_connect handler. The title is concise, specific, and uses clear language that avoids vague terms, making it easily understandable to someone reviewing the project history.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48a3a3b and 2068aaa.

📒 Files selected for processing (1)
  • pysrc/riffq/connection.py (3 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ybrs ybrs changed the title pass server_name to on_connect handler WIP: pass server_name to on_connect handler Oct 9, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib.rs (1)

1196-1214: Critical: Missing server_name argument in on_connect call.

The on_connect call at line 1202 is missing the required server_name argument, causing the compilation failure reported in the CI pipeline. This is the non-authenticated connection path, which should also support SNI-based routing.

Apply this diff to retrieve and pass server_name:

                     let _ = sender.send(id);
                 }
                 let addr = client.socket_addr();
+                // Try to get TLS SNI server_name (if any) from client metadata
+                let server_name = client
+                    .metadata()
+                    .get("server_name")
+                    .cloned();
                 let allowed = self
                     .py_worker
-                    .on_connect(id, addr.ip().to_string(), addr.port())
+                    .on_connect(id, addr.ip().to_string(), addr.port(), server_name)
                     .await;
                 if !allowed.allowed {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9d76ab and bff3692.

📒 Files selected for processing (5)
  • Cargo.toml (1 hunks)
  • pysrc/riffq/connection.py (2 hunks)
  • src/lib.rs (5 hunks)
  • tests/test_on_connect.py (1 hunks)
  • tests/test_on_connect_custom_error.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
pysrc/riffq/connection.py (2)
tests/test_on_connect.py (1)
  • handle_connect (18-19)
tests/test_on_connect_custom_error.py (1)
  • handle_connect (15-16)
tests/test_on_connect_custom_error.py (2)
pysrc/riffq/connection.py (2)
  • handle_connect (37-38)
  • handle_connect (82-85)
tests/test_on_connect.py (1)
  • handle_connect (18-19)
tests/test_on_connect.py (2)
pysrc/riffq/connection.py (2)
  • handle_connect (37-38)
  • handle_connect (82-85)
tests/test_on_connect_custom_error.py (1)
  • handle_connect (15-16)
🪛 GitHub Actions: CI
src/lib.rs

[error] 1202-1202: Step failed: cargo test --verbose. Rust compiler error E0061: on_connect called with 3 arguments, but function signature requires 4 (connection_id: u64, ip: String, port: u16, server_name: Option). Provide the missing server_name argument in the call.

🪛 Ruff (0.13.3)
pysrc/riffq/connection.py

37-37: Unused method argument: ip

(ARG002)


37-37: Unused method argument: port

(ARG002)


37-37: Unused method argument: server_name

(ARG002)

tests/test_on_connect_custom_error.py

15-15: Unused function argument: conn_id

(ARG001)


15-15: Unused function argument: ip

(ARG001)


15-15: Unused function argument: port

(ARG001)


15-15: Unused function argument: server_name

(ARG001)

tests/test_on_connect.py

18-18: Unused function argument: conn_id

(ARG001)


18-18: Unused function argument: ip

(ARG001)


18-18: Unused function argument: port

(ARG001)


18-18: Unused function argument: server_name

(ARG001)

🔇 Additional comments (8)
src/lib.rs (4)

97-97: LGTM!

The addition of server_name: Option<String> to the Connect message properly supports TLS SNI propagation.


716-748: LGTM!

The Connect message handler correctly:

  • Destructures server_name from the message
  • Passes it to Python kwargs as either the value or py.None()
  • Maintains backward compatibility with handlers that don't use it

843-855: LGTM!

The on_connect method signature and implementation correctly accept and forward server_name to the worker message.


1252-1259: LGTM!

The server_name retrieval from client metadata is correctly implemented for the authenticated path.

pysrc/riffq/connection.py (2)

37-37: LGTM!

The addition of server_name=None to BaseConnection.handle_connect properly supports SNI-based connection handling. The static analysis warnings about unused parameters can be safely ignored since this is an abstract base class method that subclasses may override to use these parameters.


82-85: LGTM!

The server_name parameter is correctly added and forwarded to the connection's handle_connect method.

tests/test_on_connect_custom_error.py (1)

15-15: LGTM!

The test's handle_connect signature correctly includes the optional server_name parameter. The static analysis warnings about unused parameters can be safely ignored since this is a test fixture that doesn't need to use all parameters.

tests/test_on_connect.py (1)

18-18: LGTM!

The test's handle_connect signature correctly includes the optional server_name parameter. The static analysis warnings about unused parameters can be safely ignored since this is a test fixture that doesn't need to use all parameters.

Comment thread Cargo.toml Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/lib.rs (1)

718-742: LGTM! Consider simplifying the server_name handling.

The logic correctly passes server_name to the Python callback. The conditional handling of the Option could be simplified.

Consider this more concise approach:

                                     let kwargs = PyDict::new(py);
                                     kwargs.set_item("callback", wrapper.clone_ref(py)).unwrap();
-                                    if let Some(name) = server_name.clone() {
-                                        let _ = kwargs.set_item("server_name", name);
-                                    } else {
-                                        let _ = kwargs.set_item("server_name", py.None());
-                                    }
+                                    let _ = kwargs.set_item("server_name", server_name.as_ref().into_py_any(py).unwrap());

This leverages PyO3's Option conversion and avoids the unnecessary clone().

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04e7d5d and 9d8e058.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • Cargo.toml (2 hunks)
  • src/lib.rs (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unit-tests
  • GitHub Check: unit-tests
🔇 Additional comments (6)
src/lib.rs (6)

98-98: LGTM!

The server_name field addition to the Connect variant is correctly typed and consistent with the message structure.


606-607: Clarify the test module visibility change.

The test module is now compiled in all builds instead of only during tests. This increases binary size and exposes test code unnecessarily.

Was this change intentional? If not, consider reverting:

-mod encode_tests {
+#[cfg(test)]
+mod encode_tests {
     use super::*;

If this change is needed for external testing or debugging, please add a comment explaining the rationale.


845-845: LGTM!

The signature change correctly adds the server_name parameter as a borrowed optional string, which is appropriate for this async function.


852-852: LGTM!

The conversion from Option<&str> to Option<String> is correctly implemented for the message payload.


1202-1206: LGTM!

The on_connect call correctly retrieves and passes the TLS SNI server name via client.sni_server_name(). The comment clearly documents the intent.


1257-1257: LGTM!

The on_connect call in the authentication path is consistent with the non-auth path, ensuring server_name is propagated in both startup flows.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pysrc/riffq/connection.py (1)

227-238: Critical: Missing server_name parameter in method signature.

Line 238 references server_name but it's not defined as a parameter in the method signature on line 227. This will cause a NameError at runtime when the Rust layer invokes this handler with the server_name argument.

Apply this diff to add the missing parameter:

-    def handle_connect(self, connection_id: int, ip: str, port: int, callback: Callable[..., None] = lambda *a, **k: None) -> None:
+    def handle_connect(self, connection_id: int, ip: str, port: int, callback: Callable[..., None] = lambda *a, **k: None, server_name: str | None = None) -> None:
         """Forward a connect notification to the connection instance.
 
         Args:
             connection_id: Server assigned identifier for this client.
             ip: Remote peer IP address.
             port: Remote peer port.
             callback: Function to acknowledge handling.
+            server_name: Optional TLS SNI server name from the client.
         """
         # logger.info(f"new connnection {connection_id} {ip} {port}")
         conn = self.get_connection(connection_id=connection_id)
         conn.handle_connect(ip, port, callback=callback, server_name=server_name)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0636c78 and 8db8bb4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • Cargo.toml (1 hunks)
  • pysrc/riffq/connection.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
pysrc/riffq/connection.py (3)
tests/test_on_connect_custom_error.py (1)
  • handle_connect (15-16)
example/psql-redis/psql_redis.py (1)
  • handle_connect (273-275)
tests/test_on_connect.py (1)
  • handle_connect (18-19)
🪛 Ruff (0.14.1)
pysrc/riffq/connection.py

92-92: Unused method argument: ip

(ARG002)


92-92: Unused method argument: port

(ARG002)


92-92: Unused method argument: server_name

(ARG002)


92-92: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


92-92: Unused lambda argument: a

(ARG005)


92-92: Unused lambda argument: k

(ARG005)


238-238: Undefined name server_name

(F821)

🔇 Additional comments (1)
Cargo.toml (1)

21-23: Good move to crates.io version.

Using the published crates.io version 0.34.0 improves build reproducibility compared to a git dependency. The commented alternative is fine to keep for reference.

Comment thread pysrc/riffq/connection.py
return callback(user == "user" and password == "secret")

def handle_connect(self, ip: str, port: int, callback: Callable[..., None] = lambda *a, **k: None) -> None:
def handle_connect(self, ip: str, port: int, server_name:str=None, callback: Callable[..., None] = lambda *a, **k: None) -> 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.

⚠️ Potential issue | 🟡 Minor

Fix the type hint for server_name.

PEP 484 requires explicit Optional when the default value is None. The current implicit optional type hint should be made explicit.

Apply this diff:

-    def handle_connect(self, ip: str, port: int, server_name:str=None, callback: Callable[..., None] = lambda *a, **k: None) -> None:
+    def handle_connect(self, ip: str, port: int, server_name: str | None = None, callback: Callable[..., None] = lambda *a, **k: None) -> None:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def handle_connect(self, ip: str, port: int, server_name:str=None, callback: Callable[..., None] = lambda *a, **k: None) -> None:
def handle_connect(self, ip: str, port: int, server_name: str | None = None, callback: Callable[..., None] = lambda *a, **k: None) -> None:
🧰 Tools
🪛 Ruff (0.14.1)

92-92: Unused method argument: ip

(ARG002)


92-92: Unused method argument: port

(ARG002)


92-92: Unused method argument: server_name

(ARG002)


92-92: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


92-92: Unused lambda argument: a

(ARG005)


92-92: Unused lambda argument: k

(ARG005)

🤖 Prompt for AI Agents
In pysrc/riffq/connection.py around line 92, the parameter server_name is
annotated as str but has a default None; update the type hint to Optional[str]
and ensure Optional is imported from typing (or typing import Optional alongside
Callable) so the annotation correctly reflects that None is an allowed value;
modify the function signature to use Optional[str] for server_name and add the
import if missing.

@ybrs ybrs merged commit 402e0ff into main Oct 23, 2025
2 of 3 checks passed
@ybrs ybrs deleted the sni-servername-from-tls branch October 23, 2025 20:11
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.

1 participant