feat: make rustls cert public#858
Conversation
|
@shaneutt |
Thank you for your time @nojima! The primary need is constructing fn ca_from_cached(
cached: &praxis_tls::CachedCaCerts,
) -> Vec<pingora_core::utils::tls::WrappedX509> {
cached
.der_certs()
.iter()
.map(|der| {
pingora_core::utils::tls::WrappedX509::new(
der.clone(),
pingora_core::utils::tls::parse_x509,
)
})
.collect()
}Called during upstream peer selection to populate per-cluster CA roots. So what this PR provides, broken down:
Items 1 and 2 are strictly required for our use case. Item 3 is not something we use today, but it seems generally useful for any downstream that needs to inspect certificate contents after construction. Let me know your thoughts? And again, thank you for your time 🙇 |
|
@shaneutt The approach makes sense, but I have a slight concern about the public API surface. I consider the fact that impl WrappedX509 {
pub fn parse(raw_cert: Vec<u8>) -> Self {
Self::new(raw_cert, parse_x509)
}
...
}With this function, you can create As for use case 3, we could support it by adding a new method rather than making the fields public: pub fn parsed(&self) -> &X509Certificate<'_> {
self.borrow_cert()
}This approach avoids exposing the various ouroboros-generated methods, leaving us with more flexibility for future design decisions. That said, I'm hesitant about implementing use case 3 right now. Removing a public function once it's been published is generally difficult. If you don't have an immediate need for it, I think it would be fine to address only use cases 1 and 2 in this PR and leave 3 for later. |
875e4d9 to
514d06b
Compare
Sounds good 👍 Switched to that approach, and pushed it up. LMKWYT.
|
A PR should contain changes for only a single purpose. If there are multiple independent changes, it's better to submit them as separate PRs rather than bundling them into one, so that each can be reviewed independently. Keeping PRs separate also simplifies things if a revert becomes necessary in the future. In this case, I think this PR should be limited to just the |
|
Yep, sounds fine. At least for now it passes CI, but I'm happy to strip it down 👍 |
c27df35 to
6945f9a
Compare
Add a public `WrappedX509::parse(Vec<u8>) -> Result<Self>` constructor to both the rustls and s2n TLS backends. This lets downstream crates build `WrappedX509` values from DER bytes without reaching into ouroboros internals (`new`, `parse_x509`). `parse_x509` is made crate-private; callers use `WrappedX509::parse` instead. Signed-off-by: Shane Utt <shaneutt@linux.com>
6945f9a to
2ec372d
Compare
race condition in a test: the client task calls send_data("", true)
to queue an empty DATA frame with END_STREAM, then immediately
returns. When the task returns, the h2 SendRequest handle is dropped.
Because the connection driver (running in a separate spawned task)
has not yet been polled to flush the queued frame, h2 sees an open
stream with a dropped handle and emits RST_STREAM(CANCEL) which the
server receives instead of the expected DATA+EOS.
The fix is a single yield_now() after send_data. This gives the
connection driver task a turn to poll, flush the tiny frame to
the duplex buffer, and update the stream state to half-closed.
Once half-closed, dropping the SendRequest no longer triggers
RST_STREAM.
This works reliably because #[tokio::test] uses the current_thread
runtime, where yield_now() deterministically schedules all other
ready tasks before resuming the current one.
Signed-off-by: Shane Utt <shaneutt@linux.com>
|
@nojima things are stripped down, tests are added. And if you want, I added a small fix for the 1.91.1 build failures on |
This enables a deeper level of control from implementations utilizing Pingora over certificates.
This is being paired with #726 in our fork right now.