emdrb 0.1.0
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/COPYING +403 -0
- data/History.txt +3 -0
- data/LEGAL +14 -0
- data/Manifest.txt +23 -0
- data/README.txt +65 -0
- data/Rakefile +21 -0
- data/lib/emdrb.rb +23 -0
- data/lib/emdrb/emdrb.rb +404 -0
- data/lib/emdrb/version.rb +34 -0
- data/tasks/ann.rake +81 -0
- data/tasks/bones.rake +21 -0
- data/tasks/gem.rake +126 -0
- data/tasks/git.rake +41 -0
- data/tasks/manifest.rake +49 -0
- data/tasks/notes.rake +28 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +57 -0
- data/tasks/setup.rb +268 -0
- data/tasks/spec.rake +55 -0
- data/tasks/svn.rake +48 -0
- data/tasks/test.rake +38 -0
- data/test/test_emdrb.rb +0 -0
- metadata +77 -0
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# $Id: Rakefile 5 2008-10-23 09:31:35Z dido $
|
3
|
+
#
|
4
|
+
load 'tasks/setup.rb'
|
5
|
+
|
6
|
+
ensure_in_path 'lib'
|
7
|
+
require 'emdrb/version'
|
8
|
+
|
9
|
+
task :default => 'test:run'
|
10
|
+
|
11
|
+
PROJ.name = 'emdrb'
|
12
|
+
PROJ.authors = 'dido@imperium.ph'
|
13
|
+
PROJ.email = 'dido@imperium.ph'
|
14
|
+
PROJ.url = 'http://emdrb.rubyforge.org'
|
15
|
+
PROJ.rubyforge.name = 'emdrb'
|
16
|
+
PROJ.version = EMDRb::Version::STRING
|
17
|
+
PROJ.dependencies = ["eventmachine"]
|
18
|
+
|
19
|
+
PROJ.spec.opts << '--color'
|
20
|
+
|
21
|
+
# EOF
|
data/lib/emdrb.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
|
3
|
+
# Copyright:: Copyright (c) 2008 Rafael R. Sevilla
|
4
|
+
# Homepage:: http://emdrb.rubyforge.org/
|
5
|
+
# License:: GNU General Public License / Ruby License
|
6
|
+
#
|
7
|
+
# $Id: emdrb.rb 7 2008-10-23 09:36:42Z dido $
|
8
|
+
#
|
9
|
+
#----------------------------------------------------------------------------
|
10
|
+
#
|
11
|
+
# Copyright (C) 2008 Rafael Sevilla
|
12
|
+
# This file is part of EMDRb
|
13
|
+
#
|
14
|
+
# This program is free software; you can redistribute it and/or modify
|
15
|
+
# it under the terms of either: 1) the GNU General Public License
|
16
|
+
# as published by the Free Software Foundation; either version 2 of the
|
17
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
18
|
+
#
|
19
|
+
# See the file COPYING for complete licensing information.
|
20
|
+
#----------------------------------------------------------------------------
|
21
|
+
|
22
|
+
require 'emdrb/version'
|
23
|
+
require 'emdrb/emdrb'
|
data/lib/emdrb/emdrb.rb
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
|
3
|
+
# Copyright:: Copyright (c) 2008 Rafael R. Sevilla
|
4
|
+
# Homepage:: http://emdrb.rubyforge.org/
|
5
|
+
# License:: GNU General Public License / Ruby License
|
6
|
+
#
|
7
|
+
# $Id: emdrb.rb 10 2008-10-23 09:48:11Z dido $
|
8
|
+
#
|
9
|
+
#----------------------------------------------------------------------------
|
10
|
+
#
|
11
|
+
# Copyright (C) 2008 Rafael Sevilla
|
12
|
+
# This file is part of EMDRb
|
13
|
+
#
|
14
|
+
# This program is free software; you can redistribute it and/or modify
|
15
|
+
# it under the terms of either: 1) the GNU General Public License
|
16
|
+
# as published by the Free Software Foundation; either version 2 of the
|
17
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
18
|
+
#
|
19
|
+
# See the file COPYING for complete licensing information.
|
20
|
+
#----------------------------------------------------------------------------
|
21
|
+
#
|
22
|
+
require 'eventmachine'
|
23
|
+
require 'drb'
|
24
|
+
|
25
|
+
module EMDRb
|
26
|
+
DEFAULT_ARGC_LIMIT = 256
|
27
|
+
DEFAULT_LOAD_LIMIT = 256 * 102400
|
28
|
+
DEFAULT_SAFE_LEVEL = 0
|
29
|
+
|
30
|
+
##
|
31
|
+
# EventMachine server module for DRb.
|
32
|
+
#
|
33
|
+
module DRbServerProtocol
|
34
|
+
##
|
35
|
+
# The front object for this server connection.
|
36
|
+
attr_accessor :front
|
37
|
+
##
|
38
|
+
# The load limit for this server connection. Serialized objects
|
39
|
+
# larger than this value will be rejected by the server with a
|
40
|
+
# DRbConnError exception. NOTE: the Ruby standard DRb will not
|
41
|
+
# propagate this exception to the caller, but instead it will
|
42
|
+
# occur internally, and the caller will receive a connection reset
|
43
|
+
# as the server thread dies because there is no way for an exception
|
44
|
+
# handler to handle the exception. I don't think this is correct
|
45
|
+
# behavior, and EMDRb will instead propagate the error to the caller.
|
46
|
+
attr_accessor :load_limit
|
47
|
+
|
48
|
+
##
|
49
|
+
# The maximum number of arguments that are allowed for this
|
50
|
+
# server connection. Method calls to this server which have
|
51
|
+
# more arguments than this will result in an ArgumentError.
|
52
|
+
attr_accessor :argc_limit
|
53
|
+
|
54
|
+
##
|
55
|
+
# The ID-to-object mapping component mapping DRb object IDs to the
|
56
|
+
# objects they refer to.
|
57
|
+
attr_accessor :idconv
|
58
|
+
|
59
|
+
##
|
60
|
+
# The server object which created this connection.
|
61
|
+
attr_accessor :server
|
62
|
+
|
63
|
+
##
|
64
|
+
# The safe level for this connection.
|
65
|
+
attr_accessor :safe_level
|
66
|
+
|
67
|
+
##
|
68
|
+
# The post initialization process sets up the default load
|
69
|
+
# and argument length limits, the idconv object, the initial
|
70
|
+
# state of the message packet state machine, clear the
|
71
|
+
# message buffer, and empty the current request hash.
|
72
|
+
def post_init
|
73
|
+
@load_limit = DEFAULT_LOAD_LIMIT
|
74
|
+
@argc_limit = DEFAULT_ARGC_LIMIT
|
75
|
+
@safe_level = DEFAULT_SAFE_LEVEL
|
76
|
+
@idconv = DRb::DRbIdConv.new
|
77
|
+
@state = :ref
|
78
|
+
@msgbuffer = ""
|
79
|
+
@request = {}
|
80
|
+
@server = @argv = @argc = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Receive data from the caller. This basically receives packets
|
85
|
+
# containing objects marshalled using Ruby's Marshal::dump prefixed
|
86
|
+
# by a length. These objects are unmarshalled and processed by the
|
87
|
+
# internal object request state machine. If an error of any kind
|
88
|
+
# occurs herein, the exception is propagated to the caller.
|
89
|
+
def receive_data(data)
|
90
|
+
begin
|
91
|
+
@msgbuffer << data
|
92
|
+
while @msgbuffer.length > 4
|
93
|
+
length = @msgbuffer.unpack("N")[0]
|
94
|
+
if length > @load_limit
|
95
|
+
raise DRb::DRbConnError, "too large packet #{length}"
|
96
|
+
end
|
97
|
+
|
98
|
+
if @msgbuffer.length < length - 4
|
99
|
+
# not enough data for this length, return to event loop
|
100
|
+
# to wait for more.
|
101
|
+
break
|
102
|
+
end
|
103
|
+
length, message, @msgbuffer = @msgbuffer.unpack("Na#{length}a*")
|
104
|
+
add_obj(obj_load(message))
|
105
|
+
end
|
106
|
+
rescue Exception => e
|
107
|
+
send_reply(false, e)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
##
|
114
|
+
# This method will dump an object +obj+ using Ruby's marshalling
|
115
|
+
# capabilities. It will make a proxy to the object instead if
|
116
|
+
# the object is undumpable. The dumps are basically data produced
|
117
|
+
# by Marshal::dump prefixed by a 32-bit length field in network
|
118
|
+
# byte order.
|
119
|
+
#
|
120
|
+
def dump(obj, error=false)
|
121
|
+
if obj.kind_of? DRb::DRbUndumped
|
122
|
+
obj = make_proxy(obj, error)
|
123
|
+
end
|
124
|
+
begin
|
125
|
+
str = Marshal::dump(obj)
|
126
|
+
rescue
|
127
|
+
str = Marshal::dump(make_proxy(obj, error))
|
128
|
+
end
|
129
|
+
return([str.size].pack("N") + str)
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Create a proxy for +obj+ that is declared to be undumpable.
|
134
|
+
#
|
135
|
+
def make_proxy(obj, error=false)
|
136
|
+
return(error ? Drb::DRbRemoteError.new(obj) : DRb::DRbObject.new(obj))
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Send a reply to the caller. The return value for distributed Ruby
|
141
|
+
# over the wire is the success as a boolean true or false value, followed
|
142
|
+
# by a dump of the data.
|
143
|
+
#
|
144
|
+
def send_reply(succ, result)
|
145
|
+
send_data(dump(succ) + dump(result, !succ))
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# This method will perform a method action if a block is not specified.
|
150
|
+
#
|
151
|
+
def perform_without_block
|
152
|
+
if Proc == @front && @request[:msg] == :__drb_yield
|
153
|
+
ary = (@request[:argv].size == 1) ? @request[:argv] : [@request[:argv]]
|
154
|
+
return(ary.collect(&@front)[0])
|
155
|
+
end
|
156
|
+
return(@front.__send__(@request[:msg], *@request[:argv]))
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# block_yield method lifted almost verbatim from InvokeMethod18Mixin
|
161
|
+
# from the standard distributed Ruby. Obviously, since EventMachine
|
162
|
+
# doesn't work with Ruby 1.6.x, we don't care about the 1.6 version...
|
163
|
+
#
|
164
|
+
def block_yield(x)
|
165
|
+
if x.size == 1 && x[0].class == Array
|
166
|
+
x[0] = DRb::DRbArray.new(x[0])
|
167
|
+
end
|
168
|
+
block_value = @request[:block].call(*x)
|
169
|
+
end
|
170
|
+
|
171
|
+
##
|
172
|
+
# Perform with a method action with a specified block.
|
173
|
+
#
|
174
|
+
def perform_with_block
|
175
|
+
@front.__send__(@request[:msg], *@request[:argv]) do |*x|
|
176
|
+
jump_error = nil
|
177
|
+
begin
|
178
|
+
block_value = block_yield(x)
|
179
|
+
rescue LocalJumpError
|
180
|
+
jump_error = $!
|
181
|
+
end
|
182
|
+
if jump_error
|
183
|
+
case jump_error.reason
|
184
|
+
when :retry
|
185
|
+
retry
|
186
|
+
when :break
|
187
|
+
break(jump_error.exit_value)
|
188
|
+
else
|
189
|
+
raise jump_error
|
190
|
+
end
|
191
|
+
end
|
192
|
+
block_value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Perform a method action. This handles the safe level invocations.
|
198
|
+
#
|
199
|
+
def perform
|
200
|
+
result = nil
|
201
|
+
succ = false
|
202
|
+
begin
|
203
|
+
@server.check_insecure_method(@front, @request[:msg])
|
204
|
+
if $SAFE < @safe_level
|
205
|
+
info = Thread.current['DRb']
|
206
|
+
result = Thread.new {
|
207
|
+
Thread.current['DRb'] = info
|
208
|
+
$SAFE = @safe_level
|
209
|
+
(@request[:block]) ? perform_with_block : perform_without_block
|
210
|
+
}.value
|
211
|
+
else
|
212
|
+
result = (@request[:block]) ? perform_with_block :
|
213
|
+
perform_without_block
|
214
|
+
succ = true
|
215
|
+
if @request[:msg] == :to_ary && result.class == Array
|
216
|
+
result = DRb::DRbArray.new(result)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
rescue StandardError, ScriptError, Interrupt
|
220
|
+
result = $!
|
221
|
+
end
|
222
|
+
return([succ, result])
|
223
|
+
end
|
224
|
+
|
225
|
+
def to_obj(ref)
|
226
|
+
if ref.nil?
|
227
|
+
return(@front)
|
228
|
+
end
|
229
|
+
return(@idconv.to_obj(ref))
|
230
|
+
end
|
231
|
+
|
232
|
+
def add_obj(obj)
|
233
|
+
@request[@state] = obj
|
234
|
+
case @state
|
235
|
+
when :ref
|
236
|
+
@request[:ro] = to_obj(@request[:ref])
|
237
|
+
@state = :msg
|
238
|
+
when :msg
|
239
|
+
@request[:msg] = @request[:msg].intern
|
240
|
+
@state = :argc
|
241
|
+
when :argc
|
242
|
+
@argc = @request[:argc]
|
243
|
+
if @argc_limit < @argc
|
244
|
+
raise ArgumentError, 'too many arguments'
|
245
|
+
end
|
246
|
+
@argv = []
|
247
|
+
@state = (@argc == 0) ? :block : :argv
|
248
|
+
when :argv
|
249
|
+
@argv << @request[:argv]
|
250
|
+
@argc -= 1
|
251
|
+
if @argc <= 1
|
252
|
+
@state = :block
|
253
|
+
end
|
254
|
+
when :block
|
255
|
+
@request[:argv] = @argv
|
256
|
+
@state = :ref
|
257
|
+
send_reply(*perform)
|
258
|
+
@request = {}
|
259
|
+
@argc = @argv = nil
|
260
|
+
else
|
261
|
+
@state = :ref
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
##
|
266
|
+
# Load a serialized object.
|
267
|
+
def obj_load(message)
|
268
|
+
begin
|
269
|
+
return(Marshal::load(message))
|
270
|
+
rescue NameError, ArgumentError
|
271
|
+
return(DRb::DRbUnknown.new($!, message))
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
class DRbServer < DRb::DRbServer
|
278
|
+
def initialize(uri=nil, front=nil, config_or_acl=nil)
|
279
|
+
if Hash === config_or_acl
|
280
|
+
config = config_or_acl.dup
|
281
|
+
else
|
282
|
+
acl = config_or_acl || @@acl
|
283
|
+
config = {
|
284
|
+
:tcp_acl => acl
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
@uri = (uri.nil?) ? "druby://:0" : uri
|
289
|
+
@config = self.class.make_config(config)
|
290
|
+
@front = front
|
291
|
+
@idconv = @config[:idconv]
|
292
|
+
@safe_level = @config[:safe_level]
|
293
|
+
@thread = run
|
294
|
+
EMDRb.regist_server(self)
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
def self.host_inaddr_any
|
300
|
+
host = Socket::gethostname
|
301
|
+
begin
|
302
|
+
host = Socket::gethostbyname(host)[0]
|
303
|
+
rescue
|
304
|
+
host = "localhost"
|
305
|
+
end
|
306
|
+
infos = Socket::getaddrinfo(host, nil,
|
307
|
+
Socket::AF_UNSPEC,
|
308
|
+
Socket::SOCK_STREAM,
|
309
|
+
0,
|
310
|
+
Socket::AI_PASSIVE)
|
311
|
+
family = infos.collect { |af, *_| af }.uniq
|
312
|
+
case family
|
313
|
+
when ['AF_INET']
|
314
|
+
return("0.0.0.0")
|
315
|
+
when ['AF_INET6']
|
316
|
+
return("::")
|
317
|
+
else
|
318
|
+
raise "Unknown network class"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def run
|
323
|
+
Thread.start do
|
324
|
+
host, port, opt = EMDRb::parse_uri(@uri)
|
325
|
+
if host.size == 0
|
326
|
+
host = self.class.host_inaddr_any
|
327
|
+
end
|
328
|
+
EventMachine::run do
|
329
|
+
EventMachine::start_server(host, port, DRbServerProtocol) do |conn|
|
330
|
+
Thread.current['DRb'] = { 'client' => conn, 'server' => self }
|
331
|
+
conn.front = @front
|
332
|
+
conn.load_limit = @config[:load_limit]
|
333
|
+
conn.argc_limit = @config[:argc_limit]
|
334
|
+
conn.idconv = @config[:idconv]
|
335
|
+
conn.server = self
|
336
|
+
conn.safe_level = self.safe_level
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
def parse_uri(uri)
|
345
|
+
if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
|
346
|
+
host = $1
|
347
|
+
port = $2.to_i
|
348
|
+
option = $4
|
349
|
+
[host, port, option]
|
350
|
+
else
|
351
|
+
raise(DRb::DRbBadScheme, uri) unless uri =~ /^druby:/
|
352
|
+
raise(DRb::DRbBadURI, 'can\'t parse uri:' + uri)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
module_function :parse_uri
|
356
|
+
|
357
|
+
@primary_server = nil
|
358
|
+
|
359
|
+
def start_service(uri=nil, front=nil, config=nil)
|
360
|
+
@primary_server = DRbServer.new(uri, front, config)
|
361
|
+
end
|
362
|
+
module_function :start_service
|
363
|
+
|
364
|
+
attr_accessor :primary_server
|
365
|
+
module_function :primary_server=, :primary_server
|
366
|
+
|
367
|
+
@server = {}
|
368
|
+
def regist_server(server)
|
369
|
+
@server[server.uri] = server
|
370
|
+
Thread.exclusive do
|
371
|
+
@primary_server = server unless @primary_server
|
372
|
+
end
|
373
|
+
end
|
374
|
+
module_function :regist_server
|
375
|
+
|
376
|
+
##
|
377
|
+
# Get the 'current' server.
|
378
|
+
#
|
379
|
+
# In the context of execution taking place within the main
|
380
|
+
# thread of a dRuby server (typically, as a result of a remote
|
381
|
+
# call on the server or one of its objects), the current
|
382
|
+
# server is that server. Otherwise, the current server is
|
383
|
+
# the primary server.
|
384
|
+
#
|
385
|
+
# If the above rule fails to find a server, a DRbServerNotFound
|
386
|
+
# error is raised.
|
387
|
+
def current_server
|
388
|
+
drb = Thread.current['DRb']
|
389
|
+
server = (drb && drb['server']) ? drb['server'] : @primary_server
|
390
|
+
raise DRb::DRbServerNotFound unless server
|
391
|
+
return server
|
392
|
+
end
|
393
|
+
module_function :current_server
|
394
|
+
|
395
|
+
##
|
396
|
+
# Get the thread of the primary server.
|
397
|
+
#
|
398
|
+
# This returns nil if there is no primary server. See #primary_server.
|
399
|
+
def thread
|
400
|
+
@primary_server ? @primary_server.thread : nil
|
401
|
+
end
|
402
|
+
module_function :thread
|
403
|
+
|
404
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
|
3
|
+
# Copyright:: Copyright (c) 2008 Rafael R. Sevilla
|
4
|
+
# Homepage:: http://emdrb.rubyforge.org/
|
5
|
+
# License:: GNU Lesser General Public License / Ruby License
|
6
|
+
#
|
7
|
+
# $Id: version.rb 4 2008-10-23 09:29:31Z dido $
|
8
|
+
#
|
9
|
+
#----------------------------------------------------------------------------
|
10
|
+
#
|
11
|
+
# Copyright (C) 2008 Rafael Sevilla
|
12
|
+
# This file is part of EMDRb
|
13
|
+
#
|
14
|
+
# This program is free software; you can redistribute it and/or modify
|
15
|
+
# it under the terms of either: 1) the GNU General Public License
|
16
|
+
# as published by the Free Software Foundation; either version 2 of the
|
17
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
18
|
+
#
|
19
|
+
# See the file COPYING for complete licensing information.
|
20
|
+
#----------------------------------------------------------------------------
|
21
|
+
#
|
22
|
+
# EMDRb version code
|
23
|
+
#
|
24
|
+
module EMDRb
|
25
|
+
module Version
|
26
|
+
|
27
|
+
MAJOR = 0
|
28
|
+
MINOR = 1
|
29
|
+
TINY = 0
|
30
|
+
|
31
|
+
# The version of EMDRb in use.
|
32
|
+
STRING = [ MAJOR, MINOR, TINY ].join(".")
|
33
|
+
end
|
34
|
+
end
|