1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
# -*- frozen-string-literal: true -*-
module Plum
module Rack
class BaseListener
def stop
@server.close
end
def to_io
raise "not implemented"
end
def method_missing(name, *args)
@server.__send__(name, *args)
end
end
class TCPListener < BaseListener
def initialize(lc)
@server = ::TCPServer.new(lc[:hostname], lc[:port])
end
def to_io
@server.to_io
end
def plum(sock)
::Plum::HTTPServerConnection.new(sock.method(:write))
end
end
class TLSListener < BaseListener
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 {
if protocols.include?("h2")
"h2"
else
protocols.first
end
}
ctx.tmp_ecdh_callback = -> (sock, ise, keyl) { OpenSSL::PKey::EC.new("prime256v1") }
ctx.cert = OpenSSL::X509::Certificate.new(cert)
ctx.key = OpenSSL::PKey::RSA.new(key)
tcp_server = ::TCPServer.new(lc[:hostname], lc[:port])
@server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
@server.start_immediately = false
end
def to_io
@server.to_io
end
def plum(sock)
raise ::Plum::LegacyHTTPError.new("client doesn't offered h2 with ALPN", nil) unless sock.alpn_protocol == "h2"
::Plum::ServerConnection.new(sock.method(:write))
end
private
# returns: [cert, key]
def dummy_key
puts "WARNING: Generating new dummy certificate..."
key = OpenSSL::PKey::RSA.new(2048)
cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=JP/O=Test/OU=Test/CN=example.com")
cert.not_before = Time.now
cert.not_after = Time.now + 363 * 24 * 60 * 60
cert.public_key = key.public_key
cert.serial = rand((1 << 20) - 1)
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension("basicConstraints", "CA:TRUE", true),
ef.create_extension("subjectKeyIdentifier", "hash"),
]
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
cert.sign key, OpenSSL::Digest::SHA1.new
[cert, key]
end
end
class UNIXListener < BaseListener
def initialize(lc)
if File.exist?(lc[:path])
begin
old = UNIXSocket.new(lc[:path])
rescue SystemCallError, IOError
File.unlink(lc[:path])
else
old.close
raise "Already a server bound to: #{lc[:path]}"
end
end
@server = ::UNIXServer.new(lc[:path])
File.chmod(lc[:mode], lc[:path]) if lc[:mode]
end
def stop
super
File.unlink(lc[:path])
end
def to_io
@server.to_io
end
def plum(sock)
::Plum::ServerConnection.new(sock.method(:write))
end
end
end
end
|