iodine 0.1.21 → 0.2.0
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.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +3 -2
- data/.travis.yml +23 -2
- data/CHANGELOG.md +9 -2
- data/README.md +232 -179
- data/Rakefile +13 -1
- data/bin/config.ru +63 -0
- data/bin/console +6 -0
- data/bin/echo +42 -32
- data/bin/http-hello +62 -0
- data/bin/http-playground +124 -0
- data/bin/playground +62 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/bin/raw-rbhttp +35 -0
- data/bin/raw_broadcast +66 -0
- data/bin/test_with_faye +40 -0
- data/bin/ws-broadcast +108 -0
- data/bin/ws-echo +108 -0
- data/exe/iodine +59 -0
- data/ext/iodine/base64.c +264 -0
- data/ext/iodine/base64.h +72 -0
- data/ext/iodine/bscrypt-common.h +109 -0
- data/ext/iodine/bscrypt.h +49 -0
- data/ext/iodine/extconf.rb +41 -0
- data/ext/iodine/hex.c +123 -0
- data/ext/iodine/hex.h +70 -0
- data/ext/iodine/http.c +200 -0
- data/ext/iodine/http.h +128 -0
- data/ext/iodine/http1.c +402 -0
- data/ext/iodine/http1.h +56 -0
- data/ext/iodine/http1_simple_parser.c +473 -0
- data/ext/iodine/http1_simple_parser.h +59 -0
- data/ext/iodine/http_request.h +128 -0
- data/ext/iodine/http_response.c +1606 -0
- data/ext/iodine/http_response.h +393 -0
- data/ext/iodine/http_response_http1.h +374 -0
- data/ext/iodine/iodine_core.c +641 -0
- data/ext/iodine/iodine_core.h +70 -0
- data/ext/iodine/iodine_http.c +615 -0
- data/ext/iodine/iodine_http.h +19 -0
- data/ext/iodine/iodine_websocket.c +430 -0
- data/ext/iodine/iodine_websocket.h +21 -0
- data/ext/iodine/libasync.c +552 -0
- data/ext/iodine/libasync.h +117 -0
- data/ext/iodine/libreact.c +347 -0
- data/ext/iodine/libreact.h +244 -0
- data/ext/iodine/libserver.c +912 -0
- data/ext/iodine/libserver.h +435 -0
- data/ext/iodine/libsock.c +950 -0
- data/ext/iodine/libsock.h +478 -0
- data/ext/iodine/misc.c +181 -0
- data/ext/iodine/misc.h +76 -0
- data/ext/iodine/random.c +193 -0
- data/ext/iodine/random.h +48 -0
- data/ext/iodine/rb-call.c +127 -0
- data/ext/iodine/rb-call.h +60 -0
- data/ext/iodine/rb-libasync.h +79 -0
- data/ext/iodine/rb-rack-io.c +389 -0
- data/ext/iodine/rb-rack-io.h +17 -0
- data/ext/iodine/rb-registry.c +213 -0
- data/ext/iodine/rb-registry.h +33 -0
- data/ext/iodine/sha1.c +359 -0
- data/ext/iodine/sha1.h +85 -0
- data/ext/iodine/sha2.c +825 -0
- data/ext/iodine/sha2.h +138 -0
- data/ext/iodine/siphash.c +136 -0
- data/ext/iodine/siphash.h +15 -0
- data/ext/iodine/spnlock.h +235 -0
- data/ext/iodine/websockets.c +696 -0
- data/ext/iodine/websockets.h +120 -0
- data/ext/iodine/xor-crypt.c +189 -0
- data/ext/iodine/xor-crypt.h +107 -0
- data/iodine.gemspec +25 -18
- data/lib/iodine.rb +57 -58
- data/lib/iodine/http.rb +0 -189
- data/lib/iodine/protocol.rb +36 -245
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +145 -2
- metadata +115 -37
- data/bin/core_http_test +0 -51
- data/bin/em playground +0 -56
- data/bin/hello_world +0 -75
- data/bin/setup +0 -7
- data/lib/iodine/client.rb +0 -5
- data/lib/iodine/core.rb +0 -102
- data/lib/iodine/core_init.rb +0 -143
- data/lib/iodine/http/hpack.rb +0 -553
- data/lib/iodine/http/http1.rb +0 -251
- data/lib/iodine/http/http2.rb +0 -507
- data/lib/iodine/http/rack_support.rb +0 -108
- data/lib/iodine/http/request.rb +0 -462
- data/lib/iodine/http/response.rb +0 -474
- data/lib/iodine/http/session.rb +0 -143
- data/lib/iodine/http/websocket_client.rb +0 -335
- data/lib/iodine/http/websocket_handler.rb +0 -101
- data/lib/iodine/http/websockets.rb +0 -336
- data/lib/iodine/io.rb +0 -56
- data/lib/iodine/logging.rb +0 -46
- data/lib/iodine/settings.rb +0 -158
- data/lib/iodine/ssl_connector.rb +0 -48
- data/lib/iodine/timers.rb +0 -95
data/lib/iodine/http/http1.rb
DELETED
@@ -1,251 +0,0 @@
|
|
1
|
-
module Iodine
|
2
|
-
module Http
|
3
|
-
class Http1 < ::Iodine::Protocol
|
4
|
-
def on_open
|
5
|
-
set_timeout 1
|
6
|
-
@refuse_requests = false
|
7
|
-
@parser = {}
|
8
|
-
end
|
9
|
-
def on_message data
|
10
|
-
return if @refuse_requests
|
11
|
-
@http2_pri_review ||= ( ::Iodine::Http.http2 && ::Iodine::Http::Http2.pre_handshake(self, data) && (return true) ) || true
|
12
|
-
|
13
|
-
data = ::StringIO.new data
|
14
|
-
until data.eof?
|
15
|
-
request = (@request ||= ::Iodine::Http::Request.new(self))
|
16
|
-
unless request[:method]
|
17
|
-
l = data.gets.strip
|
18
|
-
if l.bytesize > 16_384
|
19
|
-
write "HTTP/1.0 414 Request-URI Too Long\r\ncontent-length: 20\r\n\r\nRequest URI too Long".freeze
|
20
|
-
Iodine.warn "Http/1 URI too long, closing connection.".freeze
|
21
|
-
return close
|
22
|
-
end
|
23
|
-
next if l.empty?
|
24
|
-
request[:method], request[:query], request[:version] = l.split(/[\s]+/.freeze, 3)
|
25
|
-
return (Iodine.warn('Http1 Protocol Error, closing connection.'.freeze, l, request) && close) unless request[:method] =~ HTTP_METHODS_REGEXP
|
26
|
-
request[:version] = (request[:version] || '1.1'.freeze).match(/[\d\.]+/.freeze)[0]
|
27
|
-
request[:time_recieved] = Iodine.time
|
28
|
-
end
|
29
|
-
until request[:headers_complete] || (l = data.gets).nil?
|
30
|
-
# if l.bytesize > 16_384
|
31
|
-
# write "HTTP/1.0 413 Entity Too Large\r\ncontent-length: 16\r\n\r\nEntity Too Large".freeze
|
32
|
-
# Iodine.warn "Http/1 Header data too large, closing connection.".freeze
|
33
|
-
# return close
|
34
|
-
# end
|
35
|
-
if l.include? ':'.freeze
|
36
|
-
# n = l.slice!(0, l.index(':')); l.slice! 0
|
37
|
-
# n.strip! ; n.downcase!; n.freeze
|
38
|
-
# request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l)
|
39
|
-
request[:headers_size] ||= 0
|
40
|
-
request[:headers_size] += l.bytesize
|
41
|
-
if request.length > 2096 || request[:headers_size] > 262_144
|
42
|
-
write "HTTP/1.0 431 Request Header Fields Too Large\r\ncontent-length: 31\r\n\r\nRequest Header Fields Too Large".freeze
|
43
|
-
return (Iodine.warn('Http1 header overloading, closing connection.'.freeze) && close)
|
44
|
-
end
|
45
|
-
l = l.strip.split(/:[\s]?/.freeze, 2)
|
46
|
-
l[0].strip! ; l[0].downcase!;
|
47
|
-
request[l[0]] ? (request[l[0]].is_a?(Array) ? (request[l[0]] << l[1]) : request[l[0]] = [request[l[0]], l[1] ]) : (request[l[0]] = l[1])
|
48
|
-
elsif l =~ /^[\r]?\n/.freeze
|
49
|
-
request[:headers_complete] = true
|
50
|
-
else
|
51
|
-
#protocol error
|
52
|
-
Iodine.warn 'Protocol Error, closing connection.'.freeze
|
53
|
-
return close
|
54
|
-
end
|
55
|
-
end
|
56
|
-
next unless request[:headers_complete]
|
57
|
-
if request['transfer-coding'.freeze] == 'chunked'.freeze
|
58
|
-
until request[:body_complete]
|
59
|
-
# add mid chunk logic here
|
60
|
-
if @parser[:length].to_i == 0
|
61
|
-
chunk = data.gets
|
62
|
-
return false unless chunk
|
63
|
-
@parser[:length] = chunk.to_i(16)
|
64
|
-
return (Iodine.warn('Protocol Error, closing connection.'.freeze) && close) unless @parser[:length]
|
65
|
-
request[:body_complete] = true && break if @parser[:length] == 0
|
66
|
-
@parser[:act_length] = 0
|
67
|
-
request[:body] ||= Tempfile.new('iodine'.freeze, :encoding => 'binary'.freeze)
|
68
|
-
end
|
69
|
-
chunk = data.read(@parser[:length] - @parser[:act_length])
|
70
|
-
return false unless chunk
|
71
|
-
request[:body] << chunk
|
72
|
-
@parser[:act_length] += chunk.bytesize
|
73
|
-
(@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length]
|
74
|
-
return if bad_body_size?
|
75
|
-
end
|
76
|
-
elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0
|
77
|
-
until request[:body_complete]
|
78
|
-
request[:body] ||= Tempfile.new('iodine'.freeze, :encoding => 'binary'.freeze)
|
79
|
-
packet = data.read(request['content-length'.freeze].to_i - request[:body].size)
|
80
|
-
return false unless packet
|
81
|
-
request[:body] << packet
|
82
|
-
return if bad_body_size?
|
83
|
-
request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].size <= 0
|
84
|
-
end
|
85
|
-
elsif request['content-type'.freeze]
|
86
|
-
until request[:body_complete]
|
87
|
-
Iodine.warn 'Body type protocol error.'.freeze unless request[:body]
|
88
|
-
line = data.gets
|
89
|
-
return false unless line
|
90
|
-
(request[:body] ||= Tempfile.new('iodine'.freeze, :encoding => 'binary'.freeze) ) << line
|
91
|
-
return if bad_body_size?
|
92
|
-
request[:body_complete] = true if line =~ EOHEADERS
|
93
|
-
end
|
94
|
-
else
|
95
|
-
request[:body_complete] = true
|
96
|
-
end
|
97
|
-
(@request = ::Iodine::Http::Request.new(self)) && ( (::Iodine::Http.http2 && ::Iodine::Http::Http2.handshake(request, self, data)) || dispatch(request, data) ) if request.delete :body_complete
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def send_response response
|
102
|
-
return false if response.headers.frozen?
|
103
|
-
|
104
|
-
body = response.extract_body
|
105
|
-
request = response.request
|
106
|
-
headers = response.headers
|
107
|
-
|
108
|
-
headers['content-length'.freeze] ||= body.size if body
|
109
|
-
|
110
|
-
keep_alive = response.keep_alive
|
111
|
-
if (request[:version].to_f > 1 && request['connection'.freeze].nil?) || request['connection'.freeze].to_s =~ /ke/i.freeze || (headers['connection'.freeze] && headers['connection'.freeze] =~ /^ke/i.freeze)
|
112
|
-
keep_alive = true
|
113
|
-
headers['connection'.freeze] ||= 'keep-alive'.freeze
|
114
|
-
headers['keep-alive'.freeze] ||= "timeout=#{(@timeout ||= 3).to_s}".freeze
|
115
|
-
else
|
116
|
-
headers['connection'.freeze] ||= 'close'.freeze
|
117
|
-
end
|
118
|
-
send_headers response
|
119
|
-
return log_finished(response) && (body && body.close) if request.head? || body.nil?
|
120
|
-
|
121
|
-
until body.eof?
|
122
|
-
written = write(body.read 65_536, Thread.current[:write_buffer])
|
123
|
-
return Iodine.warn("Http/1 couldn't send response because connection was lost.".freeze) && body.close unless written
|
124
|
-
response.bytes_written += written
|
125
|
-
end
|
126
|
-
body.close
|
127
|
-
close unless keep_alive
|
128
|
-
log_finished response
|
129
|
-
end
|
130
|
-
def stream_response response, finish = false
|
131
|
-
set_timeout 15
|
132
|
-
unless response.headers.frozen?
|
133
|
-
response['transfer-encoding'.freeze] = 'chunked'.freeze
|
134
|
-
response.headers['connection'.freeze] = 'close'.freeze
|
135
|
-
send_headers response
|
136
|
-
@refuse_requests = true
|
137
|
-
end
|
138
|
-
return if response.request.head?
|
139
|
-
body = response.extract_body
|
140
|
-
until body.eof?
|
141
|
-
written = stream_data(body.read 65_536, Thread.current[:write_buffer])
|
142
|
-
return Iodine.warn("Http/1 couldn't send response because connection was lost.".freeze) && body.close unless written
|
143
|
-
response.bytes_written += written
|
144
|
-
end if body
|
145
|
-
if finish
|
146
|
-
response.bytes_written += stream_data(''.freeze)
|
147
|
-
log_finished response
|
148
|
-
close unless response.keep_alive
|
149
|
-
end
|
150
|
-
body.close if body
|
151
|
-
true
|
152
|
-
end
|
153
|
-
|
154
|
-
def go_away error_code
|
155
|
-
return false if @io.closed?
|
156
|
-
close
|
157
|
-
end
|
158
|
-
|
159
|
-
protected
|
160
|
-
|
161
|
-
HTTP_METHODS = %w{GET HEAD POST PUT DELETE TRACE OPTIONS CONNECT PATCH}
|
162
|
-
HTTP_METHODS_REGEXP = /\A#{HTTP_METHODS.join('|')}/i
|
163
|
-
|
164
|
-
def dispatch request, data
|
165
|
-
return data.string.clear if @io.closed? || @refuse_requests
|
166
|
-
::Iodine::Http::Request.parse request
|
167
|
-
#check for server-responses
|
168
|
-
case request[:method]
|
169
|
-
when 'TRACE'.freeze
|
170
|
-
close
|
171
|
-
data.string.clear
|
172
|
-
return false
|
173
|
-
when 'OPTIONS'.freeze
|
174
|
-
response = ::Iodine::Http::Response.new request
|
175
|
-
response[:Allow] = 'GET,HEAD,POST,PUT,DELETE,OPTIONS'.freeze
|
176
|
-
response['access-control-allow-origin'.freeze] = '*'.freeze
|
177
|
-
response['content-length'.freeze] = 0
|
178
|
-
send_response response
|
179
|
-
return false
|
180
|
-
end
|
181
|
-
response = ::Iodine::Http::Response.new request
|
182
|
-
begin
|
183
|
-
if request.websocket?
|
184
|
-
@refuse_requests = true
|
185
|
-
::Iodine::Http::Websockets.handshake request, response, ::Iodine::Http.on_websocket.call(request, response)
|
186
|
-
else
|
187
|
-
ret = ::Iodine::Http.on_http.call(request, response)
|
188
|
-
if ret.is_a?(String)
|
189
|
-
response << ret
|
190
|
-
elsif ret == false
|
191
|
-
response.clear && (response.status = 404) && (response << ::Iodine::Http::Response::STATUS_CODES[404])
|
192
|
-
end
|
193
|
-
end
|
194
|
-
send_response response
|
195
|
-
rescue => e
|
196
|
-
Iodine.error e
|
197
|
-
send_response ::Iodine::Http::Response.new(request, 500, {}, ::Iodine::Http::Response::STATUS_CODES[500])
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
def send_headers response
|
202
|
-
return false if response.headers.frozen?
|
203
|
-
request = response.request
|
204
|
-
headers = response.headers
|
205
|
-
|
206
|
-
# response['date'.freeze] ||= request[:time_recieved].httpdate
|
207
|
-
(out = (Thread.current[:headers_buffer] ||= String.new)).clear
|
208
|
-
|
209
|
-
out << "HTTP/#{request[:version]} #{response.status} #{::Iodine::Http::Response::STATUS_CODES[response.status] || 'unknown'}\r\n"
|
210
|
-
|
211
|
-
out << request[:time_recieved].utc.strftime("Date: %a, %d %b %Y %H:%M:%S GMT\r\n".freeze) unless headers['date'.freeze]
|
212
|
-
|
213
|
-
# unless @headers['connection'] || (@request[:version].to_f <= 1 && (@request['connection'].nil? || !@request['connection'].match(/^k/i))) || (@request['connection'] && @request['connection'].match(/^c/i))
|
214
|
-
headers.each {|k,v| out << "#{k.to_s}: #{v}\r\n"}
|
215
|
-
out << "cache-control: max-age=0, no-cache\r\n".freeze unless headers['cache-control'.freeze]
|
216
|
-
response.extract_cookies.each {|cookie| out << "set-cookie: #{cookie}\r\n"}
|
217
|
-
out << "\r\n"
|
218
|
-
|
219
|
-
response.bytes_written += (write(out) || 0)
|
220
|
-
headers.freeze
|
221
|
-
response.raw_cookies.freeze
|
222
|
-
end
|
223
|
-
def stream_data data = nil
|
224
|
-
write("#{data.to_s.bytesize.to_s(16)}\r\n#{data.to_s}\r\n")
|
225
|
-
end
|
226
|
-
|
227
|
-
def log_finished response
|
228
|
-
request = response.request
|
229
|
-
return if Iodine.logger.nil? || request[:no_log]
|
230
|
-
t_n = Time.now
|
231
|
-
# (Thread.current[:log_buffer] ||= String.new).clear
|
232
|
-
# Thread.current[:log_buffer] << "#{request[:client_ip]} [#{t_n.utc}] \"#{request[:method]} #{request[:original_path]} #{request[:scheme]}\/#{request[:version]}\" #{response.status} #{response.bytes_written.to_s} #{((t_n - request[:time_recieved])*1000).round(2)}ms\n"
|
233
|
-
# Iodine.log(Thread.current[:log_buffer])
|
234
|
-
Iodine.log("#{request[:client_ip]} [#{t_n.utc}] \"#{request[:method]} #{request[:original_path]} #{request[:scheme]}\/#{request[:version]}\" #{response.status} #{response.bytes_written.to_s} #{((t_n - request[:time_recieved])*1000).round(2)}ms\n").clear
|
235
|
-
end
|
236
|
-
|
237
|
-
def bad_body_size?
|
238
|
-
request = @request
|
239
|
-
if request[:body] && request[:body].size > ::Iodine::Http.max_body_size
|
240
|
-
Iodine.warn("Http1 message body too big, closing connection (Iodine::Http.max_body_size == #{::Iodine::Http.max_body_size} bytes) - #{request[:body].size} bytes.")
|
241
|
-
request.delete(:body).tap {|f| f.close unless f.closed? } rescue false
|
242
|
-
write "HTTP/1.0 413 Payload Too Large\r\ncontent-length: 17\r\n\r\nPayload Too Large".freeze
|
243
|
-
return true
|
244
|
-
end
|
245
|
-
false
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
|
data/lib/iodine/http/http2.rb
DELETED
@@ -1,507 +0,0 @@
|
|
1
|
-
module Iodine
|
2
|
-
module Http
|
3
|
-
class Http2 < ::Iodine::Protocol
|
4
|
-
def on_open
|
5
|
-
# not fully fanctional.
|
6
|
-
::Iodine.warn "Http/2 requested - support is still experimental."
|
7
|
-
|
8
|
-
# update the timeout to 15 seconds (ping will be sent whenever timeout is reached).
|
9
|
-
set_timeout 15
|
10
|
-
|
11
|
-
# Header compression is stateful
|
12
|
-
@hpack = ::Iodine::Http::Http2::HPACK.new
|
13
|
-
|
14
|
-
# the header-stream cache
|
15
|
-
@header_buffer = String.new
|
16
|
-
@header_end_stream = false
|
17
|
-
@header_sid = nil
|
18
|
-
@frame_locker = Mutex.new
|
19
|
-
|
20
|
-
# frame parser starting posotion
|
21
|
-
@frame = {}
|
22
|
-
|
23
|
-
# Open stream managment
|
24
|
-
@open_streams = {}
|
25
|
-
|
26
|
-
# connection is only established after the preface was sent
|
27
|
-
@connected = false
|
28
|
-
|
29
|
-
# the last stream to be processed (For the GOAWAY frame)
|
30
|
-
@last_stream = 0
|
31
|
-
@refuese_new = false
|
32
|
-
# @complete_stream = 0
|
33
|
-
|
34
|
-
# the settings state
|
35
|
-
@settings = DEFAULT_SETTING.dup
|
36
|
-
|
37
|
-
# send connection preface (Section 3.5) consisting of a (can be empty) SETTINGS frame (Section 6.5).
|
38
|
-
#
|
39
|
-
# should prepare to accept a client connection preface which starts with:
|
40
|
-
# 0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
|
41
|
-
# == PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
|
42
|
-
# + SETTINGS frame
|
43
|
-
|
44
|
-
# The @options variable contains the original Http1 request, if exists.
|
45
|
-
return unless @options
|
46
|
-
::Iodine.warn "Http/2: upgrade handshake settings not implemented. upgrade request:\n#{@options}"
|
47
|
-
@last_stream = @options[:stream_id] = 1
|
48
|
-
@options[:io] = self
|
49
|
-
# deal with the request['http2-settings'] - NO ACK
|
50
|
-
# HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
|
51
|
-
|
52
|
-
# dispatch the original request
|
53
|
-
::Iodine.run @options, &(::Iodine::Http::Http2.dispatch)
|
54
|
-
@options = nil
|
55
|
-
end
|
56
|
-
def on_message data
|
57
|
-
data = ::StringIO.new data
|
58
|
-
parse_preface data unless @connected
|
59
|
-
true while parse_frame data
|
60
|
-
end
|
61
|
-
|
62
|
-
def send_response response
|
63
|
-
return false if response.headers.frozen?
|
64
|
-
body = response.extract_body
|
65
|
-
request = response.request
|
66
|
-
return body && body.close unless send_headers response, request
|
67
|
-
return log_finished(response) && body && body.close if request.head?
|
68
|
-
if body
|
69
|
-
until body.eof?
|
70
|
-
response.bytes_written += emit_payload(body.read(@settings[SETTINGS_MAX_FRAME_SIZE], Thread.current[:write_buffer]), request[:sid], 0, (body.eof? ? 1 : 0))
|
71
|
-
end
|
72
|
-
body.close
|
73
|
-
else
|
74
|
-
emit_payload(''.freeze, request[:sid], 0, 1)
|
75
|
-
end
|
76
|
-
log_finished response
|
77
|
-
end
|
78
|
-
|
79
|
-
def stream_response response, finish = false
|
80
|
-
request = response.request
|
81
|
-
body = response.extract_body
|
82
|
-
send_headers response, request
|
83
|
-
return body && body.close if request.head?
|
84
|
-
if body
|
85
|
-
response.bytes_written += emit_payload body, request[:sid], 0,(finish ? 1 : 0)
|
86
|
-
body.close
|
87
|
-
elsif finish
|
88
|
-
emit_payload(''.freeze, request[:sid], 0, 1)
|
89
|
-
end
|
90
|
-
log_finished response if finish
|
91
|
-
end
|
92
|
-
|
93
|
-
def ping
|
94
|
-
@frame_locker.synchronize { emit_frame "pniodine".freeze, 0, 6 }
|
95
|
-
end
|
96
|
-
|
97
|
-
def push request
|
98
|
-
return false if @settings[SETTINGS_ENABLE_PUSH] == 0
|
99
|
-
@last_push ||= 0
|
100
|
-
# emit push promise
|
101
|
-
emit_payload @hpack.encode(path: request[:path], method: request[:method], scheme: request[:scheme], authority: request[:authority]), (request[:sid] = (@last_push += 2)), 5, 4
|
102
|
-
# queue for app dispatch
|
103
|
-
Iodine.run( request, &::Iodine::Http::Http2.dispatch)
|
104
|
-
end
|
105
|
-
|
106
|
-
def go_away error_code
|
107
|
-
return false if @io.closed?
|
108
|
-
@frame_locker.synchronize { emit_frame [@last_stream, error_code].pack('N*'), 0, 7 }
|
109
|
-
close
|
110
|
-
# Iodine.info "HTTP/2 connection closed with code #{error_code}"
|
111
|
-
end
|
112
|
-
|
113
|
-
# Gracefully close HTTP/2 when possible
|
114
|
-
def on_shutdown
|
115
|
-
go_away NO_ERROR
|
116
|
-
end
|
117
|
-
|
118
|
-
# clear text handshake
|
119
|
-
def self.handshake request, io, data
|
120
|
-
return false unless request['upgrade'.freeze] =~ /h2c/.freeze && request['http2-settings'.freeze]
|
121
|
-
io.write "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: h2c\r\n\r\n".freeze
|
122
|
-
http_2 = self.new(io, request)
|
123
|
-
unless data.eof?
|
124
|
-
http_2.on_message data.read
|
125
|
-
end
|
126
|
-
end
|
127
|
-
# preknowledge handshake
|
128
|
-
def self.pre_handshake io, data
|
129
|
-
return false unless data[0..23] == "PRI * HTTP\/2.0\r\n\r\nSM\r\n\r\n".freeze
|
130
|
-
self.new(io).on_message data
|
131
|
-
true
|
132
|
-
end
|
133
|
-
|
134
|
-
|
135
|
-
protected
|
136
|
-
|
137
|
-
# logs the sent response.
|
138
|
-
def log_finished response
|
139
|
-
request = response.request
|
140
|
-
return if Iodine.logger.nil? || request[:no_log]
|
141
|
-
t_n = Time.now
|
142
|
-
# (Thread.current[:log_buffer] ||= String.new).clear
|
143
|
-
# Thread.current[:log_buffer] << "#{request[:client_ip]} [#{t_n.utc}] #{request[:method]} #{request[:original_path]} #{request[:scheme]}\/2 #{response.status} #{response.bytes_written.to_s} #{((t_n - request[:time_recieved])*1000).round(2)}ms\n"
|
144
|
-
# Iodine.log Thread.current[:log_buffer]
|
145
|
-
Iodine.log("#{request[:client_ip]} [#{t_n.utc}] #{request[:method]} #{request[:original_path]} #{request[:scheme]}\/2 #{response.status} #{response.bytes_written.to_s} #{((t_n - request[:time_recieved])*1000).round(2)}ms\n").clear
|
146
|
-
end
|
147
|
-
|
148
|
-
def send_headers response, request
|
149
|
-
return false if response.headers.frozen?
|
150
|
-
headers = response.headers
|
151
|
-
# headers[:status] = response.status.to_s
|
152
|
-
headers['set-cookie'.freeze] = response.extract_cookies
|
153
|
-
headers.freeze
|
154
|
-
(Thread.current[:headers_buffer] ||= String.new).clear
|
155
|
-
Thread.current[:headers_buffer] << @hpack.encode(status: response.status)
|
156
|
-
Thread.current[:headers_buffer] << @hpack.encode(headers)
|
157
|
-
emit_payload Thread.current[:headers_buffer], request[:sid], 1, (request.head? ? 1 : 0)
|
158
|
-
return true
|
159
|
-
end
|
160
|
-
|
161
|
-
# Sends an HTTP frame with the requested payload
|
162
|
-
#
|
163
|
-
# @return [true, false] returns true if the frame was sent and false if the frame couldn't be sent (i.e. payload too big, connection closed etc').
|
164
|
-
def emit_frame payload, sid = 0, type = 0, flags = 0
|
165
|
-
# puts "Sent: #{[payload.bytesize, type, flags, sid, payload].pack('N C C N a*'.freeze)[1..-1].inspect}"
|
166
|
-
@io.write( [payload.bytesize, type, flags, sid, payload].pack('N C C N a*'.freeze)[1..-1] ) #.tap {|s| next if type !=1 ;puts "Frame: #{s.class.name} - #{s.encoding}"; puts s.inspect } )
|
167
|
-
end
|
168
|
-
|
169
|
-
# Sends an HTTP frame group with the requested payload. This means the group will not be interrupted and will be sent as one unit.
|
170
|
-
#
|
171
|
-
# @return [true, false] returns true if the frame was sent and false if the frame couldn't be sent (i.e. payload too big, connection closed etc').
|
172
|
-
def emit_payload payload, sid = 0, type = 0, flags = 0
|
173
|
-
max_frame_size = @settings[SETTINGS_MAX_FRAME_SIZE]
|
174
|
-
max_frame_size = 131_072 if max_frame_size > 131_072
|
175
|
-
return @frame_locker.synchronize { emit_frame(payload, sid, type, ( (type == 0x1 || type == 0x5) ? (flags | 0x4) : flags ) ) } if payload.bytesize <= max_frame_size
|
176
|
-
sent = 0
|
177
|
-
payload = StringIO.new payload unless payload.respond_to? :read
|
178
|
-
if type == 0x1 || type == 0x5
|
179
|
-
@frame_locker.synchronize do
|
180
|
-
sent += emit_frame(payload.read(max_frame_size, Thread.current[:write_buffer]), sid, 0x1, flags & 254)
|
181
|
-
sent += emit_frame(payload.read(max_frame_size, Thread.current[:write_buffer]), sid, 0x9, 0) while payload.size - payload.pos > max_frame_size
|
182
|
-
sent += emit_frame(payload.read(max_frame_size, Thread.current[:write_buffer]), sid, 0x9, (0x4 | (flags & 0x1)) )
|
183
|
-
end
|
184
|
-
return sent
|
185
|
-
end
|
186
|
-
sent += @frame_locker.synchronize { emit_frame(payload.read(max_frame_size, Thread.current[:write_buffer]), sid, type, (flags & 254)) } while payload.size - payload.pos > max_frame_size
|
187
|
-
sent += @frame_locker.synchronize { emit_frame(payload.read(max_frame_size, Thread.current[:write_buffer]), sid, type, flags) }
|
188
|
-
end
|
189
|
-
|
190
|
-
def parse_preface data
|
191
|
-
unless data.read(24) == "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
|
192
|
-
data.string.clear
|
193
|
-
data.rewind
|
194
|
-
return (connection_error(PROTOCOL_ERROR) && Iodine.warn("Preface not given"))
|
195
|
-
end
|
196
|
-
@connected = true
|
197
|
-
emit_frame ''.freeze, 0, 0x4
|
198
|
-
true
|
199
|
-
end
|
200
|
-
|
201
|
-
def parse_frame data
|
202
|
-
frame = (@frame ||= {})
|
203
|
-
unless frame[:length]
|
204
|
-
tmp = (frame[:length_bytes] ||= "\x00")
|
205
|
-
tmp << data.read(4 - tmp.bytesize).to_s
|
206
|
-
return false if tmp.bytesize < 4
|
207
|
-
frame[:length] = frame.delete(:length_bytes).unpack('N*'.freeze).pop
|
208
|
-
end
|
209
|
-
# TODO: error if length is greater than max_size (16_384 is default)
|
210
|
-
if frame[:length] > @settings[SETTINGS_MAX_FRAME_SIZE]
|
211
|
-
return false unless connection_error FRAME_SIZE_ERROR
|
212
|
-
end
|
213
|
-
unless frame[:type]
|
214
|
-
tmp = data.getc
|
215
|
-
return false unless tmp
|
216
|
-
frame[:type] = tmp.ord
|
217
|
-
end
|
218
|
-
unless frame[:flags]
|
219
|
-
tmp = data.getc
|
220
|
-
return false unless tmp
|
221
|
-
frame[:flags] = tmp.ord
|
222
|
-
end
|
223
|
-
unless frame[:sid]
|
224
|
-
tmp = (frame[:sid_bytes] ||= String.new)
|
225
|
-
tmp << data.read(4 - tmp.bytesize).to_s
|
226
|
-
return false if tmp.bytesize < 4
|
227
|
-
tmp = frame.delete(:sid_bytes).unpack('N')[0]
|
228
|
-
frame[:sid] = tmp & 2147483647
|
229
|
-
frame[:R] = tmp & 2147483648
|
230
|
-
end
|
231
|
-
tmp = (frame[:body] ||= String.new)
|
232
|
-
tmp << data.read(frame[:length] - tmp.bytesize).to_s
|
233
|
-
return false if tmp.bytesize < frame[:length]
|
234
|
-
#TODO: something - Async?
|
235
|
-
process_frame frame
|
236
|
-
# reset frame buffer
|
237
|
-
@frame = {} # @frame.clear
|
238
|
-
true
|
239
|
-
end
|
240
|
-
|
241
|
-
def process_frame frame
|
242
|
-
# puts "processing HTTP/2 frame: #{frame}"
|
243
|
-
(frame[:stream] = ( @open_streams[frame[:sid]] ||= ::Iodine::Http::Request.new(self) ) ) && (frame[:stream][:sid] ||= frame[:sid]) if frame[:sid] != 0
|
244
|
-
case frame[:type]
|
245
|
-
when 0 # DATA
|
246
|
-
process_data frame
|
247
|
-
when 1, 9 # HEADERS, CONTINUATION
|
248
|
-
process_headers frame
|
249
|
-
when 2 # PRIORITY
|
250
|
-
when 3 # RST_STREAM
|
251
|
-
@open_streams.delete frame[:sid]
|
252
|
-
when 4 # SETTINGS
|
253
|
-
process_settings frame
|
254
|
-
# when 5 # PUSH_PROMISE - Should only be sent by the server
|
255
|
-
when 6 # PING
|
256
|
-
process_ping frame
|
257
|
-
when 7 # GOAWAY
|
258
|
-
go_away NO_ERROR
|
259
|
-
Iodine.error "Http2 Disconnection with error (#{frame[:flags].to_s}): #{frame[:body].strip}" unless frame[:flags] == 0 && frame[:body] == ''.freeze
|
260
|
-
when 8 # WINDOW_UPDATE
|
261
|
-
else # Error, frame not recognized
|
262
|
-
end
|
263
|
-
|
264
|
-
# The PING frame (type=0x6) (most important!!!) is a mechanism for measuring a minimal round-trip time from the sender, as well as determining whether an idle connection is still functional
|
265
|
-
# ACK flag: 0x1 - if not present, must send frame back.
|
266
|
-
# PING frames are not associated with any individual stream. If a PING frame is received with a stream identifier field value other than 0x0, the recipient MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
267
|
-
# DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a stream. One or more DATA frames are used, for instance, to carry HTTP request or response payloads
|
268
|
-
# The HEADERS frame (type=0x1)
|
269
|
-
# The RST_STREAM frame (type=0x3 - 32bit error code) allows for immediate termination of a stream. RST_STREAM is sent to request cancellation of a stream or to indicate that an error condition has occurred.
|
270
|
-
# The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints communicate.
|
271
|
-
# The payload of a SETTINGS frame consists of zero or more parameters, each consisting of an unsigned 16-bit setting identifier and an unsigned 32-bit value
|
272
|
-
# The CONTINUATION frame (type=0x9)
|
273
|
-
# The CONTINUATION frame defines the following flag:
|
274
|
-
# END_HEADERS (0x4):
|
275
|
-
# When set, bit 2 indicates that this frame ends a header block
|
276
|
-
# The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream (Section 5.3). It can be sent in any stream state, including idle or closed streams
|
277
|
-
# The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the sender intends to initiate.
|
278
|
-
# The GOAWAY frame (type=0x7) is used to initiate shutdown of a connection or to signal serious error conditions.
|
279
|
-
# The GOAWAY frame applies to the connection, not a specific stream (DIS 0x0)
|
280
|
-
# R (1 bit) LAST_STREAM_ID (31 bit) ERROR_CODE (32 bit) DEBUG_DATA(optional) (*)
|
281
|
-
# The WINDOW_UPDATE frame (type=0x8) is used to implement flow control
|
282
|
-
# A WINDOW_UPDATE frame with a length other than 4 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR.
|
283
|
-
|
284
|
-
end
|
285
|
-
|
286
|
-
def process_ping frame
|
287
|
-
# Iodine.info "Got HTTP/2 #{frame[:flags][0] == 1 ? 'Pong' : 'Ping'}"
|
288
|
-
return connection_error PROTOCOL_ERROR if frame[:sid].to_i > 0
|
289
|
-
return true if frame[:flags][0] == 1
|
290
|
-
emit_frame frame[:body], 0, 6, 1
|
291
|
-
# Iodine.info "Sent HTTP/2 'Pong'"
|
292
|
-
end
|
293
|
-
def process_headers frame
|
294
|
-
if @header_sid && (frame[:type] == 1 || frame[:sid] != @header_sid)
|
295
|
-
return connection_error PROTOCOL_ERROR
|
296
|
-
end
|
297
|
-
@header_end_stream = true if frame[:type] == 1 && frame[:flags][0] == 1
|
298
|
-
|
299
|
-
if frame[:flags][3] == 1 # padded
|
300
|
-
frame[:body] = frame[:body][1...(0 - frame[:body][0].ord)]
|
301
|
-
end
|
302
|
-
if frame[:flags][5] == 1 # priority
|
303
|
-
# stream_dependency = frame[:body][0..3]
|
304
|
-
# weight = frame[:body][4]
|
305
|
-
frame[:body] = frame[:body][5..-1]
|
306
|
-
end
|
307
|
-
|
308
|
-
@header_buffer << frame[:body]
|
309
|
-
|
310
|
-
frame[:stream][:headers_size] ||= 0
|
311
|
-
frame[:stream][:headers_size] += frame[:body].bytesize
|
312
|
-
|
313
|
-
return (Iodine.warn('Http2 header overloading, closing connection.') && connection_error( ENHANCE_YOUR_CALM ) ) if frame[:stream][:headers_size] > 262_144
|
314
|
-
|
315
|
-
return unless frame[:flags][2] == 1 # fin
|
316
|
-
|
317
|
-
frame[:stream].update @hpack.decode(@header_buffer) # this is where HPACK comes in
|
318
|
-
return (Iodine.warn('Http2 header overloading, closing connection.') && connection_error( ENHANCE_YOUR_CALM ) ) if frame[:stream].length > 2096
|
319
|
-
frame[:stream][:time_recieved] ||= Iodine.time
|
320
|
-
frame[:stream][:version] ||= '2'.freeze
|
321
|
-
|
322
|
-
process_request(@open_streams.delete frame[:sid]) if @header_end_stream
|
323
|
-
|
324
|
-
# TODO: manage headers and streams
|
325
|
-
|
326
|
-
@header_buffer.clear
|
327
|
-
@header_end_stream = false
|
328
|
-
@header_sid = nil
|
329
|
-
rescue => e
|
330
|
-
connection_error 5
|
331
|
-
Iodine.warn e
|
332
|
-
end
|
333
|
-
def process_data frame
|
334
|
-
if frame[:flags][3] == 1 # padded
|
335
|
-
frame[:body] = frame[:body][1...(0 - frame[:body][0].ord)]
|
336
|
-
end
|
337
|
-
|
338
|
-
(frame[:stream][:body] ||= Tempfile.new('iodine'.freeze, :encoding => 'binary'.freeze) ) << frame[:body]
|
339
|
-
|
340
|
-
# check request size
|
341
|
-
if frame[:stream][:body].size > ::Iodine::Http.max_body_size
|
342
|
-
Iodine.warn("Http2 payload (message size) too big (Iodine::Http.max_body_size == #{::Iodine::Http.max_body_size} bytes) - #{frame[:stream][:body].size} bytes.")
|
343
|
-
return connection_error( ENHANCE_YOUR_CALM )
|
344
|
-
end
|
345
|
-
|
346
|
-
process_request(@open_streams.delete frame[:sid]) if frame[:flags][0] == 1
|
347
|
-
end
|
348
|
-
|
349
|
-
# # Settings Codes:
|
350
|
-
SETTINGS_HEADER_TABLE_SIZE = 0x1
|
351
|
-
# Allows the sender to inform the remote endpoint of the maximum size of the header compression table used to decode header blocks, in octets.
|
352
|
-
# The encoder can select any size equal to or less than this value by using signaling specific to the header compression format inside a header block (see [COMPRESSION]).
|
353
|
-
# The initial value is 4,096 octets.
|
354
|
-
SETTINGS_ENABLE_PUSH = 0x2
|
355
|
-
# This setting can be used to disable server push (Section 8.2). An endpoint MUST NOT send a PUSH_PROMISE frame if it receives this parameter set to a value of 0. An endpoint that has both set this parameter to 0 and had it acknowledged MUST treat the receipt of a PUSH_PROMISE frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
356
|
-
# The initial value is 1, which indicates that server push is permitted. Any value other than 0 or 1 MUST be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
357
|
-
SETTINGS_MAX_CONCURRENT_STREAMS = 0x3
|
358
|
-
# Indicates the maximum number of concurrent streams that the sender will allow. This limit is directional: it applies to the number of streams that the sender permits the receiver to create.
|
359
|
-
# Initially, there is no limit to this value. It is recommended that this value be no smaller than 100, so as to not unnecessarily limit parallelism.
|
360
|
-
# A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as special by endpoints.
|
361
|
-
# A zero value does prevent the creation of new streams; however, this can also happen for any limit that is exhausted with active streams.
|
362
|
-
# Servers SHOULD only set a zero value for short durations; if a server does not wish to accept requests, closing the connection is more appropriate.
|
363
|
-
SETTINGS_INITIAL_WINDOW_SIZE = 0x4
|
364
|
-
# Indicates the sender's initial window size (in octets) for stream-level flow control. The initial value is 216-1 (65,535) octets.
|
365
|
-
# This setting affects the window size of all streams (see Section 6.9.2).
|
366
|
-
# Values above the maximum flow-control window size of 231-1 MUST be treated as a connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR.
|
367
|
-
SETTINGS_MAX_FRAME_SIZE = 0x5
|
368
|
-
# Indicates the size of the largest frame payload that the sender is willing to receive, in octets.
|
369
|
-
# The initial value is 214 (16,384) octets. The value advertised by an endpoint MUST be between this initial value and
|
370
|
-
# the maximum allowed frame size (224-1 or 16,777,215 octets), inclusive. Values outside this range MUST be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
371
|
-
SETTINGS_MAX_HEADER_LIST_SIZE = 0x6
|
372
|
-
# This advisory setting informs a peer of the maximum size of header list that the sender is prepared to accept, in octets. The value is based on the uncompressed size of header fields, including the length of the name and value in octets plus an overhead of 32 octets for each header field.
|
373
|
-
DEFAULT_SETTING = { SETTINGS_ENABLE_PUSH => 1,
|
374
|
-
SETTINGS_INITIAL_WINDOW_SIZE => 65_535,
|
375
|
-
SETTINGS_MAX_FRAME_SIZE => 16_384
|
376
|
-
}
|
377
|
-
|
378
|
-
def process_settings frame
|
379
|
-
return if frame[:flags] == 1 # do nothing if it's only an ACK.
|
380
|
-
return connection_error PROTOCOL_ERROR unless frame[:sid] == 0 && (frame[:body].bytesize % 6) == 0
|
381
|
-
settings = StringIO.new frame[:body]
|
382
|
-
until settings.eof?
|
383
|
-
key = settings.read(2).unpack('n'.freeze)[0]
|
384
|
-
value = settings.read(4).unpack('N'.freeze)[0]
|
385
|
-
Iodine.info "HTTP/2 set #{key}=>#{value} for SID #{frame[:sid]}"
|
386
|
-
case frame[:body][0..1].unpack('n'.freeze)[0]
|
387
|
-
when SETTINGS_HEADER_TABLE_SIZE
|
388
|
-
return connection_error ENHANCE_YOUR_CALM if value > 4096
|
389
|
-
@hpack.resize(value)
|
390
|
-
when SETTINGS_ENABLE_PUSH
|
391
|
-
@settings[SETTINGS_ENABLE_PUSH] = value
|
392
|
-
when SETTINGS_MAX_CONCURRENT_STREAMS
|
393
|
-
@settings[SETTINGS_MAX_CONCURRENT_STREAMS] = value
|
394
|
-
when SETTINGS_INITIAL_WINDOW_SIZE
|
395
|
-
@settings[SETTINGS_INITIAL_WINDOW_SIZE] = value
|
396
|
-
when SETTINGS_MAX_FRAME_SIZE
|
397
|
-
@settings[SETTINGS_MAX_FRAME_SIZE] = value
|
398
|
-
when SETTINGS_MAX_HEADER_LIST_SIZE
|
399
|
-
@settings[SETTINGS_MAX_HEADER_LIST_SIZE] = value
|
400
|
-
else
|
401
|
-
# Unsupported parameters MUST be ignored
|
402
|
-
end
|
403
|
-
end
|
404
|
-
emit_frame ''.freeze, 0, 4, 1
|
405
|
-
end
|
406
|
-
|
407
|
-
def process_request request
|
408
|
-
return if @refuese_new
|
409
|
-
::Iodine::Http::Request.parse request
|
410
|
-
# Iodine.info "Should Process request #{request.select { |k,v| k != :io } }"
|
411
|
-
@last_stream = request[:sid] if request[:sid] > @last_stream
|
412
|
-
# emit_frame [HTTP_1_1_REQUIRED].pack('N'), request[:sid], 0x3, 0
|
413
|
-
::Iodine.run request, &(::Iodine::Http::Http2.dispatch)
|
414
|
-
|
415
|
-
end
|
416
|
-
|
417
|
-
# Error codes:
|
418
|
-
|
419
|
-
# The associated condition is not a result of an error. For example, a GOAWAY might include this code to indicate graceful shutdown of a connection.
|
420
|
-
NO_ERROR = 0x0
|
421
|
-
# The endpoint detected an unspecific protocol error. This error is for use when a more specific error code is not available.
|
422
|
-
PROTOCOL_ERROR = 0x1
|
423
|
-
# The endpoint encountered an unexpected internal error.
|
424
|
-
INTERNAL_ERROR = 0x2
|
425
|
-
# The endpoint detected that its peer violated the flow-control protocol.
|
426
|
-
FLOW_CONTROL_ERROR = 0x3
|
427
|
-
# The endpoint sent a SETTINGS frame but did not receive a response in a timely manner. See Section 6.5.3 ("Settings Synchronization").
|
428
|
-
SETTINGS_TIMEOUT = 0x4
|
429
|
-
# The endpoint received a frame after a stream was half-closed.
|
430
|
-
STREAM_CLOSED = 0x5
|
431
|
-
# The endpoint received a frame with an invalid size.
|
432
|
-
FRAME_SIZE_ERROR = 0x6
|
433
|
-
# The endpoint refused the stream prior to performing any application processing (see Section 8.1.4 for details).
|
434
|
-
REFUSED_STREAM = 0x7
|
435
|
-
# Used by the endpoint to indicate that the stream is no longer needed.
|
436
|
-
CANCEL = 0x8
|
437
|
-
# The endpoint is unable to maintain the header compression context for the connection.
|
438
|
-
COMPRESSION_ERROR = 0x9
|
439
|
-
# The connection established in response to a CONNECT request (Section 8.3) was reset or abnormally closed.
|
440
|
-
CONNECT_ERROR = 0xa
|
441
|
-
# The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load.
|
442
|
-
ENHANCE_YOUR_CALM = 0xb
|
443
|
-
# The underlying transport has properties that do not meet minimum security requirements (see Section 9.2).
|
444
|
-
INADEQUATE_SECURITY = 0xc
|
445
|
-
# The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
|
446
|
-
HTTP_1_1_REQUIRED = 0xd
|
447
|
-
|
448
|
-
# Process a connection error and act accordingly.
|
449
|
-
#
|
450
|
-
# @return [true, false, nil] returns true if connection handling can continue of false (or nil) for a fatal error.
|
451
|
-
def connection_error type
|
452
|
-
::Iodine.warn "HTTP/2 error #{type}."
|
453
|
-
go_away type
|
454
|
-
# case type
|
455
|
-
# when NO_ERROR
|
456
|
-
# when PROTOCOL_ERROR
|
457
|
-
# when INTERNAL_ERROR
|
458
|
-
# when FLOW_CONTROL_ERROR
|
459
|
-
# when SETTINGS_TIMEOUT
|
460
|
-
# when STREAM_CLOSED
|
461
|
-
# when FRAME_SIZE_ERROR
|
462
|
-
# when REFUSED_STREAM
|
463
|
-
# when CANCEL
|
464
|
-
# when COMPRESSION_ERROR
|
465
|
-
# when CONNECT_ERROR
|
466
|
-
# when ENHANCE_YOUR_CALM
|
467
|
-
# when INADEQUATE_SECURITY
|
468
|
-
# when HTTP_1_1_REQUIRED
|
469
|
-
# else
|
470
|
-
# end
|
471
|
-
# nil
|
472
|
-
end
|
473
|
-
|
474
|
-
def self.dispatch
|
475
|
-
@dispatch ||= Proc.new do |request|
|
476
|
-
case request[:method]
|
477
|
-
when 'TRACE'.freeze
|
478
|
-
close
|
479
|
-
return false
|
480
|
-
when 'OPTIONS'.freeze
|
481
|
-
response = ::Iodine::Http::Response.new request
|
482
|
-
response[:Allow] = 'GET,HEAD,POST,PUT,DELETE,OPTIONS'.freeze
|
483
|
-
response['access-control-allow-origin'.freeze] = '*'
|
484
|
-
response.finish
|
485
|
-
return false
|
486
|
-
end
|
487
|
-
response = ::Iodine::Http::Response.new request
|
488
|
-
begin
|
489
|
-
ret = Iodine::Http.on_http.call(request, response)
|
490
|
-
if ret.is_a?(String)
|
491
|
-
response << ret
|
492
|
-
elsif ret == false
|
493
|
-
response.clear && (response.status = 404) && (response << ::Iodine::Http::Response::STATUS_CODES[404])
|
494
|
-
end
|
495
|
-
response.finish
|
496
|
-
rescue => e
|
497
|
-
::Iodine.error e
|
498
|
-
# request[:io].emit_frame [INTERNAL_ERROR].pack('N'.freeze), request[:sid], 3
|
499
|
-
::Iodine::Http::Response.new(request, 500, {}, ::Iodine::Http::Response::STATUS_CODES[500]).finish
|
500
|
-
end
|
501
|
-
end
|
502
|
-
end
|
503
|
-
end
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
|