App

class
Superclass
Mongrel2::Handler
Included Modules
Strelka::Constants
Strelka::ResponseHelpers
Extended With
Loggability
Configurability
Strelka::Discovery
Strelka::MethodUtilities
Strelka::PluginLoader

The Strelka HTTP application base class.

Constants

CONFIG_DEFAULTS

Default config

Public Class Methods

anchor
configure( config=nil )

Configure the App. Override this if you wish to add additional configuration to the 'app' section of the config that will be passed to you when the config is loaded.

# File lib/strelka/app.rb, line 68
def self::configure( config=nil )
        config = Strelka::App.defaults.merge( config || {} )
        self.devmode = config[:devmode] || $DEBUG
        self.log.info "Enabled developer mode." if self.devmode?
end
anchor
default_app_instance()

Return an instance of the App configured for the handler in the currently-loaded Mongrel2 config that corresponds to the default_appid.

# File lib/strelka/app.rb, line 105
def self::default_app_instance
        appid = self.default_appid
        return self.app_instance_for( appid )
end
anchor
default_appid()

Calculate a default application ID for the class based on either its ID constant or its name and return it.

# File lib/strelka/app.rb, line 86
def self::default_appid
        self.log.info "Looking up appid for %p" % [ self.class ]
        appid = nil

        if self.const_defined?( :ID )
                appid = self.const_get( :ID )
                self.log.info "  app has an ID: %p" % [ appid ]
        else
                appid = ( self.name || "anonymous#{self.object_id}" ).downcase
                appid.gsub!( /[^[:alnum:]]+/, '-' )
                self.log.info "  deriving one from the class name: %p" % [ appid ]
        end

        return appid
end
anchor
devmode?()

Returns true if the application has been configured to run in 'developer mode'. Developer mode is mostly informational by default (it just makes logging more verbose), but plugins and such might alter their behavior based on this setting.

# File lib/strelka/app.rb, line 59
def self::devmode?
        return @devmode
end
anchor
run( appid=nil )

Overridden from Mongrel2::Handler – use the value returned from .default_appid if one is not specified.

# File lib/strelka/app.rb, line 77
def self::run( appid=nil )
        appid ||= self.default_appid
        self.log.info "Starting up with appid %p." % [ appid ]
        super( appid )
end

Public Instance Methods

anchor
devmode()

'Developer mode' flag.

# File lib/strelka/app.rb, line 53
singleton_attr_writer :devmode
anchor
subclasses()

The Hash of Strelka::App subclasses, keyed by the Pathname of the file they were loaded from, or nil if they weren't loaded via ::load.

# File lib/strelka/app.rb, line 49
singleton_attr_reader :subclasses

Application declarative methods

↑ top

Public Class Methods

anchor
default_type( newtype=nil )

Get/set the Content-type of requests that don't set one. Leaving this unset will leave the Content-type unset.

# File lib/strelka/app.rb, line 117
def self::default_type( newtype=nil )
        @default_type = newtype if newtype
        return @default_type
end
anchor
new( * )

Dump the application stack when a new instance is created.

# File lib/strelka/app.rb, line 128
def initialize( * )
        self.class.dump_application_stack
        super
end

Public Instance Methods

anchor
handle( request )

The main Mongrel2 entrypoint – accept Strelka::Requests and return Strelka::Responses.

# File lib/strelka/app.rb, line 149
def handle( request )
        response = nil

        # Dispatch the request after allowing plugins to to their thing
        status_info = catch( :finish ) do
                self.log.debug "Starting dispatch of request %p" % [ request ]

                # Run fixup hooks on the request
                request = self.fixup_request( request )
                self.log.debug "  done with request fixup"
                response = self.handle_request( request )
                self.log.debug "  done with handler"
                response = self.fixup_response( response )
                self.log.debug "  done with response fixup"

                nil # rvalue for the catch
        end

        # Status response
        if status_info
                self.log.debug "Preparing a status response: %p" % [ status_info ]
                return self.prepare_status_response( request, status_info )
        end

        return response
rescue => err
        self.log.error "%s: %s %s" % [ err.class.name, err.message, err.backtrace.first ]
        err.backtrace[ 1..-1 ].each {|frame| self.log.debug('  ' + frame) }

        status_info = { :status => HTTP::SERVER_ERROR, :message => 'internal server error' }
        return self.prepare_status_response( request, status_info )
end
anchor
handle_async_upload_start( request )

Handle uploads larger than the server's configured limit with a 413: Request Entity Too Large before dropping the connection.

# File lib/strelka/app.rb, line 185
def handle_async_upload_start( request )
        status_info = { :status => HTTP::REQUEST_ENTITY_TOO_LARGE, :message => 'Request too large.' }
        response = self.prepare_status_response( request, status_info )
        response.headers.connection = 'close'
        self.conn.reply( response )

        explanation = "If you wish to handle requests like this, either set your server's "
        explanation << "'limits.content_length' setting to a higher value than %d, or override " %
                 [ request.content_length ]
        explanation << "#handle_async_upload_start."

        self.log.warn "Async upload from %s dropped." % [ request.remote_ip ]
        self.log.info( explanation )

        self.conn.reply_close( request )

        return nil
end
anchor
run()

Run the app – overriden to set the process name to something interesting.

# File lib/strelka/app.rb, line 139
def run
        procname = "%s %s: %p %s" % [ RUBY_ENGINE, RUBY_VERSION, self.class, self.conn ]
        $0 = procname

        super
end

Protected Instance Methods

anchor
fixup_head_response( response )

Remove the entity body of responses to HEAD requests.

# File lib/strelka/app.rb, line 291
def fixup_head_response( response )
        self.log.debug "Truncating entity body of HEAD response."
        response.headers.content_length = response.get_content_length
        response.body = ''
end
anchor
fixup_request( request )

Make any changes to the request that are necessary before handling it and return it. This is an alternate extension-point for plugins that wish to modify or replace the request before the request cycle is started.

# File lib/strelka/app.rb, line 213
def fixup_request( request )
        return request
end
anchor
fixup_response( response )

Make any changes to the response that are necessary before handing it to Mongrel and return it. This is an alternate extension-point for plugins that wish to modify or replace the response after the whole request cycle is completed.

# File lib/strelka/app.rb, line 251
def fixup_response( response )
        self.log.debug "Fixing up response: %p" % [ response ]
        self.fixup_response_content_type( response )
        self.fixup_head_response( response ) if
                response.request && response.request.verb == :HEAD
        self.log.debug "  after fixup: %p" % [ response ]

        return response
end
anchor
fixup_response_content_type( response )

If the response doesn't yet have a Content-type header, and the app has defined a default (via ::default_type), set it to the default.

# File lib/strelka/app.rb, line 264
def fixup_response_content_type( response )

        # Make the error for returning something other than a Response object a little
        # nicer.
        unless response.respond_to?( :content_type )
                self.log.error "expected response (%p, a %p) to respond to #content_type" %
                        [ response, response.class ]
                finish_with( HTTP::SERVER_ERROR, "malformed response" )
        end

        restype = response.content_type

        if !restype
                if (( default = self.class.default_type ))
                        self.log.debug "Setting content type of the response to the default: %p" %
                                [ default ]
                        response.content_type = default
                else
                        self.log.debug "No default content type"
                end
        else
                self.log.debug "Content type already set: %p" % [ restype ]
        end
end
anchor
handle_request( request, &block )

Handle the request and return a response. This is the main extension-point for the plugin system. Without being overridden or extended by plugins, this method just returns the default Mongrel2::HTTPRequest#response. If you override this directly in your App subclass, you'll need to super with a block if you wish the plugins to run on the request, then do whatever it is you want in the block and return the response, which the plugins will again have an opportunity to modify.

Example:

class MyApp < Strelka::App
    def handle_request( request )
        super do |req|
            res = req.response
            res.content_type = 'text/plain'
            res.puts "Hello!"
            return res
        end
    end
end
# File lib/strelka/app.rb, line 238
def handle_request( request, &block )
        if block
                return block.call( request )
        else
                return request.response
        end
end
anchor
prepare_status_response( request, status_info )

Create a response to specified request based on the specified status_code and message.

# File lib/strelka/app.rb, line 300
def prepare_status_response( request, status_info )
        status_code, message = status_info.values_at( :status, :message )
        self.log.info "Non-OK response: %d (%s)" % [ status_code, message ]

        request.notes[:status_info] = status_info
        response = request.response
        response.reset
        response.status = status_code

        # Some status codes allow explanatory text to be returned; some forbid it. Append the
        # message for those that allow one.
        unless request.verb == :HEAD || response.bodiless?
                self.log.debug "Writing plain-text response body: %p" % [ message ]
                response.content_type = 'text/plain'
                response.puts( message )
        end

        # Now assign any headers to the response that are part of the status
        if status_info.key?( :headers )
                status_info[:headers].each do |hdr, value|
                        response.headers[ hdr ] = value
                end
        end

        return response
end