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.
@@ -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
@@ -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'
@@ -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