class H2::Server::HTTPS

'h2' server - for TLS 1.2 ALPN HTTP/2 connection

@see tools.ietf.org/html/rfc7540#section-3.3

Constants

ALPN_PROTOCOL
ALPN_SELECT_CALLBACK
ECDH_CURVES
ECDH_OPENSSL_MIN_VERSION
TMP_ECDH_CALLBACK

Public Class Methods

new(host:, port:, sni: {}) click to toggle source

create a new h2 server that uses SNI to determine TLS cert/key to use

@see en.wikipedia.org/wiki/Server_Name_Indication

@param [String] host the IP address for this server to listen on @param [Integer] port the TCP port for this server to listen on @param [Hash] sni the SNI option hash with certs/keys for domains @param [Hash] options

@option [String] :cert TLS certificate @option [String] :extra_chain_cert TLS certificate @option [String] :key TLS key

SNI options with default callback

:sni

Hash with domain name String keys and Hash values:

:cert

String TLS certificate

:extra_chain_cert

String TLS certificate

:key

String TLS key

SNI options with custom callback

:sni

Hash:

:callback

Proc creates OpenSSL::SSL::SSLContext for each

connection
Calls superclass method H2::Server::new
# File lib/h2/server/https.rb, line 43
def initialize host:, port:, sni: {}, **options, &on_connection
  @sni          = sni
  @sni_callback = @sni[:callback] || method(:sni_callback)
  @tcpserver    = Celluloid::IO::TCPServer.new host, port
  @sslserver    = Celluloid::IO::SSLServer.new @tcpserver, create_ssl_context(options)
  options.merge! host: host, port: port, sni: sni
  super @sslserver, options, &on_connection
end

Public Instance Methods

run() click to toggle source

accept a socket connection, possibly attach spy, hand off to #handle_connection asyncronously, repeat

# File lib/h2/server/https.rb, line 55
def run
  loop do
    begin
      socket = @server.accept
    rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::EPIPE,
           Errno::ETIMEDOUT, Errno::EHOSTUNREACH => ex
      Logger.warn "Error accepting SSLSocket: #{ex.class}: #{ex.to_s}"
      retry
    end

    async.handle_connection socket
  end
end

Private Instance Methods

context_cert(cert) click to toggle source
# File lib/h2/server/https.rb, line 124
def context_cert cert
  case cert
  when String
    cert = File.read cert if File.exist? cert
    OpenSSL::X509::Certificate.new cert
  when OpenSSL::X509::Certificate
    cert
  end
end
context_ecdh(ctx) click to toggle source
# File lib/h2/server/https.rb, line 110
def context_ecdh ctx
  ctx.ecdh_curves = ECDH_CURVES
end
context_extra_chain_cert(chain) click to toggle source
# File lib/h2/server/https.rb, line 144
def context_extra_chain_cert chain
  case chain
  when String
    chain = File.read chain if File.exist? chain
    [OpenSSL::X509::Certificate.new(chain)]
  when OpenSSL::X509::Certificate
    [chain]
  when Array
    chain
  end
end
context_key(key) click to toggle source
# File lib/h2/server/https.rb, line 134
def context_key key
  case key
  when String
    key = File.read key if File.exist? key
    OpenSSL::PKey::RSA.new key
  when OpenSSL::PKey::RSA
    key
  end
end
context_set_protocols(ctx) click to toggle source
# File lib/h2/server/https.rb, line 157
def context_set_protocols ctx
  ctx.alpn_protocols = [ALPN_PROTOCOL]
  ctx.alpn_select_cb = ALPN_SELECT_CALLBACK
end
create_ssl_context(**opts) click to toggle source

builds a new SSLContext suitable for use in 'h2' connections

# File lib/h2/server/https.rb, line 88
def create_ssl_context **opts
  ctx                  = OpenSSL::SSL::SSLContext.new
  ctx.ca_file          = opts[:ca_file] if opts[:ca_file]
  ctx.ca_path          = opts[:ca_path] if opts[:ca_path]
  ctx.cert             = context_cert opts[:cert]
  ctx.ciphers          = opts[:ciphers] || OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers]
  ctx.extra_chain_cert = context_extra_chain_cert opts[:extra_chain_cert]
  ctx.key              = context_key opts[:key]
  ctx.options          = opts[:options] || OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
  ctx.servername_cb    = @sni_callback
  ctx.ssl_version      = :TLSv1_2
  context_ecdh ctx

  # https://github.com/jruby/jruby-openssl/issues/99
  context_set_protocols ctx unless H2.jruby?

  ctx
end
sni_callback(args) click to toggle source

default SNI callback - builds SSLContext from cert/key by domain name in +@sni+ or returns existing one if name is not found

# File lib/h2/server/https.rb, line 74
def sni_callback args
  socket, name = args
  @contexts ||= {}
  if @contexts[name]
    @contexts[name]
  elsif sni_opts = @sni[name] and Hash === sni_opts
    @contexts[name] = create_ssl_context sni_opts
  else
    socket.context
  end
end