rubysl-drb 1.0.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.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/drb.rb +1 -0
  9. data/lib/drb/acl.rb +146 -0
  10. data/lib/drb/drb.rb +1783 -0
  11. data/lib/drb/eq.rb +16 -0
  12. data/lib/drb/extserv.rb +64 -0
  13. data/lib/drb/extservm.rb +89 -0
  14. data/lib/drb/gw.rb +122 -0
  15. data/lib/drb/invokemethod.rb +34 -0
  16. data/lib/drb/observer.rb +22 -0
  17. data/lib/drb/ssl.rb +190 -0
  18. data/lib/drb/timeridconv.rb +91 -0
  19. data/lib/drb/unix.rb +108 -0
  20. data/lib/rubysl/drb.rb +2 -0
  21. data/lib/rubysl/drb/drb.rb +2 -0
  22. data/lib/rubysl/drb/version.rb +5 -0
  23. data/rubysl-drb.gemspec +23 -0
  24. data/spec/config_spec.rb +4 -0
  25. data/spec/current_server_spec.rb +4 -0
  26. data/spec/drbarray/_dump_spec.rb +6 -0
  27. data/spec/drbarray/_load_spec.rb +6 -0
  28. data/spec/drbconn/alive_spec.rb +6 -0
  29. data/spec/drbconn/close_spec.rb +6 -0
  30. data/spec/drbconn/open_spec.rb +6 -0
  31. data/spec/drbconn/send_message_spec.rb +6 -0
  32. data/spec/drbconn/uri_spec.rb +6 -0
  33. data/spec/drbidconv/to_id_spec.rb +6 -0
  34. data/spec/drbidconv/to_obj_spec.rb +6 -0
  35. data/spec/drbmessage/dump_spec.rb +6 -0
  36. data/spec/drbmessage/load_spec.rb +6 -0
  37. data/spec/drbmessage/recv_reply_spec.rb +6 -0
  38. data/spec/drbmessage/recv_request_spec.rb +6 -0
  39. data/spec/drbmessage/send_reply_spec.rb +6 -0
  40. data/spec/drbmessage/send_request_spec.rb +6 -0
  41. data/spec/drbobject/__drbref_spec.rb +4 -0
  42. data/spec/drbobject/__drburi_spec.rb +4 -0
  43. data/spec/drbobject/_dump_spec.rb +4 -0
  44. data/spec/drbobject/_load_spec.rb +4 -0
  45. data/spec/drbobject/eql_spec.rb +4 -0
  46. data/spec/drbobject/equal_value_spec.rb +4 -0
  47. data/spec/drbobject/hash_spec.rb +4 -0
  48. data/spec/drbobject/method_missing_spec.rb +4 -0
  49. data/spec/drbobject/new_spec.rb +0 -0
  50. data/spec/drbobject/new_with_spec.rb +4 -0
  51. data/spec/drbobject/new_with_uri_spec.rb +4 -0
  52. data/spec/drbobject/prepare_backtrace_spec.rb +4 -0
  53. data/spec/drbobject/pretty_print_cycle_spec.rb +4 -0
  54. data/spec/drbobject/pretty_print_spec.rb +4 -0
  55. data/spec/drbobject/respond_to_spec.rb +4 -0
  56. data/spec/drbobject/with_friend_spec.rb +4 -0
  57. data/spec/drbprotocol/add_protocol_spec.rb +6 -0
  58. data/spec/drbprotocol/auto_load_spec.rb +6 -0
  59. data/spec/drbprotocol/open_server_spec.rb +6 -0
  60. data/spec/drbprotocol/open_spec.rb +6 -0
  61. data/spec/drbprotocol/uri_option_spec.rb +6 -0
  62. data/spec/drbserver/alive_spec.rb +6 -0
  63. data/spec/drbserver/check_insecure_method_spec.rb +6 -0
  64. data/spec/drbserver/config_spec.rb +6 -0
  65. data/spec/drbserver/default_acl_spec.rb +6 -0
  66. data/spec/drbserver/default_argc_limit_spec.rb +6 -0
  67. data/spec/drbserver/default_id_conv_spec.rb +6 -0
  68. data/spec/drbserver/default_load_limit_spec.rb +6 -0
  69. data/spec/drbserver/default_safe_level_spec.rb +6 -0
  70. data/spec/drbserver/front_spec.rb +6 -0
  71. data/spec/drbserver/invokemethod/perform_spec.rb +6 -0
  72. data/spec/drbserver/invokemethod18mixin/block_yield_spec.rb +6 -0
  73. data/spec/drbserver/invokemethod18mixin/perform_with_block_spec.rb +6 -0
  74. data/spec/drbserver/make_config_spec.rb +6 -0
  75. data/spec/drbserver/safe_level_spec.rb +6 -0
  76. data/spec/drbserver/stop_service_spec.rb +6 -0
  77. data/spec/drbserver/thread_spec.rb +6 -0
  78. data/spec/drbserver/to_id_spec.rb +6 -0
  79. data/spec/drbserver/to_obj_spec.rb +6 -0
  80. data/spec/drbserver/uri_spec.rb +6 -0
  81. data/spec/drbserver/verbose_spec.rb +18 -0
  82. data/spec/drbtcpsocket/accept_spec.rb +6 -0
  83. data/spec/drbtcpsocket/alive_spec.rb +6 -0
  84. data/spec/drbtcpsocket/close_spec.rb +6 -0
  85. data/spec/drbtcpsocket/getservername_spec.rb +6 -0
  86. data/spec/drbtcpsocket/open_server_inaddr_any_spec.rb +6 -0
  87. data/spec/drbtcpsocket/open_server_spec.rb +6 -0
  88. data/spec/drbtcpsocket/open_spec.rb +6 -0
  89. data/spec/drbtcpsocket/parse_uri_spec.rb +6 -0
  90. data/spec/drbtcpsocket/peeraddr_spec.rb +6 -0
  91. data/spec/drbtcpsocket/recv_reply_spec.rb +6 -0
  92. data/spec/drbtcpsocket/recv_request_spec.rb +6 -0
  93. data/spec/drbtcpsocket/send_reply_spec.rb +6 -0
  94. data/spec/drbtcpsocket/send_request_spec.rb +6 -0
  95. data/spec/drbtcpsocket/set_sockopt_spec.rb +6 -0
  96. data/spec/drbtcpsocket/stream_spec.rb +6 -0
  97. data/spec/drbtcpsocket/uri_option_spec.rb +6 -0
  98. data/spec/drbtcpsocket/uri_spec.rb +6 -0
  99. data/spec/drbundumped/_dump_spec.rb +6 -0
  100. data/spec/drbunknown/_dump_spec.rb +6 -0
  101. data/spec/drbunknown/_load_spec.rb +6 -0
  102. data/spec/drbunknown/buf_spec.rb +6 -0
  103. data/spec/drbunknown/exception_spec.rb +6 -0
  104. data/spec/drbunknown/name_spec.rb +6 -0
  105. data/spec/drbunknown/reload_spec.rb +6 -0
  106. data/spec/drburioption/eql_spec.rb +6 -0
  107. data/spec/drburioption/equal_value_spec.rb +6 -0
  108. data/spec/drburioption/hash_spec.rb +6 -0
  109. data/spec/drburioption/option_spec.rb +6 -0
  110. data/spec/drburioption/to_s_spec.rb +6 -0
  111. data/spec/exception/_dump_spec.rb +6 -0
  112. data/spec/exception/_load_spec.rb +6 -0
  113. data/spec/exception/reason_spec.rb +6 -0
  114. data/spec/exception/unknown_spec.rb +6 -0
  115. data/spec/fetch_server_spec.rb +4 -0
  116. data/spec/fixtures/test_server.rb +8 -0
  117. data/spec/front_spec.rb +4 -0
  118. data/spec/here_spec.rb +4 -0
  119. data/spec/install_acl_spec.rb +4 -0
  120. data/spec/install_id_conv_spec.rb +4 -0
  121. data/spec/mutex_spec.rb +6 -0
  122. data/spec/primary_server_spec.rb +8 -0
  123. data/spec/regist_server_spec.rb +4 -0
  124. data/spec/remove_server_spec.rb +4 -0
  125. data/spec/start_service_spec.rb +36 -0
  126. data/spec/stop_service_spec.rb +24 -0
  127. data/spec/thread_spec.rb +4 -0
  128. data/spec/to_id_spec.rb +4 -0
  129. data/spec/to_obj_spec.rb +4 -0
  130. data/spec/uri_spec.rb +4 -0
  131. metadata +336 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd45867e7eb55a838219dc718a431d9214b7b00a
4
+ data.tar.gz: dae31b966a9a8f731ef99dc661aa8b4a30007c67
5
+ SHA512:
6
+ metadata.gz: e3347bd1ee0152ffb00cd9377f3084d1deecc716ea39edc679b2a6525844aafd10e0398247ab837e59d8336f0d185e5fba046c38d8233cb3d84e66d47055d1b6
7
+ data.tar.gz: 1a3439c7c928ded5c29ee8fced65a15b5edbe42a54a94df423cfcb1c38923a99f0bee1fd84d4edf952b9737e90454951844e64655094097442369d351f1ae9ec
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-drb.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Rubysl::Drb
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-drb'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-drb
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/drb.rb ADDED
@@ -0,0 +1 @@
1
+ require "rubysl/drb"
data/lib/drb/acl.rb ADDED
@@ -0,0 +1,146 @@
1
+ # acl-2.0 - simple Access Control List
2
+ #
3
+ # Copyright (c) 2000,2002,2003 Masatoshi SEKI
4
+ #
5
+ # acl.rb is copyrighted free software by Masatoshi SEKI.
6
+ # You can redistribute it and/or modify it under the same terms as Ruby.
7
+
8
+ require 'ipaddr'
9
+
10
+ class ACL
11
+ VERSION=["2.0.0"]
12
+ class ACLEntry
13
+ def initialize(str)
14
+ if str == '*' or str == 'all'
15
+ @pat = [:all]
16
+ elsif str.include?('*')
17
+ @pat = [:name, dot_pat(str)]
18
+ else
19
+ begin
20
+ @pat = [:ip, IPAddr.new(str)]
21
+ rescue ArgumentError
22
+ @pat = [:name, dot_pat(str)]
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+ def dot_pat_str(str)
29
+ list = str.split('.').collect { |s|
30
+ (s == '*') ? '.+' : s
31
+ }
32
+ list.join("\\.")
33
+ end
34
+
35
+ private
36
+ def dot_pat(str)
37
+ exp = "^" + dot_pat_str(str) + "$"
38
+ Regexp.new(exp)
39
+ end
40
+
41
+ public
42
+ def match(addr)
43
+ case @pat[0]
44
+ when :all
45
+ true
46
+ when :ip
47
+ begin
48
+ ipaddr = IPAddr.new(addr[3])
49
+ ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
50
+ rescue ArgumentError
51
+ return false
52
+ end
53
+ (@pat[1].include?(ipaddr)) ? true : false
54
+ when :name
55
+ (@pat[1] =~ addr[2]) ? true : false
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
61
+
62
+ class ACLList
63
+ def initialize
64
+ @list = []
65
+ end
66
+
67
+ public
68
+ def match(addr)
69
+ @list.each do |e|
70
+ return true if e.match(addr)
71
+ end
72
+ false
73
+ end
74
+
75
+ public
76
+ def add(str)
77
+ @list.push(ACLEntry.new(str))
78
+ end
79
+ end
80
+
81
+ DENY_ALLOW = 0
82
+ ALLOW_DENY = 1
83
+
84
+ def initialize(list=nil, order = DENY_ALLOW)
85
+ @order = order
86
+ @deny = ACLList.new
87
+ @allow = ACLList.new
88
+ install_list(list) if list
89
+ end
90
+
91
+ public
92
+ def allow_socket?(soc)
93
+ allow_addr?(soc.peeraddr)
94
+ end
95
+
96
+ public
97
+ def allow_addr?(addr)
98
+ case @order
99
+ when DENY_ALLOW
100
+ return true if @allow.match(addr)
101
+ return false if @deny.match(addr)
102
+ return true
103
+ when ALLOW_DENY
104
+ return false if @deny.match(addr)
105
+ return true if @allow.match(addr)
106
+ return false
107
+ else
108
+ false
109
+ end
110
+ end
111
+
112
+ public
113
+ def install_list(list)
114
+ i = 0
115
+ while i < list.size
116
+ permission, domain = list.slice(i,2)
117
+ case permission.downcase
118
+ when 'allow'
119
+ @allow.add(domain)
120
+ when 'deny'
121
+ @deny.add(domain)
122
+ else
123
+ raise "Invalid ACL entry #{list.to_s}"
124
+ end
125
+ i += 2
126
+ end
127
+ end
128
+ end
129
+
130
+ if __FILE__ == $0
131
+ # example
132
+ list = %w(deny all
133
+ allow 192.168.1.1
134
+ allow ::ffff:192.168.1.2
135
+ allow 192.168.1.3
136
+ )
137
+
138
+ addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
139
+
140
+ acl = ACL.new
141
+ p acl.allow_addr?(addr)
142
+
143
+ acl = ACL.new(list, ACL::DENY_ALLOW)
144
+ p acl.allow_addr?(addr)
145
+ end
146
+
data/lib/drb/drb.rb ADDED
@@ -0,0 +1,1783 @@
1
+ #
2
+ # = drb/drb.rb
3
+ #
4
+ # Distributed Ruby: _dRuby_ version 2.0.4
5
+ #
6
+ # Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
7
+ # modify it under the same terms as Ruby.
8
+ #
9
+ # Author:: Masatoshi SEKI
10
+ #
11
+ # Documentation:: William Webber (william@williamwebber.com)
12
+ #
13
+ # == Overview
14
+ #
15
+ # dRuby is a distributed object system for Ruby. It allows an object in one
16
+ # Ruby process to invoke methods on an object in another Ruby process on the
17
+ # same or a different machine.
18
+ #
19
+ # The Ruby standard library contains the core classes of the dRuby package.
20
+ # However, the full package also includes access control lists and the
21
+ # Rinda tuple-space distributed task management system, as well as a
22
+ # large number of samples. The full dRuby package can be downloaded from
23
+ # the dRuby home page (see *References*).
24
+ #
25
+ # For an introduction and examples of usage see the documentation to the
26
+ # DRb module.
27
+ #
28
+ # == References
29
+ #
30
+ # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
31
+ # The dRuby home page, in Japanese. Contains the full dRuby package
32
+ # and links to other Japanese-language sources.
33
+ #
34
+ # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
35
+ # The English version of the dRuby home page.
36
+ #
37
+ # [http://www.chadfowler.com/ruby/drb.html]
38
+ # A quick tutorial introduction to using dRuby by Chad Fowler.
39
+ #
40
+ # [http://www.linux-mag.com/2002-09/ruby_05.html]
41
+ # A tutorial introduction to dRuby in Linux Magazine by Dave Thomas.
42
+ # Includes a discussion of Rinda.
43
+ #
44
+ # [http://www.eng.cse.dmu.ac.uk/~hgs/ruby/dRuby/]
45
+ # Links to English-language Ruby material collected by Hugh Sasse.
46
+ #
47
+ # [http://www.rubycentral.com/book/ospace.html]
48
+ # The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
49
+ # which discusses dRuby.
50
+ #
51
+ # [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
52
+ # Translation of presentation on Ruby by Masatoshi Seki.
53
+
54
+ require 'socket'
55
+ require 'thread'
56
+ require 'fcntl'
57
+ require 'drb/eq'
58
+
59
+ #
60
+ # == Overview
61
+ #
62
+ # dRuby is a distributed object system for Ruby. It is written in
63
+ # pure Ruby and uses its own protocol. No add-in services are needed
64
+ # beyond those provided by the Ruby runtime, such as TCP sockets. It
65
+ # does not rely on or interoperate with other distributed object
66
+ # systems such as CORBA, RMI, or .NET.
67
+ #
68
+ # dRuby allows methods to be called in one Ruby process upon a Ruby
69
+ # object located in another Ruby process, even on another machine.
70
+ # References to objects can be passed between processes. Method
71
+ # arguments and return values are dumped and loaded in marshalled
72
+ # format. All of this is done transparently to both the caller of the
73
+ # remote method and the object that it is called upon.
74
+ #
75
+ # An object in a remote process is locally represented by a
76
+ # DRb::DRbObject instance. This acts as a sort of proxy for the
77
+ # remote object. Methods called upon this DRbObject instance are
78
+ # forwarded to its remote object. This is arranged dynamically at run
79
+ # time. There are no statically declared interfaces for remote
80
+ # objects, such as CORBA's IDL.
81
+ #
82
+ # dRuby calls made into a process are handled by a DRb::DRbServer
83
+ # instance within that process. This reconstitutes the method call,
84
+ # invokes it upon the specified local object, and returns the value to
85
+ # the remote caller. Any object can receive calls over dRuby. There
86
+ # is no need to implement a special interface, or mixin special
87
+ # functionality. Nor, in the general case, does an object need to
88
+ # explicitly register itself with a DRbServer in order to receive
89
+ # dRuby calls.
90
+ #
91
+ # One process wishing to make dRuby calls upon another process must
92
+ # somehow obtain an initial reference to an object in the remote
93
+ # process by some means other than as the return value of a remote
94
+ # method call, as there is initially no remote object reference it can
95
+ # invoke a method upon. This is done by attaching to the server by
96
+ # URI. Each DRbServer binds itself to a URI such as
97
+ # 'druby://example.com:8787'. A DRbServer can have an object attached
98
+ # to it that acts as the server's *front* *object*. A DRbObject can
99
+ # be explicitly created from the server's URI. This DRbObject's
100
+ # remote object will be the server's front object. This front object
101
+ # can then return references to other Ruby objects in the DRbServer's
102
+ # process.
103
+ #
104
+ # Method calls made over dRuby behave largely the same as normal Ruby
105
+ # method calls made within a process. Method calls with blocks are
106
+ # supported, as are raising exceptions. In addition to a method's
107
+ # standard errors, a dRuby call may also raise one of the
108
+ # dRuby-specific errors, all of which are subclasses of DRb::DRbError.
109
+ #
110
+ # Any type of object can be passed as an argument to a dRuby call or
111
+ # returned as its return value. By default, such objects are dumped
112
+ # or marshalled at the local end, then loaded or unmarshalled at the
113
+ # remote end. The remote end therefore receives a copy of the local
114
+ # object, not a distributed reference to it; methods invoked upon this
115
+ # copy are executed entirely in the remote process, not passed on to
116
+ # the local original. This has semantics similar to pass-by-value.
117
+ #
118
+ # However, if an object cannot be marshalled, a dRuby reference to it
119
+ # is passed or returned instead. This will turn up at the remote end
120
+ # as a DRbObject instance. All methods invoked upon this remote proxy
121
+ # are forwarded to the local object, as described in the discussion of
122
+ # DRbObjects. This has semantics similar to the normal Ruby
123
+ # pass-by-reference.
124
+ #
125
+ # The easiest way to signal that we want an otherwise marshallable
126
+ # object to be passed or returned as a DRbObject reference, rather
127
+ # than marshalled and sent as a copy, is to include the
128
+ # DRb::DRbUndumped mixin module.
129
+ #
130
+ # dRuby supports calling remote methods with blocks. As blocks (or
131
+ # rather the Proc objects that represent them) are not marshallable,
132
+ # the block executes in the local, not the remote, context. Each
133
+ # value yielded to the block is passed from the remote object to the
134
+ # local block, then the value returned by each block invocation is
135
+ # passed back to the remote execution context to be collected, before
136
+ # the collected values are finally returned to the local context as
137
+ # the return value of the method invocation.
138
+ #
139
+ # == Examples of usage
140
+ #
141
+ # For more dRuby samples, see the +samples+ directory in the full
142
+ # dRuby distribution.
143
+ #
144
+ # === dRuby in client/server mode
145
+ #
146
+ # This illustrates setting up a simple client-server drb
147
+ # system. Run the server and client code in different terminals,
148
+ # starting the server code first.
149
+ #
150
+ # ==== Server code
151
+ #
152
+ # require 'drb/drb'
153
+ #
154
+ # # The URI for the server to connect to
155
+ # URI="druby://localhost:8787"
156
+ #
157
+ # class TimeServer
158
+ #
159
+ # def get_current_time
160
+ # return Time.now
161
+ # end
162
+ #
163
+ # end
164
+ #
165
+ # # The object that handles requests on the server
166
+ # FRONT_OBJECT=TimeServer.new
167
+ #
168
+ # $SAFE = 1 # disable eval() and friends
169
+ #
170
+ # DRb.start_service(URI, FRONT_OBJECT)
171
+ # # Wait for the drb server thread to finish before exiting.
172
+ # DRb.thread.join
173
+ #
174
+ # ==== Client code
175
+ #
176
+ # require 'drb/drb'
177
+ #
178
+ # # The URI to connect to
179
+ # SERVER_URI="druby://localhost:8787"
180
+ #
181
+ # # Start a local DRbServer to handle callbacks.
182
+ # #
183
+ # # Not necessary for this small example, but will be required
184
+ # # as soon as we pass a non-marshallable object as an argument
185
+ # # to a dRuby call.
186
+ # DRb.start_service
187
+ #
188
+ # timeserver = DRbObject.new_with_uri(SERVER_URI)
189
+ # puts timeserver.get_current_time
190
+ #
191
+ # === Remote objects under dRuby
192
+ #
193
+ # This example illustrates returning a reference to an object
194
+ # from a dRuby call. The Logger instances live in the server
195
+ # process. References to them are returned to the client process,
196
+ # where methods can be invoked upon them. These methods are
197
+ # executed in the server process.
198
+ #
199
+ # ==== Server code
200
+ #
201
+ # require 'drb/drb'
202
+ #
203
+ # URI="druby://localhost:8787"
204
+ #
205
+ # class Logger
206
+ #
207
+ # # Make dRuby send Logger instances as dRuby references,
208
+ # # not copies.
209
+ # include DRb::DRbUndumped
210
+ #
211
+ # def initialize(n, fname)
212
+ # @name = n
213
+ # @filename = fname
214
+ # end
215
+ #
216
+ # def log(message)
217
+ # File.open(@filename, "a") do |f|
218
+ # f.puts("#{Time.now}: #{@name}: #{message}")
219
+ # end
220
+ # end
221
+ #
222
+ # end
223
+ #
224
+ # # We have a central object for creating and retrieving loggers.
225
+ # # This retains a local reference to all loggers created. This
226
+ # # is so an existing logger can be looked up by name, but also
227
+ # # to prevent loggers from being garbage collected. A dRuby
228
+ # # reference to an object is not sufficient to prevent it being
229
+ # # garbage collected!
230
+ # class LoggerFactory
231
+ #
232
+ # def initialize(bdir)
233
+ # @basedir = bdir
234
+ # @loggers = {}
235
+ # end
236
+ #
237
+ # def get_logger(name)
238
+ # if !@loggers.has_key? name
239
+ # # make the filename safe, then declare it to be so
240
+ # fname = name.gsub(/[.\/]/, "_").untaint
241
+ # @loggers[name] = Logger.new(name, @basedir + "/" + fname)
242
+ # end
243
+ # return @loggers[name]
244
+ # end
245
+ #
246
+ # end
247
+ #
248
+ # FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
249
+ #
250
+ # $SAFE = 1 # disable eval() and friends
251
+ #
252
+ # DRb.start_service(URI, FRONT_OBJECT)
253
+ # DRb.thread.join
254
+ #
255
+ # ==== Client code
256
+ #
257
+ # require 'drb/drb'
258
+ #
259
+ # SERVER_URI="druby://localhost:8787"
260
+ #
261
+ # DRb.start_service
262
+ #
263
+ # log_service=DRbObject.new_with_uri(SERVER_URI)
264
+ #
265
+ # ["loga", "logb", "logc"].each do |logname|
266
+ #
267
+ # logger=log_service.get_logger(logname)
268
+ #
269
+ # logger.log("Hello, world!")
270
+ # logger.log("Goodbye, world!")
271
+ # logger.log("=== EOT ===")
272
+ #
273
+ # end
274
+ #
275
+ # == Security
276
+ #
277
+ # As with all network services, security needs to be considered when
278
+ # using dRuby. By allowing external access to a Ruby object, you are
279
+ # not only allowing outside clients to call the methods you have
280
+ # defined for that object, but by default to execute arbitrary Ruby
281
+ # code on your server. Consider the following:
282
+ #
283
+ # # !!! UNSAFE CODE !!!
284
+ # ro = DRbObject::new_with_uri("druby://your.server.com:8989")
285
+ # class << ro
286
+ # undef :instance_eval # force call to be passed to remote object
287
+ # end
288
+ # ro.instance_eval("`rm -rf *`")
289
+ #
290
+ # The dangers posed by instance_eval and friends are such that a
291
+ # DRbServer should generally be run with $SAFE set to at least
292
+ # level 1. This will disable eval() and related calls on strings
293
+ # passed across the wire. The sample usage code given above follows
294
+ # this practice.
295
+ #
296
+ # A DRbServer can be configured with an access control list to
297
+ # selectively allow or deny access from specified IP addresses. The
298
+ # main druby distribution provides the ACL class for this purpose. In
299
+ # general, this mechanism should only be used alongside, rather than
300
+ # as a replacement for, a good firewall.
301
+ #
302
+ # == dRuby internals
303
+ #
304
+ # dRuby is implemented using three main components: a remote method
305
+ # call marshaller/unmarshaller; a transport protocol; and an
306
+ # ID-to-object mapper. The latter two can be directly, and the first
307
+ # indirectly, replaced, in order to provide different behaviour and
308
+ # capabilities.
309
+ #
310
+ # Marshalling and unmarshalling of remote method calls is performed by
311
+ # a DRb::DRbMessage instance. This uses the Marshal module to dump
312
+ # the method call before sending it over the transport layer, then
313
+ # reconstitute it at the other end. There is normally no need to
314
+ # replace this component, and no direct way is provided to do so.
315
+ # However, it is possible to implement an alternative marshalling
316
+ # scheme as part of an implementation of the transport layer.
317
+ #
318
+ # The transport layer is responsible for opening client and server
319
+ # network connections and forwarding dRuby request across them.
320
+ # Normally, it uses DRb::DRbMessage internally to manage marshalling
321
+ # and unmarshalling. The transport layer is managed by
322
+ # DRb::DRbProtocol. Multiple protocols can be installed in
323
+ # DRbProtocol at the one time; selection between them is determined by
324
+ # the scheme of a dRuby URI. The default transport protocol is
325
+ # selected by the scheme 'druby:', and implemented by
326
+ # DRb::DRbTCPSocket. This uses plain TCP/IP sockets for
327
+ # communication. An alternative protocol, using UNIX domain sockets,
328
+ # is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
329
+ # selected by the scheme 'drbunix:'. A sample implementation over
330
+ # HTTP can be found in the samples accompanying the main dRuby
331
+ # distribution.
332
+ #
333
+ # The ID-to-object mapping component maps dRuby object ids to the
334
+ # objects they refer to, and vice versa. The implementation to use
335
+ # can be specified as part of a DRb::DRbServer's configuration. The
336
+ # default implementation is provided by DRb::DRbIdConv. It uses an
337
+ # object's ObjectSpace id as its dRuby id. This means that the dRuby
338
+ # reference to that object only remains meaningful for the lifetime of
339
+ # the object's process and the lifetime of the object within that
340
+ # process. A modified implementation is provided by DRb::TimerIdConv
341
+ # in the file drb/timeridconv.rb. This implementation retains a local
342
+ # reference to all objects exported over dRuby for a configurable
343
+ # period of time (defaulting to ten minutes), to prevent them being
344
+ # garbage-collected within this time. Another sample implementation
345
+ # is provided in sample/name.rb in the main dRuby distribution. This
346
+ # allows objects to specify their own id or "name". A dRuby reference
347
+ # can be made persistent across processes by having each process
348
+ # register an object using the same dRuby name.
349
+ #
350
+ module DRb
351
+
352
+ # Superclass of all errors raised in the DRb module.
353
+ class DRbError < RuntimeError; end
354
+
355
+ # Error raised when an error occurs on the underlying communication
356
+ # protocol.
357
+ class DRbConnError < DRbError; end
358
+
359
+ # Class responsible for converting between an object and its id.
360
+ #
361
+ # This, the default implementation, uses an object's local ObjectSpace
362
+ # __id__ as its id. This means that an object's identification over
363
+ # drb remains valid only while that object instance remains alive
364
+ # within the server runtime.
365
+ #
366
+ # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
367
+ # and DRbNameIdConv in sample/name.rb in the full drb distribution.
368
+ class DRbIdConv
369
+
370
+ # Convert an object reference id to an object.
371
+ #
372
+ # This implementation looks up the reference id in the local object
373
+ # space and returns the object it refers to.
374
+ def to_obj(ref)
375
+ ObjectSpace._id2ref(ref)
376
+ end
377
+
378
+ # Convert an object into a reference id.
379
+ #
380
+ # This implementation returns the object's __id__ in the local
381
+ # object space.
382
+ def to_id(obj)
383
+ obj.nil? ? nil : obj.__id__
384
+ end
385
+ end
386
+
387
+ # Mixin module making an object undumpable or unmarshallable.
388
+ #
389
+ # If an object which includes this module is returned by method
390
+ # called over drb, then the object remains in the server space
391
+ # and a reference to the object is returned, rather than the
392
+ # object being marshalled and moved into the client space.
393
+ module DRbUndumped
394
+ def _dump(dummy) # :nodoc:
395
+ raise TypeError, 'can\'t dump'
396
+ end
397
+ end
398
+
399
+ # Error raised by the DRb module when an attempt is made to refer to
400
+ # the context's current drb server but the context does not have one.
401
+ # See #current_server.
402
+ class DRbServerNotFound < DRbError; end
403
+
404
+ # Error raised by the DRbProtocol module when it cannot find any
405
+ # protocol implementation support the scheme specified in a URI.
406
+ class DRbBadURI < DRbError; end
407
+
408
+ # Error raised by a dRuby protocol when it doesn't support the
409
+ # scheme specified in a URI. See DRb::DRbProtocol.
410
+ class DRbBadScheme < DRbError; end
411
+
412
+ # An exception wrapping a DRb::DRbUnknown object
413
+ class DRbUnknownError < DRbError
414
+
415
+ # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
416
+ def initialize(unknown)
417
+ @unknown = unknown
418
+ super(unknown.name)
419
+ end
420
+
421
+ # Get the wrapped DRb::DRbUnknown object.
422
+ attr_reader :unknown
423
+
424
+ def self._load(s) # :nodoc:
425
+ Marshal::load(s)
426
+ end
427
+
428
+ def _dump(lv) # :nodoc:
429
+ Marshal::dump(@unknown)
430
+ end
431
+ end
432
+
433
+ # An exception wrapping an error object
434
+ class DRbRemoteError < DRbError
435
+ def initialize(error)
436
+ @reason = error.class.to_s
437
+ super("#{error.message} (#{error.class})")
438
+ set_backtrace(error.backtrace)
439
+ end
440
+
441
+ # the class of the error, as a string.
442
+ attr_reader :reason
443
+ end
444
+
445
+ # Class wrapping a marshalled object whose type is unknown locally.
446
+ #
447
+ # If an object is returned by a method invoked over drb, but the
448
+ # class of the object is unknown in the client namespace, or
449
+ # the object is a constant unknown in the client namespace, then
450
+ # the still-marshalled object is returned wrapped in a DRbUnknown instance.
451
+ #
452
+ # If this object is passed as an argument to a method invoked over
453
+ # drb, then the wrapped object is passed instead.
454
+ #
455
+ # The class or constant name of the object can be read from the
456
+ # +name+ attribute. The marshalled object is held in the +buf+
457
+ # attribute.
458
+ class DRbUnknown
459
+
460
+ # Create a new DRbUnknown object.
461
+ #
462
+ # +buf+ is a string containing a marshalled object that could not
463
+ # be unmarshalled. +err+ is the error message that was raised
464
+ # when the unmarshalling failed. It is used to determine the
465
+ # name of the unmarshalled object.
466
+ def initialize(err, buf)
467
+ case err.to_s
468
+ when /uninitialized constant (\S+)/
469
+ @name = $1
470
+ when /undefined class\/module (\S+)/
471
+ @name = $1
472
+ else
473
+ @name = nil
474
+ end
475
+ @buf = buf
476
+ end
477
+
478
+ # The name of the unknown thing.
479
+ #
480
+ # Class name for unknown objects; variable name for unknown
481
+ # constants.
482
+ attr_reader :name
483
+
484
+ # Buffer contained the marshalled, unknown object.
485
+ attr_reader :buf
486
+
487
+ def self._load(s) # :nodoc:
488
+ begin
489
+ Marshal::load(s)
490
+ rescue NameError, ArgumentError
491
+ DRbUnknown.new($!, s)
492
+ end
493
+ end
494
+
495
+ def _dump(lv) # :nodoc:
496
+ @buf
497
+ end
498
+
499
+ # Attempt to load the wrapped marshalled object again.
500
+ #
501
+ # If the class of the object is now known locally, the object
502
+ # will be unmarshalled and returned. Otherwise, a new
503
+ # but identical DRbUnknown object will be returned.
504
+ def reload
505
+ self.class._load(@buf)
506
+ end
507
+
508
+ # Create a DRbUnknownError exception containing this object.
509
+ def exception
510
+ DRbUnknownError.new(self)
511
+ end
512
+ end
513
+
514
+ class DRbArray
515
+ def initialize(ary)
516
+ @ary = ary.collect { |obj|
517
+ if obj.kind_of? DRbUndumped
518
+ DRbObject.new(obj)
519
+ else
520
+ begin
521
+ Marshal.dump(obj)
522
+ obj
523
+ rescue
524
+ DRbObject.new(obj)
525
+ end
526
+ end
527
+ }
528
+ end
529
+
530
+ def self._load(s)
531
+ Marshal::load(s)
532
+ end
533
+
534
+ def _dump(lv)
535
+ Marshal.dump(@ary)
536
+ end
537
+ end
538
+
539
+ # Handler for sending and receiving drb messages.
540
+ #
541
+ # This takes care of the low-level marshalling and unmarshalling
542
+ # of drb requests and responses sent over the wire between server
543
+ # and client. This relieves the implementor of a new drb
544
+ # protocol layer with having to deal with these details.
545
+ #
546
+ # The user does not have to directly deal with this object in
547
+ # normal use.
548
+ class DRbMessage
549
+ def initialize(config) # :nodoc:
550
+ @load_limit = config[:load_limit]
551
+ @argc_limit = config[:argc_limit]
552
+ end
553
+
554
+ def dump(obj, error=false) # :nodoc:
555
+ obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
556
+ begin
557
+ str = Marshal::dump(obj)
558
+ rescue
559
+ str = Marshal::dump(make_proxy(obj, error))
560
+ end
561
+ [str.size].pack('N') + str
562
+ end
563
+
564
+ def load(soc) # :nodoc:
565
+ begin
566
+ sz = soc.read(4) # sizeof (N)
567
+ rescue
568
+ raise(DRbConnError, $!.message, $!.backtrace)
569
+ end
570
+ raise(DRbConnError, 'connection closed') if sz.nil?
571
+ raise(DRbConnError, 'premature header') if sz.size < 4
572
+ sz = sz.unpack('N')[0]
573
+ raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
574
+ begin
575
+ str = soc.read(sz)
576
+ rescue
577
+ raise(DRbConnError, $!.message, $!.backtrace)
578
+ end
579
+ raise(DRbConnError, 'connection closed') if str.nil?
580
+ raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
581
+ DRb.mutex.synchronize do
582
+ begin
583
+ save = Thread.current[:drb_untaint]
584
+ Thread.current[:drb_untaint] = []
585
+ Marshal::load(str)
586
+ rescue NameError, ArgumentError
587
+ DRbUnknown.new($!, str)
588
+ ensure
589
+ Thread.current[:drb_untaint].each do |x|
590
+ x.untaint
591
+ end
592
+ Thread.current[:drb_untaint] = save
593
+ end
594
+ end
595
+ end
596
+
597
+ def send_request(stream, ref, msg_id, arg, b) # :nodoc:
598
+ ary = []
599
+ ary.push(dump(ref.__drbref))
600
+ ary.push(dump(msg_id.id2name))
601
+ ary.push(dump(arg.length))
602
+ arg.each do |e|
603
+ ary.push(dump(e))
604
+ end
605
+ ary.push(dump(b))
606
+ stream.write(ary.join(''))
607
+ rescue
608
+ raise(DRbConnError, $!.message, $!.backtrace)
609
+ end
610
+
611
+ def recv_request(stream) # :nodoc:
612
+ ref = load(stream)
613
+ ro = DRb.to_obj(ref)
614
+ msg = load(stream)
615
+ argc = load(stream)
616
+ raise ArgumentError, 'too many arguments' if @argc_limit < argc
617
+ argv = Array.new(argc, nil)
618
+ argc.times do |n|
619
+ argv[n] = load(stream)
620
+ end
621
+ block = load(stream)
622
+ return ro, msg, argv, block
623
+ end
624
+
625
+ def send_reply(stream, succ, result) # :nodoc:
626
+ stream.write(dump(succ) + dump(result, !succ))
627
+ rescue
628
+ raise(DRbConnError, $!.message, $!.backtrace)
629
+ end
630
+
631
+ def recv_reply(stream) # :nodoc:
632
+ succ = load(stream)
633
+ result = load(stream)
634
+ [succ, result]
635
+ end
636
+
637
+ private
638
+ def make_proxy(obj, error=false)
639
+ if error
640
+ DRbRemoteError.new(obj)
641
+ else
642
+ DRbObject.new(obj)
643
+ end
644
+ end
645
+ end
646
+
647
+ # Module managing the underlying network protocol(s) used by drb.
648
+ #
649
+ # By default, drb uses the DRbTCPSocket protocol. Other protocols
650
+ # can be defined. A protocol must define the following class methods:
651
+ #
652
+ # [open(uri, config)] Open a client connection to the server at +uri+,
653
+ # using configuration +config+. Return a protocol
654
+ # instance for this connection.
655
+ # [open_server(uri, config)] Open a server listening at +uri+,
656
+ # using configuration +config+. Return a
657
+ # protocol instance for this listener.
658
+ # [uri_option(uri, config)] Take a URI, possibly containing an option
659
+ # component (e.g. a trailing '?param=val'),
660
+ # and return a [uri, option] tuple.
661
+ #
662
+ # All of these methods should raise a DRbBadScheme error if the URI
663
+ # does not identify the protocol they support (e.g. "druby:" for
664
+ # the standard Ruby protocol). This is how the DRbProtocol module,
665
+ # given a URI, determines which protocol implementation serves that
666
+ # protocol.
667
+ #
668
+ # The protocol instance returned by #open_server must have the
669
+ # following methods:
670
+ #
671
+ # [accept] Accept a new connection to the server. Returns a protocol
672
+ # instance capable of communicating with the client.
673
+ # [close] Close the server connection.
674
+ # [uri] Get the URI for this server.
675
+ #
676
+ # The protocol instance returned by #open must have the following methods:
677
+ #
678
+ # [send_request (ref, msg_id, arg, b)]
679
+ # Send a request to +ref+ with the given message id and arguments.
680
+ # This is most easily implemented by calling DRbMessage.send_request,
681
+ # providing a stream that sits on top of the current protocol.
682
+ # [recv_reply]
683
+ # Receive a reply from the server and return it as a [success-boolean,
684
+ # reply-value] pair. This is most easily implemented by calling
685
+ # DRb.recv_reply, providing a stream that sits on top of the
686
+ # current protocol.
687
+ # [alive?]
688
+ # Is this connection still alive?
689
+ # [close]
690
+ # Close this connection.
691
+ #
692
+ # The protocol instance returned by #open_server().accept() must have
693
+ # the following methods:
694
+ #
695
+ # [recv_request]
696
+ # Receive a request from the client and return a [object, message,
697
+ # args, block] tuple. This is most easily implemented by calling
698
+ # DRbMessage.recv_request, providing a stream that sits on top of
699
+ # the current protocol.
700
+ # [send_reply(succ, result)]
701
+ # Send a reply to the client. This is most easily implemented
702
+ # by calling DRbMessage.send_reply, providing a stream that sits
703
+ # on top of the current protocol.
704
+ # [close]
705
+ # Close this connection.
706
+ #
707
+ # A new protocol is registered with the DRbProtocol module using
708
+ # the add_protocol method.
709
+ #
710
+ # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
711
+ # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
712
+ # drb distribution.
713
+ module DRbProtocol
714
+
715
+ # Add a new protocol to the DRbProtocol module.
716
+ def add_protocol(prot)
717
+ @protocol.push(prot)
718
+ end
719
+ module_function :add_protocol
720
+
721
+ # Open a client connection to +uri+ with the configuration +config+.
722
+ #
723
+ # The DRbProtocol module asks each registered protocol in turn to
724
+ # try to open the URI. Each protocol signals that it does not handle that
725
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
726
+ # URI, then a DRbBadURI error is raised. If a protocol accepts the
727
+ # URI, but an error occurs in opening it, a DRbConnError is raised.
728
+ def open(uri, config, first=true)
729
+ @protocol.each do |prot|
730
+ begin
731
+ return prot.open(uri, config)
732
+ rescue DRbBadScheme
733
+ rescue DRbConnError
734
+ raise($!)
735
+ rescue
736
+ raise(DRbConnError, "#{uri} - #{$!.inspect}")
737
+ end
738
+ end
739
+ if first && (config[:auto_load] != false)
740
+ auto_load(uri, config)
741
+ return open(uri, config, false)
742
+ end
743
+ raise DRbBadURI, 'can\'t parse uri:' + uri
744
+ end
745
+ module_function :open
746
+
747
+ # Open a server listening for connections at +uri+ with
748
+ # configuration +config+.
749
+ #
750
+ # The DRbProtocol module asks each registered protocol in turn to
751
+ # try to open a server at the URI. Each protocol signals that it does
752
+ # not handle that URI by raising a DRbBadScheme error. If no protocol
753
+ # recognises the URI, then a DRbBadURI error is raised. If a protocol
754
+ # accepts the URI, but an error occurs in opening it, the underlying
755
+ # error is passed on to the caller.
756
+ def open_server(uri, config, first=true)
757
+ @protocol.each do |prot|
758
+ begin
759
+ return prot.open_server(uri, config)
760
+ rescue DRbBadScheme
761
+ end
762
+ end
763
+ if first && (config[:auto_load] != false)
764
+ auto_load(uri, config)
765
+ return open_server(uri, config, false)
766
+ end
767
+ raise DRbBadURI, 'can\'t parse uri:' + uri
768
+ end
769
+ module_function :open_server
770
+
771
+ # Parse +uri+ into a [uri, option] pair.
772
+ #
773
+ # The DRbProtocol module asks each registered protocol in turn to
774
+ # try to parse the URI. Each protocol signals that it does not handle that
775
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
776
+ # URI, then a DRbBadURI error is raised.
777
+ def uri_option(uri, config, first=true)
778
+ @protocol.each do |prot|
779
+ begin
780
+ uri, opt = prot.uri_option(uri, config)
781
+ # opt = nil if opt == ''
782
+ return uri, opt
783
+ rescue DRbBadScheme
784
+ end
785
+ end
786
+ if first && (config[:auto_load] != false)
787
+ auto_load(uri, config)
788
+ return uri_option(uri, config, false)
789
+ end
790
+ raise DRbBadURI, 'can\'t parse uri:' + uri
791
+ end
792
+ module_function :uri_option
793
+
794
+ def auto_load(uri, config) # :nodoc:
795
+ if uri =~ /^drb([a-z0-9]+):/
796
+ require("drb/#{$1}") rescue nil
797
+ end
798
+ end
799
+ module_function :auto_load
800
+ end
801
+
802
+ # The default drb protocol.
803
+ #
804
+ # Communicates over a TCP socket.
805
+ class DRbTCPSocket
806
+ private
807
+ def self.parse_uri(uri)
808
+ if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
809
+ host = $1
810
+ port = $2.to_i
811
+ option = $4
812
+ [host, port, option]
813
+ else
814
+ raise(DRbBadScheme, uri) unless uri =~ /^druby:/
815
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
816
+ end
817
+ end
818
+
819
+ public
820
+
821
+ # Open a client connection to +uri+ using configuration +config+.
822
+ def self.open(uri, config)
823
+ host, port, option = parse_uri(uri)
824
+ host.untaint
825
+ port.untaint
826
+ soc = TCPSocket.open(host, port)
827
+ self.new(uri, soc, config)
828
+ end
829
+
830
+ def self.getservername
831
+ host = Socket::gethostname
832
+ begin
833
+ Socket::gethostbyname(host)[0]
834
+ rescue
835
+ 'localhost'
836
+ end
837
+ end
838
+
839
+ def self.open_server_inaddr_any(host, port)
840
+ infos = Socket::getaddrinfo(host, nil,
841
+ Socket::AF_UNSPEC,
842
+ Socket::SOCK_STREAM,
843
+ 0,
844
+ Socket::AI_PASSIVE)
845
+ family = infos.collect { |af, *_| af }.uniq
846
+ case family
847
+ when ['AF_INET']
848
+ return TCPServer.open('0.0.0.0', port)
849
+ when ['AF_INET6']
850
+ return TCPServer.open('::', port)
851
+ else
852
+ return TCPServer.open(port)
853
+ end
854
+ end
855
+
856
+ # Open a server listening for connections at +uri+ using
857
+ # configuration +config+.
858
+ def self.open_server(uri, config)
859
+ uri = 'druby://:0' unless uri
860
+ host, port, opt = parse_uri(uri)
861
+ config = {:tcp_original_host => host}.update(config)
862
+ if host.size == 0
863
+ host = getservername
864
+ soc = open_server_inaddr_any(host, port)
865
+ else
866
+ soc = TCPServer.open(host, port)
867
+ end
868
+ port = soc.addr[1] if port == 0
869
+ config[:tcp_port] = port
870
+ uri = "druby://#{host}:#{port}"
871
+ self.new(uri, soc, config)
872
+ end
873
+
874
+ # Parse +uri+ into a [uri, option] pair.
875
+ def self.uri_option(uri, config)
876
+ host, port, option = parse_uri(uri)
877
+ return "druby://#{host}:#{port}", option
878
+ end
879
+
880
+ # Create a new DRbTCPSocket instance.
881
+ #
882
+ # +uri+ is the URI we are connected to.
883
+ # +soc+ is the tcp socket we are bound to. +config+ is our
884
+ # configuration.
885
+ def initialize(uri, soc, config={})
886
+ @uri = uri
887
+ @socket = soc
888
+ @config = config
889
+ @acl = config[:tcp_acl]
890
+ @msg = DRbMessage.new(config)
891
+ set_sockopt(@socket)
892
+ end
893
+
894
+ # Get the URI that we are connected to.
895
+ attr_reader :uri
896
+
897
+ # Get the address of our TCP peer (the other end of the socket
898
+ # we are bound to.
899
+ def peeraddr
900
+ @socket.peeraddr
901
+ end
902
+
903
+ # Get the socket.
904
+ def stream; @socket; end
905
+
906
+ # On the client side, send a request to the server.
907
+ def send_request(ref, msg_id, arg, b)
908
+ @msg.send_request(stream, ref, msg_id, arg, b)
909
+ end
910
+
911
+ # On the server side, receive a request from the client.
912
+ def recv_request
913
+ @msg.recv_request(stream)
914
+ end
915
+
916
+ # On the server side, send a reply to the client.
917
+ def send_reply(succ, result)
918
+ @msg.send_reply(stream, succ, result)
919
+ end
920
+
921
+ # On the client side, receive a reply from the server.
922
+ def recv_reply
923
+ @msg.recv_reply(stream)
924
+ end
925
+
926
+ public
927
+
928
+ # Close the connection.
929
+ #
930
+ # If this is an instance returned by #open_server, then this stops
931
+ # listening for new connections altogether. If this is an instance
932
+ # returned by #open or by #accept, then it closes this particular
933
+ # client-server session.
934
+ def close
935
+ if @socket
936
+ @socket.close
937
+ @socket = nil
938
+ end
939
+ end
940
+
941
+ # On the server side, for an instance returned by #open_server,
942
+ # accept a client connection and return a new instance to handle
943
+ # the server's side of this client-server session.
944
+ def accept
945
+ while true
946
+ s = @socket.accept
947
+ break if (@acl ? @acl.allow_socket?(s) : true)
948
+ s.close
949
+ end
950
+ if @config[:tcp_original_host].to_s.size == 0
951
+ uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}"
952
+ else
953
+ uri = @uri
954
+ end
955
+ self.class.new(uri, s, @config)
956
+ end
957
+
958
+ # Check to see if this connection is alive.
959
+ def alive?
960
+ return false unless @socket
961
+ if IO.select([@socket], nil, nil, 0)
962
+ close
963
+ return false
964
+ end
965
+ true
966
+ end
967
+
968
+ def set_sockopt(soc) # :nodoc:
969
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
970
+ soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
971
+ end
972
+ end
973
+
974
+ module DRbProtocol
975
+ @protocol = [DRbTCPSocket] # default
976
+ end
977
+
978
+ class DRbURIOption # :nodoc: I don't understand the purpose of this class...
979
+ def initialize(option)
980
+ @option = option.to_s
981
+ end
982
+ attr :option
983
+ def to_s; @option; end
984
+
985
+ def ==(other)
986
+ return false unless DRbURIOption === other
987
+ @option == other.option
988
+ end
989
+
990
+ def hash
991
+ @option.hash
992
+ end
993
+
994
+ alias eql? ==
995
+ end
996
+
997
+ # Object wrapping a reference to a remote drb object.
998
+ #
999
+ # Method calls on this object are relayed to the remote
1000
+ # object that this object is a stub for.
1001
+ class DRbObject
1002
+
1003
+ # Unmarshall a marshalled DRbObject.
1004
+ #
1005
+ # If the referenced object is located within the local server, then
1006
+ # the object itself is returned. Otherwise, a new DRbObject is
1007
+ # created to act as a stub for the remote referenced object.
1008
+ def self._load(s)
1009
+ uri, ref = Marshal.load(s)
1010
+
1011
+ if DRb.here?(uri)
1012
+ obj = DRb.to_obj(ref)
1013
+ if ((! obj.tainted?) && Thread.current[:drb_untaint])
1014
+ Thread.current[:drb_untaint].push(obj)
1015
+ end
1016
+ return obj
1017
+ end
1018
+
1019
+ self.new_with(uri, ref)
1020
+ end
1021
+
1022
+ def self.new_with(uri, ref)
1023
+ it = self.allocate
1024
+ it.instance_variable_set('@uri', uri)
1025
+ it.instance_variable_set('@ref', ref)
1026
+ it
1027
+ end
1028
+
1029
+ # Create a new DRbObject from a URI alone.
1030
+ def self.new_with_uri(uri)
1031
+ self.new(nil, uri)
1032
+ end
1033
+
1034
+ # Marshall this object.
1035
+ #
1036
+ # The URI and ref of the object are marshalled.
1037
+ def _dump(lv)
1038
+ Marshal.dump([@uri, @ref])
1039
+ end
1040
+
1041
+ # Create a new remote object stub.
1042
+ #
1043
+ # +obj+ is the (local) object we want to create a stub for. Normally
1044
+ # this is +nil+. +uri+ is the URI of the remote object that this
1045
+ # will be a stub for.
1046
+ def initialize(obj, uri=nil)
1047
+ @uri = nil
1048
+ @ref = nil
1049
+ if obj.nil?
1050
+ return if uri.nil?
1051
+ @uri, option = DRbProtocol.uri_option(uri, DRb.config)
1052
+ @ref = DRbURIOption.new(option) unless option.nil?
1053
+ else
1054
+ @uri = uri ? uri : (DRb.uri rescue nil)
1055
+ @ref = obj ? DRb.to_id(obj) : nil
1056
+ end
1057
+ end
1058
+
1059
+ # Get the URI of the remote object.
1060
+ def __drburi
1061
+ @uri
1062
+ end
1063
+
1064
+ # Get the reference of the object, if local.
1065
+ def __drbref
1066
+ @ref
1067
+ end
1068
+
1069
+ undef :to_s
1070
+ undef :to_a if respond_to?(:to_a)
1071
+
1072
+ def respond_to?(msg_id, priv=false)
1073
+ case msg_id
1074
+ when :_dump
1075
+ true
1076
+ when :marshal_dump
1077
+ false
1078
+ else
1079
+ method_missing(:respond_to?, msg_id, priv)
1080
+ end
1081
+ end
1082
+
1083
+ # Routes method calls to the referenced object.
1084
+ def method_missing(msg_id, *a, &b)
1085
+ if DRb.here?(@uri)
1086
+ obj = DRb.to_obj(@ref)
1087
+ DRb.current_server.check_insecure_method(obj, msg_id)
1088
+ return obj.__send__(msg_id, *a, &b)
1089
+ end
1090
+
1091
+ succ, result = self.class.with_friend(@uri) do
1092
+ DRbConn.open(@uri) do |conn|
1093
+ conn.send_message(self, msg_id, a, b)
1094
+ end
1095
+ end
1096
+
1097
+ if succ
1098
+ return result
1099
+ elsif DRbUnknown === result
1100
+ raise result
1101
+ else
1102
+ bt = self.class.prepare_backtrace(@uri, result)
1103
+ result.set_backtrace(bt + caller)
1104
+ raise result
1105
+ end
1106
+ end
1107
+
1108
+ def self.with_friend(uri)
1109
+ friend = DRb.fetch_server(uri)
1110
+ return yield() unless friend
1111
+
1112
+ save = Thread.current['DRb']
1113
+ Thread.current['DRb'] = { 'server' => friend }
1114
+ return yield
1115
+ ensure
1116
+ Thread.current['DRb'] = save if friend
1117
+ end
1118
+
1119
+ def self.prepare_backtrace(uri, result)
1120
+ prefix = "(#{uri}) "
1121
+ bt = []
1122
+ result.backtrace.each do |x|
1123
+ break if /`__send__'$/ =~ x
1124
+ if /^\(druby:\/\// =~ x
1125
+ bt.push(x)
1126
+ else
1127
+ bt.push(prefix + x)
1128
+ end
1129
+ end
1130
+ bt
1131
+ end
1132
+
1133
+ def pretty_print(q) # :nodoc:
1134
+ q.pp_object(self)
1135
+ end
1136
+
1137
+ def pretty_print_cycle(q) # :nodoc:
1138
+ q.object_address_group(self) {
1139
+ q.breakable
1140
+ q.text '...'
1141
+ }
1142
+ end
1143
+ end
1144
+
1145
+ # Class handling the connection between a DRbObject and the
1146
+ # server the real object lives on.
1147
+ #
1148
+ # This class maintains a pool of connections, to reduce the
1149
+ # overhead of starting and closing down connections for each
1150
+ # method call.
1151
+ #
1152
+ # This class is used internally by DRbObject. The user does
1153
+ # not normally need to deal with it directly.
1154
+ class DRbConn
1155
+ POOL_SIZE = 16 # :nodoc:
1156
+ @mutex = Mutex.new
1157
+ @pool = []
1158
+
1159
+ def self.open(remote_uri) # :nodoc:
1160
+ begin
1161
+ conn = nil
1162
+
1163
+ @mutex.synchronize do
1164
+ #FIXME
1165
+ new_pool = []
1166
+ @pool.each do |c|
1167
+ if conn.nil? and c.uri == remote_uri
1168
+ conn = c if c.alive?
1169
+ else
1170
+ new_pool.push c
1171
+ end
1172
+ end
1173
+ @pool = new_pool
1174
+ end
1175
+
1176
+ conn = self.new(remote_uri) unless conn
1177
+ succ, result = yield(conn)
1178
+ return succ, result
1179
+
1180
+ ensure
1181
+ if conn
1182
+ if succ
1183
+ @mutex.synchronize do
1184
+ @pool.unshift(conn)
1185
+ @pool.pop.close while @pool.size > POOL_SIZE
1186
+ end
1187
+ else
1188
+ conn.close
1189
+ end
1190
+ end
1191
+ end
1192
+ end
1193
+
1194
+ def initialize(remote_uri) # :nodoc:
1195
+ @uri = remote_uri
1196
+ @protocol = DRbProtocol.open(remote_uri, DRb.config)
1197
+ end
1198
+ attr_reader :uri # :nodoc:
1199
+
1200
+ def send_message(ref, msg_id, arg, block) # :nodoc:
1201
+ @protocol.send_request(ref, msg_id, arg, block)
1202
+ @protocol.recv_reply
1203
+ end
1204
+
1205
+ def close # :nodoc:
1206
+ @protocol.close
1207
+ @protocol = nil
1208
+ end
1209
+
1210
+ def alive? # :nodoc:
1211
+ return false unless @protocol
1212
+ @protocol.alive?
1213
+ end
1214
+ end
1215
+
1216
+ # Class representing a drb server instance.
1217
+ #
1218
+ # A DRbServer must be running in the local process before any incoming
1219
+ # dRuby calls can be accepted, or any local objects can be passed as
1220
+ # dRuby references to remote processes, even if those local objects are
1221
+ # never actually called remotely. You do not need to start a DRbServer
1222
+ # in the local process if you are only making outgoing dRuby calls
1223
+ # passing marshalled parameters.
1224
+ #
1225
+ # Unless multiple servers are being used, the local DRbServer is normally
1226
+ # started by calling DRb.start_service.
1227
+ class DRbServer
1228
+ @@acl = nil
1229
+ @@idconv = DRbIdConv.new
1230
+ @@secondary_server = nil
1231
+ @@argc_limit = 256
1232
+ @@load_limit = 256 * 102400
1233
+ @@verbose = false
1234
+ @@safe_level = 0
1235
+
1236
+ # Set the default value for the :argc_limit option.
1237
+ #
1238
+ # See #new(). The initial default value is 256.
1239
+ def self.default_argc_limit(argc)
1240
+ @@argc_limit = argc
1241
+ end
1242
+
1243
+ # Set the default value for the :load_limit option.
1244
+ #
1245
+ # See #new(). The initial default value is 25 MB.
1246
+ def self.default_load_limit(sz)
1247
+ @@load_limit = sz
1248
+ end
1249
+
1250
+ # Set the default value for the :acl option.
1251
+ #
1252
+ # See #new(). The initial default value is nil.
1253
+ def self.default_acl(acl)
1254
+ @@acl = acl
1255
+ end
1256
+
1257
+ # Set the default value for the :id_conv option.
1258
+ #
1259
+ # See #new(). The initial default value is a DRbIdConv instance.
1260
+ def self.default_id_conv(idconv)
1261
+ @@idconv = idconv
1262
+ end
1263
+
1264
+ def self.default_safe_level(level)
1265
+ @@safe_level = level
1266
+ end
1267
+
1268
+ # Set the default value of the :verbose option.
1269
+ #
1270
+ # See #new(). The initial default value is false.
1271
+ def self.verbose=(on)
1272
+ @@verbose = on
1273
+ end
1274
+
1275
+ # Get the default value of the :verbose option.
1276
+ def self.verbose
1277
+ @@verbose
1278
+ end
1279
+
1280
+ def self.make_config(hash={}) # :nodoc:
1281
+ default_config = {
1282
+ :idconv => @@idconv,
1283
+ :verbose => @@verbose,
1284
+ :tcp_acl => @@acl,
1285
+ :load_limit => @@load_limit,
1286
+ :argc_limit => @@argc_limit,
1287
+ :safe_level => @@safe_level
1288
+ }
1289
+ default_config.update(hash)
1290
+ end
1291
+
1292
+ # Create a new DRbServer instance.
1293
+ #
1294
+ # +uri+ is the URI to bind to. This is normally of the form
1295
+ # 'druby://<hostname>:<port>' where <hostname> is a hostname of
1296
+ # the local machine. If nil, then the system's default hostname
1297
+ # will be bound to, on a port selected by the system; these value
1298
+ # can be retrieved from the +uri+ attribute. 'druby:' specifies
1299
+ # the default dRuby transport protocol: another protocol, such
1300
+ # as 'drbunix:', can be specified instead.
1301
+ #
1302
+ # +front+ is the front object for the server, that is, the object
1303
+ # to which remote method calls on the server will be passed. If
1304
+ # nil, then the server will not accept remote method calls.
1305
+ #
1306
+ # If +config_or_acl+ is a hash, it is the configuration to
1307
+ # use for this server. The following options are recognised:
1308
+ #
1309
+ # :idconv :: an id-to-object conversion object. This defaults
1310
+ # to an instance of the class DRb::DRbIdConv.
1311
+ # :verbose :: if true, all unsuccessful remote calls on objects
1312
+ # in the server will be logged to $stdout. false
1313
+ # by default.
1314
+ # :tcp_acl :: the access control list for this server. See
1315
+ # the ACL class from the main dRuby distribution.
1316
+ # :load_limit :: the maximum message size in bytes accepted by
1317
+ # the server. Defaults to 25 MB (26214400).
1318
+ # :argc_limit :: the maximum number of arguments to a remote
1319
+ # method accepted by the server. Defaults to
1320
+ # 256.
1321
+ #
1322
+ # The default values of these options can be modified on
1323
+ # a class-wide basis by the class methods #default_argc_limit,
1324
+ # #default_load_limit, #default_acl, #default_id_conv,
1325
+ # and #verbose=
1326
+ #
1327
+ # If +config_or_acl+ is not a hash, but is not nil, it is
1328
+ # assumed to be the access control list for this server.
1329
+ # See the :tcp_acl option for more details.
1330
+ #
1331
+ # If no other server is currently set as the primary server,
1332
+ # this will become the primary server.
1333
+ #
1334
+ # The server will immediately start running in its own thread.
1335
+ def initialize(uri=nil, front=nil, config_or_acl=nil)
1336
+ if Hash === config_or_acl
1337
+ config = config_or_acl.dup
1338
+ else
1339
+ acl = config_or_acl || @@acl
1340
+ config = {
1341
+ :tcp_acl => acl
1342
+ }
1343
+ end
1344
+
1345
+ @config = self.class.make_config(config)
1346
+
1347
+ @protocol = DRbProtocol.open_server(uri, @config)
1348
+ @uri = @protocol.uri
1349
+
1350
+ @front = front
1351
+ @idconv = @config[:idconv]
1352
+ @safe_level = @config[:safe_level]
1353
+
1354
+ @grp = ThreadGroup.new
1355
+ @thread = run
1356
+
1357
+ DRb.regist_server(self)
1358
+ end
1359
+
1360
+ # The URI of this DRbServer.
1361
+ attr_reader :uri
1362
+
1363
+ # The main thread of this DRbServer.
1364
+ #
1365
+ # This is the thread that listens for and accepts connections
1366
+ # from clients, not that handles each client's request-response
1367
+ # session.
1368
+ attr_reader :thread
1369
+
1370
+ # The front object of the DRbServer.
1371
+ #
1372
+ # This object receives remote method calls made on the server's
1373
+ # URI alone, with an object id.
1374
+ attr_reader :front
1375
+
1376
+ # The configuration of this DRbServer
1377
+ attr_reader :config
1378
+
1379
+ attr_reader :safe_level
1380
+
1381
+ # Set whether to operate in verbose mode.
1382
+ #
1383
+ # In verbose mode, failed calls are logged to stdout.
1384
+ def verbose=(v); @config[:verbose]=v; end
1385
+
1386
+ # Get whether the server is in verbose mode.
1387
+ #
1388
+ # In verbose mode, failed calls are logged to stdout.
1389
+ def verbose; @config[:verbose]; end
1390
+
1391
+ # Is this server alive?
1392
+ def alive?
1393
+ @thread.alive?
1394
+ end
1395
+
1396
+ # Stop this server.
1397
+ def stop_service
1398
+ DRb.remove_server(self)
1399
+ if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
1400
+ Thread.current['DRb']['stop_service'] = true
1401
+ else
1402
+ @thread.kill
1403
+ end
1404
+ end
1405
+
1406
+ # Convert a dRuby reference to the local object it refers to.
1407
+ def to_obj(ref)
1408
+ return front if ref.nil?
1409
+ return front[ref.to_s] if DRbURIOption === ref
1410
+ @idconv.to_obj(ref)
1411
+ end
1412
+
1413
+ # Convert a local object to a dRuby reference.
1414
+ def to_id(obj)
1415
+ return nil if obj.__id__ == front.__id__
1416
+ @idconv.to_id(obj)
1417
+ end
1418
+
1419
+ private
1420
+ def kill_sub_thread
1421
+ Thread.new do
1422
+ grp = ThreadGroup.new
1423
+ grp.add(Thread.current)
1424
+ list = @grp.list
1425
+ while list.size > 0
1426
+ list.each do |th|
1427
+ th.kill if th.alive?
1428
+ end
1429
+ list = @grp.list
1430
+ end
1431
+ end
1432
+ end
1433
+
1434
+ def run
1435
+ Thread.start do
1436
+ begin
1437
+ while true
1438
+ main_loop
1439
+ end
1440
+ ensure
1441
+ @protocol.close if @protocol
1442
+ kill_sub_thread
1443
+ end
1444
+ end
1445
+ end
1446
+
1447
+ # List of insecure methods.
1448
+ #
1449
+ # These methods are not callable via dRuby.
1450
+ INSECURE_METHOD = [
1451
+ :__send__
1452
+ ]
1453
+
1454
+ # Has a method been included in the list of insecure methods?
1455
+ def insecure_method?(msg_id)
1456
+ INSECURE_METHOD.include?(msg_id)
1457
+ end
1458
+
1459
+ # Coerce an object to a string, providing our own representation if
1460
+ # to_s is not defined for the object.
1461
+ def any_to_s(obj)
1462
+ obj.to_s + ":#{obj.class}"
1463
+ rescue
1464
+ sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
1465
+ end
1466
+
1467
+ # Check that a method is callable via dRuby.
1468
+ #
1469
+ # +obj+ is the object we want to invoke the method on. +msg_id+ is the
1470
+ # method name, as a Symbol.
1471
+ #
1472
+ # If the method is an insecure method (see #insecure_method?) a
1473
+ # SecurityError is thrown. If the method is private or undefined,
1474
+ # a NameError is thrown.
1475
+ def check_insecure_method(obj, msg_id)
1476
+ return true if Proc === obj && msg_id == :__drb_yield
1477
+ raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
1478
+ raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
1479
+
1480
+ if obj.private_methods.include?(msg_id.to_s)
1481
+ desc = any_to_s(obj)
1482
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
1483
+ elsif obj.protected_methods.include?(msg_id.to_s)
1484
+ desc = any_to_s(obj)
1485
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
1486
+ else
1487
+ true
1488
+ end
1489
+ end
1490
+ public :check_insecure_method
1491
+
1492
+ class InvokeMethod # :nodoc:
1493
+ def initialize(drb_server, client)
1494
+ @drb_server = drb_server
1495
+ @safe_level = drb_server.safe_level
1496
+ @client = client
1497
+ end
1498
+
1499
+ def perform
1500
+ @result = nil
1501
+ @succ = false
1502
+ setup_message
1503
+
1504
+ if $SAFE < @safe_level
1505
+ info = Thread.current['DRb']
1506
+ if @block
1507
+ @result = Thread.new {
1508
+ Thread.current['DRb'] = info
1509
+ $SAFE = @safe_level
1510
+ perform_with_block
1511
+ }.value
1512
+ else
1513
+ @result = Thread.new {
1514
+ Thread.current['DRb'] = info
1515
+ $SAFE = @safe_level
1516
+ perform_without_block
1517
+ }.value
1518
+ end
1519
+ else
1520
+ if @block
1521
+ @result = perform_with_block
1522
+ else
1523
+ @result = perform_without_block
1524
+ end
1525
+ end
1526
+ @succ = true
1527
+ if @msg_id == :to_ary && @result.class == Array
1528
+ @result = DRbArray.new(@result)
1529
+ end
1530
+ return @succ, @result
1531
+ rescue StandardError, ScriptError, Interrupt
1532
+ @result = $!
1533
+ return @succ, @result
1534
+ end
1535
+
1536
+ private
1537
+ def init_with_client
1538
+ obj, msg, argv, block = @client.recv_request
1539
+ @obj = obj
1540
+ @msg_id = msg.intern
1541
+ @argv = argv
1542
+ @block = block
1543
+ end
1544
+
1545
+ def check_insecure_method
1546
+ @drb_server.check_insecure_method(@obj, @msg_id)
1547
+ end
1548
+
1549
+ def setup_message
1550
+ init_with_client
1551
+ check_insecure_method
1552
+ end
1553
+
1554
+ def perform_without_block
1555
+ if Proc === @obj && @msg_id == :__drb_yield
1556
+ if @argv.size == 1
1557
+ ary = @argv
1558
+ else
1559
+ ary = [@argv]
1560
+ end
1561
+ ary.collect(&@obj)[0]
1562
+ else
1563
+ @obj.__send__(@msg_id, *@argv)
1564
+ end
1565
+ end
1566
+
1567
+ end
1568
+
1569
+ if RUBY_VERSION >= '1.8'
1570
+ require 'drb/invokemethod'
1571
+ class InvokeMethod
1572
+ include InvokeMethod18Mixin
1573
+ end
1574
+ else
1575
+ require 'drb/invokemethod16'
1576
+ class InvokeMethod
1577
+ include InvokeMethod16Mixin
1578
+ end
1579
+ end
1580
+
1581
+ # The main loop performed by a DRbServer's internal thread.
1582
+ #
1583
+ # Accepts a connection from a client, and starts up its own
1584
+ # thread to handle it. This thread loops, receiving requests
1585
+ # from the client, invoking them on a local object, and
1586
+ # returning responses, until the client closes the connection
1587
+ # or a local method call fails.
1588
+ def main_loop
1589
+ Thread.start(@protocol.accept) do |client|
1590
+ @grp.add Thread.current
1591
+ Thread.current['DRb'] = { 'client' => client ,
1592
+ 'server' => self }
1593
+ loop do
1594
+ begin
1595
+ succ = false
1596
+ invoke_method = InvokeMethod.new(self, client)
1597
+ succ, result = invoke_method.perform
1598
+ if !succ && verbose
1599
+ p result
1600
+ result.backtrace.each do |x|
1601
+ puts x
1602
+ end
1603
+ end
1604
+ client.send_reply(succ, result) rescue nil
1605
+ ensure
1606
+ client.close unless succ
1607
+ if Thread.current['DRb']['stop_service']
1608
+ Thread.new { stop_service }
1609
+ end
1610
+ break unless succ
1611
+ end
1612
+ end
1613
+ end
1614
+ end
1615
+ end
1616
+
1617
+ @primary_server = nil
1618
+
1619
+ # Start a dRuby server locally.
1620
+ #
1621
+ # The new dRuby server will become the primary server, even
1622
+ # if another server is currently the primary server.
1623
+ #
1624
+ # +uri+ is the URI for the server to bind to. If nil,
1625
+ # the server will bind to random port on the default local host
1626
+ # name and use the default dRuby protocol.
1627
+ #
1628
+ # +front+ is the server's front object. This may be nil.
1629
+ #
1630
+ # +config+ is the configuration for the new server. This may
1631
+ # be nil.
1632
+ #
1633
+ # See DRbServer::new.
1634
+ def start_service(uri=nil, front=nil, config=nil)
1635
+ @primary_server = DRbServer.new(uri, front, config)
1636
+ end
1637
+ module_function :start_service
1638
+
1639
+ # The primary local dRuby server.
1640
+ #
1641
+ # This is the server created by the #start_service call.
1642
+ attr_accessor :primary_server
1643
+ module_function :primary_server=, :primary_server
1644
+
1645
+ # Get the 'current' server.
1646
+ #
1647
+ # In the context of execution taking place within the main
1648
+ # thread of a dRuby server (typically, as a result of a remote
1649
+ # call on the server or one of its objects), the current
1650
+ # server is that server. Otherwise, the current server is
1651
+ # the primary server.
1652
+ #
1653
+ # If the above rule fails to find a server, a DRbServerNotFound
1654
+ # error is raised.
1655
+ def current_server
1656
+ drb = Thread.current['DRb']
1657
+ server = (drb && drb['server']) ? drb['server'] : @primary_server
1658
+ raise DRbServerNotFound unless server
1659
+ return server
1660
+ end
1661
+ module_function :current_server
1662
+
1663
+ # Stop the local dRuby server.
1664
+ #
1665
+ # This operates on the primary server. If there is no primary
1666
+ # server currently running, it is a noop.
1667
+ def stop_service
1668
+ @primary_server.stop_service if @primary_server
1669
+ @primary_server = nil
1670
+ end
1671
+ module_function :stop_service
1672
+
1673
+ # Get the URI defining the local dRuby space.
1674
+ #
1675
+ # This is the URI of the current server. See #current_server.
1676
+ def uri
1677
+ drb = Thread.current['DRb']
1678
+ client = (drb && drb['client'])
1679
+ if client
1680
+ uri = client.uri
1681
+ return uri if uri
1682
+ end
1683
+ current_server.uri
1684
+ end
1685
+ module_function :uri
1686
+
1687
+ # Is +uri+ the URI for the current local server?
1688
+ def here?(uri)
1689
+ (current_server.uri rescue nil) == uri
1690
+ end
1691
+ module_function :here?
1692
+
1693
+ # Get the configuration of the current server.
1694
+ #
1695
+ # If there is no current server, this returns the default configuration.
1696
+ # See #current_server and DRbServer::make_config.
1697
+ def config
1698
+ current_server.config
1699
+ rescue
1700
+ DRbServer.make_config
1701
+ end
1702
+ module_function :config
1703
+
1704
+ # Get the front object of the current server.
1705
+ #
1706
+ # This raises a DRbServerNotFound error if there is no current server.
1707
+ # See #current_server.
1708
+ def front
1709
+ current_server.front
1710
+ end
1711
+ module_function :front
1712
+
1713
+ # Convert a reference into an object using the current server.
1714
+ #
1715
+ # This raises a DRbServerNotFound error if there is no current server.
1716
+ # See #current_server.
1717
+ def to_obj(ref)
1718
+ current_server.to_obj(ref)
1719
+ end
1720
+
1721
+ # Get a reference id for an object using the current server.
1722
+ #
1723
+ # This raises a DRbServerNotFound error if there is no current server.
1724
+ # See #current_server.
1725
+ def to_id(obj)
1726
+ current_server.to_id(obj)
1727
+ end
1728
+ module_function :to_id
1729
+ module_function :to_obj
1730
+
1731
+ # Get the thread of the primary server.
1732
+ #
1733
+ # This returns nil if there is no primary server. See #primary_server.
1734
+ def thread
1735
+ @primary_server ? @primary_server.thread : nil
1736
+ end
1737
+ module_function :thread
1738
+
1739
+ # Set the default id conv object.
1740
+ #
1741
+ # See DRbServer#default_id_conv.
1742
+ def install_id_conv(idconv)
1743
+ DRbServer.default_id_conv(idconv)
1744
+ end
1745
+ module_function :install_id_conv
1746
+
1747
+ # Set the default acl.
1748
+ #
1749
+ # See DRb::DRbServer.default_acl.
1750
+ def install_acl(acl)
1751
+ DRbServer.default_acl(acl)
1752
+ end
1753
+ module_function :install_acl
1754
+
1755
+ @mutex = Mutex.new
1756
+ def mutex
1757
+ @mutex
1758
+ end
1759
+ module_function :mutex
1760
+
1761
+ @server = {}
1762
+ def regist_server(server)
1763
+ @server[server.uri] = server
1764
+ mutex.synchronize do
1765
+ @primary_server = server unless @primary_server
1766
+ end
1767
+ end
1768
+ module_function :regist_server
1769
+
1770
+ def remove_server(server)
1771
+ @server.delete(server.uri)
1772
+ end
1773
+ module_function :remove_server
1774
+
1775
+ def fetch_server(uri)
1776
+ @server[uri]
1777
+ end
1778
+ module_function :fetch_server
1779
+ end
1780
+
1781
+ DRbObject = DRb::DRbObject
1782
+ DRbUndumped = DRb::DRbUndumped
1783
+ DRbIdConv = DRb::DRbIdConv