cgi 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f7c88bb08e51fd6b7abd1855b87d05c8af6b8f4e89865f0315f512badcddcb9
4
- data.tar.gz: fbe294b63d488ae16c123d325b2251a9561da5b474105a0ecbb1ecd880c4cdb6
3
+ metadata.gz: 9c5aa5b0794db539b4fa7b8a3da82182b587829ac7de10a444ef52447eef3811
4
+ data.tar.gz: 9a90fdb11ac132bd2c5693fa34c9ffdd5f492e66961561ef51bfab109e1deff0
5
5
  SHA512:
6
- metadata.gz: 9d4837752a514f02e88f8f3673fa0b0b9a31195df062d79c9d5b51175288f5053409fc337b1613ad27393b92c4394663d4cfd355d30c555b565f79a9102e2feb
7
- data.tar.gz: 0c465bae726d291b6829b6f0bf9f86d05976bf61529dc8190da4fd73022b37924c7901c3b29fb3cddb7760829aa24ac5cd6999c1f1ad9b4ec825fe0afc63ef19
6
+ metadata.gz: aaa90e2e539670dd03dfea7dcc26cb6382eef97fd783bcbc99f509018492386e7160a3944f52eb6fae589bfec628c5d935d5226412243d3267d7781945dd6f70
7
+ data.tar.gz: e9b846ecf59d6834dc02e7f1e1f5eb47ca845f2f85451a85c54c088aa547d01daf3a65651d61d769a86e30da2a242ea826bb231e83cf2421c811b4de67d07144
data/README.md CHANGED
@@ -22,11 +22,15 @@ gem 'cgi'
22
22
 
23
23
  And then execute:
24
24
 
25
- $ bundle
25
+ ```bash
26
+ bundle
27
+ ```
26
28
 
27
29
  Or install it yourself as:
28
30
 
29
- $ gem install cgi
31
+ ```bash
32
+ gem install cgi
33
+ ```
30
34
 
31
35
  ## Usage
32
36
 
@@ -73,7 +77,6 @@ db.transaction do
73
77
  end
74
78
  ```
75
79
 
76
-
77
80
  ### Restore form values from file
78
81
 
79
82
  ```ruby
data/lib/cgi/core.rb CHANGED
@@ -72,91 +72,267 @@ class CGI
72
72
 
73
73
  private :env_table, :stdinput, :stdoutput
74
74
 
75
- # Create an HTTP header block as a string.
76
- #
77
75
  # :call-seq:
78
- # http_header(content_type_string="text/html")
79
- # http_header(headers_hash)
80
- #
81
- # Includes the empty line that ends the header block.
76
+ # http_header(content_type = 'text/html') -> string
77
+ # http_header(headers) -> string
78
+ #
79
+ # Creates and returns an HTTP header section as a multi-line string.
80
+ #
81
+ # The string always includes:
82
+ #
83
+ # - Header +Content-Type+ (with a default value if none given).
84
+ # - A trailing newline, which delimits the header block;
85
+ # that last line is omitted from the examples below.
86
+ #
87
+ # <b>In Brief</b>
88
+ #
89
+ # headers = {
90
+ # 'charset' => 'iso-2022-jp',
91
+ # 'connection' => 'keep-alive',
92
+ # 'cookie' => 'foo=0',
93
+ # 'expires' => Time.now + (60 * 60 * 24 * 365),
94
+ # 'language' => 'en-US, en-CA',
95
+ # 'length' => 4096,
96
+ # 'nph' => true,
97
+ # 'server' => 'Apache/2.4.1 (Unix)',
98
+ # 'status' => 'OK',
99
+ # 'type' => 'text/xml',
100
+ # MyHeader: true
101
+ # }
102
+ #
103
+ # puts cgi.http_header(headers)
104
+ # HTTP/1.0 200 OK
105
+ # Date: Mon, 01 Dec 2025 22:08:22 GMT
106
+ # Server: Apache/2.4.1 (Unix)
107
+ # Connection: keep-alive
108
+ # Content-Type: text/xml; charset=iso-2022-jp
109
+ # Content-Length: 4096
110
+ # Content-Language: en-US, en-CA
111
+ # Expires: Tue, 01 Dec 2026 22:05:30 GMT
112
+ # Set-Cookie: foo=0
113
+ # MyHeader: true
114
+ #
115
+ # headers.delete('nph')
116
+ #
117
+ # puts cgi.http_header(headers)
118
+ # Status: 200 OK
119
+ # Server: Apache/2.4.1 (Unix)
120
+ # Connection: keep-alive
121
+ # Content-Type: text/xml; charset=iso-2022-jp
122
+ # Content-Length: 4096
123
+ # Content-Language: en-US, en-CA
124
+ # Expires: Tue, 01 Dec 2026 22:05:30 GMT
125
+ # Set-Cookie: foo=0
126
+ # MyHeader: true
127
+ #
128
+ # <b>Arguments</b>
129
+ #
130
+ # With no argument given,
131
+ # includes only header +Content-Type+ with its default value <tt>'text/html'</tt>:
132
+ #
133
+ # puts cgi.http_header
134
+ # Content-Type: text/html
135
+ #
136
+ # With string argument +content_type+ given,
137
+ # includes header +Content-Type+ with the given value:
82
138
  #
83
- # +content_type_string+::
84
- # If this form is used, this string is the <tt>Content-Type</tt>
85
- # +headers_hash+::
86
- # A Hash of header values. The following header keys are recognized:
87
- #
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.
93
- # status::
94
- # The HTTP status code as a String, returned as the Status header. The
95
- # values are:
96
- #
97
- # OK:: 200 OK
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
115
- #
116
- # server:: The server software, returned as the Server header.
117
- # connection:: The connection type, returned as the Connection header (for
118
- # instance, "close".
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
122
- # header.
123
- # expires:: The time on which the current content expires, as a +Time+
124
- # object, returned as the Expires header.
125
- # cookie::
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.
130
- #
131
- # These cookies are in addition to the cookies held in the
132
- # @output_cookies field.
133
- #
134
- # Other headers can also be set; they are appended as key: value.
135
- #
136
- # Examples:
137
- #
138
- # http_header
139
- # # Content-Type: text/html
139
+ # puts cgi.http_header('text/xml')
140
+ # Content-Type: text/xml
141
+ #
142
+ # With hash argument +headers+ given,
143
+ # includes a header for hash entry, whose name is based on the entry's key,
144
+ # and whose value is the entry's value.
145
+ #
146
+ # <i>Recognized Keys</i>
147
+ #
148
+ # The following keys are recognized;
149
+ # each is a lowercase string:
150
+ #
151
+ # <tt>'charset'</tt>::
152
+ # The character set of the body; appended to the +Content-Type+ header:
153
+ #
154
+ # puts cgi.http_header('charset' => 'iso-2022-jp')
155
+ # Content-Type: text/html; charset=iso-2022-jp
156
+ #
157
+ # <tt>'connection'</tt>::
158
+ # Sets header +Connection+ to the given string:
159
+ #
160
+ # puts cgi.http_header('connection' => 'keep-alive')
161
+ # Connection: keep-alive
162
+ # Content-Type: text/html
163
+ #
164
+ # <tt>'cookie'</tt>::
165
+ # Sets one or more +Set-Cookie+ headers to the given value, which may be:
166
+ #
167
+ # - String cookie.
168
+ # - CGI::Cookie object.
169
+ # - Array of string cookies and CGI::Cookie objects.
170
+ # - A hash whose values are string cookies and CGI::Cookie objects
171
+ # (the keys are not used).
172
+ #
173
+ # Examples:
174
+ #
175
+ # foo_string = 'foo=0'
176
+ # bar_string = 'bar=1'
177
+ # foo_cookie = CGI::Cookie.new('foo', '0')
178
+ # bar_cookie = CGI::Cookie.new('bar', '1')
179
+ #
180
+ # puts cgi.http_header('cookie' => foo_string)
181
+ # Content-Type: text/html
182
+ # Set-Cookie: foo=0
183
+ #
184
+ # puts cgi.http_header('cookie' => foo_cookie)
185
+ # Content-Type: text/html
186
+ # Set-Cookie: foo=0; path=
187
+ #
188
+ # puts cgi.http_header('cookie' => [foo_cookie, bar_string])
189
+ # Content-Type: text/html
190
+ # Set-Cookie: foo=0; path=
191
+ # Set-Cookie: bar=1
192
+ #
193
+ # puts cgi.http_header('cookie' => {foo: foo_cookie, bar: bar_string})
194
+ # Content-Type: text/html
195
+ # Set-Cookie: foo=0; path=
196
+ # Set-Cookie: bar=1
197
+ #
198
+ # These cookies are in addition to the cookies held
199
+ # in the <tt>@output_cookies</tt> variable.
200
+ #
201
+ # <tt>'expires'</tt>::
202
+ # Sets header +Expires+ to the given time,
203
+ # which must be a {Time}[https://docs.ruby-lang.org/en/master/Time.html] object:
204
+ #
205
+ # puts cgi.http_header('expires' => Time.now + (60 * 60 * 24 * 365))
206
+ # Content-Type: text/html
207
+ # Expires: Tue, 01 Dec 2026 23:42:37 GMT
208
+ #
209
+ # <tt>'language'</tt>::
210
+ # Sets header +Content-Language+ to the given string:
211
+ #
212
+ # puts cgi.http_header('language' => 'en-US, en-CA')
213
+ # Content-Type: text/html
214
+ # Content-Language: en-US, en-CA
215
+ #
216
+ # <tt>'length'</tt>::
217
+ # Sets header +Content-Length+ to the given value,
218
+ # which may be an integer or a string:
219
+ #
220
+ # puts cgi.http_header('length' => 4096)
221
+ # Content-Type: text/html
222
+ # Content-Length: 4096
140
223
  #
141
- # http_header("text/plain")
142
- # # Content-Type: text/plain
224
+ # puts cgi.http_header('length' => '4096')
225
+ # Content-Type: text/html
226
+ # Content-Length: 4096
143
227
  #
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
152
- # "length" => 103,
153
- # "language" => "ja",
154
- # "expires" => Time.now + 30,
155
- # "cookie" => [cookie1, cookie2],
156
- # "my_header1" => "my_value",
157
- # "my_header2" => "my_value")
228
+ # <tt>'nph'</tt>::
229
+ # If +true+:
230
+ #
231
+ # - Adds protocol string and status code as first line.
232
+ # - Adds date as second line.
233
+ # - Adds headers +Server+ with no value,
234
+ # and +Connection+ with default value <tt>'close'</tt>;
235
+ # either or both values may be overridden with explicit values.
236
+ #
237
+ # Examples:
238
+ #
239
+ # puts cgi.http_header('nph' => true)
240
+ # HTTP/1.0 200 OK
241
+ # Date: Mon, 01 Dec 2025 19:42:22 GMT
242
+ # Server:
243
+ # Connection: close
244
+ # Content-Type: text/html
245
+ #
246
+ # puts cgi.http_header('nph' => true, 'server' => 'Apache/2.4.1 (Unix)', 'connection' => 'keep-alive')
247
+ # HTTP/1.0 200 OK
248
+ # Date: Mon, 01 Dec 2025 20:00:41 GMT
249
+ # Server: Apache/2.4.1 (Unix)
250
+ # Connection: keep-alive
251
+ # Content-Type: text/html
252
+ #
253
+ # <tt>'server'</tt>::
254
+ # Sets header +Server+ to the given string:
255
+ #
256
+ # puts cgi.http_header('server' => 'Apache/2.4.1 (Unix)')
257
+ # Server: Apache/2.4.1 (Unix)
258
+ # Content-Type: text/html
259
+ #
260
+ # <tt>'status'</tt>::
261
+ # Sets header +Status+ to the given string:
262
+ #
263
+ # puts cgi.http_header('status' => '666 MyVeryOwnStatus')
264
+ # Status: 666 MyVeryOwnStatus
265
+ # Content-Type: text/html
266
+ #
267
+ # If the given string is a key in the hash constant +CGI::HTTP_STATUS+,
268
+ # the status becomes the value for that key:
269
+ #
270
+ # CGI::HTTP_STATUS
271
+ # # =>
272
+ # {"OK" => "200 OK",
273
+ # "PARTIAL_CONTENT" => "206 Partial Content",
274
+ # "MULTIPLE_CHOICES" => "300 Multiple Choices",
275
+ # "MOVED" => "301 Moved Permanently",
276
+ # "REDIRECT" => "302 Found",
277
+ # "NOT_MODIFIED" => "304 Not Modified",
278
+ # "BAD_REQUEST" => "400 Bad Request",
279
+ # "AUTH_REQUIRED" => "401 Authorization Required",
280
+ # "FORBIDDEN" => "403 Forbidden",
281
+ # "NOT_FOUND" => "404 Not Found",
282
+ # "METHOD_NOT_ALLOWED" => "405 Method Not Allowed",
283
+ # "NOT_ACCEPTABLE" => "406 Not Acceptable",
284
+ # "LENGTH_REQUIRED" => "411 Length Required",
285
+ # "PRECONDITION_FAILED" => "412 Precondition Failed",
286
+ # "SERVER_ERROR" => "500 Internal Server Error",
287
+ # "NOT_IMPLEMENTED" => "501 Method Not Implemented",
288
+ # "BAD_GATEWAY" => "502 Bad Gateway",
289
+ # "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"}
290
+ #
291
+ # puts cgi.http_header('status' => 'OK')
292
+ # Status: 200 OK
293
+ # Content-Type: text/html
294
+ #
295
+ # puts cgi.http_header('status' => 'NOT_FOUND')
296
+ # Status: 404 Not Found
297
+ # Content-Type: text/html
298
+ #
299
+ # <tt>'type'</tt>::
300
+ # Sets +Content-Type+, overriding the default value <tt>'text/html'</tt>:
301
+ #
302
+ # puts cgi.http_header('type' => 'text/xml')
303
+ # Content-Type: text/xml
304
+ #
305
+ # <i>Unrecognized Keys</i>
306
+ #
307
+ # Headers may also be set for unrecognized keys;
308
+ # an unrecognized key becomes a header name with the given value:
309
+ #
310
+ # puts cgi.http_header('length' => 0) # Recognized key (lowercase string).
311
+ # Content-Type: text/html
312
+ # Content-Length: 0
313
+ #
314
+ # puts cgi.http_header('Length' => 0) # Unrecognized key (string key not lowercase).
315
+ # Content-Type: text/html
316
+ # Length: 0
317
+ #
318
+ # puts cgi.http_header(length: 0) # Unrecognized key (symbol key not string)
319
+ # Content-Type: text/html
320
+ # length: 0
158
321
  #
159
322
  # This method does not perform charset conversion.
323
+ #
324
+ # It's best to use this method (CGI#http_header), not its aliased method +header+,
325
+ # which is provided only for backward compatibility.
326
+ #
327
+ # Method CGI#http_header is preferred because when +tag_maker+ is <tt>'html5'</tt>,
328
+ # calling method +header+ generates an HTML +header+ element:
329
+ #
330
+ # cgi = CGI.new(tag_maker: 'html5')
331
+ # puts cgi.http_header # Works as expected.
332
+ # Content-Type: text/html
333
+ # puts cgi.header # Maybe a surprise.
334
+ # <HEADER></HEADER>
335
+ #
160
336
  def http_header(options='text/html')
161
337
  if options.is_a?(String)
162
338
  content_type = options
@@ -481,7 +657,7 @@ class CGI
481
657
 
482
658
  ##
483
659
  # Parses multipart form elements according to
484
- # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
660
+ # https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
485
661
  #
486
662
  # Returns a hash of multipart form parameters with bodies of type StringIO or
487
663
  # Tempfile depending on whether the multipart form element exceeds 10 KB
@@ -662,13 +838,16 @@ class CGI
662
838
  # Handles multipart forms (in particular, forms that involve file uploads).
663
839
  # Reads query parameters in the @params field, and cookies into @cookies.
664
840
  def initialize_query()
841
+ content_length = env_table['CONTENT_LENGTH']
842
+ content_length = nil if content_length == ''
665
843
  if ("POST" == env_table['REQUEST_METHOD']) and
666
844
  %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?| =~ env_table['CONTENT_TYPE']
667
845
  current_max_multipart_length = @max_multipart_length.respond_to?(:call) ? @max_multipart_length.call : @max_multipart_length
668
846
  raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > current_max_multipart_length
847
+ raise StandardError.new("no content length for multipart data.") if content_length.nil?
669
848
  boundary = $1.dup
670
849
  @multipart = true
671
- @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
850
+ @params = read_multipart(boundary, Integer(content_length))
672
851
  else
673
852
  @multipart = false
674
853
  @params = CGI.parse(
@@ -681,7 +860,11 @@ class CGI
681
860
  end
682
861
  when "POST"
683
862
  stdinput.binmode if defined? stdinput.binmode
684
- stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
863
+ if content_length.nil?
864
+ stdinput.read or ''
865
+ else
866
+ stdinput.read(Integer(content_length)) or ''
867
+ end
685
868
  else
686
869
  read_from_cmdline
687
870
  end.dup.force_encoding(@accept_charset)
@@ -758,12 +941,75 @@ class CGI
758
941
  #
759
942
  @@accept_charset="UTF-8" if false # needed for rdoc?
760
943
 
761
- # Return the accept character set for all new CGI instances.
944
+ # :call-seq:
945
+ # CGI.accept_charset -> encoding
946
+ #
947
+ # Returns the default accept character set to be used for new \CGI instances;
948
+ # see CGI.accept_charset=.
762
949
  def self.accept_charset
763
950
  @@accept_charset
764
951
  end
765
952
 
766
- # Set the accept character set for all new CGI instances.
953
+ # :call-seq:
954
+ # CGI.accept_charset = encoding
955
+ #
956
+ # Sets the default accept character set to be used for new \CGI instances;
957
+ # returns the argument.
958
+ #
959
+ # The argument may be an Encoding object or an Encoding name;
960
+ # see {Encodings}[https://docs.ruby-lang.org/en/master/language/encodings_rdoc.html]:
961
+ #
962
+ # # The initial value.
963
+ # CGI.accept_charset # => #<Encoding:UTF-8>
964
+ # CGI.new
965
+ # # =>
966
+ # #<CGI:0x0000018991db6ae8
967
+ # @accept_charset=#<Encoding:UTF-8>,
968
+ # @accept_charset_error_block=nil,
969
+ # @cookies={},
970
+ # @max_multipart_length=134217728,
971
+ # @multipart=false,
972
+ # @options={accept_charset: #<Encoding:UTF-8>, max_multipart_length: 134217728},
973
+ # @output_cookies=nil,
974
+ # @output_hidden=nil,
975
+ # @params={}>
976
+ #
977
+ # # Set by Encoding name.
978
+ # CGI.accept_charset = 'US-ASCII' # => "US-ASCII"
979
+ # CGI.new
980
+ # # =>
981
+ # #<CGI:0x0000018991cf4100
982
+ # @accept_charset="US-ASCII",
983
+ # @accept_charset_error_block=nil,
984
+ # @cookies={},
985
+ # @max_multipart_length=134217728,
986
+ # @multipart=false,
987
+ # @options={accept_charset: "US-ASCII", max_multipart_length: 134217728},
988
+ # @output_cookies=nil,
989
+ # @output_hidden=nil,
990
+ # @params={}>
991
+ #
992
+ # # Set by Encoding object.
993
+ # CGI.accept_charset = Encoding::ASCII_8BIT # => #<Encoding:BINARY (ASCII-8BIT)>
994
+ # CGI.new
995
+ # # =>
996
+ # #<CGI:0x0000018991cfc800
997
+ # @accept_charset=#<Encoding:BINARY (ASCII-8BIT)>,
998
+ # @accept_charset_error_block=nil,
999
+ # @cookies={},
1000
+ # @max_multipart_length=134217728,
1001
+ # @multipart=false,
1002
+ # @options={accept_charset: #<Encoding:BINARY (ASCII-8BIT)>, max_multipart_length: 134217728},
1003
+ # @output_cookies=nil,
1004
+ # @output_hidden=nil,
1005
+ # @params={}>
1006
+ #
1007
+ # The given encoding is not checked in this method,
1008
+ # but if it is invalid, a call to CGI.new will fail:
1009
+ #
1010
+ # CGI.accept_charset = 'foo'
1011
+ # CGI.new # Raises ArgumentError: unknown encoding name - foo
1012
+ #
767
1013
  def self.accept_charset=(accept_charset)
768
1014
  @@accept_charset=accept_charset
769
1015
  end
@@ -803,16 +1049,18 @@ class CGI
803
1049
  # With no argument and no block given, returns a new \CGI object with default values:
804
1050
  #
805
1051
  # cgi = CGI.new
806
- # puts cgi.pretty_inspect
807
- # #<CGI:0x000002b0ea237bc8
808
- # @accept_charset=#<Encoding:UTF-8>,
809
- # @accept_charset_error_block=nil,
810
- # @cookies={},
811
- # @max_multipart_length=134217728,
812
- # @multipart=false,
813
- # @output_cookies=nil,
814
- # @output_hidden=nil,
815
- # @params={}>
1052
+ # cgi
1053
+ # # =>
1054
+ # #<CGI:0x00000189917aff00
1055
+ # @accept_charset=#<Encoding:UTF-8>,
1056
+ # @accept_charset_error_block=nil,
1057
+ # @cookies={},
1058
+ # @max_multipart_length=134217728,
1059
+ # @multipart=false,
1060
+ # @options={accept_charset: #<Encoding:UTF-8>, max_multipart_length: 134217728},
1061
+ # @output_cookies=nil,
1062
+ # @output_hidden=nil,
1063
+ # @params={}>
816
1064
  #
817
1065
  # With hash argument +options+ given and no block given,
818
1066
  # returns a new \CGI object with the given options.
@@ -852,27 +1100,50 @@ class CGI
852
1100
  # CGI.new(max_multipart_length: -> {check_filesystem})
853
1101
  #
854
1102
  # If the option is not given, the default is +134217728+, specifying a maximum size of 128 megabytes.
855
- #
856
- # <em>Note:</em> This option configures internal behavior only.
857
- # There is no public method to retrieve this value after initialization.
1103
+ #
1104
+ # <em>Note:</em> This option configures internal behavior only.
1105
+ # There is no public method to retrieve this value after initialization.
858
1106
  #
859
1107
  # - <tt>tag_maker: _html_version_</tt>:
860
- # specifies which version of HTML to use in generating tags.
1108
+ # specifies a version of HTML;
1109
+ # this determines which tag-generating instance methods
1110
+ # (such as +html+, +head+, +body+, etc.) are to be loaded.
861
1111
  #
862
1112
  # Value _html_version_ may be one of:
863
1113
  #
864
- # - <tt>'html3'</tt>: {HTML version 3}[https://en.wikipedia.org/wiki/HTML#HTML_3].
865
- # - <tt>'html4'</tt>: {HTML version 4}[https://en.wikipedia.org/wiki/HTML#HTML_4].
866
- # - <tt>'html4Tr'</tt>: HTML 4.0 Transitional.
867
- # - <tt>'html4Fr'</tt>: HTML 4.0 with Framesets.
868
- # - <tt>'html5'</tt>: {HTML version 5}[https://en.wikipedia.org/wiki/HTML#HTML_5].
1114
+ # - <tt>'html3'</tt>: {HTML version 3}[https://www.w3.org/MarkUp/html3/Contents.html].
1115
+ # - <tt>'html4'</tt>: {HTML version 4}[https://www.w3.org/TR/html4].
1116
+ # - <tt>'html4Tr'</tt>: {HTML 4.0 Transitional}[https://www.w3.org/TR/html4/sgml/loosedtd.html].
1117
+ # - <tt>'html4Fr'</tt>: {HTML 4.0 with Framesets}[https://www.w3.org/TR/html4/present/frames.html].
1118
+ # - <tt>'html5'</tt>: {HTML version 5}[https://html.spec.whatwg.org/multipage].
869
1119
  #
870
1120
  # Example:
871
1121
  #
872
1122
  # CGI.new(tag_maker: 'html5')
873
1123
  #
874
- # If the option is not given,
875
- # no HTML generation methods are loaded.
1124
+ # If the option is not given (or if an invalid value is given),
1125
+ # no tag-generating methods are loaded.
1126
+ #
1127
+ # Examples:
1128
+ #
1129
+ # CGI.new.respond_to?(:html) # => false # Tag-generating methods not loaded.
1130
+ # CGI.new(tag_maker: 'html3').respond_to?(:html) # => true # Tag-generating methods loaded.
1131
+ # # Tag 'button' is new in HTML 4.
1132
+ # CGI.new(tag_maker: 'html3').respond_to?(:button) # => false
1133
+ # CGI.new(tag_maker: 'html4').respond_to?(:button) # => true
1134
+ # # Tag 'canvas' is new in HTML 5.
1135
+ # CGI.new(tag_maker: 'html4').respond_to?(:canvas) # => false
1136
+ # CGI.new(tag_maker: 'html5').respond_to?(:canvas) # => true
1137
+ # # Value is case-sensitive.
1138
+ # CGI.new(tag_maker: 'HTML4').respond_to?(:html) # => false
1139
+ #
1140
+ # You can determine exactly which methods have been loaded;
1141
+ # this example captures the methods loaded by <tt>'html4'</tt>:
1142
+ #
1143
+ # methods_loaded = CGI.new('html4').methods - CGI.new.methods
1144
+ # methods_loaded.size # => 98
1145
+ # methods_loaded.sort.take(10)
1146
+ # # => [:a, :abbr, :acronym, :address, :area, :b, :base, :bdo, :big, :blockquote]
876
1147
  #
877
1148
  # With string argument +tag_maker+ given as _tag_maker_ and no block given,
878
1149
  # equivalent to <tt>CGI.new(tag_maker: _tag_maker_)</tt>:
@@ -890,6 +1161,28 @@ class CGI
890
1161
  # from the command line or (failing that) from standard input;
891
1162
  # returns a new \CGI object.
892
1163
  #
1164
+ # Parameters from command line:
1165
+ #
1166
+ # $ cat t.rb
1167
+ # require 'cgi'
1168
+ # cgi = CGI.new
1169
+ # p cgi.params
1170
+ # ruby t.rb foo=0 bar=1 foo=2 bar=3
1171
+ # {"foo" => ["0", "2"], "bar" => ["1", "3"]}
1172
+ #
1173
+ # Parameters from standard input:
1174
+ #
1175
+ # cgi = CGI.new
1176
+ # (offline mode: enter name=value pairs on standard input)
1177
+ # foo=0
1178
+ # bar=1
1179
+ # ^D
1180
+ # cgi.params
1181
+ # # => {"foo" => ["0"], "bar" => ["1"]}
1182
+ #
1183
+ # The end-of-file character is Ctrl-D on a Unix-like system (as above),
1184
+ # or Ctrl-Z on Windows.
1185
+ #
893
1186
  # Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations,
894
1187
  # which vary according to the request method.
895
1188
  #
@@ -911,7 +1204,7 @@ class CGI
911
1204
  #
912
1205
  # In this example, the proc simply saves the error:
913
1206
  #
914
- # encoding_errors={}
1207
+ # encoding_errors = {}
915
1208
  # CGI.new(accept_charset: 'EUC-JP') do |name,value|
916
1209
  # encoding_errors[name] = value
917
1210
  # end
data/lib/cgi/escape.rb CHANGED
@@ -132,41 +132,32 @@ module CGI::Escape
132
132
  end
133
133
  string = string.b
134
134
  string.gsub!(/&(apos|amp|quot|gt|lt|\#[0-9]+|\#[xX][0-9A-Fa-f]+);/) do
135
- match = $1.dup
135
+ match = $1
136
136
  case match
137
137
  when 'apos' then "'"
138
138
  when 'amp' then '&'
139
139
  when 'quot' then '"'
140
140
  when 'gt' then '>'
141
141
  when 'lt' then '<'
142
- when /\A#0*(\d+)\z/
143
- n = $1.to_i
144
- if n < charlimit
145
- n.chr(enc)
146
- else
147
- "&##{$1};"
148
- end
149
- when /\A#x([0-9a-f]+)\z/i
150
- n = $1.hex
151
- if n < charlimit
152
- n.chr(enc)
142
+ else
143
+ # Numeric character reference. Decode into the binary buffer so that a
144
+ # non-ASCII byte already present in it never triggers an encoding
145
+ # compatibility error on concatenation; the trailing force_encoding
146
+ # re-tags the whole buffer. This mirrors the C extension's
147
+ # optimized_unescape_html.
148
+ n = match.start_with?('#x', '#X') ? match[2..-1].hex : match[1..-1].to_i
149
+ if n >= charlimit
150
+ "&#{match};" # out of range: keep the reference verbatim
151
+ elsif charlimit > 256
152
+ [n].pack('U').b # UTF-8: code point bytes, surrogates included (like rb_enc_mbcput)
153
153
  else
154
- "&#x#{$1};"
154
+ n.chr.b # ISO-8859-1 / ASCII: single byte
155
155
  end
156
- else
157
- "&#{match};"
158
156
  end
159
157
  end
160
158
  string.force_encoding enc
161
159
  end
162
160
 
163
- # Synonym for CGI.escapeHTML(str)
164
- alias escape_html escapeHTML
165
- alias h escapeHTML
166
-
167
- # Synonym for CGI.unescapeHTML(str)
168
- alias unescape_html unescapeHTML
169
-
170
161
  # TruffleRuby runs the pure-Ruby variant faster, do not use the C extension there
171
162
  unless RUBY_ENGINE == 'truffleruby'
172
163
  begin
@@ -175,6 +166,14 @@ module CGI::Escape
175
166
  end
176
167
  end
177
168
 
169
+ # Aliases must be defined on EscapeExt so they resolve to the C methods.
170
+ target = defined?(CGI::EscapeExt) && CGI::EscapeExt.method_defined?(:escapeHTML) ? CGI::EscapeExt : self
171
+ target.module_eval do
172
+ alias escape_html escapeHTML
173
+ alias h escapeHTML
174
+ alias unescape_html unescapeHTML
175
+ end
176
+
178
177
  # Escape only the tags of certain HTML elements in +string+.
179
178
  #
180
179
  # Takes an element or elements or array of elements. Each element
data/lib/cgi/session.rb CHANGED
@@ -206,19 +206,28 @@ class CGI
206
206
  # on Unix systems).
207
207
  # prefix:: the prefix to add to the session id when generating
208
208
  # the filename for this session's FileStore file.
209
- # Defaults to "cgi_sid_".
209
+ # Defaults to the empty string.
210
210
  # suffix:: the prefix to add to the session id when generating
211
211
  # the filename for this session's FileStore file.
212
212
  # Defaults to the empty string.
213
+ # digest:: the digest algorithm to hash the session id when
214
+ # generating the filename for this session's FileStore
215
+ # file. Defaults to "SHA256".
216
+ # length:: the length of the session id part of the filestore,
217
+ # excluding +prefix+ and +suffix+ parts. Defaults to 16.
213
218
  def new_store_file(option={}) # :nodoc:
214
219
  dir = option['tmpdir'] || Dir::tmpdir
215
220
  prefix = option['prefix']
216
221
  suffix = option['suffix']
217
- require 'digest'
218
- sha256 = Digest::SHA256.hexdigest(session_id)[0,16]
222
+ algorithm = option['digest'] || 'SHA256'
223
+ if String === algorithm
224
+ require 'digest'
225
+ algorithm = Digest(algorithm)
226
+ end
227
+ digest = algorithm.hexdigest(session_id)[0, option['length'] || 16]
219
228
  path = dir+"/"
220
229
  path << prefix if prefix
221
- path << sha256
230
+ path << digest
222
231
  path << suffix if suffix
223
232
  if File::exist? path
224
233
  hash = nil
@@ -410,6 +419,9 @@ class CGI
410
419
  # suffix:: the prefix to add to the session id when generating
411
420
  # the filename for this session's FileStore file.
412
421
  # Defaults to the empty string.
422
+ # digest:: the digest algorithm to hash the session id when
423
+ # generating the filename for this session's FileStore
424
+ # file. Defaults to "MD5".
413
425
  #
414
426
  # This session's FileStore file will be created if it does
415
427
  # not exist, or opened if it does.
data/lib/cgi.rb CHANGED
@@ -26,7 +26,7 @@
26
26
  # The file CGI::Session provides session management functionality; see that
27
27
  # class for more details.
28
28
  #
29
- # See http://www.w3.org/CGI/ for more information on the CGI protocol.
29
+ # See https://www.w3.org/CGI/ for more information on the CGI protocol.
30
30
  #
31
31
  # == Introduction
32
32
  #
@@ -300,7 +300,7 @@
300
300
 
301
301
  class CGI
302
302
  # The version string
303
- VERSION = "0.5.1"
303
+ VERSION = "0.5.2"
304
304
  end
305
305
 
306
306
  require 'cgi/util'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cgi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yukihiro Matsumoto
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-10 00:00:00.000000000 Z
10
+ date: 2026-06-23 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: Support for the Common Gateway Interface protocol.
13
13
  email:
@@ -38,6 +38,7 @@ licenses:
38
38
  metadata:
39
39
  homepage_uri: https://github.com/ruby/cgi
40
40
  source_code_uri: https://github.com/ruby/cgi
41
+ changelog_uri: https://github.com/ruby/cgi/releases
41
42
  rdoc_options: []
42
43
  require_paths:
43
44
  - lib