diff options
author | evanweaver <evanweaver@19e92222-5c0b-0410-8929-a290d50e31e9> | 2007-10-26 03:07:30 +0000 |
---|---|---|
committer | evanweaver <evanweaver@19e92222-5c0b-0410-8929-a290d50e31e9> | 2007-10-26 03:07:30 +0000 |
commit | e045f787e126f40d6cd2539632ceb02221c1e8d8 (patch) | |
tree | 47eb16d886e5ead3b4e47d716b9d23f26df624c0 /lib/mongrel.rb | |
parent | d7fa2022890e9347933591805f49d7f76fd3e141 (diff) | |
download | unicorn-e045f787e126f40d6cd2539632ceb02221c1e8d8.tar.gz |
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@744 19e92222-5c0b-0410-8929-a290d50e31e9
Diffstat (limited to 'lib/mongrel.rb')
-rw-r--r-- | lib/mongrel.rb | 96 |
1 files changed, 74 insertions, 22 deletions
diff --git a/lib/mongrel.rb b/lib/mongrel.rb index f48552b..d5ac6f8 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -37,27 +37,88 @@ require 'uri' module Mongrel class URIClassifier + + class RegistrationError < RuntimeError + end + class UsageError < RuntimeError + end + attr_reader :handler_map # Returns the URIs that have been registered with this classifier so far. - # The URIs returned should not be modified as this will cause a memory leak. - # You can use this to inspect the contents of the URIClassifier. def uris @handler_map.keys end - # Simply does an inspect that looks like a Hash inspect. - def inspect - @handler_map.inspect - end + def initialize + @handler_map = {} + @matcher = // + @root_handler = nil + end + + # Register a handler object at a particular URI. The handler can be whatever + # you want, including an array. It's up to you what to do with it. + # + # Registering a handler is not necessarily threadsafe, so be careful if you go + # mucking around once the server is running. + def register(uri, handler) + raise RegistrationError, "#{uri.inspect} is already registered" if @handler_map[uri] + raise RegistrationError, "URI is empty" if !uri or uri.empty? + raise RegistrationError, "URI must begin with a \"#{Const::SLASH}\"" unless uri[0..0] == Const::SLASH + @handler_map[uri.dup] = handler + rebuild + end + + # Unregister a particular URI and its handler. + def unregister(uri) + handler = @handler_map.delete(uri) + raise RegistrationError, "#{uri.inspect} was not registered" unless handler + rebuild + handler + end + + # Resolve a request URI by finding the best partial match in the registered + # handler URIs. + def resolve(request_uri) + if @root_handler + # Optimization for the pathological case of only one handler on "/"; e.g. Rails + [Const::SLASH, request_uri, @root_handler] + elsif match = @matcher.match(request_uri) + uri = match.to_s + # A root mounted ("/") handler must resolve such that path info matches the original URI. + [uri, (uri == Const::SLASH ? request_uri : match.post_match), @handler_map[uri]] + else + [nil, nil, nil] + end + end + + private + + def rebuild + if @handler_map.size == 1 and @handler_map[Const::SLASH] + @root_handler = @handler_map.values.first + else + @root_handler = nil + routes = @handler_map.keys.sort.sort_by do |uri| + -uri.length + end + @matcher = Regexp.new(routes.map do |uri| + Regexp.new('^' + Regexp.escape(uri)) + end.join('|')) + end + end + end + # Used to stop the HttpServer via Thread.raise. class StopServer < Exception; end + # Thrown at a thread when it is timed out. class TimeoutError < Exception; end + # Every standard HTTP code mapped to the appropriate message. These are # used so frequently that they are placed directly in Mongrel for easy # access rather than Mongrel::Const. @@ -778,25 +839,16 @@ module Mongrel # found in the prefix of a request then your handler's HttpHandler::process method # is called. See Mongrel::URIClassifier#register for more information. # - # If you set in_front=true then the passed in handler will be put in front in the list. - # Otherwise it's placed at the end of the list. + # If you set in_front=true then the passed in handler will be put in the front of the list + # for that particular URI. Otherwise it's placed at the end of the list. def register(uri, handler, in_front=false) - script_name, path_info, handlers = @classifier.resolve(uri) - - if not handlers + begin @classifier.register(uri, [handler]) - else - if path_info.length == 0 or (script_name == Const::SLASH and path_info == Const::SLASH) - if in_front - handlers.unshift(handler) - else - handlers << handler - end - else - @classifier.register(uri, [handler]) - end + rescue URIClassifier::RegistrationError + handlers = @classifier.resolve(uri)[2] + method_name = in_front ? 'unshift' : 'push' + handlers.send(method_name, handler) end - handler.listener = self end |