Class: Plum::Rack::TLSListener

Inherits:
BaseListener show all
Defined in:
lib/plum/rack/listener.rb

Instance Method Summary collapse

Methods inherited from BaseListener

#method_missing, #stop

Constructor Details

#initialize(lc) ⇒ TLSListener

Returns a new instance of TLSListener.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/plum/rack/listener.rb', line 53

def initialize(lc)
  if lc[:certificate] && lc[:certificate_key]
    cert = File.read(lc[:certificate])
    key = File.read(lc[:certificate_key])
  else
    STDERR.puts "WARNING: using dummy certificate"
    cert, key = dummy_key
  end

  ctx = OpenSSL::SSL::SSLContext.new
  ctx.ssl_version = :TLSv1_2
  ctx.alpn_select_cb = -> (protocols) { protocols.include?("h2") ? "h2" : protocols.first }
  ctx.tmp_ecdh_callback = -> (sock, ise, keyl) { OpenSSL::PKey::EC.new("prime256v1") }
  *ctx.extra_chain_cert, ctx.cert = parse_chained_cert(cert)
  ctx.key = OpenSSL::PKey::RSA.new(key)
  ctx.servername_cb = proc { |sock, hostname|
    if host = lc[:sni]&.[](hostname)
      new_ctx = ctx.dup
      *new_ctx.extra_chain_cert, new_ctx.cert = parse_chained_cert(File.read(host[:certificate]))
      new_ctx.key = OpenSSL::PKey::RSA.new(File.read(host[:certificate_key]))
      new_ctx
    else
      ctx
    end
  }
  tcp_server = ::TCPServer.new(lc[:hostname], lc[:port])
  @server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
  @server.start_immediately = false # call socket#accept twice: [tcp, tls]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Plum::Rack::BaseListener

Instance Method Details

#accept(svc) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/plum/rack/listener.rb', line 91

def accept(svc)
  sock = @server.accept
  Thread.start {
    begin
      sock = sock.accept
      raise ::Plum::LegacyHTTPError.new("client didn't offer h2 with ALPN", nil) unless sock.alpn_protocol == "h2"
      plum = ::Plum::ServerConnection.new(sock.method(:write))
      sess = Session.new(svc, sock, plum)
      sess.run
    rescue Errno::ECONNRESET, EOFError # closed
    rescue ::Plum::LegacyHTTPError => e
      @logger.info "legacy HTTP client: #{e}"
      sess = LegacySession.new(svc, e, sock)
      sess.run
    rescue => e
      svc.log_exception(e)
    ensure
      sock.close if sock
    end
  }
end

#parse_chained_cert(str) ⇒ Object



83
84
85
# File 'lib/plum/rack/listener.rb', line 83

def parse_chained_cert(str)
  str.scan(/-----BEGIN CERTIFICATE.+?END CERTIFICATE-----/m).map { |s| OpenSSL::X509::Certificate.new(s) }
end

#to_ioObject



87
88
89
# File 'lib/plum/rack/listener.rb', line 87

def to_io
  @server.to_io
end