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.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/test.yml +3 -5
- data/.gitignore +0 -1
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/doc/net-http/examples.rdoc +30 -0
- data/lib/net/http/backward.rb +26 -12
- data/lib/net/http/exceptions.rb +27 -26
- data/lib/net/http/generic_request.rb +6 -7
- data/lib/net/http/header.rb +473 -105
- data/lib/net/http/request.rb +27 -5
- data/lib/net/http/requests.rb +296 -24
- data/lib/net/http/response.rb +303 -12
- data/lib/net/http/responses.rb +638 -223
- data/lib/net/http.rb +681 -371
- data/net-http.gemspec +0 -2
- metadata +5 -18
- data/Gemfile.lock +0 -23
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
|
-
#
|
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
|
-
#
|
37
|
-
#
|
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
|
-
#
|
41
|
-
#
|
42
|
-
#
|
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
|
-
#
|
46
|
+
# == Synopsis
|
45
47
|
#
|
46
|
-
#
|
48
|
+
# If you are already familiar with \HTTP, this synopsis may be helpful.
|
47
49
|
#
|
48
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
55
|
-
#
|
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
|
-
#
|
84
|
+
# # Return string response body.
|
85
|
+
# Net::HTTP.get(hostname, path, port = 80)
|
86
|
+
# Net::HTTP.get(uri, headers = {}, port = 80)
|
59
87
|
#
|
60
|
-
#
|
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
|
-
#
|
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 =
|
65
|
-
# Net::HTTP.
|
96
|
+
# Net::HTTP.post(uri, data, headers = {})
|
97
|
+
# Net::HTTP.post_form(uri, params)
|
66
98
|
#
|
67
|
-
#
|
99
|
+
# == About the Examples
|
68
100
|
#
|
69
|
-
#
|
70
|
-
# params = { :limit => 10, :page => 3 }
|
71
|
-
# uri.query = URI.encode_www_form(params)
|
101
|
+
# :include: doc/net-http/examples.rdoc
|
72
102
|
#
|
73
|
-
#
|
74
|
-
# puts res.body if res.is_a?(Net::HTTPSuccess)
|
103
|
+
# == URIs
|
75
104
|
#
|
76
|
-
#
|
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
|
-
#
|
79
|
-
#
|
80
|
-
#
|
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
|
-
# ===
|
116
|
+
# === Schemes
|
83
117
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
# puts res.body
|
118
|
+
# An internet \URI has
|
119
|
+
# a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes].
|
87
120
|
#
|
88
|
-
#
|
121
|
+
# The two schemes supported in \Net::HTTP are <tt>'https'</tt> and <tt>'http'</tt>:
|
89
122
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
# connections.
|
123
|
+
# uri.scheme # => "https"
|
124
|
+
# URI('http://example.com').scheme # => "http"
|
93
125
|
#
|
94
|
-
#
|
126
|
+
# === Hostnames
|
95
127
|
#
|
96
|
-
#
|
97
|
-
# request = Net::HTTP::Get.new uri
|
128
|
+
# A hostname identifies a server (host) to which requests may be sent:
|
98
129
|
#
|
99
|
-
#
|
130
|
+
# hostname = uri.hostname # => "jsonplaceholder.typicode.com"
|
131
|
+
# Net::HTTP.start(hostname) do |http|
|
132
|
+
# # Some HTTP stuff.
|
100
133
|
# end
|
101
134
|
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
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
|
-
#
|
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
|
-
#
|
112
|
-
# Request Classes".
|
188
|
+
# === Session Using \Net::HTTP.start
|
113
189
|
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
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
|
-
#
|
194
|
+
# - Calling #start before block execution.
|
195
|
+
# - Executing the block.
|
196
|
+
# - Calling #finish after block execution.
|
119
197
|
#
|
120
|
-
#
|
121
|
-
#
|
198
|
+
# In the block, you can use these instance methods,
|
199
|
+
# each of which that sends a single request:
|
122
200
|
#
|
123
|
-
#
|
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
|
-
# #
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
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
|
-
#
|
135
|
-
# puts res.body if res.response_body_permitted?
|
211
|
+
# - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
|
136
212
|
#
|
137
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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.
|
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
|
-
#
|
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
|
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
|
-
#
|
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
|
-
#
|
453
|
-
#
|
454
|
-
#
|
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
|
-
#
|
432
|
+
# Sends a GET request and returns the \HTTP response body as a string.
|
457
433
|
#
|
458
|
-
#
|
434
|
+
# With string arguments +hostname+ and +path+:
|
459
435
|
#
|
460
|
-
#
|
436
|
+
# hostname = 'jsonplaceholder.typicode.com'
|
437
|
+
# path = '/todos/1'
|
438
|
+
# puts Net::HTTP.get(hostname, path)
|
461
439
|
#
|
462
|
-
#
|
440
|
+
# Output:
|
463
441
|
#
|
464
|
-
#
|
442
|
+
# {
|
443
|
+
# "userId": 1,
|
444
|
+
# "id": 1,
|
445
|
+
# "title": "delectus aut autem",
|
446
|
+
# "completed": false
|
447
|
+
# }
|
465
448
|
#
|
466
|
-
|
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
|
-
#
|
475
|
-
#
|
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
|
-
#
|
455
|
+
# Related:
|
478
456
|
#
|
479
|
-
#
|
480
|
-
#
|
457
|
+
# - Net::HTTP::Get: request class for \HTTP method +GET+.
|
458
|
+
# - Net::HTTP#get: convenience method for \HTTP method +GET+.
|
481
459
|
#
|
482
|
-
|
483
|
-
|
484
|
-
|
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
|
487
|
+
# Posts data to a host; returns a Net::HTTPResponse object.
|
504
488
|
#
|
505
|
-
#
|
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
|
-
#
|
508
|
-
# require 'uri'
|
499
|
+
# Output:
|
509
500
|
#
|
510
|
-
#
|
511
|
-
#
|
512
|
-
#
|
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
|
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
|
-
#
|
522
|
+
# Argument +url+ must be a URI;
|
523
|
+
# argument +data+ must be a hash:
|
526
524
|
#
|
527
|
-
#
|
528
|
-
#
|
529
|
-
#
|
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
|
-
#
|
532
|
-
#
|
533
|
-
# require 'net/http'
|
534
|
-
# require 'uri'
|
531
|
+
# Output:
|
535
532
|
#
|
536
|
-
#
|
537
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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,
|
574
|
-
# HTTP.start(address, port=nil, p_addr
|
575
|
-
#
|
576
|
-
# Creates a new Net::HTTP object,
|
577
|
-
#
|
578
|
-
#
|
579
|
-
#
|
580
|
-
#
|
581
|
-
#
|
582
|
-
#
|
583
|
-
#
|
584
|
-
#
|
585
|
-
#
|
586
|
-
#
|
587
|
-
#
|
588
|
-
#
|
589
|
-
#
|
590
|
-
#
|
591
|
-
#
|
592
|
-
#
|
593
|
-
#
|
594
|
-
#
|
595
|
-
#
|
596
|
-
#
|
597
|
-
#
|
598
|
-
#
|
599
|
-
#
|
600
|
-
#
|
601
|
-
#
|
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
|
-
#
|
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
|
-
#
|
632
|
-
#
|
633
|
-
#
|
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
|
-
#
|
636
|
-
#
|
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
|
-
#
|
643
|
-
#
|
644
|
-
#
|
645
|
-
#
|
646
|
-
#
|
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
|
879
|
+
# Sets the output stream for debugging:
|
719
880
|
#
|
720
881
|
# http = Net::HTTP.new(hostname)
|
721
|
-
#
|
722
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
782
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
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
|
-
|
1033
|
-
|
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) &&
|
1343
|
+
if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
|
1040
1344
|
s.post_connection_check(@address)
|
1041
1345
|
end
|
1042
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
1885
|
+
debug "Conn close because of error #{exception}, and retry"
|
1579
1886
|
retry
|
1580
1887
|
end
|
1581
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1928
|
+
debug 'Conn socket closed'
|
1622
1929
|
elsif not res.body and @close_on_empty_response
|
1623
|
-
|
1930
|
+
debug 'Conn close'
|
1624
1931
|
@socket.close
|
1625
1932
|
elsif keep_alive?(req, res)
|
1626
|
-
|
1933
|
+
debug 'Conn keep-alive'
|
1627
1934
|
@last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
1628
1935
|
else
|
1629
|
-
|
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
|
-
|
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
|