Skip to content

puma: serve TLS - Puma::MiniSSL::Engine on SslStream - #32

Merged
Largo merged 2 commits into
masterfrom
claude/dazzling-leakey-596086
Oct 27, 2026
Merged

Largo merged 2 commits into
masterfrom
claude/dazzling-leakey-596086

Conversation

@Largo

@Largo Largo commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

What

IronRuby's bundled puma (8.0.2) now serves TLS: ssl:// binds work in single mode. Until now Puma::MiniSSL::Engine was undefined, so Puma.ssl? was false and an ssl:// bind failed with "Puma compiled without SSL support".

puma does not use OpenSSL::SSL::SSLServer. It drives its C extension's MiniSSL engine, which is OpenSSL over memory BIOs: puma reads ciphertext off the socket into Engine#inject, takes plaintext from #read, encrypts with #write and sends what #extract returns. Src/Libraries/Puma/MiniSsl.cs provides Puma::MiniSSL::Engine, SSLContext and SSLError with the same contract:

  • The engine is a server SslStream over an in-memory stream. #inject fills its inbound side and #extract drains its outbound side. The handshake and the reads run as SslStream's async operations, so nothing blocks on the socket, which only puma touches.
  • #read, #write and #extract first wait until SslStream has used up its input and is waiting for more. That is where OpenSSL stands when SSL_read returns, so puma's own MiniSSL::Socket and reactor run unchanged.
  • The context takes cert/key files or cert_pem/key_pem, key_password_command, and ca with verify_mode none, peer or force_peer (puma.peercert is set). no_tlsv1 and no_tlsv1_1 are honoured too.
  • Not used: ssl_cipher_filter and ssl_ciphersuites (SslStream takes the system's cipher suites), verification_flags and reuse. Engine.client, which only puma's own tests use, is not implemented.

OpenSSL::SSL::SSLServer and SSLSocket#accept still raise NotImplementedError. puma does not need them.

Also: Initializers.Generated.cs is regenerated (the generator's output, checked identical). The puma_http11.rb header, Docs/net-http-over-tls.md and puma's default gemspec summary no longer say "no TLS".

Tests

  • New Puma_Tls1 (Src/IronRuby.Tests/Runtime/PumaTlsTests.cs, [Options(Prism = true)]). It starts puma through Puma::Launcher on three ssl:// binds (cert files, cert_pem/key_pem via ssl_bind, and force_peer with a ca) and checks:
    • Net::HTTP with ca_file: and keep-alive, a 300 KB POST and a 200 KB response;
    • two requests pipelined in one TLS record;
    • plain HTTP on the TLS port is turned away, and puma keeps serving;
    • client certificates presented through .NET's SslStream: the CA-issued one gets in, a self-signed one or none is refused.
  • Checked by hand: CRuby Net::HTTP (GET, a 1 MB POST, keep-alive, pipelining, TLS 1.2), Windows curl (SChannel) and the OpenSSL-based curl over TLS 1.3 with keep-alive, peer and force_peer from CRuby, and 16 parallel keep-alive clients × 50 requests plus 50 one-shot connections with no errors.

Verified on Windows / net10.0 / Debug only. The full IronRuby.Tests suite passed there. Linux, macOS, net8.0 and Release were not run locally; CI covers them.

Summary by CodeRabbit

  • New Features
    • Puma now supports HTTPS/TLS connections, including file-based and in-memory certificates, client-certificate verification, and common HTTP interactions over secure connections.
  • Documentation
    • Updated Puma guidance to describe supported TLS options and settings, note remaining TLS limitations, and document TLS test coverage.

puma's TLS is its C extension's MiniSSL engine, OpenSSL over memory BIOs:
puma reads ciphertext off the socket into Engine#inject, takes plaintext
from #read, and sends what #extract answers. Src/Libraries/Puma/MiniSsl.cs
provides Puma::MiniSSL::Engine, SSLContext and SSLError the same way: a
server SslStream over an in-memory stream that #inject fills and #extract
drains, with the handshake and the reads running as SslStream's async
operations. #read, #write and #extract first wait until SslStream has used
up its input, which is where OpenSSL stands when SSL_read returns, so
puma's own MiniSSL::Socket drives it unchanged. Puma.ssl? is now true and
ssl:// binds serve TLS in single mode.

The context takes cert/key files or cert_pem/key_pem, key_password_command,
ca with verify_mode none, peer or force_peer (puma.peercert is set), and
no_tlsv1/no_tlsv1_1. ssl_cipher_filter, ssl_ciphersuites,
verification_flags and reuse are not used.

Puma_Tls1 runs puma on three ssl:// binds: Net::HTTP with ca_file and
keep-alive, a large POST, pipelined requests, plain HTTP on the TLS port,
and client certificates through .NET's SslStream under force_peer.
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 47 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d9f89b79-665a-4402-9154-7b0afa3b96dc
Commits

Reviewing files that changed from the base of the PR and between d1e7636 and cd6f359.

Files ignored due to path filters: 1
  • Src/Libraries/Initializers.Generated.cs is excluded by !**/*.generated.*
Files selected for processing: 8
  • Docs/net-http-over-tls.md
  • Src/IronRuby.Tests/RubyTests.cs
  • Src/IronRuby.Tests/Runtime/PumaTlsTests.cs
  • Src/Libraries/Puma/MiniSsl.cs
  • Src/Libraries/Puma/PumaHttp11.cs
  • Src/StdLib/ironruby/puma/puma_http11.rb
  • Src/StdLib/ruby/gems/4.0.0/specifications/default/puma-8.0.2.gemspec
  • Util/gen-default-gemspecs.rb

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 17cfc1a0-1630-4126-87e4-7bdcef7a9807
Commits

Reviewing files that changed from the base of the PR and between a917334 and d1e7636.

Files ignored due to path filters: 1
  • Src/Libraries/Initializers.Generated.cs is excluded by !**/*.generated.*
Files selected for processing: 8
  • Docs/net-http-over-tls.md
  • Src/IronRuby.Tests/RubyTests.cs
  • Src/IronRuby.Tests/Runtime/PumaTlsTests.cs
  • Src/Libraries/Puma/MiniSsl.cs
  • Src/Libraries/Puma/PumaHttp11.cs
  • Src/StdLib/ironruby/puma/puma_http11.rb
  • Src/StdLib/ruby/gems/4.0.0/specifications/default/puma-8.0.2.gemspec
  • Util/gen-default-gemspecs.rb

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

Puma gains a server-side TLS engine backed by SslStream. The changes add TLS context and certificate handling, buffered encrypted I/O, and integration tests. Documentation and Puma gem metadata now describe TLS support and its stated limitations.

Changes: Puma TLS support

Layer / File(s) Summary
MiniSSL module and TLS context
Src/Libraries/Puma/MiniSsl.cs, Src/Libraries/Puma/PumaHttp11.cs
Adds the MiniSSL module and SSLContext certificate loading, verification settings, and TLS protocol selection. Makes PumaModule partial.
TLS engine and buffered I/O
Src/Libraries/Puma/MiniSsl.cs
Adds the server-side SslStream handshake, plaintext and ciphertext operations, shutdown and status queries, and memory BIO queues.
TLS integration coverage and support description
Src/IronRuby.Tests/Runtime/PumaTlsTests.cs, Src/IronRuby.Tests/RubyTests.cs, Docs/net-http-over-tls.md, Src/StdLib/ironruby/puma/puma_http11.rb, Src/StdLib/ruby/gems/4.0.0/specifications/default/puma-8.0.2.gemspec, Util/gen-default-gemspecs.rb
Adds Puma_Tls1 coverage for TLS binds, HTTP requests, and client certificates. Updates documentation and gem metadata to describe TLS support and its stated limitations.

Priority: High

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Puma
  participant Engine as MiniSSL.Engine
  participant MemoryBio
  participant SslStream
  Puma->>Engine: Inject ciphertext
  Engine->>MemoryBio: Queue inbound bytes
  SslStream->>MemoryBio: Read inbound bytes
  SslStream->>MemoryBio: Queue encrypted output
  Puma->>Engine: Extract ciphertext
Loading

Possibly related PRs

  • Largo/ironruby#28: Adds client-side non-blocking TLS in the same TLS area; this PR adds Puma server-side TLS.
Merge Risk: Minimal · previous review d1e76

No actionable TLS issue remains from this review; the change is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 28.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed 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 and concisely identifies the primary change: enabling Puma TLS serving through Puma::MiniSSL::Engine on SslStream.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 28.26% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 46 functions across 7 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@Largo
Largo merged commit 913b08c into master Oct 27, 2026
12 of 13 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Oct 28, 2026
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