eventmachine 1.2.0.dev.2-x64-mingw32
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +105 -0
- data/GNU +281 -0
- data/LICENSE +60 -0
- data/README.md +108 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/old/ChangeLog +211 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/old/EPOLL +141 -0
- data/docs/old/INSTALL +13 -0
- data/docs/old/KEYBOARD +42 -0
- data/docs/old/LEGAL +25 -0
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/PURE_RUBY +75 -0
- data/docs/old/RELEASE_NOTES +94 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/docs/old/TODO +8 -0
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/old/ex_channel.rb +43 -0
- data/examples/old/ex_queue.rb +2 -0
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/examples/old/helper.rb +2 -0
- data/ext/binder.cpp +124 -0
- data/ext/binder.h +46 -0
- data/ext/cmain.cpp +988 -0
- data/ext/ed.cpp +2111 -0
- data/ext/ed.h +442 -0
- data/ext/em.cpp +2379 -0
- data/ext/em.h +308 -0
- data/ext/eventmachine.h +143 -0
- data/ext/extconf.rb +270 -0
- data/ext/fastfilereader/extconf.rb +110 -0
- data/ext/fastfilereader/mapper.cpp +216 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +127 -0
- data/ext/kb.cpp +79 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +354 -0
- data/ext/project.h +176 -0
- data/ext/rubymain.cpp +1504 -0
- data/ext/ssl.cpp +615 -0
- data/ext/ssl.h +103 -0
- data/java/.classpath +8 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +591 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
- data/lib/2.0/fastfilereaderext.so +0 -0
- data/lib/2.0/rubyeventmachine.so +0 -0
- data/lib/2.1/fastfilereaderext.so +0 -0
- data/lib/2.1/rubyeventmachine.so +0 -0
- data/lib/2.2/fastfilereaderext.so +0 -0
- data/lib/2.2/rubyeventmachine.so +0 -0
- data/lib/2.3/fastfilereaderext.so +0 -0
- data/lib/2.3/rubyeventmachine.so +0 -0
- data/lib/em/buftok.rb +59 -0
- data/lib/em/callback.rb +58 -0
- data/lib/em/channel.rb +69 -0
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +770 -0
- data/lib/em/deferrable.rb +210 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +73 -0
- data/lib/em/future.rb +61 -0
- data/lib/em/iterator.rb +252 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/pool.rb +151 -0
- data/lib/em/process_watch.rb +45 -0
- data/lib/em/processes.rb +123 -0
- data/lib/em/protocols.rb +37 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +299 -0
- data/lib/em/protocols/httpclient2.rb +600 -0
- data/lib/em/protocols/line_and_text.rb +125 -0
- data/lib/em/protocols/line_protocol.rb +29 -0
- data/lib/em/protocols/linetext2.rb +166 -0
- data/lib/em/protocols/memcache.rb +331 -0
- data/lib/em/protocols/object_protocol.rb +46 -0
- data/lib/em/protocols/postgres3.rb +246 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +394 -0
- data/lib/em/protocols/smtpserver.rb +666 -0
- data/lib/em/protocols/socks4.rb +66 -0
- data/lib/em/protocols/stomp.rb +205 -0
- data/lib/em/protocols/tcptest.rb +54 -0
- data/lib/em/pure_ruby.rb +1022 -0
- data/lib/em/queue.rb +80 -0
- data/lib/em/resolver.rb +232 -0
- data/lib/em/spawnable.rb +84 -0
- data/lib/em/streamer.rb +118 -0
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +61 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1584 -0
- data/lib/fastfilereaderext.rb +2 -0
- data/lib/jeventmachine.rb +301 -0
- data/lib/rubyeventmachine.rb +2 -0
- data/rakelib/package.rake +120 -0
- data/rakelib/test.rake +8 -0
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/dhparam.pem +13 -0
- data/tests/em_test_helper.rb +151 -0
- data/tests/test_attach.rb +151 -0
- data/tests/test_basic.rb +283 -0
- data/tests/test_channel.rb +75 -0
- data/tests/test_completion.rb +178 -0
- data/tests/test_connection_count.rb +54 -0
- data/tests/test_connection_write.rb +35 -0
- data/tests/test_defer.rb +35 -0
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +142 -0
- data/tests/test_error_handler.rb +38 -0
- data/tests/test_exc.rb +28 -0
- data/tests/test_file_watch.rb +66 -0
- data/tests/test_fork.rb +75 -0
- data/tests/test_futures.rb +170 -0
- data/tests/test_get_sock_opt.rb +37 -0
- data/tests/test_handler_check.rb +35 -0
- data/tests/test_hc.rb +155 -0
- data/tests/test_httpclient.rb +233 -0
- data/tests/test_httpclient2.rb +128 -0
- data/tests/test_idle_connection.rb +25 -0
- data/tests/test_inactivity_timeout.rb +54 -0
- data/tests/test_ipv4.rb +125 -0
- data/tests/test_ipv6.rb +131 -0
- data/tests/test_iterator.rb +115 -0
- data/tests/test_kb.rb +28 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_ltp.rb +138 -0
- data/tests/test_ltp2.rb +308 -0
- data/tests/test_many_fds.rb +22 -0
- data/tests/test_next_tick.rb +104 -0
- data/tests/test_object_protocol.rb +36 -0
- data/tests/test_pause.rb +107 -0
- data/tests/test_pending_connect_timeout.rb +52 -0
- data/tests/test_pool.rb +196 -0
- data/tests/test_process_watch.rb +50 -0
- data/tests/test_processes.rb +128 -0
- data/tests/test_proxy_connection.rb +180 -0
- data/tests/test_pure.rb +88 -0
- data/tests/test_queue.rb +64 -0
- data/tests/test_resolver.rb +104 -0
- data/tests/test_running.rb +14 -0
- data/tests/test_sasl.rb +47 -0
- data/tests/test_send_file.rb +217 -0
- data/tests/test_servers.rb +33 -0
- data/tests/test_set_sock_opt.rb +39 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +75 -0
- data/tests/test_smtpserver.rb +57 -0
- data/tests/test_spawn.rb +293 -0
- data/tests/test_ssl_args.rb +78 -0
- data/tests/test_ssl_dhparam.rb +83 -0
- data/tests/test_ssl_ecdh_curve.rb +79 -0
- data/tests/test_ssl_extensions.rb +49 -0
- data/tests/test_ssl_methods.rb +65 -0
- data/tests/test_ssl_protocols.rb +246 -0
- data/tests/test_ssl_verify.rb +126 -0
- data/tests/test_stomp.rb +37 -0
- data/tests/test_system.rb +46 -0
- data/tests/test_threaded_resource.rb +61 -0
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +123 -0
- data/tests/test_ud.rb +8 -0
- data/tests/test_unbind_reason.rb +52 -0
- metadata +381 -0
data/lib/em/queue.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module EventMachine
|
2
|
+
# A cross thread, reactor scheduled, linear queue.
|
3
|
+
#
|
4
|
+
# This class provides a simple queue abstraction on top of the reactor
|
5
|
+
# scheduler. It services two primary purposes:
|
6
|
+
#
|
7
|
+
# * API sugar for stateful protocols
|
8
|
+
# * Pushing processing onto the reactor thread
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# q = EM::Queue.new
|
13
|
+
# q.push('one', 'two', 'three')
|
14
|
+
# 3.times do
|
15
|
+
# q.pop { |msg| puts(msg) }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
class Queue
|
19
|
+
def initialize
|
20
|
+
@sink = []
|
21
|
+
@drain = []
|
22
|
+
@popq = []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Pop items off the queue, running the block on the reactor thread. The pop
|
26
|
+
# will not happen immediately, but at some point in the future, either in
|
27
|
+
# the next tick, if the queue has data, or when the queue is populated.
|
28
|
+
#
|
29
|
+
# @return [NilClass] nil
|
30
|
+
def pop(*a, &b)
|
31
|
+
cb = EM::Callback(*a, &b)
|
32
|
+
EM.schedule do
|
33
|
+
if @drain.empty?
|
34
|
+
@drain = @sink
|
35
|
+
@sink = []
|
36
|
+
end
|
37
|
+
if @drain.empty?
|
38
|
+
@popq << cb
|
39
|
+
else
|
40
|
+
cb.call @drain.shift
|
41
|
+
end
|
42
|
+
end
|
43
|
+
nil # Always returns nil
|
44
|
+
end
|
45
|
+
|
46
|
+
# Push items onto the queue in the reactor thread. The items will not appear
|
47
|
+
# in the queue immediately, but will be scheduled for addition during the
|
48
|
+
# next reactor tick.
|
49
|
+
def push(*items)
|
50
|
+
EM.schedule do
|
51
|
+
@sink.push(*items)
|
52
|
+
unless @popq.empty?
|
53
|
+
@drain = @sink
|
54
|
+
@sink = []
|
55
|
+
@popq.shift.call @drain.shift until @drain.empty? || @popq.empty?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
alias :<< :push
|
60
|
+
|
61
|
+
# @return [Boolean]
|
62
|
+
# @note This is a peek, it's not thread safe, and may only tend toward accuracy.
|
63
|
+
def empty?
|
64
|
+
@drain.empty? && @sink.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Integer] Queue size
|
68
|
+
# @note This is a peek, it's not thread safe, and may only tend toward accuracy.
|
69
|
+
def size
|
70
|
+
@drain.size + @sink.size
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Integer] Waiting size
|
74
|
+
# @note This is a peek at the number of jobs that are currently waiting on the Queue
|
75
|
+
def num_waiting
|
76
|
+
@popq.size
|
77
|
+
end
|
78
|
+
|
79
|
+
end # Queue
|
80
|
+
end # EventMachine
|
data/lib/em/resolver.rb
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module DNS
|
3
|
+
class Resolver
|
4
|
+
|
5
|
+
def self.windows?
|
6
|
+
if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
|
7
|
+
require 'win32/resolv'
|
8
|
+
true
|
9
|
+
else
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts'
|
15
|
+
|
16
|
+
@hosts = nil
|
17
|
+
@nameservers = nil
|
18
|
+
@socket = nil
|
19
|
+
|
20
|
+
def self.resolve(hostname)
|
21
|
+
Request.new(socket, hostname)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.socket
|
25
|
+
if @socket && @socket.error?
|
26
|
+
@socket = Socket.open
|
27
|
+
else
|
28
|
+
@socket ||= Socket.open
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.nameservers=(ns)
|
33
|
+
@nameservers = ns
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.nameservers
|
37
|
+
return @nameservers if @nameservers
|
38
|
+
|
39
|
+
if windows?
|
40
|
+
_, ns = Win32::Resolv.get_resolv_info
|
41
|
+
return @nameservers = ns || []
|
42
|
+
end
|
43
|
+
|
44
|
+
@nameservers = []
|
45
|
+
IO.readlines('/etc/resolv.conf').each do |line|
|
46
|
+
if line =~ /^nameserver (.+)$/
|
47
|
+
@nameservers << $1.split(/\s+/).first
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@nameservers
|
52
|
+
rescue
|
53
|
+
@nameservers = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.nameserver
|
57
|
+
nameservers.shuffle.first
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.hosts
|
61
|
+
return @hosts if @hosts
|
62
|
+
|
63
|
+
@hosts = {}
|
64
|
+
IO.readlines(HOSTS_FILE).each do |line|
|
65
|
+
next if line =~ /^#/
|
66
|
+
addr, host = line.split(/\s+/)
|
67
|
+
|
68
|
+
next unless addr && host
|
69
|
+
@hosts[host] ||= []
|
70
|
+
@hosts[host] << addr
|
71
|
+
end
|
72
|
+
|
73
|
+
@hosts
|
74
|
+
rescue
|
75
|
+
@hosts = {}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class RequestIdAlreadyUsed < RuntimeError; end
|
80
|
+
|
81
|
+
class Socket < EventMachine::Connection
|
82
|
+
def self.open
|
83
|
+
EventMachine::open_datagram_socket('0.0.0.0', 0, self)
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
@nameserver = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def post_init
|
91
|
+
@requests = {}
|
92
|
+
end
|
93
|
+
|
94
|
+
def start_timer
|
95
|
+
@timer ||= EM.add_periodic_timer(0.1, &method(:tick))
|
96
|
+
end
|
97
|
+
|
98
|
+
def stop_timer
|
99
|
+
EM.cancel_timer(@timer)
|
100
|
+
@timer = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def unbind
|
104
|
+
end
|
105
|
+
|
106
|
+
def tick
|
107
|
+
@requests.each do |id,req|
|
108
|
+
req.tick
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def register_request(id, req)
|
113
|
+
if @requests.has_key?(id)
|
114
|
+
raise RequestIdAlreadyUsed
|
115
|
+
else
|
116
|
+
@requests[id] = req
|
117
|
+
end
|
118
|
+
|
119
|
+
start_timer
|
120
|
+
end
|
121
|
+
|
122
|
+
def deregister_request(id, req)
|
123
|
+
@requests.delete(id)
|
124
|
+
stop_timer if @requests.length == 0
|
125
|
+
end
|
126
|
+
|
127
|
+
def send_packet(pkt)
|
128
|
+
send_datagram(pkt, nameserver, 53)
|
129
|
+
end
|
130
|
+
|
131
|
+
def nameserver=(ns)
|
132
|
+
@nameserver = ns
|
133
|
+
end
|
134
|
+
|
135
|
+
def nameserver
|
136
|
+
@nameserver || Resolver.nameserver
|
137
|
+
end
|
138
|
+
|
139
|
+
# Decodes the packet, looks for the request and passes the
|
140
|
+
# response over to the requester
|
141
|
+
def receive_data(data)
|
142
|
+
msg = nil
|
143
|
+
begin
|
144
|
+
msg = Resolv::DNS::Message.decode data
|
145
|
+
rescue
|
146
|
+
else
|
147
|
+
req = @requests[msg.id]
|
148
|
+
if req
|
149
|
+
@requests.delete(msg.id)
|
150
|
+
stop_timer if @requests.length == 0
|
151
|
+
req.receive_answer(msg)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class Request
|
158
|
+
include Deferrable
|
159
|
+
attr_accessor :retry_interval, :max_tries
|
160
|
+
|
161
|
+
def initialize(socket, hostname)
|
162
|
+
@socket = socket
|
163
|
+
@hostname = hostname
|
164
|
+
@tries = 0
|
165
|
+
@last_send = Time.at(0)
|
166
|
+
@retry_interval = 3
|
167
|
+
@max_tries = 5
|
168
|
+
|
169
|
+
if addrs = Resolver.hosts[hostname]
|
170
|
+
succeed addrs
|
171
|
+
else
|
172
|
+
EM.next_tick { tick }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def tick
|
177
|
+
# Break early if nothing to do
|
178
|
+
return if @last_send + @retry_interval > Time.now
|
179
|
+
if @tries < @max_tries
|
180
|
+
send
|
181
|
+
else
|
182
|
+
@socket.deregister_request(@id, self)
|
183
|
+
fail 'retries exceeded'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def receive_answer(msg)
|
188
|
+
addrs = []
|
189
|
+
msg.each_answer do |name,ttl,data|
|
190
|
+
if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
|
191
|
+
data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
|
192
|
+
addrs << data.address.to_s
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
if addrs.empty?
|
197
|
+
fail "rcode=#{msg.rcode}"
|
198
|
+
else
|
199
|
+
succeed addrs
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def send
|
206
|
+
@tries += 1
|
207
|
+
@last_send = Time.now
|
208
|
+
@socket.send_packet(packet.encode)
|
209
|
+
end
|
210
|
+
|
211
|
+
def id
|
212
|
+
begin
|
213
|
+
@id = rand(65535)
|
214
|
+
@socket.register_request(@id, self)
|
215
|
+
rescue RequestIdAlreadyUsed
|
216
|
+
retry
|
217
|
+
end unless defined?(@id)
|
218
|
+
|
219
|
+
@id
|
220
|
+
end
|
221
|
+
|
222
|
+
def packet
|
223
|
+
msg = Resolv::DNS::Message.new
|
224
|
+
msg.id = id
|
225
|
+
msg.rd = 1
|
226
|
+
msg.add_question @hostname, Resolv::DNS::Resource::IN::A
|
227
|
+
msg
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
data/lib/em/spawnable.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 25 Aug 2007
|
6
|
+
#
|
7
|
+
# See EventMachine and EventMachine::Connection for documentation and
|
8
|
+
# usage examples.
|
9
|
+
#
|
10
|
+
#----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
|
+
# Gmail: blackhedd
|
14
|
+
#
|
15
|
+
# This program is free software; you can redistribute it and/or modify
|
16
|
+
# it under the terms of either: 1) the GNU General Public License
|
17
|
+
# as published by the Free Software Foundation; either version 2 of the
|
18
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
+
#
|
20
|
+
# See the file COPYING for complete licensing information.
|
21
|
+
#
|
22
|
+
#---------------------------------------------------------------------------
|
23
|
+
#
|
24
|
+
#
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
# Support for Erlang-style processes.
|
28
|
+
#
|
29
|
+
class SpawnedProcess
|
30
|
+
# Send a message to the spawned process
|
31
|
+
def notify *x
|
32
|
+
me = self
|
33
|
+
EM.next_tick {
|
34
|
+
# A notification executes in the context of this
|
35
|
+
# SpawnedProcess object. That makes self and notify
|
36
|
+
# work as one would expect.
|
37
|
+
#
|
38
|
+
y = me.call(*x)
|
39
|
+
if y and y.respond_to?(:pull_out_yield_block)
|
40
|
+
a,b = y.pull_out_yield_block
|
41
|
+
set_receiver a
|
42
|
+
self.notify if b
|
43
|
+
end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
alias_method :resume, :notify
|
47
|
+
alias_method :run, :notify # for formulations like (EM.spawn {xxx}).run
|
48
|
+
|
49
|
+
def set_receiver blk
|
50
|
+
(class << self ; self ; end).class_eval do
|
51
|
+
remove_method :call if method_defined? :call
|
52
|
+
define_method :call, blk
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
# @private
|
59
|
+
class YieldBlockFromSpawnedProcess
|
60
|
+
def initialize block, notify
|
61
|
+
@block = [block,notify]
|
62
|
+
end
|
63
|
+
def pull_out_yield_block
|
64
|
+
@block
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Spawn an erlang-style process
|
69
|
+
def self.spawn &block
|
70
|
+
s = SpawnedProcess.new
|
71
|
+
s.set_receiver block
|
72
|
+
s
|
73
|
+
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
def self.yield &block
|
77
|
+
return YieldBlockFromSpawnedProcess.new( block, false )
|
78
|
+
end
|
79
|
+
|
80
|
+
# @private
|
81
|
+
def self.yield_and_notify &block
|
82
|
+
return YieldBlockFromSpawnedProcess.new( block, true )
|
83
|
+
end
|
84
|
+
end
|
data/lib/em/streamer.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
module EventMachine
|
2
|
+
# Streams a file over a given connection. Streaming begins once the object is
|
3
|
+
# instantiated. Typically FileStreamer instances are not reused.
|
4
|
+
#
|
5
|
+
# Streaming uses buffering for files larger than 16K and uses so-called fast file reader (a C++ extension)
|
6
|
+
# if available (it is part of eventmachine gem itself).
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# module FileSender
|
11
|
+
# def post_init
|
12
|
+
# streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar')
|
13
|
+
# streamer.callback{
|
14
|
+
# # file was sent successfully
|
15
|
+
# close_connection_after_writing
|
16
|
+
# }
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
#
|
21
|
+
# @author Francis Cianfrocca
|
22
|
+
class FileStreamer
|
23
|
+
include Deferrable
|
24
|
+
|
25
|
+
# Use mapped streamer for files bigger than 16k
|
26
|
+
MappingThreshold = 16384
|
27
|
+
# Wait until next tick to send more data when 50k is still in the outgoing buffer
|
28
|
+
BackpressureLevel = 50000
|
29
|
+
# Send 16k chunks at a time
|
30
|
+
ChunkSize = 16384
|
31
|
+
|
32
|
+
# @param [EventMachine::Connection] connection
|
33
|
+
# @param [String] filename File path
|
34
|
+
#
|
35
|
+
# @option args [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics.
|
36
|
+
def initialize connection, filename, args = {}
|
37
|
+
@connection = connection
|
38
|
+
@http_chunks = args[:http_chunks]
|
39
|
+
|
40
|
+
if File.exist?(filename)
|
41
|
+
@size = File.size(filename)
|
42
|
+
if @size <= MappingThreshold
|
43
|
+
stream_without_mapping filename
|
44
|
+
else
|
45
|
+
stream_with_mapping filename
|
46
|
+
end
|
47
|
+
else
|
48
|
+
fail "file not found"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @private
|
53
|
+
def stream_without_mapping filename
|
54
|
+
if @http_chunks
|
55
|
+
@connection.send_data "#{@size.to_s(16)}\r\n"
|
56
|
+
@connection.send_file_data filename
|
57
|
+
@connection.send_data "\r\n0\r\n\r\n"
|
58
|
+
else
|
59
|
+
@connection.send_file_data filename
|
60
|
+
end
|
61
|
+
succeed
|
62
|
+
end
|
63
|
+
private :stream_without_mapping
|
64
|
+
|
65
|
+
# @private
|
66
|
+
def stream_with_mapping filename
|
67
|
+
ensure_mapping_extension_is_present
|
68
|
+
|
69
|
+
@position = 0
|
70
|
+
@mapping = EventMachine::FastFileReader::Mapper.new filename
|
71
|
+
stream_one_chunk
|
72
|
+
end
|
73
|
+
private :stream_with_mapping
|
74
|
+
|
75
|
+
# Used internally to stream one chunk at a time over multiple reactor ticks
|
76
|
+
# @private
|
77
|
+
def stream_one_chunk
|
78
|
+
loop {
|
79
|
+
if @position < @size
|
80
|
+
if @connection.get_outbound_data_size > BackpressureLevel
|
81
|
+
EventMachine::next_tick {stream_one_chunk}
|
82
|
+
break
|
83
|
+
else
|
84
|
+
len = @size - @position
|
85
|
+
len = ChunkSize if (len > ChunkSize)
|
86
|
+
|
87
|
+
@connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks
|
88
|
+
@connection.send_data( @mapping.get_chunk( @position, len ))
|
89
|
+
@connection.send_data("\r\n") if @http_chunks
|
90
|
+
|
91
|
+
@position += len
|
92
|
+
end
|
93
|
+
else
|
94
|
+
@connection.send_data "0\r\n\r\n" if @http_chunks
|
95
|
+
@mapping.close
|
96
|
+
succeed
|
97
|
+
break
|
98
|
+
end
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# We use an outboard extension class to get memory-mapped files.
|
104
|
+
# It's outboard to avoid polluting the core distro, but that means
|
105
|
+
# there's a "hidden" dependency on it. The first time we get here in
|
106
|
+
# any run, try to load up the dependency extension. User code will see
|
107
|
+
# a LoadError if it's not available, but code that doesn't require
|
108
|
+
# mapped files will work fine without it. This is a somewhat difficult
|
109
|
+
# compromise between usability and proper modularization.
|
110
|
+
#
|
111
|
+
# @private
|
112
|
+
def ensure_mapping_extension_is_present
|
113
|
+
@@fastfilereader ||= (require 'fastfilereaderext')
|
114
|
+
end
|
115
|
+
private :ensure_mapping_extension_is_present
|
116
|
+
|
117
|
+
end # FileStreamer
|
118
|
+
end # EventMachine
|