# # Ronin Web - A Ruby library for Ronin that provides support for web # scraping and spidering functionality. # # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of Ronin Web. # # Ronin 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 3 of the License, or # (at your option) any later version. # # Ronin 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 Ronin. If not, see <http://www.gnu.org/licenses/>. # require 'ronin/web/middleware/helpers' require 'ronin/web/middleware/files' require 'ronin/web/middleware/directories' require 'ronin/web/middleware/router' require 'ronin/web/middleware/proxy' require 'ronin/templates/erb' require 'ronin/ui/output' require 'ronin/extensions/meta' require 'thread' require 'rack' require 'sinatra' module Ronin module Web module Server # # The base-class for all Ronin Web Servers. Extends # [Sinatra::Base](http://rubydoc.info/gems/sinatra/Sinatra/Base) # with additional helper methods and Rack {Middleware}. # class Base < Sinatra::Base include Templates::Erb include UI::Output::Helpers extend UI::Output::Helpers # 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 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 # # @api public # 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 # # @api public # def Base.handler=(name) @@ronin_web_server_handler = name 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 # # @api public # 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 # # @api semipublic # 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") 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 # # @api public # 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 # # @api public # 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 # # @api public # def self.default(&block) class_def(:default_response,&block) return self end # # Hosts the contents of a file. # # @param [String] remote_path # The path the web server will host the file at. # # @param [String] local_path # The path to the local file. # # @example # file '/robots.txt', '/path/to/my_robots.txt' # # @see Middleware::Files # # @since 0.3.0 # # @api public # def self.file(remote_path,local_path) use Middleware::Files, :paths => {remote_path => local_path} end # # Hosts the contents of files. # # @yield [files] # The given block will be passed the files middleware to # configure. # # @yieldparam [Middleware::Files] # The files middleware object. # # @example # files do |files| # files.map '/foo.txt', 'foo.txt' # files.map /\.exe$/, 'trojan.exe' # end # # @see Middleware::Files # # @since 0.3.0 # # @api public # def self.files(&block) use(Middleware::Files,&block) end # # Hosts the contents of the directory. # # @param [String] remote_path # The path the web server will host the directory at. # # @param [String] local_path # The path to the local directory. # # @example # directory '/download/', '/tmp/files/' # # @see Middleware::Directories # # @since 0.2.0 # # @api public # def self.directory(remote_path,local_path) use Middleware::Directories, :paths => {remote_path => local_path} end # # Hosts the contents of directories. # # @yield [dirs] # The given block will be passed the directories middleware to # configure. # # @yieldparam [Middleware::Directories] # The directories middleware object. # # @example # directories do |dirs| # dirs.map '/downloads', '/tmp/ronin_downloads' # dirs.map '/images', '/tmp/ronin_images' # dirs.map '/pdfs', '/tmp/ronin_pdfs' # end # # @see Middleware::Directories # # @since 0.3.0 # # @api public # def self.directories(&block) use(Middleware::Directories,&block) end # # Hosts the static contents within a given directory. # # @param [String] path # The path to a directory to serve static content from. # # @example # public_dir 'path/to/another/public' # # @since 0.2.0 # # @api public # def self.public_dir(path) self.directory('/',path) end # # Routes all requests within a given directory into another # web server. # # @param [String, Regexp] dir # The directory that requests for will be routed from. # # @param [#call] server # The web server to route requests to. # # @example # map '/subapp/', SubApp # # @see Middleware::Router # # @since 0.2.0 # # @api public # def self.map(dir,server) use Middleware::Router do |router| router.draw :path => dir, :to => server end end # # Routes requests with a specific Host header to another # web server. # # @param [String, Regexp] name # The host-name to route requests for. # # @param [#call] server # The web server to route the requests to. # # @example # vhost 'cdn.evil.com', EvilServer # # @since 0.3.0 # # @api public # def self.vhost(name,server) use Middleware::Router do |router| router.draw :vhost => name, :to => server end end # # Proxies requests to a given path. # # @param [String] path # The path to proxy requests for. # # @param [Hash] options # Additional options. # # @yield [(response), body] # If a block is given, it will be passed the optional # response of the proxied request and the body received # from the proxied request. # # @yieldparam [Net::HTTP::Response] response # The response. # # @yieldparam [String] body # The body from the response. # # @example # proxy '/login.php' do |response,body| # body.gsub(/https/,'http') # end # # @see Middleware::Proxy # # @since 0.2.0 # # @api public # def self.proxy(path,options={},&block) use(Middleware::Proxy,options,&block) end protected # # Returns an HTTP 404 response with an empty body. # # @since 0.2.0 # # @api semipublic # def default_response halt 404, '' end enable :sessions any('*') { default_response } end end end end