iodine 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/iodine/http.rb +24 -11
- data/lib/iodine/http/hpack.rb +5 -5
- data/lib/iodine/http/http1.rb +168 -165
- data/lib/iodine/http/http2.rb +1 -1
- data/lib/iodine/http/rack_support.rb +7 -6
- data/lib/iodine/http/request.rb +1 -1
- data/lib/iodine/http/response.rb +1 -3
- data/lib/iodine/http/session.rb +2 -1
- data/lib/iodine/http/websocket_client.rb +1 -1
- data/lib/iodine/http/websocket_handler.rb +1 -1
- data/lib/iodine/http/websockets.rb +2 -2
- data/lib/iodine/io.rb +4 -2
- data/lib/iodine/protocol.rb +3 -0
- data/lib/iodine/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9451f837296bd4fecf60df42fd3cfee80d80ef0f
|
4
|
+
data.tar.gz: af0d8bf95137d229b23ee74aef4825f50a9aafa0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6039e844f9b6658326a70fbadc12ebd61007b466a546100670382ce119d2d0bbf193497fc2c40a4fc36945352ad94e2f83f3a1c4f5f1a6a3c88f6c0719bf3dd1
|
7
|
+
data.tar.gz: 608df34266557cb59300dd08321c3dcef74a67c7f7f0eec343ae77ac823f440d2108b40008e8c744fd753199ca73b0dffdfb96fc9bad4a2ce1aaac362319d093
|
data/lib/iodine/http.rb
CHANGED
@@ -68,14 +68,15 @@ module Iodine
|
|
68
68
|
#
|
69
69
|
# See {Iodine::Http::WebsocketHandler} for a good starting point or inherit {Iodine::Http::WebsocketHandler} in your handler.
|
70
70
|
#
|
71
|
-
|
71
|
+
module Http
|
72
|
+
public
|
72
73
|
# Sets or gets the Http callback.
|
73
74
|
#
|
74
75
|
# An Http callback is a Proc like object that answers to `call(request, response)` and returns either:
|
75
76
|
# `true`:: the response has been set by the callback and can be managed (including any streaming) by the server.
|
76
77
|
# `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
|
77
78
|
# String:: the String will be appended to the response and the response sent.
|
78
|
-
def
|
79
|
+
def on_http handler = nil, &block
|
79
80
|
@http_app = handler || block if handler || block
|
80
81
|
@http_app
|
81
82
|
end
|
@@ -84,29 +85,38 @@ module Iodine
|
|
84
85
|
# A Websockets callback is a Proc like object that answers to `call(request)` and returns either:
|
85
86
|
# `false`:: the request shouldn't be answered or resource not found (error 404 will be sent as a response).
|
86
87
|
# Websocket Handler:: a Websocket handler is an object that is expected to answer `on_message(data)` and `on_close`. See {} for more data.
|
87
|
-
def
|
88
|
+
def on_websocket handler = nil, &block
|
88
89
|
@websocket_app = handler || block if handler || block
|
89
90
|
@websocket_app
|
90
91
|
end
|
91
92
|
|
92
93
|
# Sets the session token for the Http server (String). Defaults to the name of the script + '_id'.
|
93
|
-
def
|
94
|
+
def session_token= token
|
94
95
|
@session_token = token
|
95
96
|
end
|
96
97
|
# Sets the session token for the Http server (String). Defaults to the name of the script.
|
97
|
-
def
|
98
|
+
def session_token
|
98
99
|
@session_token
|
99
100
|
end
|
100
101
|
|
101
102
|
# Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
|
102
|
-
def
|
103
|
+
def http2= allow
|
103
104
|
@http2 = allow && true
|
104
105
|
end
|
105
106
|
# Returns true if Iodine will require that new connection be encrypted.
|
106
|
-
def
|
107
|
+
def http2
|
107
108
|
@http2
|
108
109
|
end
|
109
110
|
|
111
|
+
# # Sets whether Iodine will allow connections to the experiemntal Http2 protocol. Defaults to false unless the `http2` command line flag is present.
|
112
|
+
# def self.message_buffer_size= size
|
113
|
+
# @message_buffer_size = size
|
114
|
+
# end
|
115
|
+
# # Returns true if Iodine will require that new connection be encrypted.
|
116
|
+
# def self.message_buffer_size
|
117
|
+
# @message_buffer_size
|
118
|
+
# end
|
119
|
+
|
110
120
|
# Creates a websocket client within a new task (non-blocking).
|
111
121
|
#
|
112
122
|
# Make sure to setup all the callbacks (as needed) prior to starting the connection. See {::Iodine::Http::WebsocketClient.connect}
|
@@ -132,22 +142,25 @@ module Iodine
|
|
132
142
|
# #if running from irb:
|
133
143
|
# exit
|
134
144
|
#
|
135
|
-
def
|
145
|
+
def ws_connect url, options={}, &block
|
136
146
|
::Iodine.run { ::Iodine::Http::WebsocketClient.connect url, options, &block }
|
137
147
|
end
|
138
148
|
|
149
|
+
protected
|
150
|
+
|
139
151
|
@http2 = (ARGV.index('http2') && true)
|
140
152
|
|
141
153
|
@websocket_app = @http_app = NOT_IMPLEMENTED = Proc.new { |i,o| false }
|
142
154
|
@session_token = "#{File.basename($0, '.*')}_uuid"
|
155
|
+
extend self
|
143
156
|
end
|
144
157
|
|
145
158
|
@queue.tap do |q|
|
146
159
|
arr =[];
|
147
160
|
arr << q.pop until q.empty?;
|
148
|
-
run { Iodine.ssl_protocols = { 'h2' => Iodine::Http::Http2, 'http/1.1' => Iodine::Http } if @ssl && @ssl_protocols.empty? && ::Iodine::Http.http2 }
|
161
|
+
run { ::Iodine.ssl_protocols = { 'h2' => ::Iodine::Http::Http2, 'http/1.1' => ::Iodine::Http::Http1 } if @ssl && @ssl_protocols.empty? && ::Iodine::Http.http2 }
|
149
162
|
run do
|
150
|
-
if Iodine.protocol == ::Iodine::Http && ::Iodine::Http.on_http == ::Iodine::Http::NOT_IMPLEMENTED && ::Iodine::Http.on_websocket == ::Iodine::Http::NOT_IMPLEMENTED
|
163
|
+
if Iodine.protocol == ::Iodine::Http::Http1 && ::Iodine::Http.on_http == ::Iodine::Http::NOT_IMPLEMENTED && ::Iodine::Http.on_websocket == ::Iodine::Http::NOT_IMPLEMENTED
|
151
164
|
::Iodine.protocol = :http_not_initialized
|
152
165
|
q << arr.shift until arr.empty?
|
153
166
|
run { Process.kill("INT", 0) }
|
@@ -156,4 +169,4 @@ module Iodine
|
|
156
169
|
q << arr.shift until arr.empty?
|
157
170
|
end
|
158
171
|
end
|
159
|
-
Iodine.protocol = ::Iodine::Http
|
172
|
+
Iodine.protocol = ::Iodine::Http::Http1
|
data/lib/iodine/http/hpack.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
module Iodine
|
4
|
-
|
4
|
+
module Http
|
5
5
|
class Http2 < ::Iodine::Protocol
|
6
6
|
class HPACK
|
7
7
|
class IndexTable
|
@@ -145,10 +145,10 @@ module Iodine
|
|
145
145
|
buffer
|
146
146
|
rescue
|
147
147
|
puts "HPACK failure data dump:"
|
148
|
-
puts "buffer: #{buffer} - #{buffer.encoding}"
|
149
|
-
puts "
|
150
|
-
puts "
|
151
|
-
puts "packed #{pack_string(
|
148
|
+
puts "buffer: #{buffer} - #{buffer.encoding}" if buffer
|
149
|
+
puts "name: #{name} - #{name.encoding}" if name.is_a? String
|
150
|
+
puts "value: #{value} - #{value.encoding}" if value.is_a? String
|
151
|
+
puts "packed #{pack_string(name)} - #{pack_string(value)}" if value
|
152
152
|
raise
|
153
153
|
end
|
154
154
|
def extract_number data, prefix, prefix_length
|
data/lib/iodine/http/http1.rb
CHANGED
@@ -1,195 +1,198 @@
|
|
1
1
|
module Iodine
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
until request[:headers_complete] || (l = data.gets).nil?
|
25
|
-
if l.include? ':'
|
26
|
-
# n = l.slice!(0, l.index(':')); l.slice! 0
|
27
|
-
# n.strip! ; n.downcase!; n.freeze
|
28
|
-
# request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l)
|
29
|
-
l = l.strip.split(/:[\s]?/, 2)
|
30
|
-
l[0].strip! ; l[0].downcase!;
|
31
|
-
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])
|
32
|
-
elsif l =~ /^[\r]?\n/
|
33
|
-
request[:headers_complete] = true
|
34
|
-
else
|
35
|
-
#protocol error
|
36
|
-
Iodine.warn 'Protocol Error, closing connection.'
|
37
|
-
return close
|
2
|
+
module Http
|
3
|
+
class Http1 < ::Iodine::Protocol
|
4
|
+
def on_open
|
5
|
+
set_timeout 1
|
6
|
+
@refuse_requests = false
|
7
|
+
@bytes_sent = 0
|
8
|
+
@parser = {}
|
9
|
+
end
|
10
|
+
def on_message data
|
11
|
+
return if @refuse_requests
|
12
|
+
@http2_pri_review ||= ( ::Iodine::Http.http2 && ::Iodine::Http::Http2.pre_handshake(self, data) && (return true) ) || true
|
13
|
+
|
14
|
+
data = ::StringIO.new data
|
15
|
+
until data.eof?
|
16
|
+
request = (@request ||= ::Iodine::Http::Request.new(self))
|
17
|
+
unless request[:method]
|
18
|
+
l = data.gets.strip
|
19
|
+
next if l.empty?
|
20
|
+
request[:method], request[:query], request[:version] = l.split(/[\s]+/, 3)
|
21
|
+
return (Iodine.warn('Protocol Error, closing connection.') && close) unless request[:method] =~ HTTP_METHODS_REGEXP
|
22
|
+
request[:version] = (request[:version] || '1.1'.freeze).match(/[\d\.]+/)[0]
|
23
|
+
request[:time_recieved] = Time.now
|
38
24
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
25
|
+
until request[:headers_complete] || (l = data.gets).nil?
|
26
|
+
if l.include? ':'
|
27
|
+
# n = l.slice!(0, l.index(':')); l.slice! 0
|
28
|
+
# n.strip! ; n.downcase!; n.freeze
|
29
|
+
# request[n] ? (request[n].is_a?(Array) ? (request[n] << l) : request[n] = [request[n], l ]) : (request[n] = l)
|
30
|
+
l = l.strip.split(/:[\s]?/, 2)
|
31
|
+
l[0].strip! ; l[0].downcase!;
|
32
|
+
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])
|
33
|
+
elsif l =~ /^[\r]?\n/
|
34
|
+
request[:headers_complete] = true
|
35
|
+
else
|
36
|
+
#protocol error
|
37
|
+
Iodine.warn 'Protocol Error, closing connection.'
|
38
|
+
return close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
until request[:body_complete] && request[:headers_complete]
|
42
|
+
if request['transfer-coding'.freeze] == 'chunked'.freeze
|
43
|
+
# ad mid chunk logic here
|
44
|
+
if @parser[:length].to_i == 0
|
45
|
+
chunk = data.gets
|
46
|
+
return false unless chunk
|
47
|
+
@parser[:length] = chunk.to_i(16)
|
48
|
+
return (Iodine.warn('Protocol Error, closing connection.') && close) unless @parser[:length]
|
49
|
+
request[:body_complete] = true && break if @parser[:length] == 0
|
50
|
+
@parser[:act_length] = 0
|
51
|
+
request[:body] ||= ''
|
52
|
+
end
|
53
|
+
chunk = data.read(@parser[:length] - @parser[:act_length])
|
45
54
|
return false unless chunk
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
55
|
+
request[:body] << chunk
|
56
|
+
@parser[:act_length] += chunk.bytesize
|
57
|
+
(@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length]
|
58
|
+
elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0
|
50
59
|
request[:body] ||= ''
|
60
|
+
packet = data.read(request['content-length'.freeze].to_i - request[:body].bytesize)
|
61
|
+
return false unless packet
|
62
|
+
request[:body] << packet
|
63
|
+
request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].bytesize <= 0
|
64
|
+
elsif request['content-type'.freeze]
|
65
|
+
Iodine.warn 'Body type protocol error.' unless request[:body]
|
66
|
+
line = data.gets
|
67
|
+
return false unless line
|
68
|
+
(request[:body] ||= '') << line
|
69
|
+
request[:body_complete] = true if line =~ EOHEADERS
|
70
|
+
else
|
71
|
+
request[:body_complete] = true
|
51
72
|
end
|
52
|
-
chunk = data.read(@parser[:length] - @parser[:act_length])
|
53
|
-
return false unless chunk
|
54
|
-
request[:body] << chunk
|
55
|
-
@parser[:act_length] += chunk.bytesize
|
56
|
-
(@parser[:act_length] = @parser[:length] = 0) && (data.gets) if @parser[:act_length] >= @parser[:length]
|
57
|
-
elsif request['content-length'.freeze] && request['content-length'.freeze].to_i != 0
|
58
|
-
request[:body] ||= ''
|
59
|
-
packet = data.read(request['content-length'.freeze].to_i - request[:body].bytesize)
|
60
|
-
return false unless packet
|
61
|
-
request[:body] << packet
|
62
|
-
request[:body_complete] = true if request['content-length'.freeze].to_i - request[:body].bytesize <= 0
|
63
|
-
elsif request['content-type'.freeze]
|
64
|
-
Iodine.warn 'Body type protocol error.' unless request[:body]
|
65
|
-
line = data.gets
|
66
|
-
return false unless line
|
67
|
-
(request[:body] ||= '') << line
|
68
|
-
request[:body_complete] = true if line =~ EOHEADERS
|
69
|
-
else
|
70
|
-
request[:body_complete] = true
|
71
73
|
end
|
74
|
+
(@request = ::Iodine::Http::Request.new(self)) && ( (::Iodine::Http.http2 && ::Iodine::Http::Http2.handshake(request, self, data)) || dispatch(request, data) ) if request.delete :body_complete
|
72
75
|
end
|
73
|
-
|
74
|
-
end
|
75
|
-
end
|
76
|
+
end
|
76
77
|
|
77
|
-
|
78
|
-
|
78
|
+
def send_response response
|
79
|
+
return false if response.headers.frozen?
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
request = response.request
|
82
|
+
headers = response.headers
|
83
|
+
body = response.extract_body
|
83
84
|
|
84
|
-
|
85
|
+
headers['content-length'.freeze] ||= body.to_s.bytesize
|
85
86
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
87
|
+
keep_alive = response.keep_alive
|
88
|
+
if (request[:version].to_f > 1 && request['connection'.freeze].nil?) || request['connection'.freeze].to_s =~ /ke/i || (headers['connection'.freeze] && headers['connection'.freeze] =~ /^ke/i)
|
89
|
+
keep_alive = true
|
90
|
+
headers['connection'.freeze] ||= 'keep-alive'.freeze
|
91
|
+
headers['keep-alive'.freeze] ||= "timeout=#{(@timeout ||= 3).to_s}"
|
92
|
+
else
|
93
|
+
headers['connection'.freeze] ||= 'close'.freeze
|
94
|
+
end
|
94
95
|
|
95
|
-
send_headers response
|
96
|
-
return log_finished(response) if request.head?
|
97
|
-
(response.bytes_written += (write(body) || 0)) && (body.frozen? || body.clear) if body
|
98
|
-
close unless keep_alive
|
99
|
-
log_finished response
|
100
|
-
end
|
101
|
-
def stream_response response, finish = false
|
102
|
-
unless response.headers.frozen?
|
103
|
-
response['transfer-encoding'.freeze] = 'chunked'
|
104
|
-
response.headers['connection'.freeze] = 'close'.freeze
|
105
96
|
send_headers response
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
body = response.extract_body
|
110
|
-
response.bytes_written += stream_data(body) if body || finish
|
111
|
-
if finish
|
112
|
-
response.bytes_written += stream_data('') unless body.nil?
|
97
|
+
return log_finished(response) if request.head?
|
98
|
+
(response.bytes_written += (write(body) || 0)) && (body.frozen? || body.clear) if body
|
99
|
+
close unless keep_alive
|
113
100
|
log_finished response
|
114
101
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
102
|
+
def stream_response response, finish = false
|
103
|
+
unless response.headers.frozen?
|
104
|
+
response['transfer-encoding'.freeze] = 'chunked'
|
105
|
+
response.headers['connection'.freeze] = 'close'.freeze
|
106
|
+
send_headers response
|
107
|
+
@refuse_requests = true
|
108
|
+
end
|
109
|
+
return if response.request.head?
|
110
|
+
body = response.extract_body
|
111
|
+
response.bytes_written += stream_data(body) if body || finish
|
112
|
+
if finish
|
113
|
+
response.bytes_written += stream_data('') unless body.nil?
|
114
|
+
log_finished response
|
115
|
+
close unless response.keep_alive
|
116
|
+
end
|
117
|
+
(body.frozen? || body.clear) if body
|
118
|
+
true
|
119
|
+
end
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
121
|
+
protected
|
122
|
+
|
123
|
+
HTTP_METHODS = %w{GET HEAD POST PUT DELETE TRACE OPTIONS CONNECT PATCH}
|
124
|
+
HTTP_METHODS_REGEXP = /\A#{HTTP_METHODS.join('|')}/i
|
125
|
+
|
126
|
+
def dispatch request, data
|
127
|
+
return data.string.clear if @io.closed? || @refuse_requests
|
128
|
+
::Iodine::Http::Request.parse request
|
129
|
+
#check for server-responses
|
130
|
+
case request[:method]
|
131
|
+
when 'TRACE'.freeze
|
132
|
+
close
|
133
|
+
data.string.clear
|
134
|
+
return false
|
135
|
+
when 'OPTIONS'.freeze
|
136
|
+
response = ::Iodine::Http::Response.new request
|
137
|
+
response[:Allow] = 'GET,HEAD,POST,PUT,DELETE,OPTIONS'.freeze
|
138
|
+
response['access-control-allow-origin'.freeze] = '*'
|
139
|
+
response['content-length'.freeze] = 0
|
140
|
+
send_response response
|
141
|
+
return false
|
142
|
+
end
|
134
143
|
response = ::Iodine::Http::Response.new request
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
else
|
147
|
-
ret = self.class.on_http.call(request, response)
|
148
|
-
if ret.is_a?(String)
|
149
|
-
response << ret
|
150
|
-
elsif ret == false
|
151
|
-
response.clear && (response.status = 404) && (response << ::Iodine::Http::Response::STATUS_CODES[404])
|
144
|
+
begin
|
145
|
+
if request.websocket?
|
146
|
+
@refuse_requests = true
|
147
|
+
::Iodine::Http::Websockets.handshake request, response, ::Iodine::Http.on_websocket.call(request, response)
|
148
|
+
else
|
149
|
+
ret = ::Iodine::Http.on_http.call(request, response)
|
150
|
+
if ret.is_a?(String)
|
151
|
+
response << ret
|
152
|
+
elsif ret == false
|
153
|
+
response.clear && (response.status = 404) && (response << ::Iodine::Http::Response::STATUS_CODES[404])
|
154
|
+
end
|
152
155
|
end
|
156
|
+
send_response response
|
157
|
+
rescue => e
|
158
|
+
Iodine.error e
|
159
|
+
send_response ::Iodine::Http::Response.new(request, 500, {}, ::Iodine::Http::Response::STATUS_CODES[500])
|
153
160
|
end
|
154
|
-
send_response response
|
155
|
-
rescue => e
|
156
|
-
Iodine.error e
|
157
|
-
send_response ::Iodine::Http::Response.new(request, 500, {}, ::Iodine::Http::Response::STATUS_CODES[500])
|
158
161
|
end
|
159
|
-
end
|
160
162
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
163
|
+
def send_headers response
|
164
|
+
return false if response.headers.frozen?
|
165
|
+
request = response.request
|
166
|
+
headers = response.headers
|
165
167
|
|
166
|
-
|
168
|
+
# response['date'.freeze] ||= request[:time_recieved].httpdate
|
167
169
|
|
168
|
-
|
170
|
+
out = "HTTP/#{request[:version]} #{response.status} #{::Iodine::Http::Response::STATUS_CODES[response.status] || 'unknown'}\r\n"
|
169
171
|
|
170
|
-
|
172
|
+
out << request[:time_recieved].utc.strftime("Date: %a, %d %b %Y %H:%M:%S GMT\r\n".freeze) unless headers['date'.freeze]
|
171
173
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
174
|
+
# unless @headers['connection'] || (@request[:version].to_f <= 1 && (@request['connection'].nil? || !@request['connection'].match(/^k/i))) || (@request['connection'] && @request['connection'].match(/^c/i))
|
175
|
+
headers.each {|k,v| out << "#{k.to_s}: #{v}\r\n"}
|
176
|
+
out << "cache-control: max-age=0, no-cache\r\n".freeze unless headers['cache-control'.freeze]
|
177
|
+
response.extract_cookies.each {|cookie| out << "set-cookie: #{cookie}\r\n"}
|
178
|
+
out << "\r\n"
|
177
179
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
180
|
+
response.bytes_written += (write(out) || 0)
|
181
|
+
out.clear
|
182
|
+
headers.freeze
|
183
|
+
response.raw_cookies.freeze
|
184
|
+
end
|
185
|
+
def stream_data data = nil
|
186
|
+
write("#{data.to_s.bytesize.to_s(16)}\r\n#{data.to_s}\r\n") || 0
|
187
|
+
end
|
186
188
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
189
|
+
def log_finished response
|
190
|
+
@bytes_sent = 0
|
191
|
+
request = response.request
|
192
|
+
return if Iodine.logger.nil? || request[:no_log]
|
193
|
+
t_n = Time.now
|
194
|
+
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
|
195
|
+
end
|
193
196
|
end
|
194
197
|
end
|
195
198
|
end
|
data/lib/iodine/http/http2.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
module Iodine
|
2
2
|
|
3
|
-
|
3
|
+
module Http
|
4
4
|
# This (will be) a Rack handler for the Iodine HTTP server.
|
5
5
|
module Rack
|
6
6
|
module_function
|
7
7
|
def run(app, options = {})
|
8
8
|
@app = app
|
9
9
|
|
10
|
-
Iodine.protocol ||= Iodine::HTTP
|
11
10
|
Iodine.threads = 18
|
12
|
-
|
13
|
-
Iodine.protocol
|
11
|
+
Iodine.port = options[:Port]
|
12
|
+
Iodine.protocol ||= Iodine::Http::Http1
|
13
|
+
@pre_rack_handler = Iodine::Http.on_http
|
14
|
+
Iodine::Http.on_http self
|
14
15
|
true
|
15
16
|
end
|
16
17
|
def call request, response
|
@@ -23,10 +24,10 @@ module Iodine
|
|
23
24
|
raise "Rack app returned an unexpected value: #{res.to_s}" unless res && res.is_a?(Array)
|
24
25
|
response.status = res[0]
|
25
26
|
response.headers.clear
|
26
|
-
response.headers.
|
27
|
+
res[1].each {|k, v| response.headers[k.to_s.downcase] = v }
|
27
28
|
response.body = res[2]
|
28
29
|
response.raw_cookies.clear
|
29
|
-
response.headers['
|
30
|
+
response.headers['set-cookie'] = response.headers.delete('set-cookie').split("\n").join("\r\nset-cookie: ") if request[:io].is_a?(Iodine::Http::Http1) && response.headers['set-cookie']
|
30
31
|
response.request[:no_log] = true
|
31
32
|
true
|
32
33
|
end
|
data/lib/iodine/http/request.rb
CHANGED
data/lib/iodine/http/response.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Iodine
|
2
|
-
|
3
|
-
class Http < ::Iodine::Protocol
|
4
|
-
|
2
|
+
module Http
|
5
3
|
# this class handles Http responses.
|
6
4
|
#
|
7
5
|
# The response can be sent in stages but should complete within the scope of the connecton's message. Please notice that headers and status cannot be changed once the response started sending data.
|
data/lib/iodine/http/session.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Iodine
|
2
|
-
|
2
|
+
module Http
|
3
3
|
class Websockets < ::Iodine::Protocol
|
4
4
|
# initialize the websocket protocol.
|
5
5
|
def initialize io, handler, request, ws_extentions = nil
|
@@ -159,7 +159,7 @@ module Iodine
|
|
159
159
|
else
|
160
160
|
ws_extentions = nil
|
161
161
|
end
|
162
|
-
response['
|
162
|
+
response['sec-websocket-accept'.freeze] = Digest::SHA1.base64digest(request['sec-websocket-key'.freeze] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'.freeze)
|
163
163
|
response.session
|
164
164
|
# Iodine.log "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Upgraded HTTP to WebSockets.\n"
|
165
165
|
response.finish
|
data/lib/iodine/io.rb
CHANGED
@@ -26,7 +26,7 @@ module Iodine
|
|
26
26
|
|
27
27
|
protected
|
28
28
|
|
29
|
-
@port = (ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000
|
29
|
+
@port = ((ARGV.index('-p') && ARGV[ARGV.index('-p') + 1]) || ENV['PORT'] || 3000).to_i
|
30
30
|
@bind = (ARGV.index('-ip') && ARGV[ARGV.index('-ip') + 1]) || ENV['IP'] || "0.0.0.0"
|
31
31
|
@ssl = (ARGV.index('ssl') && true) || (@port == 443)
|
32
32
|
@protocol = nil
|
@@ -75,13 +75,15 @@ module Iodine
|
|
75
75
|
def on_open
|
76
76
|
@protocol = Iodine.protocol
|
77
77
|
@ssl = Iodine.ssl
|
78
|
+
@accept_proc = @protocol.method(:accept)
|
78
79
|
end
|
79
80
|
def call
|
80
81
|
begin
|
81
82
|
n_io = nil
|
82
83
|
loop do
|
83
84
|
n_io = @io.accept_nonblock
|
84
|
-
@protocol.accept(n_io, @ssl)
|
85
|
+
# @protocol.accept(n_io, @ssl)
|
86
|
+
Iodine.run n_io, @ssl, &(@accept_proc)
|
85
87
|
end
|
86
88
|
rescue Errno::EWOULDBLOCK => e
|
87
89
|
|
data/lib/iodine/protocol.rb
CHANGED
@@ -175,6 +175,9 @@ module Iodine
|
|
175
175
|
# Normally you won't need to override this method.
|
176
176
|
def self.accept io, ssl
|
177
177
|
ssl ? SSLConnector.new(io, self) : self.new(io)
|
178
|
+
rescue
|
179
|
+
io.close unless io.closed?
|
180
|
+
raise
|
178
181
|
end
|
179
182
|
# This methos updates the timeout "watch", signifying the IO was active.
|
180
183
|
def touch
|
data/lib/iodine/version.rb
CHANGED