cool.io 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +25 -0
  2. data/CHANGES +199 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +4 -0
  5. data/Rakefile +98 -0
  6. data/VERSION +1 -0
  7. data/examples/echo_client.rb +38 -0
  8. data/examples/echo_server.rb +27 -0
  9. data/examples/google.rb +9 -0
  10. data/examples/httpclient.rb +38 -0
  11. data/ext/cool.io/.gitignore +5 -0
  12. data/ext/cool.io/cool.io.h +58 -0
  13. data/ext/cool.io/cool.io_ext.c +25 -0
  14. data/ext/cool.io/ev_wrap.h +8 -0
  15. data/ext/cool.io/extconf.rb +69 -0
  16. data/ext/cool.io/iowatcher.c +189 -0
  17. data/ext/cool.io/libev.c +8 -0
  18. data/ext/cool.io/loop.c +303 -0
  19. data/ext/cool.io/stat_watcher.c +191 -0
  20. data/ext/cool.io/timer_watcher.c +219 -0
  21. data/ext/cool.io/utils.c +122 -0
  22. data/ext/cool.io/watcher.c +264 -0
  23. data/ext/cool.io/watcher.h +71 -0
  24. data/ext/http11_client/.gitignore +5 -0
  25. data/ext/http11_client/ext_help.h +14 -0
  26. data/ext/http11_client/extconf.rb +6 -0
  27. data/ext/http11_client/http11_client.c +300 -0
  28. data/ext/http11_client/http11_parser.c +403 -0
  29. data/ext/http11_client/http11_parser.h +48 -0
  30. data/ext/http11_client/http11_parser.rl +173 -0
  31. data/ext/libev/Changes +364 -0
  32. data/ext/libev/LICENSE +36 -0
  33. data/ext/libev/README +58 -0
  34. data/ext/libev/README.embed +3 -0
  35. data/ext/libev/ev.c +3867 -0
  36. data/ext/libev/ev.h +826 -0
  37. data/ext/libev/ev_epoll.c +234 -0
  38. data/ext/libev/ev_kqueue.c +198 -0
  39. data/ext/libev/ev_poll.c +148 -0
  40. data/ext/libev/ev_port.c +164 -0
  41. data/ext/libev/ev_select.c +307 -0
  42. data/ext/libev/ev_vars.h +197 -0
  43. data/ext/libev/ev_win32.c +153 -0
  44. data/ext/libev/ev_wrap.h +186 -0
  45. data/ext/libev/test_libev_win32.c +123 -0
  46. data/ext/libev/update_ev_wrap +19 -0
  47. data/lib/.gitignore +2 -0
  48. data/lib/cool.io.rb +30 -0
  49. data/lib/cool.io/async_watcher.rb +43 -0
  50. data/lib/cool.io/dns_resolver.rb +220 -0
  51. data/lib/cool.io/eventmachine.rb +234 -0
  52. data/lib/cool.io/http_client.rb +419 -0
  53. data/lib/cool.io/io.rb +174 -0
  54. data/lib/cool.io/iowatcher.rb +17 -0
  55. data/lib/cool.io/listener.rb +93 -0
  56. data/lib/cool.io/loop.rb +130 -0
  57. data/lib/cool.io/meta.rb +49 -0
  58. data/lib/cool.io/server.rb +74 -0
  59. data/lib/cool.io/socket.rb +224 -0
  60. data/lib/cool.io/timer_watcher.rb +17 -0
  61. data/lib/coolio.rb +2 -0
  62. data/lib/rev.rb +4 -0
  63. data/spec/async_watcher_spec.rb +57 -0
  64. data/spec/possible_tests/schedules_other_threads.rb +48 -0
  65. data/spec/possible_tests/test_on_resolve_failed.rb +9 -0
  66. data/spec/possible_tests/test_resolves.rb +27 -0
  67. data/spec/possible_tests/test_write_during_resolve.rb +27 -0
  68. data/spec/possible_tests/works_straight.rb +71 -0
  69. data/spec/spec_helper.rb +5 -0
  70. data/spec/timer_watcher_spec.rb +55 -0
  71. data/spec/unix_listener_spec.rb +25 -0
  72. data/spec/unix_server_spec.rb +25 -0
  73. metadata +184 -0
@@ -0,0 +1,123 @@
1
+ // a single header file is required
2
+ #include <ev.h>
3
+ #include <stdio.h>
4
+ #include <io.h>
5
+
6
+ // every watcher type has its own typedef'd struct
7
+ // with the name ev_TYPE
8
+ ev_io stdin_watcher;
9
+ ev_timer timeout_watcher;
10
+
11
+ // all watcher callbacks have a similar signature
12
+ // this callback is called when data is readable on stdin
13
+ static void
14
+ stdin_cb (EV_P_ ev_io *w, int revents)
15
+ {
16
+ puts ("stdin ready or done or something");
17
+ // for one-shot events, one must manually stop the watcher
18
+ // with its corresponding stop function.
19
+ //ev_io_stop (EV_A_ w);
20
+
21
+ // this causes all nested ev_loop's to stop iterating
22
+ //ev_unloop (EV_A_ EVUNLOOP_ALL);
23
+ }
24
+
25
+ // another callback, this time for a time-out
26
+ static void
27
+ timeout_cb (EV_P_ ev_timer *w, int revents)
28
+ {
29
+ puts ("timeout");
30
+ // this causes the innermost ev_loop to stop iterating
31
+ ev_unloop (EV_A_ EVUNLOOP_ONE);
32
+ }
33
+
34
+
35
+
36
+ #include <winsock.h>
37
+
38
+ #include <stdlib.h>
39
+ #include <iostream>
40
+ int get_server_fd()
41
+ {
42
+
43
+ //----------------------
44
+ // Initialize Winsock.
45
+ WSADATA wsaData;
46
+ int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
47
+ if (iResult != NO_ERROR) {
48
+ printf("Error at WSAStartup()\n");
49
+ return 1;
50
+ }
51
+
52
+ //----------------------
53
+ // Create a SOCKET for listening for
54
+ // incoming connection requests.
55
+ SOCKET ListenSocket;
56
+ ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
57
+ if (ListenSocket == INVALID_SOCKET) {
58
+ printf("Error at socket(): %ld\n", WSAGetLastError());
59
+ WSACleanup();
60
+ return 1;
61
+ }
62
+ printf("socket returned %d\n", ListenSocket);
63
+
64
+ //----------------------
65
+ // The sockaddr_in structure specifies the address family,
66
+ // IP address, and port for the socket that is being bound.
67
+ sockaddr_in service;
68
+ service.sin_family = AF_INET;
69
+ service.sin_addr.s_addr = inet_addr("127.0.0.1");
70
+ service.sin_port = htons(4444);
71
+
72
+ if (bind( ListenSocket,
73
+ (SOCKADDR*) &service,
74
+ sizeof(service)) == SOCKET_ERROR) {
75
+ printf("bind() failed.\n");
76
+ closesocket(ListenSocket);
77
+ WSACleanup();
78
+ return 1;
79
+ }
80
+
81
+ //----------------------
82
+ // Listen for incoming connection requests.
83
+ // on the created socket
84
+ if (listen( ListenSocket, 1 ) == SOCKET_ERROR) {
85
+ printf("Error listening on socket.\n");
86
+ closesocket(ListenSocket);
87
+ WSACleanup();
88
+ return 1;
89
+ }
90
+
91
+
92
+ printf("sock and osf handle are %d %d, error is \n", ListenSocket, _get_osfhandle (ListenSocket)); // -1 is invalid file handle: http://msdn.microsoft.com/en-us/library/ks2530z6.aspx
93
+ printf("err was %d\n", WSAGetLastError());
94
+ //----------------------
95
+ return ListenSocket;
96
+ }
97
+
98
+
99
+ int
100
+ main (void)
101
+ {
102
+ struct ev_loop *loopy = ev_default_loop(0);
103
+ int fd = get_server_fd();
104
+ int fd_real = _open_osfhandle(fd, NULL);
105
+ int conv = _get_osfhandle(fd_real);
106
+ printf("got server fd %d, loop %d, fd_real %d, that converted %d\n", fd, loopy, fd_real, conv);
107
+ // accept(fd, NULL, NULL);
108
+ // initialise an io watcher, then start it
109
+ // this one will watch for stdin to become readable
110
+ ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ conv, EV_READ);
111
+ ev_io_start (loopy, &stdin_watcher);
112
+
113
+ // initialise a timer watcher, then start it
114
+ // simple non-repeating 5.5 second timeout
115
+ //ev_timer_init (&timeout_watcher, timeout_cb, 15.5, 0.);
116
+ //ev_timer_start (loopy, &timeout_watcher);
117
+ printf("starting loop\n");
118
+ // now wait for events to arrive
119
+ ev_loop (loopy, 0);
120
+
121
+ // unloop was called, so exit
122
+ return 0;
123
+ }
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+
3
+ (
4
+ echo '#define VAR(name,decl) name'
5
+ echo '#define EV_GENWRAP 1'
6
+ cat ev_vars.h
7
+ ) | cc -E -o - - | perl -ne '
8
+ while (<>) {
9
+ push @syms, $1 if /(^\w+)/;
10
+ }
11
+ print "/* DO NOT EDIT, automatically generated by update_ev_wrap */\n",
12
+ "#ifndef EV_WRAP_H\n",
13
+ "#define EV_WRAP_H\n",
14
+ (map "#define $_ ((loop)->$_)\n", @syms),
15
+ "#else\n",
16
+ "#undef EV_WRAP_H\n",
17
+ (map "#undef $_\n", @syms),
18
+ "#endif\n";
19
+ ' >ev_wrap.h
data/lib/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.so
2
+ *.bundle
data/lib/cool.io.rb ADDED
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'iobuffer'
8
+
9
+ require "cool.io_ext"
10
+ require "cool.io/loop"
11
+ require "cool.io/meta"
12
+ require "cool.io/io"
13
+ require "cool.io/iowatcher"
14
+ require "cool.io/timer_watcher"
15
+ require "cool.io/async_watcher"
16
+ require "cool.io/listener"
17
+ require "cool.io/dns_resolver"
18
+ require "cool.io/socket"
19
+ require "cool.io/server"
20
+ require "cool.io/http_client"
21
+
22
+ module Coolio
23
+ VERSION = File.read(File.expand_path('../../VERSION', __FILE__)).strip
24
+ def self.version; VERSION; end
25
+ end
26
+
27
+ module Cool
28
+ # Allow Coolio module to be referenced as Cool.io
29
+ def self.io; Coolio; end
30
+ end
@@ -0,0 +1,43 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ module Coolio
8
+ # The AsyncWatcher lets you signal another thread to wake up. Its
9
+ # intended use is notifying another thread of events.
10
+ class AsyncWatcher < IOWatcher
11
+ def initialize
12
+ @reader, @writer = ::IO.pipe
13
+ super(@reader)
14
+ end
15
+
16
+ # Signal the async watcher. This call is thread safe.
17
+ def signal
18
+ # Write a byte to the pipe. What we write is meaningless, it
19
+ # merely signals an event has occurred for each byte written.
20
+ @writer.write "\0"
21
+ end
22
+
23
+ # Called whenever a signal is received
24
+ def on_signal; end
25
+ event_callback :on_signal
26
+
27
+ #########
28
+ protected
29
+ #########
30
+
31
+ def on_readable
32
+ # Read a byte from the pipe. This clears readability, unless
33
+ # another signal is pending
34
+ begin
35
+ @reader.read_nonblock 1
36
+ rescue Errno::EAGAIN
37
+ # in case there are spurious wakeups from forked processs
38
+ return
39
+ end
40
+ on_signal
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,220 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #
6
+ # Gimpy hacka asynchronous DNS resolver
7
+ #
8
+ # Word to the wise: I don't know what I'm doing here. This was cobbled together
9
+ # as best I could with extremely limited knowledge of the DNS format. There's
10
+ # obviously a ton of stuff it doesn't support (like IPv6 and TCP).
11
+ #
12
+ # If you do know what you're doing with DNS, feel free to improve this!
13
+ # A good starting point my be this EventMachine Net::DNS-based asynchronous
14
+ # resolver:
15
+ #
16
+ # http://gist.github.com/663299
17
+ #
18
+ #++
19
+
20
+ module Coolio
21
+ # A non-blocking DNS resolver. It provides interfaces for querying both
22
+ # /etc/hosts and nameserves listed in /etc/resolv.conf, or nameservers of
23
+ # your choosing.
24
+ #
25
+ # Presently the client only supports UDP requests against your nameservers
26
+ # and cannot resolve anything with records larger than 512-bytes. Also,
27
+ # IPv6 is not presently supported.
28
+ #
29
+ # DNSResolver objects are one-shot. Once they resolve a domain name they
30
+ # automatically detach themselves from the event loop and cannot be used
31
+ # again.
32
+ class DNSResolver < IOWatcher
33
+ #--
34
+ # TODO check if it's caching right
35
+ RESOLV_CONF = '/etc/resolv.conf'
36
+ HOSTS = '/etc/hosts'
37
+ DNS_PORT = 53
38
+ DATAGRAM_SIZE = 512
39
+ TIMEOUT = 3 # Retry timeout for each datagram sent
40
+ RETRIES = 4 # Number of retries to attempt
41
+ # so currently total is 12s before it will err due to timeouts
42
+ # if it errs due to inability to reach the DNS server [Errno::EHOSTUNREACH], same
43
+
44
+ # Query /etc/hosts (or the specified hostfile) for the given host
45
+ def self.hosts(host, hostfile = HOSTS)
46
+ hosts = {}
47
+ File.open(hostfile).each_line do |host_entry|
48
+ entries = host_entry.gsub(/#.*$/, '').gsub(/\s+/, ' ').split(' ')
49
+ addr = entries.shift
50
+ entries.each { |e| hosts[e] ||= addr }
51
+ end
52
+
53
+ hosts[host]
54
+ end
55
+
56
+ # Create a new Coolio::Watcher descended object to resolve the
57
+ # given hostname. If you so desire you can also specify a
58
+ # list of nameservers to query. By default the resolver will
59
+ # use nameservers listed in /etc/resolv.conf
60
+ def initialize(hostname, *nameservers)
61
+ if nameservers.empty?
62
+ nameservers = File.read(RESOLV_CONF).scan(/^\s*nameserver\s+([0-9.:]+)/).flatten # TODO could optimize this to just read once
63
+ raise RuntimeError, "no nameservers found in #{RESOLV_CONF}" if nameservers.empty? # TODO just call resolve_failed, not raise [also handle Errno::ENOENT)]
64
+ end
65
+
66
+ @nameservers = nameservers
67
+ @question = request_question hostname
68
+
69
+ @socket = UDPSocket.new
70
+ @timer = Timeout.new(self)
71
+
72
+ super(@socket)
73
+ end
74
+
75
+ # Attach the DNSResolver to the given event loop
76
+ def attach(evloop)
77
+ send_request
78
+ @timer.attach(evloop)
79
+ super
80
+ end
81
+
82
+ # Detach the DNSResolver from the given event loop
83
+ def detach
84
+ @timer.detach if @timer.attached?
85
+ super
86
+ end
87
+
88
+ # Called when the name has successfully resolved to an address
89
+ def on_success(address); end
90
+ event_callback :on_success
91
+
92
+ # Called when we receive a response indicating the name didn't resolve
93
+ def on_failure; end
94
+ event_callback :on_failure
95
+
96
+ # Called if we don't receive a response, defaults to calling on_failure
97
+ def on_timeout
98
+ on_failure
99
+ end
100
+
101
+ #########
102
+ protected
103
+ #########
104
+
105
+ # Send a request to the DNS server
106
+ def send_request
107
+ nameserver = @nameservers.shift
108
+ @nameservers << nameserver # rotate them
109
+ @socket.connect @nameservers.first, DNS_PORT
110
+ begin
111
+ @socket.send request_message, 0
112
+ rescue Errno::EHOSTUNREACH # TODO figure out why it has to be wrapper here, when the other wrapper should be wrapping this one!
113
+ end
114
+ end
115
+
116
+ # Called by the subclass when the DNS response is available
117
+ def on_readable
118
+ datagram = nil
119
+ begin
120
+ datagram = @socket.recvfrom_nonblock(DATAGRAM_SIZE).first
121
+ rescue Errno::ECONNREFUSED
122
+ end
123
+
124
+ address = response_address datagram rescue nil
125
+ address ? on_success(address) : on_failure
126
+ detach
127
+ end
128
+
129
+ def request_question(hostname)
130
+ raise ArgumentError, "hostname cannot be nil" if hostname.nil?
131
+
132
+ # Query name
133
+ message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\0"
134
+
135
+ # Host address query
136
+ qtype = 1
137
+
138
+ # Internet query
139
+ qclass = 1
140
+
141
+ message << [qtype, qclass].pack('nn')
142
+ end
143
+
144
+ def request_message
145
+ # Standard query header
146
+ message = [2, 1, 0].pack('nCC')
147
+
148
+ # One entry
149
+ qdcount = 1
150
+
151
+ # No answer, authority, or additional records
152
+ ancount = nscount = arcount = 0
153
+
154
+ message << [qdcount, ancount, nscount, arcount].pack('nnnn')
155
+ message << @question
156
+ end
157
+
158
+ def response_address(message)
159
+ # Confirm the ID field
160
+ id = message[0..1].unpack('n').first.to_i
161
+ return unless id == 2
162
+
163
+ # Check the QR value and confirm this message is a response
164
+ qr = message[2..2].unpack('B1').first.to_i
165
+ return unless qr == 1
166
+
167
+ # Check the RCODE (lower nibble) and ensure there wasn't an error
168
+ rcode = message[3..3].unpack('B8').first[4..7].to_i(2)
169
+ return unless rcode == 0
170
+
171
+ # Extract the question and answer counts
172
+ qdcount, ancount = message[4..7].unpack('nn').map { |n| n.to_i }
173
+
174
+ # We only asked one question
175
+ return unless qdcount == 1
176
+ message.slice!(0, 12)
177
+
178
+ # Make sure it's the same question
179
+ return unless message[0..(@question.size-1)] == @question
180
+ message.slice!(0, @question.size)
181
+
182
+ # Extract the RDLENGTH
183
+ while not message.empty?
184
+ type = message[2..3].unpack('n').first.to_i
185
+ rdlength = message[10..11].unpack('n').first.to_i
186
+ rdata = message[12..(12 + rdlength - 1)]
187
+ message.slice!(0, 12 + rdlength)
188
+
189
+ # Only IPv4 supported
190
+ next unless rdlength == 4
191
+
192
+ # If we got an Internet address back, return it
193
+ return rdata.unpack('CCCC').join('.') if type == 1
194
+ end
195
+
196
+ nil
197
+ end
198
+
199
+ class Timeout < TimerWatcher
200
+ def initialize(resolver)
201
+ @resolver = resolver
202
+ @attempts = 0
203
+ super(TIMEOUT, true)
204
+ end
205
+
206
+ def on_timer
207
+ @attempts += 1
208
+ if @attempts <= RETRIES
209
+ begin
210
+ return @resolver.__send__(:send_request)
211
+ rescue Errno::EHOSTUNREACH # if the DNS is toast try again after the timeout occurs again
212
+ return nil
213
+ end
214
+ end
215
+ @resolver.__send__(:on_timeout)
216
+ @resolver.detach
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,234 @@
1
+ #--
2
+ # Copyright (C)2007-10 Tony Arcieri, Roger Pack
3
+ # You can redistribute this under the terms of the Ruby license
4
+ # See file LICENSE for details
5
+ #++
6
+
7
+ require 'cool.io'
8
+
9
+ # EventMachine emulation for Cool.io:
10
+ #
11
+ # require 'coolio/eventmachine'
12
+ #
13
+ # Drawbacks: slightly slower than EM.
14
+ # Benefits: timers are more accurate using libev than using EM
15
+ # TODO: some things like connection timeouts aren't implemented yet
16
+ # DONE: timers and normal socket functions are implemented.
17
+ module EventMachine
18
+ class << self
19
+ # Start the Reactor loop
20
+ def run
21
+ yield if block_given?
22
+ Coolio::Loop.default.run
23
+ end
24
+
25
+ # Stop the Reactor loop
26
+ def stop_event_loop
27
+ Coolio::Loop.default.stop
28
+ end
29
+
30
+ class OneShotEMTimer < Coolio::TimerWatcher
31
+ def setup(proc)
32
+ @proc = proc
33
+ end
34
+
35
+ def on_timer
36
+ @proc.call
37
+ end
38
+ end
39
+
40
+ # ltodo: use Coolio's PeriodicTimer to wrap EM's two similar to it
41
+ # todo: close all connections on 'stop', I believe
42
+
43
+ def add_timer(interval, proc = nil, &block)
44
+ block ||= proc
45
+ t = OneShotEMTimer.new(interval, false) # non repeating
46
+ t.setup(block)
47
+
48
+ # fire 'er off ltodo: do we keep track of these timers in memory?
49
+ t.attach(Coolio::Loop.default)
50
+ t
51
+ end
52
+
53
+ def cancel_timer(t)
54
+ # guess there's a case where EM you can say 'cancel' but it's already fired?
55
+ # kind of odd but it happens
56
+ t.detach if t.attached?
57
+ end
58
+
59
+ def set_comm_inactivity_timeout(*args); end # TODO
60
+
61
+ # Make an outgoing connection
62
+ def connect(addr, port, handler = Connection, *args, &block)
63
+ block = args.pop if Proc === args[-1]
64
+
65
+ # make sure we're a 'real' class here
66
+ klass = if (handler and handler.is_a?(Class))
67
+ handler
68
+ else
69
+ Class.new( Connection ) {handler and include handler}
70
+ end
71
+
72
+ wrapped_child = CallsBackToEM.connect(addr, port, *args) # ltodo: args? what? they're used? also TODOC TODO FIX
73
+ conn = klass.new(wrapped_child) # ltodo [?] addr, port, *args)
74
+ wrapped_child.attach(Coolio::Loop.default) # necessary
75
+ conn.heres_your_socket(wrapped_child)
76
+ wrapped_child.call_back_to_this(conn) # calls post_init for us
77
+ yield conn if block_given?
78
+ end
79
+
80
+ # Start a TCP server on the given address and port
81
+ def start_server(addr, port, handler = Connection, *args, &block)
82
+ # make sure we're a 'real' class here
83
+ klass = if (handler and handler.is_a?(Class))
84
+ handler
85
+ else
86
+ Class.new( Connection ) {handler and include handler}
87
+ end
88
+
89
+ server = Coolio::TCPServer.new(addr, port, CallsBackToEM, *args) do |wrapped_child|
90
+ conn = klass.new(wrapped_child)
91
+ conn.heres_your_socket(wrapped_child) # ideally NOT have this :)
92
+ wrapped_child.call_back_to_this(conn)
93
+ block.call(conn) if block
94
+ end
95
+
96
+ server.attach(Coolio::Loop.default)
97
+ end
98
+
99
+ def stop_server(server)
100
+ server.close
101
+ end
102
+
103
+ # Set the maximum number of descriptors available to this process
104
+ def set_descriptor_table_size(nfds)
105
+ Coolio::Utils.maxfds = nfds
106
+ end
107
+
108
+ # Compatibility noop. Handled automatically by libev
109
+ def epoll; end
110
+
111
+ # Compatibility noop. Handled automatically by libev
112
+ def kqueue; end
113
+ end
114
+
115
+ class CallsBackToEM < Coolio::TCPSocket
116
+ class ConnectTimer < Coolio::TimerWatcher
117
+ attr_accessor :parent
118
+ def on_timer
119
+ @parent.connection_has_timed_out
120
+ end
121
+ end
122
+
123
+ def call_back_to_this parent
124
+ @call_back_to_this = parent
125
+ parent.post_init
126
+ end
127
+
128
+ def on_connect
129
+ # @connection_timer.detach if @connection_timer
130
+ # won't need that anymore :) -- with server connecteds we don't have it, anyway
131
+
132
+ # TODO should server accepted's call this? They don't currently
133
+ # [and can't, since on_connect gets called basically in the initializer--needs some code love for that to happen :)
134
+ @call_back_to_this.connection_completed if @call_back_to_this
135
+ end
136
+
137
+ def connection_has_timed_out
138
+ return if closed?
139
+
140
+ # wonder if this works when you're within a half-connected phase.
141
+ # I think it does. What about TCP state?
142
+ close unless closed?
143
+ @call_back_to_this.unbind
144
+ end
145
+
146
+ def on_write_complete
147
+ close if @should_close_after_writing
148
+ end
149
+
150
+ def should_close_after_writing
151
+ @should_close_after_writing = true;
152
+ end
153
+
154
+ def on_close
155
+ @call_back_to_this.unbind # about the same ltodo check if they ARE the same here
156
+ end
157
+
158
+ def on_resolve_failed
159
+ fail
160
+ end
161
+
162
+ def on_connect_failed
163
+ fail
164
+ end
165
+
166
+ def on_read(data)
167
+ @call_back_to_this.receive_data data
168
+ end
169
+
170
+ def fail
171
+ #@connection_timer.detch if @connection_timer
172
+ @call_back_to_this.unbind
173
+ end
174
+
175
+ def self.connect(*args)
176
+ a = super *args
177
+ # the connect timer currently kills TCPServer classes. I'm not sure why.
178
+ #@connection_timer = ConnectTimer.new(14) # needs to be at least higher than 12 :)
179
+ #@connection_timer.parent = a
180
+ #@connection_timer.attach(Coolio::Loop.default)
181
+ a
182
+ end
183
+ end
184
+
185
+ class Connection
186
+ def self.new(*args)
187
+ allocate#.instance_eval do
188
+ # initialize *args
189
+ #end
190
+ end
191
+
192
+ # we will need to call 'their functions' appropriately -- the commented out ones, here
193
+ #
194
+ # Callback fired when connection is created
195
+ def post_init
196
+ # I thought we were 'overriding' EM's existing methods, here.
197
+ # Huh? Why do we have to define these then?
198
+ end
199
+
200
+ # Callback fired when connection is closed
201
+ def unbind; end
202
+
203
+ # Callback fired when data is received
204
+ # def receive_data(data); end
205
+ def heres_your_socket(instantiated_coolio_socket)
206
+ instantiated_coolio_socket.call_back_to_this self
207
+ @wrapped_coolio = instantiated_coolio_socket
208
+ end
209
+
210
+ # Send data to the current connection -- called by them
211
+ def send_data(data)
212
+ @wrapped_coolio.write data
213
+ end
214
+
215
+ # Close the connection, optionally after writing
216
+ def close_connection(after_writing = false)
217
+ return close_connection_after_writing if after_writing
218
+ @wrapped_coolio.close
219
+ end
220
+
221
+ # Close the connection after all data has been written
222
+ def close_connection_after_writing
223
+ @wrapped_coolio.output_buffer_size.zero? ? @wrapped_coolio.close : @wrapped_coolio.should_close_after_writing
224
+ end
225
+
226
+ def get_peername
227
+ family, port, host_name, host_ip = @wrapped_coolio.peeraddr
228
+ Socket.pack_sockaddr_in(port, host_ip) # pack it up :)
229
+ end
230
+ end
231
+ end
232
+
233
+ # Shortcut constant
234
+ EM = EventMachine