rubysl-net-http 1.0.1 → 2.0.4
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/.travis.yml +3 -2
- data/lib/net/http/backward.rb +25 -0
- data/lib/net/http/exceptions.rb +25 -0
- data/lib/net/http/generic_request.rb +329 -0
- data/lib/net/http/header.rb +452 -0
- data/lib/net/http/proxy_delta.rb +16 -0
- data/lib/net/http/request.rb +20 -0
- data/lib/net/http/requests.rb +122 -0
- data/lib/net/http/response.rb +405 -0
- data/lib/net/http/responses.rb +268 -0
- data/lib/rubysl/net/http/http.rb +890 -1625
- data/lib/rubysl/net/http/https.rb +6 -157
- data/lib/rubysl/net/http/version.rb +1 -1
- data/rubysl-net-http.gemspec +6 -6
- metadata +24 -14
@@ -0,0 +1,268 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
class Net::HTTPUnknownResponse < Net::HTTPResponse
|
3
|
+
HAS_BODY = true
|
4
|
+
EXCEPTION_TYPE = Net::HTTPError
|
5
|
+
end
|
6
|
+
class Net::HTTPInformation < Net::HTTPResponse # 1xx
|
7
|
+
HAS_BODY = false
|
8
|
+
EXCEPTION_TYPE = Net::HTTPError
|
9
|
+
end
|
10
|
+
class Net::HTTPSuccess < Net::HTTPResponse # 2xx
|
11
|
+
HAS_BODY = true
|
12
|
+
EXCEPTION_TYPE = Net::HTTPError
|
13
|
+
end
|
14
|
+
class Net::HTTPRedirection < Net::HTTPResponse # 3xx
|
15
|
+
HAS_BODY = true
|
16
|
+
EXCEPTION_TYPE = Net::HTTPRetriableError
|
17
|
+
end
|
18
|
+
class Net::HTTPClientError < Net::HTTPResponse # 4xx
|
19
|
+
HAS_BODY = true
|
20
|
+
EXCEPTION_TYPE = Net::HTTPServerException # for backward compatibility
|
21
|
+
end
|
22
|
+
class Net::HTTPServerError < Net::HTTPResponse # 5xx
|
23
|
+
HAS_BODY = true
|
24
|
+
EXCEPTION_TYPE = Net::HTTPFatalError # for backward compatibility
|
25
|
+
end
|
26
|
+
|
27
|
+
class Net::HTTPContinue < Net::HTTPInformation # 100
|
28
|
+
HAS_BODY = false
|
29
|
+
end
|
30
|
+
class Net::HTTPSwitchProtocol < Net::HTTPInformation # 101
|
31
|
+
HAS_BODY = false
|
32
|
+
end
|
33
|
+
# 102 - RFC 2518; removed in RFC 4918
|
34
|
+
|
35
|
+
class Net::HTTPOK < Net::HTTPSuccess # 200
|
36
|
+
HAS_BODY = true
|
37
|
+
end
|
38
|
+
class Net::HTTPCreated < Net::HTTPSuccess # 201
|
39
|
+
HAS_BODY = true
|
40
|
+
end
|
41
|
+
class Net::HTTPAccepted < Net::HTTPSuccess # 202
|
42
|
+
HAS_BODY = true
|
43
|
+
end
|
44
|
+
class Net::HTTPNonAuthoritativeInformation < Net::HTTPSuccess # 203
|
45
|
+
HAS_BODY = true
|
46
|
+
end
|
47
|
+
class Net::HTTPNoContent < Net::HTTPSuccess # 204
|
48
|
+
HAS_BODY = false
|
49
|
+
end
|
50
|
+
class Net::HTTPResetContent < Net::HTTPSuccess # 205
|
51
|
+
HAS_BODY = false
|
52
|
+
end
|
53
|
+
class Net::HTTPPartialContent < Net::HTTPSuccess # 206
|
54
|
+
HAS_BODY = true
|
55
|
+
end
|
56
|
+
class Net::HTTPMultiStatus < Net::HTTPSuccess # 207 - RFC 4918
|
57
|
+
HAS_BODY = true
|
58
|
+
end
|
59
|
+
# 208 Already Reported - RFC 5842; experimental
|
60
|
+
# 226 IM Used - RFC 3229; no famous implementation known
|
61
|
+
|
62
|
+
class Net::HTTPMultipleChoices < Net::HTTPRedirection # 300
|
63
|
+
HAS_BODY = true
|
64
|
+
end
|
65
|
+
Net::HTTPMultipleChoice = Net::HTTPMultipleChoices
|
66
|
+
class Net::HTTPMovedPermanently < Net::HTTPRedirection # 301
|
67
|
+
HAS_BODY = true
|
68
|
+
end
|
69
|
+
class Net::HTTPFound < Net::HTTPRedirection # 302
|
70
|
+
HAS_BODY = true
|
71
|
+
end
|
72
|
+
Net::HTTPMovedTemporarily = Net::HTTPFound
|
73
|
+
class Net::HTTPSeeOther < Net::HTTPRedirection # 303
|
74
|
+
HAS_BODY = true
|
75
|
+
end
|
76
|
+
class Net::HTTPNotModified < Net::HTTPRedirection # 304
|
77
|
+
HAS_BODY = false
|
78
|
+
end
|
79
|
+
class Net::HTTPUseProxy < Net::HTTPRedirection # 305
|
80
|
+
HAS_BODY = false
|
81
|
+
end
|
82
|
+
# 306 Switch Proxy - no longer unused
|
83
|
+
class Net::HTTPTemporaryRedirect < Net::HTTPRedirection # 307
|
84
|
+
HAS_BODY = true
|
85
|
+
end
|
86
|
+
# 308 Permanent Redirect - in draft
|
87
|
+
|
88
|
+
class Net::HTTPBadRequest < Net::HTTPClientError # 400
|
89
|
+
HAS_BODY = true
|
90
|
+
end
|
91
|
+
class Net::HTTPUnauthorized < Net::HTTPClientError # 401
|
92
|
+
HAS_BODY = true
|
93
|
+
end
|
94
|
+
class Net::HTTPPaymentRequired < Net::HTTPClientError # 402
|
95
|
+
HAS_BODY = true
|
96
|
+
end
|
97
|
+
class Net::HTTPForbidden < Net::HTTPClientError # 403
|
98
|
+
HAS_BODY = true
|
99
|
+
end
|
100
|
+
class Net::HTTPNotFound < Net::HTTPClientError # 404
|
101
|
+
HAS_BODY = true
|
102
|
+
end
|
103
|
+
class Net::HTTPMethodNotAllowed < Net::HTTPClientError # 405
|
104
|
+
HAS_BODY = true
|
105
|
+
end
|
106
|
+
class Net::HTTPNotAcceptable < Net::HTTPClientError # 406
|
107
|
+
HAS_BODY = true
|
108
|
+
end
|
109
|
+
class Net::HTTPProxyAuthenticationRequired < Net::HTTPClientError # 407
|
110
|
+
HAS_BODY = true
|
111
|
+
end
|
112
|
+
class Net::HTTPRequestTimeOut < Net::HTTPClientError # 408
|
113
|
+
HAS_BODY = true
|
114
|
+
end
|
115
|
+
class Net::HTTPConflict < Net::HTTPClientError # 409
|
116
|
+
HAS_BODY = true
|
117
|
+
end
|
118
|
+
class Net::HTTPGone < Net::HTTPClientError # 410
|
119
|
+
HAS_BODY = true
|
120
|
+
end
|
121
|
+
class Net::HTTPLengthRequired < Net::HTTPClientError # 411
|
122
|
+
HAS_BODY = true
|
123
|
+
end
|
124
|
+
class Net::HTTPPreconditionFailed < Net::HTTPClientError # 412
|
125
|
+
HAS_BODY = true
|
126
|
+
end
|
127
|
+
class Net::HTTPRequestEntityTooLarge < Net::HTTPClientError # 413
|
128
|
+
HAS_BODY = true
|
129
|
+
end
|
130
|
+
class Net::HTTPRequestURITooLong < Net::HTTPClientError # 414
|
131
|
+
HAS_BODY = true
|
132
|
+
end
|
133
|
+
Net::HTTPRequestURITooLarge = Net::HTTPRequestURITooLong
|
134
|
+
class Net::HTTPUnsupportedMediaType < Net::HTTPClientError # 415
|
135
|
+
HAS_BODY = true
|
136
|
+
end
|
137
|
+
class Net::HTTPRequestedRangeNotSatisfiable < Net::HTTPClientError # 416
|
138
|
+
HAS_BODY = true
|
139
|
+
end
|
140
|
+
class Net::HTTPExpectationFailed < Net::HTTPClientError # 417
|
141
|
+
HAS_BODY = true
|
142
|
+
end
|
143
|
+
# 418 I'm a teapot - RFC 2324; a joke RFC
|
144
|
+
# 420 Enhance Your Calm - Twitter
|
145
|
+
class Net::HTTPUnprocessableEntity < Net::HTTPClientError # 422 - RFC 4918
|
146
|
+
HAS_BODY = true
|
147
|
+
end
|
148
|
+
class Net::HTTPLocked < Net::HTTPClientError # 423 - RFC 4918
|
149
|
+
HAS_BODY = true
|
150
|
+
end
|
151
|
+
class Net::HTTPFailedDependency < Net::HTTPClientError # 424 - RFC 4918
|
152
|
+
HAS_BODY = true
|
153
|
+
end
|
154
|
+
# 425 Unordered Collection - existed only in draft
|
155
|
+
class Net::HTTPUpgradeRequired < Net::HTTPClientError # 426 - RFC 2817
|
156
|
+
HAS_BODY = true
|
157
|
+
end
|
158
|
+
class Net::HTTPPreconditionRequired < Net::HTTPClientError # 428 - RFC 6585
|
159
|
+
HAS_BODY = true
|
160
|
+
end
|
161
|
+
class Net::HTTPTooManyRequests < Net::HTTPClientError # 429 - RFC 6585
|
162
|
+
HAS_BODY = true
|
163
|
+
end
|
164
|
+
class Net::HTTPRequestHeaderFieldsTooLarge < Net::HTTPClientError # 431 - RFC 6585
|
165
|
+
HAS_BODY = true
|
166
|
+
end
|
167
|
+
# 444 No Response - Nginx
|
168
|
+
# 449 Retry With - Microsoft
|
169
|
+
# 450 Blocked by Windows Parental Controls - Microsoft
|
170
|
+
# 499 Client Closed Request - Nginx
|
171
|
+
|
172
|
+
class Net::HTTPInternalServerError < Net::HTTPServerError # 500
|
173
|
+
HAS_BODY = true
|
174
|
+
end
|
175
|
+
class Net::HTTPNotImplemented < Net::HTTPServerError # 501
|
176
|
+
HAS_BODY = true
|
177
|
+
end
|
178
|
+
class Net::HTTPBadGateway < Net::HTTPServerError # 502
|
179
|
+
HAS_BODY = true
|
180
|
+
end
|
181
|
+
class Net::HTTPServiceUnavailable < Net::HTTPServerError # 503
|
182
|
+
HAS_BODY = true
|
183
|
+
end
|
184
|
+
class Net::HTTPGatewayTimeOut < Net::HTTPServerError # 504
|
185
|
+
HAS_BODY = true
|
186
|
+
end
|
187
|
+
class Net::HTTPVersionNotSupported < Net::HTTPServerError # 505
|
188
|
+
HAS_BODY = true
|
189
|
+
end
|
190
|
+
# 506 Variant Also Negotiates - RFC 2295; experimental
|
191
|
+
class Net::HTTPInsufficientStorage < Net::HTTPServerError # 507 - RFC 4918
|
192
|
+
HAS_BODY = true
|
193
|
+
end
|
194
|
+
# 508 Loop Detected - RFC 5842; experimental
|
195
|
+
# 509 Bandwidth Limit Exceeded - Apache bw/limited extension
|
196
|
+
# 510 Not Extended - RFC 2774; experimental
|
197
|
+
class Net::HTTPNetworkAuthenticationRequired < Net::HTTPServerError # 511 - RFC 6585
|
198
|
+
HAS_BODY = true
|
199
|
+
end
|
200
|
+
|
201
|
+
class Net::HTTPResponse
|
202
|
+
CODE_CLASS_TO_OBJ = {
|
203
|
+
'1' => Net::HTTPInformation,
|
204
|
+
'2' => Net::HTTPSuccess,
|
205
|
+
'3' => Net::HTTPRedirection,
|
206
|
+
'4' => Net::HTTPClientError,
|
207
|
+
'5' => Net::HTTPServerError
|
208
|
+
}
|
209
|
+
CODE_TO_OBJ = {
|
210
|
+
'100' => Net::HTTPContinue,
|
211
|
+
'101' => Net::HTTPSwitchProtocol,
|
212
|
+
|
213
|
+
'200' => Net::HTTPOK,
|
214
|
+
'201' => Net::HTTPCreated,
|
215
|
+
'202' => Net::HTTPAccepted,
|
216
|
+
'203' => Net::HTTPNonAuthoritativeInformation,
|
217
|
+
'204' => Net::HTTPNoContent,
|
218
|
+
'205' => Net::HTTPResetContent,
|
219
|
+
'206' => Net::HTTPPartialContent,
|
220
|
+
'207' => Net::HTTPMultiStatus,
|
221
|
+
|
222
|
+
'300' => Net::HTTPMultipleChoices,
|
223
|
+
'301' => Net::HTTPMovedPermanently,
|
224
|
+
'302' => Net::HTTPFound,
|
225
|
+
'303' => Net::HTTPSeeOther,
|
226
|
+
'304' => Net::HTTPNotModified,
|
227
|
+
'305' => Net::HTTPUseProxy,
|
228
|
+
'307' => Net::HTTPTemporaryRedirect,
|
229
|
+
|
230
|
+
'400' => Net::HTTPBadRequest,
|
231
|
+
'401' => Net::HTTPUnauthorized,
|
232
|
+
'402' => Net::HTTPPaymentRequired,
|
233
|
+
'403' => Net::HTTPForbidden,
|
234
|
+
'404' => Net::HTTPNotFound,
|
235
|
+
'405' => Net::HTTPMethodNotAllowed,
|
236
|
+
'406' => Net::HTTPNotAcceptable,
|
237
|
+
'407' => Net::HTTPProxyAuthenticationRequired,
|
238
|
+
'408' => Net::HTTPRequestTimeOut,
|
239
|
+
'409' => Net::HTTPConflict,
|
240
|
+
'410' => Net::HTTPGone,
|
241
|
+
'411' => Net::HTTPLengthRequired,
|
242
|
+
'412' => Net::HTTPPreconditionFailed,
|
243
|
+
'413' => Net::HTTPRequestEntityTooLarge,
|
244
|
+
'414' => Net::HTTPRequestURITooLong,
|
245
|
+
'415' => Net::HTTPUnsupportedMediaType,
|
246
|
+
'416' => Net::HTTPRequestedRangeNotSatisfiable,
|
247
|
+
'417' => Net::HTTPExpectationFailed,
|
248
|
+
'422' => Net::HTTPUnprocessableEntity,
|
249
|
+
'423' => Net::HTTPLocked,
|
250
|
+
'424' => Net::HTTPFailedDependency,
|
251
|
+
'426' => Net::HTTPUpgradeRequired,
|
252
|
+
'428' => Net::HTTPPreconditionRequired,
|
253
|
+
'429' => Net::HTTPTooManyRequests,
|
254
|
+
'431' => Net::HTTPRequestHeaderFieldsTooLarge,
|
255
|
+
|
256
|
+
'500' => Net::HTTPInternalServerError,
|
257
|
+
'501' => Net::HTTPNotImplemented,
|
258
|
+
'502' => Net::HTTPBadGateway,
|
259
|
+
'503' => Net::HTTPServiceUnavailable,
|
260
|
+
'504' => Net::HTTPGatewayTimeOut,
|
261
|
+
'505' => Net::HTTPVersionNotSupported,
|
262
|
+
'507' => Net::HTTPInsufficientStorage,
|
263
|
+
'511' => Net::HTTPNetworkAuthenticationRequired,
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
# :startdoc:
|
268
|
+
|
data/lib/rubysl/net/http/http.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#
|
2
2
|
# = net/http.rb
|
3
3
|
#
|
4
|
-
# Copyright (c) 1999-
|
5
|
-
# Copyright (c) 1999-
|
4
|
+
# Copyright (c) 1999-2007 Yukihiro Matsumoto
|
5
|
+
# Copyright (c) 1999-2007 Minero Aoki
|
6
6
|
# Copyright (c) 2001 GOTOU Yuuzou
|
7
7
|
#
|
8
8
|
# Written and maintained by Minero Aoki <aamine@loveruby.net>.
|
@@ -18,299 +18,397 @@
|
|
18
18
|
#
|
19
19
|
# See Net::HTTP for an overview and examples.
|
20
20
|
#
|
21
|
-
# NOTE: You can find Japanese version of this document here:
|
22
|
-
# http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb
|
23
|
-
#
|
24
|
-
#--
|
25
|
-
# $Id$
|
26
|
-
#++
|
27
21
|
|
28
22
|
require 'net/protocol'
|
29
23
|
require 'uri'
|
30
24
|
|
31
25
|
module Net #:nodoc:
|
26
|
+
autoload :OpenSSL, 'openssl'
|
32
27
|
|
33
28
|
# :stopdoc:
|
34
29
|
class HTTPBadResponse < StandardError; end
|
35
30
|
class HTTPHeaderSyntaxError < StandardError; end
|
36
31
|
# :startdoc:
|
37
32
|
|
38
|
-
# ==
|
33
|
+
# == An HTTP client API for Ruby.
|
39
34
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# (http://www.ietf.org/rfc/rfc2616.txt).
|
35
|
+
# Net::HTTP provides a rich library which can be used to build HTTP
|
36
|
+
# user-agents. For more details about HTTP see
|
37
|
+
# [RFC2616](http://www.ietf.org/rfc/rfc2616.txt)
|
44
38
|
#
|
45
|
-
#
|
39
|
+
# Net::HTTP is designed to work closely with URI. URI::HTTP#host,
|
40
|
+
# URI::HTTP#port and URI::HTTP#request_uri are designed to work with
|
41
|
+
# Net::HTTP.
|
46
42
|
#
|
47
|
-
#
|
43
|
+
# If you are only performing a few GET requests you should try OpenURI.
|
48
44
|
#
|
49
|
-
#
|
45
|
+
# == Simple Examples
|
50
46
|
#
|
51
|
-
#
|
52
|
-
# Net::HTTP.get_print 'www.example.com', '/index.html'
|
47
|
+
# All examples assume you have loaded Net::HTTP with:
|
53
48
|
#
|
54
|
-
#
|
49
|
+
# require 'net/http'
|
55
50
|
#
|
56
|
-
#
|
57
|
-
# require 'uri'
|
58
|
-
# Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
|
51
|
+
# This will also require 'uri' so you don't need to require it separately.
|
59
52
|
#
|
60
|
-
#
|
53
|
+
# The Net::HTTP methods in the following section do not persist
|
54
|
+
# connections. They are not recommended if you are performing many HTTP
|
55
|
+
# requests.
|
61
56
|
#
|
62
|
-
#
|
63
|
-
# require 'uri'
|
57
|
+
# === GET
|
64
58
|
#
|
65
|
-
#
|
66
|
-
# res = Net::HTTP.start(url.host, url.port) {|http|
|
67
|
-
# http.get('/index.html')
|
68
|
-
# }
|
69
|
-
# puts res.body
|
59
|
+
# Net::HTTP.get('example.com', '/index.html') # => String
|
70
60
|
#
|
71
|
-
#
|
61
|
+
# === GET by URI
|
72
62
|
#
|
73
|
-
#
|
63
|
+
# uri = URI('http://example.com/index.html?count=10')
|
64
|
+
# Net::HTTP.get(uri) # => String
|
74
65
|
#
|
75
|
-
#
|
76
|
-
# req = Net::HTTP::Get.new(url.path)
|
77
|
-
# res = Net::HTTP.start(url.host, url.port) {|http|
|
78
|
-
# http.request(req)
|
79
|
-
# }
|
80
|
-
# puts res.body
|
66
|
+
# === GET with Dynamic Parameters
|
81
67
|
#
|
82
|
-
#
|
68
|
+
# uri = URI('http://example.com/index.html')
|
69
|
+
# params = { :limit => 10, :page => 3 }
|
70
|
+
# uri.query = URI.encode_www_form(params)
|
83
71
|
#
|
84
|
-
#
|
85
|
-
#
|
72
|
+
# res = Net::HTTP.get_response(uri)
|
73
|
+
# puts res.body if res.is_a?(Net::HTTPSuccess)
|
86
74
|
#
|
87
|
-
#
|
88
|
-
# res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
|
89
|
-
# {'q'=>'ruby', 'max'=>'50'})
|
90
|
-
# puts res.body
|
75
|
+
# === POST
|
91
76
|
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
# puts res.body
|
77
|
+
# uri = URI('http://www.example.com/search.cgi')
|
78
|
+
# res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
|
79
|
+
# puts res.body
|
96
80
|
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
# res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
|
103
|
-
# case res
|
104
|
-
# when Net::HTTPSuccess, Net::HTTPRedirection
|
105
|
-
# # OK
|
106
|
-
# else
|
107
|
-
# res.error!
|
108
|
-
# end
|
81
|
+
# === POST with Multiple Values
|
82
|
+
#
|
83
|
+
# uri = URI('http://www.example.com/search.cgi')
|
84
|
+
# res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
|
85
|
+
# puts res.body
|
109
86
|
#
|
110
|
-
#
|
87
|
+
# == How to use Net::HTTP
|
111
88
|
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
89
|
+
# The following example code can be used as the basis of a HTTP user-agent
|
90
|
+
# which can perform a variety of request types using persistent
|
91
|
+
# connections.
|
115
92
|
#
|
116
|
-
#
|
93
|
+
# uri = URI('http://example.com/some_path?query=string')
|
117
94
|
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
# :
|
121
|
-
# Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
|
122
|
-
# # always connect to your.proxy.addr:8080
|
123
|
-
# :
|
124
|
-
# }
|
95
|
+
# Net::HTTP.start(uri.host, uri.port) do |http|
|
96
|
+
# request = Net::HTTP::Get.new uri
|
125
97
|
#
|
126
|
-
#
|
127
|
-
#
|
98
|
+
# response = http.request request # Net::HTTPResponse object
|
99
|
+
# end
|
128
100
|
#
|
129
|
-
#
|
130
|
-
#
|
101
|
+
# Net::HTTP::start immediately creates a connection to an HTTP server which
|
102
|
+
# is kept open for the duration of the block. The connection will remain
|
103
|
+
# open for multiple requests in the block if the server indicates it
|
104
|
+
# supports persistent connections.
|
131
105
|
#
|
132
|
-
#
|
106
|
+
# The request types Net::HTTP supports are listed below in the section "HTTP
|
107
|
+
# Request Classes".
|
133
108
|
#
|
134
|
-
#
|
109
|
+
# If you wish to re-use a connection across multiple HTTP requests without
|
110
|
+
# automatically closing it you can use ::new instead of ::start. #request
|
111
|
+
# will automatically open a connection to the server if one is not currently
|
112
|
+
# open. You can manually close the connection with #finish.
|
135
113
|
#
|
136
|
-
#
|
137
|
-
#
|
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.
|
138
117
|
#
|
139
|
-
#
|
140
|
-
# proxy_port = 8080
|
141
|
-
# uri = URI.parse(ENV['http_proxy'])
|
142
|
-
# proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
|
143
|
-
# Net::HTTP::Proxy(proxy_host, proxy_port,
|
144
|
-
# proxy_user, proxy_pass).start('www.example.com') {|http|
|
145
|
-
# # always connect to your.proxy.addr:8080 using specified username and password
|
146
|
-
# :
|
147
|
-
# }
|
118
|
+
# === Response Data
|
148
119
|
#
|
149
|
-
#
|
150
|
-
#
|
120
|
+
# uri = URI('http://example.com/index.html')
|
121
|
+
# res = Net::HTTP.get_response(uri)
|
122
|
+
#
|
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}"
|
128
|
+
#
|
129
|
+
# # Status
|
130
|
+
# puts res.code # => '200'
|
131
|
+
# puts res.message # => 'OK'
|
132
|
+
# puts res.class.name # => 'HTTPOK'
|
133
|
+
#
|
134
|
+
# # Body
|
135
|
+
# puts res.body if res.response_body_permitted?
|
151
136
|
#
|
152
137
|
# === Following Redirection
|
153
138
|
#
|
154
|
-
#
|
155
|
-
# require 'uri'
|
139
|
+
# Each Net::HTTPResponse object belongs to a class for its response code.
|
156
140
|
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
141
|
+
# For example, all 2XX responses are instances of a Net::HTTPSuccess
|
142
|
+
# subclass, a 3XX response is an instance of a Net::HTTPRedirection
|
143
|
+
# subclass and a 200 response is an instance of the Net::HTTPOK class. For
|
144
|
+
# details of response classes, see the section "HTTP Response Classes"
|
145
|
+
# below.
|
160
146
|
#
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
147
|
+
# Using a case statement you can handle various types of responses properly:
|
148
|
+
#
|
149
|
+
# def fetch(uri_str, limit = 10)
|
150
|
+
# # You should choose a better exception.
|
151
|
+
# raise ArgumentError, 'too many HTTP redirects' if limit == 0
|
152
|
+
#
|
153
|
+
# response = Net::HTTP.get_response(URI(uri_str))
|
154
|
+
#
|
155
|
+
# case response
|
156
|
+
# when Net::HTTPSuccess then
|
157
|
+
# response
|
158
|
+
# when Net::HTTPRedirection then
|
159
|
+
# location = response['location']
|
160
|
+
# warn "redirected to #{location}"
|
161
|
+
# fetch(location, limit - 1)
|
162
|
+
# else
|
163
|
+
# response.value
|
168
164
|
# end
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# print fetch('http://www.ruby-lang.org')
|
168
|
+
#
|
169
|
+
# === POST
|
170
|
+
#
|
171
|
+
# A POST can be made using the Net::HTTP::Post request class. This example
|
172
|
+
# creates a urlencoded 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')
|
169
177
|
#
|
170
|
-
#
|
178
|
+
# res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
179
|
+
# http.request(req)
|
180
|
+
# end
|
171
181
|
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
182
|
+
# case res
|
183
|
+
# when Net::HTTPSuccess, Net::HTTPRedirection
|
184
|
+
# # OK
|
185
|
+
# else
|
186
|
+
# res.value
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# At this time Net::HTTP does not support multipart/form-data. To send
|
190
|
+
# multipart/form-data use Net::HTTPRequest#body= and
|
191
|
+
# Net::HTTPRequest#content_type=:
|
192
|
+
#
|
193
|
+
# req = Net::HTTP::Post.new(uri)
|
194
|
+
# req.body = multipart_data
|
195
|
+
# req.content_type = 'multipart/form-data'
|
196
|
+
#
|
197
|
+
# Other requests that can contain a body such as PUT can be created in the
|
198
|
+
# same way using the corresponding request class (Net::HTTP::Put).
|
199
|
+
#
|
200
|
+
# === Setting Headers
|
201
|
+
#
|
202
|
+
# The following example performs a conditional GET using the
|
203
|
+
# If-Modified-Since header. If the files has not been modified since the
|
204
|
+
# time in the header a Not Modified response will be returned. See RFC 2616
|
205
|
+
# section 9.3 for further details.
|
206
|
+
#
|
207
|
+
# uri = URI('http://example.com/cached_response')
|
208
|
+
# file = File.stat 'cached_response'
|
209
|
+
#
|
210
|
+
# req = Net::HTTP::Get.new(uri)
|
211
|
+
# req['If-Modified-Since'] = file.mtime.rfc2822
|
212
|
+
#
|
213
|
+
# res = Net::HTTP.start(uri.hostname, uri.port) {|http|
|
214
|
+
# http.request(req)
|
215
|
+
# }
|
216
|
+
#
|
217
|
+
# open 'cached_response', 'w' do |io|
|
218
|
+
# io.write res.body
|
219
|
+
# end if res.is_a?(Net::HTTPSuccess)
|
176
220
|
#
|
177
221
|
# === Basic Authentication
|
178
222
|
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
#
|
242
|
-
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
#
|
246
|
-
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
261
|
-
#
|
262
|
-
#
|
263
|
-
#
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
271
|
-
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
275
|
-
#
|
276
|
-
#
|
223
|
+
# Basic authentication is performed according to
|
224
|
+
# [RFC2617](http://www.ietf.org/rfc/rfc2617.txt)
|
225
|
+
#
|
226
|
+
# uri = URI('http://example.com/index.html?key=value')
|
227
|
+
#
|
228
|
+
# req = Net::HTTP::Get.new(uri)
|
229
|
+
# req.basic_auth 'user', 'pass'
|
230
|
+
#
|
231
|
+
# res = Net::HTTP.start(uri.hostname, uri.port) {|http|
|
232
|
+
# http.request(req)
|
233
|
+
# }
|
234
|
+
# puts res.body
|
235
|
+
#
|
236
|
+
# === Streaming Response Bodies
|
237
|
+
#
|
238
|
+
# By default Net::HTTP reads an entire response into memory. If you are
|
239
|
+
# handling large files or wish to implement a progress bar you can instead
|
240
|
+
# stream the body directly to an IO.
|
241
|
+
#
|
242
|
+
# uri = URI('http://example.com/large_file')
|
243
|
+
#
|
244
|
+
# Net::HTTP.start(uri.host, uri.port) do |http|
|
245
|
+
# request = Net::HTTP::Get.new uri
|
246
|
+
#
|
247
|
+
# http.request request do |response|
|
248
|
+
# open 'large_file', 'w' do |io|
|
249
|
+
# response.read_body do |chunk|
|
250
|
+
# io.write chunk
|
251
|
+
# end
|
252
|
+
# end
|
253
|
+
# end
|
254
|
+
# end
|
255
|
+
#
|
256
|
+
# === HTTPS
|
257
|
+
#
|
258
|
+
# HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=.
|
259
|
+
#
|
260
|
+
# uri = URI('https://secure.example.com/some_path?query=string')
|
261
|
+
#
|
262
|
+
# Net::HTTP.start(uri.host, uri.port,
|
263
|
+
# :use_ssl => uri.scheme == 'https') do |http|
|
264
|
+
# request = Net::HTTP::Get.new uri
|
265
|
+
#
|
266
|
+
# response = http.request request # Net::HTTPResponse object
|
267
|
+
# end
|
268
|
+
#
|
269
|
+
# In previous versions of Ruby you would need to require 'net/https' to use
|
270
|
+
# HTTPS. This is no longer true.
|
271
|
+
#
|
272
|
+
# === Proxies
|
273
|
+
#
|
274
|
+
# Net::HTTP will automatically create a proxy from the +http_proxy+
|
275
|
+
# environment variable if it is present. To disable use of +http_proxy+,
|
276
|
+
# pass +nil+ for the proxy address.
|
277
|
+
#
|
278
|
+
# You may also create a custom proxy:
|
279
|
+
#
|
280
|
+
# proxy_addr = 'your.proxy.host'
|
281
|
+
# proxy_port = 8080
|
282
|
+
#
|
283
|
+
# Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
|
284
|
+
# # always proxy via your.proxy.addr:8080
|
285
|
+
# }
|
286
|
+
#
|
287
|
+
# See Net::HTTP.new for further details and examples such as proxies that
|
288
|
+
# require a username and password.
|
289
|
+
#
|
290
|
+
# === Compression
|
291
|
+
#
|
292
|
+
# Net::HTTP automatically adds Accept-Encoding for compression of response
|
293
|
+
# bodies and automatically decompresses gzip and deflate responses unless a
|
294
|
+
# Range header was sent.
|
295
|
+
#
|
296
|
+
# Compression can be disabled through the Accept-Encoding: identity header.
|
297
|
+
#
|
298
|
+
# == HTTP Request Classes
|
299
|
+
#
|
300
|
+
# Here is the HTTP request class hierarchy.
|
301
|
+
#
|
302
|
+
# * Net::HTTPRequest
|
303
|
+
# * Net::HTTP::Get
|
304
|
+
# * Net::HTTP::Head
|
305
|
+
# * Net::HTTP::Post
|
306
|
+
# * Net::HTTP::Patch
|
307
|
+
# * Net::HTTP::Put
|
308
|
+
# * Net::HTTP::Proppatch
|
309
|
+
# * Net::HTTP::Lock
|
310
|
+
# * Net::HTTP::Unlock
|
311
|
+
# * Net::HTTP::Options
|
312
|
+
# * Net::HTTP::Propfind
|
313
|
+
# * Net::HTTP::Delete
|
314
|
+
# * Net::HTTP::Move
|
315
|
+
# * Net::HTTP::Copy
|
316
|
+
# * Net::HTTP::Mkcol
|
317
|
+
# * Net::HTTP::Trace
|
318
|
+
#
|
319
|
+
# == HTTP Response Classes
|
320
|
+
#
|
321
|
+
# Here is HTTP response class hierarchy. All classes are defined in Net
|
322
|
+
# module and are subclasses of Net::HTTPResponse.
|
323
|
+
#
|
324
|
+
# HTTPUnknownResponse:: For unhandled HTTP extensions
|
325
|
+
# HTTPInformation:: 1xx
|
326
|
+
# HTTPContinue:: 100
|
327
|
+
# HTTPSwitchProtocol:: 101
|
328
|
+
# HTTPSuccess:: 2xx
|
329
|
+
# HTTPOK:: 200
|
330
|
+
# HTTPCreated:: 201
|
331
|
+
# HTTPAccepted:: 202
|
332
|
+
# HTTPNonAuthoritativeInformation:: 203
|
333
|
+
# HTTPNoContent:: 204
|
334
|
+
# HTTPResetContent:: 205
|
335
|
+
# HTTPPartialContent:: 206
|
336
|
+
# HTTPMultiStatus:: 207
|
337
|
+
# HTTPRedirection:: 3xx
|
338
|
+
# HTTPMultipleChoices:: 300
|
339
|
+
# HTTPMovedPermanently:: 301
|
340
|
+
# HTTPFound:: 302
|
341
|
+
# HTTPSeeOther:: 303
|
342
|
+
# HTTPNotModified:: 304
|
343
|
+
# HTTPUseProxy:: 305
|
344
|
+
# HTTPTemporaryRedirect:: 307
|
345
|
+
# HTTPClientError:: 4xx
|
346
|
+
# HTTPBadRequest:: 400
|
347
|
+
# HTTPUnauthorized:: 401
|
348
|
+
# HTTPPaymentRequired:: 402
|
349
|
+
# HTTPForbidden:: 403
|
350
|
+
# HTTPNotFound:: 404
|
351
|
+
# HTTPMethodNotAllowed:: 405
|
352
|
+
# HTTPNotAcceptable:: 406
|
353
|
+
# HTTPProxyAuthenticationRequired:: 407
|
354
|
+
# HTTPRequestTimeOut:: 408
|
355
|
+
# HTTPConflict:: 409
|
356
|
+
# HTTPGone:: 410
|
357
|
+
# HTTPLengthRequired:: 411
|
358
|
+
# HTTPPreconditionFailed:: 412
|
359
|
+
# HTTPRequestEntityTooLarge:: 413
|
360
|
+
# HTTPRequestURITooLong:: 414
|
361
|
+
# HTTPUnsupportedMediaType:: 415
|
362
|
+
# HTTPRequestedRangeNotSatisfiable:: 416
|
363
|
+
# HTTPExpectationFailed:: 417
|
364
|
+
# HTTPUnprocessableEntity:: 422
|
365
|
+
# HTTPLocked:: 423
|
366
|
+
# HTTPFailedDependency:: 424
|
367
|
+
# HTTPUpgradeRequired:: 426
|
368
|
+
# HTTPPreconditionRequired:: 428
|
369
|
+
# HTTPTooManyRequests:: 429
|
370
|
+
# HTTPRequestHeaderFieldsTooLarge:: 431
|
371
|
+
# HTTPServerError:: 5xx
|
372
|
+
# HTTPInternalServerError:: 500
|
373
|
+
# HTTPNotImplemented:: 501
|
374
|
+
# HTTPBadGateway:: 502
|
375
|
+
# HTTPServiceUnavailable:: 503
|
376
|
+
# HTTPGatewayTimeOut:: 504
|
377
|
+
# HTTPVersionNotSupported:: 505
|
378
|
+
# HTTPInsufficientStorage:: 507
|
379
|
+
# HTTPNetworkAuthenticationRequired:: 511
|
380
|
+
#
|
381
|
+
# There is also the Net::HTTPBadResponse exception which is raised when
|
382
|
+
# there is a protocol error.
|
277
383
|
#
|
278
384
|
class HTTP < Protocol
|
279
385
|
|
280
386
|
# :stopdoc:
|
281
|
-
Revision = %q$Revision$.split[1]
|
387
|
+
Revision = %q$Revision: 42169 $.split[1]
|
282
388
|
HTTPVersion = '1.1'
|
283
|
-
|
389
|
+
begin
|
390
|
+
require 'zlib'
|
391
|
+
require 'stringio' #for our purposes (unpacking gzip) lump these together
|
392
|
+
HAVE_ZLIB=true
|
393
|
+
rescue LoadError
|
394
|
+
HAVE_ZLIB=false
|
395
|
+
end
|
284
396
|
# :startdoc:
|
285
397
|
|
286
|
-
# Turns on net/http 1.2 (
|
287
|
-
# Defaults to ON in
|
288
|
-
#
|
289
|
-
# I strongly recommend to call this method always.
|
290
|
-
#
|
291
|
-
# require 'net/http'
|
292
|
-
# Net::HTTP.version_1_2
|
293
|
-
#
|
398
|
+
# Turns on net/http 1.2 (Ruby 1.8) features.
|
399
|
+
# Defaults to ON in Ruby 1.8 or later.
|
294
400
|
def HTTP.version_1_2
|
295
|
-
|
401
|
+
true
|
296
402
|
end
|
297
403
|
|
298
|
-
#
|
299
|
-
# Defaults to OFF in ruby 1.8.
|
300
|
-
def HTTP.version_1_1
|
301
|
-
@newimpl = false
|
302
|
-
end
|
303
|
-
|
304
|
-
# true if net/http is in version 1.2 mode.
|
404
|
+
# Returns true if net/http is in version 1.2 mode.
|
305
405
|
# Defaults to true.
|
306
406
|
def HTTP.version_1_2?
|
307
|
-
|
407
|
+
true
|
308
408
|
end
|
309
409
|
|
310
|
-
|
311
|
-
|
312
|
-
def HTTP.version_1_1?
|
313
|
-
not @newimpl
|
410
|
+
def HTTP.version_1_1? #:nodoc:
|
411
|
+
false
|
314
412
|
end
|
315
413
|
|
316
414
|
class << HTTP
|
@@ -323,11 +421,11 @@ module Net #:nodoc:
|
|
323
421
|
#
|
324
422
|
|
325
423
|
#
|
326
|
-
#
|
327
|
-
# target can either be specified as
|
328
|
-
# (+host+, +path+, +port+ = 80); so:
|
424
|
+
# Gets the body text from the target and outputs it to $stdout. The
|
425
|
+
# target can either be specified as
|
426
|
+
# (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
329
427
|
#
|
330
|
-
# Net::HTTP.get_print URI
|
428
|
+
# Net::HTTP.get_print URI('http://www.example.com/index.html')
|
331
429
|
#
|
332
430
|
# or:
|
333
431
|
#
|
@@ -342,11 +440,11 @@ module Net #:nodoc:
|
|
342
440
|
nil
|
343
441
|
end
|
344
442
|
|
345
|
-
#
|
443
|
+
# Sends a GET request to the target and returns the HTTP response
|
346
444
|
# as a string. The target can either be specified as
|
347
445
|
# (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
348
446
|
#
|
349
|
-
# print Net::HTTP.get(URI
|
447
|
+
# print Net::HTTP.get(URI('http://www.example.com/index.html'))
|
350
448
|
#
|
351
449
|
# or:
|
352
450
|
#
|
@@ -356,11 +454,11 @@ module Net #:nodoc:
|
|
356
454
|
get_response(uri_or_host, path, port).body
|
357
455
|
end
|
358
456
|
|
359
|
-
#
|
457
|
+
# Sends a GET request to the target and returns the HTTP response
|
360
458
|
# as a Net::HTTPResponse object. The target can either be specified as
|
361
459
|
# (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
362
460
|
#
|
363
|
-
# res = Net::HTTP.get_response(URI
|
461
|
+
# res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
|
364
462
|
# print res.body
|
365
463
|
#
|
366
464
|
# or:
|
@@ -376,32 +474,37 @@ module Net #:nodoc:
|
|
376
474
|
}
|
377
475
|
else
|
378
476
|
uri = uri_or_host
|
379
|
-
|
380
|
-
|
477
|
+
start(uri.hostname, uri.port,
|
478
|
+
:use_ssl => uri.scheme == 'https') {|http|
|
479
|
+
return http.request_get(uri, &block)
|
381
480
|
}
|
382
481
|
end
|
383
482
|
end
|
384
483
|
|
385
|
-
# Posts HTML form data to the
|
386
|
-
#
|
484
|
+
# Posts HTML form data to the specified URI object.
|
485
|
+
# The form data must be provided as a Hash mapping from String to String.
|
486
|
+
# Example:
|
387
487
|
#
|
388
488
|
# { "cmd" => "search", "q" => "ruby", "max" => "50" }
|
389
489
|
#
|
390
|
-
# This method also does Basic Authentication iff +
|
490
|
+
# This method also does Basic Authentication iff +url+.user exists.
|
491
|
+
# But userinfo for authentication is deprecated (RFC3986).
|
492
|
+
# So this feature will be removed.
|
391
493
|
#
|
392
494
|
# Example:
|
393
495
|
#
|
394
496
|
# require 'net/http'
|
395
497
|
# require 'uri'
|
396
498
|
#
|
397
|
-
# HTTP.post_form URI
|
398
|
-
#
|
499
|
+
# Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
|
500
|
+
# { "q" => "ruby", "max" => "50" }
|
399
501
|
#
|
400
502
|
def HTTP.post_form(url, params)
|
401
|
-
req = Post.new(url
|
503
|
+
req = Post.new(url)
|
402
504
|
req.form_data = params
|
403
505
|
req.basic_auth url.user, url.password if url.user
|
404
|
-
|
506
|
+
start(url.hostname, url.port,
|
507
|
+
:use_ssl => url.scheme == 'https' ) {|http|
|
405
508
|
http.request(req)
|
406
509
|
}
|
407
510
|
end
|
@@ -429,59 +532,146 @@ module Net #:nodoc:
|
|
429
532
|
BufferedIO
|
430
533
|
end
|
431
534
|
|
432
|
-
#
|
433
|
-
#
|
535
|
+
# :call-seq:
|
536
|
+
# HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
|
537
|
+
# HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &block)
|
538
|
+
#
|
539
|
+
# Creates a new Net::HTTP object, then additionally opens the TCP
|
540
|
+
# connection and HTTP session.
|
541
|
+
#
|
542
|
+
# Arguments are the following:
|
543
|
+
# _address_ :: hostname or IP address of the server
|
544
|
+
# _port_ :: port of the server
|
545
|
+
# _p_addr_ :: address of proxy
|
546
|
+
# _p_port_ :: port of proxy
|
547
|
+
# _p_user_ :: user of proxy
|
548
|
+
# _p_pass_ :: pass of proxy
|
549
|
+
# _opt_ :: optional hash
|
550
|
+
#
|
551
|
+
# _opt_ sets following values by its accessor.
|
552
|
+
# The keys are ca_file, ca_path, cert, cert_store, ciphers,
|
553
|
+
# close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout,
|
554
|
+
# ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
|
555
|
+
# If you set :use_ssl as true, you can use https and default value of
|
556
|
+
# verify_mode is set as OpenSSL::SSL::VERIFY_PEER.
|
557
|
+
#
|
558
|
+
# If the optional block is given, the newly
|
434
559
|
# created Net::HTTP object is passed to it and closed when the
|
435
560
|
# block finishes. In this case, the return value of this method
|
436
561
|
# is the return value of the block. If no block is given, the
|
437
562
|
# return value of this method is the newly created Net::HTTP object
|
438
|
-
# itself, and the caller is responsible for closing it upon completion
|
439
|
-
|
440
|
-
|
563
|
+
# itself, and the caller is responsible for closing it upon completion
|
564
|
+
# using the finish() method.
|
565
|
+
def HTTP.start(address, *arg, &block) # :yield: +http+
|
566
|
+
arg.pop if opt = Hash.try_convert(arg[-1])
|
567
|
+
port, p_addr, p_port, p_user, p_pass = *arg
|
568
|
+
port = https_default_port if !port && opt && opt[:use_ssl]
|
569
|
+
http = new(address, port, p_addr, p_port, p_user, p_pass)
|
570
|
+
|
571
|
+
if opt
|
572
|
+
if opt[:use_ssl]
|
573
|
+
opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
|
574
|
+
end
|
575
|
+
http.methods.grep(/\A(\w+)=\z/) do |meth|
|
576
|
+
key = $1.to_sym
|
577
|
+
opt.key?(key) or next
|
578
|
+
http.__send__(meth, opt[key])
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
http.start(&block)
|
441
583
|
end
|
442
584
|
|
443
585
|
class << HTTP
|
444
|
-
alias newobj new
|
445
|
-
end
|
586
|
+
alias newobj new # :nodoc:
|
587
|
+
end
|
588
|
+
|
589
|
+
# Creates a new Net::HTTP object without opening a TCP connection or
|
590
|
+
# HTTP session.
|
591
|
+
#
|
592
|
+
# The +address+ should be a DNS hostname or IP address, the +port+ is the
|
593
|
+
# port the server operates on. If no +port+ is given the default port for
|
594
|
+
# HTTP or HTTPS is used.
|
595
|
+
#
|
596
|
+
# If none of the +p_+ arguments are given, the proxy host and port are
|
597
|
+
# taken from the +http_proxy+ environment variable (or its uppercase
|
598
|
+
# equivalent) if present. If the proxy requires authentication you must
|
599
|
+
# supply it by hand. See URI::Generic#find_proxy for details of proxy
|
600
|
+
# detection from the environment. To disable proxy detection set +p_addr+
|
601
|
+
# to nil.
|
602
|
+
#
|
603
|
+
# If you are connecting to a custom proxy, +p_addr+ the DNS name or IP
|
604
|
+
# address of the proxy host, +p_port+ the port to use to access the proxy,
|
605
|
+
# and +p_user+ and +p_pass+ the username and password if authorization is
|
606
|
+
# required to use the proxy.
|
607
|
+
#
|
608
|
+
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
|
609
|
+
http = super address, port
|
610
|
+
|
611
|
+
if proxy_class? then # from Net::HTTP::Proxy()
|
612
|
+
http.proxy_from_env = @proxy_from_env
|
613
|
+
http.proxy_address = @proxy_address
|
614
|
+
http.proxy_port = @proxy_port
|
615
|
+
http.proxy_user = @proxy_user
|
616
|
+
http.proxy_pass = @proxy_pass
|
617
|
+
elsif p_addr == :ENV then
|
618
|
+
http.proxy_from_env = true
|
619
|
+
else
|
620
|
+
http.proxy_address = p_addr
|
621
|
+
http.proxy_port = p_port || default_port
|
622
|
+
http.proxy_user = p_user
|
623
|
+
http.proxy_pass = p_pass
|
624
|
+
end
|
446
625
|
|
447
|
-
|
448
|
-
# If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
|
449
|
-
# This method does not open the TCP connection.
|
450
|
-
def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
|
451
|
-
h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
|
452
|
-
h.instance_eval {
|
453
|
-
@newimpl = ::Net::HTTP.version_1_2?
|
454
|
-
}
|
455
|
-
h
|
626
|
+
http
|
456
627
|
end
|
457
628
|
|
458
|
-
# Creates a new Net::HTTP object for the specified
|
459
|
-
#
|
629
|
+
# Creates a new Net::HTTP object for the specified server address,
|
630
|
+
# without opening the TCP connection or initializing the HTTP session.
|
631
|
+
# The +address+ should be a DNS hostname or IP address.
|
460
632
|
def initialize(address, port = nil)
|
461
633
|
@address = address
|
462
634
|
@port = (port || HTTP.default_port)
|
635
|
+
@local_host = nil
|
636
|
+
@local_port = nil
|
463
637
|
@curr_http_version = HTTPVersion
|
464
|
-
@
|
638
|
+
@keep_alive_timeout = 2
|
639
|
+
@last_communicated = nil
|
465
640
|
@close_on_empty_response = false
|
466
641
|
@socket = nil
|
467
642
|
@started = false
|
468
643
|
@open_timeout = nil
|
469
644
|
@read_timeout = 60
|
645
|
+
@continue_timeout = nil
|
470
646
|
@debug_output = nil
|
647
|
+
|
648
|
+
@proxy_from_env = false
|
649
|
+
@proxy_uri = nil
|
650
|
+
@proxy_address = nil
|
651
|
+
@proxy_port = nil
|
652
|
+
@proxy_user = nil
|
653
|
+
@proxy_pass = nil
|
654
|
+
|
471
655
|
@use_ssl = false
|
472
656
|
@ssl_context = nil
|
657
|
+
@ssl_session = nil
|
658
|
+
@enable_post_connection_check = true
|
659
|
+
@sspi_enabled = false
|
660
|
+
SSL_IVNAMES.each do |ivname|
|
661
|
+
instance_variable_set ivname, nil
|
662
|
+
end
|
473
663
|
end
|
474
664
|
|
475
665
|
def inspect
|
476
666
|
"#<#{self.class} #{@address}:#{@port} open=#{started?}>"
|
477
667
|
end
|
478
668
|
|
479
|
-
# *WARNING* This method
|
669
|
+
# *WARNING* This method opens a serious security hole.
|
480
670
|
# Never use this method in production code.
|
481
671
|
#
|
482
|
-
#
|
672
|
+
# Sets an output stream for debugging.
|
483
673
|
#
|
484
|
-
# http = Net::HTTP.new
|
674
|
+
# http = Net::HTTP.new(hostname)
|
485
675
|
# http.set_debug_output $stderr
|
486
676
|
# http.start { .... }
|
487
677
|
#
|
@@ -490,20 +680,34 @@ module Net #:nodoc:
|
|
490
680
|
@debug_output = output
|
491
681
|
end
|
492
682
|
|
493
|
-
# The host name to connect to.
|
683
|
+
# The DNS host name or IP address to connect to.
|
494
684
|
attr_reader :address
|
495
685
|
|
496
686
|
# The port number to connect to.
|
497
687
|
attr_reader :port
|
498
688
|
|
499
|
-
#
|
500
|
-
|
501
|
-
|
689
|
+
# The local host used to estabilish the connection.
|
690
|
+
attr_accessor :local_host
|
691
|
+
|
692
|
+
# The local port used to estabilish the connection.
|
693
|
+
attr_accessor :local_port
|
694
|
+
|
695
|
+
attr_writer :proxy_from_env
|
696
|
+
attr_writer :proxy_address
|
697
|
+
attr_writer :proxy_port
|
698
|
+
attr_writer :proxy_user
|
699
|
+
attr_writer :proxy_pass
|
700
|
+
|
701
|
+
# Number of seconds to wait for the connection to open. Any number
|
702
|
+
# may be used, including Floats for fractional seconds. If the HTTP
|
703
|
+
# object cannot open a connection in this many seconds, it raises a
|
704
|
+
# Net::OpenTimeout exception. The default value is +nil+.
|
502
705
|
attr_accessor :open_timeout
|
503
706
|
|
504
|
-
#
|
505
|
-
#
|
506
|
-
#
|
707
|
+
# Number of seconds to wait for one block to be read (via one read(2)
|
708
|
+
# call). Any number may be used, including Floats for fractional
|
709
|
+
# seconds. If the HTTP object cannot read data in this many seconds,
|
710
|
+
# it raises a Net::ReadTimeout exception. The default value is 60 seconds.
|
507
711
|
attr_reader :read_timeout
|
508
712
|
|
509
713
|
# Setter for the read_timeout attribute.
|
@@ -512,7 +716,24 @@ module Net #:nodoc:
|
|
512
716
|
@read_timeout = sec
|
513
717
|
end
|
514
718
|
|
515
|
-
#
|
719
|
+
# Seconds to wait for 100 Continue response. If the HTTP object does not
|
720
|
+
# receive a response in this many seconds it sends the request body. The
|
721
|
+
# default value is +nil+.
|
722
|
+
attr_reader :continue_timeout
|
723
|
+
|
724
|
+
# Setter for the continue_timeout attribute.
|
725
|
+
def continue_timeout=(sec)
|
726
|
+
@socket.continue_timeout = sec if @socket
|
727
|
+
@continue_timeout = sec
|
728
|
+
end
|
729
|
+
|
730
|
+
# Seconds to reuse the connection of the previous request.
|
731
|
+
# If the idle time is less than this Keep-Alive Timeout,
|
732
|
+
# Net::HTTP reuses the TCP/IP socket used by the previous communication.
|
733
|
+
# The default value is 2 seconds.
|
734
|
+
attr_accessor :keep_alive_timeout
|
735
|
+
|
736
|
+
# Returns true if the HTTP session has been started.
|
516
737
|
def started?
|
517
738
|
@started
|
518
739
|
end
|
@@ -521,19 +742,107 @@ module Net #:nodoc:
|
|
521
742
|
|
522
743
|
attr_accessor :close_on_empty_response
|
523
744
|
|
524
|
-
#
|
745
|
+
# Returns true if SSL/TLS is being used with HTTP.
|
525
746
|
def use_ssl?
|
526
|
-
|
747
|
+
@use_ssl
|
748
|
+
end
|
749
|
+
|
750
|
+
# Turn on/off SSL.
|
751
|
+
# This flag must be set before starting session.
|
752
|
+
# If you change use_ssl value after session started,
|
753
|
+
# a Net::HTTP object raises IOError.
|
754
|
+
def use_ssl=(flag)
|
755
|
+
flag = flag ? true : false
|
756
|
+
if started? and @use_ssl != flag
|
757
|
+
raise IOError, "use_ssl value changed, but session already started"
|
758
|
+
end
|
759
|
+
@use_ssl = flag
|
760
|
+
end
|
761
|
+
|
762
|
+
SSL_IVNAMES = [
|
763
|
+
:@ca_file,
|
764
|
+
:@ca_path,
|
765
|
+
:@cert,
|
766
|
+
:@cert_store,
|
767
|
+
:@ciphers,
|
768
|
+
:@key,
|
769
|
+
:@ssl_timeout,
|
770
|
+
:@ssl_version,
|
771
|
+
:@verify_callback,
|
772
|
+
:@verify_depth,
|
773
|
+
:@verify_mode,
|
774
|
+
]
|
775
|
+
SSL_ATTRIBUTES = [
|
776
|
+
:ca_file,
|
777
|
+
:ca_path,
|
778
|
+
:cert,
|
779
|
+
:cert_store,
|
780
|
+
:ciphers,
|
781
|
+
:key,
|
782
|
+
:ssl_timeout,
|
783
|
+
:ssl_version,
|
784
|
+
:verify_callback,
|
785
|
+
:verify_depth,
|
786
|
+
:verify_mode,
|
787
|
+
]
|
788
|
+
|
789
|
+
# Sets path of a CA certification file in PEM format.
|
790
|
+
#
|
791
|
+
# The file can contain several CA certificates.
|
792
|
+
attr_accessor :ca_file
|
793
|
+
|
794
|
+
# Sets path of a CA certification directory containing certifications in
|
795
|
+
# PEM format.
|
796
|
+
attr_accessor :ca_path
|
797
|
+
|
798
|
+
# Sets an OpenSSL::X509::Certificate object as client certificate.
|
799
|
+
# (This method is appeared in Michal Rokos's OpenSSL extension).
|
800
|
+
attr_accessor :cert
|
801
|
+
|
802
|
+
# Sets the X509::Store to verify peer certificate.
|
803
|
+
attr_accessor :cert_store
|
804
|
+
|
805
|
+
# Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers=
|
806
|
+
attr_accessor :ciphers
|
807
|
+
|
808
|
+
# Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
|
809
|
+
# (This method is appeared in Michal Rokos's OpenSSL extension.)
|
810
|
+
attr_accessor :key
|
811
|
+
|
812
|
+
# Sets the SSL timeout seconds.
|
813
|
+
attr_accessor :ssl_timeout
|
814
|
+
|
815
|
+
# Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version=
|
816
|
+
attr_accessor :ssl_version
|
817
|
+
|
818
|
+
# Sets the verify callback for the server certification verification.
|
819
|
+
attr_accessor :verify_callback
|
820
|
+
|
821
|
+
# Sets the maximum depth for the certificate chain verification.
|
822
|
+
attr_accessor :verify_depth
|
823
|
+
|
824
|
+
# Sets the flags for server the certification verification at beginning of
|
825
|
+
# SSL/TLS session.
|
826
|
+
#
|
827
|
+
# OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable.
|
828
|
+
attr_accessor :verify_mode
|
829
|
+
|
830
|
+
# Returns the X.509 certificates the server presented.
|
831
|
+
def peer_cert
|
832
|
+
if not use_ssl? or not @socket
|
833
|
+
return nil
|
834
|
+
end
|
835
|
+
@socket.io.peer_cert
|
527
836
|
end
|
528
837
|
|
529
|
-
# Opens TCP connection and HTTP session.
|
838
|
+
# Opens a TCP connection and HTTP session.
|
530
839
|
#
|
531
|
-
# When this method is called with block,
|
532
|
-
# to the block and closes the TCP connection
|
533
|
-
# after the block executed.
|
840
|
+
# When this method is called with a block, it passes the Net::HTTP
|
841
|
+
# object to the block, and closes the TCP connection and HTTP session
|
842
|
+
# after the block has been executed.
|
534
843
|
#
|
535
|
-
# When called with a block, returns the return value of the
|
536
|
-
# block; otherwise, returns self.
|
844
|
+
# When called with a block, it returns the return value of the
|
845
|
+
# block; otherwise, it returns self.
|
537
846
|
#
|
538
847
|
def start # :yield: http
|
539
848
|
raise IOError, 'HTTP session already opened' if @started
|
@@ -556,36 +865,66 @@ module Net #:nodoc:
|
|
556
865
|
private :do_start
|
557
866
|
|
558
867
|
def connect
|
559
|
-
|
560
|
-
|
868
|
+
if proxy? then
|
869
|
+
conn_address = proxy_address
|
870
|
+
conn_port = proxy_port
|
871
|
+
else
|
872
|
+
conn_address = address
|
873
|
+
conn_port = port
|
874
|
+
end
|
875
|
+
|
876
|
+
D "opening connection to #{conn_address}:#{conn_port}..."
|
877
|
+
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
|
878
|
+
TCPSocket.open(conn_address, conn_port, @local_host, @local_port)
|
879
|
+
}
|
880
|
+
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
561
881
|
D "opened"
|
562
882
|
if use_ssl?
|
563
|
-
|
564
|
-
|
565
|
-
|
883
|
+
ssl_parameters = Hash.new
|
884
|
+
iv_list = instance_variables
|
885
|
+
SSL_IVNAMES.each_with_index do |ivname, i|
|
886
|
+
if iv_list.include?(ivname) and
|
887
|
+
value = instance_variable_get(ivname)
|
888
|
+
ssl_parameters[SSL_ATTRIBUTES[i]] = value if value
|
889
|
+
end
|
566
890
|
end
|
891
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
892
|
+
@ssl_context.set_params(ssl_parameters)
|
893
|
+
D "starting SSL for #{conn_address}:#{conn_port}..."
|
567
894
|
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
568
895
|
s.sync_close = true
|
896
|
+
D "SSL established"
|
569
897
|
end
|
570
898
|
@socket = BufferedIO.new(s)
|
571
899
|
@socket.read_timeout = @read_timeout
|
900
|
+
@socket.continue_timeout = @continue_timeout
|
572
901
|
@socket.debug_output = @debug_output
|
573
902
|
if use_ssl?
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
903
|
+
begin
|
904
|
+
if proxy?
|
905
|
+
buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n"
|
906
|
+
buf << "Host: #{@address}:#{@port}\r\n"
|
907
|
+
if proxy_user
|
908
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
909
|
+
credential.delete!("\r\n")
|
910
|
+
buf << "Proxy-Authorization: Basic #{credential}\r\n"
|
911
|
+
end
|
912
|
+
buf << "\r\n"
|
913
|
+
@socket.write(buf)
|
914
|
+
HTTPResponse.read_new(@socket).value
|
582
915
|
end
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
916
|
+
s.session = @ssl_session if @ssl_session
|
917
|
+
# Server Name Indication (SNI) RFC 3546
|
918
|
+
s.hostname = @address if s.respond_to? :hostname=
|
919
|
+
Timeout.timeout(@open_timeout, Net::OpenTimeout) { s.connect }
|
920
|
+
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
921
|
+
s.post_connection_check(@address)
|
922
|
+
end
|
923
|
+
@ssl_session = s.session
|
924
|
+
rescue => exception
|
925
|
+
D "Conn close because of connect error #{exception}"
|
926
|
+
@socket.close if @socket and not @socket.closed?
|
927
|
+
raise exception
|
589
928
|
end
|
590
929
|
end
|
591
930
|
on_connect
|
@@ -596,8 +935,8 @@ module Net #:nodoc:
|
|
596
935
|
end
|
597
936
|
private :on_connect
|
598
937
|
|
599
|
-
# Finishes HTTP session and closes TCP connection.
|
600
|
-
# Raises IOError if not started.
|
938
|
+
# Finishes the HTTP session and closes the TCP connection.
|
939
|
+
# Raises IOError if the session has not been started.
|
601
940
|
def finish
|
602
941
|
raise IOError, 'HTTP session not yet started' unless started?
|
603
942
|
do_finish
|
@@ -618,77 +957,103 @@ module Net #:nodoc:
|
|
618
957
|
|
619
958
|
# no proxy
|
620
959
|
@is_proxy_class = false
|
960
|
+
@proxy_from_env = false
|
621
961
|
@proxy_addr = nil
|
622
962
|
@proxy_port = nil
|
623
963
|
@proxy_user = nil
|
624
964
|
@proxy_pass = nil
|
625
965
|
|
626
|
-
# Creates an HTTP proxy class
|
627
|
-
#
|
628
|
-
# if authorization on proxy server is required.
|
629
|
-
# You can replace the HTTP class with created proxy class.
|
966
|
+
# Creates an HTTP proxy class which behaves like Net::HTTP, but
|
967
|
+
# performs all access via the specified proxy.
|
630
968
|
#
|
631
|
-
#
|
632
|
-
#
|
633
|
-
|
634
|
-
# proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
|
635
|
-
# :
|
636
|
-
# proxy_class.start('www.ruby-lang.org') {|http|
|
637
|
-
# # connecting proxy.foo.org:8080
|
638
|
-
# :
|
639
|
-
# }
|
640
|
-
#
|
641
|
-
def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
|
969
|
+
# This class is obsolete. You may pass these same parameters directly to
|
970
|
+
# Net::HTTP.new. See Net::HTTP.new for details of the arguments.
|
971
|
+
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
|
642
972
|
return self unless p_addr
|
643
|
-
|
644
|
-
|
645
|
-
proxyclass.module_eval {
|
646
|
-
include delta
|
647
|
-
# with proxy
|
973
|
+
|
974
|
+
Class.new(self) {
|
648
975
|
@is_proxy_class = true
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
976
|
+
|
977
|
+
if p_addr == :ENV then
|
978
|
+
@proxy_from_env = true
|
979
|
+
@proxy_address = nil
|
980
|
+
@proxy_port = nil
|
981
|
+
else
|
982
|
+
@proxy_from_env = false
|
983
|
+
@proxy_address = p_addr
|
984
|
+
@proxy_port = p_port || default_port
|
985
|
+
end
|
986
|
+
|
987
|
+
@proxy_user = p_user
|
988
|
+
@proxy_pass = p_pass
|
653
989
|
}
|
654
|
-
proxyclass
|
655
990
|
end
|
656
991
|
|
657
992
|
class << HTTP
|
658
993
|
# returns true if self is a class which was created by HTTP::Proxy.
|
659
994
|
def proxy_class?
|
660
|
-
@is_proxy_class
|
995
|
+
defined?(@is_proxy_class) ? @is_proxy_class : false
|
661
996
|
end
|
662
997
|
|
998
|
+
# Address of proxy host. If Net::HTTP does not use a proxy, nil.
|
663
999
|
attr_reader :proxy_address
|
1000
|
+
|
1001
|
+
# Port number of proxy host. If Net::HTTP does not use a proxy, nil.
|
664
1002
|
attr_reader :proxy_port
|
1003
|
+
|
1004
|
+
# User name for accessing proxy. If Net::HTTP does not use a proxy, nil.
|
665
1005
|
attr_reader :proxy_user
|
1006
|
+
|
1007
|
+
# User password for accessing proxy. If Net::HTTP does not use a proxy,
|
1008
|
+
# nil.
|
666
1009
|
attr_reader :proxy_pass
|
667
1010
|
end
|
668
1011
|
|
669
|
-
# True if
|
1012
|
+
# True if requests for this connection will be proxied
|
670
1013
|
def proxy?
|
671
|
-
|
1014
|
+
!!if @proxy_from_env then
|
1015
|
+
proxy_uri
|
1016
|
+
else
|
1017
|
+
@proxy_address
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# True if the proxy for this connection is determined from the environment
|
1022
|
+
def proxy_from_env?
|
1023
|
+
@proxy_from_env
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
# The proxy URI determined from the environment for this connection.
|
1027
|
+
def proxy_uri # :nodoc:
|
1028
|
+
@proxy_uri ||= URI("http://#{address}:#{port}").find_proxy
|
672
1029
|
end
|
673
1030
|
|
674
|
-
#
|
1031
|
+
# The address of the proxy server, if one is configured.
|
675
1032
|
def proxy_address
|
676
|
-
|
1033
|
+
if @proxy_from_env then
|
1034
|
+
proxy_uri && proxy_uri.hostname
|
1035
|
+
else
|
1036
|
+
@proxy_address
|
1037
|
+
end
|
677
1038
|
end
|
678
1039
|
|
679
|
-
#
|
1040
|
+
# The port of the proxy server, if one is configured.
|
680
1041
|
def proxy_port
|
681
|
-
|
1042
|
+
if @proxy_from_env then
|
1043
|
+
proxy_uri && proxy_uri.port
|
1044
|
+
else
|
1045
|
+
@proxy_port
|
1046
|
+
end
|
682
1047
|
end
|
683
1048
|
|
684
|
-
#
|
1049
|
+
# The proxy username, if one is configured
|
685
1050
|
def proxy_user
|
686
|
-
|
1051
|
+
@proxy_user
|
687
1052
|
end
|
688
1053
|
|
689
|
-
#
|
1054
|
+
# The proxy password, if one is configured
|
690
1055
|
def proxy_pass
|
691
|
-
|
1056
|
+
@proxy_pass
|
692
1057
|
end
|
693
1058
|
|
694
1059
|
alias proxyaddr proxy_address #:nodoc: obsolete
|
@@ -696,33 +1061,21 @@ module Net #:nodoc:
|
|
696
1061
|
|
697
1062
|
private
|
698
1063
|
|
699
|
-
# without proxy
|
1064
|
+
# without proxy, obsolete
|
700
1065
|
|
701
|
-
def conn_address
|
1066
|
+
def conn_address # :nodoc:
|
702
1067
|
address()
|
703
1068
|
end
|
704
1069
|
|
705
|
-
def conn_port
|
1070
|
+
def conn_port # :nodoc:
|
706
1071
|
port()
|
707
1072
|
end
|
708
1073
|
|
709
1074
|
def edit_path(path)
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
private
|
715
|
-
|
716
|
-
def conn_address
|
717
|
-
proxy_address()
|
718
|
-
end
|
719
|
-
|
720
|
-
def conn_port
|
721
|
-
proxy_port()
|
722
|
-
end
|
723
|
-
|
724
|
-
def edit_path(path)
|
725
|
-
use_ssl? ? path : "http://#{addr_port()}#{path}"
|
1075
|
+
if proxy? and not use_ssl? then
|
1076
|
+
"http://#{addr_port}#{path}"
|
1077
|
+
else
|
1078
|
+
path
|
726
1079
|
end
|
727
1080
|
end
|
728
1081
|
|
@@ -732,13 +1085,23 @@ module Net #:nodoc:
|
|
732
1085
|
|
733
1086
|
public
|
734
1087
|
|
735
|
-
#
|
736
|
-
#
|
1088
|
+
# Retrieves data from +path+ on the connected-to host which may be an
|
1089
|
+
# absolute path String or a URI to extract the path from.
|
1090
|
+
#
|
1091
|
+
# +initheader+ must be a Hash like { 'Accept' => '*/*', ... },
|
1092
|
+
# and it defaults to an empty hash.
|
1093
|
+
# If +initheader+ doesn't have the key 'accept-encoding', then
|
1094
|
+
# a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used,
|
1095
|
+
# so that gzip compression is used in preference to deflate
|
1096
|
+
# compression, which is used in preference to no compression.
|
1097
|
+
# Ruby doesn't have libraries to support the compress (Lempel-Ziv)
|
1098
|
+
# compression, so that is not supported. The intent of this is
|
1099
|
+
# to reduce bandwidth by default. If this routine sets up
|
1100
|
+
# compression, then it does the decompression also, removing
|
1101
|
+
# the header as well to prevent confusion. Otherwise
|
1102
|
+
# it leaves the body as it found it.
|
737
1103
|
#
|
738
|
-
#
|
739
|
-
# a Net::HTTPResponse object and the entity body string.
|
740
|
-
# In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
|
741
|
-
# object.
|
1104
|
+
# This method returns a Net::HTTPResponse object.
|
742
1105
|
#
|
743
1106
|
# If called with a block, yields each fragment of the
|
744
1107
|
# entity body in turn as a string as it is read from
|
@@ -748,16 +1111,8 @@ module Net #:nodoc:
|
|
748
1111
|
# +dest+ argument is obsolete.
|
749
1112
|
# It still works but you must not use it.
|
750
1113
|
#
|
751
|
-
#
|
752
|
-
# 3xx (redirect). In this case you can get a HTTPResponse object
|
753
|
-
# by "anException.response".
|
754
|
-
#
|
755
|
-
# In version 1.2, this method never raises exception.
|
756
|
-
#
|
757
|
-
# # version 1.1 (bundled with Ruby 1.6)
|
758
|
-
# response, body = http.get('/index.html')
|
1114
|
+
# This method never raises an exception.
|
759
1115
|
#
|
760
|
-
# # version 1.2 (bundled with Ruby 1.8 or later)
|
761
1116
|
# response = http.get('/index.html')
|
762
1117
|
#
|
763
1118
|
# # using block
|
@@ -767,17 +1122,12 @@ module Net #:nodoc:
|
|
767
1122
|
# end
|
768
1123
|
# }
|
769
1124
|
#
|
770
|
-
def get(path, initheader =
|
1125
|
+
def get(path, initheader = {}, dest = nil, &block) # :yield: +body_segment+
|
771
1126
|
res = nil
|
772
1127
|
request(Get.new(path, initheader)) {|r|
|
773
1128
|
r.read_body dest, &block
|
774
1129
|
res = r
|
775
1130
|
}
|
776
|
-
unless @newimpl
|
777
|
-
res.value
|
778
|
-
return res, res.body
|
779
|
-
end
|
780
|
-
|
781
1131
|
res
|
782
1132
|
end
|
783
1133
|
|
@@ -786,10 +1136,7 @@ module Net #:nodoc:
|
|
786
1136
|
#
|
787
1137
|
# This method returns a Net::HTTPResponse object.
|
788
1138
|
#
|
789
|
-
#
|
790
|
-
# 3xx (redirect). On the case you can get a HTTPResponse object
|
791
|
-
# by "anException.response".
|
792
|
-
# In version 1.2, this method never raises an exception.
|
1139
|
+
# This method never raises an exception.
|
793
1140
|
#
|
794
1141
|
# response = nil
|
795
1142
|
# Net::HTTP.start('some.www.server', 80) {|http|
|
@@ -798,35 +1145,24 @@ module Net #:nodoc:
|
|
798
1145
|
# p response['content-type']
|
799
1146
|
#
|
800
1147
|
def head(path, initheader = nil)
|
801
|
-
|
802
|
-
res.value unless @newimpl
|
803
|
-
res
|
1148
|
+
request(Head.new(path, initheader))
|
804
1149
|
end
|
805
1150
|
|
806
1151
|
# Posts +data+ (must be a String) to +path+. +header+ must be a Hash
|
807
1152
|
# like { 'Accept' => '*/*', ... }.
|
808
1153
|
#
|
809
|
-
#
|
810
|
-
# Net::HTTPResponse object and an entity body string.
|
811
|
-
# In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
|
1154
|
+
# This method returns a Net::HTTPResponse object.
|
812
1155
|
#
|
813
1156
|
# If called with a block, yields each fragment of the
|
814
|
-
# entity body in turn as a string as it
|
1157
|
+
# entity body in turn as a string as it is read from
|
815
1158
|
# the socket. Note that in this case, the returned response
|
816
1159
|
# object will *not* contain a (meaningful) body.
|
817
1160
|
#
|
818
1161
|
# +dest+ argument is obsolete.
|
819
1162
|
# It still works but you must not use it.
|
820
1163
|
#
|
821
|
-
#
|
822
|
-
# 3xx (redirect). In this case you can get an HTTPResponse object
|
823
|
-
# by "anException.response".
|
824
|
-
# In version 1.2, this method never raises exception.
|
825
|
-
#
|
826
|
-
# # version 1.1
|
827
|
-
# response, body = http.post('/cgi-bin/search.rb', 'query=foo')
|
1164
|
+
# This method never raises exception.
|
828
1165
|
#
|
829
|
-
# # version 1.2
|
830
1166
|
# response = http.post('/cgi-bin/search.rb', 'query=foo')
|
831
1167
|
#
|
832
1168
|
# # using block
|
@@ -841,22 +1177,17 @@ module Net #:nodoc:
|
|
841
1177
|
# "application/x-www-form-urlencoded" by default.
|
842
1178
|
#
|
843
1179
|
def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
return res, res.body
|
852
|
-
end
|
853
|
-
res
|
1180
|
+
send_entity(path, data, initheader, dest, Post, &block)
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
# Sends a PATCH request to the +path+ and gets a response,
|
1184
|
+
# as an HTTPResponse object.
|
1185
|
+
def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
1186
|
+
send_entity(path, data, initheader, dest, Patch, &block)
|
854
1187
|
end
|
855
1188
|
|
856
1189
|
def put(path, data, initheader = nil) #:nodoc:
|
857
|
-
|
858
|
-
res.value unless @newimpl
|
859
|
-
res
|
1190
|
+
request(Put.new(path, initheader), data)
|
860
1191
|
end
|
861
1192
|
|
862
1193
|
# Sends a PROPPATCH request to the +path+ and gets a response,
|
@@ -919,12 +1250,12 @@ module Net #:nodoc:
|
|
919
1250
|
request(Trace.new(path, initheader))
|
920
1251
|
end
|
921
1252
|
|
922
|
-
# Sends a GET request to the +path
|
923
|
-
# as
|
1253
|
+
# Sends a GET request to the +path+.
|
1254
|
+
# Returns the response as a Net::HTTPResponse object.
|
924
1255
|
#
|
925
|
-
# When called with a block,
|
926
|
-
# The body of
|
927
|
-
# the
|
1256
|
+
# When called with a block, passes an HTTPResponse object to the block.
|
1257
|
+
# The body of the response will not have been read yet;
|
1258
|
+
# the block can process it using HTTPResponse#read_body,
|
928
1259
|
# if desired.
|
929
1260
|
#
|
930
1261
|
# Returns the response.
|
@@ -932,11 +1263,11 @@ module Net #:nodoc:
|
|
932
1263
|
# This method never raises Net::* exceptions.
|
933
1264
|
#
|
934
1265
|
# response = http.request_get('/index.html')
|
935
|
-
# # The entity body is already read
|
1266
|
+
# # The entity body is already read in this case.
|
936
1267
|
# p response['content-type']
|
937
1268
|
# puts response.body
|
938
1269
|
#
|
939
|
-
# #
|
1270
|
+
# # Using a block
|
940
1271
|
# http.request_get('/index.html') {|response|
|
941
1272
|
# p response['content-type']
|
942
1273
|
# response.read_body do |str| # read body now
|
@@ -948,8 +1279,8 @@ module Net #:nodoc:
|
|
948
1279
|
request(Get.new(path, initheader), &block)
|
949
1280
|
end
|
950
1281
|
|
951
|
-
# Sends a HEAD request to the +path+ and
|
952
|
-
# as
|
1282
|
+
# Sends a HEAD request to the +path+ and returns the response
|
1283
|
+
# as a Net::HTTPResponse object.
|
953
1284
|
#
|
954
1285
|
# Returns the response.
|
955
1286
|
#
|
@@ -962,13 +1293,13 @@ module Net #:nodoc:
|
|
962
1293
|
request(Head.new(path, initheader), &block)
|
963
1294
|
end
|
964
1295
|
|
965
|
-
# Sends a POST request to the +path
|
966
|
-
# as an HTTPResponse object.
|
1296
|
+
# Sends a POST request to the +path+.
|
967
1297
|
#
|
968
|
-
#
|
969
|
-
#
|
970
|
-
# the
|
971
|
-
#
|
1298
|
+
# Returns the response as a Net::HTTPResponse object.
|
1299
|
+
#
|
1300
|
+
# When called with a block, the block is passed an HTTPResponse
|
1301
|
+
# object. The body of that response will not have been read yet;
|
1302
|
+
# the block can process it using HTTPResponse#read_body, if desired.
|
972
1303
|
#
|
973
1304
|
# Returns the response.
|
974
1305
|
#
|
@@ -977,7 +1308,7 @@ module Net #:nodoc:
|
|
977
1308
|
# # example
|
978
1309
|
# response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
|
979
1310
|
# p response.status
|
980
|
-
# puts response.body # body is already read
|
1311
|
+
# puts response.body # body is already read in this case
|
981
1312
|
#
|
982
1313
|
# # using block
|
983
1314
|
# http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
|
@@ -1003,9 +1334,9 @@ module Net #:nodoc:
|
|
1003
1334
|
|
1004
1335
|
|
1005
1336
|
# Sends an HTTP request to the HTTP server.
|
1006
|
-
#
|
1337
|
+
# Also sends a DATA string if +data+ is given.
|
1007
1338
|
#
|
1008
|
-
# Returns a HTTPResponse object.
|
1339
|
+
# Returns a Net::HTTPResponse object.
|
1009
1340
|
#
|
1010
1341
|
# This method never raises Net::* exceptions.
|
1011
1342
|
#
|
@@ -1017,16 +1348,18 @@ module Net #:nodoc:
|
|
1017
1348
|
request r, data
|
1018
1349
|
end
|
1019
1350
|
|
1020
|
-
# Sends an HTTPRequest object
|
1021
|
-
# This method also sends DATA string if REQUEST is a post/put request.
|
1022
|
-
# Giving DATA for get/head request causes ArgumentError.
|
1351
|
+
# Sends an HTTPRequest object +req+ to the HTTP server.
|
1023
1352
|
#
|
1024
|
-
#
|
1025
|
-
#
|
1026
|
-
#
|
1027
|
-
#
|
1353
|
+
# If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing
|
1354
|
+
# data, the data is also sent. Providing data for a Net::HTTP::Head or
|
1355
|
+
# Net::HTTP::Get request results in an ArgumentError.
|
1356
|
+
#
|
1357
|
+
# Returns an HTTPResponse object.
|
1028
1358
|
#
|
1029
|
-
#
|
1359
|
+
# When called with a block, passes an HTTPResponse object to the block.
|
1360
|
+
# The body of the response will not have been read yet;
|
1361
|
+
# the block can process it using HTTPResponse#read_body,
|
1362
|
+
# if desired.
|
1030
1363
|
#
|
1031
1364
|
# This method never raises Net::* exceptions.
|
1032
1365
|
#
|
@@ -1038,57 +1371,107 @@ module Net #:nodoc:
|
|
1038
1371
|
}
|
1039
1372
|
end
|
1040
1373
|
if proxy_user()
|
1041
|
-
unless use_ssl?
|
1042
|
-
req.proxy_basic_auth proxy_user(), proxy_pass()
|
1043
|
-
end
|
1374
|
+
req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
|
1044
1375
|
end
|
1045
|
-
|
1046
1376
|
req.set_body_internal body
|
1377
|
+
res = transport_request(req, &block)
|
1378
|
+
if sspi_auth?(res)
|
1379
|
+
sspi_auth(req)
|
1380
|
+
res = transport_request(req, &block)
|
1381
|
+
end
|
1382
|
+
res
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
private
|
1386
|
+
|
1387
|
+
# Executes a request which uses a representation
|
1388
|
+
# and returns its body.
|
1389
|
+
def send_entity(path, data, initheader, dest, type, &block)
|
1390
|
+
res = nil
|
1391
|
+
request(type.new(path, initheader), data) {|r|
|
1392
|
+
r.read_body dest, &block
|
1393
|
+
res = r
|
1394
|
+
}
|
1395
|
+
res
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:
|
1399
|
+
|
1400
|
+
def transport_request(req)
|
1401
|
+
count = 0
|
1047
1402
|
begin
|
1048
1403
|
begin_transport req
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1404
|
+
res = catch(:response) {
|
1405
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
1406
|
+
begin
|
1407
|
+
res = HTTPResponse.read_new(@socket)
|
1408
|
+
res.decode_content = req.decode_content
|
1409
|
+
end while res.kind_of?(HTTPContinue)
|
1410
|
+
|
1411
|
+
res.uri = req.uri
|
1412
|
+
|
1413
|
+
res.reading_body(@socket, req.response_body_permitted?) {
|
1414
|
+
yield res if block_given?
|
1415
|
+
}
|
1416
|
+
res
|
1055
1417
|
}
|
1056
|
-
|
1057
|
-
|
1418
|
+
rescue Net::OpenTimeout
|
1419
|
+
raise
|
1420
|
+
rescue Net::ReadTimeout, IOError, EOFError,
|
1421
|
+
Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE,
|
1422
|
+
# avoid a dependency on OpenSSL
|
1423
|
+
defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
|
1424
|
+
Timeout::Error => exception
|
1425
|
+
if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
|
1426
|
+
count += 1
|
1427
|
+
@socket.close if @socket and not @socket.closed?
|
1428
|
+
D "Conn close because of error #{exception}, and retry"
|
1429
|
+
retry
|
1430
|
+
end
|
1058
1431
|
D "Conn close because of error #{exception}"
|
1059
1432
|
@socket.close if @socket and not @socket.closed?
|
1060
|
-
raise
|
1433
|
+
raise
|
1061
1434
|
end
|
1062
1435
|
|
1436
|
+
end_transport req, res
|
1063
1437
|
res
|
1438
|
+
rescue => exception
|
1439
|
+
D "Conn close because of error #{exception}"
|
1440
|
+
@socket.close if @socket and not @socket.closed?
|
1441
|
+
raise exception
|
1064
1442
|
end
|
1065
1443
|
|
1066
|
-
private
|
1067
|
-
|
1068
1444
|
def begin_transport(req)
|
1069
1445
|
if @socket.closed?
|
1070
1446
|
connect
|
1447
|
+
elsif @last_communicated && @last_communicated + @keep_alive_timeout < Time.now
|
1448
|
+
D 'Conn close because of keep_alive_timeout'
|
1449
|
+
@socket.close
|
1450
|
+
connect
|
1071
1451
|
end
|
1072
|
-
|
1073
|
-
req['connection'] ||= 'close'
|
1074
|
-
end
|
1452
|
+
|
1075
1453
|
if not req.response_body_permitted? and @close_on_empty_response
|
1076
1454
|
req['connection'] ||= 'close'
|
1077
1455
|
end
|
1456
|
+
|
1457
|
+
host = req['host'] || address
|
1458
|
+
host = $1 if host =~ /(.*):\d+$/
|
1459
|
+
req.update_uri host, port, use_ssl?
|
1460
|
+
|
1078
1461
|
req['host'] ||= addr_port()
|
1079
1462
|
end
|
1080
1463
|
|
1081
1464
|
def end_transport(req, res)
|
1082
1465
|
@curr_http_version = res.http_version
|
1083
|
-
|
1466
|
+
@last_communicated = nil
|
1467
|
+
if @socket.closed?
|
1468
|
+
D 'Conn socket closed'
|
1469
|
+
elsif not res.body and @close_on_empty_response
|
1084
1470
|
D 'Conn close'
|
1085
1471
|
@socket.close
|
1086
1472
|
elsif keep_alive?(req, res)
|
1087
1473
|
D 'Conn keep-alive'
|
1088
|
-
|
1089
|
-
D 'Conn (but seems 1.0 server)'
|
1090
|
-
@seems_1_0_server = true
|
1091
|
-
end
|
1474
|
+
@last_communicated = Time.now
|
1092
1475
|
else
|
1093
1476
|
D 'Conn close'
|
1094
1477
|
@socket.close
|
@@ -1096,13 +1479,40 @@ module Net #:nodoc:
|
|
1096
1479
|
end
|
1097
1480
|
|
1098
1481
|
def keep_alive?(req, res)
|
1099
|
-
return false if
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1482
|
+
return false if req.connection_close?
|
1483
|
+
if @curr_http_version <= '1.0'
|
1484
|
+
res.connection_keep_alive?
|
1485
|
+
else # HTTP/1.1 or later
|
1486
|
+
not res.connection_close?
|
1487
|
+
end
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
def sspi_auth?(res)
|
1491
|
+
return false unless @sspi_enabled
|
1492
|
+
if res.kind_of?(HTTPProxyAuthenticationRequired) and
|
1493
|
+
proxy? and res["Proxy-Authenticate"].include?("Negotiate")
|
1494
|
+
begin
|
1495
|
+
require 'win32/sspi'
|
1496
|
+
true
|
1497
|
+
rescue LoadError
|
1498
|
+
false
|
1499
|
+
end
|
1500
|
+
else
|
1501
|
+
false
|
1502
|
+
end
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
def sspi_auth(req)
|
1506
|
+
n = Win32::SSPI::NegotiateAuth.new
|
1507
|
+
req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}"
|
1508
|
+
# Some versions of ISA will close the connection if this isn't present.
|
1509
|
+
req["Connection"] = "Keep-Alive"
|
1510
|
+
req["Proxy-Connection"] = "Keep-Alive"
|
1511
|
+
res = transport_request(req)
|
1512
|
+
authphrase = res["Proxy-Authenticate"] or return res
|
1513
|
+
req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}"
|
1514
|
+
rescue => err
|
1515
|
+
raise HTTPAuthenticationError.new('HTTP authentication failed', err)
|
1106
1516
|
end
|
1107
1517
|
|
1108
1518
|
#
|
@@ -1124,1167 +1534,22 @@ module Net #:nodoc:
|
|
1124
1534
|
@debug_output << msg
|
1125
1535
|
@debug_output << "\n"
|
1126
1536
|
end
|
1127
|
-
|
1128
1537
|
end
|
1129
1538
|
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
#
|
1134
|
-
# Header module.
|
1135
|
-
#
|
1136
|
-
# Provides access to @header in the mixed-into class as a hash-like
|
1137
|
-
# object, except with case-insensitive keys. Also provides
|
1138
|
-
# methods for accessing commonly-used header values in a more
|
1139
|
-
# convenient format.
|
1140
|
-
#
|
1141
|
-
module HTTPHeader
|
1142
|
-
|
1143
|
-
def initialize_http_header(initheader)
|
1144
|
-
@header = {}
|
1145
|
-
return unless initheader
|
1146
|
-
initheader.each do |key, value|
|
1147
|
-
warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
|
1148
|
-
@header[key.downcase] = [value.strip]
|
1149
|
-
end
|
1150
|
-
end
|
1151
|
-
|
1152
|
-
def size #:nodoc: obsolete
|
1153
|
-
@header.size
|
1154
|
-
end
|
1155
|
-
|
1156
|
-
alias length size #:nodoc: obsolete
|
1157
|
-
|
1158
|
-
# Returns the header field corresponding to the case-insensitive key.
|
1159
|
-
# For example, a key of "Content-Type" might return "text/html"
|
1160
|
-
def [](key)
|
1161
|
-
a = @header[key.downcase] or return nil
|
1162
|
-
a.join(', ')
|
1163
|
-
end
|
1164
|
-
|
1165
|
-
# Sets the header field corresponding to the case-insensitive key.
|
1166
|
-
def []=(key, val)
|
1167
|
-
unless val
|
1168
|
-
@header.delete key.downcase
|
1169
|
-
return val
|
1170
|
-
end
|
1171
|
-
@header[key.downcase] = [val]
|
1172
|
-
end
|
1173
|
-
|
1174
|
-
# [Ruby 1.8.3]
|
1175
|
-
# Adds header field instead of replace.
|
1176
|
-
# Second argument +val+ must be a String.
|
1177
|
-
# See also #[]=, #[] and #get_fields.
|
1178
|
-
#
|
1179
|
-
# request.add_field 'X-My-Header', 'a'
|
1180
|
-
# p request['X-My-Header'] #=> "a"
|
1181
|
-
# p request.get_fields('X-My-Header') #=> ["a"]
|
1182
|
-
# request.add_field 'X-My-Header', 'b'
|
1183
|
-
# p request['X-My-Header'] #=> "a, b"
|
1184
|
-
# p request.get_fields('X-My-Header') #=> ["a", "b"]
|
1185
|
-
# request.add_field 'X-My-Header', 'c'
|
1186
|
-
# p request['X-My-Header'] #=> "a, b, c"
|
1187
|
-
# p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
|
1188
|
-
#
|
1189
|
-
def add_field(key, val)
|
1190
|
-
if @header.key?(key.downcase)
|
1191
|
-
@header[key.downcase].push val
|
1192
|
-
else
|
1193
|
-
@header[key.downcase] = [val]
|
1194
|
-
end
|
1195
|
-
end
|
1196
|
-
|
1197
|
-
# [Ruby 1.8.3]
|
1198
|
-
# Returns an array of header field strings corresponding to the
|
1199
|
-
# case-insensitive +key+. This method allows you to get duplicated
|
1200
|
-
# header fields without any processing. See also #[].
|
1201
|
-
#
|
1202
|
-
# p response.get_fields('Set-Cookie')
|
1203
|
-
# #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
|
1204
|
-
# "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
|
1205
|
-
# p response['Set-Cookie']
|
1206
|
-
# #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
|
1207
|
-
#
|
1208
|
-
def get_fields(key)
|
1209
|
-
return nil unless @header[key.downcase]
|
1210
|
-
@header[key.downcase].dup
|
1211
|
-
end
|
1212
|
-
|
1213
|
-
# Returns the header field corresponding to the case-insensitive key.
|
1214
|
-
# Returns the default value +args+, or the result of the block, or nil,
|
1215
|
-
# if there's no header field named key. See Hash#fetch
|
1216
|
-
def fetch(key, *args, &block) #:yield: +key+
|
1217
|
-
a = @header.fetch(key.downcase, *args, &block)
|
1218
|
-
a.join(', ')
|
1219
|
-
end
|
1220
|
-
|
1221
|
-
# Iterates for each header names and values.
|
1222
|
-
def each_header #:yield: +key+, +value+
|
1223
|
-
return to_enum(__method__) unless block_given?
|
1224
|
-
@header.each do |k,va|
|
1225
|
-
yield k, va.join(', ')
|
1226
|
-
end
|
1227
|
-
end
|
1228
|
-
|
1229
|
-
alias each each_header
|
1230
|
-
|
1231
|
-
# Iterates for each header names.
|
1232
|
-
def each_name(&block) #:yield: +key+
|
1233
|
-
return to_enum(__method__) unless block_given?
|
1234
|
-
@header.each_key(&block)
|
1235
|
-
end
|
1236
|
-
|
1237
|
-
alias each_key each_name
|
1238
|
-
|
1239
|
-
# Iterates for each capitalized header names.
|
1240
|
-
def each_capitalized_name(&block) #:yield: +key+
|
1241
|
-
return to_enum(__method__) unless block_given?
|
1242
|
-
@header.each_key do |k|
|
1243
|
-
yield capitalize(k)
|
1244
|
-
end
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
# Iterates for each header values.
|
1248
|
-
def each_value #:yield: +value+
|
1249
|
-
return to_enum(__method__) unless block_given?
|
1250
|
-
@header.each_value do |va|
|
1251
|
-
yield va.join(', ')
|
1252
|
-
end
|
1253
|
-
end
|
1254
|
-
|
1255
|
-
# Removes a header field.
|
1256
|
-
def delete(key)
|
1257
|
-
@header.delete(key.downcase)
|
1258
|
-
end
|
1259
|
-
|
1260
|
-
# true if +key+ header exists.
|
1261
|
-
def key?(key)
|
1262
|
-
@header.key?(key.downcase)
|
1263
|
-
end
|
1264
|
-
|
1265
|
-
# Returns a Hash consist of header names and values.
|
1266
|
-
def to_hash
|
1267
|
-
@header.dup
|
1268
|
-
end
|
1269
|
-
|
1270
|
-
# As for #each_header, except the keys are provided in capitalized form.
|
1271
|
-
def each_capitalized
|
1272
|
-
return to_enum(__method__) unless block_given?
|
1273
|
-
@header.each do |k,v|
|
1274
|
-
yield capitalize(k), v.join(', ')
|
1275
|
-
end
|
1276
|
-
end
|
1277
|
-
|
1278
|
-
alias canonical_each each_capitalized
|
1539
|
+
end
|
1279
1540
|
|
1280
|
-
|
1281
|
-
name.split(/-/).map {|s| s.capitalize }.join('-')
|
1282
|
-
end
|
1283
|
-
private :capitalize
|
1284
|
-
|
1285
|
-
# Returns an Array of Range objects which represents Range: header field,
|
1286
|
-
# or +nil+ if there is no such header.
|
1287
|
-
def range
|
1288
|
-
return nil unless @header['range']
|
1289
|
-
self['Range'].split(/,/).map {|spec|
|
1290
|
-
m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
|
1291
|
-
raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
|
1292
|
-
d1 = m[1].to_i
|
1293
|
-
d2 = m[2].to_i
|
1294
|
-
if m[1] and m[2] then d1..d2
|
1295
|
-
elsif m[1] then d1..-1
|
1296
|
-
elsif m[2] then -d2..-1
|
1297
|
-
else
|
1298
|
-
raise HTTPHeaderSyntaxError, 'range is not specified'
|
1299
|
-
end
|
1300
|
-
}
|
1301
|
-
end
|
1302
|
-
|
1303
|
-
# Set Range: header from Range (arg r) or beginning index and
|
1304
|
-
# length from it (arg idx&len).
|
1305
|
-
#
|
1306
|
-
# req.range = (0..1023)
|
1307
|
-
# req.set_range 0, 1023
|
1308
|
-
#
|
1309
|
-
def set_range(r, e = nil)
|
1310
|
-
unless r
|
1311
|
-
@header.delete 'range'
|
1312
|
-
return r
|
1313
|
-
end
|
1314
|
-
r = (r...r+e) if e
|
1315
|
-
case r
|
1316
|
-
when Numeric
|
1317
|
-
n = r.to_i
|
1318
|
-
rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
|
1319
|
-
when Range
|
1320
|
-
first = r.first
|
1321
|
-
last = r.last
|
1322
|
-
last -= 1 if r.exclude_end?
|
1323
|
-
if last == -1
|
1324
|
-
rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
|
1325
|
-
else
|
1326
|
-
raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
|
1327
|
-
raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
|
1328
|
-
raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
|
1329
|
-
rangestr = "#{first}-#{last}"
|
1330
|
-
end
|
1331
|
-
else
|
1332
|
-
raise TypeError, 'Range/Integer is required'
|
1333
|
-
end
|
1334
|
-
@header['range'] = ["bytes=#{rangestr}"]
|
1335
|
-
r
|
1336
|
-
end
|
1337
|
-
|
1338
|
-
alias range= set_range
|
1541
|
+
require 'net/http/exceptions'
|
1339
1542
|
|
1340
|
-
|
1341
|
-
# or +nil+ if that field is not provided.
|
1342
|
-
def content_length
|
1343
|
-
return nil unless key?('Content-Length')
|
1344
|
-
len = self['Content-Length'].slice(/\d+/) or
|
1345
|
-
raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
|
1346
|
-
len.to_i
|
1347
|
-
end
|
1348
|
-
|
1349
|
-
def content_length=(len)
|
1350
|
-
unless len
|
1351
|
-
@header.delete 'content-length'
|
1352
|
-
return nil
|
1353
|
-
end
|
1354
|
-
@header['content-length'] = [len.to_i.to_s]
|
1355
|
-
end
|
1356
|
-
|
1357
|
-
# Returns "true" if the "transfer-encoding" header is present and
|
1358
|
-
# set to "chunked". This is an HTTP/1.1 feature, allowing the
|
1359
|
-
# the content to be sent in "chunks" without at the outset
|
1360
|
-
# stating the entire content length.
|
1361
|
-
def chunked?
|
1362
|
-
return false unless @header['transfer-encoding']
|
1363
|
-
field = self['Transfer-Encoding']
|
1364
|
-
(/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
|
1365
|
-
end
|
1366
|
-
|
1367
|
-
# Returns a Range object which represents Content-Range: header field.
|
1368
|
-
# This indicates, for a partial entity body, where this fragment
|
1369
|
-
# fits inside the full entity body, as range of byte offsets.
|
1370
|
-
def content_range
|
1371
|
-
return nil unless @header['content-range']
|
1372
|
-
m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
|
1373
|
-
raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
|
1374
|
-
m[1].to_i .. m[2].to_i
|
1375
|
-
end
|
1376
|
-
|
1377
|
-
# The length of the range represented in Content-Range: header.
|
1378
|
-
def range_length
|
1379
|
-
r = content_range() or return nil
|
1380
|
-
r.end - r.begin + 1
|
1381
|
-
end
|
1382
|
-
|
1383
|
-
# Returns a content type string such as "text/html".
|
1384
|
-
# This method returns nil if Content-Type: header field does not exist.
|
1385
|
-
def content_type
|
1386
|
-
return nil unless main_type()
|
1387
|
-
if sub_type()
|
1388
|
-
then "#{main_type()}/#{sub_type()}"
|
1389
|
-
else main_type()
|
1390
|
-
end
|
1391
|
-
end
|
1392
|
-
|
1393
|
-
# Returns a content type string such as "text".
|
1394
|
-
# This method returns nil if Content-Type: header field does not exist.
|
1395
|
-
def main_type
|
1396
|
-
return nil unless @header['content-type']
|
1397
|
-
self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
|
1398
|
-
end
|
1399
|
-
|
1400
|
-
# Returns a content type string such as "html".
|
1401
|
-
# This method returns nil if Content-Type: header field does not exist
|
1402
|
-
# or sub-type is not given (e.g. "Content-Type: text").
|
1403
|
-
def sub_type
|
1404
|
-
return nil unless @header['content-type']
|
1405
|
-
main, sub = *self['Content-Type'].split(';').first.to_s.split('/')
|
1406
|
-
return nil unless sub
|
1407
|
-
sub.strip
|
1408
|
-
end
|
1409
|
-
|
1410
|
-
# Returns content type parameters as a Hash as like
|
1411
|
-
# {"charset" => "iso-2022-jp"}.
|
1412
|
-
def type_params
|
1413
|
-
result = {}
|
1414
|
-
list = self['Content-Type'].to_s.split(';')
|
1415
|
-
list.shift
|
1416
|
-
list.each do |param|
|
1417
|
-
k, v = *param.split('=', 2)
|
1418
|
-
result[k.strip] = v.strip
|
1419
|
-
end
|
1420
|
-
result
|
1421
|
-
end
|
1422
|
-
|
1423
|
-
# Set Content-Type: header field by +type+ and +params+.
|
1424
|
-
# +type+ must be a String, +params+ must be a Hash.
|
1425
|
-
def set_content_type(type, params = {})
|
1426
|
-
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
|
1427
|
-
end
|
1428
|
-
|
1429
|
-
alias content_type= set_content_type
|
1543
|
+
require 'net/http/header'
|
1430
1544
|
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
#
|
1435
|
-
# This method also set Content-Type: header field to
|
1436
|
-
# application/x-www-form-urlencoded.
|
1437
|
-
def set_form_data(params, sep = '&')
|
1438
|
-
self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
|
1439
|
-
self.content_type = 'application/x-www-form-urlencoded'
|
1440
|
-
end
|
1545
|
+
require 'net/http/generic_request'
|
1546
|
+
require 'net/http/request'
|
1547
|
+
require 'net/http/requests'
|
1441
1548
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
def urlencode(str)
|
1445
|
-
str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
|
1446
|
-
end
|
1447
|
-
private :urlencode
|
1549
|
+
require 'net/http/response'
|
1550
|
+
require 'net/http/responses'
|
1448
1551
|
|
1449
|
-
|
1450
|
-
def basic_auth(account, password)
|
1451
|
-
@header['authorization'] = [basic_encode(account, password)]
|
1452
|
-
end
|
1453
|
-
|
1454
|
-
# Set Proxy-Authorization: header for "Basic" authorization.
|
1455
|
-
def proxy_basic_auth(account, password)
|
1456
|
-
@header['proxy-authorization'] = [basic_encode(account, password)]
|
1457
|
-
end
|
1458
|
-
|
1459
|
-
def basic_encode(account, password)
|
1460
|
-
'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
|
1461
|
-
end
|
1462
|
-
private :basic_encode
|
1463
|
-
|
1464
|
-
end
|
1465
|
-
|
1466
|
-
|
1467
|
-
#
|
1468
|
-
# Parent of HTTPRequest class. Do not use this directly; use
|
1469
|
-
# a subclass of HTTPRequest.
|
1470
|
-
#
|
1471
|
-
# Mixes in the HTTPHeader module.
|
1472
|
-
#
|
1473
|
-
class HTTPGenericRequest
|
1474
|
-
|
1475
|
-
include HTTPHeader
|
1476
|
-
|
1477
|
-
BUFSIZE = 16*1024
|
1478
|
-
|
1479
|
-
def initialize(m, reqbody, resbody, path, initheader = nil)
|
1480
|
-
@method = m
|
1481
|
-
@request_has_body = reqbody
|
1482
|
-
@response_has_body = resbody
|
1483
|
-
raise ArgumentError, "HTTP request path is empty" if path.empty?
|
1484
|
-
@path = path
|
1485
|
-
initialize_http_header initheader
|
1486
|
-
self['Accept'] ||= '*/*'
|
1487
|
-
@body = nil
|
1488
|
-
@body_stream = nil
|
1489
|
-
end
|
1490
|
-
|
1491
|
-
attr_reader :method
|
1492
|
-
attr_reader :path
|
1493
|
-
|
1494
|
-
def inspect
|
1495
|
-
"\#<#{self.class} #{@method}>"
|
1496
|
-
end
|
1497
|
-
|
1498
|
-
def request_body_permitted?
|
1499
|
-
@request_has_body
|
1500
|
-
end
|
1501
|
-
|
1502
|
-
def response_body_permitted?
|
1503
|
-
@response_has_body
|
1504
|
-
end
|
1505
|
-
|
1506
|
-
def body_exist?
|
1507
|
-
warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
|
1508
|
-
response_body_permitted?
|
1509
|
-
end
|
1510
|
-
|
1511
|
-
attr_reader :body
|
1512
|
-
|
1513
|
-
def body=(str)
|
1514
|
-
@body = str
|
1515
|
-
@body_stream = nil
|
1516
|
-
str
|
1517
|
-
end
|
1518
|
-
|
1519
|
-
attr_reader :body_stream
|
1520
|
-
|
1521
|
-
def body_stream=(input)
|
1522
|
-
@body = nil
|
1523
|
-
@body_stream = input
|
1524
|
-
input
|
1525
|
-
end
|
1526
|
-
|
1527
|
-
def set_body_internal(str) #:nodoc: internal use only
|
1528
|
-
raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
|
1529
|
-
self.body = str if str
|
1530
|
-
end
|
1531
|
-
|
1532
|
-
#
|
1533
|
-
# write
|
1534
|
-
#
|
1535
|
-
|
1536
|
-
def exec(sock, ver, path) #:nodoc: internal use only
|
1537
|
-
if @body
|
1538
|
-
send_request_with_body sock, ver, path, @body
|
1539
|
-
elsif @body_stream
|
1540
|
-
send_request_with_body_stream sock, ver, path, @body_stream
|
1541
|
-
else
|
1542
|
-
write_header sock, ver, path
|
1543
|
-
end
|
1544
|
-
end
|
1545
|
-
|
1546
|
-
private
|
1547
|
-
|
1548
|
-
def send_request_with_body(sock, ver, path, body)
|
1549
|
-
self.content_length = body.length
|
1550
|
-
delete 'Transfer-Encoding'
|
1551
|
-
supply_default_content_type
|
1552
|
-
write_header sock, ver, path
|
1553
|
-
sock.write body
|
1554
|
-
end
|
1555
|
-
|
1556
|
-
def send_request_with_body_stream(sock, ver, path, f)
|
1557
|
-
unless content_length() or chunked?
|
1558
|
-
raise ArgumentError,
|
1559
|
-
"Content-Length not given and Transfer-Encoding is not `chunked'"
|
1560
|
-
end
|
1561
|
-
supply_default_content_type
|
1562
|
-
write_header sock, ver, path
|
1563
|
-
if chunked?
|
1564
|
-
while s = f.read(BUFSIZE)
|
1565
|
-
sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
|
1566
|
-
end
|
1567
|
-
sock.write "0\r\n\r\n"
|
1568
|
-
else
|
1569
|
-
while s = f.read(BUFSIZE)
|
1570
|
-
sock.write s
|
1571
|
-
end
|
1572
|
-
end
|
1573
|
-
end
|
1574
|
-
|
1575
|
-
def supply_default_content_type
|
1576
|
-
return if content_type()
|
1577
|
-
warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
|
1578
|
-
set_content_type 'application/x-www-form-urlencoded'
|
1579
|
-
end
|
1580
|
-
|
1581
|
-
def write_header(sock, ver, path)
|
1582
|
-
buf = "#{@method} #{path} HTTP/#{ver}\r\n"
|
1583
|
-
each_capitalized do |k,v|
|
1584
|
-
buf << "#{k}: #{v}\r\n"
|
1585
|
-
end
|
1586
|
-
buf << "\r\n"
|
1587
|
-
sock.write buf
|
1588
|
-
end
|
1589
|
-
|
1590
|
-
end
|
1591
|
-
|
1592
|
-
|
1593
|
-
#
|
1594
|
-
# HTTP request class. This class wraps request header and entity path.
|
1595
|
-
# You *must* use its subclass, Net::HTTP::Get, Post, Head.
|
1596
|
-
#
|
1597
|
-
class HTTPRequest < HTTPGenericRequest
|
1598
|
-
|
1599
|
-
# Creates HTTP request object.
|
1600
|
-
def initialize(path, initheader = nil)
|
1601
|
-
super self.class::METHOD,
|
1602
|
-
self.class::REQUEST_HAS_BODY,
|
1603
|
-
self.class::RESPONSE_HAS_BODY,
|
1604
|
-
path, initheader
|
1605
|
-
end
|
1606
|
-
end
|
1607
|
-
|
1608
|
-
|
1609
|
-
class HTTP # reopen
|
1610
|
-
#
|
1611
|
-
# HTTP 1.1 methods --- RFC2616
|
1612
|
-
#
|
1613
|
-
|
1614
|
-
class Get < HTTPRequest
|
1615
|
-
METHOD = 'GET'
|
1616
|
-
REQUEST_HAS_BODY = false
|
1617
|
-
RESPONSE_HAS_BODY = true
|
1618
|
-
end
|
1619
|
-
|
1620
|
-
class Head < HTTPRequest
|
1621
|
-
METHOD = 'HEAD'
|
1622
|
-
REQUEST_HAS_BODY = false
|
1623
|
-
RESPONSE_HAS_BODY = false
|
1624
|
-
end
|
1625
|
-
|
1626
|
-
class Post < HTTPRequest
|
1627
|
-
METHOD = 'POST'
|
1628
|
-
REQUEST_HAS_BODY = true
|
1629
|
-
RESPONSE_HAS_BODY = true
|
1630
|
-
end
|
1631
|
-
|
1632
|
-
class Put < HTTPRequest
|
1633
|
-
METHOD = 'PUT'
|
1634
|
-
REQUEST_HAS_BODY = true
|
1635
|
-
RESPONSE_HAS_BODY = true
|
1636
|
-
end
|
1637
|
-
|
1638
|
-
class Delete < HTTPRequest
|
1639
|
-
METHOD = 'DELETE'
|
1640
|
-
REQUEST_HAS_BODY = false
|
1641
|
-
RESPONSE_HAS_BODY = true
|
1642
|
-
end
|
1643
|
-
|
1644
|
-
class Options < HTTPRequest
|
1645
|
-
METHOD = 'OPTIONS'
|
1646
|
-
REQUEST_HAS_BODY = false
|
1647
|
-
RESPONSE_HAS_BODY = false
|
1648
|
-
end
|
1649
|
-
|
1650
|
-
class Trace < HTTPRequest
|
1651
|
-
METHOD = 'TRACE'
|
1652
|
-
REQUEST_HAS_BODY = false
|
1653
|
-
RESPONSE_HAS_BODY = true
|
1654
|
-
end
|
1655
|
-
|
1656
|
-
#
|
1657
|
-
# WebDAV methods --- RFC2518
|
1658
|
-
#
|
1659
|
-
|
1660
|
-
class Propfind < HTTPRequest
|
1661
|
-
METHOD = 'PROPFIND'
|
1662
|
-
REQUEST_HAS_BODY = true
|
1663
|
-
RESPONSE_HAS_BODY = true
|
1664
|
-
end
|
1665
|
-
|
1666
|
-
class Proppatch < HTTPRequest
|
1667
|
-
METHOD = 'PROPPATCH'
|
1668
|
-
REQUEST_HAS_BODY = true
|
1669
|
-
RESPONSE_HAS_BODY = true
|
1670
|
-
end
|
1671
|
-
|
1672
|
-
class Mkcol < HTTPRequest
|
1673
|
-
METHOD = 'MKCOL'
|
1674
|
-
REQUEST_HAS_BODY = true
|
1675
|
-
RESPONSE_HAS_BODY = true
|
1676
|
-
end
|
1677
|
-
|
1678
|
-
class Copy < HTTPRequest
|
1679
|
-
METHOD = 'COPY'
|
1680
|
-
REQUEST_HAS_BODY = false
|
1681
|
-
RESPONSE_HAS_BODY = true
|
1682
|
-
end
|
1683
|
-
|
1684
|
-
class Move < HTTPRequest
|
1685
|
-
METHOD = 'MOVE'
|
1686
|
-
REQUEST_HAS_BODY = false
|
1687
|
-
RESPONSE_HAS_BODY = true
|
1688
|
-
end
|
1689
|
-
|
1690
|
-
class Lock < HTTPRequest
|
1691
|
-
METHOD = 'LOCK'
|
1692
|
-
REQUEST_HAS_BODY = true
|
1693
|
-
RESPONSE_HAS_BODY = true
|
1694
|
-
end
|
1695
|
-
|
1696
|
-
class Unlock < HTTPRequest
|
1697
|
-
METHOD = 'UNLOCK'
|
1698
|
-
REQUEST_HAS_BODY = true
|
1699
|
-
RESPONSE_HAS_BODY = true
|
1700
|
-
end
|
1701
|
-
end
|
1702
|
-
|
1703
|
-
|
1704
|
-
###
|
1705
|
-
### Response
|
1706
|
-
###
|
1707
|
-
|
1708
|
-
# HTTP exception class.
|
1709
|
-
# You must use its subclasses.
|
1710
|
-
module HTTPExceptions
|
1711
|
-
def initialize(msg, res) #:nodoc:
|
1712
|
-
super msg
|
1713
|
-
@response = res
|
1714
|
-
end
|
1715
|
-
attr_reader :response
|
1716
|
-
alias data response #:nodoc: obsolete
|
1717
|
-
end
|
1718
|
-
class HTTPError < ProtocolError
|
1719
|
-
include HTTPExceptions
|
1720
|
-
end
|
1721
|
-
class HTTPRetriableError < ProtoRetriableError
|
1722
|
-
include HTTPExceptions
|
1723
|
-
end
|
1724
|
-
class HTTPServerException < ProtoServerError
|
1725
|
-
# We cannot use the name "HTTPServerError", it is the name of the response.
|
1726
|
-
include HTTPExceptions
|
1727
|
-
end
|
1728
|
-
class HTTPFatalError < ProtoFatalError
|
1729
|
-
include HTTPExceptions
|
1730
|
-
end
|
1731
|
-
|
1732
|
-
|
1733
|
-
# HTTP response class. This class wraps response header and entity.
|
1734
|
-
# Mixes in the HTTPHeader module, which provides access to response
|
1735
|
-
# header values both via hash-like methods and individual readers.
|
1736
|
-
# Note that each possible HTTP response code defines its own
|
1737
|
-
# HTTPResponse subclass. These are listed below.
|
1738
|
-
# All classes are
|
1739
|
-
# defined under the Net module. Indentation indicates inheritance.
|
1740
|
-
#
|
1741
|
-
# xxx HTTPResponse
|
1742
|
-
#
|
1743
|
-
# 1xx HTTPInformation
|
1744
|
-
# 100 HTTPContinue
|
1745
|
-
# 101 HTTPSwitchProtocol
|
1746
|
-
#
|
1747
|
-
# 2xx HTTPSuccess
|
1748
|
-
# 200 HTTPOK
|
1749
|
-
# 201 HTTPCreated
|
1750
|
-
# 202 HTTPAccepted
|
1751
|
-
# 203 HTTPNonAuthoritativeInformation
|
1752
|
-
# 204 HTTPNoContent
|
1753
|
-
# 205 HTTPResetContent
|
1754
|
-
# 206 HTTPPartialContent
|
1755
|
-
#
|
1756
|
-
# 3xx HTTPRedirection
|
1757
|
-
# 300 HTTPMultipleChoice
|
1758
|
-
# 301 HTTPMovedPermanently
|
1759
|
-
# 302 HTTPFound
|
1760
|
-
# 303 HTTPSeeOther
|
1761
|
-
# 304 HTTPNotModified
|
1762
|
-
# 305 HTTPUseProxy
|
1763
|
-
# 307 HTTPTemporaryRedirect
|
1764
|
-
#
|
1765
|
-
# 4xx HTTPClientError
|
1766
|
-
# 400 HTTPBadRequest
|
1767
|
-
# 401 HTTPUnauthorized
|
1768
|
-
# 402 HTTPPaymentRequired
|
1769
|
-
# 403 HTTPForbidden
|
1770
|
-
# 404 HTTPNotFound
|
1771
|
-
# 405 HTTPMethodNotAllowed
|
1772
|
-
# 406 HTTPNotAcceptable
|
1773
|
-
# 407 HTTPProxyAuthenticationRequired
|
1774
|
-
# 408 HTTPRequestTimeOut
|
1775
|
-
# 409 HTTPConflict
|
1776
|
-
# 410 HTTPGone
|
1777
|
-
# 411 HTTPLengthRequired
|
1778
|
-
# 412 HTTPPreconditionFailed
|
1779
|
-
# 413 HTTPRequestEntityTooLarge
|
1780
|
-
# 414 HTTPRequestURITooLong
|
1781
|
-
# 415 HTTPUnsupportedMediaType
|
1782
|
-
# 416 HTTPRequestedRangeNotSatisfiable
|
1783
|
-
# 417 HTTPExpectationFailed
|
1784
|
-
#
|
1785
|
-
# 5xx HTTPServerError
|
1786
|
-
# 500 HTTPInternalServerError
|
1787
|
-
# 501 HTTPNotImplemented
|
1788
|
-
# 502 HTTPBadGateway
|
1789
|
-
# 503 HTTPServiceUnavailable
|
1790
|
-
# 504 HTTPGatewayTimeOut
|
1791
|
-
# 505 HTTPVersionNotSupported
|
1792
|
-
#
|
1793
|
-
# xxx HTTPUnknownResponse
|
1794
|
-
#
|
1795
|
-
class HTTPResponse
|
1796
|
-
# true if the response has body.
|
1797
|
-
def HTTPResponse.body_permitted?
|
1798
|
-
self::HAS_BODY
|
1799
|
-
end
|
1800
|
-
|
1801
|
-
def HTTPResponse.exception_type # :nodoc: internal use only
|
1802
|
-
self::EXCEPTION_TYPE
|
1803
|
-
end
|
1804
|
-
end # reopened after
|
1805
|
-
|
1806
|
-
# :stopdoc:
|
1807
|
-
|
1808
|
-
class HTTPUnknownResponse < HTTPResponse
|
1809
|
-
HAS_BODY = true
|
1810
|
-
EXCEPTION_TYPE = HTTPError
|
1811
|
-
end
|
1812
|
-
class HTTPInformation < HTTPResponse # 1xx
|
1813
|
-
HAS_BODY = false
|
1814
|
-
EXCEPTION_TYPE = HTTPError
|
1815
|
-
end
|
1816
|
-
class HTTPSuccess < HTTPResponse # 2xx
|
1817
|
-
HAS_BODY = true
|
1818
|
-
EXCEPTION_TYPE = HTTPError
|
1819
|
-
end
|
1820
|
-
class HTTPRedirection < HTTPResponse # 3xx
|
1821
|
-
HAS_BODY = true
|
1822
|
-
EXCEPTION_TYPE = HTTPRetriableError
|
1823
|
-
end
|
1824
|
-
class HTTPClientError < HTTPResponse # 4xx
|
1825
|
-
HAS_BODY = true
|
1826
|
-
EXCEPTION_TYPE = HTTPServerException # for backward compatibility
|
1827
|
-
end
|
1828
|
-
class HTTPServerError < HTTPResponse # 5xx
|
1829
|
-
HAS_BODY = true
|
1830
|
-
EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
|
1831
|
-
end
|
1832
|
-
|
1833
|
-
class HTTPContinue < HTTPInformation # 100
|
1834
|
-
HAS_BODY = false
|
1835
|
-
end
|
1836
|
-
class HTTPSwitchProtocol < HTTPInformation # 101
|
1837
|
-
HAS_BODY = false
|
1838
|
-
end
|
1839
|
-
|
1840
|
-
class HTTPOK < HTTPSuccess # 200
|
1841
|
-
HAS_BODY = true
|
1842
|
-
end
|
1843
|
-
class HTTPCreated < HTTPSuccess # 201
|
1844
|
-
HAS_BODY = true
|
1845
|
-
end
|
1846
|
-
class HTTPAccepted < HTTPSuccess # 202
|
1847
|
-
HAS_BODY = true
|
1848
|
-
end
|
1849
|
-
class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
|
1850
|
-
HAS_BODY = true
|
1851
|
-
end
|
1852
|
-
class HTTPNoContent < HTTPSuccess # 204
|
1853
|
-
HAS_BODY = false
|
1854
|
-
end
|
1855
|
-
class HTTPResetContent < HTTPSuccess # 205
|
1856
|
-
HAS_BODY = false
|
1857
|
-
end
|
1858
|
-
class HTTPPartialContent < HTTPSuccess # 206
|
1859
|
-
HAS_BODY = true
|
1860
|
-
end
|
1861
|
-
|
1862
|
-
class HTTPMultipleChoice < HTTPRedirection # 300
|
1863
|
-
HAS_BODY = true
|
1864
|
-
end
|
1865
|
-
class HTTPMovedPermanently < HTTPRedirection # 301
|
1866
|
-
HAS_BODY = true
|
1867
|
-
end
|
1868
|
-
class HTTPFound < HTTPRedirection # 302
|
1869
|
-
HAS_BODY = true
|
1870
|
-
end
|
1871
|
-
HTTPMovedTemporarily = HTTPFound
|
1872
|
-
class HTTPSeeOther < HTTPRedirection # 303
|
1873
|
-
HAS_BODY = true
|
1874
|
-
end
|
1875
|
-
class HTTPNotModified < HTTPRedirection # 304
|
1876
|
-
HAS_BODY = false
|
1877
|
-
end
|
1878
|
-
class HTTPUseProxy < HTTPRedirection # 305
|
1879
|
-
HAS_BODY = false
|
1880
|
-
end
|
1881
|
-
# 306 unused
|
1882
|
-
class HTTPTemporaryRedirect < HTTPRedirection # 307
|
1883
|
-
HAS_BODY = true
|
1884
|
-
end
|
1885
|
-
|
1886
|
-
class HTTPBadRequest < HTTPClientError # 400
|
1887
|
-
HAS_BODY = true
|
1888
|
-
end
|
1889
|
-
class HTTPUnauthorized < HTTPClientError # 401
|
1890
|
-
HAS_BODY = true
|
1891
|
-
end
|
1892
|
-
class HTTPPaymentRequired < HTTPClientError # 402
|
1893
|
-
HAS_BODY = true
|
1894
|
-
end
|
1895
|
-
class HTTPForbidden < HTTPClientError # 403
|
1896
|
-
HAS_BODY = true
|
1897
|
-
end
|
1898
|
-
class HTTPNotFound < HTTPClientError # 404
|
1899
|
-
HAS_BODY = true
|
1900
|
-
end
|
1901
|
-
class HTTPMethodNotAllowed < HTTPClientError # 405
|
1902
|
-
HAS_BODY = true
|
1903
|
-
end
|
1904
|
-
class HTTPNotAcceptable < HTTPClientError # 406
|
1905
|
-
HAS_BODY = true
|
1906
|
-
end
|
1907
|
-
class HTTPProxyAuthenticationRequired < HTTPClientError # 407
|
1908
|
-
HAS_BODY = true
|
1909
|
-
end
|
1910
|
-
class HTTPRequestTimeOut < HTTPClientError # 408
|
1911
|
-
HAS_BODY = true
|
1912
|
-
end
|
1913
|
-
class HTTPConflict < HTTPClientError # 409
|
1914
|
-
HAS_BODY = true
|
1915
|
-
end
|
1916
|
-
class HTTPGone < HTTPClientError # 410
|
1917
|
-
HAS_BODY = true
|
1918
|
-
end
|
1919
|
-
class HTTPLengthRequired < HTTPClientError # 411
|
1920
|
-
HAS_BODY = true
|
1921
|
-
end
|
1922
|
-
class HTTPPreconditionFailed < HTTPClientError # 412
|
1923
|
-
HAS_BODY = true
|
1924
|
-
end
|
1925
|
-
class HTTPRequestEntityTooLarge < HTTPClientError # 413
|
1926
|
-
HAS_BODY = true
|
1927
|
-
end
|
1928
|
-
class HTTPRequestURITooLong < HTTPClientError # 414
|
1929
|
-
HAS_BODY = true
|
1930
|
-
end
|
1931
|
-
HTTPRequestURITooLarge = HTTPRequestURITooLong
|
1932
|
-
class HTTPUnsupportedMediaType < HTTPClientError # 415
|
1933
|
-
HAS_BODY = true
|
1934
|
-
end
|
1935
|
-
class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
|
1936
|
-
HAS_BODY = true
|
1937
|
-
end
|
1938
|
-
class HTTPExpectationFailed < HTTPClientError # 417
|
1939
|
-
HAS_BODY = true
|
1940
|
-
end
|
1941
|
-
|
1942
|
-
class HTTPInternalServerError < HTTPServerError # 500
|
1943
|
-
HAS_BODY = true
|
1944
|
-
end
|
1945
|
-
class HTTPNotImplemented < HTTPServerError # 501
|
1946
|
-
HAS_BODY = true
|
1947
|
-
end
|
1948
|
-
class HTTPBadGateway < HTTPServerError # 502
|
1949
|
-
HAS_BODY = true
|
1950
|
-
end
|
1951
|
-
class HTTPServiceUnavailable < HTTPServerError # 503
|
1952
|
-
HAS_BODY = true
|
1953
|
-
end
|
1954
|
-
class HTTPGatewayTimeOut < HTTPServerError # 504
|
1955
|
-
HAS_BODY = true
|
1956
|
-
end
|
1957
|
-
class HTTPVersionNotSupported < HTTPServerError # 505
|
1958
|
-
HAS_BODY = true
|
1959
|
-
end
|
1960
|
-
|
1961
|
-
# :startdoc:
|
1962
|
-
|
1963
|
-
|
1964
|
-
class HTTPResponse # reopen
|
1965
|
-
|
1966
|
-
CODE_CLASS_TO_OBJ = {
|
1967
|
-
'1' => HTTPInformation,
|
1968
|
-
'2' => HTTPSuccess,
|
1969
|
-
'3' => HTTPRedirection,
|
1970
|
-
'4' => HTTPClientError,
|
1971
|
-
'5' => HTTPServerError
|
1972
|
-
}
|
1973
|
-
CODE_TO_OBJ = {
|
1974
|
-
'100' => HTTPContinue,
|
1975
|
-
'101' => HTTPSwitchProtocol,
|
1976
|
-
|
1977
|
-
'200' => HTTPOK,
|
1978
|
-
'201' => HTTPCreated,
|
1979
|
-
'202' => HTTPAccepted,
|
1980
|
-
'203' => HTTPNonAuthoritativeInformation,
|
1981
|
-
'204' => HTTPNoContent,
|
1982
|
-
'205' => HTTPResetContent,
|
1983
|
-
'206' => HTTPPartialContent,
|
1984
|
-
|
1985
|
-
'300' => HTTPMultipleChoice,
|
1986
|
-
'301' => HTTPMovedPermanently,
|
1987
|
-
'302' => HTTPFound,
|
1988
|
-
'303' => HTTPSeeOther,
|
1989
|
-
'304' => HTTPNotModified,
|
1990
|
-
'305' => HTTPUseProxy,
|
1991
|
-
'307' => HTTPTemporaryRedirect,
|
1992
|
-
|
1993
|
-
'400' => HTTPBadRequest,
|
1994
|
-
'401' => HTTPUnauthorized,
|
1995
|
-
'402' => HTTPPaymentRequired,
|
1996
|
-
'403' => HTTPForbidden,
|
1997
|
-
'404' => HTTPNotFound,
|
1998
|
-
'405' => HTTPMethodNotAllowed,
|
1999
|
-
'406' => HTTPNotAcceptable,
|
2000
|
-
'407' => HTTPProxyAuthenticationRequired,
|
2001
|
-
'408' => HTTPRequestTimeOut,
|
2002
|
-
'409' => HTTPConflict,
|
2003
|
-
'410' => HTTPGone,
|
2004
|
-
'411' => HTTPLengthRequired,
|
2005
|
-
'412' => HTTPPreconditionFailed,
|
2006
|
-
'413' => HTTPRequestEntityTooLarge,
|
2007
|
-
'414' => HTTPRequestURITooLong,
|
2008
|
-
'415' => HTTPUnsupportedMediaType,
|
2009
|
-
'416' => HTTPRequestedRangeNotSatisfiable,
|
2010
|
-
'417' => HTTPExpectationFailed,
|
2011
|
-
|
2012
|
-
'500' => HTTPInternalServerError,
|
2013
|
-
'501' => HTTPNotImplemented,
|
2014
|
-
'502' => HTTPBadGateway,
|
2015
|
-
'503' => HTTPServiceUnavailable,
|
2016
|
-
'504' => HTTPGatewayTimeOut,
|
2017
|
-
'505' => HTTPVersionNotSupported
|
2018
|
-
}
|
2019
|
-
|
2020
|
-
class << HTTPResponse
|
2021
|
-
def read_new(sock) #:nodoc: internal use only
|
2022
|
-
httpv, code, msg = read_status_line(sock)
|
2023
|
-
res = response_class(code).new(httpv, code, msg)
|
2024
|
-
each_response_header(sock) do |k,v|
|
2025
|
-
res.add_field k, v
|
2026
|
-
end
|
2027
|
-
res
|
2028
|
-
end
|
2029
|
-
|
2030
|
-
private
|
2031
|
-
|
2032
|
-
def read_status_line(sock)
|
2033
|
-
str = sock.readline
|
2034
|
-
m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
|
2035
|
-
raise HTTPBadResponse, "wrong status line: #{str.dump}"
|
2036
|
-
m.captures
|
2037
|
-
end
|
2038
|
-
|
2039
|
-
def response_class(code)
|
2040
|
-
CODE_TO_OBJ[code] or
|
2041
|
-
CODE_CLASS_TO_OBJ[code[0,1]] or
|
2042
|
-
HTTPUnknownResponse
|
2043
|
-
end
|
2044
|
-
|
2045
|
-
def each_response_header(sock)
|
2046
|
-
while true
|
2047
|
-
line = sock.readuntil("\n", true).sub(/\s+\z/, '')
|
2048
|
-
break if line.empty?
|
2049
|
-
m = /\A([^:]+):\s*/.match(line) or
|
2050
|
-
raise HTTPBadResponse, 'wrong header line format'
|
2051
|
-
yield m[1], m.post_match
|
2052
|
-
end
|
2053
|
-
end
|
2054
|
-
end
|
2055
|
-
|
2056
|
-
# next is to fix bug in RDoc, where the private inside class << self
|
2057
|
-
# spills out.
|
2058
|
-
public
|
2059
|
-
|
2060
|
-
include HTTPHeader
|
2061
|
-
|
2062
|
-
def initialize(httpv, code, msg) #:nodoc: internal use only
|
2063
|
-
@http_version = httpv
|
2064
|
-
@code = code
|
2065
|
-
@message = msg
|
2066
|
-
initialize_http_header nil
|
2067
|
-
@body = nil
|
2068
|
-
@read = false
|
2069
|
-
end
|
2070
|
-
|
2071
|
-
# The HTTP version supported by the server.
|
2072
|
-
attr_reader :http_version
|
2073
|
-
|
2074
|
-
# HTTP result code string. For example, '302'. You can also
|
2075
|
-
# determine the response type by which response subclass the
|
2076
|
-
# response object is an instance of.
|
2077
|
-
attr_reader :code
|
2078
|
-
|
2079
|
-
# HTTP result message. For example, 'Not Found'.
|
2080
|
-
attr_reader :message
|
2081
|
-
alias msg message # :nodoc: obsolete
|
2082
|
-
|
2083
|
-
def inspect
|
2084
|
-
"#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
|
2085
|
-
end
|
2086
|
-
|
2087
|
-
# For backward compatibility.
|
2088
|
-
# To allow Net::HTTP 1.1 style assignment
|
2089
|
-
# e.g.
|
2090
|
-
# response, body = Net::HTTP.get(....)
|
2091
|
-
#
|
2092
|
-
def to_ary
|
2093
|
-
warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
|
2094
|
-
res = self.dup
|
2095
|
-
class << res
|
2096
|
-
undef to_ary
|
2097
|
-
end
|
2098
|
-
[res, res.body]
|
2099
|
-
end
|
2100
|
-
|
2101
|
-
#
|
2102
|
-
# response <-> exception relationship
|
2103
|
-
#
|
2104
|
-
|
2105
|
-
def code_type #:nodoc:
|
2106
|
-
self.class
|
2107
|
-
end
|
2108
|
-
|
2109
|
-
def error! #:nodoc:
|
2110
|
-
raise error_type().new(@code + ' ' + @message.dump, self)
|
2111
|
-
end
|
2112
|
-
|
2113
|
-
def error_type #:nodoc:
|
2114
|
-
self.class::EXCEPTION_TYPE
|
2115
|
-
end
|
2116
|
-
|
2117
|
-
# Raises HTTP error if the response is not 2xx.
|
2118
|
-
def value
|
2119
|
-
error! unless self.kind_of?(HTTPSuccess)
|
2120
|
-
end
|
2121
|
-
|
2122
|
-
#
|
2123
|
-
# header (for backward compatibility only; DO NOT USE)
|
2124
|
-
#
|
2125
|
-
|
2126
|
-
def response #:nodoc:
|
2127
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
|
2128
|
-
self
|
2129
|
-
end
|
2130
|
-
|
2131
|
-
def header #:nodoc:
|
2132
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
|
2133
|
-
self
|
2134
|
-
end
|
2135
|
-
|
2136
|
-
def read_header #:nodoc:
|
2137
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
|
2138
|
-
self
|
2139
|
-
end
|
2140
|
-
|
2141
|
-
#
|
2142
|
-
# body
|
2143
|
-
#
|
2144
|
-
|
2145
|
-
def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
|
2146
|
-
@socket = sock
|
2147
|
-
@body_exist = reqmethodallowbody && self.class.body_permitted?
|
2148
|
-
begin
|
2149
|
-
yield
|
2150
|
-
self.body # ensure to read body
|
2151
|
-
ensure
|
2152
|
-
@socket = nil
|
2153
|
-
end
|
2154
|
-
end
|
2155
|
-
|
2156
|
-
# Gets entity body. If the block given, yields it to +block+.
|
2157
|
-
# The body is provided in fragments, as it is read in from the socket.
|
2158
|
-
#
|
2159
|
-
# Calling this method a second or subsequent time will return the
|
2160
|
-
# already read string.
|
2161
|
-
#
|
2162
|
-
# http.request_get('/index.html') {|res|
|
2163
|
-
# puts res.read_body
|
2164
|
-
# }
|
2165
|
-
#
|
2166
|
-
# http.request_get('/index.html') {|res|
|
2167
|
-
# p res.read_body.object_id # 538149362
|
2168
|
-
# p res.read_body.object_id # 538149362
|
2169
|
-
# }
|
2170
|
-
#
|
2171
|
-
# # using iterator
|
2172
|
-
# http.request_get('/index.html') {|res|
|
2173
|
-
# res.read_body do |segment|
|
2174
|
-
# print segment
|
2175
|
-
# end
|
2176
|
-
# }
|
2177
|
-
#
|
2178
|
-
def read_body(dest = nil, &block)
|
2179
|
-
if @read
|
2180
|
-
raise IOError, "#{self.class}\#read_body called twice" if dest or block
|
2181
|
-
return @body
|
2182
|
-
end
|
2183
|
-
to = procdest(dest, block)
|
2184
|
-
stream_check
|
2185
|
-
if @body_exist
|
2186
|
-
read_body_0 to
|
2187
|
-
@body = to
|
2188
|
-
else
|
2189
|
-
@body = nil
|
2190
|
-
end
|
2191
|
-
@read = true
|
2192
|
-
|
2193
|
-
@body
|
2194
|
-
end
|
2195
|
-
|
2196
|
-
# Returns the entity body.
|
2197
|
-
#
|
2198
|
-
# Calling this method a second or subsequent time will return the
|
2199
|
-
# already read string.
|
2200
|
-
#
|
2201
|
-
# http.request_get('/index.html') {|res|
|
2202
|
-
# puts res.body
|
2203
|
-
# }
|
2204
|
-
#
|
2205
|
-
# http.request_get('/index.html') {|res|
|
2206
|
-
# p res.body.object_id # 538149362
|
2207
|
-
# p res.body.object_id # 538149362
|
2208
|
-
# }
|
2209
|
-
#
|
2210
|
-
def body
|
2211
|
-
read_body()
|
2212
|
-
end
|
2213
|
-
|
2214
|
-
alias entity body #:nodoc: obsolete
|
2215
|
-
|
2216
|
-
private
|
2217
|
-
|
2218
|
-
def read_body_0(dest)
|
2219
|
-
if chunked?
|
2220
|
-
read_chunked dest
|
2221
|
-
return
|
2222
|
-
end
|
2223
|
-
clen = content_length()
|
2224
|
-
if clen
|
2225
|
-
@socket.read clen, dest, true # ignore EOF
|
2226
|
-
return
|
2227
|
-
end
|
2228
|
-
clen = range_length()
|
2229
|
-
if clen
|
2230
|
-
@socket.read clen, dest
|
2231
|
-
return
|
2232
|
-
end
|
2233
|
-
@socket.read_all dest
|
2234
|
-
end
|
2235
|
-
|
2236
|
-
def read_chunked(dest)
|
2237
|
-
len = nil
|
2238
|
-
total = 0
|
2239
|
-
while true
|
2240
|
-
line = @socket.readline
|
2241
|
-
hexlen = line.slice(/[0-9a-fA-F]+/) or
|
2242
|
-
raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
2243
|
-
len = hexlen.hex
|
2244
|
-
break if len == 0
|
2245
|
-
@socket.read len, dest; total += len
|
2246
|
-
@socket.read 2 # \r\n
|
2247
|
-
end
|
2248
|
-
until @socket.readline.empty?
|
2249
|
-
# none
|
2250
|
-
end
|
2251
|
-
end
|
2252
|
-
|
2253
|
-
def stream_check
|
2254
|
-
raise IOError, 'attempt to read body out of block' if @socket.closed?
|
2255
|
-
end
|
2256
|
-
|
2257
|
-
def procdest(dest, block)
|
2258
|
-
raise ArgumentError, 'both arg and block given for HTTP method' \
|
2259
|
-
if dest and block
|
2260
|
-
if block
|
2261
|
-
ReadAdapter.new(block)
|
2262
|
-
else
|
2263
|
-
dest || ''
|
2264
|
-
end
|
2265
|
-
end
|
2266
|
-
|
2267
|
-
end
|
2268
|
-
|
2269
|
-
|
2270
|
-
# :enddoc:
|
2271
|
-
|
2272
|
-
#--
|
2273
|
-
# for backward compatibility
|
2274
|
-
class HTTP
|
2275
|
-
ProxyMod = ProxyDelta
|
2276
|
-
end
|
2277
|
-
module NetPrivate
|
2278
|
-
HTTPRequest = ::Net::HTTPRequest
|
2279
|
-
end
|
1552
|
+
require 'net/http/proxy_delta'
|
2280
1553
|
|
2281
|
-
|
2282
|
-
HTTPSuccessCode = HTTPSuccess
|
2283
|
-
HTTPRedirectionCode = HTTPRedirection
|
2284
|
-
HTTPRetriableCode = HTTPRedirection
|
2285
|
-
HTTPClientErrorCode = HTTPClientError
|
2286
|
-
HTTPFatalErrorCode = HTTPClientError
|
2287
|
-
HTTPServerErrorCode = HTTPServerError
|
2288
|
-
HTTPResponceReceiver = HTTPResponse
|
1554
|
+
require 'net/http/backward'
|
2289
1555
|
|
2290
|
-
end # module Net
|