Take the 2008 Git User's Survey and help out! [ hide ]

public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Search Repo:
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
Reorganize exception hierarchy so that errors are easier identifiable.
Hongli Lai (Phusion) (author)
Fri Mar 07 11:56:09 -0800 2008
commit  cec5c76b2780aa871f6dee4fed941d1f2eed69f3
tree    53fb61676c6af10931e6f7fafffe6bd55e656496
parent  ca9a130d2b937472e54468b529efacb15d9cc055
...
64
65
66
 
 
 
 
67
68
69
...
64
65
66
67
68
69
70
71
72
73
0
@@ -64,6 +64,10 @@ class AbstractServer
0
   class ServerNotStarted < StandardError
0
   end
0
   
0
+ # This exception means that the server process exited unexpectedly.
0
+ class ServerError < StandardError
0
+ end
0
+
0
   def initialize
0
     @done = false
0
     @message_handlers = {}
...
79
80
81
82
 
83
84
85
...
79
80
81
 
82
83
84
85
0
@@ -79,7 +79,7 @@ class Application
0
   #
0
   # See also RequestHandler#owner_pipe.
0
   def close
0
- @owner_pipe.close
0
+ @owner_pipe.close rescue nil
0
   end
0
 end
0
 
...
6
7
8
9
10
11
12
13
14
15
16
 
 
17
18
19
20
 
21
22
 
 
 
23
24
25
26
27
28
 
 
 
 
 
 
 
 
 
29
30
31
...
39
40
41
 
 
 
 
42
43
44
...
83
84
85
86
 
87
88
89
...
94
95
96
97
 
98
99
100
101
102
103
 
104
105
 
106
107
108
...
110
111
112
113
 
114
115
116
117
118
119
 
120
121
122
123
 
124
125
126
...
6
7
8
 
 
 
 
 
 
 
 
9
10
11
 
 
 
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
43
44
45
46
47
48
49
50
51
52
...
91
92
93
 
94
95
96
97
...
102
103
104
 
105
106
107
108
109
110
 
111
112
 
113
114
115
116
...
118
119
120
 
121
122
123
124
125
126
 
127
128
129
130
 
131
132
133
134
0
@@ -6,26 +6,30 @@ require 'passenger/utils'
0
 require 'passenger/request_handler'
0
 module Passenger
0
 
0
-# Raised when an ApplicationSpawner, FrameworkSpawner or SpawnManager is
0
-# unable to spawn a new application.
0
-class SpawnError < StandardError
0
-end
0
-
0
-# Raised when an ApplicationSpawner, FrameworkSpawner or SpawnManager
0
-# is able to spawn an application instance, but the application instance
0
-# exited during startup, or raised an exception during startup.
0
+# An abstract base class for AppInitError and FrameworkInitError. This represents
0
+# the failure when initializing something.
0
 class InitializationError < StandardError
0
- # The exception that the application instance raised during startup.
0
- # This may be nil, which means that the application instance exited
0
- # with exit() instead of having raised an exception.
0
+ # The exception that caused initialization to fail. This may be nil.
0
   attr_accessor :child_exception
0
 
0
+ # Create a new InitializationError. +message+ is the error message,
0
+ # and +child_exception+ is the exception that caused initialization
0
+ # to fail.
0
   def initialize(message, child_exception = nil)
0
     super(message)
0
     @child_exception = child_exception
0
   end
0
 end
0
 
0
+# Raised when ApplicationSpawner, FrameworkSpawner or SpawnManager was unable
0
+# spawn a Ruby on Rails application, because the application either threw an
0
+# exception or called exit.
0
+#
0
+# If the +child_exception+ attribute is nil, then it means that the application
0
+# called exit.
0
+class AppInitError < InitializationError
0
+end
0
+
0
 # This class is capable of spawns instances of a single Ruby on Rails application.
0
 # It does so by preloading as much of the application's code as possible, then creating
0
 # instances of the application using what is already preloaded. This makes it spawning
0
@@ -39,6 +43,10 @@ end
0
 class ApplicationSpawner < AbstractServer
0
   include Utils
0
   
0
+ # This exception means that the ApplicationSpawner server process exited unexpectedly.
0
+ class Error < AbstractServer::ServerError
0
+ end
0
+
0
   # The user ID of the root user.
0
   ROOT_UID = 0
0
   # The group ID of the root user.
0
@@ -83,7 +91,7 @@ class ApplicationSpawner < AbstractServer
0
   #
0
   # Raises:
0
   # - AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn't already been started.
0
- # - SpawnError: The ApplicationSpawner server exited unexpectedly.
0
+ # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
0
   def spawn_application
0
     server.write("spawn_application")
0
     pid, socket_name, using_abstract_namespace = server.read
0
@@ -94,15 +102,15 @@ class ApplicationSpawner < AbstractServer
0
     return Application.new(@app_root, pid, socket_name,
0
       using_abstract_namespace == "true", owner_pipe)
0
   rescue SystemCallError, IOError, SocketError => e
0
- raise SpawnError, "The application spawner server exited unexpectedly"
0
+ raise Error, "The application spawner server exited unexpectedly"
0
   end
0
   
0
   # Overrided from AbstractServer#start.
0
   #
0
   # May raise these additional exceptions:
0
- # - InitializationError: The Ruby on Rails application raised an exception
0
+ # - AppInitError: The Ruby on Rails application raised an exception
0
   # or called exit() during startup.
0
- # - IOError: The ApplicationSpawner server exited unexpectedly.
0
+ # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
0
   def start
0
     super
0
     begin
0
@@ -110,17 +118,17 @@ class ApplicationSpawner < AbstractServer
0
       if status == 'exception'
0
         child_exception = unmarshal_exception(server.read_scalar)
0
         stop
0
- raise InitializationError.new(
0
+ raise AppInitError.new(
0
           "Application '#{@app_root}' raised an exception: " <<
0
           "#{child_exception.class} (#{child_exception.message})",
0
           child_exception)
0
       elsif status == 'exit'
0
         stop
0
- raise InitializationError.new("Application '#{@app_root}' exited during startup")
0
+ raise AppInitError.new("Application '#{@app_root}' exited during startup")
0
       end
0
     rescue IOError, SystemCallError, SocketError
0
       stop
0
- raise IOError, "The application spawner server exited unexpectedly"
0
+ raise Error, "The application spawner server exited unexpectedly"
0
     end
0
   end
0
 
...
6
7
8
 
 
 
 
 
9
10
11
...
25
26
27
 
 
 
 
28
29
30
...
61
62
63
64
65
 
 
66
67
68
...
77
78
79
80
 
81
82
83
84
 
85
86
87
...
102
103
104
105
106
107
 
 
 
108
109
110
...
116
117
118
119
120
 
121
122
123
...
128
129
130
131
132
133
134
135
 
136
137
138
...
151
152
153
154
 
155
156
157
...
159
160
161
162
 
163
164
165
...
243
244
245
246
 
247
248
249
...
253
254
255
256
 
257
258
259
...
6
7
8
9
10
11
12
13
14
15
16
...
30
31
32
33
34
35
36
37
38
39
...
70
71
72
 
 
73
74
75
76
77
...
86
87
88
 
89
90
91
92
 
93
94
95
96
...
111
112
113
 
 
 
114
115
116
117
118
119
...
125
126
127
 
 
128
129
130
131
...
136
137
138
 
 
 
 
 
139
140
141
142
...
155
156
157
 
158
159
160
161
...
163
164
165
 
166
167
168
169
...
247
248
249
 
250
251
252
253
...
257
258
259
 
260
261
262
263
0
@@ -6,6 +6,11 @@ require 'passenger/application_spawner'
0
 require 'passenger/utils'
0
 module Passenger
0
 
0
+# Raised when FrameworkSpawner or SpawnManager was unable to load a version of
0
+# the Ruby on Rails framework.
0
+class FrameworkInitError < InitializationError
0
+end
0
+
0
 # This class is capable of spawning Ruby on Rails application instances
0
 # quickly. This is done by preloading the Ruby on Rails framework into memory,
0
 # before spawning the application instances.
0
@@ -25,6 +30,10 @@ class FrameworkSpawner < AbstractServer
0
 
0
   include Utils
0
   
0
+ # This exception means that the FrameworkSpawner server process exited unexpectedly.
0
+ class Error < AbstractServer::ServerError
0
+ end
0
+
0
   # An attribute, used internally. This should not be used outside Passenger.
0
   attr_accessor :time
0
 
0
@@ -61,8 +70,8 @@ class FrameworkSpawner < AbstractServer
0
   # Overrided from AbstractServer#start.
0
   #
0
   # May raise these additional exceptions:
0
- # - InitializationError: The specified Ruby on Rails framework could not be loaded.
0
- # - IOError, SystemCallError, SocketError: The FrameworkSpawner server crashed.
0
+ # - FrameworkInitError: The specified Ruby on Rails framework could not be loaded.
0
+ # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
0
   def start
0
     super
0
     begin
0
@@ -77,11 +86,11 @@ class FrameworkSpawner < AbstractServer
0
           message = "Could not load Ruby on Rails framework at '#{@vendor}': " <<
0
             "#{child_exception.class} (#{child_exception.message})"
0
         end
0
- raise InitializationError.new(message, child_exception)
0
+ raise FrameworkInitError.new(message, child_exception)
0
       end
0
     rescue IOError, SystemCallError, SocketError
0
       stop
0
- raise
0
+ raise Error, "The framework spawner server exited unexpectedly"
0
     end
0
   end
0
   
0
@@ -102,9 +111,9 @@ class FrameworkSpawner < AbstractServer
0
   # Raises:
0
   # - AbstractServer::ServerNotStarted: The FrameworkSpawner server hasn't already been started.
0
   # - ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
0
- # - InitializationError: The application raised an exception or called exit() during startup.
0
- # - IOError: The ApplicationSpawner server exited unexpectedly.
0
- # - SpawnError: The FrameworkSpawner server exited unexpectedly.
0
+ # - AppInitError: The application raised an exception or called exit() during startup.
0
+ # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
0
+ # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
0
   def spawn_application(app_root, lower_privilege = true, lowest_user = "nobody")
0
     app_root = normalize_path(app_root)
0
     assert_valid_app_root(app_root)
0
@@ -116,8 +125,7 @@ class FrameworkSpawner < AbstractServer
0
         raise IOError, "Connection closed"
0
       end
0
       if result[0] == 'exception'
0
- exception_to_propagate = unmarshal_exception(server.read_scalar)
0
- raise exception_to_propagate
0
+ raise unmarshal_exception(server.read_scalar)
0
       else
0
         pid, listen_socket_name, using_abstract_namespace = server.read
0
         if pid.nil?
0
@@ -128,11 +136,7 @@ class FrameworkSpawner < AbstractServer
0
           using_abstract_namespace == "true", owner_pipe)
0
       end
0
     rescue SystemCallError, IOError, SocketError => e
0
- if e == exception_to_propagate
0
- raise
0
- else
0
- raise SpawnError, "The framework spawner server exited unexpectedly"
0
- end
0
+ raise Error, "The framework spawner server exited unexpectedly"
0
     end
0
   end
0
   
0
@@ -151,7 +155,7 @@ class FrameworkSpawner < AbstractServer
0
   # Raises:
0
   # - ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails
0
   # application root.
0
- # - IOError: The FrameworkSpawner server exited unexpectedly.
0
+ # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
0
   def reload(app_root = nil)
0
     if app_root.nil?
0
       server.write("reload")
0
@@ -159,7 +163,7 @@ class FrameworkSpawner < AbstractServer
0
       server.write("reload", normalize_path(app_root))
0
     end
0
   rescue SystemCallError, IOError, SocketError
0
- raise IOError, "The framework spawner server exited unexpectedly"
0
+ raise Error, "The framework spawner server exited unexpectedly"
0
   end
0
 
0
 protected
0
@@ -243,7 +247,7 @@ private
0
         begin
0
           spawner = ApplicationSpawner.new(app_root, lower_privilege, lowest_user)
0
           spawner.start
0
- rescue ArgumentError, InitializationError, IOError => e
0
+ rescue ArgumentError, AppInitError, ApplicationSpawner::Error => e
0
           client.write('exception')
0
           client.write_scalar(marshal_exception(e))
0
           return
0
@@ -253,7 +257,7 @@ private
0
       spawner.time = Time.now
0
       begin
0
         app = spawner.spawn_application
0
- rescue SpawnError => e
0
+ rescue ApplicationSpawner::Error => e
0
         client.write('exception')
0
         client.write_scalar(marshal_exception(e))
0
         return
...
237
238
239
 
 
240
241
242
...
237
238
239
240
241
242
243
244
0
@@ -237,6 +237,8 @@ private
0
   
0
   def process_request(socket)
0
     channel = MessageChannel.new(socket)
0
+ # TODO: We should check the size of the header data. We don't want attackers to
0
+ # make the server swap storm to dead by sending a header of 4 GB.
0
     headers_data = channel.read_scalar
0
     if headers_data.nil?
0
       socket.close
...
54
55
56
57
58
59
60
61
 
 
 
62
63
64
...
74
75
76
77
78
 
79
80
81
...
94
95
96
97
 
98
99
100
...
103
104
105
106
107
 
108
109
110
...
54
55
56
 
 
 
 
 
57
58
59
60
61
62
...
72
73
74
 
75
76
77
78
79
...
92
93
94
 
95
96
97
98
...
101
102
103
 
 
104
105
106
107
0
@@ -54,11 +54,9 @@ class SpawnManager < AbstractServer
0
   # - ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
0
   # - VersionNotFound: The Ruby on Rails framework version that the given application requires
0
   # is not installed.
0
- # - InitializationError: Either the Ruby on Rails framework version that the given application
0
- # requires could not be loaded, or the application raised an exception or called exit()
0
- # during startup.
0
- # - IOError: The ApplicationSpawner server exited unexpectedly.
0
- # - SpawnError: The FrameworkSpawner server exited unexpectedly.
0
+ # - AbstractServer::ServerError: One of the server processes exited unexpectedly.
0
+ # - FrameworkInitError: The Ruby on Rails framework that the application requires could not be loaded.
0
+ # - AppInitError: The application raised an exception or called exit() during startup.
0
   def spawn_application(app_root, lower_privilege = true, lowest_user = "nobody")
0
     options = {}
0
     framework_version = Application.detect_framework_version(app_root)
0
@@ -74,8 +72,8 @@ class SpawnManager < AbstractServer
0
       spawner = @spawners[key]
0
       if !spawner
0
         spawner = FrameworkSpawner.new(options)
0
- @spawners[key] = spawner
0
         spawner.start
0
+ @spawners[key] = spawner
0
       end
0
     end
0
     spawner.time = Time.now
0
@@ -94,7 +92,7 @@ class SpawnManager < AbstractServer
0
   # application instance is spawned, the application code will be freshly
0
   # loaded into memory.
0
   #
0
- # Raises IOError if something went wrong.
0
+ # Raises AbstractServer::SpawnError if something went wrong.
0
   def reload(app_root = nil)
0
     @lock.synchronize do
0
       @spawners.each_value do |spawner|
0
@@ -103,8 +101,7 @@ class SpawnManager < AbstractServer
0
     end
0
   end
0
   
0
- # Cleanup resources. Should be called after AbstractServer#start_synchronously
0
- # is called.
0
+ # Cleanup resources. Should be called when this SpawnManager is no longer needed.
0
   def cleanup
0
     @lock.synchronize do
0
       @cond.signal
...
27
28
29
 
 
30
31
32
...
71
72
73
 
74
75
76
...
88
89
90
91
 
92
93
94
95
96
97
 
 
 
 
 
 
 
 
 
 
98
99
100
...
27
28
29
30
31
32
33
34
...
73
74
75
76
77
78
79
...
91
92
93
 
94
95
96
97
98
99
 
100
101
102
103
104
105
106
107
108
109
110
111
112
0
@@ -27,6 +27,8 @@ protected
0
   def normalize_path(path)
0
     raise ArgumentError, "The 'path' argument may not be nil" if path.nil?
0
     return Pathname.new(path).realpath.to_s
0
+ rescue Errno::ENOENT => e
0
+ raise ArgumentError, e.message
0
   end
0
   
0
   # Assert that +app_root+ is a valid Ruby on Rails application root.
0
@@ -71,6 +73,7 @@ protected
0
       :backtrace => exception.backtrace
0
     }
0
     if exception.is_a?(InitializationError)
0
+ data[:is_initialization_error] = true
0
       if exception.child_exception
0
         data[:child_exception] = marshal_exception(exception.child_exception)
0
       end
0
@@ -88,13 +91,22 @@ protected
0
   
0
   def unmarshal_exception(data)
0
     hash = Marshal.load(data)
0
- if hash[:class] == InitializationError.to_s
0
+ if hash[:is_initialization_error]
0
       if hash[:child_exception]
0
         child_exception = unmarshal_exception(hash[:child_exception])
0
       else
0
         child_exception = nil
0
       end
0
- return InitializationError.new(hash[:message], child_exception)
0
+
0
+ case hash[:class]
0
+ when AppInitError.to_s
0
+ exception_class = AppInitError
0
+ when FrameworkInitError.to_s
0
+ exception_class = FrameworkInitError
0
+ else
0
+ exception_class = InitializationError
0
+ end
0
+ return exception_class.new(hash[:message], child_exception)
0
     else
0
       begin
0
         return Marshal.load(hash[:exception])
...
12
13
14
 
 
 
 
 
15
...
12
13
14
15
16
17
18
19
20
0
@@ -12,4 +12,9 @@ describe Application do
0
     rails_version = Application.detect_framework_version('stub/minimal-railsapp')
0
     rails_version.should be_nil
0
   end
0
+
0
+ it "should raise VersionNotFound if a nonexistant Rails version is specified" do
0
+ detector = lambda { Application.detect_framework_version('stub/broken-railsapp4') }
0
+ detector.should raise_error(VersionNotFound)
0
+ end
0
 end
...
5
6
7
 
8
9
10
...
61
62
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
5
6
7
8
9
10
11
...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
0
@@ -5,6 +5,7 @@ require 'passenger/utils'
0
 require 'abstract_server_spec'
0
 require 'minimal_spawner_spec'
0
 require 'spawner_privilege_lowering_spec'
0
+require 'spawner_error_handling_spec'
0
 include Passenger
0
 include Passenger::Utils
0
 
0
@@ -61,3 +62,32 @@ describe SpawnManager do
0
   end
0
 end
0
 
0
+describe SpawnManager do
0
+ it_should_behave_like "handling errors in application initialization"
0
+ it_should_behave_like "handling errors in framework initialization"
0
+
0
+ def spawn_application(app_root)
0
+ spawner = SpawnManager.new
0
+ begin
0
+ return spawner.spawn_application(app_root)
0
+ ensure
0
+ spawner.cleanup
0
+ end
0
+ end
0
+
0
+ def load_nonexistant_framework
0
+ Application.instance_eval do
0
+ alias orig_detect_framework_version detect_framework_version
0
+ def detect_framework_version(app_root)
0
+ return "1.9.827"
0
+ end
0
+ end
0
+ begin
0
+ return spawn_application('stub/broken-railsapp4')
0
+ ensure
0
+ Application.instance_eval do
0
+ alias detect_framework_version orig_detect_framework_version
0
+ end
0
+ end
0
+ end
0
+end
...
4
5
6
7
 
8
9
10
 
11
12
13
14
15
16
 
17
18
19
...
4
5
6
 
7
8
9
 
10
11
12
13
14
15
 
16
17
18
19
0
@@ -4,16 +4,16 @@ require 'abstract_server_spec'
0
 shared_examples_for "a spawn server" do
0
   it_should_behave_like "AbstractServer"
0
   
0
- it "should raise a SpawnError if something went wrong" do
0
+ it "should raise an AbstractServer::ServerError if the server was killed" do
0
     Process.kill('SIGABRT', @spawner.server_pid)
0
     spawning = lambda { spawn_application }
0
- spawning.should raise_error(SpawnError)
0
+ spawning.should raise_error(AbstractServer::ServerError)
0
   end
0
   
0
   it "should work correctly after a restart, if something went wrong" do
0
     Process.kill('SIGABRT', @spawner.server_pid)
0
     spawning = lambda { spawn_application }
0
- spawning.should raise_error(SpawnError)
0
+ spawning.should raise_error(AbstractServer::ServerError)
0
     
0
     @spawner.stop
0
     @spawner.start
...
1
2
 
3
4
5
6
 
7
8
9
10
11
 
12
13
14
15
 
16
17
18
19
20
 
21
22
23
24
 
25
26
27
...
29
30
31
32
33
 
 
34
35
...
1
 
2
3
4
5
 
6
7
8
9
10
 
11
12
13
14
 
15
16
17
18
19
 
20
21
22
23
 
24
25
26
27
...
29
30
31
 
 
32
33
34
35
0
@@ -1,27 +1,27 @@
0
 shared_examples_for "handling errors in application initialization" do
0
- it "should raise an InitializationError if the spawned app raises a standard exception during startup" do
0
+ it "should raise an AppInitError if the spawned app raises a standard exception during startup" do
0
     begin
0
       spawn_application('stub/broken-railsapp')
0
       violated "Spawning the application should have raised an InitializationError."
0
- rescue InitializationError => e
0
+ rescue AppInitError => e
0
       e.child_exception.message.should == "This is a dummy exception."
0
     end
0
   end
0
   
0
- it "should raise an InitializationError if the spawned app raises a custom-defined exception during startup" do
0
+ it "should raise an AppInitError if the spawned app raises a custom-defined exception during startup" do
0
     begin
0
       spawn_application('stub/broken-railsapp3')
0
       violated "Spawning the application should have raised an InitializationError."
0
- rescue InitializationError => e
0
+ rescue AppInitError => e
0
       e.child_exception.message.should == "This is a custom exception. (MyError)"
0
     end
0
   end
0
   
0
- it "should raise an InitializationError if the spawned app calls exit() during startup" do
0
+ it "should raise an AppInitError if the spawned app calls exit() during startup" do
0
     begin
0
       spawn_application('stub/broken-railsapp2')
0
       violated "Spawning the application should have raised an InitializationError."
0
- rescue InitializationError => e
0
+ rescue AppInitError => e
0
       e.child_exception.should be_nil
0
     end
0
   end
0
@@ -29,7 +29,7 @@ end
0
 
0
 shared_examples_for "handling errors in framework initialization" do
0
   include Utils
0
- it "should raise InitializationError if the framework could not be loaded" do
0
- lambda { load_nonexistant_framework }.should raise_error(InitializationError)
0
+ it "should raise FrameworkInitError if the framework could not be loaded" do
0
+ lambda { load_nonexistant_framework }.should raise_error(FrameworkInitError)
0
   end
0
 end

Comments

    No one has commented yet.