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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
require "plum"
require "openssl"
require "socket"
require "cgi"
begin
require "sslkeylog/autotrace"
rescue LoadError
end
def log(con, stream, s)
prefix = "[%02x;%02x] " % [con, stream]
if s.is_a?(Enumerable)
puts s.map {|a| prefix + a.to_s }.join("\n")
else
puts prefix + s.to_s
end
end
ctx = OpenSSL::SSL::SSLContext.new
ctx.ssl_version = :TLSv1_2
ctx.alpn_select_cb = -> protocols {
raise "Client does not support HTTP/2: #{protocols}" unless protocols.include?("h2")
"h2"
}
ctx.tmp_ecdh_callback = -> (sock, ise, keyl) {
OpenSSL::PKey::EC.new("prime256v1")
}
ctx.cert = OpenSSL::X509::Certificate.new File.read(".crt.local")
ctx.key = OpenSSL::PKey::RSA.new File.read(".key.local")
tcp_server = TCPServer.new("0.0.0.0", 40443)
ssl_server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
loop do
begin
sock = ssl_server.accept
id = sock.io.fileno
puts "#{id}: accept!"
rescue => e
STDERR.puts e
next
end
plum = Plum::ServerConnection.new(sock)
plum.on(:frame) do |frame|
log(id, frame.stream_id, "recv: #{frame.inspect}")
end
plum.on(:send_frame) do |frame|
log(id, frame.stream_id, "send: #{frame.inspect}")
end
plum.on(:connection_error) do |exception|
puts exception
puts exception.backtrace
end
plum.on(:stream) do |stream|
stream.on(:stream_error) do |exception|
puts exception
puts exception.backtrace
end
stream.on(:send_deferred) do |frame|
log(id, frame.stream_id, "send (deferred): #{frame.inspect}")
end
headers = data = nil
stream.on(:open) do
headers = nil
data = ""
end
stream.on(:headers) do |headers_|
log(id, stream.id, headers_.map {|name, value| "#{name}: #{value}" })
headers = headers_.to_h
end
stream.on(:data) do |data_|
log(id, stream.id, data_)
data << data_
end
stream.on(:end_stream) do
case [headers[":method"], headers[":path"]]
when ["GET", "/"]
body = "Hello World! <a href=/abc.html>ABC</a> <a href=/fgsd>Not found</a>"
body << <<-EOF
<form action=post.page method=post>
<input type=text name=key value=default_value>
<input type=submit>
</form>
EOF
stream.respond({
":status": "200",
"server": "plum",
"content-type": "text/html",
"content-length": body.size
}, body)
when ["GET", "/abc.html"]
body = "ABC! <a href=/>Back to top page</a><br><img src=/image.nyan>"
i_stream = stream.promise({
":authority": "localhost:40443",
":method": "GET",
":scheme": "https",
":path": "/image.nyan"
})
stream.respond({
":status": "200",
"server": "plum",
"content-type": "text/html",
"content-length": body.size
}, body)
image = ("iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAgMAAADXB5lNAAAACVBMVEX///93o0jG/4mTMy20AAAA" <<
"bklEQVQ4y2NgoAoIRQJkCoSimIdTgJGBBU1ABE1A1AVdBQuaACu6gCALhhZ0axlZCDgMWYAB6ilU" <<
"35IoADEMxWyyBDD45AhQCFahM0kXWIVu3sAJrILzyBcgytoFeATABBcXWohhCEC14BCgGAAAX1ZQ" <<
"ZtJp0zAAAAAASUVORK5CYII=").unpack("m")[0]
i_stream.respond({
":status": "200",
"server": "plum",
"content-type": "image/png",
"content-length": image.size
}, image)
when ["POST", "/post.page"]
body = "Posted value is: #{CGI.unescape(data).gsub("<", "<").gsub(">", ">")}<br> <a href=/>Back to top page</a>"
stream.respond({
":status": "200",
"server": "plum",
"content-type": "text/html",
"content-length": body.size
}, body)
else
body = "Page not found! <a href=/>Back to top page</a>"
stream.respond({
":status": "404",
"server": "plum",
"content-type": "text/html",
"content-length": body.size
}, body)
end
end
end
Thread.new {
begin
plum.start
rescue
puts $!
puts $!.backtrace
ensure
sock.close
end
}
end
|