rev 0.1.4 → 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.
- data/CHANGES +102 -0
- data/README +5 -5
- data/Rakefile +70 -0
- data/examples/echo_server.rb +24 -0
- data/ext/http11_client/http11_parser.rl +173 -0
- data/ext/libev/Changes +40 -0
- data/ext/libev/LICENSE +25 -0
- data/ext/libev/README +130 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/update_ev_wrap +19 -0
- data/ext/rev/extconf.rb +24 -11
- data/ext/rev/rev.h +7 -0
- data/ext/rev/rev_buffer.c +17 -8
- data/ext/rev/rev_ext.c +5 -4
- data/ext/rev/rev_io_watcher.c +7 -6
- data/ext/rev/rev_loop.c +48 -6
- data/ext/rev/rev_ssl.c +255 -0
- data/ext/rev/rev_timer_watcher.c +3 -6
- data/ext/rev/rev_utils.c +108 -0
- data/ext/rev/rev_watcher.c +0 -3
- data/lib/rev.rb +2 -1
- data/lib/rev/async_watcher.rb +38 -0
- data/lib/rev/dns_resolver.rb +6 -8
- data/lib/rev/http_client.rb +35 -32
- data/lib/rev/io.rb +47 -43
- data/lib/rev/io_watcher.rb +0 -2
- data/lib/rev/listener.rb +0 -1
- data/lib/rev/loop.rb +11 -4
- data/lib/rev/server.rb +2 -4
- data/lib/rev/socket.rb +1 -2
- data/lib/rev/ssl.rb +184 -0
- data/lib/rev/timer_watcher.rb +0 -2
- data/lib/rev/watcher.rb +0 -2
- data/rev.gemspec +27 -0
- metadata +69 -61
- data/lib/http11_client.bundle +0 -0
- data/lib/rev_ext.bundle +0 -0
- data/spec/rev_spec.rb +0 -26
data/lib/rev/dns_resolver.rb
CHANGED
@@ -2,11 +2,7 @@
|
|
2
2
|
# Copyright (C)2007 Tony Arcieri
|
3
3
|
# You can redistribute this under the terms of the Ruby license
|
4
4
|
# See file LICENSE for details
|
5
|
-
|
6
|
-
|
7
|
-
require File.dirname(__FILE__) + '/../rev'
|
8
|
-
|
9
|
-
#--
|
5
|
+
#
|
10
6
|
# Gimpy hacka asynchronous DNS resolver
|
11
7
|
#
|
12
8
|
# Word to the wise: I don't know what I'm doing here. This was cobbled together as
|
@@ -110,6 +106,8 @@ module Rev
|
|
110
106
|
end
|
111
107
|
|
112
108
|
def request_question(hostname)
|
109
|
+
raise ArgumentError, "hostname cannot be nil" if hostname.nil?
|
110
|
+
|
113
111
|
# Query name
|
114
112
|
message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\0"
|
115
113
|
|
@@ -142,15 +140,15 @@ module Rev
|
|
142
140
|
return unless id == 2
|
143
141
|
|
144
142
|
# Check the QR value and confirm this message is a response
|
145
|
-
qr = message[2].unpack('B1').first.to_i
|
143
|
+
qr = message[2..2].unpack('B1').first.to_i
|
146
144
|
return unless qr == 1
|
147
145
|
|
148
146
|
# Check the RCODE (lower nibble) and ensure there wasn't an error
|
149
|
-
rcode = message[3].unpack('B8').first[4..7].to_i(2)
|
147
|
+
rcode = message[3..3].unpack('B8').first[4..7].to_i(2)
|
150
148
|
return unless rcode == 0
|
151
149
|
|
152
150
|
# Extract the question and answer counts
|
153
|
-
qdcount, ancount = message[4..7].unpack('nn').map
|
151
|
+
qdcount, ancount = message[4..7].unpack('nn').map { |n| n.to_i }
|
154
152
|
|
155
153
|
# We only asked one question
|
156
154
|
return unless qdcount == 1
|
data/lib/rev/http_client.rb
CHANGED
@@ -5,7 +5,6 @@
|
|
5
5
|
# See file LICENSE for details
|
6
6
|
#++
|
7
7
|
|
8
|
-
require File.dirname(__FILE__) + '/../rev'
|
9
8
|
require File.dirname(__FILE__) + '/../http11_client'
|
10
9
|
|
11
10
|
module Rev
|
@@ -79,13 +78,13 @@ module Rev
|
|
79
78
|
remote_host + (remote_port.to_i != 80 ? ":#{remote_port}" : "")
|
80
79
|
end
|
81
80
|
|
82
|
-
def encode_request(method,
|
83
|
-
HTTP_REQUEST_HEADER % [method.to_s.upcase, encode_query(
|
81
|
+
def encode_request(method, path, query)
|
82
|
+
HTTP_REQUEST_HEADER % [method.to_s.upcase, encode_query(path, query)]
|
84
83
|
end
|
85
84
|
|
86
|
-
def encode_query(
|
87
|
-
return
|
88
|
-
|
85
|
+
def encode_query(path, query)
|
86
|
+
return path unless query
|
87
|
+
path + "?" + query.map { |k, v| encode_param(k, v) }.join('&')
|
89
88
|
end
|
90
89
|
|
91
90
|
# URL encodes a single k=v parameter.
|
@@ -167,27 +166,21 @@ module Rev
|
|
167
166
|
# body: String
|
168
167
|
# Specify the request body (you must encode it for now)
|
169
168
|
#
|
170
|
-
def request(method,
|
169
|
+
def request(method, path, options = {})
|
170
|
+
raise ArgumentError, "invalid request path" unless path[0] == '/'
|
171
171
|
raise RuntimeError, "request already sent" if @requested
|
172
172
|
|
173
|
-
@method, @
|
173
|
+
@method, @path, @options = method, path, options
|
174
174
|
@requested = true
|
175
175
|
|
176
176
|
return unless @connected
|
177
177
|
send_request
|
178
178
|
end
|
179
|
-
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
# Valid for: get, post, put, delete, head
|
185
|
-
#
|
186
|
-
# To use other HTTP methods, invoke the request method directly
|
187
|
-
#
|
188
|
-
def method_missing(method, *args)
|
189
|
-
raise NoMethodError, "method not supported" unless ALLOWED_METHODS.include? method.to_sym
|
190
|
-
request method, *args
|
179
|
+
|
180
|
+
# Enable the HttpClient if it has been disabled
|
181
|
+
def enable
|
182
|
+
super
|
183
|
+
dispatch unless @data.empty?
|
191
184
|
end
|
192
185
|
|
193
186
|
# Called when response header has been received
|
@@ -205,8 +198,9 @@ module Rev
|
|
205
198
|
close
|
206
199
|
end
|
207
200
|
|
208
|
-
# Called when an error occurs
|
201
|
+
# Called when an error occurs dispatching the request
|
209
202
|
def on_error(reason)
|
203
|
+
close
|
210
204
|
raise RuntimeError, reason
|
211
205
|
end
|
212
206
|
|
@@ -217,10 +211,10 @@ module Rev
|
|
217
211
|
#
|
218
212
|
# Rev callbacks
|
219
213
|
#
|
220
|
-
|
214
|
+
|
221
215
|
def on_connect
|
222
216
|
@connected = true
|
223
|
-
send_request if @method and @
|
217
|
+
send_request if @method and @path
|
224
218
|
end
|
225
219
|
|
226
220
|
def on_read(data)
|
@@ -256,7 +250,7 @@ module Rev
|
|
256
250
|
head['connection'] ||= 'close'
|
257
251
|
|
258
252
|
# Build the request
|
259
|
-
request_header = encode_request(@method, @
|
253
|
+
request_header = encode_request(@method, @path, query)
|
260
254
|
request_header << encode_headers(head)
|
261
255
|
request_header << encode_cookies(cookies) if cookies
|
262
256
|
request_header << CRLF
|
@@ -273,7 +267,7 @@ module Rev
|
|
273
267
|
#
|
274
268
|
|
275
269
|
def dispatch
|
276
|
-
while case @state
|
270
|
+
while enabled? and case @state
|
277
271
|
when :response_header
|
278
272
|
parse_response_header
|
279
273
|
when :chunk_header
|
@@ -296,7 +290,13 @@ module Rev
|
|
296
290
|
def parse_header(header)
|
297
291
|
return false if @data.empty?
|
298
292
|
|
299
|
-
|
293
|
+
begin
|
294
|
+
@parser_nbytes = @parser.execute(header, @data.to_str, @parser_nbytes)
|
295
|
+
rescue Rev::HttpClientParserError
|
296
|
+
on_error "invalid HTTP format, parsing fails"
|
297
|
+
@state = :invalid
|
298
|
+
end
|
299
|
+
|
300
300
|
return false unless @parser.finished?
|
301
301
|
|
302
302
|
# Clear parsed data from the buffer
|
@@ -385,12 +385,15 @@ module Rev
|
|
385
385
|
end
|
386
386
|
|
387
387
|
def process_body
|
388
|
-
# FIXME the proper thing to do here is probably to keep reading until
|
389
|
-
# the socket closes, then assume that's the end of the body, provided
|
390
|
-
# the server has specified Connection: close
|
391
388
|
if @bytes_remaining.nil?
|
392
|
-
|
393
|
-
|
389
|
+
on_body_data @data.read
|
390
|
+
return false
|
391
|
+
end
|
392
|
+
|
393
|
+
if @bytes_remaining.zero?
|
394
|
+
on_request_complete
|
395
|
+
@state = :finished
|
396
|
+
return false
|
394
397
|
end
|
395
398
|
|
396
399
|
if @data.size < @bytes_remaining
|
@@ -413,4 +416,4 @@ module Rev
|
|
413
416
|
false
|
414
417
|
end
|
415
418
|
end
|
416
|
-
end
|
419
|
+
end
|
data/lib/rev/io.rb
CHANGED
@@ -4,12 +4,10 @@
|
|
4
4
|
# See file LICENSE for details
|
5
5
|
#++
|
6
6
|
|
7
|
-
require File.dirname(__FILE__) + '/../rev'
|
8
|
-
|
9
7
|
module Rev
|
10
8
|
# A buffered I/O class witch fits into the Rev Watcher framework.
|
11
9
|
# It provides both an observer which reads data as it's received
|
12
|
-
# from the wire and a buffered
|
10
|
+
# from the wire and a buffered write watcher which stores data and writes
|
13
11
|
# it out each time the socket becomes writable.
|
14
12
|
#
|
15
13
|
# This class is primarily meant as a base class for other streams
|
@@ -20,11 +18,8 @@ module Rev
|
|
20
18
|
INPUT_SIZE = 16384
|
21
19
|
|
22
20
|
def initialize(io)
|
23
|
-
|
21
|
+
@io = io
|
24
22
|
@write_buffer = Rev::Buffer.new
|
25
|
-
|
26
|
-
# Coerce the argument into an IO object if possible
|
27
|
-
@io = ::IO.try_convert(io)
|
28
23
|
super(@io)
|
29
24
|
end
|
30
25
|
|
@@ -50,7 +45,9 @@ module Rev
|
|
50
45
|
|
51
46
|
# Write data in a buffered, non-blocking manner
|
52
47
|
def write(data)
|
53
|
-
|
48
|
+
@write_buffer << data
|
49
|
+
schedule_write
|
50
|
+
data.size
|
54
51
|
end
|
55
52
|
|
56
53
|
# Number of bytes are currently in the output buffer
|
@@ -61,7 +58,7 @@ module Rev
|
|
61
58
|
# Close the IO stream
|
62
59
|
def close
|
63
60
|
detach if attached?
|
64
|
-
|
61
|
+
detach_write_watcher
|
65
62
|
@io.close unless @io.closed?
|
66
63
|
|
67
64
|
on_close
|
@@ -76,62 +73,69 @@ module Rev
|
|
76
73
|
#########
|
77
74
|
protected
|
78
75
|
#########
|
79
|
-
|
80
|
-
# Buffered writer
|
81
|
-
def buffered_write(data)
|
82
|
-
@write_buffer << data
|
83
|
-
schedule_write
|
84
|
-
data.size
|
85
|
-
end
|
86
76
|
|
87
|
-
#
|
88
|
-
def
|
77
|
+
# Read from the input buffer and dispatch to on_read
|
78
|
+
def on_readable
|
79
|
+
begin
|
80
|
+
on_read @io.read_nonblock(INPUT_SIZE)
|
81
|
+
rescue Errno::ECONNRESET, EOFError
|
82
|
+
close
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Write the contents of the output buffer
|
87
|
+
def on_writable
|
89
88
|
begin
|
90
89
|
@write_buffer.write_to(@io)
|
91
|
-
rescue Errno::EPIPE
|
90
|
+
rescue Errno::EPIPE, Errno::ECONNRESET
|
92
91
|
return close
|
93
92
|
end
|
94
93
|
|
95
94
|
if @write_buffer.empty?
|
96
|
-
|
95
|
+
disable_write_watcher
|
97
96
|
on_write_complete
|
98
97
|
end
|
99
98
|
end
|
100
99
|
|
101
|
-
#
|
102
|
-
def
|
100
|
+
# Schedule a write to be performed when the IO object becomes writable
|
101
|
+
def schedule_write
|
103
102
|
begin
|
104
|
-
|
105
|
-
rescue
|
106
|
-
close
|
103
|
+
enable_write_watcher
|
104
|
+
rescue IOError
|
107
105
|
end
|
108
106
|
end
|
109
107
|
|
110
|
-
#
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
# Return a handle to the writing IOWatcher
|
109
|
+
def write_watcher
|
110
|
+
@write_watcher ||= WriteWatcher.new(@io, self)
|
111
|
+
end
|
112
|
+
|
113
|
+
def enable_write_watcher
|
114
|
+
if write_watcher.attached?
|
115
|
+
write_watcher.enable unless write_watcher.enabled?
|
115
116
|
else
|
116
|
-
|
117
|
-
@writer = Writer.new(@io, self)
|
118
|
-
rescue IOError
|
119
|
-
return
|
120
|
-
end
|
121
|
-
|
122
|
-
@writer.attach(evloop)
|
117
|
+
write_watcher.attach(evloop)
|
123
118
|
end
|
124
119
|
end
|
120
|
+
|
121
|
+
def disable_write_watcher
|
122
|
+
@write_watcher.disable if @write_watcher and @write_watcher.enabled?
|
123
|
+
end
|
124
|
+
|
125
|
+
def detach_write_watcher
|
126
|
+
@write_watcher.detach if @write_watcher and @write_watcher.attached?
|
127
|
+
end
|
125
128
|
|
126
|
-
class
|
127
|
-
def initialize(
|
128
|
-
@
|
129
|
-
super(
|
129
|
+
class WriteWatcher < IOWatcher
|
130
|
+
def initialize(ruby_io, rev_io)
|
131
|
+
@rev_io = rev_io
|
132
|
+
super(ruby_io, :w)
|
130
133
|
end
|
131
134
|
|
135
|
+
# Delegate on_writable to the Rev::IO object
|
132
136
|
def on_writable
|
133
|
-
@
|
137
|
+
@rev_io.__send__(:on_writable)
|
134
138
|
end
|
135
139
|
end
|
136
140
|
end
|
137
|
-
end
|
141
|
+
end
|
data/lib/rev/io_watcher.rb
CHANGED
data/lib/rev/listener.rb
CHANGED
data/lib/rev/loop.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# See file LICENSE for details
|
5
5
|
#++
|
6
6
|
|
7
|
-
require File.dirname(__FILE__) + '/../rev'
|
8
7
|
require 'thread'
|
9
8
|
|
10
9
|
# Monkeypatch Thread to include a method for obtaining the default Rev::Loop
|
@@ -18,9 +17,17 @@ module Rev
|
|
18
17
|
class Loop
|
19
18
|
attr_reader :watchers
|
20
19
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
20
|
+
# In Ruby 1.9 we want a Rev::Loop per thread, but Ruby 1.8 is unithreaded
|
21
|
+
if RUBY_VERSION.gsub('.', '').to_i >= 190
|
22
|
+
# Retrieve the default event loop for the current thread
|
23
|
+
def self.default
|
24
|
+
Thread.current._rev_loop
|
25
|
+
end
|
26
|
+
else
|
27
|
+
# Retrieve the default event loop
|
28
|
+
def self.default
|
29
|
+
@@_rev_loop ||= Rev::Loop.new
|
30
|
+
end
|
24
31
|
end
|
25
32
|
|
26
33
|
# Create a new Rev::Loop
|
data/lib/rev/server.rb
CHANGED
@@ -4,8 +4,6 @@
|
|
4
4
|
# See file LICENSE for details
|
5
5
|
#++
|
6
6
|
|
7
|
-
require File.dirname(__FILE__) + '/../rev'
|
8
|
-
|
9
7
|
module Rev
|
10
8
|
class Server < Listener
|
11
9
|
# Servers listen for incoming connections and create new connection objects
|
@@ -35,8 +33,8 @@ module Rev
|
|
35
33
|
|
36
34
|
def on_connection(socket)
|
37
35
|
connection = @klass.new(socket, *@args).attach(evloop)
|
38
|
-
|
39
|
-
connection
|
36
|
+
connection.__send__(:on_connect)
|
37
|
+
@block.call(connection) if @block
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
data/lib/rev/socket.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
|
7
7
|
require 'socket'
|
8
8
|
require 'resolv'
|
9
|
-
require File.dirname(__FILE__) + '/../rev'
|
10
9
|
|
11
10
|
module Rev
|
12
11
|
class Socket < IO
|
@@ -173,7 +172,7 @@ module Rev
|
|
173
172
|
# DNSResolver only supports IPv4 so we can safely assume an IPv4 address
|
174
173
|
socket = TCPConnectSocket.new(::Socket::AF_INET, addr, port, host)
|
175
174
|
initialize(socket, *args)
|
176
|
-
@connector = Connector.new(self, socket)
|
175
|
+
@connector = Socket::Connector.new(self, socket)
|
177
176
|
@resolver = nil
|
178
177
|
}
|
179
178
|
@sock.attach(evloop)
|
data/lib/rev/ssl.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C)2007 Tony Arcieri
|
3
|
+
# You can redistribute this under the terms of the Ruby license
|
4
|
+
# See file LICENSE for details
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'openssl'
|
8
|
+
|
9
|
+
# --
|
10
|
+
# Rev implements SSL by subclassing OpenSSL::SSL::SSLSocket in C
|
11
|
+
# and adding implementations for non-blocking versions of the
|
12
|
+
# methods it provides. Unfortunately, this relies on hacks specific
|
13
|
+
# to Ruby 1.9. If you'd like to find a workaround which is compatible
|
14
|
+
# with Ruby 1.8, have a look at rev_ssl.c and find out if you can
|
15
|
+
# properly initialize an OpenSSL::SSL::SSLSocket.
|
16
|
+
# ++
|
17
|
+
if RUBY_VERSION.gsub('.', '').to_i < 190
|
18
|
+
raise LoadError, "Rev::SSL not supported in this Ruby version, sorry"
|
19
|
+
end
|
20
|
+
|
21
|
+
module Rev
|
22
|
+
# Monkeypatch Rev::IO to include SSL support. This can be accomplished
|
23
|
+
# by extending any Rev:IO (or subclass) object with Rev::SSL after the
|
24
|
+
# connection has completed, e.g.
|
25
|
+
#
|
26
|
+
# class MySocket < Rev::TCPSocket
|
27
|
+
# def on_connect
|
28
|
+
# extend Rev::SSL
|
29
|
+
# ssl_client_start
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Please be aware that SSL is only supported on Ruby 1.9 at the present time.
|
34
|
+
#
|
35
|
+
module SSL
|
36
|
+
# Start SSL explicitly in client mode. After calling this, callbacks
|
37
|
+
# will fire for checking the peer certificate (ssl_peer_cert) and
|
38
|
+
# its validity (ssl_verify_result)
|
39
|
+
def ssl_client_start
|
40
|
+
raise "ssl already started" if @ssl_socket
|
41
|
+
|
42
|
+
context = respond_to?(:ssl_context) ? ssl_context : OpenSSL::SSL::SSLContext.new
|
43
|
+
@ssl_socket = SSL::IO.new(@io, context)
|
44
|
+
@ssl_init = proc { @ssl_socket.connect_nonblock }
|
45
|
+
|
46
|
+
ssl_init
|
47
|
+
end
|
48
|
+
|
49
|
+
# Start SSL explicitly in server mode. After calling this, callbacks
|
50
|
+
# will fire for checking the peer certificate (ssl_peer_cert) and
|
51
|
+
# its validity (ssl_verify_result)
|
52
|
+
def ssl_server_start
|
53
|
+
raise "ssl already started" if @ssl_socket
|
54
|
+
|
55
|
+
@ssl_socket = SSL::IO.new(@io, ssl_context)
|
56
|
+
@ssl_init = proc { @ssl_socket.accept_nonblock }
|
57
|
+
|
58
|
+
ssl_init
|
59
|
+
end
|
60
|
+
|
61
|
+
#########
|
62
|
+
protected
|
63
|
+
#########
|
64
|
+
|
65
|
+
def ssl_init
|
66
|
+
begin
|
67
|
+
@ssl_init.call
|
68
|
+
ssl_init_complete
|
69
|
+
rescue SSL::IO::ReadAgain
|
70
|
+
enable unless enabled?
|
71
|
+
rescue SSL::IO::WriteAgain
|
72
|
+
enable_write_watcher
|
73
|
+
rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
|
74
|
+
close
|
75
|
+
rescue => ex
|
76
|
+
if respond_to? :on_ssl_error
|
77
|
+
on_ssl_error(ex)
|
78
|
+
else raise ex
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def ssl_init_complete
|
84
|
+
@ssl_init = nil
|
85
|
+
enable unless enabled?
|
86
|
+
|
87
|
+
on_peer_cert(@ssl_socket.peer_cert) if respond_to? :on_peer_cert
|
88
|
+
on_ssl_result(@ssl_socket.verify_result) if respond_to? :on_ssl_result
|
89
|
+
on_ssl_connect if respond_to? :on_ssl_connect
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_readable
|
93
|
+
if @ssl_init
|
94
|
+
disable
|
95
|
+
ssl_init
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
begin
|
100
|
+
on_read @ssl_socket.read_nonblock(Rev::IO::INPUT_SIZE)
|
101
|
+
rescue Errno::AGAIN, SSL::IO::ReadAgain
|
102
|
+
return
|
103
|
+
rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET, EOFError
|
104
|
+
close
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def on_writable
|
109
|
+
if @ssl_init
|
110
|
+
disable_write_watcher
|
111
|
+
ssl_init
|
112
|
+
return
|
113
|
+
end
|
114
|
+
|
115
|
+
begin
|
116
|
+
nbytes = @ssl_socket.write_nonblock @write_buffer.to_str
|
117
|
+
rescue Errno::EAGAIN, SSL::IO::WriteAgain
|
118
|
+
return
|
119
|
+
rescue OpenSSL::SSL::SSLError, Errno::EPIPE, Errno::ECONNRESET
|
120
|
+
close
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
@write_buffer.read(nbytes)
|
125
|
+
|
126
|
+
if @write_buffer.empty?
|
127
|
+
disable_write_watcher
|
128
|
+
on_write_complete
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# A socket class for outgoing and incoming SSL connections. Please be aware
|
134
|
+
# that SSL is only supported on Ruby 1.9 at the present time.
|
135
|
+
class SSLSocket < TCPSocket
|
136
|
+
# Perform a non-blocking connect to the given host and port
|
137
|
+
def self.connect(addr, port, *args)
|
138
|
+
super.instance_eval { @connecting = true; self }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns the OpenSSL::SSL::SSLContext for to use for the session.
|
142
|
+
# By default no certificates will be checked. If you would like
|
143
|
+
# any certificate checking to be performed, please override this
|
144
|
+
# class and return a context loaded with the appropriate certificates.
|
145
|
+
def ssl_context
|
146
|
+
@ssl_context ||= OpenSSL::SSL::SSLContext.new
|
147
|
+
end
|
148
|
+
|
149
|
+
# Called when SSL handshaking has successfully completed
|
150
|
+
def on_ssl_connect; end
|
151
|
+
event_callback :on_ssl_connect
|
152
|
+
|
153
|
+
# Called when peer certificate has successfully been received.
|
154
|
+
# Equivalent to OpenSSL::SSL::SSLSocket#peer_cert
|
155
|
+
def on_peer_cert(peer_cert); end
|
156
|
+
event_callback :on_peer_cert
|
157
|
+
|
158
|
+
# Called when SSL handshaking has been completed successfully.
|
159
|
+
# Equivalent to OpenSSL::SSL::SSLSocket#verify_result
|
160
|
+
def on_ssl_result(result); end
|
161
|
+
event_callback :on_ssl_result
|
162
|
+
|
163
|
+
# Called if an error occurs during SSL session initialization
|
164
|
+
def on_ssl_error(exception); end
|
165
|
+
event_callback :on_ssl_error
|
166
|
+
|
167
|
+
#########
|
168
|
+
protected
|
169
|
+
#########
|
170
|
+
|
171
|
+
def on_connect
|
172
|
+
extend SSL
|
173
|
+
@connecting ? ssl_client_start : ssl_server_start
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# A class for implementing SSL servers. Please be aware that SSL is only
|
178
|
+
# supported on Ruby 1.9 at the present time.
|
179
|
+
class SSLServer < TCPServer
|
180
|
+
def initialize(host, port, klass = SSLSocket, *args, &block)
|
181
|
+
super
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|