# # Ronin Web - A Ruby library for Ronin that provides support for web # scraping and spidering functionality. # # Copyright (c) 2006-2009 Hal Brodigan (postmodern.mod3 at gmail.com) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # require 'ronin/web/server/helpers/rendering' require 'ronin/web/server/helpers/proxy' require 'ronin/web/server/files' require 'ronin/web/server/hosts' require 'ronin/static/finders' require 'ronin/templates/erb' require 'ronin/ui/output' require 'ronin/extensions/meta' require 'set' require 'thread' require 'rack' require 'sinatra' module Ronin module Web module Server class Base < Sinatra::Base include Static::Finders include Rack::Utils include Templates::Erb extend UI::Output include Files include Hosts # Default interface to run the Web Server on DEFAULT_HOST = '0.0.0.0' # Default port to run the Web Server on DEFAULT_PORT = 8000 # Default list of index file-names to search for in directories DEFAULT_INDICES = ['index.html', 'index.htm'] # Directory to search for views within VIEWS_DIR = File.join('ronin','web','server','views') set :host, DEFAULT_HOST set :port, DEFAULT_PORT # # The default Rack Handler to run all web servers with. # # @return [String] # The class name of the Rack Handler to use. # # @since 0.2.0 # def Base.handler @@ronin_web_server_handler ||= nil end # # Sets the default Rack Handler to run all web servers with. # # @param [String] name # The name of the handler. # # @return [String] # The name of the new handler. # # @since 0.2.0 # def Base.handler=(name) @@ronin_web_server_handler = name end # # The list of index files to search for when requesting the # contents of a directory. # # @return [Set] # The names of index files. # # @since 0.2.0 # def Base.indices @@ronin_web_server_indices ||= Set[*DEFAULT_INDICES] end # # Adds a new index to the +Base.indices+ list. # # @param [String, Symbol] name # The index name to add. # # @since 0.2.0 # def Base.index(name) Base.indices << name.to_s end # # The list of Rack Handlers to attempt to use with the web server. # # @return [Array] # The names of handler classes. # # @since 0.2.0 # def self.handlers handlers = self.server if Base.handler handlers = [Base.handler] + handlers end return handlers end # # Attempts to load the desired Rack Handler to run the web server # with. # # @return [Rack::Handler] # The handler class to use to run the web server. # # @raise [StandardError] # None of the handlers could be loaded. # # @since 0.2.0 # def self.handler_class self.handlers.find do |name| begin return Rack::Handler.get(name) rescue Gem::LoadError => e raise(e) rescue NameError, ::LoadError next end end raise(StandardError,"unable to find any Rack handlers",caller) end # # Run the web server using the Rack Handler returned by # +handler_class+. # # @param [Hash] options Additional options. # # @option options [String] :host # The host the server will listen on. # # @option options [Integer] :port # The port the server will bind to. # # @option options [Boolean] :background (false) # Specifies wether the server will run in the background or run # in the foreground. # # @since 0.2.0 # def self.run!(options={}) rack_options = { :Host => (options[:host] || self.host), :Port => (options[:port] || self.port) } runner = lambda { |handler,server,options| print_info "Starting Web Server on #{options[:Host]}:#{options[:Port]}" print_debug "Using Web Server handler #{handler}" handler.run(server,options) do |server| trap(:INT) do # Use thins' hard #stop! if available, # otherwise just #stop server.respond_to?(:stop!) ? server.stop! : server.stop end set :running, true end } handler = self.handler_class if options[:background] Thread.new(handler,self,rack_options,&runner) else runner.call(handler,self,rack_options) end return self end # # Route any type of request for a given URL pattern. # # @param [String] path # The URL pattern to handle requests for. # # @yield [] # The block that will handle the request. # # @example # any '/submit' do # puts request.inspect # end # # @since 0.2.0 # def self.any(path,options={},&block) get(path,options,&block) put(path,options,&block) post(path,options,&block) delete(path,options,&block) end # # Sets the default route. # # @yield [] # The block that will handle all other requests. # # @example # default do # status 200 # content_type :html # # %{ # <html> # <body> # <center><h1>YOU LOSE THE GAME</h1></center> # </body> # </html> # } # end # # @since 0.2.0 # def self.default(&block) class_def(:default_response,&block) return self end # # Routes all requests within a given directory into another # web server. # # @param [String] dir # The directory that requests for will be routed from. # # @param [Base, #call] server # The web server to route requests to. # # @example # MyApp.map '/subapp/', SubApp # # @since 0.2.0 # def self.map(dir,server) dir = File.join(dir,'') before do if dir == request.path_info[0,dir.length] # remove the dir from the beginning of the path # before passing it to the server request.env['PATH_INFO'] = request.path_info[dir.length-1..-1] halt(*server.call(request.env)) end end end # # Hosts the static contents within a given directory. # # @param [String] directory # The path to a directory to serve static content from. # # @example # MyApp.public_dir 'path/to/another/public' # # @since 0.2.0 # def self.public_dir(directory) directory = File.expand_path(directory) before do sub_path = File.expand_path(File.join('',request.path_info)) full_path = File.join(directory,sub_path) return_file(full_path) if File.file?(full_path) end end protected # # Returns an HTTP 404 response with an empty body. # # @since 0.2.0 # def default_response halt 404, '' end enable :sessions helpers Helpers::Rendering helpers Helpers::Proxy not_found do default_response end end end end end