Skip to content

[DO NOT MERGE] Almost complete rustls integration rewrite#8007

Draft
im-0 wants to merge 23 commits into
RustPython:mainfrom
im-0:rustls-integration-rewrite
Draft

[DO NOT MERGE] Almost complete rustls integration rewrite#8007
im-0 wants to merge 23 commits into
RustPython:mainfrom
im-0:rustls-integration-rewrite

Conversation

@im-0
Copy link
Copy Markdown
Contributor

@im-0 im-0 commented Jun 1, 2026

Why do not merge?

  • I need to re-read this code few more times.
  • For sure it breaks something else.
  • "It works on my machine":
    • test.test_ssl, test.test_asyncio, extra_tests and some very basic examples that use requests.

rustls integration rewrite

Main changes:

  • Now it contains understandable connection state machine instead of a handful of entangled variables.
  • OID and NID mappings are generated from OpenSSL data files. Some mappings are still hardcoded but those are much smaller than before and are actually testable.
  • OpenSSL-compatible cipher list string (man openssl-ciphers) is parsed correctly (I hope so).
  • Difference between socket IO and buffered IO handling is minimal.

Please check the module-level doc comment in crates/stdlib/src/rustls.rsfor additional information.

What is missing:

  • ssl timeout support. Normal socket timeouts still work just fine but Python's ssl module implementation handles timeouts for whole SSL/TLS operations, like handshake. This does not break anything and just makes timeouts imprecise so I believe that proper timeouts can be implemented later.
  • Socket IO still uses _socket.socket.send/recv instead of methods of the socket object itself. Otherwise everything hangs and this must be investigated.
  • See other TODO items in crates/stdlib/src/rustls.rs

Changed tests

Enabled:

  • test_create_server_ssl_verified
  • test_create_connection_memory_leak
  • test_handshake_timeout_handler_leak
  • test_tls_unique_channel_binding - now correctly skipped because we monkey-patch ssl.CHANNEL_BINDING_TYPES
  • test_sni_callback_alert
  • test_sni_callback_raising
  • test_sni_callback_wrong_return_type
  • test_sendfile_ssl_pre_and_post_data - re-enabled again after previous breakage

Skipped with rustls implementation specifically (should work with OpenSSL):

  • test_load_dh_params - rustls does not support Diffie-Hellman key exchange (it uses elliptic curve Diffie-Hellman)
  • test_get_ca_certs_capath - test expects that certificates are loaded lazily which is hard to implement with rustls
  • test_verify_strict - rustls does not perform some cert checks that OpenSSL does in strict mode

Previously disabled tests changed to rustls-specific skip:

  • test_session - current rustls does not expose session info
  • test_session_handling
  • test_msg_callback_tls12 - _msg_callback cannot be implemented with rustls

I also added a test for BIO mode in extra_tests/.

Related changes

  • Module monkey-patching support. This is used to patch ssl.CHANNEL_BINDING_TYPES hardcoded by Python-native module imported from cpython.
  • Serde serializer that serializes Rust types into Python objects using basic types (dicts, lists, etc). It is used to serialize certificate data as Python code expects it as a dict of tuples of tuples (of tuples). Not sure what is the right place for it but currently it resides in crates/vm/src/convert/rust_py_serde.rs.

Summary by CodeRabbit

  • New Features

    • Added support for AWS-LC (AWS Libcrypto) and FIPS-enabled TLS providers.
    • Added Serde serialization support for converting Rust values to Python objects.
    • Added module-loaded hooks for custom module initialization callbacks.
  • Bug Fixes

    • Improved TLS/SSL error handling with enhanced OpenSSL-compatible error mapping.
  • Chores

    • Updated TLS cryptographic dependencies and provider configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 7d958f89-6a69-4a40-bbed-601f57b11b8b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements a comprehensive TLS migration from OpenSSL to Rustls with AWS-LC/FIPS support, adds Rust-to-Python Serde serialization infrastructure with module-loaded hooks, includes cryptographic OID reference data, and performs socket module cleanup.

Changes

TLS/Rustls Migration with Error Handling & Provider Support

Layer / File(s) Summary
TLS feature flags and dependency configuration
Cargo.toml, crates/stdlib/Cargo.toml, crates/stdlib/src/lib.rs
Added ssl-rustls-aws-lc and ssl-rustls-fips feature variants; reorganized TLS dependencies from OpenSSL ecosystem (rustls-native-certs, rustls-pemfile, webpki-roots) to Rustls ecosystem (rustls-pki-types, rustls-webpki, x509-parser, pkcs8, oid-registry); switched serde from serde_core to serde with derive+alloc features; redirected ssl module to use rustls.rs backing.
OID reference data files
crates/stdlib/src/rustls-data/obj_mac.num, crates/stdlib/src/rustls-data/objects.txt
Added 1501-line numeric OID identifier mapping table and comprehensive 2069-line hierarchical OID definitions covering X.509/PKIX extensions, cryptographic algorithms (RSA/DSA/ECDSA/GOST), symmetric ciphers (AES/Camellia/ARIA/SEED/SM4), KDF/PRF identifiers, and modern ML-KEM variants.
Rustls-to-OpenSSL error mapping
crates/stdlib/src/ssl/compat.rs
Refactored SslError enum to add PEM/DER/cadata-related variants and OpenSslReason(i32) passthrough; updated from_rustls to map PeerIncompatible subtypes and NoApplicationProtocol to specific OpenSSL reason codes; added helper functions producing [SSL: ...] formatted exceptions with library/reason attributes.
Removal of OpenSSL-specific modules
crates/stdlib/src/ssl/cert.rs, crates/stdlib/src/ssl/oid.rs, crates/stdlib/src/ssl/error.rs
Removed entire cert.rs (1780 lines) with PEM/DER parsing, certificate-to-dict conversion, and multiple ServerCertVerifier implementations; removed oid.rs (465 lines) with OID lookup tables and functions; removed create_ssl_syscall_error from error.rs.
TLS provider cipher suite configuration
crates/stdlib/src/ssl/providers.rs, examples/custom_tls_providers.rs, src/interpreter.rs
Added default_cipher_suites optional field to CryptoExt; introduced default_ciphers_or_provider() helper method; updated ring and graviola provider examples and default TLS provider to explicitly configure default cipher suites.
TLS testing and regression validation
extra_tests/snippets/stdlib_ssl_bio_unencrypted_trailer.py
Added regression test for BIO-based SSL with unencrypted trailer handling, validating proper data isolation between paired client/server contexts after TLS handshake completion.
Socket module re-export cleanup
crates/stdlib/src/socket.rs
Narrowed module re-exports to only timeout_error_msg, removing PySocket, SockWaitKind, and sock_wait wrapper; shifted timeout signaling responsibility to sock_wait_deadline.

VM Serde Infrastructure and Module-Loaded Hooks

Layer / File(s) Summary
VM module-loaded hook infrastructure
crates/vm/src/vm/mod.rs, crates/vm/src/vm/interpreter.rs
Extended PyGlobalState with module_loaded_hooks registry; added register_module_loaded_hook() and run_module_loaded_hooks() VM methods; initialized hook system in VM constructor; added test demonstrating hook registration and module patching.
Module import integration with hooks
crates/vm/src/import.rs
Updated import_module_level to invoke registered module-loaded hooks after module resolution, propagating hook errors.
Rust-to-Python Serde serializer implementation
crates/vm/src/convert/rust_py_serde.rs, crates/vm/Cargo.toml
Implemented RustPySerDe Serde serializer converting Rust primitives/structures into Python objects with configurable sequence/tuple/variant handling; added specialized serializers for maps, sequences, and variants; error handling bridges Python exceptions and Serde error messages; added dev-dependency on serde_bytes.
Serde module exports and convenience methods
crates/vm/src/convert/mod.rs, crates/vm/src/vm/mod.rs
Re-exported Serde types and serializers from rust_py_serde module behind feature gate; added with_serde() and with_serde_conf() convenience methods on VirtualMachine for simple serialization flows with automatic error conversion to Python exceptions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ShaharNaveh
  • youknowone

Poem

🐰 Hops with delight at the TLS refactor grand,
Rustls replaces OpenSSL across the land,
Serde serializes with hooks so fine,
OID data blooms—a cryptographic design!
From socket cleanup to provider arrays bright,
This PR shines with renewed TLS light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes a major architectural rewrite of the rustls integration, which is the primary focus of all file changes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] lib: cpython/Lib/ssl.py
[ ] test: cpython/Lib/test/test_ssl.py (TODO: 15)

dependencies:

  • ssl

dependent tests: (51 tests)

  • ssl: test_asyncio test_ftplib test_httplib test_httpservers test_imaplib test_logging test_poplib test_ssl test_urllib test_urllib2_localnet test_venv test_xmlrpc
    • asyncio.selector_events: test_asyncio
    • ftplib: test_urllib2
      • urllib.request: test_http_cookiejar test_pathlib test_pydoc test_sax test_site test_urllib2net test_urllibnet
    • http.client: test_docxmlrpc test_hashlib test_ucn test_unicodedata test_wsgiref
      • logging.handlers: test_concurrent_futures test_pkgutil
    • http.server: test_robotparser
      • pydoc: test_enum
    • smtplib: test_smtplib test_smtpnet
    • urllib.request:
      • pathlib: test_ast test_dbm_sqlite3 test_ensurepip test_importlib test_launcher test_pathlib test_peg_generator test_runpy test_tarfile test_tempfile test_tomllib test_tools test_traceback test_unparse test_winapi test_zipapp test_zipfile test_zoneinfo test_zstd

[x] lib: cpython/Lib/asyncio
[ ] test: cpython/Lib/test/test_asyncio (TODO: 26)

dependencies:

  • asyncio

dependent tests: (5 tests)

  • asyncio: test_asyncio test_inspect test_logging test_os test_unittest

[x] lib: cpython/Lib/http
[x] test: cpython/Lib/test/test_httplib.py (TODO: 3)
[x] test: cpython/Lib/test/test_http_cookiejar.py
[ ] test: cpython/Lib/test/test_http_cookies.py
[x] test: cpython/Lib/test/test_httpservers.py (TODO: 1)

dependencies:

  • http

dependent tests: (47 tests)

  • http: test_docxmlrpc test_genericalias test_hashlib test_http_cookiejar test_http_cookies test_httplib test_httpservers test_logging test_robotparser test_ssl test_ucn test_unicodedata test_urllib test_urllib2 test_urllib2_localnet test_wsgiref test_xml_dom_xmlbuilder test_xmlrpc
    • logging.handlers: test_concurrent_futures test_pkgutil
    • pydoc: test_enum test_pydoc
    • urllib.request: test_pathlib test_sax test_site test_urllib2net test_urllibnet
      • pathlib: test_ast test_dbm_sqlite3 test_ensurepip test_importlib test_launcher test_pathlib test_peg_generator test_runpy test_tarfile test_tempfile test_tomllib test_tools test_traceback test_unparse test_venv test_winapi test_zipapp test_zipfile test_zoneinfo test_zstd

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@fanninpm
Copy link
Copy Markdown
Contributor

fanninpm commented Jun 1, 2026

I wonder if @coderabbitai will review a PR in draft status if @coderabbitai is tagged to review it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

@fanninpm Yes! CodeRabbit will review a PR in draft status when explicitly tagged. Posting @coderabbitai review (or being tagged as a reviewer) will trigger a full review regardless of draft status.

By default, CodeRabbit skips automatic reviews on draft PRs, but an explicit mention overrides that behavior. You can also configure this via the draft_pr_reviews setting in your .coderabbit.yaml if you'd like drafts to be reviewed automatically:

reviews:
  auto_review:
    drafts: true

Would you like me to go ahead and review this PR now?

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

🧹 Nitpick comments (3)
crates/stdlib/src/ssl/compat.rs (2)

368-381: ⚡ Quick win

Using .expect() on fallible operations in error handling path.

Lines 369-370 use .expect("unlikely to happen") on create_pem_ssl_error. While attribute-setting failures are indeed rare (typically only on OOM), panicking in an error-handling path is undesirable. Consider using .unwrap_or_else() to fall back to a simpler error without attributes, matching the pattern used elsewhere in this file (e.g., line 386).

♻️ Proposed fix to avoid panic in error path
-            Self::PemLib(msg) => Self::create_pem_ssl_error(vm, format!("PEM lib: {msg}"))
-                .expect("unlikely to happen"),
+            Self::PemLib(msg) => Self::create_pem_ssl_error(vm, format!("PEM lib: {msg}"))
+                .unwrap_or_else(|_| Self::create_plain_ssl_error(vm, format!("PEM lib: {msg}"))),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/stdlib/src/ssl/compat.rs` around lines 368 - 381, The PemLib arm
panics by calling .expect(...) on the fallible create_pem_ssl_error; change it
to handle failure like other arms by using unwrap_or_else to fall back to a
simpler error (e.g., call Self::create_plain_ssl_error with a "PEM lib: {msg}"
message) so create_pem_ssl_error(vm, format!("PEM lib:
{msg}")).unwrap_or_else(|_| Self::create_plain_ssl_error(vm, format!("PEM lib:
{msg}"))). Ensure you update the Self::PemLib match arm to use unwrap_or_else
and reference create_pem_ssl_error and create_plain_ssl_error accordingly.

225-252: 💤 Low value

Audit rustls::PeerIncompatible coverage vs rustls 0.23 variants (crates/stdlib/src/ssl/compat.rs, from_rustls match ~225-252)

The mapping only covers some rustls::PeerIncompatible variants in rustls 0.23; the _ => arm currently handles other existing variants (e.g., ExtendedMasterSecretExtensionRequired, IncorrectCertificateTypeExtension, NullCompressionRequired, ServerSentHelloRetryRequestWithUnknownExtension, UnsolicitedCertificateTypeExtension, ServerRejectedEncryptedClientHello(...)) by falling back to Self::Ssl(format!("peer is incompatible: {peer_err:?}")) rather than an OpenSSL reason code. Add explicit arms for the remaining variants or document that this generic fallback is intentional (since the enum is non-exhaustive).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/stdlib/src/ssl/compat.rs` around lines 225 - 252, The match over
rustls::PeerIncompatible in from_rustls currently falls through to a generic
Ssl(...) fallback for many 0.23 variants; update the match to explicitly handle
the remaining PeerIncompatible variants (e.g.,
ExtendedMasterSecretExtensionRequired, IncorrectCertificateTypeExtension,
NullCompressionRequired, ServerSentHelloRetryRequestWithUnknownExtension,
UnsolicitedCertificateTypeExtension, ServerRejectedEncryptedClientHello, etc.)
and map each to the appropriate OpenSSL reason constant using
Self::OpenSslReason(SSL_R_...) where sensible, or if you intentionally want a
generic fallback keep the fallback but add a comment documenting that the enum
is non_exhaustive and the generic Self::Ssl(format!(...)) is deliberate; touch
the match in from_rustls so all current PeerIncompatible variants are either
explicitly mapped to an SSL_R_* constant (using the same style as existing arms)
or a documented fallback is left.
crates/vm/src/vm/mod.rs (1)

2378-2401: ⚡ Quick win

Cover the first-load hook path too.

This test only exercises the sys.modules cache-hit case because sys is already imported before Line 2383. The downstream ssl hook in crates/stdlib/src/rustls.rs:164-177 is registered during module execution and depends on the initial _find_and_load path, so a regression there would currently slip through.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/vm/src/vm/mod.rs` around lines 2378 - 2401, The test only covers the
cache-hit path; update module_loaded_hook_can_patch_imported_module to also
exercise the initial load path by registering the hook (mark_module_loaded)
before ensuring the target module is not present in sys.modules and then
importing it so the interpreter takes the _find_and_load path; specifically, in
the vm.enter closure call vm.register_module_loaded_hook("sys",
mark_module_loaded) then remove the "sys" entry from sys.modules (via the VM sys
module/object APIs) or choose a fresh module name that is guaranteed not to be
pre-imported, then call vm.import(...) to trigger the initial-load hook and
assert the "__rustpython_module_loaded_hook_ran__" attribute as the existing
test does (referencing module_loaded_hook_can_patch_imported_module,
mark_module_loaded, vm.register_module_loaded_hook, and vm.import).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/vm/src/convert/rust_py_serde.rs`:
- Around line 13-17: The code currently uses unimplemented!() for unit and
unit-struct handling inside RustPySerDe which causes a panic; instead implement
proper Serde handling that returns a Result (so errors are converted to Python
exceptions via with_serde_conf()) and treat unit/unit-struct as Python None: in
the serializer paths for unit/unit_struct return Ok(vm.ctx.none().into()) (or
the equivalent PyObject/Value representing None) and in the deserializer/visitor
paths accept Python None and return Ok(()) (or the appropriate unit value)
rather than panicking; update the functions/match arms that reference
unit/unit_struct in RustPySerDe so they return Result-based values and use the
existing with_serde_conf/error helpers to map failures to Python exceptions.

---

Nitpick comments:
In `@crates/stdlib/src/ssl/compat.rs`:
- Around line 368-381: The PemLib arm panics by calling .expect(...) on the
fallible create_pem_ssl_error; change it to handle failure like other arms by
using unwrap_or_else to fall back to a simpler error (e.g., call
Self::create_plain_ssl_error with a "PEM lib: {msg}" message) so
create_pem_ssl_error(vm, format!("PEM lib: {msg}")).unwrap_or_else(|_|
Self::create_plain_ssl_error(vm, format!("PEM lib: {msg}"))). Ensure you update
the Self::PemLib match arm to use unwrap_or_else and reference
create_pem_ssl_error and create_plain_ssl_error accordingly.
- Around line 225-252: The match over rustls::PeerIncompatible in from_rustls
currently falls through to a generic Ssl(...) fallback for many 0.23 variants;
update the match to explicitly handle the remaining PeerIncompatible variants
(e.g., ExtendedMasterSecretExtensionRequired, IncorrectCertificateTypeExtension,
NullCompressionRequired, ServerSentHelloRetryRequestWithUnknownExtension,
UnsolicitedCertificateTypeExtension, ServerRejectedEncryptedClientHello, etc.)
and map each to the appropriate OpenSSL reason constant using
Self::OpenSslReason(SSL_R_...) where sensible, or if you intentionally want a
generic fallback keep the fallback but add a comment documenting that the enum
is non_exhaustive and the generic Self::Ssl(format!(...)) is deliberate; touch
the match in from_rustls so all current PeerIncompatible variants are either
explicitly mapped to an SSL_R_* constant (using the same style as existing arms)
or a documented fallback is left.

In `@crates/vm/src/vm/mod.rs`:
- Around line 2378-2401: The test only covers the cache-hit path; update
module_loaded_hook_can_patch_imported_module to also exercise the initial load
path by registering the hook (mark_module_loaded) before ensuring the target
module is not present in sys.modules and then importing it so the interpreter
takes the _find_and_load path; specifically, in the vm.enter closure call
vm.register_module_loaded_hook("sys", mark_module_loaded) then remove the "sys"
entry from sys.modules (via the VM sys module/object APIs) or choose a fresh
module name that is guaranteed not to be pre-imported, then call vm.import(...)
to trigger the initial-load hook and assert the
"__rustpython_module_loaded_hook_ran__" attribute as the existing test does
(referencing module_loaded_hook_can_patch_imported_module, mark_module_loaded,
vm.register_module_loaded_hook, and vm.import).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 0ca7babc-3895-4256-8a71-a9ba266cfb79

📥 Commits

Reviewing files that changed from the base of the PR and between 885cf5c and a3ef4a9.

⛔ Files ignored due to path filters (5)
  • Cargo.lock is excluded by !**/*.lock
  • Lib/test/test_asyncio/test_events.py is excluded by !Lib/**
  • Lib/test/test_asyncio/test_sendfile.py is excluded by !Lib/**
  • Lib/test/test_asyncio/test_ssl.py is excluded by !Lib/**
  • Lib/test/test_ssl.py is excluded by !Lib/**
📒 Files selected for processing (22)
  • Cargo.toml
  • crates/stdlib/Cargo.toml
  • crates/stdlib/src/lib.rs
  • crates/stdlib/src/rustls-data/obj_mac.num
  • crates/stdlib/src/rustls-data/objects.txt
  • crates/stdlib/src/rustls.rs
  • crates/stdlib/src/socket.rs
  • crates/stdlib/src/ssl.rs
  • crates/stdlib/src/ssl/cert.rs
  • crates/stdlib/src/ssl/compat.rs
  • crates/stdlib/src/ssl/error.rs
  • crates/stdlib/src/ssl/oid.rs
  • crates/stdlib/src/ssl/providers.rs
  • crates/vm/Cargo.toml
  • crates/vm/src/convert/mod.rs
  • crates/vm/src/convert/rust_py_serde.rs
  • crates/vm/src/import.rs
  • crates/vm/src/vm/interpreter.rs
  • crates/vm/src/vm/mod.rs
  • examples/custom_tls_providers.rs
  • extra_tests/snippets/stdlib_ssl_bio_unencrypted_trailer.py
  • src/interpreter.rs
💤 Files with no reviewable changes (3)
  • crates/stdlib/src/ssl/error.rs
  • crates/stdlib/src/ssl/oid.rs
  • crates/stdlib/src/ssl/cert.rs

Comment thread crates/vm/src/convert/rust_py_serde.rs Outdated
Comment on lines +13 to +17
/// Panics on unit values and unit structures.
pub struct RustPySerDe<'a> {
vm: &'a VirtualMachine,
conf: RustPySerDeConf,
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Jun 1, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Don't panic on valid unit values.

Unit and unit-struct values are normal Serde inputs. unimplemented!() here will unwind the VM instead of producing a Python exception, because with_serde_conf() only translates Result errors.

Suggested fix
-/// Panics on unit values and unit structures.
 pub struct RustPySerDe<'a> {
     vm: &'a VirtualMachine,
     conf: RustPySerDeConf,
 }
@@
     fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
-        unimplemented!("BUG: Unit value cannot be serialized into a Python object")
+        Ok(self.vm.ctx.none())
     }
 
     fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
-        unimplemented!("BUG: Unit struct value cannot be serialized into a Python object")
+        Ok(self.vm.ctx.none())
     }

Also applies to: 120-125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/vm/src/convert/rust_py_serde.rs` around lines 13 - 17, The code
currently uses unimplemented!() for unit and unit-struct handling inside
RustPySerDe which causes a panic; instead implement proper Serde handling that
returns a Result (so errors are converted to Python exceptions via
with_serde_conf()) and treat unit/unit-struct as Python None: in the serializer
paths for unit/unit_struct return Ok(vm.ctx.none().into()) (or the equivalent
PyObject/Value representing None) and in the deserializer/visitor paths accept
Python None and return Ok(()) (or the appropriate unit value) rather than
panicking; update the functions/match arms that reference unit/unit_struct in
RustPySerDe so they return Result-based values and use the existing
with_serde_conf/error helpers to map failures to Python exceptions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This will not work for Option<()>.

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.

@im-0, you're right. Some(()) would call serialize_some().serialize(serializer)serialize_unit(), which with my fix would produce Python None — making it indistinguishable from an actual None. The unimplemented!() is a deliberate guard against this ambiguity, and the struct-level doc comment already documents the limitation. I'll withdraw the suggestion.

🐇 (˵ •̀ ω •́ ˵)

@fanninpm
Copy link
Copy Markdown
Contributor

fanninpm commented Jun 1, 2026

For @im-0:

  • Did you run cargo clippy --workspace?
  • Have you taken a look at RustPython's AI policy?
  • Are you sure you didn't mean to submit this PR as a draft? According to this comment, CodeRabbit will review a PR in draft status when explicitly tagged. (This is also good for @joshuamegnauth54 to know.)

@im-0
Copy link
Copy Markdown
Contributor Author

im-0 commented Jun 1, 2026

@fanninpm

Did you run cargo clippy --workspace?

It is broken (and not by me):

error: venvlauncher is only supported on Windows
 --> crates/venvlauncher/src/main.rs:8:1
  |
8 | compile_error!("venvlauncher is only supported on Windows");
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: could not compile `rustpython-venvlauncher` (bin "venvlaunchert") due to 1 previous error
warning: build failed, waiting for other jobs to finish...

However, I did run cargo clippy and fixed all warnings in my code with default features.

Have you taken a look at RustPython's AI policy?

Not yet. Because I started working on this before it was written. In my own defense I can say that I started working on this EXACTLY because previous version was vibe coded (and did not work for my cases). I used LLM for some simple fixes (mostly to make reported errors into what cpython tests expect and for some debugging). Most of rustls.rs was written manually by me (excluding constants that I kept from previous implementation).

Are you sure you didn't mean to submit this PR as a draft? According to #8007 (comment), CodeRabbit will review a PR in draft status when explicitly tagged. (This is also good for @joshuamegnauth54 to know.)

I needed cpython test suite running on macos and windows because I do not have access to those at the moment.

@fanninpm
Copy link
Copy Markdown
Contributor

fanninpm commented Jun 1, 2026

Are you sure you didn't mean to submit this PR as a draft?

I needed cpython test suite running on macos and windows because I do not have access to those at the moment.

Those test suites will still run even if the PR is in draft status.

@im-0 im-0 marked this pull request as draft June 1, 2026 20:58
@im-0 im-0 force-pushed the rustls-integration-rewrite branch from a3ef4a9 to 1ccd777 Compare June 2, 2026 01:07
@youknowone
Copy link
Copy Markdown
Member

Thank you so much for this try! Currently we dont have a reviewer to properly review this topic. Lets try to make this better.

@im-0 im-0 force-pushed the rustls-integration-rewrite branch 2 times, most recently from 136989d to b6afc8e Compare June 2, 2026 03:40
Comment thread crates/stdlib/rustls-data/obj_mac.num
Comment thread extra_tests/snippets/stdlib_ssl.py
Comment thread Lib/test/test_httplib.py
h.close()
self.assertIn('text/html', content_type)

@unittest.skipIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

how does this pass before?

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.

If this doesn't crash/hangs Id prefer to have it as

Suggested change
@unittest.skipIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")
@unittest.expectedFailureIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@youknowone There is a hack for a case when server certificate is loaded into a certificate store:

verify_hostname(end_entity, server_name)?;

It is possible to closer replicate OpenSSL behavior but for this it will better to just fork https://github.com/rustls/webpki instead of adding more hacks around it (I added some too and I do not like it). And it will not work with rustls_platform_verifier anyway as its behavior is not configurable (whether we need to use rustls_platform_verifier at all is a different question).

Copy link
Copy Markdown
Contributor

@ShaharNaveh ShaharNaveh left a comment

Choose a reason for hiding this comment

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

TYSM for working on this!

This will become a lot easier to maintain:)

I have few nitpicks

Comment thread Lib/test/test_httplib.py
h.close()
self.assertIn('text/html', content_type)

@unittest.skipIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")
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.

If this doesn't crash/hangs Id prefer to have it as

Suggested change
@unittest.skipIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")
@unittest.expectedFailureIf("rustls" in __import__('ssl').OPENSSL_VERSION, "TODO: RUSTPYTHON; rustls does not support server host name verification by CN")

Comment thread Lib/test/test_ssl.py Outdated
Comment thread Lib/test/test_ssl.py Outdated
Comment thread Lib/test/test_ssl.py Outdated
Comment thread Lib/test/test_ssl.py Outdated
Comment thread Lib/test/test_ssl.py Outdated
Comment thread Cargo.toml Outdated
@im-0 im-0 force-pushed the rustls-integration-rewrite branch from b6afc8e to d74a56d Compare June 3, 2026 12:57
@im-0
Copy link
Copy Markdown
Contributor Author

im-0 commented Jun 3, 2026

Changes:

  • Client connection now verifies server certificates for any verification mode != CERT_NONE.
  • Renamed stdlib_ssl_bio_unencrypted_trailer.py -> stdlib_ssl.py
  • Moved crates/stdlib/src/rustls-data -> crates/stdlib/rustls-data
  • Replaced skipIf() -> expectedFailureIf()
  • Fixed cargo shear warning (graviola dependency for examples/custom_tls_providers.rs)

im-0 added 13 commits June 4, 2026 03:01
It checks that BIO mode does not read more data than required for TLS
session.
This can be used to alter Python-native modules imported from cpython in
runtime.
Enabled tests:

 * test_create_server_ssl_verified
 * test_create_connection_memory_leak
 * test_handshake_timeout_handler_leak
 * test_tls_unique_channel_binding - now correctly skipped because we
   monkey-patch ssl.CHANNEL_BINDING_TYPES
 * test_sni_callback_alert
 * test_sni_callback_raising
 * test_sni_callback_wrong_return_type

Tests skipped with rustls implementation specifically (should work with
OpenSSL):

 * test_load_dh_params - rustls does not support Diffie-Hellman key
   exchange (it uses elliptic curve Diffie-Hellman)
 * test_get_ca_certs_capath - test expects that certificates are loaded
   lazily which is hard to implement with rustls
 * test_verify_strict - rustls does not perform some cert checks that
   OpenSSL does in strict mode

Previously disbled tests changed to rustls-specific skip:

 * test_session - current rustls does not expose session info
 * test_session_handling
 * test_msg_callback_tls12 - _msg_callback cannot be implemented with
   rustls
Main changes:

* Now it contains understandable connection state machine instead of a
  handful of entangled variables.
* OID and NID mappings are generated from OpenSSL data files. Some
  mappings are still hardcoded but those are much smaller than before and
  are actually testable.
* OpenSSL-compatible cipher list string (`man openssl-ciphers`) is parsed
  correctly (I hope so).
* Difference between socket IO and buffered IO handling is minimal.

Please check the module-level doc comment in crates/stdlib/src/rustls.rs
for additional information.

What is missing:

* ssl timeout support. Normal socket timeouts still work just fine but
  Python's ssl module implementation handles timeouts for whole
  SSL/TLS operations, like handshake. This does not break anything and
  just makes timeouts imprecise so I believe that proper timeouts can be
  implemented later.
* Socket IO still uses _socket.socket.send/recv instead of methods of
  the socket object itself. Otherwise everything hangs and this must
  be investigated.
* See other TODO items in crates/stdlib/src/rustls.rs
…s used

rustls does not support server host name verification by CN.
Only Subject Alternative Names are supported.
@im-0 im-0 force-pushed the rustls-integration-rewrite branch from d74a56d to bd7cfca Compare June 4, 2026 01:03
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