packet 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +4 -0
- data/README +251 -0
- data/Rakefile +87 -0
- data/TODO +0 -0
- data/bin/packet_mongrel.rb +215 -0
- data/bin/runner.rb +35 -0
- data/lib/attribute_accessors.rb +48 -0
- data/lib/bin_parser.rb +61 -0
- data/lib/buftok.rb +127 -0
- data/lib/callback.rb +14 -0
- data/lib/connection.rb +33 -0
- data/lib/core.rb +241 -0
- data/lib/cpu_worker.rb +19 -0
- data/lib/deferrable.rb +210 -0
- data/lib/disconnect_error.rb +8 -0
- data/lib/double_keyed_hash.rb +19 -0
- data/lib/event.rb +25 -0
- data/lib/io_worker.rb +6 -0
- data/lib/meta_pimp.rb +66 -0
- data/lib/nbio.rb +81 -0
- data/lib/packet.rb +37 -0
- data/lib/packet_guid.rb +16 -0
- data/lib/packet_master.rb +148 -0
- data/lib/packet_mongrel.rb +214 -0
- data/lib/periodic_event.rb +27 -0
- data/lib/pimp.rb +31 -0
- data/lib/ruby_hacks.rb +125 -0
- data/lib/worker.rb +120 -0
- metadata +75 -0
data/bin/runner.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
EVAL_APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__) + "/.."))
|
2
|
+
["bin","worker","lib"].each { |x| $LOAD_PATH.unshift(EVAL_APP_ROOT + "/#{x}")}
|
3
|
+
WORKER_ROOT = EVAL_APP_ROOT + "/worker"
|
4
|
+
|
5
|
+
require "packet"
|
6
|
+
class Foo
|
7
|
+
def receive_data p_data
|
8
|
+
ask_worker(:no_proxy_worker,:data => p_data, :type => :request)
|
9
|
+
end
|
10
|
+
|
11
|
+
def worker_receive p_data
|
12
|
+
send_data "#{p_data[:data]}\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
def show_result p_data
|
16
|
+
send_data("#{p_data[:response]}\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection_completed
|
20
|
+
end
|
21
|
+
|
22
|
+
def post_init
|
23
|
+
end
|
24
|
+
|
25
|
+
def wow
|
26
|
+
puts "Wow"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Packet::Reactor.run do |t_reactor|
|
31
|
+
t_reactor.start_server("localhost", 11006,Foo) do |instance|
|
32
|
+
instance.wow
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Extends the class object with class and instance accessors for class attributes,
|
2
|
+
# just like the native attr* accessors for instance attributes.
|
3
|
+
class Class # :nodoc:
|
4
|
+
def cattr_reader(*syms)
|
5
|
+
syms.flatten.each do |sym|
|
6
|
+
next if sym.is_a?(Hash)
|
7
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
8
|
+
unless defined? @@#{sym}
|
9
|
+
@@#{sym} = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.#{sym}
|
13
|
+
@@#{sym}
|
14
|
+
end
|
15
|
+
|
16
|
+
def #{sym}
|
17
|
+
@@#{sym}
|
18
|
+
end
|
19
|
+
EOS
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def cattr_writer(*syms)
|
24
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
25
|
+
syms.flatten.each do |sym|
|
26
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
27
|
+
unless defined? @@#{sym}
|
28
|
+
@@#{sym} = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.#{sym}=(obj)
|
32
|
+
@@#{sym} = obj
|
33
|
+
end
|
34
|
+
|
35
|
+
#{"
|
36
|
+
def #{sym}=(obj)
|
37
|
+
@@#{sym} = obj
|
38
|
+
end
|
39
|
+
" unless options[:instance_writer] == false }
|
40
|
+
EOS
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def cattr_accessor(*syms)
|
45
|
+
cattr_reader(*syms)
|
46
|
+
cattr_writer(*syms)
|
47
|
+
end
|
48
|
+
end
|
data/lib/bin_parser.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# FIXME: write this stupid parser in C.
|
2
|
+
class BinParser
|
3
|
+
def initialize
|
4
|
+
@size = 0
|
5
|
+
@data = []
|
6
|
+
# 0 => reading length
|
7
|
+
# 1 => reading actual data
|
8
|
+
@parser_state = 0
|
9
|
+
@length_string = ""
|
10
|
+
@numeric_length = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
# if you look at it, it could be a suicidal function
|
14
|
+
def extract new_data
|
15
|
+
if @parser_state == 0
|
16
|
+
length_to_read = 9 - @length_string.length
|
17
|
+
len_str,remaining = new_data.unpack("a#{length_to_read}a*")
|
18
|
+
if len_str.length < length_to_read
|
19
|
+
@length_string << len_str
|
20
|
+
return
|
21
|
+
else
|
22
|
+
@length_string << len_str
|
23
|
+
@numeric_length = @length_string.to_i
|
24
|
+
@parser_state = 1
|
25
|
+
if remaining.length < @numeric_length
|
26
|
+
@data << remaining
|
27
|
+
@numeric_length = @numeric_length - remaining.length
|
28
|
+
else
|
29
|
+
@data << remaining
|
30
|
+
yield(@data.join)
|
31
|
+
@data = []
|
32
|
+
@parser_state = 0
|
33
|
+
@length_string = ""
|
34
|
+
@numeric_length = 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
elsif @parser_state == 1
|
38
|
+
pack_data,remaining = new_data.unpack("a#{@numeric_length}a*")
|
39
|
+
if pack_data.length < @numeric_length
|
40
|
+
@data << pack_data
|
41
|
+
@numeric_length = @numeric_length - pack_data.length
|
42
|
+
elsif pack_data.length == @numeric_length
|
43
|
+
@data << pack_data
|
44
|
+
yield(@data.join)
|
45
|
+
@data = []
|
46
|
+
@parser_state = 0
|
47
|
+
@length_string = ""
|
48
|
+
@numeric_length = 0
|
49
|
+
else
|
50
|
+
@data << pack_data
|
51
|
+
yield(@data.join)
|
52
|
+
@data = []
|
53
|
+
@parser_state = 0
|
54
|
+
@length_string = ""
|
55
|
+
@numeric_length = 0
|
56
|
+
extract(remaining)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
data/lib/buftok.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# BufferedTokenizer - Statefully split input data by a specifiable token
|
2
|
+
#
|
3
|
+
# Authors:: Tony Arcieri, Martin Emde
|
4
|
+
#
|
5
|
+
#----------------------------------------------------------------------------
|
6
|
+
#
|
7
|
+
# Copyright (C) 2006-07 by Tony Arcieri and Martin Emde
|
8
|
+
#
|
9
|
+
# Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)
|
10
|
+
#
|
11
|
+
#---------------------------------------------------------------------------
|
12
|
+
#
|
13
|
+
|
14
|
+
# (C)2006 Tony Arcieri, Martin Emde
|
15
|
+
# Distributed under the Ruby license (http://www.ruby-lang.org/en/LICENSE.txt)
|
16
|
+
|
17
|
+
# BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
|
18
|
+
# by default. It allows input to be spoon-fed from some outside source which
|
19
|
+
# receives arbitrary length datagrams which may-or-may-not contain the token
|
20
|
+
# by which entities are delimited.
|
21
|
+
|
22
|
+
class BufferedTokenizer
|
23
|
+
# New BufferedTokenizers will operate on lines delimited by "\n" by default
|
24
|
+
# or allow you to specify any delimiter token you so choose, which will then
|
25
|
+
# be used by String#split to tokenize the input data
|
26
|
+
def initialize(delimiter = "\n", size_limit = nil)
|
27
|
+
# Store the specified delimiter
|
28
|
+
@delimiter = delimiter
|
29
|
+
|
30
|
+
# Store the specified size limitation
|
31
|
+
@size_limit = size_limit
|
32
|
+
|
33
|
+
# The input buffer is stored as an array. This is by far the most efficient
|
34
|
+
# approach given language constraints (in C a linked list would be a more
|
35
|
+
# appropriate data structure). Segments of input data are stored in a list
|
36
|
+
# which is only joined when a token is reached, substantially reducing the
|
37
|
+
# number of objects required for the operation.
|
38
|
+
@input = []
|
39
|
+
|
40
|
+
# Size of the input buffer
|
41
|
+
@input_size = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
# Extract takes an arbitrary string of input data and returns an array of
|
45
|
+
# tokenized entities, provided there were any available to extract. This
|
46
|
+
# makes for easy processing of datagrams using a pattern like:
|
47
|
+
#
|
48
|
+
# tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...
|
49
|
+
def extract(data)
|
50
|
+
# Extract token-delimited entities from the input string with the split command.
|
51
|
+
# There's a bit of craftiness here with the -1 parameter. Normally split would
|
52
|
+
# behave no differently regardless of if the token lies at the very end of the
|
53
|
+
# input buffer or not (i.e. a literal edge case) Specifying -1 forces split to
|
54
|
+
# return "" in this case, meaning that the last entry in the list represents a
|
55
|
+
# new segment of data where the token has not been encountered
|
56
|
+
entities = data.split @delimiter, -1
|
57
|
+
|
58
|
+
# Check to see if the buffer has exceeded capacity, if we're imposing a limit
|
59
|
+
if @size_limit
|
60
|
+
raise 'input buffer full' if @input_size + entities.first.size > @size_limit
|
61
|
+
@input_size += entities.first.size
|
62
|
+
end
|
63
|
+
|
64
|
+
# Move the first entry in the resulting array into the input buffer. It represents
|
65
|
+
# the last segment of a token-delimited entity unless it's the only entry in the list.
|
66
|
+
@input << entities.shift
|
67
|
+
|
68
|
+
# If the resulting array from the split is empty, the token was not encountered
|
69
|
+
# (not even at the end of the buffer). Since we've encountered no token-delimited
|
70
|
+
# entities this go-around, return an empty array.
|
71
|
+
return [] if entities.empty?
|
72
|
+
|
73
|
+
# At this point, we've hit a token, or potentially multiple tokens. Now we can bring
|
74
|
+
# together all the data we've buffered from earlier calls without hitting a token,
|
75
|
+
# and add it to our list of discovered entities.
|
76
|
+
entities.unshift @input.join
|
77
|
+
|
78
|
+
=begin
|
79
|
+
# Note added by FC, 10Jul07. This paragraph contains a regression. It breaks
|
80
|
+
# empty tokens. Think of the empty line that delimits an HTTP header. It will have
|
81
|
+
# two "\n" delimiters in a row, and this code mishandles the resulting empty token.
|
82
|
+
# It someone figures out how to fix the problem, we can re-enable this code branch.
|
83
|
+
# Multi-character token support.
|
84
|
+
# Split any tokens that were incomplete on the last iteration buf complete now.
|
85
|
+
entities.map! do |e|
|
86
|
+
e.split @delimiter, -1
|
87
|
+
end
|
88
|
+
# Flatten the resulting array. This has the side effect of removing the empty
|
89
|
+
# entry at the end that was produced by passing -1 to split. Add it again if
|
90
|
+
# necessary.
|
91
|
+
if (entities[-1] == [])
|
92
|
+
entities.flatten! << []
|
93
|
+
else
|
94
|
+
entities.flatten!
|
95
|
+
end
|
96
|
+
=end
|
97
|
+
|
98
|
+
# Now that we've hit a token, joined the input buffer and added it to the entities
|
99
|
+
# list, we can go ahead and clear the input buffer. All of the segments that were
|
100
|
+
# stored before the join can now be garbage collected.
|
101
|
+
@input.clear
|
102
|
+
|
103
|
+
# The last entity in the list is not token delimited, however, thanks to the -1
|
104
|
+
# passed to split. It represents the beginning of a new list of as-yet-untokenized
|
105
|
+
# data, so we add it to the start of the list.
|
106
|
+
@input << entities.pop
|
107
|
+
|
108
|
+
# Set the new input buffer size, provided we're keeping track
|
109
|
+
@input_size = @input.first.size if @size_limit
|
110
|
+
|
111
|
+
# Now we're left with the list of extracted token-delimited entities we wanted
|
112
|
+
# in the first place. Hooray!
|
113
|
+
entities
|
114
|
+
end
|
115
|
+
|
116
|
+
# Flush the contents of the input buffer, i.e. return the input buffer even though
|
117
|
+
# a token has not yet been encountered
|
118
|
+
def flush
|
119
|
+
buffer = @input.join
|
120
|
+
@input.clear
|
121
|
+
buffer
|
122
|
+
end
|
123
|
+
|
124
|
+
def empty?
|
125
|
+
@input.empty?
|
126
|
+
end
|
127
|
+
end
|
data/lib/callback.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# class implements a simple callback mechanism for invoking callbacks
|
2
|
+
module Packet
|
3
|
+
class Callback
|
4
|
+
attr_accessor :signature,:stored_proc
|
5
|
+
def initialize(&block)
|
6
|
+
@signature = Guid.hexdigest
|
7
|
+
@stored_proc = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def invoke(*args)
|
11
|
+
@stored_proc.call(*args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/connection.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# FIMXE: following class must modify the fd_watchlist thats being monitored by
|
2
|
+
# main eventloop.
|
3
|
+
|
4
|
+
module Packet
|
5
|
+
class Connection
|
6
|
+
# method gets called when connection to external server is completed
|
7
|
+
def connection_completed
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
# method gets called when external client is disconnected
|
12
|
+
def unbind
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
# method gets called just at the beginning of initializing things.
|
17
|
+
def post_init
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_data
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def ask_worker
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def receive_data
|
30
|
+
|
31
|
+
end
|
32
|
+
end # end of class Connection
|
33
|
+
end # end of module Packet
|
data/lib/core.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
# FIXME: timer implementation can be optimized
|
2
|
+
module Packet
|
3
|
+
module Core
|
4
|
+
def self.included(base_klass)
|
5
|
+
base_klass.extend(ClassMethods)
|
6
|
+
base_klass.instance_eval do
|
7
|
+
# iattr_accessor :connection_callbacks
|
8
|
+
@@connection_callbacks ||= {}
|
9
|
+
cattr_accessor :connection_callbacks
|
10
|
+
attr_accessor :read_ios, :write_ios, :listen_sockets
|
11
|
+
attr_accessor :connection_completion_awaited
|
12
|
+
attr_accessor :connections
|
13
|
+
include CommonMethods
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def after_connection p_method
|
19
|
+
connection_callbacks[:after_connection] ||= []
|
20
|
+
connection_callbacks[:after_connection] << p_method
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_unbind p_method
|
24
|
+
connection_callbacks[:after_unbind] ||= []
|
25
|
+
connection_callbacks[:after_unbind] << p_method
|
26
|
+
end
|
27
|
+
|
28
|
+
def before_unbind p_method
|
29
|
+
connection_callbacks[:before_unbind] ||= []
|
30
|
+
connection_callbacks[:before_unbind] << p_method
|
31
|
+
end
|
32
|
+
end # end of module#ClassMethods
|
33
|
+
|
34
|
+
module CommonMethods
|
35
|
+
include NbioHelper
|
36
|
+
# method
|
37
|
+
def connect(ip,port,t_module,&block)
|
38
|
+
t_socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)
|
39
|
+
t_sock_addr = Socket.sockaddr_in(port,ip)
|
40
|
+
t_socket.setsockopt(Socket::IPPROTO_TCP,Socket::TCP_NODELAY,1)
|
41
|
+
|
42
|
+
connection_completion_awaited[t_socket.fileno] =
|
43
|
+
{ :sock_addr => t_sock_addr, :module => t_module,:block => block }
|
44
|
+
begin
|
45
|
+
t_socket.connect_nonblock(t_sock_addr)
|
46
|
+
immediate_complete(t_socket,t_sock_addr,t_module,&block)
|
47
|
+
rescue Errno::EINPROGRESS
|
48
|
+
write_ios << t_socket
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def reconnect(server,port,handler)
|
53
|
+
raise "invalid handler" unless handler.respond_to?(:connection_completed)
|
54
|
+
return handler if connections.keys.include?(handler.connection.fileno)
|
55
|
+
connect(server,port,handler)
|
56
|
+
end
|
57
|
+
|
58
|
+
def immediate_complete(t_socket,sock_addr,t_module,&block)
|
59
|
+
read_ios << t_socket
|
60
|
+
write_ios.delete(t_socket)
|
61
|
+
decorate_handler(t_socket,true,sock_addr,t_module,&block)
|
62
|
+
connection_completion_awaited.delete(t_socket.fileno)
|
63
|
+
end
|
64
|
+
|
65
|
+
def accept_connection(sock_opts)
|
66
|
+
sock_io = sock_opts[:socket]
|
67
|
+
|
68
|
+
begin
|
69
|
+
client_socket,client_sockaddr = sock_io.accept_nonblock
|
70
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
|
71
|
+
puts "not ready yet"
|
72
|
+
return
|
73
|
+
end
|
74
|
+
read_ios << client_socket
|
75
|
+
decorate_handler(client_socket,true,client_sockaddr,sock_opts[:module],&sock_opts[:block])
|
76
|
+
end
|
77
|
+
|
78
|
+
def complete_connection(t_sock,sock_opts)
|
79
|
+
actually_connected = true
|
80
|
+
begin
|
81
|
+
t_sock.connect_nonblock(sock_opts[:sock_addr])
|
82
|
+
rescue Errno::EISCONN
|
83
|
+
puts "Socket already connected"
|
84
|
+
rescue Errno::ECONNREFUSED
|
85
|
+
actually_connected = false
|
86
|
+
end
|
87
|
+
|
88
|
+
read_ios << t_sock if actually_connected
|
89
|
+
write_ios.delete(t_sock)
|
90
|
+
decorate_handler(t_sock,actually_connected,sock_opts[:sock_addr],sock_opts[:module],&sock_opts[:block])
|
91
|
+
connection_completion_awaited.delete(t_sock.fileno)
|
92
|
+
end
|
93
|
+
|
94
|
+
# method removes the connection and closes the socket
|
95
|
+
def remove_connection(t_sock)
|
96
|
+
@read_ios.delete(t_sock)
|
97
|
+
@write_ios.delete(t_sock)
|
98
|
+
connections.delete(t_sock.fileno)
|
99
|
+
t_sock.close
|
100
|
+
end
|
101
|
+
|
102
|
+
def socket_really_connected?(t_sock)
|
103
|
+
begin
|
104
|
+
t_data = read_data(t_sock)
|
105
|
+
return true
|
106
|
+
rescue DisconnectError
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# method opens a socket for listening
|
112
|
+
def start_server(ip,port,t_module,&block)
|
113
|
+
t_socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)
|
114
|
+
t_socket.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR,true)
|
115
|
+
sockaddr = Socket.sockaddr_in(port.to_i,ip)
|
116
|
+
t_socket.bind(sockaddr)
|
117
|
+
t_socket.listen(50)
|
118
|
+
t_socket.setsockopt(Socket::IPPROTO_TCP,Socket::TCP_NODELAY,1)
|
119
|
+
listen_sockets[t_socket.fileno] = { :socket => t_socket,:block => block,:module => t_module }
|
120
|
+
@read_ios << t_socket
|
121
|
+
end
|
122
|
+
|
123
|
+
# method starts event loop in the process
|
124
|
+
def start_reactor
|
125
|
+
Signal.trap("TERM") { terminate_me }
|
126
|
+
Signal.trap("INT") { shutdown }
|
127
|
+
loop do
|
128
|
+
check_for_timer_events
|
129
|
+
ready_fds = select(@read_ios,@write_ios,nil,0.005)
|
130
|
+
next if ready_fds.blank?
|
131
|
+
ready_fds = ready_fds.flatten.compact
|
132
|
+
ready_fds.each do |t_sock|
|
133
|
+
if t_sock.is_a? UNIXSocket
|
134
|
+
handle_internal_messages(t_sock)
|
135
|
+
else
|
136
|
+
handle_external_messages(t_sock)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def terminate_me
|
143
|
+
exit
|
144
|
+
end
|
145
|
+
|
146
|
+
def shutdown
|
147
|
+
exit
|
148
|
+
end
|
149
|
+
|
150
|
+
def handle_internal_messages(t_sock)
|
151
|
+
raise "Method should be implemented by concerned classes"
|
152
|
+
end
|
153
|
+
|
154
|
+
def handle_external_messages(t_sock)
|
155
|
+
sock_fd = t_sock.fileno
|
156
|
+
if sock_opts = listen_sockets[sock_fd]
|
157
|
+
accept_connection(sock_opts)
|
158
|
+
elsif extern_opts = connection_completion_awaited[sock_fd]
|
159
|
+
complete_connection(t_sock,extern_opts)
|
160
|
+
else
|
161
|
+
read_external_socket(t_sock)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def read_external_socket(t_sock)
|
166
|
+
handler_instance = connections[t_sock.fileno].instance
|
167
|
+
begin
|
168
|
+
t_data = read_data(t_sock)
|
169
|
+
handler_instance.receive_data(t_data) if handler_instance.respond_to?(:receive_data)
|
170
|
+
rescue DisconnectError => sock_error
|
171
|
+
handler_instance.unbind if handler_instance.respond_to?(:unbind)
|
172
|
+
connections.delete(t_sock.fileno)
|
173
|
+
read_ios.delete(t_sock)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_periodic_timer(interval,&block)
|
178
|
+
t_timer = PeriodicEvent.new(interval,&block)
|
179
|
+
@timer_hash[t_timer.timer_signature] = t_timer
|
180
|
+
return t_timer
|
181
|
+
end
|
182
|
+
|
183
|
+
def add_timer(elapsed_time,&block)
|
184
|
+
t_timer = Event.new(elapsed_time,&block)
|
185
|
+
@timer_hash[t_timer.timer_signature] = t_timer
|
186
|
+
return t_timer
|
187
|
+
end
|
188
|
+
|
189
|
+
def cancel_timer(t_timer)
|
190
|
+
@timer_hash.delete(t_timer.timer_signature)
|
191
|
+
end
|
192
|
+
|
193
|
+
def initialize
|
194
|
+
@read_ios ||= []
|
195
|
+
@write_ios ||= []
|
196
|
+
@connection_completion_awaited ||= {}
|
197
|
+
@connections ||= {}
|
198
|
+
@listen_sockets ||= {}
|
199
|
+
|
200
|
+
@timer_hash ||= {}
|
201
|
+
end
|
202
|
+
|
203
|
+
def check_for_timer_events
|
204
|
+
@timer_hash.each do |key,timer|
|
205
|
+
if timer.run_now?
|
206
|
+
timer.run
|
207
|
+
@timer_hash.delete(key) if !timer.respond_to?(:interval)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def initialize_handler(p_module)
|
213
|
+
return p_module if(!p_module.is_a?(Class) and !p_module.is_a?(Module))
|
214
|
+
handler =
|
215
|
+
if(p_module and p_module.is_a?(Class))
|
216
|
+
p_module
|
217
|
+
else
|
218
|
+
Class.new(Connection) { p_module and include p_module }
|
219
|
+
end
|
220
|
+
return handler.new
|
221
|
+
end
|
222
|
+
|
223
|
+
def decorate_handler(t_socket,actually_connected,sock_addr,t_module,&block)
|
224
|
+
handler_instance = initialize_handler(t_module)
|
225
|
+
connection_callbacks[:after_connection].each { |t_callback| self.send(t_callback,handler_instance,t_socket)}
|
226
|
+
handler_instance.invoke_init unless handler_instance.initialized
|
227
|
+
unless actually_connected
|
228
|
+
handler_instance.unbind if handler_instance.respond_to?(:unbind)
|
229
|
+
return
|
230
|
+
end
|
231
|
+
t_signature = Guid.hexdigest
|
232
|
+
handler_instance.signature = t_signature
|
233
|
+
connections[t_socket.fileno] =
|
234
|
+
OpenStruct.new( :socket => t_socket, :instance => handler_instance, :signature => t_signature,:sock_addr => sock_addr)
|
235
|
+
block.call(handler_instance) if block
|
236
|
+
handler_instance.connection_completed if handler_instance.respond_to?(:connection_completed)
|
237
|
+
end
|
238
|
+
|
239
|
+
end # end of module#CommonMethods
|
240
|
+
end #end of module#Core
|
241
|
+
end #end of module#Packet
|