hyperion-rb 1.0.0.rc17 → 1.0.0.rc18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcb08723cb65420d073fe0e239c039946649ed4b2d5ec913391edaa9f9022f5c
4
- data.tar.gz: 87f88b3956f6879defe23ba4f9e4b6a5b41bcf4db787b553ed7a3608ef9f6760
3
+ metadata.gz: d339041ba36ea34d86f65b15d4160dc3e8e3484e793b59bb6bd4381e496d6ef7
4
+ data.tar.gz: 156ba026f90f7d422623be5d1f708d45d328567a7f708ac788f91fea80f5d63b
5
5
  SHA512:
6
- metadata.gz: b0fecfb34f4cd2a2b272858305fc43a46a9f09d0ac9a77e13b2977c0793bd92a5eac024d8d6f080eaaa565f99f87e092d919a1ff4662eb3a205a29cefa9826fd
7
- data.tar.gz: 4a86ffdf69d7ba24e3dbac045c26c26aed00ee07f43db3eaabe725c3ce5a52b9b3af7f94cfefa80ca743a75db0c882d931e1ae4953131acc70a20b5104ccdbf0
6
+ metadata.gz: 8b8a5516872e28faa553398dcd8fb3debfc41d6e3a17e7cb31563f8a4db7a52044984826909015a31f8176e5fcebffeba4ec3129b0891f6c773a938b41ee1e0c
7
+ data.tar.gz: f8c9fb9a2f0bd2935aa8d70b191107923870aaeb0030e6357f4f30469ec1284e3432dc3706168537c1218fa95a3ce3f0cc6e1fb9f56349b6ab7cf580aae7263c
data/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  High-performance Ruby HTTP server. Falcon-class fiber concurrency, Puma-class compatibility.
4
4
 
5
- [![Specs](https://img.shields.io/badge/specs-114%20passing-brightgreen)]()
6
- [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-red)]()
7
- [![License](https://img.shields.io/badge/license-MIT-blue)]()
5
+ [![CI](https://github.com/andrew-woblavobla/hyperion/actions/workflows/ci.yml/badge.svg)](https://github.com/andrew-woblavobla/hyperion/actions/workflows/ci.yml)
6
+ [![Gem Version](https://img.shields.io/gem/v/hyperion-rb.svg)](https://rubygems.org/gems/hyperion-rb)
7
+ [![License: MIT](https://img.shields.io/github/license/andrew-woblavobla/hyperion.svg)](https://github.com/andrew-woblavobla/hyperion/blob/master/LICENSE)
8
8
 
9
9
  ```sh
10
- gem install hyperion
10
+ gem install hyperion-rb --pre
11
11
  bundle exec hyperion config.ru
12
12
  ```
13
13
 
data/lib/hyperion/cli.rb CHANGED
@@ -25,8 +25,16 @@ module Hyperion
25
25
  o.on('-t', '--threads N', Integer, 'Rack handler thread pool size (0 disables)') do |t|
26
26
  cli_opts[:thread_count] = t
27
27
  end
28
- o.on('--tls-cert PATH', 'TLS certificate (PEM)') do |p|
29
- cli_opts[:tls_cert] = OpenSSL::X509::Certificate.new(File.read(p))
28
+ o.on('--tls-cert PATH', 'TLS certificate (PEM; chained intermediates supported)') do |p|
29
+ # Parse every BEGIN/END block in the file — production certs ship
30
+ # as leaf+intermediate(s) bundled together. `OpenSSL::X509::Certificate.new`
31
+ # only reads the first block, so loading via that single call would
32
+ # silently drop the chain. See Hyperion::TLS.parse_pem_chain.
33
+ certs = Hyperion::TLS.parse_pem_chain(File.read(p))
34
+ abort("[hyperion] no certificates found in #{p}") if certs.empty?
35
+
36
+ cli_opts[:tls_cert] = certs.first
37
+ cli_opts[:tls_chain] = certs[1..]
30
38
  end
31
39
  o.on('--tls-key PATH', 'TLS private key (PEM)') do |p|
32
40
  cli_opts[:tls_key] = OpenSSL::PKey.read(File.read(p))
@@ -51,6 +51,13 @@ module Hyperion
51
51
  # routes both streams to the same target (e.g. a StringIO in specs).
52
52
  @out = io || out
53
53
  @err = io || err
54
+ # Force line-immediate mode on real IO destinations. When stdout is
55
+ # redirected (piped, journald, kubectl logs), Ruby/glibc default to
56
+ # 4-KiB block buffering and small log lines never reach the consumer
57
+ # until the buffer fills or the process exits. Operators expect to see
58
+ # boot lines + access logs in real time. Match Puma's behaviour.
59
+ @out.sync = true if @out.is_a?(::IO) && @out.respond_to?(:sync=)
60
+ @err.sync = true if @err.is_a?(::IO) && @err.respond_to?(:sync=)
54
61
  @level = parse_level(level || ENV.fetch('HYPERION_LOG_LEVEL', 'info'))
55
62
  requested = format || ENV['HYPERION_LOG_FORMAT']
56
63
  @format = pick_format(requested)
@@ -39,7 +39,7 @@ module Hyperion
39
39
  @port = tcp.addr[1]
40
40
 
41
41
  if @tls
42
- @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key])
42
+ @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key], chain: @tls[:chain])
43
43
  ssl_server = ::OpenSSL::SSL::SSLServer.new(tcp, @ssl_ctx)
44
44
  ssl_server.start_immediately = false
45
45
  @server = ssl_server
@@ -67,7 +67,7 @@ module Hyperion
67
67
  else
68
68
  sock.local_address.ip_port
69
69
  end
70
- @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key]) if @tls
70
+ @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key], chain: @tls[:chain]) if @tls
71
71
  self
72
72
  end
73
73
 
data/lib/hyperion/tls.rb CHANGED
@@ -11,12 +11,18 @@ module Hyperion
11
11
  module TLS
12
12
  SUPPORTED_PROTOCOLS = %w[h2 http/1.1].freeze
13
13
 
14
+ PEM_CERT_RE = /-----BEGIN CERTIFICATE-----.+?-----END CERTIFICATE-----/m
15
+
14
16
  module_function
15
17
 
16
- def context(cert:, key:)
18
+ def context(cert:, key:, chain: nil)
17
19
  ctx = OpenSSL::SSL::SSLContext.new
18
20
  ctx.cert = cert
19
21
  ctx.key = key
22
+ # NB: do NOT switch to `chain.present?` — that's ActiveSupport, which
23
+ # this gem does not depend on (would NameError at runtime). The
24
+ # explicit guard below is the plain-Ruby equivalent.
25
+ ctx.extra_chain_cert = chain unless chain.nil? || chain.empty?
20
26
  ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
21
27
  ctx.alpn_protocols = SUPPORTED_PROTOCOLS
22
28
  ctx.alpn_select_cb = lambda do |client_protocols|
@@ -25,5 +31,14 @@ module Hyperion
25
31
  end
26
32
  ctx
27
33
  end
34
+
35
+ # Split a PEM blob into one OpenSSL::X509::Certificate per BEGIN/END
36
+ # block. Production cert files commonly bundle leaf + intermediate(s) in
37
+ # a single file, but `OpenSSL::X509::Certificate.new(pem)` only parses
38
+ # the FIRST block — so if we don't split here the intermediates are
39
+ # silently dropped and clients see an incomplete chain.
40
+ def parse_pem_chain(pem)
41
+ pem.scan(PEM_CERT_RE).map { |block| OpenSSL::X509::Certificate.new(block) }
42
+ end
28
43
  end
29
44
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hyperion
4
- VERSION = '1.0.0.rc17'
4
+ VERSION = '1.0.0.rc18'
5
5
  end
@@ -77,7 +77,7 @@ module Hyperion
77
77
  sock.listen(::Socket::SOMAXCONN)
78
78
 
79
79
  if @tls
80
- ctx = Hyperion::TLS.context(cert: @tls[:cert], key: @tls[:key])
80
+ ctx = Hyperion::TLS.context(cert: @tls[:cert], key: @tls[:key], chain: @tls[:chain])
81
81
  ssl = ::OpenSSL::SSL::SSLServer.new(sock, ctx)
82
82
  ssl.start_immediately = false
83
83
  ssl
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Convenience shim so Bundler's auto-require pattern works:
4
+ #
5
+ # gem 'hyperion-rb' # in a Gemfile, no `require:` needed
6
+ #
7
+ # The canonical require path remains `require 'hyperion'`.
8
+ require 'hyperion'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperion-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc17
4
+ version: 1.0.0.rc18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Lobanov
@@ -145,6 +145,7 @@ files:
145
145
  - ext/hyperion_http/llhttp/llhttp.c
146
146
  - ext/hyperion_http/llhttp/llhttp.h
147
147
  - ext/hyperion_http/parser.c
148
+ - lib/hyperion-rb.rb
148
149
  - lib/hyperion.rb
149
150
  - lib/hyperion/adapter/rack.rb
150
151
  - lib/hyperion/c_parser.rb