From 071a678a156dde974d8e470b659c89cb02b07b3b Mon Sep 17 00:00:00 2001 From: drbrain Date: Tue, 10 May 2011 00:13:58 +0000 Subject: * lib/webrick: Add Documentation git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31499 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/webrick/accesslog.rb | 75 +++++++++++++++++++++++++++-- lib/webrick/htmlutils.rb | 3 ++ lib/webrick/httpproxy.rb | 17 +++++++ lib/webrick/httprequest.rb | 55 +++++++++++++++++---- lib/webrick/httpresponse.rb | 72 +++++++++++++++++++++++++++- lib/webrick/httpserver.rb | 47 ++++++++++++++++++ lib/webrick/httpservlet/abstract.rb | 87 +++++++++++++++++++++++++++++++++- lib/webrick/httpservlet/filehandler.rb | 31 ++++++++++++ lib/webrick/log.rb | 14 ++++++ lib/webrick/server.rb | 8 ++++ 10 files changed, 393 insertions(+), 16 deletions(-) (limited to 'lib/webrick') diff --git a/lib/webrick/accesslog.rb b/lib/webrick/accesslog.rb index 9a14ae58e5..2b46a805d2 100644 --- a/lib/webrick/accesslog.rb +++ b/lib/webrick/accesslog.rb @@ -8,21 +8,90 @@ # $IPR: accesslog.rb,v 1.1 2002/10/01 17:16:32 gotoyuzo Exp $ module WEBrick + + ## + # AccessLog provides logging to various files in various formats. + # + # Multiple logs may be written to at the same time: + # + # access_log = [ + # [$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT], + # [$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT], + # ] + # + # server = WEBrick::HTTPServer.new :AccessLog => access_log + # + # Custom log formats may be defined. WEBrick::AccessLog provides a subset + # of the formatting from Apache's mod_log_config + # http://httpd.apache.org/docs/mod/mod_log_config.html#formats. See + # AccessLog::setup_params for a list of supported options + module AccessLog + + ## + # Raised if a parameter such as %e, %i, %o or %n is used without fetching + # a specific field. + class AccessLogError < StandardError; end + ## + # The Common Log Format's time format + CLF_TIME_FORMAT = "[%d/%b/%Y:%H:%M:%S %Z]" + + ## + # Common Log Format + COMMON_LOG_FORMAT = "%h %l %u %t \"%r\" %s %b" + + ## + # Short alias for Common Log Format + CLF = COMMON_LOG_FORMAT + + ## + # Referer Log Format + REFERER_LOG_FORMAT = "%{Referer}i -> %U" + + ## + # User-Agent Log Format + AGENT_LOG_FORMAT = "%{User-Agent}i" + + ## + # Combined Log Format + COMBINED_LOG_FORMAT = "#{CLF} \"%{Referer}i\" \"%{User-agent}i\"" module_function - # This format specification is a subset of mod_log_config of Apache. - # http://httpd.apache.org/docs/mod/mod_log_config.html#formats - def setup_params(config, req, res) + # This format specification is a subset of mod_log_config of Apache: + # + # %a:: Remote IP address + # %b:: Total response size + # %e{variable}:: Given variable in ENV + # %f:: Response filename + # %h:: Remote host name + # %{header}i:: Given request header + # %l:: Remote logname, always "-" + # %m:: Request method + # %{attr}n:: Given request attribute from req.attributes + # %{header}o:: Given response header + # %p:: Server's request port + # %{format}p:: The canonical port of the server serving the request or the + # actual port or the client's actual port. Valid formats are + # canonical, local or remote. + # %q:: Request query string + # %r:: First line of the request + # %s:: Request status + # %t:: Time the request was recieved + # %T:: Time taken to process the request + # %u:: Remote user from auth + # %U:: Unparsed URI + # %%:: Literal % + + def setup_params(config, req, res) params = Hash.new("") params["a"] = req.peeraddr[3] params["b"] = res.sent_size diff --git a/lib/webrick/htmlutils.rb b/lib/webrick/htmlutils.rb index 01b9d2cc26..ed901f1ce2 100644 --- a/lib/webrick/htmlutils.rb +++ b/lib/webrick/htmlutils.rb @@ -11,6 +11,9 @@ module WEBrick module HTMLUtils + ## + # Escapes &, ", > and < in +string+ + def escape(string) str = string ? string.dup : "" str.gsub!(/&/n, '&') diff --git a/lib/webrick/httpproxy.rb b/lib/webrick/httpproxy.rb index ce99c55d8f..33ce17b2d4 100644 --- a/lib/webrick/httpproxy.rb +++ b/lib/webrick/httpproxy.rb @@ -33,7 +33,24 @@ module WEBrick end end + ## + # An HTTP Proxy server which proxies GET, HEAD and POST requests. + class HTTPProxyServer < HTTPServer + + ## + # Proxy server configurations. The proxy server handles the following + # configuration items in addition to those supported by HTTPServer: + # + # :ProxyAuthProc:: Called with a request and response to authorize a + # request + # :ProxyVia:: Appended to the via header + # :ProxyURI:: The proxy server's URI + # :ProxyContentHandler:: Called with a request and resopnse and allows + # modification of the response + # :ProxyTimeout:: Sets the proxy timeouts to 30 seconds for open and 60 + # seconds for read operations + def initialize(config={}, default=Config::HTTP) super(config, default) c = @config diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb index d179995d77..5dda878e99 100644 --- a/lib/webrick/httprequest.rb +++ b/lib/webrick/httprequest.rb @@ -15,23 +15,27 @@ require 'webrick/httputils' require 'webrick/cookie' module WEBrick + + ## + # An HTTP request. class HTTPRequest + BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] - # Request line + # :section: Request line attr_reader :request_line attr_reader :request_method, :unparsed_uri, :http_version - # Request-URI + # :section: Request-URI attr_reader :request_uri, :path attr_accessor :script_name, :path_info, :query_string - # Header and entity body + # :section: Header and entity body attr_reader :raw_header, :header, :cookies attr_reader :accept, :accept_charset attr_reader :accept_encoding, :accept_language - # Misc + # :section: attr_accessor :user attr_reader :addr, :peeraddr attr_reader :attributes @@ -137,6 +141,9 @@ module WEBrick @body.empty? ? nil : @body end + ## + # Request query as a Hash + def query unless @query parse_query() @@ -144,14 +151,23 @@ module WEBrick @query end + ## + # The content-length header + def content_length return Integer(self['content-length']) end + ## + # The content-type header + def content_type return self['content-type'] end + ## + # Retrieves +header_name+ + def [](header_name) if @header value = @header[header_name.downcase] @@ -159,6 +175,9 @@ module WEBrick end end + ## + # Iterates over the request headers + def each @header.each{|k, v| value = @header[k] @@ -166,31 +185,49 @@ module WEBrick } end + ## + # The host this request is for + def host return @forwarded_host || @host end + ## + # The port this request is for + def port return @forwarded_port || @port end + ## + # The server name this request is for + def server_name return @forwarded_server || @config[:ServerName] end + ## + # The client's IP address + def remote_ip return self["client-ip"] || @forwarded_for || @peeraddr[3] end + ## + # Is this an SSL request? + def ssl? return @request_uri.scheme == "https" end + ## + # Should the connection this request was made on be kept alive? + def keep_alive? @keep_alive end - def to_s + def to_s # :nodoc: ret = @request_line.dup @raw_header.each{|line| ret << line } ret << CRLF @@ -210,11 +247,11 @@ module WEBrick end end - def meta_vars - # This method provides the metavariables defined by the revision 3 - # of ``The WWW Common Gateway Interface Version 1.1''. - # (http://Web.Golux.Com/coar/cgi/) + # This method provides the metavariables defined by the revision 3 + # of "The WWW Common Gateway Interface Version 1.1" + # http://Web.Golux.Com/coar/cgi/ + def meta_vars meta = Hash.new cl = self["Content-Length"] diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb index 0c292bed2d..b7c61a2b2f 100644 --- a/lib/webrick/httpresponse.rb +++ b/lib/webrick/httpresponse.rb @@ -15,10 +15,17 @@ require 'webrick/httputils' require 'webrick/httpstatus' module WEBrick + ## + # An HTTP response. + class HTTPResponse attr_reader :http_version, :status, :header attr_reader :cookies attr_accessor :reason_phrase + + ## + # Body may be a String or IO subclass. + attr_accessor :body attr_accessor :request_method, :request_uri, :request_http_version @@ -26,6 +33,9 @@ module WEBrick attr_accessor :keep_alive attr_reader :config, :sent_size + ## + # Creates a new HTTP response object + def initialize(config) @config = config @buffer_size = config[:OutputBufferSize] @@ -45,57 +55,96 @@ module WEBrick @sent_size = 0 end + ## + # The response's HTTP status line + def status_line "HTTP/#@http_version #@status #@reason_phrase #{CRLF}" end + ## + # Sets the response's status to the +status+ code + def status=(status) @status = status @reason_phrase = HTTPStatus::reason_phrase(status) end + ## + # Retrieves the response header +field+ + def [](field) @header[field.downcase] end + ## + # Sets the response header +field+ to +value+ + def []=(field, value) @header[field.downcase] = value.to_s end + ## + # The content-length header + def content_length if len = self['content-length'] return Integer(len) end end + ## + # Sets the content-length header to +len+ + def content_length=(len) self['content-length'] = len.to_s end + ## + # The content-type header + def content_type self['content-type'] end + ## + # Sets the content-type header to +type+ + def content_type=(type) self['content-type'] = type end + ## + # Iterates over each header in the resopnse + def each - @header.each{|k, v| yield(k, v) } + @header.each{|field, value| yield(field, value) } end + ## + # Will this response body be returned using chunked transfer-encoding? + def chunked? @chunked end + ## + # Enables chunked transfer encoding. + def chunked=(val) @chunked = val ? true : false end + ## + # Will this response's connection be kept alive? + def keep_alive? @keep_alive end + ## + # Sends the response on +socket+ + def send_response(socket) begin setup_header() @@ -110,6 +159,9 @@ module WEBrick end end + ## + # Sets up the headers for sending + def setup_header() @reason_phrase ||= HTTPStatus::reason_phrase(@status) @header['server'] ||= @config[:ServerSoftware] @@ -165,6 +217,9 @@ module WEBrick end end + ## + # Sends the headers on +socket+ + def send_header(socket) if @http_version.major > 0 data = status_line() @@ -180,6 +235,9 @@ module WEBrick end end + ## + # Sends the body on +socket+ + def send_body(socket) case @body when IO then send_body_io(socket) @@ -187,18 +245,28 @@ module WEBrick end end - def to_s + def to_s # :nodoc: ret = "" send_response(ret) ret end + ## + # Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+. + # + # Example: + # + # res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect + def set_redirect(status, url) @body = "#{url.to_s}.\n" @header['location'] = url.to_s raise status end + ## + # Creates an error page for exception +ex+ with an optional +backtrace+ + def set_error(ex, backtrace=false) case ex when HTTPStatus::Status diff --git a/lib/webrick/httpserver.rb b/lib/webrick/httpserver.rb index 929d856a4a..ddf1ac7404 100644 --- a/lib/webrick/httpserver.rb +++ b/lib/webrick/httpserver.rb @@ -19,7 +19,28 @@ require 'webrick/accesslog' module WEBrick class HTTPServerError < ServerError; end + ## + # An HTTP Server + class HTTPServer < ::WEBrick::GenericServer + ## + # Creates a new HTTP server according to +config+ + # + # An HTTP server uses the following attributes: + # + # :AccessLog:: An array of access logs. See WEBrick::AccessLog + # :BindAddress:: Local address for the server to bind to + # :DocumentRoot:: Root path to serve files from + # :DocumentRootOptions:: Options for the default HTTPServlet::FileHandler + # :HTTPVersion:: The HTTP version of this server + # :Port:: Port to listen on + # :RequestCallback:: Called with a request and response before each + # request is serviced. + # :RequestTimeout:: Maximum time to wait between requests + # :ServerAlias:: Array of alternate names for this server for virtual + # hosting + # :ServerName:: Name for this server for virtual hosting + def initialize(config={}, default=Config::HTTP) super(config, default) @http_version = HTTPVersion::convert(@config[:HTTPVersion]) @@ -40,6 +61,9 @@ module WEBrick @virtual_hosts = Array.new end + ## + # Processes requests on +sock+ + def run(sock) while true res = HTTPResponse.new(@config) @@ -93,6 +117,9 @@ module WEBrick end end + ## + # Services +req+ and fills in +res+ + def service(req, res) if req.unparsed_uri == "*" if req.request_method == "OPTIONS" @@ -115,23 +142,37 @@ module WEBrick res["allow"] = "GET,HEAD,POST,OPTIONS" end + ## + # Mounts +servlet+ on +dir+ passing +options+ to the servlet at creation + # time + def mount(dir, servlet, *options) @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir)) @mount_tab[dir] = [ servlet, options ] end + ## + # Mounts +proc+ or +block+ on +dir+ and calls it with a + # WEBrick::HTTPRequest and WEBrick::HTTPResponse + def mount_proc(dir, proc=nil, &block) proc ||= block raise HTTPServerError, "must pass a proc or block" unless proc mount(dir, HTTPServlet::ProcHandler.new(proc)) end + ## + # Unmounts +dir+ + def unmount(dir) @logger.debug(sprintf("unmount %s.", dir)) @mount_tab.delete(dir) end alias umount unmount + ## + # Finds a servlet for +path+ + def search_servlet(path) script_name, path_info = @mount_tab.scan(path) servlet, options = @mount_tab[script_name] @@ -140,6 +181,9 @@ module WEBrick end end + ## + # Adds +server+ as a virtual host. + def virtual_host(server) @virtual_hosts << server @virtual_hosts = @virtual_hosts.sort_by{|s| @@ -151,6 +195,9 @@ module WEBrick } end + ## + # Finds the appropriate virtual host to handle +req+ + def lookup_server(req) @virtual_hosts.find{|s| (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) && diff --git a/lib/webrick/httpservlet/abstract.rb b/lib/webrick/httpservlet/abstract.rb index f8bf14a330..0d5c5ae48d 100644 --- a/lib/webrick/httpservlet/abstract.rb +++ b/lib/webrick/httpservlet/abstract.rb @@ -18,17 +18,88 @@ module WEBrick module HTTPServlet class HTTPServletError < StandardError; end + ## + # AbstractServlet allows HTTP server modules to be reused across multiple + # servers and allows encapsulation of functionality. + # + # By default a servlet will respond to GET, HEAD (through an alias to GET) + # and OPTIONS requests. + # + # By default a new servlet is initialized for every request. A servlet + # instance can be reused by overriding ::get_instance in the + # AbstractServlet subclass. + # + # == A Simple Servlet + # + # class Simple < WEBrick::HTTPServlet::AbstractServlet + # def do_GET request, response + # status, content_type, body = do_stuff_with request + # + # response.status = status + # response['Content-Type'] = content_type + # response.body = body + # end + # + # def do_stuff_with request + # return 200, 'text/plain', 'you got a page' + # end + # end + # + # This servlet can be mounted on a server at a given path: + # + # server.mount '/simple', Simple + # + # == Servlet Configuration + # + # Servlets can be configured via initialize. The first argument is the + # HTTP server the servlet is being initialized for. + # + # class Configureable < Simple + # def initialize server, color, size + # super server + # @color = color + # @size = size + # end + # + # def do_stuff_with request + # content = "

Hello, World!" + # + # return 200, "text/html", content + # end + # end + # + # This servlet must be provided two arguments at mount time: + # + # server.mount '/configurable', Configurable, 'red', '2em' + class AbstractServlet - def self.get_instance(config, *options) - self.new(config, *options) + + ## + # Factory for servlet instances that will handle a request from +server+ + # using +options+ from the mount point. By default a new servlet + # instance is created for every call. + + def self.get_instance(server, *options) + self.new(server, *options) end + ## + # Initializes a new servlet for +server+ using +options+ which are + # stored as-is in +@options+. +@logger+ is also provided. + def initialize(server, *options) @server = @config = server @logger = @server[:Logger] @options = options end + ## + # Dispatches to a +do_+ method based on +req+ if such a method is + # available. (+do_GET+ for a GET request). Raises a MethodNotAllowed + # exception if the method is not implemented. + def service(req, res) method_name = "do_" + req.request_method.gsub(/-/, "_") if respond_to?(method_name) @@ -39,14 +110,23 @@ module WEBrick end end + ## + # Raises a NotFound exception + def do_GET(req, res) raise HTTPStatus::NotFound, "not found." end + ## + # Dispatches to do_GET + def do_HEAD(req, res) do_GET(req, res) end + ## + # Returns the allowed HTTP request methods + def do_OPTIONS(req, res) m = self.methods.grep(/\Ado_([A-Z]+)\z/) {$1} m.sort! @@ -55,6 +135,9 @@ module WEBrick private + ## + # Redirects to a path ending in / + def redirect_to_directory_uri(req, res) if req.path[-1] != ?/ location = WEBrick::HTTPUtils.escape_path(req.path + "/") diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb index daad8abd27..263c9fc80f 100644 --- a/lib/webrick/httpservlet/filehandler.rb +++ b/lib/webrick/httpservlet/filehandler.rb @@ -125,17 +125,48 @@ module WEBrick end end + ## + # Serves files from a directory + class FileHandler < AbstractServlet HandlerTable = Hash.new + ## + # Allow custom handling of requests for files with +suffix+ by class + # +handler+ + def self.add_handler(suffix, handler) HandlerTable[suffix] = handler end + ## + # Remove custom handling of requests for files with +suffix+ + def self.remove_handler(suffix) HandlerTable.delete(suffix) end + ## + # Creates a FileHandler servlet on +server+ that serves files starting + # at directory +root+ + # + # If +options+ is a Hash the following keys are allowed: + # + # :AcceptableLanguages:: Array of languages allowed for accept-language + # :DirectoryCallback:: Allows preprocessing of directory requests + # :FancyIndexing:: If true, show an index for directories + # :FileCallback:: Allows preprocessing of file requests + # :HandlerCallback:: Allows preprocessing of requests + # :HandlerTable:: Maps file suffixes to file handlers. + # DefaultFileHandler is used by default but any servlet + # can be used. + # :NondisclosureName:: Do not show files matching this array of globs + # :UserDir:: Directory inside ~user to serve content from for /~user + # requests. Only works if mounted on / + # + # If +options+ is true or false then +:FancyIndexing+ is enabled or + # disabled respectively. + def initialize(server, root, options={}, default=Config::FileHandler) @config = server.config @logger = @config[:Logger] diff --git a/lib/webrick/log.rb b/lib/webrick/log.rb index 765599aa5b..fe253e51a2 100644 --- a/lib/webrick/log.rb +++ b/lib/webrick/log.rb @@ -9,12 +9,23 @@ # $IPR: log.rb,v 1.26 2002/10/06 17:06:10 gotoyuzo Exp $ module WEBrick + + ## + # A generic logging class + class BasicLog # log-level constant FATAL, ERROR, WARN, INFO, DEBUG = 1, 2, 3, 4, 5 attr_accessor :level + ## + # Initializes a new logger for +log_file+ that outputs messages at +level+ + # or higher. +log_file+ can be a filename, an IO-like object that + # responds to #<< or nil which outputs to $stderr. + # + # If no level is given INFO is chosen by default + def initialize(log_file=nil, level=nil) @level = level || INFO case log_file @@ -71,6 +82,9 @@ module WEBrick end end + ## + # A logging class with timestamps + class Log < BasicLog attr_accessor :time_format diff --git a/lib/webrick/server.rb b/lib/webrick/server.rb index c4d23bd67b..30f4dc3143 100644 --- a/lib/webrick/server.rb +++ b/lib/webrick/server.rb @@ -23,7 +23,15 @@ module WEBrick end end + ## + # A generic module for daemonizing a process + class Daemon + + ## + # Performs the standard operations for daemonizing a process. Runs a + # block, if given. + def Daemon.start exit!(0) if fork Process::setsid -- cgit v1.2.3