mechanize 0.6.11 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mechanize might be problematic. Click here for more details.
- data/CHANGELOG.txt +8 -0
- data/Manifest.txt +31 -22
- data/lib/mechanize.rb +2 -652
- data/lib/www/mechanize.rb +635 -0
- data/lib/www/mechanize/content_type_error.rb +16 -0
- data/lib/www/mechanize/cookie.rb +64 -0
- data/lib/{mechanize/cookie.rb → www/mechanize/cookie_jar.rb} +0 -60
- data/lib/www/mechanize/file.rb +73 -0
- data/lib/www/mechanize/file_saver.rb +39 -0
- data/lib/{mechanize → www/mechanize}/form.rb +119 -137
- data/lib/www/mechanize/form/button.rb +8 -0
- data/lib/www/mechanize/form/check_box.rb +13 -0
- data/lib/www/mechanize/form/field.rb +28 -0
- data/lib/www/mechanize/form/file_upload.rb +24 -0
- data/lib/www/mechanize/form/image_button.rb +23 -0
- data/lib/www/mechanize/form/multi_select_list.rb +69 -0
- data/lib/www/mechanize/form/option.rb +51 -0
- data/lib/www/mechanize/form/radio_button.rb +38 -0
- data/lib/www/mechanize/form/select_list.rb +41 -0
- data/lib/www/mechanize/headers.rb +12 -0
- data/lib/{mechanize → www/mechanize}/history.rb +0 -0
- data/lib/{mechanize → www/mechanize}/inspect.rb +21 -28
- data/lib/{mechanize → www/mechanize}/list.rb +0 -0
- data/lib/{mechanize → www/mechanize}/monkey_patch.rb +19 -0
- data/lib/www/mechanize/page.rb +121 -0
- data/lib/www/mechanize/page/base.rb +10 -0
- data/lib/www/mechanize/page/frame.rb +22 -0
- data/lib/www/mechanize/page/link.rb +50 -0
- data/lib/www/mechanize/page/meta.rb +10 -0
- data/lib/www/mechanize/pluggable_parsers.rb +93 -0
- data/lib/{mechanize/errors.rb → www/mechanize/response_code_error.rb} +1 -13
- data/test/{test_includes.rb → helper.rb} +4 -18
- data/test/{test_servlets.rb → servlets.rb} +0 -0
- data/test/tc_authenticate.rb +1 -8
- data/test/tc_bad_links.rb +3 -10
- data/test/tc_blank_form.rb +1 -8
- data/test/tc_checkboxes.rb +1 -8
- data/test/tc_cookie_class.rb +1 -6
- data/test/tc_cookie_jar.rb +1 -7
- data/test/tc_cookies.rb +10 -17
- data/test/tc_encoded_links.rb +5 -12
- data/test/tc_errors.rb +4 -11
- data/test/tc_follow_meta.rb +1 -8
- data/test/tc_form_action.rb +6 -14
- data/test/tc_form_as_hash.rb +1 -9
- data/test/tc_form_button.rb +5 -8
- data/test/tc_form_no_inputname.rb +1 -8
- data/test/tc_forms.rb +16 -24
- data/test/tc_frames.rb +3 -10
- data/test/tc_gzipping.rb +2 -9
- data/test/tc_history.rb +5 -12
- data/test/tc_html_unscape_forms.rb +8 -15
- data/test/tc_if_modified_since.rb +1 -6
- data/test/tc_keep_alive.rb +1 -8
- data/test/tc_links.rb +12 -19
- data/test/tc_mech.rb +26 -34
- data/test/{test_mechanize_file.rb → tc_mechanize_file.rb} +1 -6
- data/test/tc_multi_select.rb +10 -17
- data/test/tc_no_attributes.rb +1 -8
- data/test/tc_page.rb +3 -10
- data/test/tc_pluggable_parser.rb +8 -15
- data/test/tc_post_form.rb +3 -10
- data/test/tc_pretty_print.rb +3 -10
- data/test/tc_radiobutton.rb +2 -9
- data/test/tc_referer.rb +13 -20
- data/test/tc_relative_links.rb +1 -8
- data/test/tc_response_code.rb +14 -21
- data/test/tc_save_file.rb +1 -9
- data/test/tc_select.rb +3 -10
- data/test/tc_select_all.rb +2 -10
- data/test/tc_select_none.rb +2 -10
- data/test/tc_select_noopts.rb +2 -9
- data/test/tc_set_fields.rb +2 -9
- data/test/tc_ssl_server.rb +5 -12
- data/test/tc_subclass.rb +2 -9
- data/test/tc_textarea.rb +2 -9
- data/test/tc_upload.rb +2 -9
- data/test/test_all.rb +4 -43
- metadata +96 -80
- data/lib/mechanize/form_elements.rb +0 -254
- data/lib/mechanize/net-overrides/net/http.rb +0 -2107
- data/lib/mechanize/net-overrides/net/https.rb +0 -172
- data/lib/mechanize/net-overrides/net/protocol.rb +0 -380
- data/lib/mechanize/page.rb +0 -138
- data/lib/mechanize/page_elements.rb +0 -77
- data/lib/mechanize/parsers/rexml_page.rb +0 -35
- data/lib/mechanize/pluggable_parsers.rb +0 -204
- data/lib/mechanize/rexml.rb +0 -236
- data/setup.rb +0 -1585
- data/test/tc_proxy.rb +0 -25
- data/test/tc_watches.rb +0 -32
@@ -1,2107 +0,0 @@
|
|
1
|
-
# :enddoc:
|
2
|
-
# = net/http.rb
|
3
|
-
#
|
4
|
-
# Copyright (C) 1999-2005 Yukihiro Matsumoto
|
5
|
-
# Copyright (C) 1999-2005 Minero Aoki
|
6
|
-
# Copyright (C) 2001 GOTOU Yuuzou
|
7
|
-
#
|
8
|
-
# Written and maintained by Minero Aoki <aamine@loveruby.net>.
|
9
|
-
# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>.
|
10
|
-
#
|
11
|
-
# This file is derived from "http-access.rb".
|
12
|
-
#
|
13
|
-
# Documented by Minero Aoki; converted to RDoc by William Webber.
|
14
|
-
#
|
15
|
-
# This program is free software. You can re-distribute and/or
|
16
|
-
# modify this program under the same terms of ruby itself ---
|
17
|
-
# Ruby Distribution License or GNU General Public License.
|
18
|
-
#
|
19
|
-
# See Net:::HTTP for an overview and examples.
|
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: http.rb,v 1.123 2005/01/01 08:59:47 aamine Exp $
|
26
|
-
#++
|
27
|
-
|
28
|
-
require 'net/protocol'
|
29
|
-
require 'uri'
|
30
|
-
|
31
|
-
module Net # :nodoc:
|
32
|
-
|
33
|
-
# :stopdoc:
|
34
|
-
class HTTPBadResponse < StandardError; end
|
35
|
-
class HTTPHeaderSyntaxError < StandardError; end
|
36
|
-
# :startdoc:
|
37
|
-
|
38
|
-
# == What Is This Library?
|
39
|
-
#
|
40
|
-
# This library provides your program functions to access WWW
|
41
|
-
# documents via HTTP, Hyper Text Transfer Protocol version 1.1.
|
42
|
-
# For details of HTTP, refer [RFC2616]
|
43
|
-
# (http://www.ietf.org/rfc/rfc2616.txt).
|
44
|
-
#
|
45
|
-
# == Examples
|
46
|
-
#
|
47
|
-
# === Getting Document From WWW Server
|
48
|
-
#
|
49
|
-
# (formal version)
|
50
|
-
#
|
51
|
-
# require 'net/http'
|
52
|
-
# Net::HTTP.start('www.example.com', 80) {|http|
|
53
|
-
# response = http.get('/index.html')
|
54
|
-
# puts response.body
|
55
|
-
# }
|
56
|
-
#
|
57
|
-
# (shorter version)
|
58
|
-
#
|
59
|
-
# require 'net/http'
|
60
|
-
# Net::HTTP.get_print 'www.example.com', '/index.html'
|
61
|
-
#
|
62
|
-
# or
|
63
|
-
#
|
64
|
-
# require 'net/http'
|
65
|
-
# require 'uri'
|
66
|
-
# Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
|
67
|
-
#
|
68
|
-
# === Posting Form Data
|
69
|
-
#
|
70
|
-
# require 'net/http'
|
71
|
-
# Net::HTTP.start('some.www.server', 80) {|http|
|
72
|
-
# response = http.post('/cgi-bin/search.rb', 'query=ruby')
|
73
|
-
# }
|
74
|
-
#
|
75
|
-
# === Accessing via Proxy
|
76
|
-
#
|
77
|
-
# Net::HTTP.Proxy creates http proxy class. It has same
|
78
|
-
# methods of Net::HTTP but its instances always connect to
|
79
|
-
# proxy, instead of given host.
|
80
|
-
#
|
81
|
-
# require 'net/http'
|
82
|
-
#
|
83
|
-
# proxy_addr = 'your.proxy.host'
|
84
|
-
# proxy_port = 8080
|
85
|
-
# :
|
86
|
-
# Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
|
87
|
-
# # always connect to your.proxy.addr:8080
|
88
|
-
# :
|
89
|
-
# }
|
90
|
-
#
|
91
|
-
# Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
|
92
|
-
# there's no need to change code if there's proxy or not.
|
93
|
-
#
|
94
|
-
# There are two additional parameters in Net::HTTP.Proxy which allow to
|
95
|
-
# specify proxy user name and password:
|
96
|
-
#
|
97
|
-
# Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
|
98
|
-
#
|
99
|
-
# You may use them to work with authorization-enabled proxies:
|
100
|
-
#
|
101
|
-
# require 'net/http'
|
102
|
-
# require 'uri'
|
103
|
-
#
|
104
|
-
# proxy_host = 'your.proxy.host'
|
105
|
-
# proxy_port = 8080
|
106
|
-
# uri = URI.parse(ENV['http_proxy'])
|
107
|
-
# proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
|
108
|
-
# Net::HTTP::Proxy(proxy_host, proxy_port,
|
109
|
-
# proxy_user, proxy_pass).start('www.example.com') {|http|
|
110
|
-
# # always connect to your.proxy.addr:8080 using specified username and password
|
111
|
-
# :
|
112
|
-
# }
|
113
|
-
#
|
114
|
-
# Note that net/http never rely on HTTP_PROXY environment variable.
|
115
|
-
# If you want to use proxy, set it explicitly.
|
116
|
-
#
|
117
|
-
# === Following Redirection
|
118
|
-
#
|
119
|
-
# require 'net/http'
|
120
|
-
# require 'uri'
|
121
|
-
#
|
122
|
-
# def fetch( uri_str, limit = 10 )
|
123
|
-
# # You should choose better exception.
|
124
|
-
# raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
125
|
-
#
|
126
|
-
# response = Net::HTTP.get_response(URI.parse(uri_str))
|
127
|
-
# case response
|
128
|
-
# when Net::HTTPSuccess then response
|
129
|
-
# when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
130
|
-
# else
|
131
|
-
# response.error!
|
132
|
-
# end
|
133
|
-
# end
|
134
|
-
#
|
135
|
-
# print fetch('http://www.ruby-lang.org')
|
136
|
-
#
|
137
|
-
# Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
|
138
|
-
# All HTTPResponse objects belong to its own response class which
|
139
|
-
# indicate HTTP result status. For details of response classes,
|
140
|
-
# see section "HTTP Response Classes".
|
141
|
-
#
|
142
|
-
# === Basic Authentication
|
143
|
-
#
|
144
|
-
# require 'net/http'
|
145
|
-
#
|
146
|
-
# Net::HTTP.start('www.example.com') {|http|
|
147
|
-
# req = Net::HTTP::Get.new('/secret-page.html')
|
148
|
-
# req.basic_auth 'account', 'password'
|
149
|
-
# response = http.request(req)
|
150
|
-
# print response.body
|
151
|
-
# }
|
152
|
-
#
|
153
|
-
# === HTTP Response Classes
|
154
|
-
#
|
155
|
-
# TODO: write me.
|
156
|
-
#
|
157
|
-
# == Switching Net::HTTP versions
|
158
|
-
#
|
159
|
-
# You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
|
160
|
-
# by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
|
161
|
-
# allows you to use 1.2 features again.
|
162
|
-
#
|
163
|
-
# # example
|
164
|
-
# Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
|
165
|
-
#
|
166
|
-
# Net::HTTP.version_1_1
|
167
|
-
# Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
|
168
|
-
#
|
169
|
-
# Net::HTTP.version_1_2
|
170
|
-
# Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
|
171
|
-
#
|
172
|
-
# This function is NOT thread-safe.
|
173
|
-
#
|
174
|
-
class HTTP < Protocol
|
175
|
-
|
176
|
-
# :stopdoc:
|
177
|
-
Revision = %q$Revision: 1.123 $.split[1]
|
178
|
-
HTTPVersion = '1.1'
|
179
|
-
@newimpl = true # for backward compatability
|
180
|
-
# :startdoc:
|
181
|
-
|
182
|
-
# Turns on net/http 1.2 (ruby 1.8) features.
|
183
|
-
# Defaults to ON in ruby 1.8.
|
184
|
-
#
|
185
|
-
# I strongly recommend to call this method always.
|
186
|
-
#
|
187
|
-
# require 'net/http'
|
188
|
-
# Net::HTTP.version_1_2
|
189
|
-
#
|
190
|
-
def HTTP.version_1_2
|
191
|
-
@newimpl = true
|
192
|
-
end
|
193
|
-
|
194
|
-
# Turns on net/http 1.1 (ruby 1.6) features.
|
195
|
-
# Defaults to OFF in ruby 1.8.
|
196
|
-
def HTTP.version_1_1
|
197
|
-
@newimpl = false
|
198
|
-
end
|
199
|
-
|
200
|
-
# true if net/http is in version 1.2 mode.
|
201
|
-
# Defaults to true.
|
202
|
-
def HTTP.version_1_2?
|
203
|
-
@newimpl
|
204
|
-
end
|
205
|
-
|
206
|
-
# true if net/http is in version 1.1 compatible mode.
|
207
|
-
# Defaults to true.
|
208
|
-
def HTTP.version_1_1?
|
209
|
-
not @newimpl
|
210
|
-
end
|
211
|
-
|
212
|
-
class << HTTP
|
213
|
-
alias is_version_1_1? version_1_1? #:nodoc:
|
214
|
-
alias is_version_1_2? version_1_2? #:nodoc:
|
215
|
-
end
|
216
|
-
|
217
|
-
#
|
218
|
-
# short cut methods
|
219
|
-
#
|
220
|
-
|
221
|
-
#
|
222
|
-
# Get body from target and output it to +$stdout+. The
|
223
|
-
# target can either be specified as (+uri+), or as
|
224
|
-
# (+host+, +path+, +port+ = 80); so:
|
225
|
-
#
|
226
|
-
# Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
|
227
|
-
#
|
228
|
-
# or:
|
229
|
-
#
|
230
|
-
# Net::HTTP.get_print('www.example.com', '/index.html')
|
231
|
-
#
|
232
|
-
def HTTP.get_print(arg1, arg2 = nil, port = nil)
|
233
|
-
if arg2
|
234
|
-
addr, path = arg1, arg2
|
235
|
-
else
|
236
|
-
uri = arg1
|
237
|
-
addr = uri.host
|
238
|
-
path = uri.request_uri
|
239
|
-
port = uri.port
|
240
|
-
end
|
241
|
-
new(addr, port || HTTP.default_port).start {|http|
|
242
|
-
http.get path, nil, $stdout
|
243
|
-
}
|
244
|
-
nil
|
245
|
-
end
|
246
|
-
|
247
|
-
# Send a GET request to the target and return the response
|
248
|
-
# as a string. The target can either be specified as
|
249
|
-
# (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
250
|
-
#
|
251
|
-
# print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
|
252
|
-
#
|
253
|
-
# or:
|
254
|
-
#
|
255
|
-
# print Net::HTTP.get('www.example.com', '/index.html')
|
256
|
-
#
|
257
|
-
def HTTP.get(arg1, arg2 = nil, arg3 = nil)
|
258
|
-
get_response(arg1,arg2,arg3).body
|
259
|
-
end
|
260
|
-
|
261
|
-
# Send a GET request to the target and return the response
|
262
|
-
# as a Net::HTTPResponse object. The target can either be specified as
|
263
|
-
# (+uri+), or as (+host+, +path+, +port+ = 80); so:
|
264
|
-
#
|
265
|
-
# res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
|
266
|
-
# print res.body
|
267
|
-
#
|
268
|
-
# or:
|
269
|
-
#
|
270
|
-
# res = Net::HTTP.get_response('www.example.com', '/index.html')
|
271
|
-
# print res.body
|
272
|
-
#
|
273
|
-
def HTTP.get_response(arg1, arg2 = nil, arg3 = nil)
|
274
|
-
if arg2
|
275
|
-
get_by_path(arg1, arg2, arg3)
|
276
|
-
else
|
277
|
-
get_by_uri(arg1)
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
def HTTP.get_by_path(addr, path, port = nil) #:nodoc:
|
282
|
-
new(addr, port || HTTP.default_port).start {|http|
|
283
|
-
return http.request(Get.new(path))
|
284
|
-
}
|
285
|
-
end
|
286
|
-
private_class_method :get_by_path
|
287
|
-
|
288
|
-
def HTTP.get_by_uri(uri) #:nodoc:
|
289
|
-
# Should we allow this?
|
290
|
-
# uri = URI.parse(uri) unless uri.respond_to?(:host)
|
291
|
-
new(uri.host, uri.port).start {|http|
|
292
|
-
return http.request(Get.new(uri.request_uri))
|
293
|
-
}
|
294
|
-
end
|
295
|
-
private_class_method :get_by_uri
|
296
|
-
|
297
|
-
#
|
298
|
-
# HTTP session management
|
299
|
-
#
|
300
|
-
|
301
|
-
# The default port to use for HTTP requests; defaults to 80.
|
302
|
-
def HTTP.default_port
|
303
|
-
http_default_port()
|
304
|
-
end
|
305
|
-
|
306
|
-
# The default port to use for HTTP requests; defaults to 80.
|
307
|
-
def HTTP.http_default_port
|
308
|
-
80
|
309
|
-
end
|
310
|
-
|
311
|
-
# The default port to use for HTTPS requests; defaults to 443.
|
312
|
-
def HTTP.https_default_port
|
313
|
-
443
|
314
|
-
end
|
315
|
-
|
316
|
-
def HTTP.socket_type #:nodoc: obsolete
|
317
|
-
InternetMessageIO
|
318
|
-
end
|
319
|
-
|
320
|
-
# creates a new Net::HTTP object and opens its TCP connection and
|
321
|
-
# HTTP session. If the optional block is given, the newly
|
322
|
-
# created Net::HTTP object is passed to it and closed when the
|
323
|
-
# block finishes. In this case, the return value of this method
|
324
|
-
# is the return value of the block. If no block is given, the
|
325
|
-
# return value of this method is the newly created Net::HTTP object
|
326
|
-
# itself, and the caller is responsible for closing it upon completion.
|
327
|
-
def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
|
328
|
-
new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
|
329
|
-
end
|
330
|
-
|
331
|
-
class << HTTP
|
332
|
-
alias newobj new
|
333
|
-
end
|
334
|
-
|
335
|
-
# Creates a new Net::HTTP object.
|
336
|
-
# If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
|
337
|
-
# This method does not open the TCP connection.
|
338
|
-
def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
|
339
|
-
h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
|
340
|
-
h.instance_eval {
|
341
|
-
@newimpl = ::Net::HTTP.version_1_2?
|
342
|
-
}
|
343
|
-
h
|
344
|
-
end
|
345
|
-
|
346
|
-
# Creates a new Net::HTTP object for the specified +address+.
|
347
|
-
# This method does not open the TCP connection.
|
348
|
-
def initialize(address, port = nil)
|
349
|
-
@address = address
|
350
|
-
@port = (port || HTTP.default_port)
|
351
|
-
@curr_http_version = HTTPVersion
|
352
|
-
@seems_1_0_server = false
|
353
|
-
@close_on_empty_response = false
|
354
|
-
@socket = nil
|
355
|
-
@started = false
|
356
|
-
@open_timeout = 30
|
357
|
-
@read_timeout = 60
|
358
|
-
@debug_output = nil
|
359
|
-
@use_ssl = false
|
360
|
-
@ssl_context = nil
|
361
|
-
end
|
362
|
-
|
363
|
-
def inspect
|
364
|
-
"#<#{self.class} #{@address}:#{@port} open=#{started?}>"
|
365
|
-
end
|
366
|
-
|
367
|
-
# *WARNING* This method causes serious security hole.
|
368
|
-
# Never use this method in production code.
|
369
|
-
#
|
370
|
-
# Set an output stream for debugging.
|
371
|
-
#
|
372
|
-
# http = Net::HTTP.new
|
373
|
-
# http.set_debug_output $stderr
|
374
|
-
# http.start { .... }
|
375
|
-
#
|
376
|
-
def set_debug_output(output)
|
377
|
-
warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
|
378
|
-
@debug_output = output
|
379
|
-
end
|
380
|
-
|
381
|
-
# The host name to connect to.
|
382
|
-
attr_reader :address
|
383
|
-
|
384
|
-
# The port number to connect to.
|
385
|
-
attr_reader :port
|
386
|
-
|
387
|
-
# Seconds to wait until connection is opened.
|
388
|
-
# If the HTTP object cannot open a connection in this many seconds,
|
389
|
-
# it raises a TimeoutError exception.
|
390
|
-
attr_accessor :open_timeout
|
391
|
-
|
392
|
-
# Seconds to wait until reading one block (by one read(2) call).
|
393
|
-
# If the HTTP object cannot open a connection in this many seconds,
|
394
|
-
# it raises a TimeoutError exception.
|
395
|
-
attr_reader :read_timeout
|
396
|
-
|
397
|
-
# Setter for the read_timeout attribute.
|
398
|
-
def read_timeout=(sec)
|
399
|
-
@socket.read_timeout = sec if @socket
|
400
|
-
@read_timeout = sec
|
401
|
-
end
|
402
|
-
|
403
|
-
# returns true if the HTTP session is started.
|
404
|
-
def started?
|
405
|
-
@started
|
406
|
-
end
|
407
|
-
|
408
|
-
alias active? started? #:nodoc: obsolete
|
409
|
-
|
410
|
-
attr_accessor :close_on_empty_response
|
411
|
-
|
412
|
-
# returns true if use SSL/TLS with HTTP.
|
413
|
-
def use_ssl?
|
414
|
-
false # redefined in net/https
|
415
|
-
end
|
416
|
-
|
417
|
-
# Opens TCP connection and HTTP session.
|
418
|
-
#
|
419
|
-
# When this method is called with block, gives a HTTP object
|
420
|
-
# to the block and closes the TCP connection / HTTP session
|
421
|
-
# after the block executed.
|
422
|
-
#
|
423
|
-
# When called with a block, returns the return value of the
|
424
|
-
# block; otherwise, returns self.
|
425
|
-
#
|
426
|
-
def start # :yield: http
|
427
|
-
raise IOError, 'HTTP session already opened' if @started
|
428
|
-
if block_given?
|
429
|
-
begin
|
430
|
-
do_start
|
431
|
-
return yield(self)
|
432
|
-
ensure
|
433
|
-
do_finish
|
434
|
-
end
|
435
|
-
end
|
436
|
-
do_start
|
437
|
-
self
|
438
|
-
end
|
439
|
-
|
440
|
-
def do_start
|
441
|
-
connect
|
442
|
-
@started = true
|
443
|
-
end
|
444
|
-
private :do_start
|
445
|
-
|
446
|
-
def connect
|
447
|
-
D "opening connection to #{conn_address()}..."
|
448
|
-
s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
|
449
|
-
D "opened"
|
450
|
-
if use_ssl?
|
451
|
-
unless @ssl_context.verify_mode
|
452
|
-
warn "warning: peer certificate won't be verified in this SSL session"
|
453
|
-
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
454
|
-
end
|
455
|
-
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
456
|
-
s.sync_close = true
|
457
|
-
end
|
458
|
-
@socket = BufferedIO.new(s)
|
459
|
-
@socket.read_timeout = @read_timeout
|
460
|
-
@socket.debug_output = @debug_output
|
461
|
-
if use_ssl?
|
462
|
-
if proxy?
|
463
|
-
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
464
|
-
@address, @port, HTTPVersion)
|
465
|
-
@socket.writeline ''
|
466
|
-
HTTPResponse.read_new(@socket).value
|
467
|
-
end
|
468
|
-
s.connect
|
469
|
-
end
|
470
|
-
on_connect
|
471
|
-
end
|
472
|
-
private :connect
|
473
|
-
|
474
|
-
def on_connect
|
475
|
-
end
|
476
|
-
private :on_connect
|
477
|
-
|
478
|
-
# Finishes HTTP session and closes TCP connection.
|
479
|
-
# Raises IOError if not started.
|
480
|
-
def finish
|
481
|
-
raise IOError, 'HTTP session not yet started' unless started?
|
482
|
-
do_finish
|
483
|
-
end
|
484
|
-
|
485
|
-
def do_finish
|
486
|
-
@started = false
|
487
|
-
@socket.close if @socket and not @socket.closed?
|
488
|
-
@socket = nil
|
489
|
-
end
|
490
|
-
private :do_finish
|
491
|
-
|
492
|
-
#
|
493
|
-
# proxy
|
494
|
-
#
|
495
|
-
|
496
|
-
public
|
497
|
-
|
498
|
-
# no proxy
|
499
|
-
@is_proxy_class = false
|
500
|
-
@proxy_addr = nil
|
501
|
-
@proxy_port = nil
|
502
|
-
@proxy_user = nil
|
503
|
-
@proxy_pass = nil
|
504
|
-
|
505
|
-
# Creates an HTTP proxy class.
|
506
|
-
# Arguments are address/port of proxy host and username/password
|
507
|
-
# if authorization on proxy server is required.
|
508
|
-
# You can replace the HTTP class with created proxy class.
|
509
|
-
#
|
510
|
-
# If ADDRESS is nil, this method returns self (Net::HTTP).
|
511
|
-
#
|
512
|
-
# # Example
|
513
|
-
# proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
|
514
|
-
# :
|
515
|
-
# proxy_class.start('www.ruby-lang.org') {|http|
|
516
|
-
# # connecting proxy.foo.org:8080
|
517
|
-
# :
|
518
|
-
# }
|
519
|
-
#
|
520
|
-
def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
|
521
|
-
return self unless p_addr
|
522
|
-
delta = ProxyDelta
|
523
|
-
proxyclass = Class.new(self)
|
524
|
-
proxyclass.module_eval {
|
525
|
-
include delta
|
526
|
-
# with proxy
|
527
|
-
@is_proxy_class = true
|
528
|
-
@proxy_address = p_addr
|
529
|
-
@proxy_port = p_port || default_port()
|
530
|
-
@proxy_user = p_user
|
531
|
-
@proxy_pass = p_pass
|
532
|
-
}
|
533
|
-
proxyclass
|
534
|
-
end
|
535
|
-
|
536
|
-
class << HTTP
|
537
|
-
# returns true if self is a class which was created by HTTP::Proxy.
|
538
|
-
def proxy_class?
|
539
|
-
@is_proxy_class
|
540
|
-
end
|
541
|
-
|
542
|
-
attr_reader :proxy_address
|
543
|
-
attr_reader :proxy_port
|
544
|
-
attr_reader :proxy_user
|
545
|
-
attr_reader :proxy_pass
|
546
|
-
end
|
547
|
-
|
548
|
-
# True if self is a HTTP proxy class.
|
549
|
-
def proxy?
|
550
|
-
self.class.proxy_class?
|
551
|
-
end
|
552
|
-
|
553
|
-
# Address of proxy host. If self does not use a proxy, nil.
|
554
|
-
def proxy_address
|
555
|
-
self.class.proxy_address
|
556
|
-
end
|
557
|
-
|
558
|
-
# Port number of proxy host. If self does not use a proxy, nil.
|
559
|
-
def proxy_port
|
560
|
-
self.class.proxy_port
|
561
|
-
end
|
562
|
-
|
563
|
-
# User name for accessing proxy. If self does not use a proxy, nil.
|
564
|
-
def proxy_user
|
565
|
-
self.class.proxy_user
|
566
|
-
end
|
567
|
-
|
568
|
-
# User password for accessing proxy. If self does not use a proxy, nil.
|
569
|
-
def proxy_pass
|
570
|
-
self.class.proxy_pass
|
571
|
-
end
|
572
|
-
|
573
|
-
alias proxyaddr proxy_address #:nodoc: obsolete
|
574
|
-
alias proxyport proxy_port #:nodoc: obsolete
|
575
|
-
|
576
|
-
private
|
577
|
-
|
578
|
-
# without proxy
|
579
|
-
|
580
|
-
def conn_address
|
581
|
-
address()
|
582
|
-
end
|
583
|
-
|
584
|
-
def conn_port
|
585
|
-
port()
|
586
|
-
end
|
587
|
-
|
588
|
-
def edit_path(path)
|
589
|
-
path
|
590
|
-
end
|
591
|
-
|
592
|
-
module ProxyDelta #:nodoc: internal use only
|
593
|
-
private
|
594
|
-
|
595
|
-
def conn_address
|
596
|
-
proxy_address()
|
597
|
-
end
|
598
|
-
|
599
|
-
def conn_port
|
600
|
-
proxy_port()
|
601
|
-
end
|
602
|
-
|
603
|
-
def edit_path(path)
|
604
|
-
if use_ssl?
|
605
|
-
"https://#{addr_port()}#{path}"
|
606
|
-
else
|
607
|
-
"http://#{addr_port()}#{path}"
|
608
|
-
end
|
609
|
-
end
|
610
|
-
end
|
611
|
-
|
612
|
-
#
|
613
|
-
# HTTP operations
|
614
|
-
#
|
615
|
-
|
616
|
-
public
|
617
|
-
|
618
|
-
# Gets data from +path+ on the connected-to host.
|
619
|
-
# +header+ must be a Hash like { 'Accept' => '*/*', ... }.
|
620
|
-
#
|
621
|
-
# In version 1.1 (ruby 1.6), this method returns a pair of objects,
|
622
|
-
# a Net::HTTPResponse object and the entity body string.
|
623
|
-
# In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
|
624
|
-
# object.
|
625
|
-
#
|
626
|
-
# If called with a block, yields each fragment of the
|
627
|
-
# entity body in turn as a string as it is read from
|
628
|
-
# the socket. Note that in this case, the returned response
|
629
|
-
# object will *not* contain a (meaningful) body.
|
630
|
-
#
|
631
|
-
# +dest+ argument is obsolete.
|
632
|
-
# It still works but you must not use it.
|
633
|
-
#
|
634
|
-
# In version 1.1, this method might raise an exception for
|
635
|
-
# 3xx (redirect). In this case you can get a HTTPResponse object
|
636
|
-
# by "anException.response".
|
637
|
-
#
|
638
|
-
# In version 1.2, this method never raises exception.
|
639
|
-
#
|
640
|
-
# # version 1.1 (bundled with Ruby 1.6)
|
641
|
-
# response, body = http.get('/index.html')
|
642
|
-
#
|
643
|
-
# # version 1.2 (bundled with Ruby 1.8 or later)
|
644
|
-
# response = http.get('/index.html')
|
645
|
-
#
|
646
|
-
# # using block
|
647
|
-
# File.open('result.txt', 'w') {|f|
|
648
|
-
# http.get('/~foo/') do |str|
|
649
|
-
# f.write str
|
650
|
-
# end
|
651
|
-
# }
|
652
|
-
#
|
653
|
-
def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
654
|
-
res = nil
|
655
|
-
request(Get.new(path, initheader)) {|r|
|
656
|
-
r.read_body dest, &block
|
657
|
-
res = r
|
658
|
-
}
|
659
|
-
unless @newimpl
|
660
|
-
res.value
|
661
|
-
return res, res.body
|
662
|
-
end
|
663
|
-
|
664
|
-
res
|
665
|
-
end
|
666
|
-
|
667
|
-
# Gets only the header from +path+ on the connected-to host.
|
668
|
-
# +header+ is a Hash like { 'Accept' => '*/*', ... }.
|
669
|
-
#
|
670
|
-
# This method returns a Net::HTTPResponse object.
|
671
|
-
#
|
672
|
-
# In version 1.1, this method might raise an exception for
|
673
|
-
# 3xx (redirect). On the case you can get a HTTPResponse object
|
674
|
-
# by "anException.response".
|
675
|
-
# In version 1.2, this method never raises an exception.
|
676
|
-
#
|
677
|
-
# response = nil
|
678
|
-
# Net::HTTP.start('some.www.server', 80) {|http|
|
679
|
-
# response = http.head('/index.html')
|
680
|
-
# }
|
681
|
-
# p response['content-type']
|
682
|
-
#
|
683
|
-
def head(path, initheader = nil)
|
684
|
-
res = request(Head.new(path, initheader))
|
685
|
-
res.value unless @newimpl
|
686
|
-
res
|
687
|
-
end
|
688
|
-
|
689
|
-
# Posts +data+ (must be a String) to +path+. +header+ must be a Hash
|
690
|
-
# like { 'Accept' => '*/*', ... }.
|
691
|
-
#
|
692
|
-
# In version 1.1 (ruby 1.6), this method returns a pair of objects, a
|
693
|
-
# Net::HTTPResponse object and an entity body string.
|
694
|
-
# In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
|
695
|
-
#
|
696
|
-
# If called with a block, yields each fragment of the
|
697
|
-
# entity body in turn as a string as it are read from
|
698
|
-
# the socket. Note that in this case, the returned response
|
699
|
-
# object will *not* contain a (meaningful) body.
|
700
|
-
#
|
701
|
-
# +dest+ is an alternative method of collecting the body. It
|
702
|
-
# must be an object responding to the "<<" operator (such as
|
703
|
-
# a String or an Array). Each fragment of the entity body
|
704
|
-
# will be "<<"-ed in turn onto +dest+ if provided, and it will
|
705
|
-
# also become the body of the returned response object.
|
706
|
-
#
|
707
|
-
# You must *not* provide both +dest+ and a block; doing so
|
708
|
-
# will result in an ArgumentError.
|
709
|
-
#
|
710
|
-
# In version 1.1, this method might raise an exception for
|
711
|
-
# 3xx (redirect). In this case you can get an HTTPResponse object
|
712
|
-
# by "anException.response".
|
713
|
-
# In version 1.2, this method never raises exception.
|
714
|
-
#
|
715
|
-
# # version 1.1
|
716
|
-
# response, body = http.post('/cgi-bin/search.rb', 'query=foo')
|
717
|
-
#
|
718
|
-
# # version 1.2
|
719
|
-
# response = http.post('/cgi-bin/search.rb', 'query=foo')
|
720
|
-
#
|
721
|
-
# # using block
|
722
|
-
# File.open('result.txt', 'w') {|f|
|
723
|
-
# http.post('/cgi-bin/search.rb', 'query=foo') do |str|
|
724
|
-
# f.write str
|
725
|
-
# end
|
726
|
-
# }
|
727
|
-
#
|
728
|
-
def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
|
729
|
-
res = nil
|
730
|
-
request(Post.new(path, initheader), data) {|r|
|
731
|
-
r.read_body dest, &block
|
732
|
-
res = r
|
733
|
-
}
|
734
|
-
unless @newimpl
|
735
|
-
res.value
|
736
|
-
return res, res.body
|
737
|
-
end
|
738
|
-
|
739
|
-
res
|
740
|
-
end
|
741
|
-
|
742
|
-
def put(path, data, initheader = nil) #:nodoc:
|
743
|
-
res = request(Put.new(path, initheader), data)
|
744
|
-
res.value unless @newimpl
|
745
|
-
res
|
746
|
-
end
|
747
|
-
|
748
|
-
# Sends a PROPPATCH request to the +path+ and gets a response,
|
749
|
-
# as an HTTPResponse object.
|
750
|
-
def proppatch(path, body, initheader = nil)
|
751
|
-
request(Proppatch.new(path, initheader), body)
|
752
|
-
end
|
753
|
-
|
754
|
-
# Sends a LOCK request to the +path+ and gets a response,
|
755
|
-
# as an HTTPResponse object.
|
756
|
-
def lock(path, body, initheader = nil)
|
757
|
-
request(Lock.new(path, initheader), body)
|
758
|
-
end
|
759
|
-
|
760
|
-
# Sends a UNLOCK request to the +path+ and gets a response,
|
761
|
-
# as an HTTPResponse object.
|
762
|
-
def unlock(path, body, initheader = nil)
|
763
|
-
request(Unlock.new(path, initheader), body)
|
764
|
-
end
|
765
|
-
|
766
|
-
# Sends a OPTIONS request to the +path+ and gets a response,
|
767
|
-
# as an HTTPResponse object.
|
768
|
-
def options(path, initheader = nil)
|
769
|
-
request(Options.new(path, initheader))
|
770
|
-
end
|
771
|
-
|
772
|
-
# Sends a PROPFIND request to the +path+ and gets a response,
|
773
|
-
# as an HTTPResponse object.
|
774
|
-
def propfind(path, body = nil, initheader = {'Depth' => '0'})
|
775
|
-
request(Propfind.new(path, initheader), body)
|
776
|
-
end
|
777
|
-
|
778
|
-
# Sends a DELETE request to the +path+ and gets a response,
|
779
|
-
# as an HTTPResponse object.
|
780
|
-
def delete(path, initheader = {'Depth' => 'Infinity'})
|
781
|
-
request(Delete.new(path, initheader))
|
782
|
-
end
|
783
|
-
|
784
|
-
# Sends a MOVE request to the +path+ and gets a response,
|
785
|
-
# as an HTTPResponse object.
|
786
|
-
def move(path, initheader = nil)
|
787
|
-
request(Move.new(path, initheader))
|
788
|
-
end
|
789
|
-
|
790
|
-
# Sends a COPY request to the +path+ and gets a response,
|
791
|
-
# as an HTTPResponse object.
|
792
|
-
def copy(path, initheader = nil)
|
793
|
-
request(Copy.new(path, initheader))
|
794
|
-
end
|
795
|
-
|
796
|
-
# Sends a MKCOL request to the +path+ and gets a response,
|
797
|
-
# as an HTTPResponse object.
|
798
|
-
def mkcol(path, body = nil, initheader = nil)
|
799
|
-
request(Mkcol.new(path, initheader), body)
|
800
|
-
end
|
801
|
-
|
802
|
-
# Sends a TRACE request to the +path+ and gets a response,
|
803
|
-
# as an HTTPResponse object.
|
804
|
-
def trace(path, initheader = nil)
|
805
|
-
request(Trace.new(path, initheader))
|
806
|
-
end
|
807
|
-
|
808
|
-
# Sends a GET request to the +path+ and gets a response,
|
809
|
-
# as an HTTPResponse object.
|
810
|
-
#
|
811
|
-
# When called with a block, yields an HTTPResponse object.
|
812
|
-
# The body of this response will not have been read yet;
|
813
|
-
# the caller can process it using HTTPResponse#read_body,
|
814
|
-
# if desired.
|
815
|
-
#
|
816
|
-
# Returns the response.
|
817
|
-
#
|
818
|
-
# This method never raises Net::* exceptions.
|
819
|
-
#
|
820
|
-
# response = http.request_get('/index.html')
|
821
|
-
# # The entity body is already read here.
|
822
|
-
# p response['content-type']
|
823
|
-
# puts response.body
|
824
|
-
#
|
825
|
-
# # using block
|
826
|
-
# http.request_get('/index.html') {|response|
|
827
|
-
# p response['content-type']
|
828
|
-
# response.read_body do |str| # read body now
|
829
|
-
# print str
|
830
|
-
# end
|
831
|
-
# }
|
832
|
-
#
|
833
|
-
def request_get(path, initheader = nil, &block) # :yield: +response+
|
834
|
-
request(Get.new(path, initheader), &block)
|
835
|
-
end
|
836
|
-
|
837
|
-
# Sends a HEAD request to the +path+ and gets a response,
|
838
|
-
# as an HTTPResponse object.
|
839
|
-
#
|
840
|
-
# Returns the response.
|
841
|
-
#
|
842
|
-
# This method never raises Net::* exceptions.
|
843
|
-
#
|
844
|
-
# response = http.request_head('/index.html')
|
845
|
-
# p response['content-type']
|
846
|
-
#
|
847
|
-
def request_head(path, initheader = nil, &block)
|
848
|
-
request(Head.new(path, initheader), &block)
|
849
|
-
end
|
850
|
-
|
851
|
-
# Sends a POST request to the +path+ and gets a response,
|
852
|
-
# as an HTTPResponse object.
|
853
|
-
#
|
854
|
-
# When called with a block, yields an HTTPResponse object.
|
855
|
-
# The body of this response will not have been read yet;
|
856
|
-
# the caller can process it using HTTPResponse#read_body,
|
857
|
-
# if desired.
|
858
|
-
#
|
859
|
-
# Returns the response.
|
860
|
-
#
|
861
|
-
# This method never raises Net::* exceptions.
|
862
|
-
#
|
863
|
-
# # example
|
864
|
-
# response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
|
865
|
-
# p response.status
|
866
|
-
# puts response.body # body is already read
|
867
|
-
#
|
868
|
-
# # using block
|
869
|
-
# http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
|
870
|
-
# p response.status
|
871
|
-
# p response['content-type']
|
872
|
-
# response.read_body do |str| # read body now
|
873
|
-
# print str
|
874
|
-
# end
|
875
|
-
# }
|
876
|
-
#
|
877
|
-
def request_post(path, data, initheader = nil, &block) # :yield: +response+
|
878
|
-
request Post.new(path, initheader), data, &block
|
879
|
-
end
|
880
|
-
|
881
|
-
def request_put(path, data, initheader = nil, &block) #:nodoc:
|
882
|
-
request Put.new(path, initheader), data, &block
|
883
|
-
end
|
884
|
-
|
885
|
-
alias get2 request_get #:nodoc: obsolete
|
886
|
-
alias head2 request_head #:nodoc: obsolete
|
887
|
-
alias post2 request_post #:nodoc: obsolete
|
888
|
-
alias put2 request_put #:nodoc: obsolete
|
889
|
-
|
890
|
-
|
891
|
-
# Sends an HTTP request to the HTTP server.
|
892
|
-
# This method also sends DATA string if DATA is given.
|
893
|
-
#
|
894
|
-
# Returns a HTTPResponse object.
|
895
|
-
#
|
896
|
-
# This method never raises Net::* exceptions.
|
897
|
-
#
|
898
|
-
# response = http.send_request('GET', '/index.html')
|
899
|
-
# puts response.body
|
900
|
-
#
|
901
|
-
def send_request(name, path, data = nil, header = nil)
|
902
|
-
r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
|
903
|
-
request r, data
|
904
|
-
end
|
905
|
-
|
906
|
-
# Sends an HTTPRequest object REQUEST to the HTTP server.
|
907
|
-
# This method also sends DATA string if REQUEST is a post/put request.
|
908
|
-
# Giving DATA for get/head request causes ArgumentError.
|
909
|
-
#
|
910
|
-
# When called with a block, yields an HTTPResponse object.
|
911
|
-
# The body of this response will not have been read yet;
|
912
|
-
# the caller can process it using HTTPResponse#read_body,
|
913
|
-
# if desired.
|
914
|
-
#
|
915
|
-
# Returns a HTTPResponse object.
|
916
|
-
#
|
917
|
-
# This method never raises Net::* exceptions.
|
918
|
-
#
|
919
|
-
def request(req, body = nil, &block) # :yield: +response+
|
920
|
-
unless started?
|
921
|
-
start {
|
922
|
-
req['connection'] ||= 'close'
|
923
|
-
return request(req, body, &block)
|
924
|
-
}
|
925
|
-
end
|
926
|
-
if proxy_user()
|
927
|
-
req.proxy_basic_auth proxy_user(), proxy_pass()
|
928
|
-
end
|
929
|
-
|
930
|
-
req.set_body_internal body
|
931
|
-
begin_transport req
|
932
|
-
req.exec @socket, @curr_http_version, edit_path(req.path)
|
933
|
-
begin
|
934
|
-
res = HTTPResponse.read_new(@socket)
|
935
|
-
end while res.kind_of?(HTTPContinue)
|
936
|
-
res.reading_body(@socket, req.response_body_permitted?) {
|
937
|
-
yield res if block_given?
|
938
|
-
}
|
939
|
-
end_transport req, res
|
940
|
-
|
941
|
-
res
|
942
|
-
end
|
943
|
-
|
944
|
-
private
|
945
|
-
|
946
|
-
def begin_transport(req)
|
947
|
-
if @socket.closed?
|
948
|
-
connect
|
949
|
-
end
|
950
|
-
if @seems_1_0_server
|
951
|
-
req['connection'] ||= 'close'
|
952
|
-
end
|
953
|
-
if not req.response_body_permitted? and @close_on_empty_response
|
954
|
-
req['connection'] ||= 'close'
|
955
|
-
end
|
956
|
-
req['host'] ||= addr_port()
|
957
|
-
end
|
958
|
-
|
959
|
-
def end_transport(req, res)
|
960
|
-
@curr_http_version = res.http_version
|
961
|
-
if not res.body and @close_on_empty_response
|
962
|
-
D 'Conn close'
|
963
|
-
@socket.close
|
964
|
-
elsif keep_alive?(req, res)
|
965
|
-
D 'Conn keep-alive'
|
966
|
-
if @socket.closed?
|
967
|
-
D 'Conn (but seems 1.0 server)'
|
968
|
-
@seems_1_0_server = true
|
969
|
-
end
|
970
|
-
else
|
971
|
-
D 'Conn close'
|
972
|
-
@socket.close
|
973
|
-
end
|
974
|
-
end
|
975
|
-
|
976
|
-
def keep_alive?(req, res)
|
977
|
-
return false if /close/i =~ req['connection'].to_s
|
978
|
-
return false if @seems_1_0_server
|
979
|
-
return true if /keep-alive/i =~ res['connection'].to_s
|
980
|
-
return false if /close/i =~ res['connection'].to_s
|
981
|
-
return true if /keep-alive/i =~ res['proxy-connection'].to_s
|
982
|
-
return false if /close/i =~ res['proxy-connection'].to_s
|
983
|
-
(@curr_http_version == '1.1')
|
984
|
-
end
|
985
|
-
|
986
|
-
#
|
987
|
-
# utils
|
988
|
-
#
|
989
|
-
|
990
|
-
private
|
991
|
-
|
992
|
-
def addr_port
|
993
|
-
if use_ssl?
|
994
|
-
address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
|
995
|
-
else
|
996
|
-
address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
|
997
|
-
end
|
998
|
-
end
|
999
|
-
|
1000
|
-
def D(msg)
|
1001
|
-
return unless @debug_output
|
1002
|
-
@debug_output << msg
|
1003
|
-
@debug_output << "\n"
|
1004
|
-
end
|
1005
|
-
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
HTTPSession = HTTP
|
1009
|
-
|
1010
|
-
|
1011
|
-
#
|
1012
|
-
# Header module.
|
1013
|
-
#
|
1014
|
-
# Provides access to @header in the mixed-into class as a hash-like
|
1015
|
-
# object, except with case-insensitive keys. Also provides
|
1016
|
-
# methods for accessing commonly-used header values in a more
|
1017
|
-
# convenient format.
|
1018
|
-
#
|
1019
|
-
module HTTPHeader
|
1020
|
-
|
1021
|
-
def initialize_http_header(initheader)
|
1022
|
-
@header = {}
|
1023
|
-
return unless initheader
|
1024
|
-
initheader.each do |key, value|
|
1025
|
-
warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
|
1026
|
-
@header[key.downcase] = [value.strip]
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
def size #:nodoc: obsolete
|
1031
|
-
@header.size
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
alias length size #:nodoc: obsolete
|
1035
|
-
|
1036
|
-
# Returns the header field corresponding to the case-insensitive key.
|
1037
|
-
# For example, a key of "Content-Type" might return "text/html"
|
1038
|
-
def [](key)
|
1039
|
-
a = @header[key.downcase] or return nil
|
1040
|
-
a.join(', ')
|
1041
|
-
end
|
1042
|
-
|
1043
|
-
# Sets the header field corresponding to the case-insensitive key.
|
1044
|
-
def []=(key, val)
|
1045
|
-
unless val
|
1046
|
-
@header.delete key.downcase
|
1047
|
-
return val
|
1048
|
-
end
|
1049
|
-
@header[key.downcase] = Array(val).map {|s| s.to_str }
|
1050
|
-
end
|
1051
|
-
|
1052
|
-
# Adds header name and field instead of replace.
|
1053
|
-
#
|
1054
|
-
# request.add_header 'X-My-Header', 'a'
|
1055
|
-
# p request['X-My-Header'] #=> "a"
|
1056
|
-
# request.add_header 'X-My-Header', 'b'
|
1057
|
-
# p request['X-My-Header'] #=> "a, b"
|
1058
|
-
# request.add_header 'X-My-Header', 'c'
|
1059
|
-
# p request['X-My-Header'] #=> "a, b, c"
|
1060
|
-
# p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
|
1061
|
-
#
|
1062
|
-
def add_field(key, val)
|
1063
|
-
if @header.key?(key.downcase)
|
1064
|
-
@header[key.downcase].concat Array(val)
|
1065
|
-
else
|
1066
|
-
@header[key.downcase] = Array(val).dup
|
1067
|
-
end
|
1068
|
-
end
|
1069
|
-
|
1070
|
-
# Returns the header field by Array, corresponding to the
|
1071
|
-
# case-insensitive key. This method allows you to get duplicated
|
1072
|
-
# fields without any processing.
|
1073
|
-
#
|
1074
|
-
# p response.get_fields('Set-Cookie')
|
1075
|
-
# #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
|
1076
|
-
# "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
|
1077
|
-
# p response['Set-Cookie']
|
1078
|
-
# #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
|
1079
|
-
#
|
1080
|
-
def get_fields(key)
|
1081
|
-
return nil unless @header[key.downcase]
|
1082
|
-
@header[key.downcase].dup
|
1083
|
-
end
|
1084
|
-
|
1085
|
-
# Returns the header field corresponding to the case-insensitive key.
|
1086
|
-
# Returns the default value +args+, or the result of the block, or nil,
|
1087
|
-
# if there's no header field named key. See Hash#fetch
|
1088
|
-
def fetch(key, *args, &block) #:yield: +key+
|
1089
|
-
a = @header.fetch(key.downcase, *args, &block)
|
1090
|
-
a.join(', ')
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
# Iterates for each header names and values.
|
1094
|
-
def each_header #:yield: +key+, +value+
|
1095
|
-
@header.each do |k,va|
|
1096
|
-
yield k, va.join(', ')
|
1097
|
-
end
|
1098
|
-
end
|
1099
|
-
|
1100
|
-
alias each each_header
|
1101
|
-
|
1102
|
-
# Iterates for each header names.
|
1103
|
-
def each_name(&block) #:yield: +key+
|
1104
|
-
@header.each_key(&block)
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
alias each_key each_name
|
1108
|
-
|
1109
|
-
# Iterates for each capitalized header names.
|
1110
|
-
def each_capitalized_name(&block) #:yield: +key+
|
1111
|
-
@header.each_key do |k|
|
1112
|
-
yield capitalize(k)
|
1113
|
-
end
|
1114
|
-
end
|
1115
|
-
|
1116
|
-
# Iterates for each header values.
|
1117
|
-
def each_value #:yield: +value+
|
1118
|
-
@header.each_value do |va|
|
1119
|
-
yield va.join(', ')
|
1120
|
-
end
|
1121
|
-
end
|
1122
|
-
|
1123
|
-
# Removes a header field.
|
1124
|
-
def delete(key)
|
1125
|
-
@header.delete(key.downcase)
|
1126
|
-
end
|
1127
|
-
|
1128
|
-
# true if +key+ header exists.
|
1129
|
-
def key?(key)
|
1130
|
-
@header.key?(key.downcase)
|
1131
|
-
end
|
1132
|
-
|
1133
|
-
# Returns a Hash consist of header names and values.
|
1134
|
-
def to_hash
|
1135
|
-
@header.dup
|
1136
|
-
end
|
1137
|
-
|
1138
|
-
# As for #each_header, except the keys are provided in capitalized form.
|
1139
|
-
def each_capitalized
|
1140
|
-
@header.each do |k,v|
|
1141
|
-
yield capitalize(k), v.join(', ')
|
1142
|
-
end
|
1143
|
-
end
|
1144
|
-
|
1145
|
-
alias canonical_each each_capitalized
|
1146
|
-
|
1147
|
-
def capitalize(name)
|
1148
|
-
name.split(/-/).map {|s| s.capitalize }.join('-')
|
1149
|
-
end
|
1150
|
-
private :capitalize
|
1151
|
-
|
1152
|
-
# Returns an Array of Range objects which represents Range: header field,
|
1153
|
-
# or +nil+ if there is no such header.
|
1154
|
-
def range
|
1155
|
-
return nil unless @header['range']
|
1156
|
-
self['Range'].split(/,/).map {|spec|
|
1157
|
-
m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
|
1158
|
-
raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
|
1159
|
-
d1 = m[1].to_i
|
1160
|
-
d2 = m[2].to_i
|
1161
|
-
if m[1] and m[2] then d1..d2
|
1162
|
-
elsif m[1] then d1..-1
|
1163
|
-
elsif m[2] then -d2..-1
|
1164
|
-
else
|
1165
|
-
raise HTTPHeaderSyntaxError, 'range is not specified'
|
1166
|
-
end
|
1167
|
-
}
|
1168
|
-
end
|
1169
|
-
|
1170
|
-
# Set Range: header from Range (arg r) or beginning index and
|
1171
|
-
# length from it (arg idx&len).
|
1172
|
-
#
|
1173
|
-
# req.range = (0..1023)
|
1174
|
-
# req.set_range 0, 1023
|
1175
|
-
#
|
1176
|
-
def set_range(r, e = nil)
|
1177
|
-
unless r
|
1178
|
-
@header.delete 'range'
|
1179
|
-
return r
|
1180
|
-
end
|
1181
|
-
r = (r...r+e) if e
|
1182
|
-
case r
|
1183
|
-
when Numeric
|
1184
|
-
n = r.to_i
|
1185
|
-
rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
|
1186
|
-
when Range
|
1187
|
-
first = r.first
|
1188
|
-
last = r.last
|
1189
|
-
last -= 1 if r.exclude_end?
|
1190
|
-
if last == -1
|
1191
|
-
rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
|
1192
|
-
else
|
1193
|
-
raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
|
1194
|
-
raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
|
1195
|
-
raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
|
1196
|
-
rangestr = "#{first}-#{last}"
|
1197
|
-
end
|
1198
|
-
else
|
1199
|
-
raise TypeError, 'Range/Integer is required'
|
1200
|
-
end
|
1201
|
-
@header['range'] = ["bytes=#{rangestr}"]
|
1202
|
-
r
|
1203
|
-
end
|
1204
|
-
|
1205
|
-
alias range= set_range
|
1206
|
-
|
1207
|
-
# Returns an Integer object which represents the Content-Length: header field
|
1208
|
-
# or +nil+ if that field is not provided.
|
1209
|
-
def content_length
|
1210
|
-
return nil unless key?('Content-Length')
|
1211
|
-
len = self['Content-Length'].slice(/\d+/) or
|
1212
|
-
raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
|
1213
|
-
len.to_i
|
1214
|
-
end
|
1215
|
-
|
1216
|
-
def content_length=(len)
|
1217
|
-
unless len
|
1218
|
-
@header.delete 'content-length'
|
1219
|
-
return nil
|
1220
|
-
end
|
1221
|
-
@header['content-length'] = [len.to_i.to_s]
|
1222
|
-
end
|
1223
|
-
|
1224
|
-
# Returns "true" if the "transfer-encoding" header is present and
|
1225
|
-
# set to "chunked". This is an HTTP/1.1 feature, allowing the
|
1226
|
-
# the content to be sent in "chunks" without at the outset
|
1227
|
-
# stating the entire content length.
|
1228
|
-
def chunked?
|
1229
|
-
return false unless @header['transfer-encoding']
|
1230
|
-
field = self['Transfer-Encoding']
|
1231
|
-
(/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
|
1232
|
-
end
|
1233
|
-
|
1234
|
-
# Returns a Range object which represents Content-Range: header field.
|
1235
|
-
# This indicates, for a partial entity body, where this fragment
|
1236
|
-
# fits inside the full entity body, as range of byte offsets.
|
1237
|
-
def content_range
|
1238
|
-
return nil unless @header['content-range']
|
1239
|
-
m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
|
1240
|
-
raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
|
1241
|
-
m[1].to_i .. m[2].to_i + 1
|
1242
|
-
end
|
1243
|
-
|
1244
|
-
# The length of the range represented in Content-Range: header.
|
1245
|
-
def range_length
|
1246
|
-
r = content_range() or return nil
|
1247
|
-
r.end - r.begin
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
def content_type
|
1251
|
-
"#{main_type()}/#{sub_type()}"
|
1252
|
-
end
|
1253
|
-
|
1254
|
-
def main_type
|
1255
|
-
return nil unless @header['content-type']
|
1256
|
-
self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
def sub_type
|
1260
|
-
return nil unless @header['content-type']
|
1261
|
-
self['Content-Type'].split(';').first.to_s.split('/')[1].to_s.strip
|
1262
|
-
end
|
1263
|
-
|
1264
|
-
def type_params
|
1265
|
-
result = {}
|
1266
|
-
self['Content-Type'].to_s.split(';')[1..-1].each do |param|
|
1267
|
-
k, v = *param.split('=', 2)
|
1268
|
-
result[k.strip] = v.strip
|
1269
|
-
end
|
1270
|
-
result
|
1271
|
-
end
|
1272
|
-
|
1273
|
-
def set_content_type(type, params = {})
|
1274
|
-
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
alias content_type= set_content_type
|
1278
|
-
|
1279
|
-
# Set the Authorization: header for "Basic" authorization.
|
1280
|
-
def basic_auth(account, password)
|
1281
|
-
@header['authorization'] = [basic_encode(account, password)]
|
1282
|
-
end
|
1283
|
-
|
1284
|
-
# Set Proxy-Authorization: header for "Basic" authorization.
|
1285
|
-
def proxy_basic_auth(account, password)
|
1286
|
-
@header['proxy-authorization'] = [basic_encode(account, password)]
|
1287
|
-
end
|
1288
|
-
|
1289
|
-
def basic_encode(account, password)
|
1290
|
-
'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
|
1291
|
-
end
|
1292
|
-
private :basic_encode
|
1293
|
-
|
1294
|
-
end
|
1295
|
-
|
1296
|
-
|
1297
|
-
#
|
1298
|
-
# Parent of HTTPRequest class. Do not use this directly; use
|
1299
|
-
# a subclass of HTTPRequest.
|
1300
|
-
#
|
1301
|
-
# Mixes in the HTTPHeader module.
|
1302
|
-
#
|
1303
|
-
class HTTPGenericRequest
|
1304
|
-
|
1305
|
-
include HTTPHeader
|
1306
|
-
|
1307
|
-
def initialize(m, reqbody, resbody, path, initheader = nil)
|
1308
|
-
@method = m
|
1309
|
-
@request_has_body = reqbody
|
1310
|
-
@response_has_body = resbody
|
1311
|
-
raise ArgumentError, "HTTP request path is empty" if path.empty?
|
1312
|
-
@path = path
|
1313
|
-
initialize_http_header initheader
|
1314
|
-
self['Accept'] ||= '*/*'
|
1315
|
-
@body = nil
|
1316
|
-
@body_stream = nil
|
1317
|
-
end
|
1318
|
-
|
1319
|
-
attr_reader :method
|
1320
|
-
attr_reader :path
|
1321
|
-
|
1322
|
-
def inspect
|
1323
|
-
"\#<#{self.class} #{@method}>"
|
1324
|
-
end
|
1325
|
-
|
1326
|
-
def request_body_permitted?
|
1327
|
-
@request_has_body
|
1328
|
-
end
|
1329
|
-
|
1330
|
-
def response_body_permitted?
|
1331
|
-
@response_has_body
|
1332
|
-
end
|
1333
|
-
|
1334
|
-
def body_exist?
|
1335
|
-
warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
|
1336
|
-
response_body_permitted?
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
attr_reader :body
|
1340
|
-
|
1341
|
-
def body=(str)
|
1342
|
-
@body = str
|
1343
|
-
@body_stream = nil
|
1344
|
-
str
|
1345
|
-
end
|
1346
|
-
|
1347
|
-
attr_reader :body_stream
|
1348
|
-
|
1349
|
-
def body_stream=(input)
|
1350
|
-
@body = nil
|
1351
|
-
@body_stream = input
|
1352
|
-
input
|
1353
|
-
end
|
1354
|
-
|
1355
|
-
def set_body_internal(str) #:nodoc: internal use only
|
1356
|
-
raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
|
1357
|
-
self.body = str if str
|
1358
|
-
end
|
1359
|
-
|
1360
|
-
#
|
1361
|
-
# write
|
1362
|
-
#
|
1363
|
-
|
1364
|
-
def exec(sock, ver, path) #:nodoc: internal use only
|
1365
|
-
if @body
|
1366
|
-
send_request_with_body sock, ver, path, @body
|
1367
|
-
elsif @body_stream
|
1368
|
-
send_request_with_body_stream sock, ver, path, @body_stream
|
1369
|
-
else
|
1370
|
-
write_header sock, ver, path
|
1371
|
-
end
|
1372
|
-
end
|
1373
|
-
|
1374
|
-
private
|
1375
|
-
|
1376
|
-
def send_request_with_body(sock, ver, path, body)
|
1377
|
-
self.content_length = body.length
|
1378
|
-
delete 'Transfer-Encoding'
|
1379
|
-
unless content_type()
|
1380
|
-
warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
|
1381
|
-
set_content_type 'application/x-www-form-urlencoded'
|
1382
|
-
end
|
1383
|
-
write_header sock, ver, path
|
1384
|
-
sock.write body
|
1385
|
-
end
|
1386
|
-
|
1387
|
-
def send_request_with_body_stream(sock, ver, path, f)
|
1388
|
-
raise ArgumentError, "Content-Length not given and Transfer-Encoding is not `chunked'" unless content_length() or chunked?
|
1389
|
-
unless content_type()
|
1390
|
-
warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
|
1391
|
-
set_content_type 'application/x-www-form-urlencoded'
|
1392
|
-
end
|
1393
|
-
write_header sock, ver, path
|
1394
|
-
if chunked?
|
1395
|
-
while s = f.read(1024)
|
1396
|
-
sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
|
1397
|
-
end
|
1398
|
-
sock.write "0\r\n\r\n"
|
1399
|
-
else
|
1400
|
-
while s = f.read(1024)
|
1401
|
-
sock.write s
|
1402
|
-
end
|
1403
|
-
end
|
1404
|
-
end
|
1405
|
-
|
1406
|
-
def write_header(sock, ver, path)
|
1407
|
-
buf = "#{@method} #{path} HTTP/#{ver}\r\n"
|
1408
|
-
each_capitalized do |k,v|
|
1409
|
-
buf << "#{k}: #{v}\r\n"
|
1410
|
-
end
|
1411
|
-
buf << "\r\n"
|
1412
|
-
sock.write buf
|
1413
|
-
end
|
1414
|
-
|
1415
|
-
end
|
1416
|
-
|
1417
|
-
|
1418
|
-
#
|
1419
|
-
# HTTP request class. This class wraps request header and entity path.
|
1420
|
-
# You *must* use its subclass, Net::HTTP::Get, Post, Head.
|
1421
|
-
#
|
1422
|
-
class HTTPRequest < HTTPGenericRequest
|
1423
|
-
|
1424
|
-
# Creates HTTP request object.
|
1425
|
-
def initialize(path, initheader = nil)
|
1426
|
-
super self.class::METHOD,
|
1427
|
-
self.class::REQUEST_HAS_BODY,
|
1428
|
-
self.class::RESPONSE_HAS_BODY,
|
1429
|
-
path, initheader
|
1430
|
-
end
|
1431
|
-
end
|
1432
|
-
|
1433
|
-
|
1434
|
-
class HTTP
|
1435
|
-
class Get < HTTPRequest
|
1436
|
-
METHOD = 'GET'
|
1437
|
-
REQUEST_HAS_BODY = false
|
1438
|
-
RESPONSE_HAS_BODY = true
|
1439
|
-
end
|
1440
|
-
|
1441
|
-
class Head < HTTPRequest
|
1442
|
-
METHOD = 'HEAD'
|
1443
|
-
REQUEST_HAS_BODY = false
|
1444
|
-
RESPONSE_HAS_BODY = false
|
1445
|
-
end
|
1446
|
-
|
1447
|
-
class Post < HTTPRequest
|
1448
|
-
METHOD = 'POST'
|
1449
|
-
REQUEST_HAS_BODY = true
|
1450
|
-
RESPONSE_HAS_BODY = true
|
1451
|
-
end
|
1452
|
-
|
1453
|
-
class Put < HTTPRequest
|
1454
|
-
METHOD = 'PUT'
|
1455
|
-
REQUEST_HAS_BODY = true
|
1456
|
-
RESPONSE_HAS_BODY = true
|
1457
|
-
end
|
1458
|
-
|
1459
|
-
class Proppatch < HTTPRequest
|
1460
|
-
METHOD = 'PROPPATCH'
|
1461
|
-
REQUEST_HAS_BODY = true
|
1462
|
-
RESPONSE_HAS_BODY = true
|
1463
|
-
end
|
1464
|
-
|
1465
|
-
class Lock < HTTPRequest
|
1466
|
-
METHOD = 'LOCK'
|
1467
|
-
REQUEST_HAS_BODY = true
|
1468
|
-
RESPONSE_HAS_BODY = true
|
1469
|
-
end
|
1470
|
-
|
1471
|
-
class Unlock < HTTPRequest
|
1472
|
-
METHOD = 'UNLOCK'
|
1473
|
-
REQUEST_HAS_BODY = true
|
1474
|
-
RESPONSE_HAS_BODY = true
|
1475
|
-
end
|
1476
|
-
|
1477
|
-
class Options < HTTPRequest
|
1478
|
-
METHOD = 'OPTIONS'
|
1479
|
-
REQUEST_HAS_BODY = false
|
1480
|
-
RESPONSE_HAS_BODY = false
|
1481
|
-
end
|
1482
|
-
|
1483
|
-
class Propfind < HTTPRequest
|
1484
|
-
METHOD = 'PROPFIND'
|
1485
|
-
REQUEST_HAS_BODY = true
|
1486
|
-
RESPONSE_HAS_BODY = true
|
1487
|
-
end
|
1488
|
-
|
1489
|
-
class Delete < HTTPRequest
|
1490
|
-
METHOD = 'DELETE'
|
1491
|
-
REQUEST_HAS_BODY = false
|
1492
|
-
RESPONSE_HAS_BODY = true
|
1493
|
-
end
|
1494
|
-
|
1495
|
-
class Move < HTTPRequest
|
1496
|
-
METHOD = 'MOVE'
|
1497
|
-
REQUEST_HAS_BODY = false
|
1498
|
-
RESPONSE_HAS_BODY = true
|
1499
|
-
end
|
1500
|
-
|
1501
|
-
class Copy < HTTPRequest
|
1502
|
-
METHOD = 'COPY'
|
1503
|
-
REQUEST_HAS_BODY = false
|
1504
|
-
RESPONSE_HAS_BODY = true
|
1505
|
-
end
|
1506
|
-
|
1507
|
-
class Mkcol < HTTPRequest
|
1508
|
-
METHOD = 'MKCOL'
|
1509
|
-
REQUEST_HAS_BODY = true
|
1510
|
-
RESPONSE_HAS_BODY = true
|
1511
|
-
end
|
1512
|
-
|
1513
|
-
class Trace < HTTPRequest
|
1514
|
-
METHOD = 'TRACE'
|
1515
|
-
REQUEST_HAS_BODY = false
|
1516
|
-
RESPONSE_HAS_BODY = true
|
1517
|
-
end
|
1518
|
-
end
|
1519
|
-
|
1520
|
-
|
1521
|
-
###
|
1522
|
-
### Response
|
1523
|
-
###
|
1524
|
-
|
1525
|
-
# HTTP exception class.
|
1526
|
-
# You must use its subclasses.
|
1527
|
-
module HTTPExceptions
|
1528
|
-
def initialize(msg, res) #:nodoc:
|
1529
|
-
super msg
|
1530
|
-
@response = res
|
1531
|
-
end
|
1532
|
-
attr_reader :response
|
1533
|
-
alias data response #:nodoc: obsolete
|
1534
|
-
end
|
1535
|
-
class HTTPError < ProtocolError
|
1536
|
-
include HTTPExceptions
|
1537
|
-
end
|
1538
|
-
class HTTPRetriableError < ProtoRetriableError
|
1539
|
-
include HTTPExceptions
|
1540
|
-
end
|
1541
|
-
class HTTPServerException < ProtoServerError
|
1542
|
-
# We cannot use the name "HTTPServerError", it is the name of the response.
|
1543
|
-
include HTTPExceptions
|
1544
|
-
end
|
1545
|
-
class HTTPFatalError < ProtoFatalError
|
1546
|
-
include HTTPExceptions
|
1547
|
-
end
|
1548
|
-
|
1549
|
-
|
1550
|
-
# HTTP response class. This class wraps response header and entity.
|
1551
|
-
# Mixes in the HTTPHeader module, which provides access to response
|
1552
|
-
# header values both via hash-like methods and individual readers.
|
1553
|
-
# Note that each possible HTTP response code defines its own
|
1554
|
-
# HTTPResponse subclass. These are listed below.
|
1555
|
-
# All classes are
|
1556
|
-
# defined under the Net module. Indentation indicates inheritance.
|
1557
|
-
#
|
1558
|
-
# xxx HTTPResponse
|
1559
|
-
#
|
1560
|
-
# 1xx HTTPInformation
|
1561
|
-
# 100 HTTPContinue
|
1562
|
-
# 101 HTTPSwitchProtocol
|
1563
|
-
#
|
1564
|
-
# 2xx HTTPSuccess
|
1565
|
-
# 200 HTTPOK
|
1566
|
-
# 201 HTTPCreated
|
1567
|
-
# 202 HTTPAccepted
|
1568
|
-
# 203 HTTPNonAuthoritativeInformation
|
1569
|
-
# 204 HTTPNoContent
|
1570
|
-
# 205 HTTPResetContent
|
1571
|
-
# 206 HTTPPartialContent
|
1572
|
-
#
|
1573
|
-
# 3xx HTTPRedirection
|
1574
|
-
# 300 HTTPMultipleChoice
|
1575
|
-
# 301 HTTPMovedPermanently
|
1576
|
-
# 302 HTTPFound
|
1577
|
-
# 303 HTTPSeeOther
|
1578
|
-
# 304 HTTPNotModified
|
1579
|
-
# 305 HTTPUseProxy
|
1580
|
-
# 307 HTTPTemporaryRedirect
|
1581
|
-
#
|
1582
|
-
# 4xx HTTPClientError
|
1583
|
-
# 400 HTTPBadRequest
|
1584
|
-
# 401 HTTPUnauthorized
|
1585
|
-
# 402 HTTPPaymentRequired
|
1586
|
-
# 403 HTTPForbidden
|
1587
|
-
# 404 HTTPNotFound
|
1588
|
-
# 405 HTTPMethodNotAllowed
|
1589
|
-
# 406 HTTPNotAcceptable
|
1590
|
-
# 407 HTTPProxyAuthenticationRequired
|
1591
|
-
# 408 HTTPRequestTimeOut
|
1592
|
-
# 409 HTTPConflict
|
1593
|
-
# 410 HTTPGone
|
1594
|
-
# 411 HTTPLengthRequired
|
1595
|
-
# 412 HTTPPreconditionFailed
|
1596
|
-
# 413 HTTPRequestEntityTooLarge
|
1597
|
-
# 414 HTTPRequestURITooLong
|
1598
|
-
# 415 HTTPUnsupportedMediaType
|
1599
|
-
# 416 HTTPRequestedRangeNotSatisfiable
|
1600
|
-
# 417 HTTPExpectationFailed
|
1601
|
-
#
|
1602
|
-
# 5xx HTTPServerError
|
1603
|
-
# 500 HTTPInternalServerError
|
1604
|
-
# 501 HTTPNotImplemented
|
1605
|
-
# 502 HTTPBadGateway
|
1606
|
-
# 503 HTTPServiceUnavailable
|
1607
|
-
# 504 HTTPGatewayTimeOut
|
1608
|
-
# 505 HTTPVersionNotSupported
|
1609
|
-
#
|
1610
|
-
# xxx HTTPUnknownResponse
|
1611
|
-
#
|
1612
|
-
class HTTPResponse
|
1613
|
-
# true if the response has body.
|
1614
|
-
def HTTPResponse.body_permitted?
|
1615
|
-
self::HAS_BODY
|
1616
|
-
end
|
1617
|
-
|
1618
|
-
def HTTPResponse.exception_type # :nodoc: internal use only
|
1619
|
-
self::EXCEPTION_TYPE
|
1620
|
-
end
|
1621
|
-
end # redefined after
|
1622
|
-
|
1623
|
-
# :stopdoc:
|
1624
|
-
|
1625
|
-
class HTTPUnknownResponse < HTTPResponse
|
1626
|
-
HAS_BODY = true
|
1627
|
-
EXCEPTION_TYPE = HTTPError
|
1628
|
-
end
|
1629
|
-
class HTTPInformation < HTTPResponse # 1xx
|
1630
|
-
HAS_BODY = false
|
1631
|
-
EXCEPTION_TYPE = HTTPError
|
1632
|
-
end
|
1633
|
-
class HTTPSuccess < HTTPResponse # 2xx
|
1634
|
-
HAS_BODY = true
|
1635
|
-
EXCEPTION_TYPE = HTTPError
|
1636
|
-
end
|
1637
|
-
class HTTPRedirection < HTTPResponse # 3xx
|
1638
|
-
HAS_BODY = true
|
1639
|
-
EXCEPTION_TYPE = HTTPRetriableError
|
1640
|
-
end
|
1641
|
-
class HTTPClientError < HTTPResponse # 4xx
|
1642
|
-
HAS_BODY = true
|
1643
|
-
EXCEPTION_TYPE = HTTPServerException # for backward compatibility
|
1644
|
-
end
|
1645
|
-
class HTTPServerError < HTTPResponse # 5xx
|
1646
|
-
HAS_BODY = true
|
1647
|
-
EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
|
1648
|
-
end
|
1649
|
-
|
1650
|
-
class HTTPContinue < HTTPInformation # 100
|
1651
|
-
HAS_BODY = false
|
1652
|
-
end
|
1653
|
-
class HTTPSwitchProtocol < HTTPInformation # 101
|
1654
|
-
HAS_BODY = false
|
1655
|
-
end
|
1656
|
-
|
1657
|
-
class HTTPOK < HTTPSuccess # 200
|
1658
|
-
HAS_BODY = true
|
1659
|
-
end
|
1660
|
-
class HTTPCreated < HTTPSuccess # 201
|
1661
|
-
HAS_BODY = true
|
1662
|
-
end
|
1663
|
-
class HTTPAccepted < HTTPSuccess # 202
|
1664
|
-
HAS_BODY = true
|
1665
|
-
end
|
1666
|
-
class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
|
1667
|
-
HAS_BODY = true
|
1668
|
-
end
|
1669
|
-
class HTTPNoContent < HTTPSuccess # 204
|
1670
|
-
HAS_BODY = false
|
1671
|
-
end
|
1672
|
-
class HTTPResetContent < HTTPSuccess # 205
|
1673
|
-
HAS_BODY = false
|
1674
|
-
end
|
1675
|
-
class HTTPPartialContent < HTTPSuccess # 206
|
1676
|
-
HAS_BODY = true
|
1677
|
-
end
|
1678
|
-
|
1679
|
-
class HTTPMultipleChoice < HTTPRedirection # 300
|
1680
|
-
HAS_BODY = true
|
1681
|
-
end
|
1682
|
-
class HTTPMovedPermanently < HTTPRedirection # 301
|
1683
|
-
HAS_BODY = true
|
1684
|
-
end
|
1685
|
-
class HTTPFound < HTTPRedirection # 302
|
1686
|
-
HAS_BODY = true
|
1687
|
-
end
|
1688
|
-
HTTPMovedTemporarily = HTTPFound
|
1689
|
-
class HTTPSeeOther < HTTPRedirection # 303
|
1690
|
-
HAS_BODY = true
|
1691
|
-
end
|
1692
|
-
class HTTPNotModified < HTTPRedirection # 304
|
1693
|
-
HAS_BODY = false
|
1694
|
-
end
|
1695
|
-
class HTTPUseProxy < HTTPRedirection # 305
|
1696
|
-
HAS_BODY = false
|
1697
|
-
end
|
1698
|
-
# 306 unused
|
1699
|
-
class HTTPTemporaryRedirect < HTTPRedirection # 307
|
1700
|
-
HAS_BODY = true
|
1701
|
-
end
|
1702
|
-
|
1703
|
-
class HTTPBadRequest < HTTPClientError # 400
|
1704
|
-
HAS_BODY = true
|
1705
|
-
end
|
1706
|
-
class HTTPUnauthorized < HTTPClientError # 401
|
1707
|
-
HAS_BODY = true
|
1708
|
-
end
|
1709
|
-
class HTTPPaymentRequired < HTTPClientError # 402
|
1710
|
-
HAS_BODY = true
|
1711
|
-
end
|
1712
|
-
class HTTPForbidden < HTTPClientError # 403
|
1713
|
-
HAS_BODY = true
|
1714
|
-
end
|
1715
|
-
class HTTPNotFound < HTTPClientError # 404
|
1716
|
-
HAS_BODY = true
|
1717
|
-
end
|
1718
|
-
class HTTPMethodNotAllowed < HTTPClientError # 405
|
1719
|
-
HAS_BODY = true
|
1720
|
-
end
|
1721
|
-
class HTTPNotAcceptable < HTTPClientError # 406
|
1722
|
-
HAS_BODY = true
|
1723
|
-
end
|
1724
|
-
class HTTPProxyAuthenticationRequired < HTTPClientError # 407
|
1725
|
-
HAS_BODY = true
|
1726
|
-
end
|
1727
|
-
class HTTPRequestTimeOut < HTTPClientError # 408
|
1728
|
-
HAS_BODY = true
|
1729
|
-
end
|
1730
|
-
class HTTPConflict < HTTPClientError # 409
|
1731
|
-
HAS_BODY = true
|
1732
|
-
end
|
1733
|
-
class HTTPGone < HTTPClientError # 410
|
1734
|
-
HAS_BODY = true
|
1735
|
-
end
|
1736
|
-
class HTTPLengthRequired < HTTPClientError # 411
|
1737
|
-
HAS_BODY = true
|
1738
|
-
end
|
1739
|
-
class HTTPPreconditionFailed < HTTPClientError # 412
|
1740
|
-
HAS_BODY = true
|
1741
|
-
end
|
1742
|
-
class HTTPRequestEntityTooLarge < HTTPClientError # 413
|
1743
|
-
HAS_BODY = true
|
1744
|
-
end
|
1745
|
-
class HTTPRequestURITooLong < HTTPClientError # 414
|
1746
|
-
HAS_BODY = true
|
1747
|
-
end
|
1748
|
-
HTTPRequestURITooLarge = HTTPRequestURITooLong
|
1749
|
-
class HTTPUnsupportedMediaType < HTTPClientError # 415
|
1750
|
-
HAS_BODY = true
|
1751
|
-
end
|
1752
|
-
class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
|
1753
|
-
HAS_BODY = true
|
1754
|
-
end
|
1755
|
-
class HTTPExpectationFailed < HTTPClientError # 417
|
1756
|
-
HAS_BODY = true
|
1757
|
-
end
|
1758
|
-
|
1759
|
-
class HTTPInternalServerError < HTTPServerError # 500
|
1760
|
-
HAS_BODY = true
|
1761
|
-
end
|
1762
|
-
class HTTPNotImplemented < HTTPServerError # 501
|
1763
|
-
HAS_BODY = true
|
1764
|
-
end
|
1765
|
-
class HTTPBadGateway < HTTPServerError # 502
|
1766
|
-
HAS_BODY = true
|
1767
|
-
end
|
1768
|
-
class HTTPServiceUnavailable < HTTPServerError # 503
|
1769
|
-
HAS_BODY = true
|
1770
|
-
end
|
1771
|
-
class HTTPGatewayTimeOut < HTTPServerError # 504
|
1772
|
-
HAS_BODY = true
|
1773
|
-
end
|
1774
|
-
class HTTPVersionNotSupported < HTTPServerError # 505
|
1775
|
-
HAS_BODY = true
|
1776
|
-
end
|
1777
|
-
|
1778
|
-
# :startdoc:
|
1779
|
-
|
1780
|
-
|
1781
|
-
class HTTPResponse # redefine
|
1782
|
-
|
1783
|
-
CODE_CLASS_TO_OBJ = {
|
1784
|
-
'1' => HTTPInformation,
|
1785
|
-
'2' => HTTPSuccess,
|
1786
|
-
'3' => HTTPRedirection,
|
1787
|
-
'4' => HTTPClientError,
|
1788
|
-
'5' => HTTPServerError
|
1789
|
-
}
|
1790
|
-
CODE_TO_OBJ = {
|
1791
|
-
'100' => HTTPContinue,
|
1792
|
-
'101' => HTTPSwitchProtocol,
|
1793
|
-
|
1794
|
-
'200' => HTTPOK,
|
1795
|
-
'201' => HTTPCreated,
|
1796
|
-
'202' => HTTPAccepted,
|
1797
|
-
'203' => HTTPNonAuthoritativeInformation,
|
1798
|
-
'204' => HTTPNoContent,
|
1799
|
-
'205' => HTTPResetContent,
|
1800
|
-
'206' => HTTPPartialContent,
|
1801
|
-
|
1802
|
-
'300' => HTTPMultipleChoice,
|
1803
|
-
'301' => HTTPMovedPermanently,
|
1804
|
-
'302' => HTTPFound,
|
1805
|
-
'303' => HTTPSeeOther,
|
1806
|
-
'304' => HTTPNotModified,
|
1807
|
-
'305' => HTTPUseProxy,
|
1808
|
-
'307' => HTTPTemporaryRedirect,
|
1809
|
-
|
1810
|
-
'400' => HTTPBadRequest,
|
1811
|
-
'401' => HTTPUnauthorized,
|
1812
|
-
'402' => HTTPPaymentRequired,
|
1813
|
-
'403' => HTTPForbidden,
|
1814
|
-
'404' => HTTPNotFound,
|
1815
|
-
'405' => HTTPMethodNotAllowed,
|
1816
|
-
'406' => HTTPNotAcceptable,
|
1817
|
-
'407' => HTTPProxyAuthenticationRequired,
|
1818
|
-
'408' => HTTPRequestTimeOut,
|
1819
|
-
'409' => HTTPConflict,
|
1820
|
-
'410' => HTTPGone,
|
1821
|
-
'411' => HTTPLengthRequired,
|
1822
|
-
'412' => HTTPPreconditionFailed,
|
1823
|
-
'413' => HTTPRequestEntityTooLarge,
|
1824
|
-
'414' => HTTPRequestURITooLong,
|
1825
|
-
'415' => HTTPUnsupportedMediaType,
|
1826
|
-
'416' => HTTPRequestedRangeNotSatisfiable,
|
1827
|
-
'417' => HTTPExpectationFailed,
|
1828
|
-
|
1829
|
-
'500' => HTTPInternalServerError,
|
1830
|
-
'501' => HTTPNotImplemented,
|
1831
|
-
'502' => HTTPBadGateway,
|
1832
|
-
'503' => HTTPServiceUnavailable,
|
1833
|
-
'504' => HTTPGatewayTimeOut,
|
1834
|
-
'505' => HTTPVersionNotSupported
|
1835
|
-
}
|
1836
|
-
|
1837
|
-
class << HTTPResponse
|
1838
|
-
def read_new(sock) #:nodoc: internal use only
|
1839
|
-
httpv, code, msg = read_status_line(sock)
|
1840
|
-
res = response_class(code).new(httpv, code, msg)
|
1841
|
-
each_response_header(sock) do |k,v|
|
1842
|
-
res.add_field k, v
|
1843
|
-
end
|
1844
|
-
res
|
1845
|
-
end
|
1846
|
-
|
1847
|
-
private
|
1848
|
-
|
1849
|
-
def read_status_line(sock)
|
1850
|
-
str = sock.readline
|
1851
|
-
m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
|
1852
|
-
raise HTTPBadResponse, "wrong status line: #{str.dump}"
|
1853
|
-
m.captures
|
1854
|
-
end
|
1855
|
-
|
1856
|
-
def response_class(code)
|
1857
|
-
CODE_TO_OBJ[code] or
|
1858
|
-
CODE_CLASS_TO_OBJ[code[0,1]] or
|
1859
|
-
HTTPUnknownResponse
|
1860
|
-
end
|
1861
|
-
|
1862
|
-
def each_response_header(sock)
|
1863
|
-
while true
|
1864
|
-
line = sock.readuntil("\n", true).sub(/\s+\z/, '')
|
1865
|
-
break if line.empty?
|
1866
|
-
m = /\A([^:]+):\s*/.match(line) or
|
1867
|
-
raise HTTPBadResponse, 'wrong header line format'
|
1868
|
-
yield m[1], m.post_match
|
1869
|
-
end
|
1870
|
-
end
|
1871
|
-
end
|
1872
|
-
|
1873
|
-
# next is to fix bug in RDoc, where the private inside class << self
|
1874
|
-
# spills out.
|
1875
|
-
public
|
1876
|
-
|
1877
|
-
include HTTPHeader
|
1878
|
-
|
1879
|
-
def initialize(httpv, code, msg) #:nodoc: internal use only
|
1880
|
-
@http_version = httpv
|
1881
|
-
@code = code
|
1882
|
-
@message = msg
|
1883
|
-
initialize_http_header nil
|
1884
|
-
@body = nil
|
1885
|
-
@read = false
|
1886
|
-
end
|
1887
|
-
|
1888
|
-
# The HTTP version supported by the server.
|
1889
|
-
attr_reader :http_version
|
1890
|
-
|
1891
|
-
# HTTP result code string. For example, '302'. You can also
|
1892
|
-
# determine the response type by which response subclass the
|
1893
|
-
# response object is an instance of.
|
1894
|
-
attr_reader :code
|
1895
|
-
|
1896
|
-
# HTTP result message. For example, 'Not Found'.
|
1897
|
-
attr_reader :message
|
1898
|
-
alias msg message # :nodoc: obsolete
|
1899
|
-
|
1900
|
-
def inspect
|
1901
|
-
"#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
|
1902
|
-
end
|
1903
|
-
|
1904
|
-
# For backward compatibility.
|
1905
|
-
# To allow Net::HTTP 1.1 style assignment
|
1906
|
-
# e.g.
|
1907
|
-
# response, body = Net::HTTP.get(....)
|
1908
|
-
#
|
1909
|
-
def to_ary
|
1910
|
-
warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
|
1911
|
-
res = self.dup
|
1912
|
-
class << res
|
1913
|
-
undef to_ary
|
1914
|
-
end
|
1915
|
-
[res, res.body]
|
1916
|
-
end
|
1917
|
-
|
1918
|
-
#
|
1919
|
-
# response <-> exception relationship
|
1920
|
-
#
|
1921
|
-
|
1922
|
-
def code_type #:nodoc:
|
1923
|
-
self.class
|
1924
|
-
end
|
1925
|
-
|
1926
|
-
def error! #:nodoc:
|
1927
|
-
raise error_type().new(@code + ' ' + @message.dump, self)
|
1928
|
-
end
|
1929
|
-
|
1930
|
-
def error_type #:nodoc:
|
1931
|
-
self.class::EXCEPTION_TYPE
|
1932
|
-
end
|
1933
|
-
|
1934
|
-
# Raises HTTP error if the response is not 2xx.
|
1935
|
-
def value
|
1936
|
-
error! unless self.kind_of?(HTTPSuccess)
|
1937
|
-
end
|
1938
|
-
|
1939
|
-
#
|
1940
|
-
# header (for backward compatibility only; DO NOT USE)
|
1941
|
-
#
|
1942
|
-
|
1943
|
-
def response #:nodoc:
|
1944
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
|
1945
|
-
self
|
1946
|
-
end
|
1947
|
-
|
1948
|
-
def header #:nodoc:
|
1949
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
|
1950
|
-
self
|
1951
|
-
end
|
1952
|
-
|
1953
|
-
def read_header #:nodoc:
|
1954
|
-
warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
|
1955
|
-
self
|
1956
|
-
end
|
1957
|
-
|
1958
|
-
#
|
1959
|
-
# body
|
1960
|
-
#
|
1961
|
-
|
1962
|
-
def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
|
1963
|
-
@socket = sock
|
1964
|
-
@body_exist = reqmethodallowbody && self.class.body_permitted?
|
1965
|
-
begin
|
1966
|
-
yield
|
1967
|
-
self.body # ensure to read body
|
1968
|
-
ensure
|
1969
|
-
@socket = nil
|
1970
|
-
end
|
1971
|
-
end
|
1972
|
-
|
1973
|
-
# Gets entity body. If the block given, yields it to +block+.
|
1974
|
-
# The body is provided in fragments, as it is read in from the socket.
|
1975
|
-
#
|
1976
|
-
# Calling this method a second or subsequent time will return the
|
1977
|
-
# already read string.
|
1978
|
-
#
|
1979
|
-
# http.request_get('/index.html') {|res|
|
1980
|
-
# puts res.read_body
|
1981
|
-
# }
|
1982
|
-
#
|
1983
|
-
# http.request_get('/index.html') {|res|
|
1984
|
-
# p res.read_body.object_id # 538149362
|
1985
|
-
# p res.read_body.object_id # 538149362
|
1986
|
-
# }
|
1987
|
-
#
|
1988
|
-
# # using iterator
|
1989
|
-
# http.request_get('/index.html') {|res|
|
1990
|
-
# res.read_body do |segment|
|
1991
|
-
# print segment
|
1992
|
-
# end
|
1993
|
-
# }
|
1994
|
-
#
|
1995
|
-
def read_body(dest = nil, &block)
|
1996
|
-
if @read
|
1997
|
-
raise IOError, "#{self.class}\#read_body called twice" if dest or block
|
1998
|
-
return @body
|
1999
|
-
end
|
2000
|
-
to = procdest(dest, block)
|
2001
|
-
stream_check
|
2002
|
-
if @body_exist
|
2003
|
-
read_body_0 to
|
2004
|
-
@body = to
|
2005
|
-
else
|
2006
|
-
@body = nil
|
2007
|
-
end
|
2008
|
-
@read = true
|
2009
|
-
|
2010
|
-
@body
|
2011
|
-
end
|
2012
|
-
|
2013
|
-
# Returns the entity body.
|
2014
|
-
#
|
2015
|
-
# Calling this method a second or subsequent time will return the
|
2016
|
-
# already read string.
|
2017
|
-
#
|
2018
|
-
# http.request_get('/index.html') {|res|
|
2019
|
-
# puts res.body
|
2020
|
-
# }
|
2021
|
-
#
|
2022
|
-
# http.request_get('/index.html') {|res|
|
2023
|
-
# p res.body.object_id # 538149362
|
2024
|
-
# p res.body.object_id # 538149362
|
2025
|
-
# }
|
2026
|
-
#
|
2027
|
-
def body
|
2028
|
-
read_body()
|
2029
|
-
end
|
2030
|
-
|
2031
|
-
alias entity body #:nodoc: obsolete
|
2032
|
-
|
2033
|
-
private
|
2034
|
-
|
2035
|
-
def read_body_0(dest)
|
2036
|
-
if chunked?
|
2037
|
-
read_chunked dest
|
2038
|
-
return
|
2039
|
-
end
|
2040
|
-
clen = content_length()
|
2041
|
-
if clen
|
2042
|
-
@socket.read clen, dest, true # ignore EOF
|
2043
|
-
return
|
2044
|
-
end
|
2045
|
-
clen = range_length()
|
2046
|
-
if clen
|
2047
|
-
@socket.read clen, dest
|
2048
|
-
return
|
2049
|
-
end
|
2050
|
-
@socket.read_all dest
|
2051
|
-
end
|
2052
|
-
|
2053
|
-
def read_chunked(dest)
|
2054
|
-
len = nil
|
2055
|
-
total = 0
|
2056
|
-
while true
|
2057
|
-
line = @socket.readline
|
2058
|
-
hexlen = line.slice(/[0-9a-fA-F]+/) or
|
2059
|
-
raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
2060
|
-
len = hexlen.hex
|
2061
|
-
break if len == 0
|
2062
|
-
@socket.read len, dest; total += len
|
2063
|
-
@socket.read 2 # \r\n
|
2064
|
-
end
|
2065
|
-
until @socket.readline.empty?
|
2066
|
-
# none
|
2067
|
-
end
|
2068
|
-
end
|
2069
|
-
|
2070
|
-
def stream_check
|
2071
|
-
raise IOError, 'attempt to read body out of block' if @socket.closed?
|
2072
|
-
end
|
2073
|
-
|
2074
|
-
def procdest(dest, block)
|
2075
|
-
raise ArgumentError, 'both arg and block given for HTTP method' \
|
2076
|
-
if dest and block
|
2077
|
-
if block
|
2078
|
-
ReadAdapter.new(block)
|
2079
|
-
else
|
2080
|
-
dest || ''
|
2081
|
-
end
|
2082
|
-
end
|
2083
|
-
|
2084
|
-
end
|
2085
|
-
|
2086
|
-
|
2087
|
-
# :enddoc:
|
2088
|
-
|
2089
|
-
#--
|
2090
|
-
# for backward compatibility
|
2091
|
-
class HTTP
|
2092
|
-
ProxyMod = ProxyDelta
|
2093
|
-
end
|
2094
|
-
module NetPrivate
|
2095
|
-
HTTPRequest = ::Net::HTTPRequest
|
2096
|
-
end
|
2097
|
-
|
2098
|
-
HTTPInformationCode = HTTPInformation
|
2099
|
-
HTTPSuccessCode = HTTPSuccess
|
2100
|
-
HTTPRedirectionCode = HTTPRedirection
|
2101
|
-
HTTPRetriableCode = HTTPRedirection
|
2102
|
-
HTTPClientErrorCode = HTTPClientError
|
2103
|
-
HTTPFatalErrorCode = HTTPClientError
|
2104
|
-
HTTPServerErrorCode = HTTPServerError
|
2105
|
-
HTTPResponceReceiver = HTTPResponse
|
2106
|
-
|
2107
|
-
end # module Net
|