ftw 0.0.1 → 0.0.4
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/README.md +7 -8
- data/lib/ftw.rb +4 -0
- data/lib/ftw/agent.rb +203 -20
- data/lib/ftw/connection.rb +117 -63
- data/lib/ftw/cookies.rb +87 -0
- data/lib/ftw/crlf.rb +1 -1
- data/lib/ftw/dns.rb +14 -5
- data/lib/ftw/http/headers.rb +15 -1
- data/lib/ftw/http/message.rb +9 -1
- data/lib/ftw/namespace.rb +1 -0
- data/lib/ftw/pool.rb +50 -0
- data/lib/ftw/poolable.rb +19 -0
- data/lib/ftw/request.rb +92 -28
- data/lib/ftw/response.rb +179 -0
- data/lib/ftw/version.rb +1 -1
- data/lib/ftw/websocket.rb +194 -0
- data/lib/ftw/websocket/parser.rb +183 -0
- data/test/all.rb +16 -0
- data/test/ftw/crlf.rb +12 -0
- data/test/ftw/http/dns.rb +6 -0
- data/test/{net/ftw → ftw}/http/headers.rb +5 -5
- data/test/testing.rb +0 -9
- metadata +13 -26
- data/lib/net-ftw.rb +0 -1
- data/lib/net/ftw.rb +0 -5
- data/lib/net/ftw/agent.rb +0 -10
- data/lib/net/ftw/connection.rb +0 -296
- data/lib/net/ftw/connection2.rb +0 -247
- data/lib/net/ftw/crlf.rb +0 -6
- data/lib/net/ftw/dns.rb +0 -57
- data/lib/net/ftw/http.rb +0 -2
- data/lib/net/ftw/http/client.rb +0 -116
- data/lib/net/ftw/http/client2.rb +0 -80
- data/lib/net/ftw/http/connection.rb +0 -42
- data/lib/net/ftw/http/headers.rb +0 -122
- data/lib/net/ftw/http/machine.rb +0 -38
- data/lib/net/ftw/http/message.rb +0 -91
- data/lib/net/ftw/http/request.rb +0 -80
- data/lib/net/ftw/http/response.rb +0 -80
- data/lib/net/ftw/http/server.rb +0 -5
- data/lib/net/ftw/machine.rb +0 -59
- data/lib/net/ftw/namespace.rb +0 -6
- data/lib/net/ftw/protocol/tls.rb +0 -12
- data/lib/net/ftw/websocket.rb +0 -139
- data/test/net/ftw/crlf.rb +0 -12
- data/test/net/ftw/http/dns.rb +0 -6
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ftw
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-13 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Trying to build a solid and sane API for client and server web stuff.
|
15
15
|
email:
|
@@ -19,39 +19,26 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- lib/ftw/agent.rb
|
22
|
+
- lib/ftw/pool.rb
|
23
|
+
- lib/ftw/cookies.rb
|
22
24
|
- lib/ftw/http/headers.rb
|
23
25
|
- lib/ftw/http/message.rb
|
26
|
+
- lib/ftw/websocket/parser.rb
|
24
27
|
- lib/ftw/connection.rb
|
25
28
|
- lib/ftw/request.rb
|
26
29
|
- lib/ftw/namespace.rb
|
27
30
|
- lib/ftw/dns.rb
|
31
|
+
- lib/ftw/response.rb
|
32
|
+
- lib/ftw/poolable.rb
|
33
|
+
- lib/ftw/websocket.rb
|
28
34
|
- lib/ftw/crlf.rb
|
29
35
|
- lib/ftw/version.rb
|
30
|
-
- lib/
|
31
|
-
-
|
32
|
-
-
|
33
|
-
-
|
34
|
-
- lib/net/ftw/http/headers.rb
|
35
|
-
- lib/net/ftw/http/machine.rb
|
36
|
-
- lib/net/ftw/http/server.rb
|
37
|
-
- lib/net/ftw/http/client2.rb
|
38
|
-
- lib/net/ftw/http/message.rb
|
39
|
-
- lib/net/ftw/http/connection.rb
|
40
|
-
- lib/net/ftw/http/request.rb
|
41
|
-
- lib/net/ftw/http/response.rb
|
42
|
-
- lib/net/ftw/http/client.rb
|
43
|
-
- lib/net/ftw/connection.rb
|
44
|
-
- lib/net/ftw/namespace.rb
|
45
|
-
- lib/net/ftw/dns.rb
|
46
|
-
- lib/net/ftw/websocket.rb
|
47
|
-
- lib/net/ftw/http.rb
|
48
|
-
- lib/net/ftw/crlf.rb
|
49
|
-
- lib/net/ftw.rb
|
50
|
-
- lib/net-ftw.rb
|
36
|
+
- lib/ftw.rb
|
37
|
+
- test/ftw/http/headers.rb
|
38
|
+
- test/ftw/http/dns.rb
|
39
|
+
- test/ftw/crlf.rb
|
51
40
|
- test/testing.rb
|
52
|
-
- test/
|
53
|
-
- test/net/ftw/http/dns.rb
|
54
|
-
- test/net/ftw/crlf.rb
|
41
|
+
- test/all.rb
|
55
42
|
- README.md
|
56
43
|
homepage: http://github.com/jordansissel/ruby-ftw
|
57
44
|
licenses:
|
data/lib/net-ftw.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require "net/ftw"
|
data/lib/net/ftw.rb
DELETED
data/lib/net/ftw/agent.rb
DELETED
data/lib/net/ftw/connection.rb
DELETED
@@ -1,296 +0,0 @@
|
|
1
|
-
require "cabin" # rubygem "cabin"
|
2
|
-
require "net/ftw/dns"
|
3
|
-
require "net/ftw/namespace"
|
4
|
-
require "socket"
|
5
|
-
require "timeout" # ruby stdlib, just for the Timeout exception.
|
6
|
-
require "backport-bij" # for Array#rotate, IO::WaitWritable, etc, in ruby < 1.9
|
7
|
-
|
8
|
-
# TODO(sissel): What's the API look like here?
|
9
|
-
# EventMachine::Connection has these:
|
10
|
-
# * events: post_init (and connection_completed), receive_data, unbind
|
11
|
-
# * methods: send_data, close
|
12
|
-
# Socket has
|
13
|
-
# * no events
|
14
|
-
# * methods: connect, read, write, close
|
15
|
-
#
|
16
|
-
# Actual events:
|
17
|
-
# * connected
|
18
|
-
# * disconnected(reason)
|
19
|
-
# * timeout, connection reset, connection refused, write error, read
|
20
|
-
# error, etc
|
21
|
-
# * data received
|
22
|
-
#
|
23
|
-
# Methods
|
24
|
-
# * send data
|
25
|
-
# * reconnect
|
26
|
-
# * get socket
|
27
|
-
# * disconnect
|
28
|
-
#
|
29
|
-
|
30
|
-
# A network connection. This is TCP.
|
31
|
-
#
|
32
|
-
# Example:
|
33
|
-
#
|
34
|
-
# conn = Net::FTW::Connection.new("www.google.com:80")
|
35
|
-
# conn.on(CONNECTED) do |address|
|
36
|
-
# puts "Connected to #{address} (#{conn.peer})"
|
37
|
-
# conn.write("GET / HTTP/1.0\r\n\r\n")
|
38
|
-
# end
|
39
|
-
# conn.on(DATA) do |data|
|
40
|
-
# puts data
|
41
|
-
# end
|
42
|
-
# conn.run
|
43
|
-
#
|
44
|
-
# You can use IO::select on this objects of this type.
|
45
|
-
class Net::FTW::Connection
|
46
|
-
|
47
|
-
# Events
|
48
|
-
CONNECTED = :connected
|
49
|
-
DISCONNECTED = :disconnected
|
50
|
-
READER_CLOSED = :reader_closed
|
51
|
-
DATA = :data
|
52
|
-
|
53
|
-
# Disconnection reasons
|
54
|
-
TIMEOUT = :timeout
|
55
|
-
REFUSED = :refused
|
56
|
-
LOST = :lost
|
57
|
-
INTENTIONAL = :intentional
|
58
|
-
|
59
|
-
# A new network connection.
|
60
|
-
# The 'destination' argument can be an array of strings or a single string.
|
61
|
-
# String format is expected to be "host:port"
|
62
|
-
#
|
63
|
-
# Example:
|
64
|
-
#
|
65
|
-
# conn = Net::FTW::Connection.new(["1.2.3.4:80", "1.2.3.5:80"])
|
66
|
-
#
|
67
|
-
# If you specify multiple destinations, they are used in a round-robin
|
68
|
-
# decision made during reconnection.
|
69
|
-
public
|
70
|
-
def initialize(destinations)
|
71
|
-
if destinations.is_a?(String)
|
72
|
-
@destinations = [destinations]
|
73
|
-
else
|
74
|
-
@destinations = destinations
|
75
|
-
end
|
76
|
-
|
77
|
-
# Handlers are key => array of callbacks
|
78
|
-
@handlers = Hash.new { |h,k| h[k] = [] }
|
79
|
-
|
80
|
-
on(CONNECTED) { |address| connected(address) }
|
81
|
-
on(DISCONNECTED) { |reason, error| disconnected(reason, error) }
|
82
|
-
|
83
|
-
@connect_timeout = 2
|
84
|
-
|
85
|
-
# Use a fixed-size string that we set to BINARY encoding.
|
86
|
-
# Not all byte sequences are UTF-8 friendly :0
|
87
|
-
@read_size = 16384
|
88
|
-
@read_buffer = " " * @read_size
|
89
|
-
|
90
|
-
# Tell Ruby 1.9 that this string is a binary string, not utf-8 or somesuch.
|
91
|
-
if @read_buffer.respond_to?(:force_encoding)
|
92
|
-
@read_buffer.force_encoding("BINARY")
|
93
|
-
end
|
94
|
-
|
95
|
-
# TODO(sissel): Validate @destinations
|
96
|
-
end # def initialize
|
97
|
-
|
98
|
-
# Register an event callback
|
99
|
-
# Valid events:
|
100
|
-
#
|
101
|
-
# * Net::FTW::Connection::CONNECTED - 1 argument, the host:port string connected to.
|
102
|
-
# * Net::FTW::Connection::DISCONNECTED - 2 arguments, the reason and the
|
103
|
-
# exception (if any)
|
104
|
-
# * Net::FTW::Connection::DATA - 1 argument to block, the data read
|
105
|
-
#
|
106
|
-
# Disconnection reasons:
|
107
|
-
# * :timeout
|
108
|
-
# * :refused
|
109
|
-
# * :closed
|
110
|
-
# * :lost
|
111
|
-
public
|
112
|
-
def on(event, &block)
|
113
|
-
@handlers[event] << block
|
114
|
-
end # def on
|
115
|
-
|
116
|
-
# Trigger an event with arguments.
|
117
|
-
# All callbacks for the event will be invoked in the order they were
|
118
|
-
# registered. See the 'on' method for registering callbacks.
|
119
|
-
public
|
120
|
-
def trigger(event, *args)
|
121
|
-
@handlers[event].each do |block|
|
122
|
-
block.call(*args)
|
123
|
-
end
|
124
|
-
end # def trigger
|
125
|
-
|
126
|
-
public
|
127
|
-
def connect(timeout=nil)
|
128
|
-
# TODO(sissel): Raise if we're already connected?
|
129
|
-
close if connected?
|
130
|
-
host, port = @destinations.first.split(":")
|
131
|
-
@destinations = @destinations.rotate # round-robin
|
132
|
-
|
133
|
-
# Do dns resolution on the host. If there are multiple
|
134
|
-
# addresses resolved, return one at random.
|
135
|
-
@remote_address = Net::FTW::DNS.singleton.resolve_random(host)
|
136
|
-
|
137
|
-
family = @remote_address.include?(":") ? Socket::AF_INET6 : Socket::AF_INET
|
138
|
-
@socket = Socket.new(family, Socket::SOCK_STREAM, 0)
|
139
|
-
sockaddr = Socket.pack_sockaddr_in(port, @remote_address)
|
140
|
-
# TODO(sissel): Support local address binding
|
141
|
-
|
142
|
-
# Connect with timeout
|
143
|
-
begin
|
144
|
-
@socket.connect_nonblock(sockaddr)
|
145
|
-
rescue IO::WaitWritable
|
146
|
-
# Ruby actually raises Errno::EINPROGRESS, but for some reason
|
147
|
-
# the documentation says to use this IO::WaitWritable thing...
|
148
|
-
# I don't get it, but whatever :(
|
149
|
-
if writable?(timeout)
|
150
|
-
begin
|
151
|
-
@socket.connect_nonblock(sockaddr) # check connection failure
|
152
|
-
rescue Errno::EISCONN # Ignore, we're already connected.
|
153
|
-
rescue Errno::ECONNREFUSED => e
|
154
|
-
# Fire 'disconnected' event with reason :refused
|
155
|
-
trigger(DISCONNECTED, :refused, e)
|
156
|
-
end
|
157
|
-
else
|
158
|
-
# Connection timeout
|
159
|
-
# Fire 'disconnected' event with reason :timeout
|
160
|
-
trigger(DISCONNECTED, :connect_timeout, nil)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# We're now connected.
|
165
|
-
trigger(CONNECTED, "#{host}:#{port}")
|
166
|
-
end # def connect
|
167
|
-
|
168
|
-
# Is this Connection connected?
|
169
|
-
public
|
170
|
-
def connected?
|
171
|
-
return @connected
|
172
|
-
end # def connected?
|
173
|
-
|
174
|
-
# Write data to this connection.
|
175
|
-
# This method blocks until the write succeeds unless a timeout is given.
|
176
|
-
#
|
177
|
-
# Returns the number of bytes written (See IO#syswrite)
|
178
|
-
public
|
179
|
-
def write(data, timeout=nil)
|
180
|
-
#connect if !connected?
|
181
|
-
if writable?(timeout)
|
182
|
-
return @socket.syswrite(data)
|
183
|
-
else
|
184
|
-
raise Timeout::Error.new
|
185
|
-
end
|
186
|
-
end # def write
|
187
|
-
|
188
|
-
# Read data from this connection
|
189
|
-
# This method blocks until the read succeeds unless a timeout is given.
|
190
|
-
#
|
191
|
-
# This method is not guaranteed to read exactly 'length' bytes. See
|
192
|
-
# IO#sysread
|
193
|
-
public
|
194
|
-
def read(length, timeout=nil)
|
195
|
-
if readable?(timeout)
|
196
|
-
begin
|
197
|
-
@socket.sysread(length, @read_buffer)
|
198
|
-
return @read_buffer
|
199
|
-
rescue EOFError
|
200
|
-
trigger(READER_CLOSED)
|
201
|
-
end
|
202
|
-
else
|
203
|
-
raise Timeout::Error.new
|
204
|
-
end
|
205
|
-
end # def read
|
206
|
-
|
207
|
-
# End this connection
|
208
|
-
public
|
209
|
-
def disconnect(reason=INTENTIONAL)
|
210
|
-
begin
|
211
|
-
#@reader_closed = true
|
212
|
-
@socket.close_read
|
213
|
-
rescue IOError => e
|
214
|
-
# Ignore
|
215
|
-
end
|
216
|
-
|
217
|
-
begin
|
218
|
-
@socket.close_write
|
219
|
-
rescue IOError => e
|
220
|
-
# Ignore
|
221
|
-
end
|
222
|
-
|
223
|
-
trigger(DISCONNECTED, reason)
|
224
|
-
end # def disconnect
|
225
|
-
|
226
|
-
# Is this connection writable? Returns true if it is writable within
|
227
|
-
# the timeout period. False otherwise.
|
228
|
-
#
|
229
|
-
# The time out is in seconds. Fractional seconds are OK.
|
230
|
-
public
|
231
|
-
def writable?(timeout)
|
232
|
-
ready = IO.select(nil, [@socket], nil, timeout)
|
233
|
-
return !ready.nil?
|
234
|
-
end # def writable?
|
235
|
-
|
236
|
-
# Is this connection readable? Returns true if it is readable within
|
237
|
-
# the timeout period. False otherwise.
|
238
|
-
#
|
239
|
-
# The time out is in seconds. Fractional seconds are OK.
|
240
|
-
public
|
241
|
-
def readable?(timeout)
|
242
|
-
#return false if @reader_closed
|
243
|
-
ready = IO.select([@socket], nil, nil, timeout)
|
244
|
-
return !ready.nil?
|
245
|
-
end # def readable?
|
246
|
-
|
247
|
-
protected
|
248
|
-
def connected(address)
|
249
|
-
@remote_address = nil
|
250
|
-
@connected = true
|
251
|
-
end # def connected
|
252
|
-
|
253
|
-
protected
|
254
|
-
def disconnected(reason, error)
|
255
|
-
@remote_address = nil
|
256
|
-
@connected = false
|
257
|
-
end # def disconnected
|
258
|
-
|
259
|
-
# The host:port
|
260
|
-
public
|
261
|
-
def peer
|
262
|
-
return @remote_address
|
263
|
-
end # def peer
|
264
|
-
|
265
|
-
# Run this Connection.
|
266
|
-
# This is generally meant for Threaded or synchronous operation.
|
267
|
-
# For EventMachine, see TODO(sissel): Implement EventMachine support.
|
268
|
-
public
|
269
|
-
def run
|
270
|
-
connect(@connect_timeout) if not connected?
|
271
|
-
while connected?
|
272
|
-
read_and_trigger
|
273
|
-
end
|
274
|
-
end # def run
|
275
|
-
|
276
|
-
# Read data and trigger data callbacks.
|
277
|
-
#
|
278
|
-
# This is mainly useful if you are implementing your own run loops
|
279
|
-
# and IO::select shenanigans.
|
280
|
-
public
|
281
|
-
def read_and_trigger
|
282
|
-
data = read(@read_size)
|
283
|
-
if data.length == 0
|
284
|
-
disconnect(EOFError)
|
285
|
-
else
|
286
|
-
trigger(DATA, data)
|
287
|
-
end
|
288
|
-
end # def read_and_trigger
|
289
|
-
|
290
|
-
# Support 'to_io' so you can use IO::select on this object.
|
291
|
-
public
|
292
|
-
def to_io
|
293
|
-
return @socket
|
294
|
-
end
|
295
|
-
end # class Net::FTW::Connection
|
296
|
-
|
data/lib/net/ftw/connection2.rb
DELETED
@@ -1,247 +0,0 @@
|
|
1
|
-
require "cabin" # rubygem "cabin"
|
2
|
-
require "net/ftw/dns"
|
3
|
-
require "net/ftw/namespace"
|
4
|
-
require "socket"
|
5
|
-
require "timeout" # ruby stdlib, just for the Timeout exception.
|
6
|
-
require "backport-bij" # for Array#rotate, IO::WaitWritable, etc, in ruby < 1.9
|
7
|
-
|
8
|
-
# A network connection. This is TCP.
|
9
|
-
#
|
10
|
-
# Example:
|
11
|
-
#
|
12
|
-
# conn = Net::FTW::Connection.new("www.google.com:80")
|
13
|
-
# conn.on(CONNECTED) do |address|
|
14
|
-
# puts "Connected to #{address} (#{conn.peer})"
|
15
|
-
# conn.write("GET / HTTP/1.0\r\n\r\n")
|
16
|
-
# end
|
17
|
-
# conn.on(DATA) do |data|
|
18
|
-
# puts data
|
19
|
-
# end
|
20
|
-
# conn.run
|
21
|
-
#
|
22
|
-
# You can use IO::select on this objects of this type.
|
23
|
-
class Net::FTW::Connection2
|
24
|
-
|
25
|
-
# Events
|
26
|
-
CONNECTED = :connected
|
27
|
-
DISCONNECTED = :disconnected
|
28
|
-
READER_CLOSED = :reader_closed
|
29
|
-
DATA = :data
|
30
|
-
|
31
|
-
# Disconnection reasons
|
32
|
-
TIMEOUT = :timeout
|
33
|
-
REFUSED = :refused
|
34
|
-
LOST = :lost
|
35
|
-
INTENTIONAL = :intentional
|
36
|
-
|
37
|
-
# A new network connection.
|
38
|
-
# The 'destination' argument can be an array of strings or a single string.
|
39
|
-
# String format is expected to be "host:port"
|
40
|
-
#
|
41
|
-
# Example:
|
42
|
-
#
|
43
|
-
# conn = Net::FTW::Connection.new(["1.2.3.4:80", "1.2.3.5:80"])
|
44
|
-
#
|
45
|
-
# If you specify multiple destinations, they are used in a round-robin
|
46
|
-
# decision made during reconnection.
|
47
|
-
public
|
48
|
-
def initialize(destinations)
|
49
|
-
if destinations.is_a?(String)
|
50
|
-
@destinations = [destinations]
|
51
|
-
else
|
52
|
-
@destinations = destinations
|
53
|
-
end
|
54
|
-
|
55
|
-
# Handlers are key => array of callbacks
|
56
|
-
@handlers = Hash.new { |h,k| h[k] = [] }
|
57
|
-
|
58
|
-
@connect_timeout = 2
|
59
|
-
|
60
|
-
# Use a fixed-size string that we set to BINARY encoding.
|
61
|
-
# Not all byte sequences are UTF-8 friendly :0
|
62
|
-
@read_size = 16384
|
63
|
-
@read_buffer = " " * @read_size
|
64
|
-
|
65
|
-
# Tell Ruby 1.9 that this string is a binary string, not utf-8 or somesuch.
|
66
|
-
if @read_buffer.respond_to?(:force_encoding)
|
67
|
-
@read_buffer.force_encoding("BINARY")
|
68
|
-
end
|
69
|
-
|
70
|
-
# TODO(sissel): Validate @destinations
|
71
|
-
end # def initialize
|
72
|
-
|
73
|
-
public
|
74
|
-
def connect(timeout=nil)
|
75
|
-
# TODO(sissel): Raise if we're already connected?
|
76
|
-
close if connected?
|
77
|
-
host, port = @destinations.first.split(":")
|
78
|
-
@destinations = @destinations.rotate # round-robin
|
79
|
-
|
80
|
-
# Do dns resolution on the host. If there are multiple
|
81
|
-
# addresses resolved, return one at random.
|
82
|
-
@remote_address = Net::FTW::DNS.singleton.resolve_random(host)
|
83
|
-
|
84
|
-
family = @remote_address.include?(":") ? Socket::AF_INET6 : Socket::AF_INET
|
85
|
-
@socket = Socket.new(family, Socket::SOCK_STREAM, 0)
|
86
|
-
sockaddr = Socket.pack_sockaddr_in(port, @remote_address)
|
87
|
-
# TODO(sissel): Support local address binding
|
88
|
-
|
89
|
-
# Connect with timeout
|
90
|
-
begin
|
91
|
-
@socket.connect_nonblock(sockaddr)
|
92
|
-
rescue IO::WaitWritable
|
93
|
-
# Ruby actually raises Errno::EINPROGRESS, but for some reason
|
94
|
-
# the documentation says to use this IO::WaitWritable thing...
|
95
|
-
# I don't get it, but whatever :(
|
96
|
-
if writable?(timeout)
|
97
|
-
begin
|
98
|
-
@socket.connect_nonblock(sockaddr) # check connection failure
|
99
|
-
rescue Errno::EISCONN # Ignore, we're already connected.
|
100
|
-
rescue Errno::ECONNREFUSED => e
|
101
|
-
# Fire 'disconnected' event with reason :refused
|
102
|
-
trigger(DISCONNECTED, :refused, e)
|
103
|
-
end
|
104
|
-
else
|
105
|
-
# Connection timeout
|
106
|
-
# Fire 'disconnected' event with reason :timeout
|
107
|
-
trigger(DISCONNECTED, :connect_timeout, nil)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# We're now connected.
|
112
|
-
trigger(CONNECTED, "#{host}:#{port}")
|
113
|
-
end # def connect
|
114
|
-
|
115
|
-
# Is this Connection connected?
|
116
|
-
public
|
117
|
-
def connected?
|
118
|
-
return @connected
|
119
|
-
end # def connected?
|
120
|
-
|
121
|
-
# Write data to this connection.
|
122
|
-
# This method blocks until the write succeeds unless a timeout is given.
|
123
|
-
#
|
124
|
-
# Returns the number of bytes written (See IO#syswrite)
|
125
|
-
public
|
126
|
-
def write(data, timeout=nil)
|
127
|
-
#connect if !connected?
|
128
|
-
if writable?(timeout)
|
129
|
-
return @socket.syswrite(data)
|
130
|
-
else
|
131
|
-
raise Timeout::Error.new
|
132
|
-
end
|
133
|
-
end # def write
|
134
|
-
|
135
|
-
# Read data from this connection
|
136
|
-
# This method blocks until the read succeeds unless a timeout is given.
|
137
|
-
#
|
138
|
-
# This method is not guaranteed to read exactly 'length' bytes. See
|
139
|
-
# IO#sysread
|
140
|
-
public
|
141
|
-
def read(length, timeout=nil)
|
142
|
-
if readable?(timeout)
|
143
|
-
begin
|
144
|
-
@socket.sysread(length, @read_buffer)
|
145
|
-
return @read_buffer
|
146
|
-
rescue EOFError
|
147
|
-
trigger(READER_CLOSED)
|
148
|
-
end
|
149
|
-
else
|
150
|
-
raise Timeout::Error.new
|
151
|
-
end
|
152
|
-
end # def read
|
153
|
-
|
154
|
-
# End this connection
|
155
|
-
public
|
156
|
-
def disconnect(reason=INTENTIONAL)
|
157
|
-
begin
|
158
|
-
#@reader_closed = true
|
159
|
-
@socket.close_read
|
160
|
-
rescue IOError => e
|
161
|
-
# Ignore
|
162
|
-
end
|
163
|
-
|
164
|
-
begin
|
165
|
-
@socket.close_write
|
166
|
-
rescue IOError => e
|
167
|
-
# Ignore
|
168
|
-
end
|
169
|
-
|
170
|
-
trigger(DISCONNECTED, reason)
|
171
|
-
end # def disconnect
|
172
|
-
|
173
|
-
# Is this connection writable? Returns true if it is writable within
|
174
|
-
# the timeout period. False otherwise.
|
175
|
-
#
|
176
|
-
# The time out is in seconds. Fractional seconds are OK.
|
177
|
-
public
|
178
|
-
def writable?(timeout)
|
179
|
-
ready = IO.select(nil, [@socket], nil, timeout)
|
180
|
-
return !ready.nil?
|
181
|
-
end # def writable?
|
182
|
-
|
183
|
-
# Is this connection readable? Returns true if it is readable within
|
184
|
-
# the timeout period. False otherwise.
|
185
|
-
#
|
186
|
-
# The time out is in seconds. Fractional seconds are OK.
|
187
|
-
public
|
188
|
-
def readable?(timeout)
|
189
|
-
#return false if @reader_closed
|
190
|
-
ready = IO.select([@socket], nil, nil, timeout)
|
191
|
-
return !ready.nil?
|
192
|
-
end # def readable?
|
193
|
-
|
194
|
-
protected
|
195
|
-
def connected(address)
|
196
|
-
@remote_address = nil
|
197
|
-
@connected = true
|
198
|
-
end # def connected
|
199
|
-
|
200
|
-
protected
|
201
|
-
def disconnected(reason, error)
|
202
|
-
@remote_address = nil
|
203
|
-
@connected = false
|
204
|
-
end # def disconnected
|
205
|
-
|
206
|
-
# The host:port
|
207
|
-
public
|
208
|
-
def peer
|
209
|
-
return @remote_address
|
210
|
-
end # def peer
|
211
|
-
|
212
|
-
# Run this Connection.
|
213
|
-
# This is generally meant for Threaded or synchronous operation.
|
214
|
-
# For EventMachine, see TODO(sissel): Implement EventMachine support.
|
215
|
-
public
|
216
|
-
def run
|
217
|
-
connect(@connect_timeout) if not connected?
|
218
|
-
while connected?
|
219
|
-
read_and_trigger
|
220
|
-
end
|
221
|
-
end # def run
|
222
|
-
|
223
|
-
# Read data and trigger data callbacks.
|
224
|
-
#
|
225
|
-
# This is mainly useful if you are implementing your own run loops
|
226
|
-
# and IO::select shenanigans.
|
227
|
-
public
|
228
|
-
def read_and_trigger
|
229
|
-
data = read(@read_size)
|
230
|
-
if data.length == 0
|
231
|
-
disconnect(EOFError)
|
232
|
-
else
|
233
|
-
trigger(DATA, data)
|
234
|
-
end
|
235
|
-
end # def read_and_trigger
|
236
|
-
|
237
|
-
# Support 'to_io' so you can use IO::select on this object.
|
238
|
-
public
|
239
|
-
def to_io
|
240
|
-
return @socket
|
241
|
-
end
|
242
|
-
|
243
|
-
def trigger(*args)
|
244
|
-
p :trigger => args
|
245
|
-
end
|
246
|
-
end # class Net::FTW::Connection
|
247
|
-
|