From 3a7753b13b3f9754edd410173086959d189bfe8f Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Thu, 4 Dec 2025 10:30:26 +0900 Subject: [PATCH] Add CGI --- gems/cgi/.rubocop.yml | 19 + gems/cgi/0.5/_test/metadata.yaml | 4 + gems/cgi/0.5/_test/test.rb | 8 + gems/cgi/0.5/core.rbs | 901 +++++++++++++++++++++++++++++++ gems/cgi/0.5/manifest.yaml | 3 + 5 files changed, 935 insertions(+) create mode 100644 gems/cgi/.rubocop.yml create mode 100644 gems/cgi/0.5/_test/metadata.yaml create mode 100644 gems/cgi/0.5/_test/test.rb create mode 100644 gems/cgi/0.5/core.rbs create mode 100644 gems/cgi/0.5/manifest.yaml diff --git a/gems/cgi/.rubocop.yml b/gems/cgi/.rubocop.yml new file mode 100644 index 000000000..7d0be8ff2 --- /dev/null +++ b/gems/cgi/.rubocop.yml @@ -0,0 +1,19 @@ +# This configuration inherits from /.rubocop.yml. +# You can configure RBS style of this gem. +# This file is used on CI. It is configured to automatically +# make rubocop suggestions on pull requests for this gem. +# If you do not like the style enforcement, you should remove this file. +inherit_from: ../../.rubocop.yml + +## +# If you want to customize the style, please consult with the gem reviewers. +# You can see the list of cops at https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc + +RBS/Layout: + Enabled: true + +RBS/Lint: + Enabled: true + +RBS/Style: + Enabled: true diff --git a/gems/cgi/0.5/_test/metadata.yaml b/gems/cgi/0.5/_test/metadata.yaml new file mode 100644 index 000000000..29015bc02 --- /dev/null +++ b/gems/cgi/0.5/_test/metadata.yaml @@ -0,0 +1,4 @@ +# If the test needs gem's RBS files, declare them here. +# +# additional_gems: +# - GEM_NAME diff --git a/gems/cgi/0.5/_test/test.rb b/gems/cgi/0.5/_test/test.rb new file mode 100644 index 000000000..7fec9c5ba --- /dev/null +++ b/gems/cgi/0.5/_test/test.rb @@ -0,0 +1,8 @@ +# Write Ruby code to test the RBS. +# It is type checked by `steep check` command. + +require "cgi" +cgi = CGI.new +cgi.out("text/plain") { "string" } + +cookies = CGI::Cookie.parse("raw_cookie_string") diff --git a/gems/cgi/0.5/core.rbs b/gems/cgi/0.5/core.rbs new file mode 100644 index 000000000..4c563f0de --- /dev/null +++ b/gems/cgi/0.5/core.rbs @@ -0,0 +1,901 @@ +# +# :stopdoc +# +class CGI + include CGI::Util + + extend CGI::Util + + # + # Create a new CGI instance. + # + # `tag_maker` + # : This is the same as using the `options_hash` form with the value `{ + # :tag_maker => tag_maker }` Note that it is recommended to use the + # `options_hash` form, since it also allows you specify the charset you will + # accept. + # + # `options_hash` + # : A Hash that recognizes three options: + # + # `:accept_charset` + # : specifies encoding of received query string. If omitted, + # `@@accept_charset` is used. If the encoding is not valid, a + # CGI::InvalidEncoding will be raised. + # + # Example. Suppose `@@accept_charset` is "UTF-8" + # + # when not specified: + # + # cgi=CGI.new # @accept_charset # => "UTF-8" + # + # when specified as "EUC-JP": + # + # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP" + # + # + # `:tag_maker` + # : String that specifies which version of the HTML generation methods to + # use. If not specified, no HTML generation methods will be loaded. + # + # The following values are supported: + # + # "html3" + # : HTML 3.x + # + # "html4" + # : HTML 4.0 + # + # "html4Tr" + # : HTML 4.0 Transitional + # + # "html4Fr" + # : HTML 4.0 with Framesets + # + # "html5" + # : HTML 5 + # + # + # + # `:max_multipart_length` + # : Specifies maximum length of multipart data. Can be an Integer scalar + # or a lambda, that will be evaluated when the request is parsed. This + # allows more complex logic to be set when determining whether to accept + # multipart data (e.g. consult a registered users upload allowance) + # + # Default is 128 * 1024 * 1024 bytes + # + # cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar + # + # cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda + # + # + # + # `block` + # : If provided, the block is called when an invalid encoding is encountered. + # For example: + # + # encoding_errors={} + # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value| + # encoding_errors[name] = value + # end + # + # + # Finally, if the CGI object is not created in a standard CGI call environment + # (that is, it can't locate REQUEST_METHOD in its environment), then it will run + # in "offline" mode. In this mode, it reads its parameters from the command + # line or (failing that) from standard input. Otherwise, cookies and other + # parameters are parsed automatically from the standard CGI locations, which + # varies according to the REQUEST_METHOD. + # + def initialize: (?String tag_maker) ?{ (String name, String value) -> void } -> void + | (Hash[Symbol, untyped] options_hash) ?{ (String name, String value) -> void } -> void + + # + # Return the accept character set for all new CGI instances. + # + def self.accept_charset: () -> encoding + + # + # Set the accept character set for all new CGI instances. + # + def self.accept_charset=: (encoding accept_charset) -> encoding + + # + # Parse an HTTP query string into a hash of key=>value pairs. + # + # params = CGI.parse("query_string") + # # {"name1" => ["value1", "value2", ...], + # # "name2" => ["value1", "value2", ...], ... } + # + def self.parse: (String query) -> Hash[String, Array[String]] + + # + # Return the accept character set for this CGI instance. + # + attr_reader accept_charset: encoding + + # + # This method is an alias for #http_header, when HTML5 tag maker is inactive. + # + # NOTE: use #http_header to create HTTP header blocks, this alias is only + # provided for backwards compatibility. + # + # Using #header with the HTML5 tag maker will create a
element. + # + alias header http_header + + # + # Create an HTTP header block as a string. + # + # Includes the empty line that ends the header block. + # + # `content_type_string` + # : If this form is used, this string is the `Content-Type` + # + # `headers_hash` + # : A Hash of header values. The following header keys are recognized: + # + # type + # : The Content-Type header. Defaults to "text/html" + # + # charset + # : The charset of the body, appended to the Content-Type header. + # + # nph + # : A boolean value. If true, prepend protocol string and status code, + # and date; and sets default values for "server" and "connection" if not + # explicitly set. + # + # status + # : The HTTP status code as a String, returned as the Status header. The + # values are: + # + # OK + # : 200 OK + # + # PARTIAL_CONTENT + # : 206 Partial Content + # + # MULTIPLE_CHOICES + # : 300 Multiple Choices + # + # MOVED + # : 301 Moved Permanently + # + # REDIRECT + # : 302 Found + # + # NOT_MODIFIED + # : 304 Not Modified + # + # BAD_REQUEST + # : 400 Bad Request + # + # AUTH_REQUIRED + # : 401 Authorization Required + # + # FORBIDDEN + # : 403 Forbidden + # + # NOT_FOUND + # : 404 Not Found + # + # METHOD_NOT_ALLOWED + # : 405 Method Not Allowed + # + # NOT_ACCEPTABLE + # : 406 Not Acceptable + # + # LENGTH_REQUIRED + # : 411 Length Required + # + # PRECONDITION_FAILED + # : 412 Precondition Failed + # + # SERVER_ERROR + # : 500 Internal Server Error + # + # NOT_IMPLEMENTED + # : 501 Method Not Implemented + # + # BAD_GATEWAY + # : 502 Bad Gateway + # + # VARIANT_ALSO_VARIES + # : 506 Variant Also Negotiates + # + # + # + # server + # : The server software, returned as the Server header. + # + # connection + # : The connection type, returned as the Connection header (for instance, + # "close". + # + # length + # : The length of the content that will be sent, returned as the + # Content-Length header. + # + # language + # : The language of the content, returned as the Content-Language header. + # + # expires + # : The time on which the current content expires, as a `Time` object, + # returned as the Expires header. + # + # cookie + # : A cookie or cookies, returned as one or more Set-Cookie headers. The + # value can be the literal string of the cookie; a CGI::Cookie object; + # an Array of literal cookie strings or Cookie objects; or a hash all of + # whose values are literal cookie strings or Cookie objects. + # + # These cookies are in addition to the cookies held in the + # @output_cookies field. + # + # + # Other headers can also be set; they are appended as key: value. + # + # + # Examples: + # + # http_header + # # Content-Type: text/html + # + # http_header("text/plain") + # # Content-Type: text/plain + # + # http_header("nph" => true, + # "status" => "OK", # == "200 OK" + # # "status" => "200 GOOD", + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "length" => 103, + # "language" => "ja", + # "expires" => Time.now + 30, + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value", + # "my_header2" => "my_value") + # + # This method does not perform charset conversion. + # + def http_header: (?String options) -> String + | (?Hash[interned, untyped] header_hash) -> String + + def nph?: () -> untyped + + # + # Print an HTTP header and body to $DEFAULT_OUTPUT ($>) + # + # `content_type_string` + # : If a string is passed, it is assumed to be the content type. + # + # `headers_hash` + # : This is a Hash of headers, similar to that used by #http_header. + # + # `block` + # : A block is required and should evaluate to the body of the response. + # + # + # `Content-Length` is automatically calculated from the size of the String + # returned by the content block. + # + # If `ENV['REQUEST_METHOD'] == "HEAD"`, then only the header is output (the + # content block is still required, but it is ignored). + # + # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then the content is + # converted to this charset, and the language is set to "ja". + # + # Example: + # + # cgi = CGI.new + # cgi.out{ "string" } + # # Content-Type: text/html + # # Content-Length: 6 + # # + # # string + # + # cgi.out("text/plain") { "string" } + # # Content-Type: text/plain + # # Content-Length: 6 + # # + # # string + # + # cgi.out("nph" => true, + # "status" => "OK", # == "200 OK" + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "language" => "ja", + # "expires" => Time.now + (3600 * 24 * 30), + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value", + # "my_header2" => "my_value") { "string" } + # # HTTP/1.1 200 OK + # # Date: Sun, 15 May 2011 17:35:54 GMT + # # Server: Apache 2.2.0 + # # Connection: close + # # Content-Type: text/html; charset=iso-2022-jp + # # Content-Length: 6 + # # Content-Language: ja + # # Expires: Tue, 14 Jun 2011 17:35:54 GMT + # # Set-Cookie: foo + # # Set-Cookie: bar + # # my_header1: my_value + # # my_header2: my_value + # # + # # string + # + def out: (?String content_type_string) { () -> String } -> void + | (Hash[interned, untyped] headers_hash) { () -> String } -> void + + # + # Print an argument or list of arguments to the default output stream + # + # cgi = CGI.new + # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print + # + def print: (*String options) -> void + + private + + # + # Synonym for $stdin. + # + def stdinput: () -> ::IO + + # + # Synonym for $stdout. + # + def stdoutput: () -> ::IO + + # + # String for carriage return + # + CR: String + + # + # Standard internet newline sequence + # + EOL: String + + # + # HTTP status codes. + # + HTTP_STATUS: Hash[String, String] + + # + # String for linefeed + # + LF: String + + # + # Maximum number of request parameters when multipart + # + MAX_MULTIPART_COUNT: Integer + + # + # Whether processing will be required in binary vs text + # + NEEDS_BINMODE: bool + + # + # Path separators in different environments. + # + PATH_SEPARATOR: Hash[String, String] + + REVISION: String + + VERSION: String + + # + # Class representing an HTTP cookie. + # + # In addition to its specific fields and methods, a Cookie instance is a + # delegator to the array of its values. + # + # See RFC 2965. + # + # ## Examples of use + # cookie1 = CGI::Cookie.new("name", "value1", "value2", ...) + # cookie1 = CGI::Cookie.new("name" => "name", "value" => "value") + # cookie1 = CGI::Cookie.new('name' => 'name', + # 'value' => ['value1', 'value2', ...], + # 'path' => 'path', # optional + # 'domain' => 'domain', # optional + # 'expires' => Time.now, # optional + # 'secure' => true, # optional + # 'httponly' => true # optional + # ) + # + # cgi.out("cookie" => [cookie1, cookie2]) { "string" } + # + # name = cookie1.name + # values = cookie1.value + # path = cookie1.path + # domain = cookie1.domain + # expires = cookie1.expires + # secure = cookie1.secure + # httponly = cookie1.httponly + # + # cookie1.name = 'name' + # cookie1.value = ['value1', 'value2', ...] + # cookie1.path = 'path' + # cookie1.domain = 'domain' + # cookie1.expires = Time.now + 30 + # cookie1.secure = true + # cookie1.httponly = true + # + class Cookie < Array[String] + # + # Parse a raw cookie string into a hash of cookie-name=>Cookie pairs. + # + # cookies = CGI::Cookie.parse("raw_cookie_string") + # # { "name1" => cookie1, "name2" => cookie2, ... } + # + def self.parse: (String raw_cookie) -> Hash[String, instance] + + # + # Domain for which this cookie applies, as a `String` + # + def domain: () -> String? + + # + # Set domain for which this cookie applies + # + def domain=: (String domain) -> String + + # + # Time at which this cookie expires, as a `Time` + # + def expires: () -> Time? + + # + # Time at which this cookie expires, as a `Time` + # + def expires=: (Time time) -> Time + + # + # True if this cookie is httponly; false otherwise + # + def httponly: () -> bool + + # + # Set whether the Cookie is a httponly cookie or not. + # + # `val` must be a boolean. + # + def httponly=: (boolish val) -> bool + + # + # A summary of cookie string. + # + def inspect: () -> String + + # + # Name of this cookie, as a `String` + # + def name: () -> String + + # + # Set name of this cookie + # + def name=: (String name) -> String + + # + # Path for which this cookie applies, as a `String` + # + def path: () -> String? + + # + # Set path for which this cookie applies + # + def path=: (String path) -> String + + # + # True if this cookie is secure; false otherwise + # + def secure: () -> bool + + # + # Set whether the Cookie is a secure cookie or not. + # + # `val` must be a boolean. + # + def secure=: [A] (boolish val) -> A + + # + # Convert the Cookie to its string representation. + # + def to_s: () -> String + + # + # Returns the value or list of values for this cookie. + # + def value: () -> Array[String] + + # + # Replaces the value of this cookie with a new value or list of values. + # + def value=: (String val) -> String + | (Array[String] val) -> Array[String] + + private + + # + # Create a new CGI::Cookie object. + # + # `name_string` + # : The name of the cookie; in this form, there is no #domain or #expiration. + # The #path is gleaned from the `SCRIPT_NAME` environment variable, and + # #secure is false. + # + # `*value` + # : value or list of values of the cookie + # + # `options_hash` + # : A Hash of options to initialize this Cookie. Possible options are: + # + # name + # : the name of the cookie. Required. + # + # value + # : the cookie's value or list of values. + # + # path + # : the path for which this cookie applies. Defaults to the value of the + # `SCRIPT_NAME` environment variable. + # + # domain + # : the domain for which this cookie applies. + # + # expires + # : the time at which this cookie expires, as a `Time` object. + # + # secure + # : whether this cookie is a secure cookie or not (default to false). + # Secure cookies are only transmitted to HTTPS servers. + # + # httponly + # : whether this cookie is a HttpOnly cookie or not (default to + # + # false). HttpOnly cookies are not available to javascript. + # + # These keywords correspond to attributes of the cookie object. + # + def initialize: (String name_string, *String value) -> void + | (Hash[String, untyped] options_hash) -> void + end + + # + # Exception raised when there is an invalid encoding detected + # + class InvalidEncoding < Exception + end + + # + # Mixin module that provides the following: + # + # 1. Access to the CGI environment variables as methods. See documentation to + # the CGI class for a list of these variables. The methods are exposed by + # removing the leading `HTTP_` (if it exists) and downcasing the name. For + # example, `auth_type` will return the environment variable `AUTH_TYPE`, and + # `accept` will return the value for `HTTP_ACCEPT`. + # + # 2. Access to cookies, including the cookies attribute. + # + # 3. Access to parameters, including the params attribute, and overloading #[] + # to perform parameter value lookup by key. + # + # 4. The initialize_query method, for initializing the above mechanisms, + # handling multipart forms, and allowing the class to be used in "offline" + # mode. + # + module QueryExtension + # + # Get the value for the parameter with a given key. + # + # If the parameter has multiple values, only the first will be retrieved; use + # #params to get the array of values. + # + def []: (String key) -> String? + + def accept: () -> String? + + def accept_charset: () -> String? + + def accept_encoding: () -> String? + + def accept_language: () -> String? + + def auth_type: () -> String? + + def cache_control: () -> String? + + def content_length: () -> String? + + def content_type: () -> String? + + # + # Get the cookies as a hash of cookie-name=>Cookie pairs. + # + def cookies: () -> Hash[String, Cookie] + + # + # Get the cookies as a hash of cookie-name=>Cookie pairs. + # + def cookies=: (Hash[String, Cookie] cookies) -> Hash[String, Cookie] + + def create_body: (boolish is_large) -> (Tempfile | StringIO) + + # + # Get the uploaded files as a hash of name=>values pairs + # + def files: () -> Hash[String, Tempfile | StringIO] + + def from: () -> String? + + def gateway_interface: () -> String? + + # + # Returns true if a given query string parameter exists. + # + def has_key?: (String key) -> bool + + def host: () -> String? + + # + # + alias include? has_key? + + # + # + alias key? has_key? + + # + # Return all query parameter names as an array of String. + # + def keys: () -> Array[String] + + # + # Returns whether the form contained multipart/form-data + # + def multipart?: () -> bool + + def negotiate: () -> String? + + # + # Get the parameters as a hash of name=>values pairs, where values is an Array. + # + def params: () -> Hash[String, Array[String] | Tempfile | StringIO] + + # + # Set all the parameters. + # + def params=: (Hash[String, Array[String] | Tempfile | StringIO] hash) -> Hash[String, Array[String] | Tempfile | StringIO] + + def path_info: () -> String? + + def path_translated: () -> String? + + def pragma: () -> String? + + def query_string: () -> String? + + # + # Get the raw cookies as a string. + # + def raw_cookie: () -> String? + + # + # Get the raw RFC2965 cookies as a string. + # + def raw_cookie2: () -> String? + + def referer: () -> String? + + def remote_addr: () -> String? + + def remote_host: () -> String? + + def remote_ident: () -> String? + + def remote_user: () -> String? + + def request_method: () -> String? + + def script_name: () -> String? + + def server_name: () -> String? + + def server_port: () -> String? + + def server_protocol: () -> String? + + def server_software: () -> String? + + def unescape_filename?: () -> bool + + def user_agent: () -> String? + + private + + # + # A wrapper class to use a StringIO object as the body and switch to a TempFile + # when the passed threshold is passed. Initialize the data from the query. + # + # Handles multipart forms (in particular, forms that involve file uploads). + # Reads query parameters in the @params field, and cookies into @cookies. + # + def initialize_query: () -> void + + # + # offline mode. read name=value pairs on standard input. + # + def read_from_cmdline: () -> String + + # + # Parses multipart form elements according to + # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 + # + # Returns a hash of multipart form parameters with bodies of type StringIO or + # Tempfile depending on whether the multipart form element exceeds 10 KB + # + # params[name => body] + # + def read_multipart: (String boundary, Integer content_length) -> (Tempfile | StringIO) + end + + module Util + # + # Prettify (indent) an HTML string. + # + # `string` is the HTML string to indent. `shift` is the indentation unit to + # use; it defaults to two spaces. + # + # print CGI.pretty("") + # # + # # + # # + # # + # + # print CGI.pretty("", "\t") + # # + # # + # # + # # + # + def pretty: (string string, ?String shift) -> String + + # + # Format a `Time` object as a String using the format specified by RFC 1123. + # + # CGI.rfc1123_date(Time.now) + # # Sat, 01 Jan 2000 00:00:00 GMT + # + def rfc1123_date: (Time time) -> String + + # Abbreviated day-of-week names specified by RFC 822 + RFC822_DAYS: Array[String] + + # Abbreviated month names specified by RFC 822 + RFC822_MONTHS: Array[String] + + # + # The set of special characters and their escaped values + # + TABLE_FOR_ESCAPE_HTML__: Hash[String, String] + end +end diff --git a/gems/cgi/0.5/manifest.yaml b/gems/cgi/0.5/manifest.yaml new file mode 100644 index 000000000..f97f1bdf5 --- /dev/null +++ b/gems/cgi/0.5/manifest.yaml @@ -0,0 +1,3 @@ +dependencies: + - name: tempfile + - name: stringio