net-http 0.1.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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