net-http 0.1.1 → 0.3.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.
data/lib/net/http.rb CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'net/protocol'
24
24
  require 'uri'
25
+ require 'resolv'
25
26
  autoload :OpenSSL, 'openssl'
26
27
 
27
28
  module Net #:nodoc:
@@ -31,110 +32,237 @@ module Net #:nodoc:
31
32
  class HTTPHeaderSyntaxError < StandardError; end
32
33
  # :startdoc:
33
34
 
34
- # == An HTTP client API for Ruby.
35
+ # \Class \Net::HTTP provides a rich library that implements the client
36
+ # in a client-server model that uses the \HTTP request-response protocol.
37
+ # For information about \HTTP, see
35
38
  #
36
- # Net::HTTP provides a rich library which can be used to build HTTP
37
- # user-agents. For more details about HTTP see
38
- # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt).
39
+ # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol].
40
+ # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview].
39
41
  #
40
- # Net::HTTP is designed to work closely with URI. URI::HTTP#host,
41
- # URI::HTTP#port and URI::HTTP#request_uri are designed to work with
42
- # Net::HTTP.
42
+ # Note: If you are performing only a few GET requests, consider using
43
+ # {OpenURI}[https://docs.ruby-lang.org/en/master/OpenURI.html];
44
+ # otherwise, read on.
43
45
  #
44
- # If you are only performing a few GET requests you should try OpenURI.
46
+ # == Synopsis
45
47
  #
46
- # == Simple Examples
48
+ # If you are already familiar with \HTTP, this synopsis may be helpful.
47
49
  #
48
- # All examples assume you have loaded Net::HTTP with:
50
+ # {Session}[rdoc-ref:Net::HTTP@Sessions] with multiple requests for
51
+ # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
49
52
  #
50
- # require 'net/http'
53
+ # Net::HTTP.start(hostname) do |http|
54
+ # # Session started automatically before block execution.
55
+ # http.get(path_or_uri, headers = {})
56
+ # http.head(path_or_uri, headers = {})
57
+ # http.post(path_or_uri, data, headers = {}) # Can also have a block.
58
+ # http.put(path_or_uri, data, headers = {})
59
+ # http.delete(path_or_uri, headers = {Depth: 'Infinity'})
60
+ # http.options(path_or_uri, headers = {})
61
+ # http.trace(path_or_uri, headers = {})
62
+ # http.patch(path_or_uri, data, headers = {}) # Can also have a block.
63
+ # # Session finished automatically at block exit.
64
+ # end
51
65
  #
52
- # This will also require 'uri' so you don't need to require it separately.
66
+ # {Session}[rdoc-ref:Net::HTTP@Sessions] with multiple requests for
67
+ # {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
68
+ #
69
+ # Net::HTTP.start(hostname) do |http|
70
+ # # Session started automatically before block execution.
71
+ # http.copy(path_or_uri, headers = {})
72
+ # http.lock(path_or_uri, body, headers = {})
73
+ # http.mkcol(path_or_uri, body = nil, headers = {})
74
+ # http.move(path_or_uri, headers = {})
75
+ # http.propfind(path_or_uri, body = nil, headers = {'Depth' => '0'})
76
+ # http.proppatch(path_or_uri, body, headers = {})
77
+ # http.unlock(path_or_uri, body, headers = {})
78
+ # # Session finished automatically at block exit.
79
+ # end
53
80
  #
54
- # The Net::HTTP methods in the following section do not persist
55
- # connections. They are not recommended if you are performing many HTTP
56
- # requests.
81
+ # Each of the following methods automatically starts and finishes
82
+ # a {session}[rdoc-ref:Net::HTTP@Sessions] that sends a single request:
57
83
  #
58
- # === GET
84
+ # # Return string response body.
85
+ # Net::HTTP.get(hostname, path, port = 80)
86
+ # Net::HTTP.get(uri, headers = {}, port = 80)
59
87
  #
60
- # Net::HTTP.get('example.com', '/index.html') # => String
88
+ # # Write string response body to $stdout.
89
+ # Net::HTTP.get_print(hostname, path_or_uri, port = 80)
90
+ # Net::HTTP.get_print(uri, headers = {}, port = 80)
61
91
  #
62
- # === GET by URI
92
+ # # Return response as Net::HTTPResponse object.
93
+ # Net::HTTP.get_response(hostname, path_or_uri, port = 80)
94
+ # Net::HTTP.get_response(uri, headers = {}, port = 80)
63
95
  #
64
- # uri = URI('http://example.com/index.html?count=10')
65
- # Net::HTTP.get(uri) # => String
96
+ # Net::HTTP.post(uri, data, headers = {})
97
+ # Net::HTTP.post_form(uri, params)
66
98
  #
67
- # === GET with Dynamic Parameters
99
+ # == About the Examples
68
100
  #
69
- # uri = URI('http://example.com/index.html')
70
- # params = { :limit => 10, :page => 3 }
71
- # uri.query = URI.encode_www_form(params)
101
+ # :include: doc/net-http/examples.rdoc
72
102
  #
73
- # res = Net::HTTP.get_response(uri)
74
- # puts res.body if res.is_a?(Net::HTTPSuccess)
103
+ # == URIs
75
104
  #
76
- # === POST
105
+ # On the internet, a URI
106
+ # ({Universal Resource Identifier}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier])
107
+ # is a string that identifies a particular resource.
108
+ # It consists of some or all of: scheme, hostname, path, query, and fragment;
109
+ # see {URI syntax}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax].
77
110
  #
78
- # uri = URI('http://www.example.com/search.cgi')
79
- # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
80
- # puts res.body
111
+ # A Ruby {URI::Generic}[https://docs.ruby-lang.org/en/master/URI/Generic.html] object
112
+ # represents an internet URI.
113
+ # It provides, among others, methods
114
+ # +scheme+, +hostname+, +path+, +query+, and +fragment+.
81
115
  #
82
- # === POST with Multiple Values
116
+ # === Schemes
83
117
  #
84
- # uri = URI('http://www.example.com/search.cgi')
85
- # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
86
- # puts res.body
118
+ # An internet \URI has
119
+ # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes].
87
120
  #
88
- # == How to use Net::HTTP
121
+ # The two schemes supported in \Net::HTTP are <tt>'https'</tt> and <tt>'http'</tt>:
89
122
  #
90
- # The following example code can be used as the basis of an HTTP user-agent
91
- # which can perform a variety of request types using persistent
92
- # connections.
123
+ # uri.scheme # => "https"
124
+ # URI('http://example.com').scheme # => "http"
93
125
  #
94
- # uri = URI('http://example.com/some_path?query=string')
126
+ # === Hostnames
95
127
  #
96
- # Net::HTTP.start(uri.host, uri.port) do |http|
97
- # request = Net::HTTP::Get.new uri
128
+ # A hostname identifies a server (host) to which requests may be sent:
98
129
  #
99
- # response = http.request request # Net::HTTPResponse object
130
+ # hostname = uri.hostname # => "jsonplaceholder.typicode.com"
131
+ # Net::HTTP.start(hostname) do |http|
132
+ # # Some HTTP stuff.
100
133
  # end
101
134
  #
102
- # Net::HTTP::start immediately creates a connection to an HTTP server which
103
- # is kept open for the duration of the block. The connection will remain
104
- # open for multiple requests in the block if the server indicates it
105
- # supports persistent connections.
135
+ # === Paths
136
+ #
137
+ # A host-specific path identifies a resource on the host:
138
+ #
139
+ # _uri = uri.dup
140
+ # _uri.path = '/todos/1'
141
+ # hostname = _uri.hostname
142
+ # path = _uri.path
143
+ # Net::HTTP.get(hostname, path)
144
+ #
145
+ # === Queries
146
+ #
147
+ # A host-specific query adds name/value pairs to the URI:
148
+ #
149
+ # _uri = uri.dup
150
+ # params = {userId: 1, completed: false}
151
+ # _uri.query = URI.encode_www_form(params)
152
+ # _uri # => #<URI::HTTPS https://jsonplaceholder.typicode.com?userId=1&completed=false>
153
+ # Net::HTTP.get(_uri)
154
+ #
155
+ # === Fragments
156
+ #
157
+ # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect
158
+ # in \Net::HTTP;
159
+ # the same data is returned, regardless of whether a fragment is included.
160
+ #
161
+ # == Request Headers
162
+ #
163
+ # Request headers may be used to pass additional information to the host,
164
+ # similar to arguments passed in a method call;
165
+ # each header is a name/value pair.
166
+ #
167
+ # Each of the \Net::HTTP methods that sends a request to the host
168
+ # has optional argument +headers+,
169
+ # where the headers are expressed as a hash of field-name/value pairs:
170
+ #
171
+ # headers = {Accept: 'application/json', Connection: 'Keep-Alive'}
172
+ # Net::HTTP.get(uri, headers)
173
+ #
174
+ # See lists of both standard request fields and common request fields at
175
+ # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
176
+ # A host may also accept other custom fields.
177
+ #
178
+ # == Sessions
179
+ #
180
+ # A _session_ is a connection between a server (host) and a client that:
181
+ #
182
+ # - Is begun by instance method Net::HTTP#start.
183
+ # - May contain any number of requests.
184
+ # - Is ended by instance method Net::HTTP#finish.
106
185
  #
107
- # If you wish to re-use a connection across multiple HTTP requests without
108
- # automatically closing it you can use ::new and then call #start and
109
- # #finish manually.
186
+ # See example sessions at the {Synopsis}[rdoc-ref:Net::HTTP@Synopsis].
110
187
  #
111
- # The request types Net::HTTP supports are listed below in the section "HTTP
112
- # Request Classes".
188
+ # === Session Using \Net::HTTP.start
113
189
  #
114
- # For all the Net::HTTP request objects and shortcut request methods you may
115
- # supply either a String for the request path or a URI from which Net::HTTP
116
- # will extract the request path.
190
+ # If you have many requests to make to a single host (and port),
191
+ # consider using singleton method Net::HTTP.start with a block;
192
+ # the method handles the session automatically by:
117
193
  #
118
- # === Response Data
194
+ # - Calling #start before block execution.
195
+ # - Executing the block.
196
+ # - Calling #finish after block execution.
119
197
  #
120
- # uri = URI('http://example.com/index.html')
121
- # res = Net::HTTP.get_response(uri)
198
+ # In the block, you can use these instance methods,
199
+ # each of which that sends a single request:
122
200
  #
123
- # # Headers
124
- # res['Set-Cookie'] # => String
125
- # res.get_fields('set-cookie') # => Array
126
- # res.to_hash['set-cookie'] # => Array
127
- # puts "Headers: #{res.to_hash.inspect}"
201
+ # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
128
202
  #
129
- # # Status
130
- # puts res.code # => '200'
131
- # puts res.message # => 'OK'
132
- # puts res.class.name # => 'HTTPOK'
203
+ # - #get, #request_get: GET.
204
+ # - #head, #request_head: HEAD.
205
+ # - #post, #request_post: POST.
206
+ # - #delete: DELETE.
207
+ # - #options: OPTIONS.
208
+ # - #trace: TRACE.
209
+ # - #patch: PATCH.
133
210
  #
134
- # # Body
135
- # puts res.body if res.response_body_permitted?
211
+ # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
136
212
  #
137
- # === Following Redirection
213
+ # - #copy: COPY.
214
+ # - #lock: LOCK.
215
+ # - #mkcol: MKCOL.
216
+ # - #move: MOVE.
217
+ # - #propfind: PROPFIND.
218
+ # - #proppatch: PROPPATCH.
219
+ # - #unlock: UNLOCK.
220
+ #
221
+ # === Session Using \Net::HTTP.start and \Net::HTTP.finish
222
+ #
223
+ # You can manage a session manually using methods #start and #finish:
224
+ #
225
+ # http = Net::HTTP.new(hostname)
226
+ # http.start
227
+ # http.get('/todos/1')
228
+ # http.get('/todos/2')
229
+ # http.delete('/posts/1')
230
+ # http.finish # Needed to free resources.
231
+ #
232
+ # === Single-Request Session
233
+ #
234
+ # Certain convenience methods automatically handle a session by:
235
+ #
236
+ # - Creating an \HTTP object
237
+ # - Starting a session.
238
+ # - Sending a single request.
239
+ # - Finishing the session.
240
+ # - Destroying the object.
241
+ #
242
+ # Such methods that send GET requests:
243
+ #
244
+ # - ::get: Returns the string response body.
245
+ # - ::get_print: Writes the string response body to $stdout.
246
+ # - ::get_response: Returns a Net::HTTPResponse object.
247
+ #
248
+ # Such methods that send POST requests:
249
+ #
250
+ # - ::post: Posts data to the host.
251
+ # - ::post_form: Posts form data to the host.
252
+ #
253
+ # == \HTTP Requests and Responses
254
+ #
255
+ # Many of the methods above are convenience methods,
256
+ # each of which sends a request and returns a string
257
+ # without directly using \Net::HTTPRequest and \Net::HTTPResponse objects.
258
+ #
259
+ # You can, however, directly create a request object, send the request,
260
+ # and retrieve the response object; see:
261
+ #
262
+ # - Net::HTTPRequest.
263
+ # - Net::HTTPResponse.
264
+ #
265
+ # == Following Redirection
138
266
  #
139
267
  # Each Net::HTTPResponse object belongs to a class for its response code.
140
268
  #
@@ -166,56 +294,7 @@ module Net #:nodoc:
166
294
  #
167
295
  # print fetch('http://www.ruby-lang.org')
168
296
  #
169
- # === POST
170
- #
171
- # A POST can be made using the Net::HTTP::Post request class. This example
172
- # creates a URL encoded POST body:
173
- #
174
- # uri = URI('http://www.example.com/todo.cgi')
175
- # req = Net::HTTP::Post.new(uri)
176
- # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31')
177
- #
178
- # res = Net::HTTP.start(uri.hostname, uri.port) do |http|
179
- # http.request(req)
180
- # end
181
- #
182
- # case res
183
- # when Net::HTTPSuccess, Net::HTTPRedirection
184
- # # OK
185
- # else
186
- # res.value
187
- # end
188
- #
189
- # To send multipart/form-data use Net::HTTPHeader#set_form:
190
- #
191
- # req = Net::HTTP::Post.new(uri)
192
- # req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data')
193
- #
194
- # Other requests that can contain a body such as PUT can be created in the
195
- # same way using the corresponding request class (Net::HTTP::Put).
196
- #
197
- # === Setting Headers
198
- #
199
- # The following example performs a conditional GET using the
200
- # If-Modified-Since header. If the files has not been modified since the
201
- # time in the header a Not Modified response will be returned. See RFC 2616
202
- # section 9.3 for further details.
203
- #
204
- # uri = URI('http://example.com/cached_response')
205
- # file = File.stat 'cached_response'
206
- #
207
- # req = Net::HTTP::Get.new(uri)
208
- # req['If-Modified-Since'] = file.mtime.rfc2822
209
- #
210
- # res = Net::HTTP.start(uri.hostname, uri.port) {|http|
211
- # http.request(req)
212
- # }
213
- #
214
- # open 'cached_response', 'w' do |io|
215
- # io.write res.body
216
- # end if res.is_a?(Net::HTTPSuccess)
217
- #
218
- # === Basic Authentication
297
+ # == Basic Authentication
219
298
  #
220
299
  # Basic authentication is performed according to
221
300
  # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
@@ -230,7 +309,7 @@ module Net #:nodoc:
230
309
  # }
231
310
  # puts res.body
232
311
  #
233
- # === Streaming Response Bodies
312
+ # == Streaming Response Bodies
234
313
  #
235
314
  # By default Net::HTTP reads an entire response into memory. If you are
236
315
  # handling large files or wish to implement a progress bar you can instead
@@ -250,7 +329,7 @@ module Net #:nodoc:
250
329
  # end
251
330
  # end
252
331
  #
253
- # === HTTPS
332
+ # == HTTPS
254
333
  #
255
334
  # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=.
256
335
  #
@@ -271,7 +350,7 @@ module Net #:nodoc:
271
350
  # In previous versions of Ruby you would need to require 'net/https' to use
272
351
  # HTTPS. This is no longer true.
273
352
  #
274
- # === Proxies
353
+ # == Proxies
275
354
  #
276
355
  # Net::HTTP will automatically create a proxy from the +http_proxy+
277
356
  # environment variable if it is present. To disable use of +http_proxy+,
@@ -289,7 +368,7 @@ module Net #:nodoc:
289
368
  # See Net::HTTP.new for further details and examples such as proxies that
290
369
  # require a username and password.
291
370
  #
292
- # === Compression
371
+ # == Compression
293
372
  #
294
373
  # Net::HTTP automatically adds Accept-Encoding for compression of response
295
374
  # bodies and automatically decompresses gzip and deflate responses unless a
@@ -297,121 +376,31 @@ module Net #:nodoc:
297
376
  #
298
377
  # Compression can be disabled through the Accept-Encoding: identity header.
299
378
  #
300
- # == HTTP Request Classes
301
- #
302
- # Here is the HTTP request class hierarchy.
303
- #
304
- # * Net::HTTPRequest
305
- # * Net::HTTP::Get
306
- # * Net::HTTP::Head
307
- # * Net::HTTP::Post
308
- # * Net::HTTP::Patch
309
- # * Net::HTTP::Put
310
- # * Net::HTTP::Proppatch
311
- # * Net::HTTP::Lock
312
- # * Net::HTTP::Unlock
313
- # * Net::HTTP::Options
314
- # * Net::HTTP::Propfind
315
- # * Net::HTTP::Delete
316
- # * Net::HTTP::Move
317
- # * Net::HTTP::Copy
318
- # * Net::HTTP::Mkcol
319
- # * Net::HTTP::Trace
320
- #
321
- # == HTTP Response Classes
322
- #
323
- # Here is HTTP response class hierarchy. All classes are defined in Net
324
- # module and are subclasses of Net::HTTPResponse.
325
- #
326
- # HTTPUnknownResponse:: For unhandled HTTP extensions
327
- # HTTPInformation:: 1xx
328
- # HTTPContinue:: 100
329
- # HTTPSwitchProtocol:: 101
330
- # HTTPSuccess:: 2xx
331
- # HTTPOK:: 200
332
- # HTTPCreated:: 201
333
- # HTTPAccepted:: 202
334
- # HTTPNonAuthoritativeInformation:: 203
335
- # HTTPNoContent:: 204
336
- # HTTPResetContent:: 205
337
- # HTTPPartialContent:: 206
338
- # HTTPMultiStatus:: 207
339
- # HTTPIMUsed:: 226
340
- # HTTPRedirection:: 3xx
341
- # HTTPMultipleChoices:: 300
342
- # HTTPMovedPermanently:: 301
343
- # HTTPFound:: 302
344
- # HTTPSeeOther:: 303
345
- # HTTPNotModified:: 304
346
- # HTTPUseProxy:: 305
347
- # HTTPTemporaryRedirect:: 307
348
- # HTTPClientError:: 4xx
349
- # HTTPBadRequest:: 400
350
- # HTTPUnauthorized:: 401
351
- # HTTPPaymentRequired:: 402
352
- # HTTPForbidden:: 403
353
- # HTTPNotFound:: 404
354
- # HTTPMethodNotAllowed:: 405
355
- # HTTPNotAcceptable:: 406
356
- # HTTPProxyAuthenticationRequired:: 407
357
- # HTTPRequestTimeOut:: 408
358
- # HTTPConflict:: 409
359
- # HTTPGone:: 410
360
- # HTTPLengthRequired:: 411
361
- # HTTPPreconditionFailed:: 412
362
- # HTTPRequestEntityTooLarge:: 413
363
- # HTTPRequestURITooLong:: 414
364
- # HTTPUnsupportedMediaType:: 415
365
- # HTTPRequestedRangeNotSatisfiable:: 416
366
- # HTTPExpectationFailed:: 417
367
- # HTTPUnprocessableEntity:: 422
368
- # HTTPLocked:: 423
369
- # HTTPFailedDependency:: 424
370
- # HTTPUpgradeRequired:: 426
371
- # HTTPPreconditionRequired:: 428
372
- # HTTPTooManyRequests:: 429
373
- # HTTPRequestHeaderFieldsTooLarge:: 431
374
- # HTTPUnavailableForLegalReasons:: 451
375
- # HTTPServerError:: 5xx
376
- # HTTPInternalServerError:: 500
377
- # HTTPNotImplemented:: 501
378
- # HTTPBadGateway:: 502
379
- # HTTPServiceUnavailable:: 503
380
- # HTTPGatewayTimeOut:: 504
381
- # HTTPVersionNotSupported:: 505
382
- # HTTPInsufficientStorage:: 507
383
- # HTTPNetworkAuthenticationRequired:: 511
384
- #
385
- # There is also the Net::HTTPBadResponse exception which is raised when
386
- # there is a protocol error.
387
- #
388
379
  class HTTP < Protocol
389
380
 
390
381
  # :stopdoc:
391
- VERSION = "0.1.1"
382
+ VERSION = "0.3.2"
392
383
  Revision = %q$Revision$.split[1]
393
384
  HTTPVersion = '1.1'
394
385
  begin
395
386
  require 'zlib'
396
- require 'stringio' #for our purposes (unpacking gzip) lump these together
397
387
  HAVE_ZLIB=true
398
388
  rescue LoadError
399
389
  HAVE_ZLIB=false
400
390
  end
401
391
  # :startdoc:
402
392
 
403
- # Turns on net/http 1.2 (Ruby 1.8) features.
404
- # Defaults to ON in Ruby 1.8 or later.
393
+ # Returns +true+; retained for compatibility.
405
394
  def HTTP.version_1_2
406
395
  true
407
396
  end
408
397
 
409
- # Returns true if net/http is in version 1.2 mode.
410
- # Defaults to true.
398
+ # Returns +true+; retained for compatibility.
411
399
  def HTTP.version_1_2?
412
400
  true
413
401
  end
414
402
 
403
+ # Returns +false+; retained for compatibility.
415
404
  def HTTP.version_1_1? #:nodoc:
416
405
  false
417
406
  end
@@ -421,25 +410,12 @@ module Net #:nodoc:
421
410
  alias is_version_1_2? version_1_2? #:nodoc:
422
411
  end
423
412
 
413
+ # :call-seq:
414
+ # Net::HTTP.get_print(hostname, path, port = 80) -> nil
415
+ # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil
424
416
  #
425
- # short cut methods
426
- #
427
-
428
- #
429
- # Gets the body text from the target and outputs it to $stdout. The
430
- # target can either be specified as
431
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
432
- #
433
- # Net::HTTP.get_print URI('http://www.example.com/index.html')
434
- #
435
- # or:
436
- #
437
- # Net::HTTP.get_print 'www.example.com', '/index.html'
438
- #
439
- # you can also specify request headers:
440
- #
441
- # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }
442
- #
417
+ # Like Net::HTTP.get, but writes the returned body to $stdout;
418
+ # returns +nil+.
443
419
  def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
444
420
  get_response(uri_or_host, path_or_headers, port) {|res|
445
421
  res.read_body do |chunk|
@@ -449,40 +425,48 @@ module Net #:nodoc:
449
425
  nil
450
426
  end
451
427
 
452
- # Sends a GET request to the target and returns the HTTP response
453
- # as a string. The target can either be specified as
454
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
428
+ # :call-seq:
429
+ # Net::HTTP.get(hostname, path, port = 80) -> body
430
+ # Net::HTTP:get(uri, headers = {}, port = uri.port) -> body
455
431
  #
456
- # print Net::HTTP.get(URI('http://www.example.com/index.html'))
432
+ # Sends a GET request and returns the \HTTP response body as a string.
457
433
  #
458
- # or:
434
+ # With string arguments +hostname+ and +path+:
459
435
  #
460
- # print Net::HTTP.get('www.example.com', '/index.html')
436
+ # hostname = 'jsonplaceholder.typicode.com'
437
+ # path = '/todos/1'
438
+ # puts Net::HTTP.get(hostname, path)
461
439
  #
462
- # you can also specify request headers:
440
+ # Output:
463
441
  #
464
- # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
442
+ # {
443
+ # "userId": 1,
444
+ # "id": 1,
445
+ # "title": "delectus aut autem",
446
+ # "completed": false
447
+ # }
465
448
  #
466
- def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
467
- get_response(uri_or_host, path_or_headers, port).body
468
- end
469
-
470
- # Sends a GET request to the target and returns the HTTP response
471
- # as a Net::HTTPResponse object. The target can either be specified as
472
- # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
449
+ # With URI object +uri+ and optional hash argument +headers+:
473
450
  #
474
- # res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
475
- # print res.body
451
+ # uri = URI('https://jsonplaceholder.typicode.com/todos/1')
452
+ # headers = {'Content-type' => 'application/json; charset=UTF-8'}
453
+ # Net::HTTP.get(uri, headers)
476
454
  #
477
- # or:
455
+ # Related:
478
456
  #
479
- # res = Net::HTTP.get_response('www.example.com', '/index.html')
480
- # print res.body
457
+ # - Net::HTTP::Get: request class for \HTTP method +GET+.
458
+ # - Net::HTTP#get: convenience method for \HTTP method +GET+.
481
459
  #
482
- # you can also specify request headers:
483
- #
484
- # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
460
+ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
461
+ get_response(uri_or_host, path_or_headers, port).body
462
+ end
463
+
464
+ # :call-seq:
465
+ # Net::HTTP.get_response(hostname, path, port = 80) -> http_response
466
+ # Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response
485
467
  #
468
+ # Like Net::HTTP.get, but returns a Net::HTTPResponse object
469
+ # instead of the body string.
486
470
  def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
487
471
  if path_or_headers && !path_or_headers.is_a?(Hash)
488
472
  host = uri_or_host
@@ -500,16 +484,31 @@ module Net #:nodoc:
500
484
  end
501
485
  end
502
486
 
503
- # Posts data to the specified URI object.
487
+ # Posts data to a host; returns a Net::HTTPResponse object.
504
488
  #
505
- # Example:
489
+ # Argument +url+ must be a URL;
490
+ # argument +data+ must be a string:
491
+ #
492
+ # _uri = uri.dup
493
+ # _uri.path = '/posts'
494
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
495
+ # headers = {'content-type': 'application/json'}
496
+ # res = Net::HTTP.post(_uri, data, headers) # => #<Net::HTTPCreated 201 Created readbody=true>
497
+ # puts res.body
506
498
  #
507
- # require 'net/http'
508
- # require 'uri'
499
+ # Output:
509
500
  #
510
- # Net::HTTP.post URI('http://www.example.com/api/search'),
511
- # { "q" => "ruby", "max" => "50" }.to_json,
512
- # "Content-Type" => "application/json"
501
+ # {
502
+ # "title": "foo",
503
+ # "body": "bar",
504
+ # "userId": 1,
505
+ # "id": 101
506
+ # }
507
+ #
508
+ # Related:
509
+ #
510
+ # - Net::HTTP::Post: request class for \HTTP method +POST+.
511
+ # - Net::HTTP#post: convenience method for \HTTP method +POST+.
513
512
  #
514
513
  def HTTP.post(url, data, header = nil)
515
514
  start(url.hostname, url.port,
@@ -518,23 +517,25 @@ module Net #:nodoc:
518
517
  }
519
518
  end
520
519
 
521
- # Posts HTML form data to the specified URI object.
522
- # The form data must be provided as a Hash mapping from String to String.
523
- # Example:
520
+ # Posts data to a host; returns a Net::HTTPResponse object.
524
521
  #
525
- # { "cmd" => "search", "q" => "ruby", "max" => "50" }
522
+ # Argument +url+ must be a URI;
523
+ # argument +data+ must be a hash:
526
524
  #
527
- # This method also does Basic Authentication iff +url+.user exists.
528
- # But userinfo for authentication is deprecated (RFC3986).
529
- # So this feature will be removed.
525
+ # _uri = uri.dup
526
+ # _uri.path = '/posts'
527
+ # data = {title: 'foo', body: 'bar', userId: 1}
528
+ # res = Net::HTTP.post_form(_uri, data) # => #<Net::HTTPCreated 201 Created readbody=true>
529
+ # puts res.body
530
530
  #
531
- # Example:
532
- #
533
- # require 'net/http'
534
- # require 'uri'
531
+ # Output:
535
532
  #
536
- # Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
537
- # { "q" => "ruby", "max" => "50" }
533
+ # {
534
+ # "title": "foo",
535
+ # "body": "bar",
536
+ # "userId": "1",
537
+ # "id": 101
538
+ # }
538
539
  #
539
540
  def HTTP.post_form(url, params)
540
541
  req = Post.new(url)
@@ -550,17 +551,26 @@ module Net #:nodoc:
550
551
  # HTTP session management
551
552
  #
552
553
 
553
- # The default port to use for HTTP requests; defaults to 80.
554
+ # Returns intger +80+, the default port to use for HTTP requests:
555
+ #
556
+ # Net::HTTP.default_port # => 80
557
+ #
554
558
  def HTTP.default_port
555
559
  http_default_port()
556
560
  end
557
561
 
558
- # The default port to use for HTTP requests; defaults to 80.
562
+ # Returns integer +80+, the default port to use for HTTP requests:
563
+ #
564
+ # Net::HTTP.http_default_port # => 80
565
+ #
559
566
  def HTTP.http_default_port
560
567
  80
561
568
  end
562
569
 
563
- # The default port to use for HTTPS requests; defaults to 443.
570
+ # Returns integer +443+, the default port to use for HTTPS requests:
571
+ #
572
+ # Net::HTTP.https_default_port # => 443
573
+ #
564
574
  def HTTP.https_default_port
565
575
  443
566
576
  end
@@ -570,35 +580,91 @@ module Net #:nodoc:
570
580
  end
571
581
 
572
582
  # :call-seq:
573
- # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
574
- # HTTP.start(address, port=nil, p_addr=:ENV, p_port=nil, p_user=nil, p_pass=nil, opt, &block)
575
- #
576
- # Creates a new Net::HTTP object, then additionally opens the TCP
577
- # connection and HTTP session.
578
- #
579
- # Arguments are the following:
580
- # _address_ :: hostname or IP address of the server
581
- # _port_ :: port of the server
582
- # _p_addr_ :: address of proxy
583
- # _p_port_ :: port of proxy
584
- # _p_user_ :: user of proxy
585
- # _p_pass_ :: pass of proxy
586
- # _opt_ :: optional hash
587
- #
588
- # _opt_ sets following values by its accessor.
589
- # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers, keep_alive_timeout,
590
- # close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
591
- # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
592
- # If you set :use_ssl as true, you can use https and default value of
593
- # verify_mode is set as OpenSSL::SSL::VERIFY_PEER.
594
- #
595
- # If the optional block is given, the newly
596
- # created Net::HTTP object is passed to it and closed when the
597
- # block finishes. In this case, the return value of this method
598
- # is the return value of the block. If no block is given, the
599
- # return value of this method is the newly created Net::HTTP object
600
- # itself, and the caller is responsible for closing it upon completion
601
- # using the finish() method.
583
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) -> http
584
+ # HTTP.start(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, opts) {|http| ... } -> object
585
+ #
586
+ # Creates a new \Net::HTTP object, +http+, via \Net::HTTP.new:
587
+ #
588
+ # Net::HTTP.new(address, port, p_addr, p_port, p_user, p_pass)
589
+ #
590
+ # - For arguments +hostname+ through +p_pass+, see Net::HTTP.new.
591
+ # - For argument +opts+, see below.
592
+ #
593
+ # Note: If +port+ is +nil+ and <tt>opts[:use_ssl]</tt> is a truthy value,
594
+ # the value passed to +new+ is Net::HTTP.https_default_port, not +port+.
595
+ #
596
+ # With no block given:
597
+ #
598
+ # - Calls <tt>http.start</tt> with no block (see #start),
599
+ # which opens a TCP connection and \HTTP session.
600
+ # - Returns +http+.
601
+ # - The caller should call #finish to close the session:
602
+ #
603
+ # http = Net::HTTP.start(hostname)
604
+ # http.started? # => true
605
+ # http.finish
606
+ # http.started? # => false
607
+ #
608
+ # With a block given:
609
+ #
610
+ # - Calls <tt>http.start</tt> with the block (see #start), which:
611
+ #
612
+ # - Opens a TCP connection and \HTTP session.
613
+ # - Calls the block,
614
+ # which may make any number of requests to the host.
615
+ # - Closes the \HTTP session and TCP connection on block exit.
616
+ # - Returns the block's value +object+.
617
+ #
618
+ # - Returns +object+.
619
+ #
620
+ # Example:
621
+ #
622
+ # hostname = 'jsonplaceholder.typicode.com'
623
+ # Net::HTTP.start(hostname) do |http|
624
+ # puts http.get('/todos/1').body
625
+ # puts http.get('/todos/2').body
626
+ # end
627
+ #
628
+ # Output:
629
+ #
630
+ # {
631
+ # "userId": 1,
632
+ # "id": 1,
633
+ # "title": "delectus aut autem",
634
+ # "completed": false
635
+ # }
636
+ # {
637
+ # "userId": 1,
638
+ # "id": 2,
639
+ # "title": "quis ut nam facilis et officia qui",
640
+ # "completed": false
641
+ # }
642
+ #
643
+ # If the last argument given is a hash, it is the +opts+ hash,
644
+ # where each key is a method or accessor to be called,
645
+ # and its value is the value to be set.
646
+ #
647
+ # The keys may include:
648
+ #
649
+ # - #ca_file
650
+ # - #ca_path
651
+ # - #cert
652
+ # - #cert_store
653
+ # - #ciphers
654
+ # - #close_on_empty_response
655
+ # - +ipaddr+ (calls #ipaddr=)
656
+ # - #keep_alive_timeout
657
+ # - #key
658
+ # - #open_timeout
659
+ # - #read_timeout
660
+ # - #ssl_timeout
661
+ # - #ssl_version
662
+ # - +use_ssl+ (calls #use_ssl=)
663
+ # - #verify_callback
664
+ # - #verify_depth
665
+ # - #verify_mode
666
+ # - #write_timeout
667
+ #
602
668
  def HTTP.start(address, *arg, &block) # :yield: +http+
603
669
  arg.pop if opt = Hash.try_convert(arg[-1])
604
670
  port, p_addr, p_port, p_user, p_pass = *arg
@@ -625,25 +691,113 @@ module Net #:nodoc:
625
691
  alias newobj new # :nodoc:
626
692
  end
627
693
 
628
- # Creates a new Net::HTTP object without opening a TCP connection or
629
- # HTTP session.
694
+ # Returns a new Net::HTTP object +http+
695
+ # (but does not open a TCP connection or HTTP session).
696
+ #
697
+ # <b>No Proxy</b>
698
+ #
699
+ # With only string argument +hostname+ given
700
+ # (and <tt>ENV['http_proxy']</tt> undefined or +nil+),
701
+ # the returned +http+:
702
+ #
703
+ # - Has the given address.
704
+ # - Has the default port number, Net::HTTP.default_port (80).
705
+ # - Has no proxy.
706
+ #
707
+ # Example:
630
708
  #
631
- # The +address+ should be a DNS hostname or IP address, the +port+ is the
632
- # port the server operates on. If no +port+ is given the default port for
633
- # HTTP or HTTPS is used.
709
+ # http = Net::HTTP.new(hostname)
710
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
711
+ # http.address # => "jsonplaceholder.typicode.com"
712
+ # http.port # => 80
713
+ # http.proxy? # => false
714
+ #
715
+ # With integer argument +port+ also given,
716
+ # the returned +http+ has the given port:
717
+ #
718
+ # http = Net::HTTP.new(hostname, 8000)
719
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:8000 open=false>
720
+ # http.port # => 8000
721
+ #
722
+ # <b>Proxy Using Argument +p_addr+ as a \String</b>
723
+ #
724
+ # When argument +p_addr+ is a string hostname,
725
+ # the returned +http+ has a proxy:
726
+ #
727
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example')
728
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
729
+ # http.proxy? # => true
730
+ # http.proxy_address # => "proxy.example"
731
+ # # These use default values.
732
+ # http.proxy_port # => 80
733
+ # http.proxy_user # => nil
734
+ # http.proxy_pass # => nil
735
+ #
736
+ # The port, username, and password for the proxy may also be given:
737
+ #
738
+ # http = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
739
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
740
+ # http.proxy? # => true
741
+ # http.proxy_address # => "proxy.example"
742
+ # http.proxy_port # => 8000
743
+ # http.proxy_user # => "pname"
744
+ # http.proxy_pass # => "ppass"
745
+ #
746
+ # <b>Proxy Using <tt>ENV['http_proxy']</tt></b>
747
+ #
748
+ # When environment variable <tt>'http_proxy'</tt>
749
+ # is set to a \URI string,
750
+ # the returned +http+ will have that URI as its proxy;
751
+ # note that the \URI string must have a protocol
752
+ # such as <tt>'http'</tt> or <tt>'https'</tt>:
753
+ #
754
+ # ENV['http_proxy'] = 'http://example.com'
755
+ # # => "http://example.com"
756
+ # http = Net::HTTP.new(hostname)
757
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
758
+ # http.proxy? # => true
759
+ # http.address # => "jsonplaceholder.typicode.com"
760
+ # http.proxy_address # => "example.com"
761
+ #
762
+ # The \URI string may include proxy username, password, and port number:
763
+ #
764
+ # ENV['http_proxy'] = 'http://pname:ppass@example.com:8000'
765
+ # # => "http://pname:ppass@example.com:8000"
766
+ # http = Net::HTTP.new(hostname)
767
+ # # => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
768
+ # http.proxy_port # => 8000
769
+ # http.proxy_user # => "pname"
770
+ # http.proxy_pass # => "ppass"
771
+ #
772
+ # <b>Argument +p_no_proxy+</b>
773
+ #
774
+ # You can use argument +p_no_proxy+ to reject certain proxies:
775
+ #
776
+ # - Reject a certain address:
777
+ #
778
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
779
+ # http.proxy_address # => nil
780
+ #
781
+ # - Reject certain domains or subdomains:
782
+ #
783
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy.example', 8000, 'pname', 'ppass', 'proxy.example')
784
+ # http.proxy_address # => nil
785
+ #
786
+ # - Reject certain addresses and port combinations:
634
787
  #
635
- # If none of the +p_+ arguments are given, the proxy host and port are
636
- # taken from the +http_proxy+ environment variable (or its uppercase
637
- # equivalent) if present. If the proxy requires authentication you must
638
- # supply it by hand. See URI::Generic#find_proxy for details of proxy
639
- # detection from the environment. To disable proxy detection set +p_addr+
640
- # to nil.
788
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:1234')
789
+ # http.proxy_address # => "proxy.example"
641
790
  #
642
- # If you are connecting to a custom proxy, +p_addr+ specifies the DNS name
643
- # or IP address of the proxy host, +p_port+ the port to use to access the
644
- # proxy, +p_user+ and +p_pass+ the username and password if authorization
645
- # is required to use the proxy, and p_no_proxy hosts which do not
646
- # use the proxy.
791
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'proxy.example:8000')
792
+ # http.proxy_address # => nil
793
+ #
794
+ # - Reject a list of the types above delimited using a comma:
795
+ #
796
+ # http = Net::HTTP.new('example.com', nil, 'proxy.example', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
797
+ # http.proxy_address # => nil
798
+ #
799
+ # http = Net::HTTP.new('example.com', nil, 'my.proxy', 8000, 'pname', 'ppass', 'my.proxy,proxy.example:8000')
800
+ # http.proxy_address # => nil
647
801
  #
648
802
  def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
649
803
  http = super address, port
@@ -691,6 +845,8 @@ module Net #:nodoc:
691
845
  @continue_timeout = nil
692
846
  @max_retries = 1
693
847
  @debug_output = nil
848
+ @response_body_encoding = false
849
+ @ignore_eof = true
694
850
 
695
851
  @proxy_from_env = false
696
852
  @proxy_uri = nil
@@ -708,6 +864,11 @@ module Net #:nodoc:
708
864
  end
709
865
  end
710
866
 
867
+ # Returns a string representation of +self+:
868
+ #
869
+ # Net::HTTP.new(hostname).inspect
870
+ # # => "#<Net::HTTP jsonplaceholder.typicode.com:80 open=false>"
871
+ #
711
872
  def inspect
712
873
  "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
713
874
  end
@@ -715,11 +876,51 @@ module Net #:nodoc:
715
876
  # *WARNING* This method opens a serious security hole.
716
877
  # Never use this method in production code.
717
878
  #
718
- # Sets an output stream for debugging.
879
+ # Sets the output stream for debugging:
719
880
  #
720
881
  # http = Net::HTTP.new(hostname)
721
- # http.set_debug_output $stderr
722
- # http.start { .... }
882
+ # File.open('t.tmp', 'w') do |file|
883
+ # http.set_debug_output(file)
884
+ # http.start
885
+ # http.get('/nosuch/1')
886
+ # http.finish
887
+ # end
888
+ # puts File.read('t.tmp')
889
+ #
890
+ # Output:
891
+ #
892
+ # opening connection to jsonplaceholder.typicode.com:80...
893
+ # opened
894
+ # <- "GET /nosuch/1 HTTP/1.1\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nAccept: */*\r\nUser-Agent: Ruby\r\nHost: jsonplaceholder.typicode.com\r\n\r\n"
895
+ # -> "HTTP/1.1 404 Not Found\r\n"
896
+ # -> "Date: Mon, 12 Dec 2022 21:14:11 GMT\r\n"
897
+ # -> "Content-Type: application/json; charset=utf-8\r\n"
898
+ # -> "Content-Length: 2\r\n"
899
+ # -> "Connection: keep-alive\r\n"
900
+ # -> "X-Powered-By: Express\r\n"
901
+ # -> "X-Ratelimit-Limit: 1000\r\n"
902
+ # -> "X-Ratelimit-Remaining: 999\r\n"
903
+ # -> "X-Ratelimit-Reset: 1670879660\r\n"
904
+ # -> "Vary: Origin, Accept-Encoding\r\n"
905
+ # -> "Access-Control-Allow-Credentials: true\r\n"
906
+ # -> "Cache-Control: max-age=43200\r\n"
907
+ # -> "Pragma: no-cache\r\n"
908
+ # -> "Expires: -1\r\n"
909
+ # -> "X-Content-Type-Options: nosniff\r\n"
910
+ # -> "Etag: W/\"2-vyGp6PvFo4RvsFtPoIWeCReyIC8\"\r\n"
911
+ # -> "Via: 1.1 vegur\r\n"
912
+ # -> "CF-Cache-Status: MISS\r\n"
913
+ # -> "Server-Timing: cf-q-config;dur=1.3000000762986e-05\r\n"
914
+ # -> "Report-To: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=yOr40jo%2BwS1KHzhTlVpl54beJ5Wx2FcG4gGV0XVrh3X9OlR5q4drUn2dkt5DGO4GDcE%2BVXT7CNgJvGs%2BZleIyMu8CLieFiDIvOviOY3EhHg94m0ZNZgrEdpKD0S85S507l1vsEwEHkoTm%2Ff19SiO\"}],\"group\":\"cf-nel\",\"max_age\":604800}\r\n"
915
+ # -> "NEL: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\r\n"
916
+ # -> "Server: cloudflare\r\n"
917
+ # -> "CF-RAY: 778977dc484ce591-DFW\r\n"
918
+ # -> "alt-svc: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\r\n"
919
+ # -> "\r\n"
920
+ # reading 2 bytes...
921
+ # -> "{}"
922
+ # read 2 bytes
923
+ # Conn keep-alive
723
924
  #
724
925
  def set_debug_output(output)
725
926
  warn 'Net::HTTP#set_debug_output called after HTTP started', uplevel: 1 if started?
@@ -738,18 +939,71 @@ module Net #:nodoc:
738
939
  # The local port used to establish the connection.
739
940
  attr_accessor :local_port
740
941
 
942
+ # The encoding to use for the response body. If Encoding, uses the
943
+ # specified encoding. If other true value, tries to detect the response
944
+ # body encoding.
945
+ attr_reader :response_body_encoding
946
+
947
+ # Sets the encoding to be used for the response body;
948
+ # returns the encoding.
949
+ #
950
+ # The given +value+ may be:
951
+ #
952
+ # - An Encoding object.
953
+ # - The name of an encoding.
954
+ # - An alias for an encoding name.
955
+ #
956
+ # See {Encoding}[https://docs.ruby-lang.org/en/master/Encoding.html].
957
+ #
958
+ # Examples:
959
+ #
960
+ # http = Net::HTTP.new(hostname)
961
+ # http.response_body_encoding = Encoding::US_ASCII # => #<Encoding:US-ASCII>
962
+ # http.response_body_encoding = 'US-ASCII' # => "US-ASCII"
963
+ # http.response_body_encoding = 'ASCII' # => "ASCII"
964
+ #
965
+ def response_body_encoding=(value)
966
+ value = Encoding.find(value) if value.is_a?(String)
967
+ @response_body_encoding = value
968
+ end
969
+
741
970
  attr_writer :proxy_from_env
742
971
  attr_writer :proxy_address
743
972
  attr_writer :proxy_port
744
973
  attr_writer :proxy_user
745
974
  attr_writer :proxy_pass
746
975
 
747
- # The IP address to connect to/used to connect to
976
+ # Returns the IP address for the connection.
977
+ #
978
+ # If the session has not been started,
979
+ # returns the value set by #ipaddr=,
980
+ # or +nil+ if it has not been set:
981
+ #
982
+ # http = Net::HTTP.new(hostname)
983
+ # http.ipaddr # => nil
984
+ # http.ipaddr = '172.67.155.76'
985
+ # http.ipaddr # => "172.67.155.76"
986
+ #
987
+ # If the session has been started,
988
+ # returns the IP address from the socket:
989
+ #
990
+ # http = Net::HTTP.new(hostname)
991
+ # http.start
992
+ # http.ipaddr # => "172.67.155.76"
993
+ # http.finish
994
+ #
748
995
  def ipaddr
749
996
  started? ? @socket.io.peeraddr[3] : @ipaddr
750
997
  end
751
998
 
752
- # Set the IP address to connect to
999
+ # Sets the IP address for the connection:
1000
+ #
1001
+ # http = Net::HTTP.new(hostname)
1002
+ # http.ipaddr # => nil
1003
+ # http.ipaddr = '172.67.155.76'
1004
+ # http.ipaddr # => "172.67.155.76"
1005
+ #
1006
+ # The IP address may not be set if the session has been started.
753
1007
  def ipaddr=(addr)
754
1008
  raise IOError, "ipaddr value changed, but session already started" if started?
755
1009
  @ipaddr = addr
@@ -774,12 +1028,18 @@ module Net #:nodoc:
774
1028
  # Net::WriteTimeout is not raised on Windows.
775
1029
  attr_reader :write_timeout
776
1030
 
777
- # Maximum number of times to retry an idempotent request in case of
1031
+ # Sets the maximum number of times to retry an idempotent request in case of
778
1032
  # Net::ReadTimeout, IOError, EOFError, Errno::ECONNRESET,
779
1033
  # Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError,
780
1034
  # Timeout::Error.
781
- # Should be a non-negative integer number. Zero means no retries.
782
- # The default value is 1.
1035
+ # The initial value is 1.
1036
+ #
1037
+ # Argument +retries+ must be a non-negative numeric value:
1038
+ #
1039
+ # http = Net::HTTP.new(hostname)
1040
+ # http.max_retries = 2 # => 2
1041
+ # http.max_retries # => 2
1042
+ #
783
1043
  def max_retries=(retries)
784
1044
  retries = retries.to_int
785
1045
  if retries < 0
@@ -790,13 +1050,27 @@ module Net #:nodoc:
790
1050
 
791
1051
  attr_reader :max_retries
792
1052
 
793
- # Setter for the read_timeout attribute.
1053
+ # Sets the read timeout, in seconds, for +self+ to integer +sec+;
1054
+ # the initial value is 60.
1055
+ #
1056
+ # Argument +sec+ must be a non-negative numeric value:
1057
+ #
1058
+ # http = Net::HTTP.new(hostname)
1059
+ # http.read_timeout # => 60
1060
+ # http.get('/todos/1') # => #<Net::HTTPOK 200 OK readbody=true>
1061
+ # http.read_timeout = 0
1062
+ # http.get('/todos/1') # Raises Net::ReadTimeout.
1063
+ #
794
1064
  def read_timeout=(sec)
795
1065
  @socket.read_timeout = sec if @socket
796
1066
  @read_timeout = sec
797
1067
  end
798
1068
 
799
- # Setter for the write_timeout attribute.
1069
+ # Sets the write timeout, in seconds, for +self+ to integer +sec+;
1070
+ # the initial value is 60.
1071
+ #
1072
+ # Argument +sec+ must be a non-negative numeric value.
1073
+ #
800
1074
  def write_timeout=(sec)
801
1075
  @socket.write_timeout = sec if @socket
802
1076
  @write_timeout = sec
@@ -819,6 +1093,10 @@ module Net #:nodoc:
819
1093
  # The default value is 2 seconds.
820
1094
  attr_accessor :keep_alive_timeout
821
1095
 
1096
+ # Whether to ignore EOF when reading response bodies with defined
1097
+ # Content-Length headers. For backwards compatibility, the default is true.
1098
+ attr_accessor :ignore_eof
1099
+
822
1100
  # Returns true if the HTTP session has been started.
823
1101
  def started?
824
1102
  @started
@@ -973,6 +1251,12 @@ module Net #:nodoc:
973
1251
  private :do_start
974
1252
 
975
1253
  def connect
1254
+ if use_ssl?
1255
+ # reference early to load OpenSSL before connecting,
1256
+ # as OpenSSL may take time to load.
1257
+ @ssl_context = OpenSSL::SSL::SSLContext.new
1258
+ end
1259
+
976
1260
  if proxy? then
977
1261
  conn_addr = proxy_address
978
1262
  conn_port = proxy_port
@@ -981,7 +1265,7 @@ module Net #:nodoc:
981
1265
  conn_port = port
982
1266
  end
983
1267
 
984
- D "opening connection to #{conn_addr}:#{conn_port}..."
1268
+ debug "opening connection to #{conn_addr}:#{conn_port}..."
985
1269
  s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
986
1270
  begin
987
1271
  TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
@@ -991,7 +1275,7 @@ module Net #:nodoc:
991
1275
  end
992
1276
  }
993
1277
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
994
- D "opened"
1278
+ debug "opened"
995
1279
  if use_ssl?
996
1280
  if proxy?
997
1281
  plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
@@ -1020,35 +1304,56 @@ module Net #:nodoc:
1020
1304
  end
1021
1305
  end
1022
1306
  end
1023
- @ssl_context = OpenSSL::SSL::SSLContext.new
1024
1307
  @ssl_context.set_params(ssl_parameters)
1025
- @ssl_context.session_cache_mode =
1026
- OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
1027
- OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
1028
- @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
1029
- D "starting SSL for #{conn_addr}:#{conn_port}..."
1308
+ unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby
1309
+ @ssl_context.session_cache_mode =
1310
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
1311
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
1312
+ end
1313
+ if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby
1314
+ @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
1315
+ end
1316
+
1317
+ # Still do the post_connection_check below even if connecting
1318
+ # to IP address
1319
+ verify_hostname = @ssl_context.verify_hostname
1320
+
1321
+ # Server Name Indication (SNI) RFC 3546/6066
1322
+ case @address
1323
+ when Resolv::IPv4::Regex, Resolv::IPv6::Regex
1324
+ # don't set SNI, as IP addresses in SNI is not valid
1325
+ # per RFC 6066, section 3.
1326
+
1327
+ # Avoid openssl warning
1328
+ @ssl_context.verify_hostname = false
1329
+ else
1330
+ ssl_host_address = @address
1331
+ end
1332
+
1333
+ debug "starting SSL for #{conn_addr}:#{conn_port}..."
1030
1334
  s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
1031
1335
  s.sync_close = true
1032
- # Server Name Indication (SNI) RFC 3546
1033
- s.hostname = @address if s.respond_to? :hostname=
1336
+ s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address
1337
+
1034
1338
  if @ssl_session and
1035
1339
  Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
1036
1340
  s.session = @ssl_session
1037
1341
  end
1038
1342
  ssl_socket_connect(s, @open_timeout)
1039
- if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && @ssl_context.verify_hostname
1343
+ if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
1040
1344
  s.post_connection_check(@address)
1041
1345
  end
1042
- D "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
1346
+ debug "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
1043
1347
  end
1044
1348
  @socket = BufferedIO.new(s, read_timeout: @read_timeout,
1045
1349
  write_timeout: @write_timeout,
1046
1350
  continue_timeout: @continue_timeout,
1047
1351
  debug_output: @debug_output)
1352
+ @last_communicated = nil
1048
1353
  on_connect
1049
1354
  rescue => exception
1050
1355
  if s
1051
- D "Conn close because of connect error #{exception}"
1356
+ debug "Conn close because of connect error #{exception}"
1052
1357
  s.close
1053
1358
  end
1054
1359
  raise
@@ -1092,7 +1397,7 @@ module Net #:nodoc:
1092
1397
  #
1093
1398
  # This class is obsolete. You may pass these same parameters directly to
1094
1399
  # Net::HTTP.new. See Net::HTTP.new for details of the arguments.
1095
- def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
1400
+ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
1096
1401
  return self unless p_addr
1097
1402
 
1098
1403
  Class.new(self) {
@@ -1170,17 +1475,11 @@ module Net #:nodoc:
1170
1475
  end
1171
1476
  end
1172
1477
 
1173
- # [Bug #12921]
1174
- if /linux|freebsd|darwin/ =~ RUBY_PLATFORM
1175
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = true
1176
- else
1177
- ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE = false
1178
- end
1179
-
1180
1478
  # The username of the proxy server, if one is configured.
1181
1479
  def proxy_user
1182
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
1183
- proxy_uri&.user
1480
+ if @proxy_from_env
1481
+ user = proxy_uri&.user
1482
+ unescape(user) if user
1184
1483
  else
1185
1484
  @proxy_user
1186
1485
  end
@@ -1188,8 +1487,9 @@ module Net #:nodoc:
1188
1487
 
1189
1488
  # The password of the proxy server, if one is configured.
1190
1489
  def proxy_pass
1191
- if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
1192
- proxy_uri&.password
1490
+ if @proxy_from_env
1491
+ pass = proxy_uri&.password
1492
+ unescape(pass) if pass
1193
1493
  else
1194
1494
  @proxy_pass
1195
1495
  end
@@ -1200,6 +1500,11 @@ module Net #:nodoc:
1200
1500
 
1201
1501
  private
1202
1502
 
1503
+ def unescape(value)
1504
+ require 'cgi/util'
1505
+ CGI.unescape(value)
1506
+ end
1507
+
1203
1508
  # without proxy, obsolete
1204
1509
 
1205
1510
  def conn_address # :nodoc:
@@ -1556,6 +1861,8 @@ module Net #:nodoc:
1556
1861
  begin
1557
1862
  res = HTTPResponse.read_new(@socket)
1558
1863
  res.decode_content = req.decode_content
1864
+ res.body_encoding = @response_body_encoding
1865
+ res.ignore_eof = @ignore_eof
1559
1866
  end while res.kind_of?(HTTPInformation)
1560
1867
 
1561
1868
  res.uri = req.uri
@@ -1575,10 +1882,10 @@ module Net #:nodoc:
1575
1882
  if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method)
1576
1883
  count += 1
1577
1884
  @socket.close if @socket
1578
- D "Conn close because of error #{exception}, and retry"
1885
+ debug "Conn close because of error #{exception}, and retry"
1579
1886
  retry
1580
1887
  end
1581
- D "Conn close because of error #{exception}"
1888
+ debug "Conn close because of error #{exception}"
1582
1889
  @socket.close if @socket
1583
1890
  raise
1584
1891
  end
@@ -1586,7 +1893,7 @@ module Net #:nodoc:
1586
1893
  end_transport req, res
1587
1894
  res
1588
1895
  rescue => exception
1589
- D "Conn close because of error #{exception}"
1896
+ debug "Conn close because of error #{exception}"
1590
1897
  @socket.close if @socket
1591
1898
  raise exception
1592
1899
  end
@@ -1596,11 +1903,11 @@ module Net #:nodoc:
1596
1903
  connect
1597
1904
  elsif @last_communicated
1598
1905
  if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC)
1599
- D 'Conn close because of keep_alive_timeout'
1906
+ debug 'Conn close because of keep_alive_timeout'
1600
1907
  @socket.close
1601
1908
  connect
1602
1909
  elsif @socket.io.to_io.wait_readable(0) && @socket.eof?
1603
- D "Conn close because of EOF"
1910
+ debug "Conn close because of EOF"
1604
1911
  @socket.close
1605
1912
  connect
1606
1913
  end
@@ -1618,15 +1925,15 @@ module Net #:nodoc:
1618
1925
  @curr_http_version = res.http_version
1619
1926
  @last_communicated = nil
1620
1927
  if @socket.closed?
1621
- D 'Conn socket closed'
1928
+ debug 'Conn socket closed'
1622
1929
  elsif not res.body and @close_on_empty_response
1623
- D 'Conn close'
1930
+ debug 'Conn close'
1624
1931
  @socket.close
1625
1932
  elsif keep_alive?(req, res)
1626
- D 'Conn keep-alive'
1933
+ debug 'Conn keep-alive'
1627
1934
  @last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1628
1935
  else
1629
- D 'Conn close'
1936
+ debug 'Conn close'
1630
1937
  @socket.close
1631
1938
  end
1632
1939
  end
@@ -1681,11 +1988,14 @@ module Net #:nodoc:
1681
1988
  default_port == port ? addr : "#{addr}:#{port}"
1682
1989
  end
1683
1990
 
1684
- def D(msg)
1991
+ # Adds a message to debugging output
1992
+ def debug(msg)
1685
1993
  return unless @debug_output
1686
1994
  @debug_output << msg
1687
1995
  @debug_output << "\n"
1688
1996
  end
1997
+
1998
+ alias_method :D, :debug
1689
1999
  end
1690
2000
 
1691
2001
  end