cool.io 1.2.0-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/.rspec +3 -0
- data/.travis.yml +4 -0
- data/CHANGES.md +176 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +172 -0
- data/Rakefile +81 -0
- data/cool.io.gemspec +28 -0
- data/examples/dslified_echo_client.rb +34 -0
- data/examples/dslified_echo_server.rb +24 -0
- data/examples/echo_client.rb +38 -0
- data/examples/echo_server.rb +27 -0
- data/examples/google.rb +9 -0
- data/examples/httpclient.rb +38 -0
- data/ext/cool.io/.gitignore +5 -0
- data/ext/cool.io/cool.io.h +58 -0
- data/ext/cool.io/cool.io_ext.c +25 -0
- data/ext/cool.io/ev_wrap.h +8 -0
- data/ext/cool.io/extconf.rb +73 -0
- data/ext/cool.io/iowatcher.c +189 -0
- data/ext/cool.io/libev.c +8 -0
- data/ext/cool.io/loop.c +301 -0
- data/ext/cool.io/stat_watcher.c +269 -0
- data/ext/cool.io/timer_watcher.c +219 -0
- data/ext/cool.io/utils.c +122 -0
- data/ext/cool.io/watcher.c +264 -0
- data/ext/cool.io/watcher.h +71 -0
- data/ext/http11_client/.gitignore +5 -0
- data/ext/http11_client/LICENSE +31 -0
- data/ext/http11_client/ext_help.h +14 -0
- data/ext/http11_client/extconf.rb +6 -0
- data/ext/http11_client/http11_client.c +300 -0
- data/ext/http11_client/http11_parser.c +403 -0
- data/ext/http11_client/http11_parser.h +48 -0
- data/ext/http11_client/http11_parser.rl +173 -0
- data/ext/iobuffer/extconf.rb +9 -0
- data/ext/iobuffer/iobuffer.c +765 -0
- data/ext/libev/Changes +388 -0
- data/ext/libev/LICENSE +36 -0
- data/ext/libev/README +58 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +4803 -0
- data/ext/libev/ev.h +845 -0
- data/ext/libev/ev_epoll.c +279 -0
- data/ext/libev/ev_kqueue.c +214 -0
- data/ext/libev/ev_poll.c +148 -0
- data/ext/libev/ev_port.c +185 -0
- data/ext/libev/ev_select.c +314 -0
- data/ext/libev/ev_vars.h +203 -0
- data/ext/libev/ev_win32.c +163 -0
- data/ext/libev/ev_wrap.h +200 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/lib/.gitignore +2 -0
- data/lib/cool.io.rb +32 -0
- data/lib/cool.io/async_watcher.rb +43 -0
- data/lib/cool.io/custom_require.rb +9 -0
- data/lib/cool.io/dns_resolver.rb +225 -0
- data/lib/cool.io/dsl.rb +135 -0
- data/lib/cool.io/eventmachine.rb +234 -0
- data/lib/cool.io/http_client.rb +427 -0
- data/lib/cool.io/io.rb +174 -0
- data/lib/cool.io/iowatcher.rb +17 -0
- data/lib/cool.io/listener.rb +93 -0
- data/lib/cool.io/loop.rb +130 -0
- data/lib/cool.io/meta.rb +49 -0
- data/lib/cool.io/server.rb +74 -0
- data/lib/cool.io/socket.rb +230 -0
- data/lib/cool.io/timer_watcher.rb +17 -0
- data/lib/cool.io/version.rb +5 -0
- data/lib/coolio.rb +2 -0
- data/spec/async_watcher_spec.rb +57 -0
- data/spec/dns_spec.rb +39 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/stat_watcher_spec.rb +77 -0
- data/spec/timer_watcher_spec.rb +55 -0
- data/spec/unix_listener_spec.rb +25 -0
- data/spec/unix_server_spec.rb +25 -0
- metadata +200 -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
|
+
}
|
data/lib/.gitignore
ADDED
data/lib/cool.io.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C)2011 Tony Arcieri
|
3
|
+
# You can redistribute this under the terms of the Ruby license
|
4
|
+
# See file LICENSE for details
|
5
|
+
#++
|
6
|
+
|
7
|
+
require "cool.io/version"
|
8
|
+
require "cool.io/custom_require"
|
9
|
+
cool_require "iobuffer_ext"
|
10
|
+
cool_require "cool.io_ext"
|
11
|
+
|
12
|
+
require "cool.io/loop"
|
13
|
+
require "cool.io/meta"
|
14
|
+
require "cool.io/io"
|
15
|
+
require "cool.io/iowatcher"
|
16
|
+
require "cool.io/timer_watcher"
|
17
|
+
require "cool.io/async_watcher"
|
18
|
+
require "cool.io/listener"
|
19
|
+
require "cool.io/dns_resolver"
|
20
|
+
require "cool.io/socket"
|
21
|
+
require "cool.io/server"
|
22
|
+
require "cool.io/http_client"
|
23
|
+
require "cool.io/dsl"
|
24
|
+
|
25
|
+
module Coolio
|
26
|
+
def self.inspect; "Cool.io"; end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Cool
|
30
|
+
# Allow Coolio module to be referenced as Cool.io
|
31
|
+
def self.io; Coolio; end
|
32
|
+
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,225 @@
|
|
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
|
+
if RUBY_PLATFORM =~ /mingw|win32/
|
36
|
+
HOSTS = 'c:\WINDOWS\system32\drivers\etc\hosts'
|
37
|
+
else
|
38
|
+
HOSTS = '/etc/hosts'
|
39
|
+
end
|
40
|
+
DNS_PORT = 53
|
41
|
+
DATAGRAM_SIZE = 512
|
42
|
+
TIMEOUT = 3 # Retry timeout for each datagram sent
|
43
|
+
RETRIES = 4 # Number of retries to attempt
|
44
|
+
# so currently total is 12s before it will err due to timeouts
|
45
|
+
# if it errs due to inability to reach the DNS server [Errno::EHOSTUNREACH], same
|
46
|
+
# Query /etc/hosts (or the specified hostfile) for the given host
|
47
|
+
def self.hosts(host, hostfile = HOSTS)
|
48
|
+
hosts = {}
|
49
|
+
File.open(hostfile) do |f|
|
50
|
+
f.each_line do |host_entry|
|
51
|
+
entries = host_entry.gsub(/#.*$/, '').gsub(/\s+/, ' ').split(' ')
|
52
|
+
addr = entries.shift
|
53
|
+
entries.each { |e| hosts[e] ||= addr }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
hosts[host]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Create a new Coolio::Watcher descended object to resolve the
|
61
|
+
# given hostname. If you so desire you can also specify a
|
62
|
+
# list of nameservers to query. By default the resolver will
|
63
|
+
# use nameservers listed in /etc/resolv.conf
|
64
|
+
def initialize(hostname, *nameservers)
|
65
|
+
if nameservers.empty?
|
66
|
+
require 'resolv'
|
67
|
+
nameservers = Resolv::DNS::Config.default_config_hash[:nameserver]
|
68
|
+
raise RuntimeError, "no nameservers found" if nameservers.empty? # TODO just call resolve_failed, not raise [also handle Errno::ENOENT)]
|
69
|
+
end
|
70
|
+
|
71
|
+
@nameservers = nameservers
|
72
|
+
@question = request_question hostname
|
73
|
+
|
74
|
+
@socket = UDPSocket.new
|
75
|
+
@timer = Timeout.new(self)
|
76
|
+
|
77
|
+
super(@socket)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Attach the DNSResolver to the given event loop
|
81
|
+
def attach(evloop)
|
82
|
+
send_request
|
83
|
+
@timer.attach(evloop)
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Detach the DNSResolver from the given event loop
|
88
|
+
def detach
|
89
|
+
@timer.detach if @timer.attached?
|
90
|
+
super
|
91
|
+
end
|
92
|
+
|
93
|
+
# Called when the name has successfully resolved to an address
|
94
|
+
def on_success(address); end
|
95
|
+
event_callback :on_success
|
96
|
+
|
97
|
+
# Called when we receive a response indicating the name didn't resolve
|
98
|
+
def on_failure; end
|
99
|
+
event_callback :on_failure
|
100
|
+
|
101
|
+
# Called if we don't receive a response, defaults to calling on_failure
|
102
|
+
def on_timeout
|
103
|
+
on_failure
|
104
|
+
end
|
105
|
+
|
106
|
+
#########
|
107
|
+
protected
|
108
|
+
#########
|
109
|
+
|
110
|
+
# Send a request to the DNS server
|
111
|
+
def send_request
|
112
|
+
nameserver = @nameservers.shift
|
113
|
+
@nameservers << nameserver # rotate them
|
114
|
+
@socket.connect @nameservers.first, DNS_PORT
|
115
|
+
begin
|
116
|
+
@socket.send request_message, 0
|
117
|
+
rescue Errno::EHOSTUNREACH # TODO figure out why it has to be wrapper here, when the other wrapper should be wrapping this one!
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Called by the subclass when the DNS response is available
|
122
|
+
def on_readable
|
123
|
+
datagram = nil
|
124
|
+
begin
|
125
|
+
datagram = @socket.recvfrom_nonblock(DATAGRAM_SIZE).first
|
126
|
+
rescue Errno::ECONNREFUSED
|
127
|
+
end
|
128
|
+
|
129
|
+
address = response_address datagram rescue nil
|
130
|
+
address ? on_success(address) : on_failure
|
131
|
+
detach
|
132
|
+
end
|
133
|
+
|
134
|
+
def request_question(hostname)
|
135
|
+
raise ArgumentError, "hostname cannot be nil" if hostname.nil?
|
136
|
+
|
137
|
+
# Query name
|
138
|
+
message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\0"
|
139
|
+
|
140
|
+
# Host address query
|
141
|
+
qtype = 1
|
142
|
+
|
143
|
+
# Internet query
|
144
|
+
qclass = 1
|
145
|
+
|
146
|
+
message << [qtype, qclass].pack('nn')
|
147
|
+
end
|
148
|
+
|
149
|
+
def request_message
|
150
|
+
# Standard query header
|
151
|
+
message = [2, 1, 0].pack('nCC')
|
152
|
+
|
153
|
+
# One entry
|
154
|
+
qdcount = 1
|
155
|
+
|
156
|
+
# No answer, authority, or additional records
|
157
|
+
ancount = nscount = arcount = 0
|
158
|
+
|
159
|
+
message << [qdcount, ancount, nscount, arcount].pack('nnnn')
|
160
|
+
message << @question
|
161
|
+
end
|
162
|
+
|
163
|
+
def response_address(message)
|
164
|
+
# Confirm the ID field
|
165
|
+
id = message[0..1].unpack('n').first.to_i
|
166
|
+
return unless id == 2
|
167
|
+
|
168
|
+
# Check the QR value and confirm this message is a response
|
169
|
+
qr = message[2..2].unpack('B1').first.to_i
|
170
|
+
return unless qr == 1
|
171
|
+
|
172
|
+
# Check the RCODE (lower nibble) and ensure there wasn't an error
|
173
|
+
rcode = message[3..3].unpack('B8').first[4..7].to_i(2)
|
174
|
+
return unless rcode == 0
|
175
|
+
|
176
|
+
# Extract the question and answer counts
|
177
|
+
qdcount, ancount = message[4..7].unpack('nn').map { |n| n.to_i }
|
178
|
+
|
179
|
+
# We only asked one question
|
180
|
+
return unless qdcount == 1
|
181
|
+
message.slice!(0, 12)
|
182
|
+
|
183
|
+
# Make sure it's the same question
|
184
|
+
return unless message[0..(@question.size-1)] == @question
|
185
|
+
message.slice!(0, @question.size)
|
186
|
+
|
187
|
+
# Extract the RDLENGTH
|
188
|
+
while not message.empty?
|
189
|
+
type = message[2..3].unpack('n').first.to_i
|
190
|
+
rdlength = message[10..11].unpack('n').first.to_i
|
191
|
+
rdata = message[12..(12 + rdlength - 1)]
|
192
|
+
message.slice!(0, 12 + rdlength)
|
193
|
+
|
194
|
+
# Only IPv4 supported
|
195
|
+
next unless rdlength == 4
|
196
|
+
|
197
|
+
# If we got an Internet address back, return it
|
198
|
+
return rdata.unpack('CCCC').join('.') if type == 1
|
199
|
+
end
|
200
|
+
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
|
204
|
+
class Timeout < TimerWatcher
|
205
|
+
def initialize(resolver)
|
206
|
+
@resolver = resolver
|
207
|
+
@attempts = 0
|
208
|
+
super(TIMEOUT, true)
|
209
|
+
end
|
210
|
+
|
211
|
+
def on_timer
|
212
|
+
@attempts += 1
|
213
|
+
if @attempts <= RETRIES
|
214
|
+
begin
|
215
|
+
return @resolver.__send__(:send_request)
|
216
|
+
rescue Errno::EHOSTUNREACH # if the DNS is toast try again after the timeout occurs again
|
217
|
+
return nil
|
218
|
+
end
|
219
|
+
end
|
220
|
+
@resolver.__send__(:on_timeout)
|
221
|
+
@resolver.detach
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
data/lib/cool.io/dsl.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C)2010 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
|
+
# A module we stash all the connections defined by the DSL under
|
9
|
+
module Connections; end
|
10
|
+
|
11
|
+
# A DSL for defining Cool.io connection types and servers
|
12
|
+
module DSL
|
13
|
+
# Define all methods on the metaclass
|
14
|
+
module_function
|
15
|
+
|
16
|
+
# Run the default Cool.io event loop
|
17
|
+
def run
|
18
|
+
Cool.io::Loop.default.run
|
19
|
+
end
|
20
|
+
|
21
|
+
# Connect to the given host and port using the given connection class
|
22
|
+
def connect(host, port, connection_name = nil, *initializer_args, &block)
|
23
|
+
if block_given?
|
24
|
+
initializer_args.unshift connection_name if connection_name
|
25
|
+
|
26
|
+
klass = Class.new Cool.io::TCPSocket
|
27
|
+
connection_builder = ConnectionBuilder.new klass
|
28
|
+
connection_builder.instance_eval(&block)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "no connection name or block given" unless connection_name
|
31
|
+
klass = self[connection_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
client = klass.connect host, port, *initializer_args
|
35
|
+
client.attach Cool.io::Loop.default
|
36
|
+
client
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a new Cool.io::TCPServer
|
40
|
+
def server(host, port, connection_name = nil, *initializer_args, &block)
|
41
|
+
if block_given?
|
42
|
+
initializer_args.unshift connection_name if connection_name
|
43
|
+
|
44
|
+
klass = Class.new Cool.io::TCPSocket
|
45
|
+
connection_builder = ConnectionBuilder.new klass
|
46
|
+
connection_builder.instance_eval(&block)
|
47
|
+
else
|
48
|
+
raise ArgumentError, "no connection name or block given" unless connection_name
|
49
|
+
klass = self[connection_name]
|
50
|
+
end
|
51
|
+
|
52
|
+
server = Cool.io::TCPServer.new host, port, klass, *initializer_args
|
53
|
+
server.attach Cool.io::Loop.default
|
54
|
+
server
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a new Cool.io::TCPSocket class
|
58
|
+
def connection(name, &block)
|
59
|
+
# Camelize class name
|
60
|
+
class_name = name.to_s.split('_').map { |s| s.capitalize }.join
|
61
|
+
|
62
|
+
connection = Class.new Cool.io::TCPSocket
|
63
|
+
connection_builder = ConnectionBuilder.new connection
|
64
|
+
connection_builder.instance_eval(&block)
|
65
|
+
|
66
|
+
Coolio::Connections.const_set class_name, connection
|
67
|
+
end
|
68
|
+
|
69
|
+
# Look up a connection class by its name
|
70
|
+
def [](connection_name)
|
71
|
+
class_name = connection_name.to_s.split('_').map { |s| s.capitalize }.join
|
72
|
+
|
73
|
+
begin
|
74
|
+
Coolio::Connections.const_get class_name
|
75
|
+
rescue NameError
|
76
|
+
raise NameError, "No connection type registered for #{connection_name.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Builder for Cool.io::TCPSocket classes
|
81
|
+
class ConnectionBuilder
|
82
|
+
def initialize(klass)
|
83
|
+
@klass = klass
|
84
|
+
end
|
85
|
+
|
86
|
+
# Declare an initialize function
|
87
|
+
def initializer(&action)
|
88
|
+
@klass.send :define_method, :initialize, &action
|
89
|
+
end
|
90
|
+
|
91
|
+
# Declare the on_connect callback
|
92
|
+
def on_connect(&action)
|
93
|
+
@klass.send :define_method, :on_connect, &action
|
94
|
+
end
|
95
|
+
|
96
|
+
# Declare a callback fired if we failed to connect
|
97
|
+
def on_connect_failed(&action)
|
98
|
+
@klass.send :define_method, :on_connect_failed, &action
|
99
|
+
end
|
100
|
+
|
101
|
+
# Declare a callback fired if DNS resolution failed
|
102
|
+
def on_resolve_failed(&action)
|
103
|
+
@klass.send :define_method, :on_resolve_failed, &action
|
104
|
+
end
|
105
|
+
|
106
|
+
# Declare the on_close callback
|
107
|
+
def on_close(&action)
|
108
|
+
@klass.send :define_method, :on_close, &action
|
109
|
+
end
|
110
|
+
|
111
|
+
# Declare the on_read callback
|
112
|
+
def on_read(&action)
|
113
|
+
@klass.send :define_method, :on_read, &action
|
114
|
+
end
|
115
|
+
|
116
|
+
# Declare the on_write_complete callback
|
117
|
+
def on_write_complete(&action)
|
118
|
+
@klass.send :define_method, :on_write_complete, &action
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# The Cool module containing all our coolness
|
125
|
+
module Cool
|
126
|
+
module Coolness
|
127
|
+
def cool; Cool::IOThunk; end
|
128
|
+
end
|
129
|
+
|
130
|
+
module IOThunk
|
131
|
+
def self.io; Coolio::DSL; end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
extend Cool::Coolness
|