From 21538fc0240bba207f2900be9a6cc5d79065b8e0 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Wed, 18 May 2022 09:56:32 -0500 Subject: [PATCH 1/2] Enhanced RDoc for Net::HTTP --- lib/net/http.rb | 133 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 32 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index acfdbec4..56292495 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -36,7 +36,7 @@ class HTTPHeaderSyntaxError < StandardError; end # # Net::HTTP provides a rich library which can be used to build HTTP # user-agents. For more details about HTTP see - # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt). + # {RFC2616}[https://datatracker.ietf.org/doc/html/rfc2616]. # # Net::HTTP is designed to work closely with URI. URI::HTTP#host, # URI::HTTP#port and URI::HTTP#request_uri are designed to work with @@ -44,17 +44,21 @@ class HTTPHeaderSyntaxError < StandardError; end # # If you are only performing a few GET requests you should try OpenURI. # - # == Simple Examples + # == About the Examples + # + # Many examples on this page refer to one of these two websites: + # + # - {Example Domain}[http://www.example.com]. + # - {JSONPlaceholder}[https://jsonplaceholder.typicode.com]. # # All examples assume you have loaded Net::HTTP with: # # require 'net/http' # - # This will also require 'uri' so you don't need to require it separately. + # == Simple Examples # - # The Net::HTTP methods in the following section do not persist - # connections. They are not recommended if you are performing many HTTP - # requests. + # The Net::HTTP methods in this section do not persist connections. + # They are not recommended if you are performing many HTTP requests. # # === GET # @@ -433,20 +437,41 @@ class << HTTP # short cut methods # + # Prints the body text from the target to $stdout; + # returns +nil+. + # + # The target may be specified either as: + # + # - A URI: # - # Gets the body text from the target and outputs it to $stdout. The - # target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: + # uri = URI('https://jsonplaceholder.typicode.com/todos/1') + # Net::HTTP.get_print(uri) # - # Net::HTTP.get_print URI('http://www.example.com/index.html') + # With this form, you may specify a hash of headers: # - # or: + # headers = {Accept: 'text/json'} + # Net::HTTP.get_print(uri, headers) # - # Net::HTTP.get_print 'www.example.com', '/index.html' + # - Host and path: # - # you can also specify request headers: + # host = 'jsonplaceholder.typicode.com' + # path = '/todos/1' + # Net::HTTP.get_print(host, path) # - # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' } + # With this form, you may specify an integer port: + # + # Net::HTTP.get_print(host, path, 80) + # + # Any of the above prints: + # + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": false + # } + # + # Related: HTTP.get, HTTP.get_response. # def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) get_response(uri_or_host, path_or_headers, port) {|res| @@ -457,39 +482,83 @@ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) nil end - # Sends a GET request to the target and returns the HTTP response - # as a string. The target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: + # Returns the body text (string) from the target to $stdout. + # + # The target may be specified either as: + # + # - A URI: # - # print Net::HTTP.get(URI('http://www.example.com/index.html')) + # uri = URI('https://jsonplaceholder.typicode.com/todos/1') + # puts Net::HTTP.get(uri) # - # or: + # With this form, you may specify a hash of headers: # - # print Net::HTTP.get('www.example.com', '/index.html') + # headers = {Accept: 'text/json'} + # puts Net::HTTP.get(uri, headers) # - # you can also specify request headers: + # - Host and path: # - # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # host = 'jsonplaceholder.typicode.com' + # path = '/todos/1' + # puts Net::HTTP.get(host, path) + # + # With this form, you may specify an integer port: + # + # puts Net::HTTP.get(host, path, 80) + # + # Any of the above prints (via +puts+): + # + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": false + # } + # + # Related: HTTP.get_print, HTTP.get_response. # def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) get_response(uri_or_host, path_or_headers, port).body end - # Sends a GET request to the target and returns the HTTP response - # as a Net::HTTPResponse object. The target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: + # Gets the response from the target as an instance + # of a subclass of HTTPResponse (such as HTTPOK). + # + # With no block given, returns the response; + # with a block given, calls the block with the response + # and returns the block's value. + # + # The target may be specified either as: + # + # - A URI: + # + # uri = URI('https://jsonplaceholder.typicode.com/todos/1') + # Net::HTTP.get_response(uri) + # # => # + # Net::HTTP.get_response(uri) {|response| p response.class } + # # Prints Net::HTTPOK + # + # With this form, you may specify a hash of headers: + # + # headers = {Accept: 'text/json'} + # Net::HTTP.get_response(uri, headers) + # # => # # - # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) - # print res.body + # - Host and path: # - # or: + # host = 'jsonplaceholder.typicode.com' + # path = '/todos/1' + # Net::HTTP.get_response(host, path) + # # => # + # Net::HTTP.get_response(host, path) {|response| p response.class } + # # Prints Net::HTTPOK # - # res = Net::HTTP.get_response('www.example.com', '/index.html') - # print res.body + # With this form, you may specify an integer port: # - # you can also specify request headers: + # Net::HTTP.get_response(host, path, 80) + # # => # # - # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # Related: HTTP.get_print, HTTP.get. # def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block) if path_or_headers && !path_or_headers.is_a?(Hash) From 0369825e74516974964c98934e9ba1f5d8af7217 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Mon, 23 May 2022 14:25:38 -0500 Subject: [PATCH 2/2] Enhanced RDoc for HTTPHeader --- lib/net/http/header.rb | 255 +++++++++++++++++++++++++++++++++-------- 1 file changed, 205 insertions(+), 50 deletions(-) diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb index a8901e79..682d1aa1 100644 --- a/lib/net/http/header.rb +++ b/lib/net/http/header.rb @@ -1,15 +1,59 @@ # frozen_string_literal: false -# The HTTPHeader module defines methods for reading and writing -# HTTP headers. # -# It is used as a mixin by other classes, to provide hash-like -# access to HTTP header values. Unlike raw hash access, HTTPHeader -# provides access via case-insensitive keys. It also provides -# methods for accessing commonly-used HTTP header values in more -# convenient formats. +# \Module \Net::HTTPHeader provides methods +# for managing \HTTP headers. +# It is included in classes Net::HTTPRequest and NET::HTTPResponse, +# providing: +# +# - Hash-like access to header fields. +# (Note that keys are case-insensitive.) +# - Convenience methods. +# +# Each stored field is a name/value pair, where: +# +# - The stored name is a string or symbol that has been +# {downcased}[https://docs.ruby-lang.org/en/master/String.html#method-i-downcase]. +# - Each stored value is a string that may have been +# {stripped}[https://docs.ruby-lang.org/en/master/String.html#method-i-strip] +# (depending on the method that set the value). +# +# Example: +# +# req = Net::HTTP::Get.new('github.com') +# fields = {' Foo ' => ' Bar '} +# req.initialize_http_header(fields) +# req.to_hash # => {" foo "=>["Bar"]} +# # module Net::HTTPHeader + # Initializes fields in +self+, after removing any existing fields; + # returns the argument: + # + # - Each name +name+ must be a string or a symbol; + # stored as name.downcase. + # - Each value +value+ must be a string, and may not include newlines; + # stored as value.strip. + # + # Argument +initheader+ may be either: + # + # - A hash of name/value pairs: + # + # req = Net::HTTP::Get.new('github.com') + # fields = {' Foo ' => ' Bar ', ' Baz ' => ' Bat '} + # req.initialize_http_header(fields) + # req.to_hash # => {" foo "=>["Bar"], " baz "=>["Bat"]} + # fields = {' Bat ' => ' Bah '} + # req.initialize_http_header(fields) + # req.to_hash # => {" bat "=>["Bah"]} + # + # - An array of 2-element arrays, each element being a name/value pair: + # + # req = Net::HTTP::Get.new('github.com') + # fields = [[' Foo ', ' Bar '], [' Baz ', ' Bat ']] + # req.initialize_http_header(fields) + # req.to_hash # => {" foo "=>["Bar"], " baz "=>["Bat"]} + # def initialize_http_header(initheader) @header = {} return unless initheader @@ -33,14 +77,60 @@ def size #:nodoc: obsolete alias length size #:nodoc: obsolete - # Returns the header field corresponding to the case-insensitive key. - # For example, a key of "Content-Type" might return "text/html" + # Returns a string containing the comma-separated values + # of the field named key.downcase + # if the field exists, or +nil+ otherwise: + # + # req = Net::HTTP::Get.new('github.com', ' Foo ' => ' Bar ') + # req[' foo '] # => "Bar" + # req[' Foo '] # => "Bar" + # req.add_field(' Foo ', [' Baz ', ' Bat ']) + # req[' Foo '] # => "Bar, Baz , Bat " + # req['Foo'] # => nil + # def [](key) a = @header[key.downcase.to_s] or return nil a.join(', ') end - # Sets the header field corresponding to the case-insensitive key. + # Sets the given string values (not stripped) + # for the field named key.downcase, + # overwriting the old value if the field exists + # or creating the field if necessary; returns +val+. + # + # When +val+ is a string, + # sets the field value to val.strip: + # + # req = Net::HTTP::Get.new('github.com') + # req[' Foo '] = ' Bar ' + # req[' foo '] # => "Bar" + # req[' foo '] = 'Baz' + # req[' foo '] # => "Baz" + # + # When +val+ is +nil+, removes the field if it exists: + # + # req.key?(' foo ') # => true + # req[' foo '] = nil + # req.key?(' foo ') # => false + # + # When +val+ is an + # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes], + # adds each element of +val+: + # + # # Array. + # req = Net::HTTP::Get.new('github.com') + # req[' Foo '] = [' Bar ', ' Baz '] + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # req[' Foo '] = [' Bat ', ' Bag '] + # req.get_fields(' foo ') # => [" Bat ", " Bag "] + # + # # Hash. + # req = Net::HTTP::Get.new('github.com') + # req[' Foo '] = {' Bar ' => ' Baz '} + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # req[' Foo '] = {' Bat ' => ' Bag '} + # req.get_fields(' foo ') # => [" Bat ", " Bag "] + # def []=(key, val) unless val @header.delete key.downcase.to_s @@ -49,20 +139,36 @@ def []=(key, val) set_field(key, val) end - # [Ruby 1.8.3] - # Adds a value to a named header field, instead of replacing its value. - # Second argument +val+ must be a String. - # See also #[]=, #[] and #get_fields. + # Adds the given string values (not stripped) to the existing values + # for the field named key.downcase; returns the argument. + # + # When +val+ is a string, adds the string: + # + # req = Net::HTTP::Get.new('github.com') + # req.add_field(' Foo ' , ' Bar ') + # req.get_fields(' foo ') # => [" Bar "] + # req.add_field(' Foo ', ' Baz ') + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # + # When +val+ is an + # {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes], + # adds each element: + # + # # Array. + # req = Net::HTTP::Get.new('github.com') + # req.add_field(' Foo ', [' Bar ', ' Baz ']) + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # req.add_field(' Foo ', [' Bat ', ' Bag ']) + # req.get_fields(' foo ') # => [" Bar ", " Baz ", " Bat ", " Bag "] + # + # # Hash. + # req = Net::HTTP::Get.new('github.com') + # req.add_field(' Foo ', {' Bar ' => ' Baz '}) + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # req.add_field(' Foo ', {' Bat ' => ' Bag '}) + # req.get_fields(' foo ') # => [" Bar ", " Baz ", " Bat ", " Bag "] # - # request.add_field 'X-My-Header', 'a' - # p request['X-My-Header'] #=> "a" - # p request.get_fields('X-My-Header') #=> ["a"] - # request.add_field 'X-My-Header', 'b' - # p request['X-My-Header'] #=> "a, b" - # p request.get_fields('X-My-Header') #=> ["a", "b"] - # request.add_field 'X-My-Header', 'c' - # p request['X-My-Header'] #=> "a, b, c" - # p request.get_fields('X-My-Header') #=> ["a", "b", "c"] + # Related: #get_fields. # def add_field(key, val) stringified_downcased_key = key.downcase.to_s @@ -101,16 +207,17 @@ def add_field(key, val) end end - # [Ruby 1.8.3] - # Returns an array of header field strings corresponding to the - # case-insensitive +key+. This method allows you to get duplicated - # header fields without any processing. See also #[]. + + # Returns an array of the values + # for the field named key.downcase, + # or +nil+ if there is no such field: + # + # req = Net::HTTP::Get.new('github.com') + # req.add_field(' Foo ' , [' Bar ', ' Baz ']) + # req.get_fields(' foo ') # => [" Bar ", " Baz "] + # req.get_fields('foo') # => nil # - # p response.get_fields('Set-Cookie') - # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23", - # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"] - # p response['Set-Cookie'] - # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23" + # Related: #add_fields. # def get_fields(key) stringified_downcased_key = key.downcase.to_s @@ -118,23 +225,44 @@ def get_fields(key) @header[stringified_downcased_key].dup end - # Returns the header field corresponding to the case-insensitive key. - # Returns the default value +args+, or the result of the block, or - # raises an IndexError if there's no header field named +key+ - # See Hash#fetch + # With no +args+ and no block given, behaves like #[], + # but raises an exception if the field does not exist: + # + # req = Net::HTTP::Get.new('github.com', ' Foo ' => ' Bar ') + # req.fetch(' foo ') # => "Bar" + # req.add_field(' Foo ', [' Baz ', ' Bat ']) + # req.fetch(' foo ') # => "Bar, Baz , Bat " + # req.fetch('foo') # Raises KeyError. + # + # With +args+ given and no block given, behaves like #[], + # but returns +args+ if the field does not exist: + # + # req.fetch('foo', '') # => "" + # + # With a block given and no +args+ given, behaves like #[], + # but returns the called block's value if the field does not exist: + # + # req.fetch('foo') { '' } # => "" + # def fetch(key, *args, &block) #:yield: +key+ a = @header.fetch(key.downcase.to_s, *args, &block) a.kind_of?(Array) ? a.join(', ') : a end - # Iterates through the header names and values, passing in the name - # and value to the code block supplied. + # Calls the given block with each field's name/value pair: # - # Returns an enumerator if no block is given. + # req = Net::HTTP::Get.new('github.com') + # req.initialize_http_header('Foo' => 'Bar', 'Baz' => 'Bat') + # req.each_header {|name, value| p "#{name}: #{value}" } # - # Example: + # Output: # - # response.header.each_header {|key,value| puts "#{key} = #{value}" } + # "foo: Bar" + # "baz: Bat" + # + # Returns an enumerator if no block is given. + # + # #each is an alias for #each_header. # def each_header #:yield: +key+, +value+ block_given? or return enum_for(__method__) { @header.size } @@ -145,10 +273,21 @@ def each_header #:yield: +key+, +value+ alias each each_header - # Iterates through the header names in the header, passing - # each header name to the code block. + # Calls the given block with each field's name: + # + # req = Net::HTTP::Get.new('github.com') + # req.initialize_http_header('Foo' => 'Bar', 'Baz' => 'Bat') + # req.each_header {|name| p name } + # + # Output: + # + # "foo" + # "baz" # # Returns an enumerator if no block is given. + # + # #each_key is an alias for #each_name. + # def each_name(&block) #:yield: +key+ block_given? or return enum_for(__method__) { @header.size } @header.each_key(&block) @@ -156,14 +295,21 @@ def each_name(&block) #:yield: +key+ alias each_key each_name - # Iterates through the header names in the header, passing - # capitalized header names to the code block. + # Calls the given block with each field's capitalized name; + # note that capitalization is system-dependent, + # and so may differ between server and client: # - # Note that header names are capitalized systematically; - # capitalization may not match that used by the remote HTTP - # server in its response. + # req = Net::HTTP::Get.new('github.com') + # req.initialize_http_header('FOO' => 'Bar', 'BAZ' => 'Bat') + # req.each_capitalized_name {|name| p name } + # + # Output: + # + # "Foo" + # "Baz" # # Returns an enumerator if no block is given. + # def each_capitalized_name #:yield: +key+ block_given? or return enum_for(__method__) { @header.size } @header.each_key do |k| @@ -171,10 +317,19 @@ def each_capitalized_name #:yield: +key+ end end - # Iterates through header values, passing each value to the - # code block. + # Calls the given block with each field's value: + # + # req = Net::HTTP::Get.new('github.com') + # req.initialize_http_header('Foo' => 'Bar', 'Baz' => 'Bat') + # req.each_value {|value| p value } + # + # Output: + # + # "Bar" + # "Bat" # # Returns an enumerator if no block is given. + # def each_value #:yield: +value+ block_given? or return enum_for(__method__) { @header.size } @header.each_value do |va|