diff --git a/stdlib/open-uri/0/manifest.yaml b/stdlib/open-uri/0/manifest.yaml
new file mode 100644
index 000000000..d5cea9508
--- /dev/null
+++ b/stdlib/open-uri/0/manifest.yaml
@@ -0,0 +1,3 @@
+dependencies:
+ - name: tempfile
+ - name: uri
diff --git a/stdlib/open-uri/0/open-uri.rbs b/stdlib/open-uri/0/open-uri.rbs
new file mode 100644
index 000000000..7b6649c08
--- /dev/null
+++ b/stdlib/open-uri/0/open-uri.rbs
@@ -0,0 +1,341 @@
+%a{annotate:rdoc:skip}
+module URI
+ #
+ # Allows the opening of various resources including URIs.
+ #
+ # If the first argument responds to the 'open' method, 'open' is called on it
+ # with the rest of the arguments.
+ #
+ # If the first argument is a string that begins with `(protocol)://`, it is
+ # parsed by URI.parse. If the parsed object responds to the 'open' method,
+ # 'open' is called on it with the rest of the arguments.
+ #
+ # Otherwise, Kernel#open is called.
+ #
+ # OpenURI::OpenRead#open provides URI::HTTP#open, URI::HTTPS#open and
+ # URI::FTP#open, Kernel#open.
+ #
+ # We can accept URIs and strings that begin with http://, https:// and ftp://.
+ # In these cases, the opened file object is extended by OpenURI::Meta.
+ #
+ def self.open: (String name, ?String mode, ?Integer perm, ?untyped options) -> (StringIO & OpenURI::Meta | Tempfile & OpenURI::Meta)
+ | [T] (String name, ?String mode, ?Integer perm, ?untyped options) { (StringIO | Tempfile) -> T } -> T
+end
+
+#
+# OpenURI is an easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP.
+#
+# ## Example
+#
+# It is possible to open an http, https or ftp URL as though it were a file:
+#
+# URI.open("http://www.ruby-lang.org/") {|f|
+# f.each_line {|line| p line}
+# }
+#
+# The opened file has several getter methods for its meta-information, as
+# follows, since it is extended by OpenURI::Meta.
+#
+# URI.open("http://www.ruby-lang.org/en") {|f|
+# f.each_line {|line| p line}
+# p f.base_uri #
+# p f.content_type # "text/html"
+# p f.charset # "iso-8859-1"
+# p f.content_encoding # []
+# p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
+# }
+#
+# Additional header fields can be specified by an optional hash argument.
+#
+# URI.open("http://www.ruby-lang.org/en/",
+# "User-Agent" => "Ruby/#{RUBY_VERSION}",
+# "From" => "foo@bar.invalid",
+# "Referer" => "http://www.ruby-lang.org/") {|f|
+# # ...
+# }
+#
+# The environment variables such as http_proxy, https_proxy and ftp_proxy are in
+# effect by default. Here we disable proxy:
+#
+# URI.open("http://www.ruby-lang.org/en/", :proxy => nil) {|f|
+# # ...
+# }
+#
+# See OpenURI::OpenRead.open and URI.open for more on available options.
+#
+# URI objects can be opened in a similar way.
+#
+# uri = URI.parse("http://www.ruby-lang.org/en/")
+# uri.open {|f|
+# # ...
+# }
+#
+# URI objects can be read directly. The returned string is also extended by
+# OpenURI::Meta.
+#
+# str = uri.read
+# p str.base_uri
+#
+# Author
+# : Tanaka Akira
+#
+module OpenURI
+ #
+ # Mixin for holding meta-information.
+ #
+ module Meta
+ #
+ # returns an Array that consists of status code and message.
+ #
+ attr_accessor status: [ String, String ]
+
+ #
+ # returns a URI that is the base of relative URIs in the data. It may differ
+ # from the URI supplied by a user due to redirection.
+ #
+ attr_accessor base_uri: URI::Generic
+
+ #
+ # returns a Hash that represents header fields. The Hash keys are downcased for
+ # canonicalization. The Hash values are a field body. If there are multiple
+ # field with same field name, the field values are concatenated with a comma.
+ #
+ attr_reader meta: Hash[String, String]
+
+ #
+ # returns a Time that represents the Last-Modified field.
+ #
+ def last_modified: () -> Time?
+
+ #
+ # returns "type/subtype" which is MIME Content-Type. It is downcased for
+ # canonicalization. Content-Type parameters are stripped.
+ #
+ def content_type: () -> String
+
+ def charet: () -> String?
+
+ #
+ # Returns a list of encodings in Content-Encoding field as an array of strings.
+ #
+ # The encodings are downcased for canonicalization.
+ #
+ def content_encoding: () -> Array[String]
+ end
+
+ #
+ # Mixin for HTTP and FTP URIs.
+ #
+ module OpenRead
+ #
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
+ #
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
+ #
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
+ #
+ # OpenURI::OpenRead#open returns an IO-like object if block is not given.
+ # Otherwise it yields the IO object and return the value of the block. The IO
+ # object is extended with OpenURI::Meta.
+ #
+ # `mode` and `perm` are the same as Kernel#open.
+ #
+ # However, `mode` must be read mode because OpenURI::OpenRead#open doesn't
+ # support write mode (yet). Also `perm` is ignored because it is meaningful only
+ # for file creation.
+ #
+ # `options` must be a hash.
+ #
+ # Each option with a string key specifies an extra header field for HTTP. I.e.,
+ # it is ignored for FTP without HTTP proxy.
+ #
+ # The hash may include other options, where keys are symbols:
+ #
+ # :proxy
+ # : Synopsis:
+ # :proxy => "http://proxy.foo.com:8000/"
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
+ # :proxy => true
+ # :proxy => false
+ # :proxy => nil
+ #
+ # If :proxy option is specified, the value should be String, URI, boolean or
+ # nil.
+ #
+ # When String or URI is given, it is treated as proxy URI.
+ #
+ # When true is given or the option itself is not specified, environment
+ # variable `scheme_proxy' is examined. `scheme' is replaced by `http',
+ # `https' or `ftp'.
+ #
+ # When false or nil is given, the environment variables are ignored and
+ # connection will be made to a server directly.
+ #
+ # :proxy_http_basic_authentication
+ # : Synopsis:
+ # :proxy_http_basic_authentication =>
+ # ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"]
+ # :proxy_http_basic_authentication =>
+ # [URI.parse("http://proxy.foo.com:8000/"),
+ # "proxy-user", "proxy-password"]
+ #
+ # If :proxy option is specified, the value should be an Array with 3
+ # elements. It should contain a proxy URI, a proxy user name and a proxy
+ # password. The proxy URI should be a String, an URI or nil. The proxy
+ # user name and password should be a String.
+ #
+ # If nil is given for the proxy URI, this option is just ignored.
+ #
+ # If :proxy and :proxy_http_basic_authentication is specified, ArgumentError
+ # is raised.
+ #
+ # :http_basic_authentication
+ # : Synopsis:
+ # :http_basic_authentication=>[user, password]
+ #
+ # If :http_basic_authentication is specified, the value should be an array
+ # which contains 2 strings: username and password. It is used for HTTP Basic
+ # authentication defined by RFC 2617.
+ #
+ # :content_length_proc
+ # : Synopsis:
+ # :content_length_proc => lambda {|content_length| ... }
+ #
+ # If :content_length_proc option is specified, the option value procedure is
+ # called before actual transfer is started. It takes one argument, which is
+ # expected content length in bytes.
+ #
+ # If two or more transfers are performed by HTTP redirection, the procedure
+ # is called only once for the last transfer.
+ #
+ # When expected content length is unknown, the procedure is called with nil.
+ # This happens when the HTTP response has no Content-Length header.
+ #
+ # :progress_proc
+ # : Synopsis:
+ # :progress_proc => lambda {|size| ...}
+ #
+ # If :progress_proc option is specified, the proc is called with one
+ # argument each time when `open' gets content fragment from network. The
+ # argument `size` is the accumulated transferred size in bytes.
+ #
+ # If two or more transfer is done by HTTP redirection, the procedure is
+ # called only one for a last transfer.
+ #
+ # :progress_proc and :content_length_proc are intended to be used for
+ # progress bar. For example, it can be implemented as follows using
+ # Ruby/ProgressBar.
+ #
+ # pbar = nil
+ # open("http://...",
+ # :content_length_proc => lambda {|t|
+ # if t && 0 < t
+ # pbar = ProgressBar.new("...", t)
+ # pbar.file_transfer_mode
+ # end
+ # },
+ # :progress_proc => lambda {|s|
+ # pbar.set s if pbar
+ # }) {|f| ... }
+ #
+ # :read_timeout
+ # : Synopsis:
+ # :read_timeout=>nil (no timeout)
+ # :read_timeout=>10 (10 second)
+ #
+ # :read_timeout option specifies a timeout of read for http connections.
+ #
+ # :open_timeout
+ # : Synopsis:
+ # :open_timeout=>nil (no timeout)
+ # :open_timeout=>10 (10 second)
+ #
+ # :open_timeout option specifies a timeout of open for http connections.
+ #
+ # :ssl_ca_cert
+ # : Synopsis:
+ # :ssl_ca_cert=>filename or an Array of filenames
+ #
+ # :ssl_ca_cert is used to specify CA certificate for SSL. If it is given,
+ # default certificates are not used.
+ #
+ # :ssl_verify_mode
+ # : Synopsis:
+ # :ssl_verify_mode=>mode
+ #
+ # :ssl_verify_mode is used to specify openssl verify mode.
+ #
+ # :ssl_min_version
+ # : Synopsis:
+ # :ssl_min_version=>:TLS1_2
+ #
+ # :ssl_min_version option specifies the minimum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#min_version=.
+ #
+ # :ssl_max_version
+ # : Synopsis:
+ # :ssl_max_version=>:TLS1_2
+ #
+ # :ssl_max_version option specifies the maximum allowed SSL/TLS protocol
+ # version. See also OpenSSL::SSL::SSLContext#max_version=.
+ #
+ # :ftp_active_mode
+ # : Synopsis:
+ # :ftp_active_mode=>bool
+ #
+ # `:ftp_active_mode => true` is used to make ftp active mode. Ruby 1.9 uses
+ # passive mode by default. Note that the active mode is default in Ruby 1.8
+ # or prior.
+ #
+ # :redirect
+ # : Synopsis:
+ # :redirect=>bool
+ #
+ # `:redirect` is true by default. `:redirect => false` is used to disable
+ # all HTTP redirects.
+ #
+ # OpenURI::HTTPRedirect exception raised on redirection. Using `true` also
+ # means that redirections between http and ftp are permitted.
+ #
+ def open: (*untyped) -> IO
+ | [T] (*untyped) { (IO) -> T } -> T
+
+ #
+ # OpenURI::OpenRead#read([ options ]) reads a content referenced by self and
+ # returns the content as string. The string is extended with OpenURI::Meta. The
+ # argument `options` is same as OpenURI::OpenRead#open.
+ #
+ def read: (untyped options) -> String
+ end
+end
+
+%a{annotate:rdoc:skip}
+module URI
+ %a{annotate:rdoc:skip}
+ class HTTP
+ include OpenURI::OpenRead
+ end
+
+ %a{annotate:rdoc:skip}
+ class FTP
+ include OpenURI::OpenRead
+ end
+end
diff --git a/test/stdlib/open-uri_test.rb b/test/stdlib/open-uri_test.rb
new file mode 100644
index 000000000..acc9f03c9
--- /dev/null
+++ b/test/stdlib/open-uri_test.rb
@@ -0,0 +1,16 @@
+require_relative "test_helper"
+require "open-uri"
+
+class OpenURISingletonTest < Test::Unit::TestCase
+ include TypeAssertions
+
+ library "open-uri"
+ testing "singleton(::URI)"
+
+ def test_URI_open
+ assert_send_type "(String) -> StringIO",
+ URI, :open, "https://www.ruby-lang.org"
+ assert_send_type "(String) { (StringIO) -> String } -> String",
+ URI, :open, "https://www.ruby-lang.org" do |io| io.read end
+ end
+end