1 # frozen_string_literal: true
3 # Methods for generating HTML, parsing CGI-related parameters, and
4 # generating HTTP responses.
7 unless const_defined?(:Util)
9 @@accept_charset = "UTF-8" # :nodoc:
15 $CGI_ENV = ENV # for FCGI support
17 # String for carriage return
23 # Standard internet newline sequence
26 REVISION = '$Id$' #:nodoc:
28 # Whether processing will be required in binary vs text
29 NEEDS_BINMODE = File::BINARY != 0
31 # Path separators in different environments.
32 PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
37 "PARTIAL_CONTENT" => "206 Partial Content",
38 "MULTIPLE_CHOICES" => "300 Multiple Choices",
39 "MOVED" => "301 Moved Permanently",
40 "REDIRECT" => "302 Found",
41 "NOT_MODIFIED" => "304 Not Modified",
42 "BAD_REQUEST" => "400 Bad Request",
43 "AUTH_REQUIRED" => "401 Authorization Required",
44 "FORBIDDEN" => "403 Forbidden",
45 "NOT_FOUND" => "404 Not Found",
46 "METHOD_NOT_ALLOWED" => "405 Method Not Allowed",
47 "NOT_ACCEPTABLE" => "406 Not Acceptable",
48 "LENGTH_REQUIRED" => "411 Length Required",
49 "PRECONDITION_FAILED" => "412 Precondition Failed",
50 "SERVER_ERROR" => "500 Internal Server Error",
51 "NOT_IMPLEMENTED" => "501 Method Not Implemented",
52 "BAD_GATEWAY" => "502 Bad Gateway",
53 "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
68 # Synonym for $stdout.
73 private :env_table, :stdinput, :stdoutput
75 # Create an HTTP header block as a string.
78 # http_header(content_type_string="text/html")
79 # http_header(headers_hash)
81 # Includes the empty line that ends the header block.
83 # +content_type_string+::
84 # If this form is used, this string is the <tt>Content-Type</tt>
86 # A Hash of header values. The following header keys are recognized:
88 # type:: The Content-Type header. Defaults to "text/html"
89 # charset:: The charset of the body, appended to the Content-Type header.
90 # nph:: A boolean value. If true, prepend protocol string and status
91 # code, and date; and sets default values for "server" and
92 # "connection" if not explicitly set.
94 # The HTTP status code as a String, returned as the Status header. The
98 # PARTIAL_CONTENT:: 206 Partial Content
99 # MULTIPLE_CHOICES:: 300 Multiple Choices
100 # MOVED:: 301 Moved Permanently
101 # REDIRECT:: 302 Found
102 # NOT_MODIFIED:: 304 Not Modified
103 # BAD_REQUEST:: 400 Bad Request
104 # AUTH_REQUIRED:: 401 Authorization Required
105 # FORBIDDEN:: 403 Forbidden
106 # NOT_FOUND:: 404 Not Found
107 # METHOD_NOT_ALLOWED:: 405 Method Not Allowed
108 # NOT_ACCEPTABLE:: 406 Not Acceptable
109 # LENGTH_REQUIRED:: 411 Length Required
110 # PRECONDITION_FAILED:: 412 Precondition Failed
111 # SERVER_ERROR:: 500 Internal Server Error
112 # NOT_IMPLEMENTED:: 501 Method Not Implemented
113 # BAD_GATEWAY:: 502 Bad Gateway
114 # VARIANT_ALSO_VARIES:: 506 Variant Also Negotiates
116 # server:: The server software, returned as the Server header.
117 # connection:: The connection type, returned as the Connection header (for
119 # length:: The length of the content that will be sent, returned as the
120 # Content-Length header.
121 # language:: The language of the content, returned as the Content-Language
123 # expires:: The time on which the current content expires, as a +Time+
124 # object, returned as the Expires header.
126 # A cookie or cookies, returned as one or more Set-Cookie headers. The
127 # value can be the literal string of the cookie; a CGI::Cookie object;
128 # an Array of literal cookie strings or Cookie objects; or a hash all of
129 # whose values are literal cookie strings or Cookie objects.
131 # These cookies are in addition to the cookies held in the
132 # @output_cookies field.
134 # Other headers can also be set; they are appended as key: value.
139 # # Content-Type: text/html
141 # http_header("text/plain")
142 # # Content-Type: text/plain
144 # http_header("nph" => true,
145 # "status" => "OK", # == "200 OK"
146 # # "status" => "200 GOOD",
147 # "server" => ENV['SERVER_SOFTWARE'],
148 # "connection" => "close",
149 # "type" => "text/html",
150 # "charset" => "iso-2022-jp",
151 # # Content-Type: text/html; charset=iso-2022-jp
153 # "language" => "ja",
154 # "expires" => Time.now + 30,
155 # "cookie" => [cookie1, cookie2],
156 # "my_header1" => "my_value",
157 # "my_header2" => "my_value")
159 # This method does not perform charset conversion.
160 def http_header(options='text/html')
161 if options.is_a?(String)
162 content_type = options
163 buf = _header_for_string(content_type)
164 elsif options.is_a?(Hash)
165 if options.size == 1 && options.has_key?('type')
166 content_type = options['type']
167 buf = _header_for_string(content_type)
169 buf = _header_for_hash(options.dup)
172 raise ArgumentError.new("expected String or Hash but got #{options.class}")
174 if defined?(MOD_RUBY)
175 _header_for_modruby(buf)
178 buf << EOL # empty line of separator
183 # This method is an alias for #http_header, when HTML5 tag maker is inactive.
185 # NOTE: use #http_header to create HTTP header blocks, this alias is only
186 # provided for backwards compatibility.
188 # Using #header with the HTML5 tag maker will create a <header> element.
189 alias :header :http_header
191 def _no_crlf_check(str)
194 raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
200 private :_no_crlf_check
202 def _header_for_string(content_type) #:nodoc:
205 buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}"
206 buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
207 buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}"
208 buf << "Connection: close#{EOL}"
210 buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}"
212 @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" }
215 end # _header_for_string
216 private :_header_for_string
218 def _header_for_hash(options) #:nodoc:
220 ## add charset to option['type']
221 options['type'] ||= 'text/html'
222 charset = options.delete('charset')
223 options['type'] += "; charset=#{charset}" if charset
225 options.delete('nph') if defined?(MOD_RUBY)
226 if options.delete('nph') || nph?()
227 protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'
228 status = options.delete('status')
229 status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK'
230 buf << "#{protocol} #{status}#{EOL}"
231 buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
232 options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
233 options['connection'] ||= 'close'
236 status = options.delete('status')
237 buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status
238 server = options.delete('server')
239 buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server
240 connection = options.delete('connection')
241 buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection
242 type = options.delete('type')
243 buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type
244 length = options.delete('length')
245 buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length
246 language = options.delete('language')
247 buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language
248 expires = options.delete('expires')
249 buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
251 if cookie = options.delete('cookie')
254 buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}"
257 arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
260 hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
264 @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
267 options.each do |key, value|
268 buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}"
271 end # _header_for_hash
272 private :_header_for_hash
275 return /IIS\/(\d+)/ =~ $CGI_ENV['SERVER_SOFTWARE'] && $1.to_i < 5
278 def _header_for_modruby(buf) #:nodoc:
279 request = Apache::request
280 buf.scan(/([^:]+): (.+)#{EOL}/o) do |name, value|
281 $stderr.printf("name:%s value:%s\n", name, value) if $DEBUG
284 request.headers_out.add(name, value)
286 request.status_line = value
287 request.status = value.to_i
288 when /^content-type$/i
289 request.content_type = value
290 when /^content-encoding$/i
291 request.content_encoding = value
293 request.status = 302 if request.status == 200
294 request.headers_out[name] = value
296 request.headers_out[name] = value
299 request.send_http_header
302 private :_header_for_modruby
304 # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
307 # cgi.out(content_type_string='text/html')
308 # cgi.out(headers_hash)
310 # +content_type_string+::
311 # If a string is passed, it is assumed to be the content type.
313 # This is a Hash of headers, similar to that used by #http_header.
315 # A block is required and should evaluate to the body of the response.
317 # <tt>Content-Length</tt> is automatically calculated from the size of
318 # the String returned by the content block.
320 # If <tt>ENV['REQUEST_METHOD'] == "HEAD"</tt>, then only the header
321 # is output (the content block is still required, but it is ignored).
323 # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then the
324 # content is converted to this charset, and the language is set to "ja".
329 # cgi.out{ "string" }
330 # # Content-Type: text/html
331 # # Content-Length: 6
335 # cgi.out("text/plain") { "string" }
336 # # Content-Type: text/plain
337 # # Content-Length: 6
341 # cgi.out("nph" => true,
342 # "status" => "OK", # == "200 OK"
343 # "server" => ENV['SERVER_SOFTWARE'],
344 # "connection" => "close",
345 # "type" => "text/html",
346 # "charset" => "iso-2022-jp",
347 # # Content-Type: text/html; charset=iso-2022-jp
348 # "language" => "ja",
349 # "expires" => Time.now + (3600 * 24 * 30),
350 # "cookie" => [cookie1, cookie2],
351 # "my_header1" => "my_value",
352 # "my_header2" => "my_value") { "string" }
354 # # Date: Sun, 15 May 2011 17:35:54 GMT
355 # # Server: Apache 2.2.0
356 # # Connection: close
357 # # Content-Type: text/html; charset=iso-2022-jp
358 # # Content-Length: 6
359 # # Content-Language: ja
360 # # Expires: Tue, 14 Jun 2011 17:35:54 GMT
363 # # my_header1: my_value
364 # # my_header2: my_value
367 def out(options = "text/html") # :yield:
369 options = { "type" => options } if options.kind_of?(String)
371 options["length"] = content.bytesize.to_s
373 output.binmode if defined? output.binmode
374 output.print http_header(options)
375 output.print content unless "HEAD" == env_table['REQUEST_METHOD']
379 # Print an argument or list of arguments to the default output stream
382 # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
384 stdoutput.print(*options)
387 # Parse an HTTP query string into a hash of key=>value pairs.
389 # params = CGI.parse("query_string")
390 # # {"name1" => ["value1", "value2", ...],
391 # # "name2" => ["value1", "value2", ...], ... }
393 def self.parse(query)
395 query.split(/[&;]/).each do |pairs|
396 key, value = pairs.split('=',2).collect{|v| CGI.unescape(v) }
401 params[key].push(value) if value
404 params.default=[].freeze
408 # Maximum content length of post data
409 ##MAX_CONTENT_LENGTH = 2 * 1024 * 1024
411 # Maximum number of request parameters when multipart
412 MAX_MULTIPART_COUNT = 128
414 # Mixin module that provides the following:
416 # 1. Access to the CGI environment variables as methods. See
417 # documentation to the CGI class for a list of these variables. The
418 # methods are exposed by removing the leading +HTTP_+ (if it exists) and
419 # downcasing the name. For example, +auth_type+ will return the
420 # environment variable +AUTH_TYPE+, and +accept+ will return the value
423 # 2. Access to cookies, including the cookies attribute.
425 # 3. Access to parameters, including the params attribute, and overloading
426 # #[] to perform parameter value lookup by key.
428 # 4. The initialize_query method, for initializing the above
429 # mechanisms, handling multipart forms, and allowing the
430 # class to be used in "offline" mode.
432 module QueryExtension
434 %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
435 define_method(env.delete_prefix('HTTP_').downcase) do
436 (val = env_table[env]) && Integer(val)
440 %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
441 PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
442 REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
443 SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
445 HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
446 HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
447 HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
448 define_method(env.delete_prefix('HTTP_').downcase) do
453 # Get the raw cookies as a string.
455 env_table["HTTP_COOKIE"]
458 # Get the raw RFC2965 cookies as a string.
460 env_table["HTTP_COOKIE2"]
463 # Get the cookies as a hash of cookie-name=>Cookie pairs.
464 attr_accessor :cookies
466 # Get the parameters as a hash of name=>values pairs, where
467 # values is an Array.
470 # Get the uploaded files as a hash of name=>values pairs
473 # Set all the parameters.
480 # Parses multipart form elements according to
481 # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
483 # Returns a hash of multipart form parameters with bodies of type StringIO or
484 # Tempfile depending on whether the multipart form element exceeds 10 KB
486 # params[name => body]
488 def read_multipart(boundary, content_length)
489 ## read first boundary
491 first_line = "--#{boundary}#{EOL}"
492 content_length -= first_line.bytesize
493 status = stdin.read(first_line.bytesize)
494 raise EOFError.new("no content body") unless status
495 raise EOFError.new("bad content body") unless first_line == status
496 ## parse and set params
499 boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
500 boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
503 max_count = MAX_MULTIPART_COUNT
507 (n += 1) < max_count or raise StandardError.new("too many parameters.")
508 ## create body (StringIO or Tempfile)
509 body = create_body(bufsize < content_length)
510 tempfiles << body if defined?(Tempfile) && body.kind_of?(Tempfile)
512 if method_defined?(:path)
513 alias local_path path
519 attr_reader :original_filename, :content_type
521 ## find head and boundary
524 until head && matched = boundary_rexp.match(buf)
525 if !head && pos = buf.index(separator)
526 len = pos + EOL.bytesize
528 buf = buf[(pos+separator.bytesize)..-1]
530 if head && buf.size > boundary_size
531 len = buf.size - boundary_size
532 body.print(buf[0, len])
535 c = stdin.read(bufsize < content_length ? bufsize : content_length)
536 raise EOFError.new("bad content body") if c.nil? || c.empty?
538 content_length -= c.bytesize
541 ## read to end of boundary
546 s = buf[0, len - $1.bytesize]
549 buf = buf[m.end(0)..-1]
551 content_length = -1 if boundary_end == '--'
552 ## reset file cursor position
555 /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
556 filename = $1 || $2 || ''.dup
557 filename = CGI.unescape(filename) if unescape_filename?()
558 body.instance_variable_set(:@original_filename, filename)
560 /Content-Type: (.*)/i.match(head)
561 (content_type = $1 || ''.dup).chomp!
562 body.instance_variable_set(:@content_type, content_type)
563 ## query parameter name
564 /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
565 name = $1 || $2 || ''
566 if body.original_filename.empty?
567 value=body.read.dup.force_encoding(@accept_charset)
568 body.close! if defined?(Tempfile) && body.kind_of?(Tempfile)
569 (params[name] ||= []) << value
570 unless value.valid_encoding?
571 if @accept_charset_error_block
572 @accept_charset_error_block.call(name,value)
574 raise InvalidEncoding,"Accept-Charset encoding error"
577 class << params[name].last;self;end.class_eval do
578 define_method(:read){self}
579 define_method(:original_filename){""}
580 define_method(:content_type){""}
583 (params[name] ||= []) << body
587 break if content_length == -1
589 raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
602 private :read_multipart
603 def create_body(is_large) #:nodoc:
606 body = Tempfile.new('CGI', encoding: Encoding::ASCII_8BIT)
610 body = StringIO.new("".b)
613 body = Tempfile.new('CGI', encoding: Encoding::ASCII_8BIT)
616 body.binmode if defined? body.binmode
619 def unescape_filename? #:nodoc:
620 user_agent = $CGI_ENV['HTTP_USER_AGENT']
621 return false unless user_agent
622 return /Mac/i.match(user_agent) && /Mozilla/i.match(user_agent) && !/MSIE/i.match(user_agent)
625 # offline mode. read name=value pairs on standard input.
626 def read_from_cmdline
629 string = unless ARGV.empty?
634 %|(offline mode: enter name=value pairs on standard input)\n|
637 array = readlines rescue nil
639 array.join(' ').gsub(/\n/n, '')
643 end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
645 words = Shellwords.shellwords(string)
647 if words.find{|x| /=/n.match(x) }
653 private :read_from_cmdline
655 # A wrapper class to use a StringIO object as the body and switch
656 # to a TempFile when the passed threshold is passed.
657 # Initialize the data from the query.
659 # Handles multipart forms (in particular, forms that involve file uploads).
660 # Reads query parameters in the @params field, and cookies into @cookies.
661 def initialize_query()
662 if ("POST" == env_table['REQUEST_METHOD']) and
663 %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?| =~ env_table['CONTENT_TYPE']
664 current_max_multipart_length = @max_multipart_length.respond_to?(:call) ? @max_multipart_length.call : @max_multipart_length
665 raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > current_max_multipart_length
668 @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
672 case env_table['REQUEST_METHOD']
674 if defined?(MOD_RUBY)
675 Apache::request.args or ""
677 env_table['QUERY_STRING'] or ""
680 stdinput.binmode if defined? stdinput.binmode
681 stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
684 end.dup.force_encoding(@accept_charset)
686 unless Encoding.find(@accept_charset) == Encoding::ASCII_8BIT
687 @params.each do |key,values|
688 values.each do |value|
689 unless value.valid_encoding?
690 if @accept_charset_error_block
691 @accept_charset_error_block.call(key,value)
693 raise InvalidEncoding,"Accept-Charset encoding error"
701 @cookies = CGI::Cookie.parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
703 private :initialize_query
705 # Returns whether the form contained multipart/form-data
710 # Get the value for the parameter with a given key.
712 # If the parameter has multiple values, only the first will be
713 # retrieved; use #params to get the array of values.
715 params = @params[key]
716 return '' unless params
721 elsif defined? StringIO
724 Tempfile.new("CGI",encoding: Encoding::ASCII_8BIT)
727 str = if value then value.dup else "" end
732 # Return all query parameter names as an array of String.
737 # Returns true if a given query string parameter exists.
739 @params.has_key?(*args)
742 alias include? has_key?
746 # Exception raised when there is an invalid encoding detected
747 class InvalidEncoding < Exception; end
749 # @@accept_charset is default accept character set.
750 # This default value default is "UTF-8"
751 # If you want to change the default accept character set
752 # when create a new CGI instance, set this:
754 # CGI.accept_charset = "EUC-JP"
756 @@accept_charset="UTF-8" if false # needed for rdoc?
758 # Return the accept character set for all new CGI instances.
759 def self.accept_charset
763 # Set the accept character set for all new CGI instances.
764 def self.accept_charset=(accept_charset)
765 @@accept_charset=accept_charset
768 # Return the accept character set for this CGI instance.
769 attr_reader :accept_charset
771 # @@max_multipart_length is the maximum length of multipart data.
772 # The default value is 128 * 1024 * 1024 bytes
774 # The default can be set to something else in the CGI constructor,
775 # via the :max_multipart_length key in the option hash.
777 # See CGI.new documentation.
779 @@max_multipart_length= 128 * 1024 * 1024
781 # Create a new CGI instance.
784 # CGI.new(tag_maker) { block }
785 # CGI.new(options_hash = {}) { block }
788 # <tt>tag_maker</tt>::
789 # This is the same as using the +options_hash+ form with the value <tt>{
790 # :tag_maker => tag_maker }</tt> Note that it is recommended to use the
791 # +options_hash+ form, since it also allows you specify the charset you
793 # <tt>options_hash</tt>::
794 # A Hash that recognizes three options:
796 # <tt>:accept_charset</tt>::
797 # specifies encoding of received query string. If omitted,
798 # <tt>@@accept_charset</tt> is used. If the encoding is not valid, a
799 # CGI::InvalidEncoding will be raised.
801 # Example. Suppose <tt>@@accept_charset</tt> is "UTF-8"
803 # when not specified:
805 # cgi=CGI.new # @accept_charset # => "UTF-8"
807 # when specified as "EUC-JP":
809 # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP"
811 # <tt>:tag_maker</tt>::
812 # String that specifies which version of the HTML generation methods to
813 # use. If not specified, no HTML generation methods will be loaded.
815 # The following values are supported:
819 # "html4Tr":: HTML 4.0 Transitional
820 # "html4Fr":: HTML 4.0 with Framesets
823 # <tt>:max_multipart_length</tt>::
824 # Specifies maximum length of multipart data. Can be an Integer scalar or
825 # a lambda, that will be evaluated when the request is parsed. This
826 # allows more complex logic to be set when determining whether to accept
827 # multipart data (e.g. consult a registered users upload allowance)
829 # Default is 128 * 1024 * 1024 bytes
831 # cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar
833 # cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda
836 # If provided, the block is called when an invalid encoding is
837 # encountered. For example:
840 # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
841 # encoding_errors[name] = value
844 # Finally, if the CGI object is not created in a standard CGI call
845 # environment (that is, it can't locate REQUEST_METHOD in its environment),
846 # then it will run in "offline" mode. In this mode, it reads its parameters
847 # from the command line or (failing that) from standard input. Otherwise,
848 # cookies and other parameters are parsed automatically from the standard
849 # CGI locations, which varies according to the REQUEST_METHOD.
850 def initialize(options = {}, &block) # :yields: name, value
851 @accept_charset_error_block = block_given? ? block : nil
853 :accept_charset=>@@accept_charset,
854 :max_multipart_length=>@@max_multipart_length
858 @options.merge!(options)
860 @options[:tag_maker]=options
862 @accept_charset=@options[:accept_charset]
863 @max_multipart_length=@options[:max_multipart_length]
864 if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
865 Apache.request.setup_cgi_env
868 extend QueryExtension
871 initialize_query() # set @params, @cookies
872 @output_cookies = nil
875 case @options[:tag_maker]
877 require_relative 'html'
881 require_relative 'html'
885 require_relative 'html'
889 require_relative 'html'
894 require_relative 'html'