From 28afe277a8e543da0e6353bdacbcad0b69739e06 Mon Sep 17 00:00:00 2001 From: drbrain Date: Sat, 26 Jan 2013 01:12:54 +0000 Subject: * lib/webrick/accesslog.rb: Improved WEBrick documentation. * lib/webrick/cgi.rb: ditto. * lib/webrick/config.rb: ditto. * lib/webrick/cookie.rb: ditto. * lib/webrick/httpauth/authenticator.rb: ditto. * lib/webrick/httpauth/basicauth.rb: ditto. * lib/webrick/httpauth/digestauth.rb: ditto. * lib/webrick/httpproxy.rb: ditto. * lib/webrick/httprequest.rb: ditto. * lib/webrick/httpresponse.rb: ditto. * lib/webrick/https.rb: ditto. * lib/webrick/httpserver.rb: ditto. * lib/webrick/httpservlet/cgihandler.rb: ditto. * lib/webrick/httpservlet/filehandler.rb: ditto. * lib/webrick/httpservlet/prochandler.rb: ditto. * lib/webrick/httputils.rb: ditto. * lib/webrick/httpversion.rb: ditto. * lib/webrick/log.rb: ditto. * lib/webrick/server.rb: ditto. * lib/webrick/ssl.rb: ditto. * lib/webrick/utils.rb: ditto. * lib/webrick/version.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38945 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/webrick/accesslog.rb | 7 ++ lib/webrick/cgi.rb | 52 +++++++++++- lib/webrick/config.rb | 32 +++++++- lib/webrick/cookie.rb | 71 ++++++++++++++-- lib/webrick/httpauth/authenticator.rb | 20 +++-- lib/webrick/httpauth/basicauth.rb | 2 +- lib/webrick/httpauth/digestauth.rb | 24 +++++- lib/webrick/httpproxy.rb | 14 +++- lib/webrick/httprequest.rb | 146 ++++++++++++++++++++++++++++++--- lib/webrick/httpresponse.rb | 78 ++++++++++++++++-- lib/webrick/https.rb | 24 +++++- lib/webrick/httpserver.rb | 16 +++- lib/webrick/httpservlet/cgihandler.rb | 19 ++++- lib/webrick/httpservlet/filehandler.rb | 46 +++++++---- lib/webrick/httpservlet/prochandler.rb | 13 +++ lib/webrick/httputils.rb | 127 +++++++++++++++++++++++++--- lib/webrick/httpversion.rb | 28 ++++++- lib/webrick/log.rb | 23 +++++- lib/webrick/server.rb | 74 ++++++++++++++++- lib/webrick/ssl.rb | 72 +++++++++++++++- lib/webrick/utils.rb | 3 + lib/webrick/version.rb | 4 + 22 files changed, 812 insertions(+), 83 deletions(-) (limited to 'lib') diff --git a/lib/webrick/accesslog.rb b/lib/webrick/accesslog.rb index 029b5a4902..0a3c380406 100644 --- a/lib/webrick/accesslog.rb +++ b/lib/webrick/accesslog.rb @@ -115,6 +115,10 @@ module WEBrick params end + ## + # Formats +params+ according to +format_string+ which is described in + # setup_params. + def format(format_string, params) format_string.gsub(/\%(?:\{(.*?)\})?>?([a-zA-Z%])/){ param, spec = $1, $2 @@ -140,6 +144,9 @@ module WEBrick } end + ## + # Escapes control characters in +data+ + def escape(data) if data.tainted? data.gsub(/[[:cntrl:]\\]+/) {$&.dump[1...-1]}.untaint diff --git a/lib/webrick/cgi.rb b/lib/webrick/cgi.rb index 806d050bc9..80f636edc3 100644 --- a/lib/webrick/cgi.rb +++ b/lib/webrick/cgi.rb @@ -13,10 +13,44 @@ require "webrick/config" require "stringio" module WEBrick + + # A CGI library using WEBrick requests and responses. + # + # Example: + # + # class MyCGI < WEBrick::CGI + # def do_GET req, res + # res.body = 'it worked!' + # res.status = 200 + # end + # end + # + # MyCGI.new.start + class CGI + + # The CGI error exception class + CGIError = Class.new(StandardError) - attr_reader :config, :logger + ## + # The CGI configuration. This is based on WEBrick::Config::HTTP + + attr_reader :config + + ## + # The CGI logger + + attr_reader :logger + + ## + # Creates a new CGI interface. + # + # The first argument in +args+ is a configuration hash which would update + # WEBrick::Config::HTTP. + # + # Any remaining arguments are stored in the @options instance + # variable for use by a subclass. def initialize(*args) if defined?(MOD_RUBY) @@ -41,10 +75,17 @@ module WEBrick @options = args end + ## + # Reads +key+ from the configuration + def [](key) @config[key] end + ## + # Starts the CGI process with the given environment +env+ and standard + # input and output +stdin+ and +stdout+. + def start(env=ENV, stdin=$stdin, stdout=$stdout) sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout) req = HTTPRequest.new(@config) @@ -108,6 +149,10 @@ module WEBrick end end + ## + # Services the request +req+ which will fill in the response +res+. See + # WEBrick::HTTPServlet::AbstractServlet#service for details. + def service(req, res) method_name = "do_" + req.request_method.gsub(/-/, "_") if respond_to?(method_name) @@ -118,7 +163,10 @@ module WEBrick end end - class Socket + ## + # Provides HTTP socket emulation from the CGI environment + + class Socket # :nodoc: include Enumerable private diff --git a/lib/webrick/config.rb b/lib/webrick/config.rb index 8c6427020d..c347da4be6 100644 --- a/lib/webrick/config.rb +++ b/lib/webrick/config.rb @@ -16,7 +16,7 @@ require 'webrick/log' module WEBrick module Config - LIBDIR = File::dirname(__FILE__) + LIBDIR = File::dirname(__FILE__) # :nodoc: # for GenericServer General = { @@ -67,6 +67,30 @@ module WEBrick :Escape8bitURI => false ) + ## + # Default configuration for WEBrick::HTTPServlet::FileHandler + # + # :AcceptableLanguages:: + # Array of languages allowed for accept-language. There is no default + # :DirectoryCallback:: + # Allows preprocessing of directory requests. There is no default + # callback. + # :FancyIndexing:: + # If true, show an index for directories. The default is true. + # :FileCallback:: + # Allows preprocessing of file requests. There is no default callback. + # :HandlerCallback:: + # Allows preprocessing of requests. There is no default callback. + # :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. .ht* and *~ are + # excluded by default. + # :UserDir:: + # Directory inside ~user to serve content from for /~user requests. + # Only works if mounted on /. Disabled by default. + FileHandler = { :NondisclosureName => [".ht*", "*~"], :FancyIndexing => false, @@ -78,6 +102,12 @@ module WEBrick :AcceptableLanguages => [] # ["en", "ja", ... ] } + ## + # Default configuration for WEBrick::HTTPAuth::BasicAuth + # + # :AutoReloadUserDB:: Reload the user database provided by :UserDB + # automatically? + BasicAuth = { :AutoReloadUserDB => true, } diff --git a/lib/webrick/cookie.rb b/lib/webrick/cookie.rb index 814e6645a3..3cf8928bb0 100644 --- a/lib/webrick/cookie.rb +++ b/lib/webrick/cookie.rb @@ -12,14 +12,56 @@ require 'time' require 'webrick/httputils' module WEBrick + + ## + # Processes HTTP cookies + class Cookie + ## + # The cookie name + attr_reader :name - attr_accessor :value, :version - attr_accessor :domain, :path, :secure - attr_accessor :comment, :max_age + + ## + # The cookie value + + attr_accessor :value + + ## + # The cookie version + + attr_accessor :version + + ## + # The cookie domain + attr_accessor :domain + + ## + # The cookie path + + attr_accessor :path + + ## + # Is this a secure cookie? + + attr_accessor :secure + + ## + # The cookie comment + + attr_accessor :comment + + ## + # The maximum age of the cookie + + attr_accessor :max_age + #attr_accessor :comment_url, :discard, :port + ## + # Creates a new cookie with the given +name+ and +value+ + def initialize(name, value) @name = name @value = value @@ -29,14 +71,25 @@ module WEBrick @expires = @comment_url = @discard = @port = nil end + ## + # Sets the cookie expiration to the time +t+. The expiration time may be + # a false value to disable expiration or a Time or HTTP format time string + # to set the expiration date. + def expires=(t) @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s) end + ## + # Retrieves the expiration time as a Time + def expires @expires && Time.parse(@expires) end + ## + # The cookie string suitable for use in an HTTP header + def to_s ret = "" ret << @name << "=" << @value @@ -50,8 +103,10 @@ module WEBrick ret end - # Cookie::parse() - # It parses Cookie field sent from the user agent. + ## + # Parses a Cookie field sent from the user-agent. Returns an array of + # cookies. + def self.parse(str) if str ret = [] @@ -76,6 +131,9 @@ module WEBrick end end + ## + # Parses the cookie in +str+ + def self.parse_set_cookie(str) cookie_elem = str.split(/;/) first_elem = cookie_elem.shift @@ -101,6 +159,9 @@ module WEBrick return cookie end + ## + # Parses the cookies in +str+ + def self.parse_set_cookies(str) return str.split(/,(?=[^;,]*=)|,$/).collect{|c| parse_set_cookie(c) diff --git a/lib/webrick/httpauth/authenticator.rb b/lib/webrick/httpauth/authenticator.rb index 9b9beeceba..f6d4ab844f 100644 --- a/lib/webrick/httpauth/authenticator.rb +++ b/lib/webrick/httpauth/authenticator.rb @@ -16,10 +16,10 @@ module WEBrick module Authenticator - RequestField = "Authorization" - ResponseField = "WWW-Authenticate" - ResponseInfoField = "Authentication-Info" - AuthException = HTTPStatus::Unauthorized + RequestField = "Authorization" # :nodoc: + ResponseField = "WWW-Authenticate" # :nodoc: + ResponseInfoField = "Authentication-Info" # :nodoc: + AuthException = HTTPStatus::Unauthorized # :nodoc: ## # Method of authentication, must be overridden by the including class @@ -43,6 +43,8 @@ module WEBrick private + # :stopdoc: + ## # Initializes the authenticator from +config+ @@ -96,6 +98,8 @@ module WEBrick log(:info, fmt, *args) end end + + # :startdoc: end ## @@ -103,10 +107,10 @@ module WEBrick # authentication schemes for proxies. module ProxyAuthenticator - RequestField = "Proxy-Authorization" - ResponseField = "Proxy-Authenticate" - InfoField = "Proxy-Authentication-Info" - AuthException = HTTPStatus::ProxyAuthenticationRequired + RequestField = "Proxy-Authorization" # :nodoc: + ResponseField = "Proxy-Authenticate" # :nodoc: + InfoField = "Proxy-Authentication-Info" # :nodoc: + AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc: end end end diff --git a/lib/webrick/httpauth/basicauth.rb b/lib/webrick/httpauth/basicauth.rb index 4c51e53199..3ff20b56d2 100644 --- a/lib/webrick/httpauth/basicauth.rb +++ b/lib/webrick/httpauth/basicauth.rb @@ -34,7 +34,7 @@ module WEBrick class BasicAuth include Authenticator - AuthScheme = "Basic" + AuthScheme = "Basic" # :nodoc: ## # Used by UserDB to create a basic password entry diff --git a/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb index 4e47fe163f..78ad45b233 100644 --- a/lib/webrick/httpauth/digestauth.rb +++ b/lib/webrick/httpauth/digestauth.rb @@ -45,9 +45,22 @@ module WEBrick class DigestAuth include Authenticator - AuthScheme = "Digest" - OpaqueInfo = Struct.new(:time, :nonce, :nc) - attr_reader :algorithm, :qop + AuthScheme = "Digest" # :nodoc: + + ## + # Struct containing the opaque portion of the digest authentication + + OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc: + + ## + # Digest authentication algorithm + + attr_reader :algorithm + + ## + # Quality of protection. RFC 2617 defines "auth" and "auth-int" + + attr_reader :qop ## # Used by UserDB to create a digest password entry @@ -142,6 +155,8 @@ module WEBrick private + # :stopdoc: + MustParams = ['username','realm','nonce','uri','response'] MustParamsAuth = ['cnonce','nc'] @@ -375,6 +390,7 @@ module WEBrick @h.hexdigest(args.join(":")) end + # :startdoc: end ## @@ -384,7 +400,7 @@ module WEBrick include ProxyAuthenticator private - def check_uri(req, auth_req) + def check_uri(req, auth_req) # :nodoc: return true end end diff --git a/lib/webrick/httpproxy.rb b/lib/webrick/httpproxy.rb index e7226a0a6c..3ad573d5d9 100644 --- a/lib/webrick/httpproxy.rb +++ b/lib/webrick/httpproxy.rb @@ -15,16 +15,17 @@ require "net/http" Net::HTTP::version_1_2 if RUBY_VERSION < "1.7" module WEBrick - NullReader = Object.new - class << NullReader + + NullReader = Object.new # :nodoc: + class << NullReader # :nodoc: def read(*args) nil end alias gets read end - FakeProxyURI = Object.new - class << FakeProxyURI + FakeProxyURI = Object.new # :nodoc: + class << FakeProxyURI # :nodoc: def method_missing(meth, *args) if %w(scheme host port path query userinfo).member?(meth.to_s) return nil @@ -33,6 +34,8 @@ module WEBrick end end + # :startdoc: + ## # An HTTP Proxy server which proxies GET, HEAD and POST requests. # @@ -85,6 +88,7 @@ module WEBrick @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}" end + # :stopdoc: def service(req, res) if req.request_method == "CONNECT" do_CONNECT(req, res) @@ -329,5 +333,7 @@ module WEBrick set_via(res) res.body = response.body end + + # :stopdoc: end end diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb index 050b5ed45b..10b9e6131e 100644 --- a/lib/webrick/httprequest.rb +++ b/lib/webrick/httprequest.rb @@ -17,31 +17,137 @@ require 'webrick/cookie' module WEBrick ## - # An HTTP request. + # An HTTP request. This is consumed by service and do_* methods in + # WEBrick servlets + class HTTPRequest - BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] + BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ] # :nodoc: # :section: Request line + + ## + # The complete request line such as: + # + # GET / HTTP/1.1 + attr_reader :request_line - attr_reader :request_method, :unparsed_uri, :http_version + + ## + # The request method, GET, POST, PUT, etc. + + attr_reader :request_method + + ## + # The unparsed URI of the request + + attr_reader :unparsed_uri + + ## + # The HTTP version of the request + + attr_reader :http_version # :section: Request-URI - attr_reader :request_uri, :path - attr_accessor :script_name, :path_info, :query_string + + ## + # The parsed URI of the request + + attr_reader :request_uri + + ## + # The request path + + attr_reader :path + + ## + # The script name (CGI variable) + + attr_accessor :script_name + + ## + # The path info (CGI variable) + + attr_accessor :path_info + + ## + # The query from the URI of the request + + attr_accessor :query_string # :section: Header and entity body - attr_reader :raw_header, :header, :cookies - attr_reader :accept, :accept_charset - attr_reader :accept_encoding, :accept_language + + ## + # The raw header of the request + + attr_reader :raw_header + + ## + # The parsed header of the request + + attr_reader :header + + ## + # The parsed request cookies + + attr_reader :cookies + + ## + # The Accept header value + + attr_reader :accept + + ## + # The Accept-Charset header value + + attr_reader :accept_charset + + ## + # The Accept-Encoding header value + + attr_reader :accept_encoding + + ## + # The Accept-Language header value + + attr_reader :accept_language # :section: + + ## + # The remote user (CGI variable) + attr_accessor :user - attr_reader :addr, :peeraddr + + ## + # The socket address of the server + + attr_reader :addr + + ## + # The socket address of the client + + attr_reader :peeraddr + + ## + # Hash of request attributes + attr_reader :attributes + + ## + # Is this a keep-alive connection? + attr_reader :keep_alive + + ## + # The local time this request was received + attr_reader :request_time + ## + # Creates a new HTTP request. WEBrick::Config::HTTP is the default + # configuration. + def initialize(config) @config = config @buffer_size = @config[:InputBufferSize] @@ -78,6 +184,10 @@ module WEBrick @forwarded_server = @forwarded_for = nil end + ## + # Parses a request from +socket+. This is called internally by + # WEBrick::HTTPServer. + def parse(socket=nil) @socket = socket begin @@ -126,16 +236,21 @@ module WEBrick end end + ## # Generate HTTP/1.1 100 continue response if the client expects it, # otherwise does nothing. - def continue + + def continue # :nodoc: if self['expect'] == '100-continue' && @config[:HTTPVersion] >= "1.1" @socket << "HTTP/#{@config[:HTTPVersion]} 100 continue#{CRLF}#{CRLF}" @header.delete('expect') end end - def body(&block) + ## + # Returns the request body. + + def body(&block) # :yields: body_chunk block ||= Proc.new{|chunk| @body << chunk } read_body(@socket, block) @body.empty? ? nil : @body @@ -237,7 +352,10 @@ module WEBrick ret end - def fixup() + ## + # Consumes any remaining body and updates keep-alive status + + def fixup() # :nodoc: begin body{|chunk| } # read remaining body rescue HTTPStatus::Error => ex @@ -290,6 +408,8 @@ module WEBrick private + # :stopdoc: + MAX_URI_LENGTH = 2083 # :nodoc: def read_request_line(socket) @@ -457,5 +577,7 @@ module WEBrick @forwarded_for = addrs.first end end + + # :startdoc: end end diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb index 5adbc82173..8e3eb39a31 100644 --- a/lib/webrick/httpresponse.rb +++ b/lib/webrick/httpresponse.rb @@ -16,11 +16,34 @@ require 'webrick/httpstatus' module WEBrick ## - # An HTTP response. + # An HTTP response. This is filled in by the service or do_* methods of a + # WEBrick HTTP Servlet. class HTTPResponse - attr_reader :http_version, :status, :header + + ## + # HTTP Response version + + attr_reader :http_version + + ## + # Response status code (200) + + attr_reader :status + + ## + # Response header + + attr_reader :header + + ## + # Response cookies + attr_reader :cookies + + ## + # Response reason phrase ("OK") + attr_accessor :reason_phrase ## @@ -28,13 +51,45 @@ module WEBrick attr_accessor :body - attr_accessor :request_method, :request_uri, :request_http_version + ## + # Request method for this response + + attr_accessor :request_method + + ## + # Request URI for this response + + attr_accessor :request_uri + + ## + # Request HTTP version for this response + + attr_accessor :request_http_version + + ## + # Filename of the static file in this response. Only used by the + # FileHandler servlet. + attr_accessor :filename + + ## + # Is this a keep-alive response? + attr_accessor :keep_alive - attr_reader :config, :sent_size ## - # Creates a new HTTP response object + # Configuration for this response + + attr_reader :config + + ## + # Bytes sent in this response + + attr_reader :sent_size + + ## + # Creates a new HTTP response object. WEBrick::Config::HTTP is the + # default configuration. def initialize(config) @config = config @@ -145,7 +200,7 @@ module WEBrick ## # Sends the response on +socket+ - def send_response(socket) + def send_response(socket) # :nodoc: begin setup_header() send_header(socket) @@ -162,7 +217,7 @@ module WEBrick ## # Sets up the headers for sending - def setup_header() + def setup_header() # :nodoc: @reason_phrase ||= HTTPStatus::reason_phrase(@status) @header['server'] ||= @config[:ServerSoftware] @header['date'] ||= Time.now.httpdate @@ -225,7 +280,7 @@ module WEBrick ## # Sends the headers on +socket+ - def send_header(socket) + def send_header(socket) # :nodoc: if @http_version.major > 0 data = status_line() @header.each{|key, value| @@ -243,7 +298,7 @@ module WEBrick ## # Sends the body on +socket+ - def send_body(socket) + def send_body(socket) # :nodoc: case @body when IO then send_body_io(socket) else send_body_string(socket) @@ -325,6 +380,8 @@ module WEBrick private + # :stopdoc: + def send_body_io(socket) begin if @request_method == "HEAD" @@ -400,5 +457,8 @@ module WEBrick def _write_data(socket, data) socket << data end + + # :startdoc: end + end diff --git a/lib/webrick/https.rb b/lib/webrick/https.rb index abb428451f..9194f9411c 100644 --- a/lib/webrick/https.rb +++ b/lib/webrick/https.rb @@ -15,8 +15,28 @@ module WEBrick HTTP.update(SSL) end + ## + #-- + # Adds SSL functionality to WEBrick::HTTPRequest + class HTTPRequest - attr_reader :cipher, :server_cert, :client_cert + + ## + # HTTP request SSL cipher + + attr_reader :cipher + + ## + # HTTP request server certificate + + attr_reader :server_cert + + ## + # HTTP request client certificate + + attr_reader :client_cert + + # :stopdoc: alias orig_parse parse @@ -60,5 +80,7 @@ module WEBrick end meta end + + # :startdoc: end end diff --git a/lib/webrick/httpserver.rb b/lib/webrick/httpserver.rb index ddf1ac7404..7a7b931dad 100644 --- a/lib/webrick/httpserver.rb +++ b/lib/webrick/httpserver.rb @@ -138,6 +138,10 @@ module WEBrick si.service(req, res) end + ## + # The default OPTIONS request handler says GET, HEAD, POST and OPTIONS + # requests are allowed. + def do_OPTIONS(req, res) res["allow"] = "GET,HEAD,POST,OPTIONS" end @@ -207,6 +211,10 @@ module WEBrick } end + ## + # Logs +req+ and +res+ in the access logs. +config+ is used for the + # server name. + def access_log(config, req, res) param = AccessLog::setup_params(config, req, res) @config[:AccessLog].each{|logger, fmt| @@ -214,7 +222,13 @@ module WEBrick } end - class MountTable + ## + # Mount table for the path a servlet is mounted on in the directory space + # of the server. Users of WEBrick can only access this indirectly via + # WEBrick::HTTPServer#mount, WEBrick::HTTPServer#unmount and + # WEBrick::HTTPServer#search_servlet + + class MountTable # :nodoc: def initialize @tab = Hash.new compile diff --git a/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb index 1976ae6948..7c012ca64b 100644 --- a/lib/webrick/httpservlet/cgihandler.rb +++ b/lib/webrick/httpservlet/cgihandler.rb @@ -16,9 +16,20 @@ require 'webrick/httpservlet/abstract' module WEBrick module HTTPServlet + ## + # Servlet for handling CGI scripts + # + # Example: + # + # server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler, + # '/path/to/my_script') + class CGIHandler < AbstractServlet - Ruby = RbConfig.ruby - CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" + Ruby = RbConfig.ruby # :nodoc: + CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc: + + ## + # Creates a new CGI script servlet for the script at +name+ def initialize(server, name) super(server, name) @@ -27,6 +38,8 @@ module WEBrick @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}" end + # :stopdoc: + def do_GET(req, res) data = nil status = -1 @@ -102,6 +115,8 @@ module WEBrick res.body = body end alias do_POST do_GET + + # :startdoc: end end diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb index 8736f5773a..d8c66dfdc3 100644 --- a/lib/webrick/httpservlet/filehandler.rb +++ b/lib/webrick/httpservlet/filehandler.rb @@ -18,12 +18,29 @@ require 'webrick/httpstatus' module WEBrick module HTTPServlet + ## + # Servlet for serving a single file. You probably want to use the + # FileHandler servlet instead as it handles directories and fancy indexes. + # + # Example: + # + # server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler, + # '/path/to/my_page.txt') + # + # This servlet handles If-Modified-Since and Range requests. + class DefaultFileHandler < AbstractServlet + + ## + # Creates a DefaultFileHandler instance for the file at +local_path+. + def initialize(server, local_path) super(server, local_path) @local_path = local_path end + # :stopdoc: + def do_GET(req, res) st = File::stat(@local_path) mtime = st.mtime @@ -123,13 +140,20 @@ module WEBrick last = filesize - 1 if last >= filesize return first, last end + + # :startdoc: end ## - # Serves files from a directory + # Serves a directory including fancy indexing and a variety of other + # options. + # + # Example: + # + # server.mount '/assets', WEBrick::FileHandler, '/path/to/assets' class FileHandler < AbstractServlet - HandlerTable = Hash.new + HandlerTable = Hash.new # :nodoc: ## # Allow custom handling of requests for files with +suffix+ by class @@ -150,19 +174,8 @@ module WEBrick # 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 / + # +options+ may be a Hash containing keys from + # WEBrick::Config::FileHandler or +true+ or +false+. # # If +options+ is true or false then +:FancyIndexing+ is enabled or # disabled respectively. @@ -177,6 +190,8 @@ module WEBrick @options = default.dup.update(options) end + # :stopdoc: + def service(req, res) # if this class is mounted on "/" and /~username is requested. # we're going to override path informations before invoking service. @@ -465,6 +480,7 @@ module WEBrick _end_of_html_ end + # :startdoc: end end end diff --git a/lib/webrick/httpservlet/prochandler.rb b/lib/webrick/httpservlet/prochandler.rb index 2be3c854c1..2f5aa66f45 100644 --- a/lib/webrick/httpservlet/prochandler.rb +++ b/lib/webrick/httpservlet/prochandler.rb @@ -13,7 +13,19 @@ require 'webrick/httpservlet/abstract.rb' module WEBrick module HTTPServlet + ## + # Mounts a proc at a path that accepts a request and response. + # + # Instead of mounting this servlet with WEBrick::HTTPServer#mount use + # WEBrick::HTTPServer#mount_proc: + # + # server.mount_proc '/' do |req, res| + # res.body = 'it worked!' + # res.status = 200 + # end + class ProcHandler < AbstractServlet + # :stopdoc: def get_instance(server, *options) self end @@ -27,6 +39,7 @@ module WEBrick end alias do_POST do_GET + # :startdoc: end end diff --git a/lib/webrick/httputils.rb b/lib/webrick/httputils.rb index 57ae364b35..a0ca3a48c7 100644 --- a/lib/webrick/httputils.rb +++ b/lib/webrick/httputils.rb @@ -12,12 +12,21 @@ require 'socket' require 'tempfile' module WEBrick - CR = "\x0d" - LF = "\x0a" - CRLF = "\x0d\x0a" + CR = "\x0d" # :nodoc: + LF = "\x0a" # :nodoc: + CRLF = "\x0d\x0a" # :nodoc: + + ## + # HTTPUtils provides utility methods for working with the HTTP protocol. + # + # This module is generally used internally by WEBrick module HTTPUtils + ## + # Normalizes a request path. Raises an exception if the path cannot be + # normalized. + def normalize_path(path) raise "abnormal path `#{path}'" if path[0] != ?/ ret = path.dup @@ -31,7 +40,8 @@ module WEBrick end module_function :normalize_path - ##### + ## + # Default mime types DefaultMimeTypes = { "ai" => "application/postscript", @@ -92,7 +102,9 @@ module WEBrick "zip" => "application/zip", } - # Load Apache compatible mime.types file. + ## + # Loads Apache-compatible mime.types in +file+. + def load_mime_types(file) open(file){ |io| hash = Hash.new @@ -109,6 +121,10 @@ module WEBrick end module_function :load_mime_types + ## + # Returns the mime type of +filename+ from the list in +mime_tab+. If no + # mime type was found application/octet-stream is returned. + def mime_type(filename, mime_tab) suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase) suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase) @@ -116,7 +132,9 @@ module WEBrick end module_function :mime_type - ##### + ## + # Parses an HTTP header +raw+ into a hash of header fields with an Array + # of values. def parse_header(raw) header = Hash.new([].freeze) @@ -148,12 +166,18 @@ module WEBrick end module_function :parse_header + ## + # Splits a header value +str+ according to HTTP specification. + def split_header_value(str) str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+) (?:,\s*|\Z)'xn).flatten end module_function :split_header_value + ## + # Parses a Range header value +ranges_specifier+ + def parse_range_header(ranges_specifier) if /^bytes=(.*)/ =~ ranges_specifier byte_range_set = split_header_value($1) @@ -169,6 +193,9 @@ module WEBrick end module_function :parse_range_header + ## + # Parses q values in +value+ as used in Accept headers. + def parse_qvalues(value) tmp = [] if value @@ -187,7 +214,8 @@ module WEBrick end module_function :parse_qvalues - ##### + ## + # Removes quotes and escapes from +str+ def dequote(str) ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup @@ -196,20 +224,43 @@ module WEBrick end module_function :dequote + ## + # Quotes and escapes quotes in +str+ + def quote(str) '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' end module_function :quote - ##### + ## + # Stores multipart form data. FormData objects are created when + # WEBrick::HTTPUtils.parse_form_data is called. class FormData < String - EmptyRawHeader = [].freeze - EmptyHeader = {}.freeze + EmptyRawHeader = [].freeze # :nodoc: + EmptyHeader = {}.freeze # :nodoc: + + ## + # The name of the form data part - attr_accessor :name, :filename, :next_data + attr_accessor :name + + ## + # The filename of the form data part + + attr_accessor :filename + + attr_accessor :next_data # :nodoc: protected :next_data + ## + # Creates a new FormData object. + # + # +args+ is an Array of form data entries. One FormData will be created + # for each entry. + # + # This is called by WEBrick::HTTPUtils.parse_form_data for you + def initialize(*args) @name = @filename = @next_data = nil if args.empty? @@ -226,6 +277,9 @@ module WEBrick end end + ## + # Retrieves the header at the first entry in +key+ + def [](*key) begin @header[key[0].downcase].join(", ") @@ -234,6 +288,12 @@ module WEBrick end end + ## + # Adds +str+ to this FormData which may be the body, a header or a + # header entry. + # + # This is called by WEBrick::HTTPUtils.parse_form_data for you + def <<(str) if @header super @@ -249,6 +309,11 @@ module WEBrick self end + ## + # Adds +data+ at the end of the chain of entries + # + # This is called by WEBrick::HTTPUtils.parse_form_data for you. + def append_data(data) tmp = self while tmp @@ -261,6 +326,9 @@ module WEBrick self end + ## + # Yields each entry in this FormData + def each_data tmp = self while tmp @@ -270,6 +338,9 @@ module WEBrick end end + ## + # Returns all the FormData as an Array + def list ret = [] each_data{|data| @@ -278,13 +349,22 @@ module WEBrick ret end + ## + # A FormData will behave like an Array + alias :to_ary :list + ## + # This FormData's body + def to_s String.new(self) end end + ## + # Parses the query component of a URI in +str+ + def parse_query(str) query = Hash.new if str @@ -306,6 +386,9 @@ module WEBrick end module_function :parse_query + ## + # Parses form data in +io+ with the given +boundary+ + def parse_form_data(io, boundary) boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/ form_data = Hash.new @@ -350,6 +433,8 @@ module WEBrick module_function + # :stopdoc: + def _make_regex(str) /([#{Regexp.escape(str)}])/n end def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1.ord } end @@ -361,24 +446,41 @@ module WEBrick ESCAPED = /%([0-9a-fA-F]{2})/ UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,") + # :startdoc: + + ## + # Escapes HTTP reserved and unwise characters in +str+ + def escape(str) _escape(str, UNESCAPED) end + ## + # Unescapes HTTP reserved and unwise characters in +str+ + def unescape(str) _unescape(str, ESCAPED) end + ## + # Escapes form reserved characters in +str+ + def escape_form(str) ret = _escape(str, UNESCAPED_FORM) ret.gsub!(/ /, "+") ret end + ## + # Unescapes form reserved characters in +str+ + def unescape_form(str) _unescape(str.gsub(/\+/, " "), ESCAPED) end + ## + # Escapes path +str+ + def escape_path(str) result = "" str.scan(%r{/([^/]*)}).each{|i| @@ -387,6 +489,9 @@ module WEBrick return result end + ## + # Escapes 8 bit characters in +str+ + def escape8bit(str) _escape(str, NONASCII) end diff --git a/lib/webrick/httpversion.rb b/lib/webrick/httpversion.rb index 5cf0ee400d..cdfb957296 100644 --- a/lib/webrick/httpversion.rb +++ b/lib/webrick/httpversion.rb @@ -8,15 +8,33 @@ # $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $ module WEBrick + + ## + # Represents an HTTP protocol version + class HTTPVersion include Comparable - attr_accessor :major, :minor + ## + # The major protocol version number + + attr_accessor :major + + ## + # The minor protocol version number + + attr_accessor :minor + + ## + # Converts +version+ into an HTTPVersion def self.convert(version) version.is_a?(self) ? version : new(version) end + ## + # Creates a new HTTPVersion from +version+. + def initialize(version) case version when HTTPVersion @@ -32,6 +50,10 @@ module WEBrick end end + ## + # Compares this version with +other+ according to the HTTP specification + # rules. + def <=>(other) unless other.is_a?(self.class) other = self.class.new(other) @@ -42,6 +64,10 @@ module WEBrick return ret end + ## + # The HTTP version as show in the HTTP request and response. For example, + # "1.1" + def to_s format("%d.%d", @major, @minor) end diff --git a/lib/webrick/log.rb b/lib/webrick/log.rb index 546e52700b..41cde4a740 100644 --- a/lib/webrick/log.rb +++ b/lib/webrick/log.rb @@ -14,8 +14,27 @@ module WEBrick # A generic logging class class BasicLog - # log-level constants - FATAL, ERROR, WARN, INFO, DEBUG = 1, 2, 3, 4, 5 + + # Fatal log level which indicates a server crash + + FATAL = 1 + + # Error log level which indicates a recoverable error + + ERROR = 2 + + # Warning log level which indicates a possible problem + + WARN = 3 + + # Information log level which indicates possibly useful information + + INFO = 4 + + # Debugging error level for messages used in server development or + # debugging + + DEBUG = 5 # log-level, messages above this level will be logged attr_accessor :level diff --git a/lib/webrick/server.rb b/lib/webrick/server.rb index 2eabf5d55c..3f5371ba47 100644 --- a/lib/webrick/server.rb +++ b/lib/webrick/server.rb @@ -15,9 +15,19 @@ require 'webrick/log' module WEBrick + ## + # Server error exception + class ServerError < StandardError; end + ## + # Base server class + class SimpleServer + + ## + # A SimpleServer only yields when you start it + def SimpleServer.start yield end @@ -45,8 +55,41 @@ module WEBrick end end + ## + # Base TCP server class. You must subclass GenericServer and provide a #run + # method. + class GenericServer - attr_reader :status, :config, :logger, :tokens, :listeners + + ## + # The server status. One of :Stop, :Running or :Shutdown + + attr_reader :status + + ## + # The server configuration + + attr_reader :config + + ## + # The server logger. This is independent from the HTTP access log. + + attr_reader :logger + + ## + # Tokens control the number of outstanding clients. The + # :MaxClients configuration sets this. + + attr_reader :tokens + + ## + # Sockets listening for connections. + + attr_reader :listeners + + ## + # Creates a new generic server from +config+. The default configuration + # comes from +default+. def initialize(config={}, default=Config::General) @config = default.dup.update(config) @@ -74,10 +117,17 @@ module WEBrick end end + ## + # Retrieves +key+ from the configuration + def [](key) @config[key] end + ## + # Adds listeners from +address+ and +port+ to the server. See + # WEBrick::Utils::create_listeners for details. + def listen(address, port) @listeners += Utils::create_listeners(address, port, @logger) end @@ -189,12 +239,22 @@ module WEBrick @listeners.clear end + ## + # You must subclass GenericServer and implement \#run which accepts a TCP + # client socket + def run(sock) @logger.fatal "run() must be provided by user." end private + # :stopdoc: + + ## + # Accepts a TCP client socket from the TCP server socket +svr+ and returns + # the client socket. + def accept_client(svr) sock = nil begin @@ -211,6 +271,15 @@ module WEBrick return sock end + ## + # Starts a server thread for the client socket +sock+ that runs the given + # +block+. + # + # Sets the socket to the :WEBrickSocket thread local variable + # in the thread. + # + # If any errors occur in the block they are logged and handled. + def start_thread(sock, &block) Thread.start{ begin @@ -244,6 +313,9 @@ module WEBrick } end + ## + # Calls the callback +callback_name+ from the configuration with +args+ + def call_callback(callback_name, *args) if cb = @config[callback_name] cb.call(*args) diff --git a/lib/webrick/ssl.rb b/lib/webrick/ssl.rb index 02f1be5c21..cf0f3ddb23 100644 --- a/lib/webrick/ssl.rb +++ b/lib/webrick/ssl.rb @@ -12,6 +12,53 @@ module WEBrick module Config svrsoft = General[:ServerSoftware] osslv = ::OpenSSL::OPENSSL_VERSION.split[1] + + ## + # Default SSL server configuration. + # + # WEBrick can automatically create a self-signed certificate if + # :SSLCertName is set. For more information on the various + # SSL options see OpenSSL::SSL::SSLContext. + # + # :ServerSoftware :: + # The server software name used in the Server: header. + # :SSLEnable :: false, + # Enable SSL for this server. Defaults to false. + # :SSLCertificate :: + # The SSL certificate for the server. + # :SSLPrivateKey :: + # The SSL private key for the server certificate. + # :SSLClientCA :: nil, + # Array of certificates that will be sent to the client. + # :SSLExtraChainCert :: nil, + # Array of certificates that willbe added to the certificate chain + # :SSLCACertificateFile :: nil, + # Path to a CA certificate file + # :SSLCACertificatePath :: nil, + # Path to a directory containing CA certificates + # :SSLCertificateStore :: nil, + # OpenSSL::X509::Store used for certificate validation of the client + # :SSLTmpDhCallback :: nil, + # Callback invoked when DH parameters are required. + # :SSLVerifyClient :: + # Sets whether the client is verified. This defaults to VERIFY_NONE + # which is typical for an HTTPS server. + # :SSLVerifyDepth :: + # Number of CA certificates to walk when verifying a certificate chain + # :SSLVerifyCallback :: + # Custom certificate verification callback + # :SSLTimeout :: + # Maximum session lifetime + # :SSLOptions :: + # Various SSL options + # :SSLStartImmediately :: + # Immediately start SSL upon connection? Defaults to true + # :SSLCertName :: + # SSL certificate name. Must be set to enable automatic certificate + # creation. + # :SSLCertComment :: + # Comment used during automatic certificate creation. + SSL = { :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}", :SSLEnable => false, @@ -37,6 +84,10 @@ module WEBrick end module Utils + ## + # Creates a self-signed certificate with the given number of +bits+, + # the issuer +cn+ and a +comment+ to be stored in the certificate. + def create_self_signed_cert(bits, cn, comment) rsa = OpenSSL::PKey::RSA.new(bits){|p, n| case p @@ -79,13 +130,25 @@ module WEBrick module_function :create_self_signed_cert end + ## + #-- + # Updates WEBrick::GenericServer with SSL functionality + class GenericServer - def ssl_context + + ## + # SSL context for the server when run in SSL mode + + def ssl_context # :nodoc: @ssl_context ||= nil end undef listen - def listen(address, port) + + ## + # Updates +listen+ to enable SSL when the SSL configuration is active. + + def listen(address, port) # :nodoc: listeners = Utils::create_listeners(address, port, @logger) if @config[:SSLEnable] unless ssl_context @@ -101,7 +164,10 @@ module WEBrick @listeners += listeners end - def setup_ssl_context(config) + ## + # Sets up an SSL context for +config+ + + def setup_ssl_context(config) # :nodoc: unless config[:SSLCertificate] cn = config[:SSLCertName] comment = config[:SSLCertComment] diff --git a/lib/webrick/utils.rb b/lib/webrick/utils.rb index eab2375968..f2373572ce 100644 --- a/lib/webrick/utils.rb +++ b/lib/webrick/utils.rb @@ -170,6 +170,9 @@ module WEBrick } end + ## + # Creates a new TimeoutHandler. You should use ::register and ::cancel + # instead of creating the timeout handler directly. def initialize @timeout_info = Hash.new Thread.start{ diff --git a/lib/webrick/version.rb b/lib/webrick/version.rb index a49090828d..48bdc6d94d 100644 --- a/lib/webrick/version.rb +++ b/lib/webrick/version.rb @@ -9,5 +9,9 @@ # $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $ module WEBrick + + ## + # The WEBrick version + VERSION = "1.3.1" end -- cgit v1.2.1