rubysl-net-http 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +0 -1
  3. data/.travis.yml +7 -0
  4. data/README.md +2 -2
  5. data/Rakefile +0 -1
  6. data/lib/net/http.rb +1 -0
  7. data/lib/rubysl/net/http.rb +2 -0
  8. data/lib/rubysl/net/http/http.rb +2290 -0
  9. data/lib/rubysl/net/http/https.rb +173 -0
  10. data/lib/{rubysl-net-http → rubysl/net/http}/version.rb +2 -2
  11. data/rubysl-net-http.gemspec +18 -18
  12. data/spec/HTTPBadResponse_spec.rb +7 -0
  13. data/spec/HTTPError_spec.rb +11 -0
  14. data/spec/HTTPFatalError_spec.rb +11 -0
  15. data/spec/HTTPHeaderSyntaxError_spec.rb +7 -0
  16. data/spec/HTTPRetriableError_spec.rb +11 -0
  17. data/spec/HTTPServerException_spec.rb +11 -0
  18. data/spec/http/Proxy_spec.rb +34 -0
  19. data/spec/http/active_spec.rb +7 -0
  20. data/spec/http/address_spec.rb +8 -0
  21. data/spec/http/close_on_empty_response_spec.rb +9 -0
  22. data/spec/http/copy_spec.rb +26 -0
  23. data/spec/http/default_port_spec.rb +7 -0
  24. data/spec/http/delete_spec.rb +26 -0
  25. data/spec/http/finish_spec.rb +30 -0
  26. data/spec/http/fixtures/http_server.rb +97 -0
  27. data/spec/http/get2_spec.rb +7 -0
  28. data/spec/http/get_print_spec.rb +28 -0
  29. data/spec/http/get_response_spec.rb +28 -0
  30. data/spec/http/get_spec.rb +52 -0
  31. data/spec/http/head2_spec.rb +8 -0
  32. data/spec/http/head_spec.rb +30 -0
  33. data/spec/http/http_default_port_spec.rb +7 -0
  34. data/spec/http/https_default_port_spec.rb +7 -0
  35. data/spec/http/initialize_spec.rb +45 -0
  36. data/spec/http/inspect_spec.rb +25 -0
  37. data/spec/http/is_version_1_1_spec.rb +6 -0
  38. data/spec/http/is_version_1_2_spec.rb +6 -0
  39. data/spec/http/lock_spec.rb +26 -0
  40. data/spec/http/mkcol_spec.rb +26 -0
  41. data/spec/http/move_spec.rb +30 -0
  42. data/spec/http/new_spec.rb +96 -0
  43. data/spec/http/newobj_spec.rb +47 -0
  44. data/spec/http/open_timeout_spec.rb +23 -0
  45. data/spec/http/options_spec.rb +30 -0
  46. data/spec/http/port_spec.rb +8 -0
  47. data/spec/http/post2_spec.rb +7 -0
  48. data/spec/http/post_form_spec.rb +20 -0
  49. data/spec/http/post_spec.rb +43 -0
  50. data/spec/http/propfind_spec.rb +29 -0
  51. data/spec/http/proppatch_spec.rb +29 -0
  52. data/spec/http/proxy_address_spec.rb +30 -0
  53. data/spec/http/proxy_class_spec.rb +8 -0
  54. data/spec/http/proxy_pass_spec.rb +38 -0
  55. data/spec/http/proxy_port_spec.rb +38 -0
  56. data/spec/http/proxy_user_spec.rb +38 -0
  57. data/spec/http/put2_spec.rb +7 -0
  58. data/spec/http/put_spec.rb +29 -0
  59. data/spec/http/read_timeout_spec.rb +23 -0
  60. data/spec/http/request_get_spec.rb +7 -0
  61. data/spec/http/request_head_spec.rb +7 -0
  62. data/spec/http/request_post_spec.rb +7 -0
  63. data/spec/http/request_put_spec.rb +7 -0
  64. data/spec/http/request_spec.rb +114 -0
  65. data/spec/http/request_types_spec.rb +253 -0
  66. data/spec/http/send_request_spec.rb +123 -0
  67. data/spec/http/set_debug_output_spec.rb +34 -0
  68. data/spec/http/shared/request_get.rb +49 -0
  69. data/spec/http/shared/request_head.rb +49 -0
  70. data/spec/http/shared/request_post.rb +49 -0
  71. data/spec/http/shared/request_put.rb +49 -0
  72. data/spec/http/shared/started.rb +28 -0
  73. data/spec/http/shared/version_1_1.rb +19 -0
  74. data/spec/http/shared/version_1_2.rb +6 -0
  75. data/spec/http/socket_type_spec.rb +7 -0
  76. data/spec/http/start_spec.rb +115 -0
  77. data/spec/http/started_spec.rb +7 -0
  78. data/spec/http/trace_spec.rb +29 -0
  79. data/spec/http/unlock_spec.rb +29 -0
  80. data/spec/http/use_ssl_spec.rb +8 -0
  81. data/spec/http/version_1_1_spec.rb +21 -0
  82. data/spec/http/version_1_2_spec.rb +19 -0
  83. data/spec/httpexceptions/fixtures/classes.rb +5 -0
  84. data/spec/httpexceptions/initialize_spec.rb +16 -0
  85. data/spec/httpexceptions/response_spec.rb +9 -0
  86. data/spec/httpgenericrequest/body_exist_spec.rb +26 -0
  87. data/spec/httpgenericrequest/body_spec.rb +29 -0
  88. data/spec/httpgenericrequest/body_stream_spec.rb +31 -0
  89. data/spec/httpgenericrequest/exec_spec.rb +135 -0
  90. data/spec/httpgenericrequest/inspect_spec.rb +24 -0
  91. data/spec/httpgenericrequest/method_spec.rb +14 -0
  92. data/spec/httpgenericrequest/path_spec.rb +11 -0
  93. data/spec/httpgenericrequest/request_body_permitted_spec.rb +11 -0
  94. data/spec/httpgenericrequest/response_body_permitted_spec.rb +11 -0
  95. data/spec/httpgenericrequest/set_body_internal_spec.rb +20 -0
  96. data/spec/httpheader/add_field_spec.rb +30 -0
  97. data/spec/httpheader/basic_auth_spec.rb +13 -0
  98. data/spec/httpheader/canonical_each_spec.rb +7 -0
  99. data/spec/httpheader/chunked_spec.rb +21 -0
  100. data/spec/httpheader/content_length_spec.rb +53 -0
  101. data/spec/httpheader/content_range_spec.rb +33 -0
  102. data/spec/httpheader/content_type_spec.rb +25 -0
  103. data/spec/httpheader/delete_spec.rb +29 -0
  104. data/spec/httpheader/each_capitalized_name_spec.rb +44 -0
  105. data/spec/httpheader/each_capitalized_spec.rb +8 -0
  106. data/spec/httpheader/each_header_spec.rb +7 -0
  107. data/spec/httpheader/each_key_spec.rb +7 -0
  108. data/spec/httpheader/each_name_spec.rb +7 -0
  109. data/spec/httpheader/each_spec.rb +7 -0
  110. data/spec/httpheader/each_value_spec.rb +45 -0
  111. data/spec/httpheader/element_reference_spec.rb +38 -0
  112. data/spec/httpheader/element_set_spec.rb +40 -0
  113. data/spec/httpheader/fetch_spec.rb +68 -0
  114. data/spec/httpheader/fixtures/classes.rb +11 -0
  115. data/spec/httpheader/form_data_spec.rb +7 -0
  116. data/spec/httpheader/get_fields_spec.rb +38 -0
  117. data/spec/httpheader/initialize_http_header_spec.rb +28 -0
  118. data/spec/httpheader/key_spec.rb +20 -0
  119. data/spec/httpheader/length_spec.rb +7 -0
  120. data/spec/httpheader/main_type_spec.rb +23 -0
  121. data/spec/httpheader/proxy_basic_auth_spec.rb +13 -0
  122. data/spec/httpheader/range_length_spec.rb +31 -0
  123. data/spec/httpheader/range_spec.rb +47 -0
  124. data/spec/httpheader/set_content_type_spec.rb +7 -0
  125. data/spec/httpheader/set_form_data_spec.rb +7 -0
  126. data/spec/httpheader/set_range_spec.rb +7 -0
  127. data/spec/httpheader/shared/each_capitalized.rb +42 -0
  128. data/spec/httpheader/shared/each_header.rb +42 -0
  129. data/spec/httpheader/shared/each_name.rb +39 -0
  130. data/spec/httpheader/shared/set_content_type.rb +20 -0
  131. data/spec/httpheader/shared/set_form_data.rb +27 -0
  132. data/spec/httpheader/shared/set_range.rb +89 -0
  133. data/spec/httpheader/shared/size.rb +18 -0
  134. data/spec/httpheader/size_spec.rb +7 -0
  135. data/spec/httpheader/sub_type_spec.rb +31 -0
  136. data/spec/httpheader/to_hash_spec.rb +24 -0
  137. data/spec/httpheader/type_params_spec.rb +23 -0
  138. data/spec/httprequest/initialize_spec.rb +44 -0
  139. data/spec/httpresponse/body_permitted_spec.rb +12 -0
  140. data/spec/httpresponse/body_spec.rb +6 -0
  141. data/spec/httpresponse/code_spec.rb +23 -0
  142. data/spec/httpresponse/code_type_spec.rb +23 -0
  143. data/spec/httpresponse/entity_spec.rb +6 -0
  144. data/spec/httpresponse/error_spec.rb +23 -0
  145. data/spec/httpresponse/error_type_spec.rb +23 -0
  146. data/spec/httpresponse/exception_type_spec.rb +12 -0
  147. data/spec/httpresponse/header_spec.rb +8 -0
  148. data/spec/httpresponse/http_version_spec.rb +11 -0
  149. data/spec/httpresponse/initialize_spec.rb +10 -0
  150. data/spec/httpresponse/inspect_spec.rb +14 -0
  151. data/spec/httpresponse/message_spec.rb +8 -0
  152. data/spec/httpresponse/msg_spec.rb +8 -0
  153. data/spec/httpresponse/read_body_spec.rb +85 -0
  154. data/spec/httpresponse/read_header_spec.rb +8 -0
  155. data/spec/httpresponse/read_new_spec.rb +21 -0
  156. data/spec/httpresponse/reading_body_spec.rb +57 -0
  157. data/spec/httpresponse/response_spec.rb +8 -0
  158. data/spec/httpresponse/shared/body.rb +18 -0
  159. data/spec/httpresponse/to_ary_spec.rb +25 -0
  160. data/spec/httpresponse/value_spec.rb +23 -0
  161. metadata +366 -88
  162. data/lib/rubysl-net-http.rb +0 -9
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c7de1a540a0892197aa189bb2444957414e358f6
4
+ data.tar.gz: 492a0485426db11663d0088d94fc96594f827a35
5
+ SHA512:
6
+ metadata.gz: 72d0fbb535b33c32f99df8f838d8a9e0037e29d4c4db1125389444207c1ec6da5c92702049bf7bdb9ea42fab84ec9e945f55e7efa19960de5db2238725852483
7
+ data.tar.gz: fe90ef53f1ff9a5626034cc796e51a29cd73f99a580698b562f255ce12749997a7d3551ca86f66853fe2137035d2e2ac193600cbc1f8e5e87d9d2e1a6409d2b7
data/.gitignore CHANGED
@@ -15,4 +15,3 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- .rbx
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.8.7
7
+ - rbx-nightly-18mode
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RubySL::Net::Http
1
+ # Rubysl::Net::Http
2
2
 
3
3
  TODO: Write a gem description
4
4
 
@@ -24,6 +24,6 @@ TODO: Write usage instructions here
24
24
 
25
25
  1. Fork it
26
26
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Added some feature'`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
28
  4. Push to the branch (`git push origin my-new-feature`)
29
29
  5. Create new Pull Request
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
data/lib/net/http.rb ADDED
@@ -0,0 +1 @@
1
+ require "rubysl/net/http"
@@ -0,0 +1,2 @@
1
+ require "rubysl/net/http/version"
2
+ require "rubysl/net/http/http"
@@ -0,0 +1,2290 @@
1
+ #
2
+ # = net/http.rb
3
+ #
4
+ # Copyright (c) 1999-2006 Yukihiro Matsumoto
5
+ # Copyright (c) 1999-2006 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$
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
+ # Example #1: Simple GET+print
50
+ #
51
+ # require 'net/http'
52
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
53
+ #
54
+ # Example #2: Simple GET+print by URL
55
+ #
56
+ # require 'net/http'
57
+ # require 'uri'
58
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
59
+ #
60
+ # Example #3: More generic GET+print
61
+ #
62
+ # require 'net/http'
63
+ # require 'uri'
64
+ #
65
+ # url = URI.parse('http://www.example.com/index.html')
66
+ # res = Net::HTTP.start(url.host, url.port) {|http|
67
+ # http.get('/index.html')
68
+ # }
69
+ # puts res.body
70
+ #
71
+ # Example #4: More generic GET+print
72
+ #
73
+ # require 'net/http'
74
+ #
75
+ # url = URI.parse('http://www.example.com/index.html')
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
81
+ #
82
+ # === Posting Form Data
83
+ #
84
+ # require 'net/http'
85
+ # require 'uri'
86
+ #
87
+ # #1: Simple POST
88
+ # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
89
+ # {'q'=>'ruby', 'max'=>'50'})
90
+ # puts res.body
91
+ #
92
+ # #2: POST with basic authentication
93
+ # res = Net::HTTP.post_form(URI.parse('http://jack:pass@www.example.com/todo.cgi'),
94
+ # {'from'=>'2005-01-01', 'to'=>'2005-03-31'})
95
+ # puts res.body
96
+ #
97
+ # #3: Detailed control
98
+ # url = URI.parse('http://www.example.com/todo.cgi')
99
+ # req = Net::HTTP::Post.new(url.path)
100
+ # req.basic_auth 'jack', 'pass'
101
+ # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
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
109
+ #
110
+ # === Accessing via Proxy
111
+ #
112
+ # Net::HTTP.Proxy creates http proxy class. It has same
113
+ # methods of Net::HTTP but its instances always connect to
114
+ # proxy, instead of given host.
115
+ #
116
+ # require 'net/http'
117
+ #
118
+ # proxy_addr = 'your.proxy.host'
119
+ # proxy_port = 8080
120
+ # :
121
+ # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
122
+ # # always connect to your.proxy.addr:8080
123
+ # :
124
+ # }
125
+ #
126
+ # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
127
+ # there's no need to change code if there's proxy or not.
128
+ #
129
+ # There are two additional parameters in Net::HTTP.Proxy which allow to
130
+ # specify proxy user name and password:
131
+ #
132
+ # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
133
+ #
134
+ # You may use them to work with authorization-enabled proxies:
135
+ #
136
+ # require 'net/http'
137
+ # require 'uri'
138
+ #
139
+ # proxy_host = 'your.proxy.host'
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
+ # }
148
+ #
149
+ # Note that net/http never rely on HTTP_PROXY environment variable.
150
+ # If you want to use proxy, set it explicitly.
151
+ #
152
+ # === Following Redirection
153
+ #
154
+ # require 'net/http'
155
+ # require 'uri'
156
+ #
157
+ # def fetch(uri_str, limit = 10)
158
+ # # You should choose better exception.
159
+ # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
160
+ #
161
+ # response = Net::HTTP.get_response(URI.parse(uri_str))
162
+ # case response
163
+ # when Net::HTTPSuccess then response
164
+ # when Net::HTTPRedirection then fetch(response['location'], limit - 1)
165
+ # else
166
+ # response.error!
167
+ # end
168
+ # end
169
+ #
170
+ # print fetch('http://www.ruby-lang.org')
171
+ #
172
+ # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
173
+ # All HTTPResponse objects belong to its own response class which
174
+ # indicate HTTP result status. For details of response classes,
175
+ # see section "HTTP Response Classes".
176
+ #
177
+ # === Basic Authentication
178
+ #
179
+ # require 'net/http'
180
+ #
181
+ # Net::HTTP.start('www.example.com') {|http|
182
+ # req = Net::HTTP::Get.new('/secret-page.html')
183
+ # req.basic_auth 'account', 'password'
184
+ # response = http.request(req)
185
+ # print response.body
186
+ # }
187
+ #
188
+ # === HTTP Request Classes
189
+ #
190
+ # Here is HTTP request class hierarchy.
191
+ #
192
+ # Net::HTTPRequest
193
+ # Net::HTTP::Get
194
+ # Net::HTTP::Head
195
+ # Net::HTTP::Post
196
+ # Net::HTTP::Put
197
+ # Net::HTTP::Proppatch
198
+ # Net::HTTP::Lock
199
+ # Net::HTTP::Unlock
200
+ # Net::HTTP::Options
201
+ # Net::HTTP::Propfind
202
+ # Net::HTTP::Delete
203
+ # Net::HTTP::Move
204
+ # Net::HTTP::Copy
205
+ # Net::HTTP::Mkcol
206
+ # Net::HTTP::Trace
207
+ #
208
+ # === HTTP Response Classes
209
+ #
210
+ # Here is HTTP response class hierarchy.
211
+ # All classes are defined in Net module.
212
+ #
213
+ # HTTPResponse
214
+ # HTTPUnknownResponse
215
+ # HTTPInformation # 1xx
216
+ # HTTPContinue # 100
217
+ # HTTPSwitchProtocl # 101
218
+ # HTTPSuccess # 2xx
219
+ # HTTPOK # 200
220
+ # HTTPCreated # 201
221
+ # HTTPAccepted # 202
222
+ # HTTPNonAuthoritativeInformation # 203
223
+ # HTTPNoContent # 204
224
+ # HTTPResetContent # 205
225
+ # HTTPPartialContent # 206
226
+ # HTTPRedirection # 3xx
227
+ # HTTPMultipleChoice # 300
228
+ # HTTPMovedPermanently # 301
229
+ # HTTPFound # 302
230
+ # HTTPSeeOther # 303
231
+ # HTTPNotModified # 304
232
+ # HTTPUseProxy # 305
233
+ # HTTPTemporaryRedirect # 307
234
+ # HTTPClientError # 4xx
235
+ # HTTPBadRequest # 400
236
+ # HTTPUnauthorized # 401
237
+ # HTTPPaymentRequired # 402
238
+ # HTTPForbidden # 403
239
+ # HTTPNotFound # 404
240
+ # HTTPMethodNotAllowed # 405
241
+ # HTTPNotAcceptable # 406
242
+ # HTTPProxyAuthenticationRequired # 407
243
+ # HTTPRequestTimeOut # 408
244
+ # HTTPConflict # 409
245
+ # HTTPGone # 410
246
+ # HTTPLengthRequired # 411
247
+ # HTTPPreconditionFailed # 412
248
+ # HTTPRequestEntityTooLarge # 413
249
+ # HTTPRequestURITooLong # 414
250
+ # HTTPUnsupportedMediaType # 415
251
+ # HTTPRequestedRangeNotSatisfiable # 416
252
+ # HTTPExpectationFailed # 417
253
+ # HTTPServerError # 5xx
254
+ # HTTPInternalServerError # 500
255
+ # HTTPNotImplemented # 501
256
+ # HTTPBadGateway # 502
257
+ # HTTPServiceUnavailable # 503
258
+ # HTTPGatewayTimeOut # 504
259
+ # HTTPVersionNotSupported # 505
260
+ #
261
+ # == Switching Net::HTTP versions
262
+ #
263
+ # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
264
+ # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
265
+ # allows you to use 1.2 features again.
266
+ #
267
+ # # example
268
+ # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
269
+ #
270
+ # Net::HTTP.version_1_1
271
+ # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
272
+ #
273
+ # Net::HTTP.version_1_2
274
+ # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
275
+ #
276
+ # This function is NOT thread-safe.
277
+ #
278
+ class HTTP < Protocol
279
+
280
+ # :stopdoc:
281
+ Revision = %q$Revision$.split[1]
282
+ HTTPVersion = '1.1'
283
+ @newimpl = true
284
+ # :startdoc:
285
+
286
+ # Turns on net/http 1.2 (ruby 1.8) features.
287
+ # Defaults to ON in ruby 1.8.
288
+ #
289
+ # I strongly recommend to call this method always.
290
+ #
291
+ # require 'net/http'
292
+ # Net::HTTP.version_1_2
293
+ #
294
+ def HTTP.version_1_2
295
+ @newimpl = true
296
+ end
297
+
298
+ # Turns on net/http 1.1 (ruby 1.6) features.
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.
305
+ # Defaults to true.
306
+ def HTTP.version_1_2?
307
+ @newimpl
308
+ end
309
+
310
+ # true if net/http is in version 1.1 compatible mode.
311
+ # Defaults to true.
312
+ def HTTP.version_1_1?
313
+ not @newimpl
314
+ end
315
+
316
+ class << HTTP
317
+ alias is_version_1_1? version_1_1? #:nodoc:
318
+ alias is_version_1_2? version_1_2? #:nodoc:
319
+ end
320
+
321
+ #
322
+ # short cut methods
323
+ #
324
+
325
+ #
326
+ # Get body from target and output it to +$stdout+. The
327
+ # target can either be specified as (+uri+), or as
328
+ # (+host+, +path+, +port+ = 80); so:
329
+ #
330
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
331
+ #
332
+ # or:
333
+ #
334
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
335
+ #
336
+ def HTTP.get_print(uri_or_host, path = nil, port = nil)
337
+ get_response(uri_or_host, path, port) {|res|
338
+ res.read_body do |chunk|
339
+ $stdout.print chunk
340
+ end
341
+ }
342
+ nil
343
+ end
344
+
345
+ # Send a GET request to the target and return the response
346
+ # as a string. The target can either be specified as
347
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
348
+ #
349
+ # print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
350
+ #
351
+ # or:
352
+ #
353
+ # print Net::HTTP.get('www.example.com', '/index.html')
354
+ #
355
+ def HTTP.get(uri_or_host, path = nil, port = nil)
356
+ get_response(uri_or_host, path, port).body
357
+ end
358
+
359
+ # Send a GET request to the target and return the response
360
+ # as a Net::HTTPResponse object. The target can either be specified as
361
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
362
+ #
363
+ # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
364
+ # print res.body
365
+ #
366
+ # or:
367
+ #
368
+ # res = Net::HTTP.get_response('www.example.com', '/index.html')
369
+ # print res.body
370
+ #
371
+ def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
372
+ if path
373
+ host = uri_or_host
374
+ new(host, port || HTTP.default_port).start {|http|
375
+ return http.request_get(path, &block)
376
+ }
377
+ else
378
+ uri = uri_or_host
379
+ new(uri.host, uri.port).start {|http|
380
+ return http.request_get(uri.request_uri, &block)
381
+ }
382
+ end
383
+ end
384
+
385
+ # Posts HTML form data to the +URL+.
386
+ # Form data must be represented as a Hash of String to String, e.g:
387
+ #
388
+ # { "cmd" => "search", "q" => "ruby", "max" => "50" }
389
+ #
390
+ # This method also does Basic Authentication iff +URL+.user exists.
391
+ #
392
+ # Example:
393
+ #
394
+ # require 'net/http'
395
+ # require 'uri'
396
+ #
397
+ # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
398
+ # { "q" => "ruby", "max" => "50" }
399
+ #
400
+ def HTTP.post_form(url, params)
401
+ req = Post.new(url.path)
402
+ req.form_data = params
403
+ req.basic_auth url.user, url.password if url.user
404
+ new(url.host, url.port).start {|http|
405
+ http.request(req)
406
+ }
407
+ end
408
+
409
+ #
410
+ # HTTP session management
411
+ #
412
+
413
+ # The default port to use for HTTP requests; defaults to 80.
414
+ def HTTP.default_port
415
+ http_default_port()
416
+ end
417
+
418
+ # The default port to use for HTTP requests; defaults to 80.
419
+ def HTTP.http_default_port
420
+ 80
421
+ end
422
+
423
+ # The default port to use for HTTPS requests; defaults to 443.
424
+ def HTTP.https_default_port
425
+ 443
426
+ end
427
+
428
+ def HTTP.socket_type #:nodoc: obsolete
429
+ BufferedIO
430
+ end
431
+
432
+ # creates a new Net::HTTP object and opens its TCP connection and
433
+ # HTTP session. If the optional block is given, the newly
434
+ # created Net::HTTP object is passed to it and closed when the
435
+ # block finishes. In this case, the return value of this method
436
+ # is the return value of the block. If no block is given, the
437
+ # 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
+ def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
440
+ new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
441
+ end
442
+
443
+ class << HTTP
444
+ alias newobj new
445
+ end
446
+
447
+ # Creates a new Net::HTTP object.
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
456
+ end
457
+
458
+ # Creates a new Net::HTTP object for the specified +address+.
459
+ # This method does not open the TCP connection.
460
+ def initialize(address, port = nil)
461
+ @address = address
462
+ @port = (port || HTTP.default_port)
463
+ @curr_http_version = HTTPVersion
464
+ @seems_1_0_server = false
465
+ @close_on_empty_response = false
466
+ @socket = nil
467
+ @started = false
468
+ @open_timeout = nil
469
+ @read_timeout = 60
470
+ @debug_output = nil
471
+ @use_ssl = false
472
+ @ssl_context = nil
473
+ end
474
+
475
+ def inspect
476
+ "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
477
+ end
478
+
479
+ # *WARNING* This method causes serious security hole.
480
+ # Never use this method in production code.
481
+ #
482
+ # Set an output stream for debugging.
483
+ #
484
+ # http = Net::HTTP.new
485
+ # http.set_debug_output $stderr
486
+ # http.start { .... }
487
+ #
488
+ def set_debug_output(output)
489
+ warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
490
+ @debug_output = output
491
+ end
492
+
493
+ # The host name to connect to.
494
+ attr_reader :address
495
+
496
+ # The port number to connect to.
497
+ attr_reader :port
498
+
499
+ # Seconds to wait until connection is opened.
500
+ # If the HTTP object cannot open a connection in this many seconds,
501
+ # it raises a TimeoutError exception.
502
+ attr_accessor :open_timeout
503
+
504
+ # Seconds to wait until reading one block (by one read(2) call).
505
+ # If the HTTP object cannot open a connection in this many seconds,
506
+ # it raises a TimeoutError exception.
507
+ attr_reader :read_timeout
508
+
509
+ # Setter for the read_timeout attribute.
510
+ def read_timeout=(sec)
511
+ @socket.read_timeout = sec if @socket
512
+ @read_timeout = sec
513
+ end
514
+
515
+ # returns true if the HTTP session is started.
516
+ def started?
517
+ @started
518
+ end
519
+
520
+ alias active? started? #:nodoc: obsolete
521
+
522
+ attr_accessor :close_on_empty_response
523
+
524
+ # returns true if use SSL/TLS with HTTP.
525
+ def use_ssl?
526
+ false # redefined in net/https
527
+ end
528
+
529
+ # Opens TCP connection and HTTP session.
530
+ #
531
+ # When this method is called with block, gives a HTTP object
532
+ # to the block and closes the TCP connection / HTTP session
533
+ # after the block executed.
534
+ #
535
+ # When called with a block, returns the return value of the
536
+ # block; otherwise, returns self.
537
+ #
538
+ def start # :yield: http
539
+ raise IOError, 'HTTP session already opened' if @started
540
+ if block_given?
541
+ begin
542
+ do_start
543
+ return yield(self)
544
+ ensure
545
+ do_finish
546
+ end
547
+ end
548
+ do_start
549
+ self
550
+ end
551
+
552
+ def do_start
553
+ connect
554
+ @started = true
555
+ end
556
+ private :do_start
557
+
558
+ def connect
559
+ D "opening connection to #{conn_address()}..."
560
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
561
+ D "opened"
562
+ if use_ssl?
563
+ unless @ssl_context.verify_mode
564
+ warn "warning: peer certificate won't be verified in this SSL session"
565
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
566
+ end
567
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
568
+ s.sync_close = true
569
+ end
570
+ @socket = BufferedIO.new(s)
571
+ @socket.read_timeout = @read_timeout
572
+ @socket.debug_output = @debug_output
573
+ if use_ssl?
574
+ if proxy?
575
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
576
+ @address, @port, HTTPVersion)
577
+ @socket.writeline "Host: #{@address}:#{@port}"
578
+ if proxy_user
579
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
580
+ credential.delete!("\r\n")
581
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
582
+ end
583
+ @socket.writeline ''
584
+ HTTPResponse.read_new(@socket).value
585
+ end
586
+ s.connect
587
+ if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
588
+ s.post_connection_check(@address)
589
+ end
590
+ end
591
+ on_connect
592
+ end
593
+ private :connect
594
+
595
+ def on_connect
596
+ end
597
+ private :on_connect
598
+
599
+ # Finishes HTTP session and closes TCP connection.
600
+ # Raises IOError if not started.
601
+ def finish
602
+ raise IOError, 'HTTP session not yet started' unless started?
603
+ do_finish
604
+ end
605
+
606
+ def do_finish
607
+ @started = false
608
+ @socket.close if @socket and not @socket.closed?
609
+ @socket = nil
610
+ end
611
+ private :do_finish
612
+
613
+ #
614
+ # proxy
615
+ #
616
+
617
+ public
618
+
619
+ # no proxy
620
+ @is_proxy_class = false
621
+ @proxy_addr = nil
622
+ @proxy_port = nil
623
+ @proxy_user = nil
624
+ @proxy_pass = nil
625
+
626
+ # Creates an HTTP proxy class.
627
+ # Arguments are address/port of proxy host and username/password
628
+ # if authorization on proxy server is required.
629
+ # You can replace the HTTP class with created proxy class.
630
+ #
631
+ # If ADDRESS is nil, this method returns self (Net::HTTP).
632
+ #
633
+ # # Example
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)
642
+ return self unless p_addr
643
+ delta = ProxyDelta
644
+ proxyclass = Class.new(self)
645
+ proxyclass.module_eval {
646
+ include delta
647
+ # with proxy
648
+ @is_proxy_class = true
649
+ @proxy_address = p_addr
650
+ @proxy_port = p_port || default_port()
651
+ @proxy_user = p_user
652
+ @proxy_pass = p_pass
653
+ }
654
+ proxyclass
655
+ end
656
+
657
+ class << HTTP
658
+ # returns true if self is a class which was created by HTTP::Proxy.
659
+ def proxy_class?
660
+ @is_proxy_class
661
+ end
662
+
663
+ attr_reader :proxy_address
664
+ attr_reader :proxy_port
665
+ attr_reader :proxy_user
666
+ attr_reader :proxy_pass
667
+ end
668
+
669
+ # True if self is a HTTP proxy class.
670
+ def proxy?
671
+ self.class.proxy_class?
672
+ end
673
+
674
+ # Address of proxy host. If self does not use a proxy, nil.
675
+ def proxy_address
676
+ self.class.proxy_address
677
+ end
678
+
679
+ # Port number of proxy host. If self does not use a proxy, nil.
680
+ def proxy_port
681
+ self.class.proxy_port
682
+ end
683
+
684
+ # User name for accessing proxy. If self does not use a proxy, nil.
685
+ def proxy_user
686
+ self.class.proxy_user
687
+ end
688
+
689
+ # User password for accessing proxy. If self does not use a proxy, nil.
690
+ def proxy_pass
691
+ self.class.proxy_pass
692
+ end
693
+
694
+ alias proxyaddr proxy_address #:nodoc: obsolete
695
+ alias proxyport proxy_port #:nodoc: obsolete
696
+
697
+ private
698
+
699
+ # without proxy
700
+
701
+ def conn_address
702
+ address()
703
+ end
704
+
705
+ def conn_port
706
+ port()
707
+ end
708
+
709
+ def edit_path(path)
710
+ path
711
+ end
712
+
713
+ module ProxyDelta #:nodoc: internal use only
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}"
726
+ end
727
+ end
728
+
729
+ #
730
+ # HTTP operations
731
+ #
732
+
733
+ public
734
+
735
+ # Gets data from +path+ on the connected-to host.
736
+ # +header+ must be a Hash like { 'Accept' => '*/*', ... }.
737
+ #
738
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects,
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.
742
+ #
743
+ # If called with a block, yields each fragment of the
744
+ # entity body in turn as a string as it is read from
745
+ # the socket. Note that in this case, the returned response
746
+ # object will *not* contain a (meaningful) body.
747
+ #
748
+ # +dest+ argument is obsolete.
749
+ # It still works but you must not use it.
750
+ #
751
+ # In version 1.1, this method might raise an exception for
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')
759
+ #
760
+ # # version 1.2 (bundled with Ruby 1.8 or later)
761
+ # response = http.get('/index.html')
762
+ #
763
+ # # using block
764
+ # File.open('result.txt', 'w') {|f|
765
+ # http.get('/~foo/') do |str|
766
+ # f.write str
767
+ # end
768
+ # }
769
+ #
770
+ def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
771
+ res = nil
772
+ request(Get.new(path, initheader)) {|r|
773
+ r.read_body dest, &block
774
+ res = r
775
+ }
776
+ unless @newimpl
777
+ res.value
778
+ return res, res.body
779
+ end
780
+
781
+ res
782
+ end
783
+
784
+ # Gets only the header from +path+ on the connected-to host.
785
+ # +header+ is a Hash like { 'Accept' => '*/*', ... }.
786
+ #
787
+ # This method returns a Net::HTTPResponse object.
788
+ #
789
+ # In version 1.1, this method might raise an exception for
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.
793
+ #
794
+ # response = nil
795
+ # Net::HTTP.start('some.www.server', 80) {|http|
796
+ # response = http.head('/index.html')
797
+ # }
798
+ # p response['content-type']
799
+ #
800
+ def head(path, initheader = nil)
801
+ res = request(Head.new(path, initheader))
802
+ res.value unless @newimpl
803
+ res
804
+ end
805
+
806
+ # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
807
+ # like { 'Accept' => '*/*', ... }.
808
+ #
809
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
810
+ # Net::HTTPResponse object and an entity body string.
811
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
812
+ #
813
+ # If called with a block, yields each fragment of the
814
+ # entity body in turn as a string as it are read from
815
+ # the socket. Note that in this case, the returned response
816
+ # object will *not* contain a (meaningful) body.
817
+ #
818
+ # +dest+ argument is obsolete.
819
+ # It still works but you must not use it.
820
+ #
821
+ # In version 1.1, this method might raise an exception for
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')
828
+ #
829
+ # # version 1.2
830
+ # response = http.post('/cgi-bin/search.rb', 'query=foo')
831
+ #
832
+ # # using block
833
+ # File.open('result.txt', 'w') {|f|
834
+ # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
835
+ # f.write str
836
+ # end
837
+ # }
838
+ #
839
+ # You should set Content-Type: header field for POST.
840
+ # If no Content-Type: field given, this method uses
841
+ # "application/x-www-form-urlencoded" by default.
842
+ #
843
+ def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
844
+ res = nil
845
+ request(Post.new(path, initheader), data) {|r|
846
+ r.read_body dest, &block
847
+ res = r
848
+ }
849
+ unless @newimpl
850
+ res.value
851
+ return res, res.body
852
+ end
853
+ res
854
+ end
855
+
856
+ def put(path, data, initheader = nil) #:nodoc:
857
+ res = request(Put.new(path, initheader), data)
858
+ res.value unless @newimpl
859
+ res
860
+ end
861
+
862
+ # Sends a PROPPATCH request to the +path+ and gets a response,
863
+ # as an HTTPResponse object.
864
+ def proppatch(path, body, initheader = nil)
865
+ request(Proppatch.new(path, initheader), body)
866
+ end
867
+
868
+ # Sends a LOCK request to the +path+ and gets a response,
869
+ # as an HTTPResponse object.
870
+ def lock(path, body, initheader = nil)
871
+ request(Lock.new(path, initheader), body)
872
+ end
873
+
874
+ # Sends a UNLOCK request to the +path+ and gets a response,
875
+ # as an HTTPResponse object.
876
+ def unlock(path, body, initheader = nil)
877
+ request(Unlock.new(path, initheader), body)
878
+ end
879
+
880
+ # Sends a OPTIONS request to the +path+ and gets a response,
881
+ # as an HTTPResponse object.
882
+ def options(path, initheader = nil)
883
+ request(Options.new(path, initheader))
884
+ end
885
+
886
+ # Sends a PROPFIND request to the +path+ and gets a response,
887
+ # as an HTTPResponse object.
888
+ def propfind(path, body = nil, initheader = {'Depth' => '0'})
889
+ request(Propfind.new(path, initheader), body)
890
+ end
891
+
892
+ # Sends a DELETE request to the +path+ and gets a response,
893
+ # as an HTTPResponse object.
894
+ def delete(path, initheader = {'Depth' => 'Infinity'})
895
+ request(Delete.new(path, initheader))
896
+ end
897
+
898
+ # Sends a MOVE request to the +path+ and gets a response,
899
+ # as an HTTPResponse object.
900
+ def move(path, initheader = nil)
901
+ request(Move.new(path, initheader))
902
+ end
903
+
904
+ # Sends a COPY request to the +path+ and gets a response,
905
+ # as an HTTPResponse object.
906
+ def copy(path, initheader = nil)
907
+ request(Copy.new(path, initheader))
908
+ end
909
+
910
+ # Sends a MKCOL request to the +path+ and gets a response,
911
+ # as an HTTPResponse object.
912
+ def mkcol(path, body = nil, initheader = nil)
913
+ request(Mkcol.new(path, initheader), body)
914
+ end
915
+
916
+ # Sends a TRACE request to the +path+ and gets a response,
917
+ # as an HTTPResponse object.
918
+ def trace(path, initheader = nil)
919
+ request(Trace.new(path, initheader))
920
+ end
921
+
922
+ # Sends a GET request to the +path+ and gets a response,
923
+ # as an HTTPResponse object.
924
+ #
925
+ # When called with a block, yields an HTTPResponse object.
926
+ # The body of this response will not have been read yet;
927
+ # the caller can process it using HTTPResponse#read_body,
928
+ # if desired.
929
+ #
930
+ # Returns the response.
931
+ #
932
+ # This method never raises Net::* exceptions.
933
+ #
934
+ # response = http.request_get('/index.html')
935
+ # # The entity body is already read here.
936
+ # p response['content-type']
937
+ # puts response.body
938
+ #
939
+ # # using block
940
+ # http.request_get('/index.html') {|response|
941
+ # p response['content-type']
942
+ # response.read_body do |str| # read body now
943
+ # print str
944
+ # end
945
+ # }
946
+ #
947
+ def request_get(path, initheader = nil, &block) # :yield: +response+
948
+ request(Get.new(path, initheader), &block)
949
+ end
950
+
951
+ # Sends a HEAD request to the +path+ and gets a response,
952
+ # as an HTTPResponse object.
953
+ #
954
+ # Returns the response.
955
+ #
956
+ # This method never raises Net::* exceptions.
957
+ #
958
+ # response = http.request_head('/index.html')
959
+ # p response['content-type']
960
+ #
961
+ def request_head(path, initheader = nil, &block)
962
+ request(Head.new(path, initheader), &block)
963
+ end
964
+
965
+ # Sends a POST request to the +path+ and gets a response,
966
+ # as an HTTPResponse object.
967
+ #
968
+ # When called with a block, yields an HTTPResponse object.
969
+ # The body of this response will not have been read yet;
970
+ # the caller can process it using HTTPResponse#read_body,
971
+ # if desired.
972
+ #
973
+ # Returns the response.
974
+ #
975
+ # This method never raises Net::* exceptions.
976
+ #
977
+ # # example
978
+ # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
979
+ # p response.status
980
+ # puts response.body # body is already read
981
+ #
982
+ # # using block
983
+ # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
984
+ # p response.status
985
+ # p response['content-type']
986
+ # response.read_body do |str| # read body now
987
+ # print str
988
+ # end
989
+ # }
990
+ #
991
+ def request_post(path, data, initheader = nil, &block) # :yield: +response+
992
+ request Post.new(path, initheader), data, &block
993
+ end
994
+
995
+ def request_put(path, data, initheader = nil, &block) #:nodoc:
996
+ request Put.new(path, initheader), data, &block
997
+ end
998
+
999
+ alias get2 request_get #:nodoc: obsolete
1000
+ alias head2 request_head #:nodoc: obsolete
1001
+ alias post2 request_post #:nodoc: obsolete
1002
+ alias put2 request_put #:nodoc: obsolete
1003
+
1004
+
1005
+ # Sends an HTTP request to the HTTP server.
1006
+ # This method also sends DATA string if DATA is given.
1007
+ #
1008
+ # Returns a HTTPResponse object.
1009
+ #
1010
+ # This method never raises Net::* exceptions.
1011
+ #
1012
+ # response = http.send_request('GET', '/index.html')
1013
+ # puts response.body
1014
+ #
1015
+ def send_request(name, path, data = nil, header = nil)
1016
+ r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
1017
+ request r, data
1018
+ end
1019
+
1020
+ # Sends an HTTPRequest object REQUEST to the HTTP server.
1021
+ # This method also sends DATA string if REQUEST is a post/put request.
1022
+ # Giving DATA for get/head request causes ArgumentError.
1023
+ #
1024
+ # When called with a block, yields an HTTPResponse object.
1025
+ # The body of this response will not have been read yet;
1026
+ # the caller can process it using HTTPResponse#read_body,
1027
+ # if desired.
1028
+ #
1029
+ # Returns a HTTPResponse object.
1030
+ #
1031
+ # This method never raises Net::* exceptions.
1032
+ #
1033
+ def request(req, body = nil, &block) # :yield: +response+
1034
+ unless started?
1035
+ start {
1036
+ req['connection'] ||= 'close'
1037
+ return request(req, body, &block)
1038
+ }
1039
+ end
1040
+ if proxy_user()
1041
+ unless use_ssl?
1042
+ req.proxy_basic_auth proxy_user(), proxy_pass()
1043
+ end
1044
+ end
1045
+
1046
+ req.set_body_internal body
1047
+ begin
1048
+ begin_transport req
1049
+ req.exec @socket, @curr_http_version, edit_path(req.path)
1050
+ begin
1051
+ res = HTTPResponse.read_new(@socket)
1052
+ end while res.kind_of?(HTTPContinue)
1053
+ res.reading_body(@socket, req.response_body_permitted?) {
1054
+ yield res if block_given?
1055
+ }
1056
+ end_transport req, res
1057
+ rescue => exception
1058
+ D "Conn close because of error #{exception}"
1059
+ @socket.close if @socket and not @socket.closed?
1060
+ raise exception
1061
+ end
1062
+
1063
+ res
1064
+ end
1065
+
1066
+ private
1067
+
1068
+ def begin_transport(req)
1069
+ if @socket.closed?
1070
+ connect
1071
+ end
1072
+ if @seems_1_0_server
1073
+ req['connection'] ||= 'close'
1074
+ end
1075
+ if not req.response_body_permitted? and @close_on_empty_response
1076
+ req['connection'] ||= 'close'
1077
+ end
1078
+ req['host'] ||= addr_port()
1079
+ end
1080
+
1081
+ def end_transport(req, res)
1082
+ @curr_http_version = res.http_version
1083
+ if not res.body and @close_on_empty_response
1084
+ D 'Conn close'
1085
+ @socket.close
1086
+ elsif keep_alive?(req, res)
1087
+ D 'Conn keep-alive'
1088
+ if @socket.closed?
1089
+ D 'Conn (but seems 1.0 server)'
1090
+ @seems_1_0_server = true
1091
+ end
1092
+ else
1093
+ D 'Conn close'
1094
+ @socket.close
1095
+ end
1096
+ end
1097
+
1098
+ def keep_alive?(req, res)
1099
+ return false if /close/i =~ req['connection'].to_s
1100
+ return false if @seems_1_0_server
1101
+ return true if /keep-alive/i =~ res['connection'].to_s
1102
+ return false if /close/i =~ res['connection'].to_s
1103
+ return true if /keep-alive/i =~ res['proxy-connection'].to_s
1104
+ return false if /close/i =~ res['proxy-connection'].to_s
1105
+ (@curr_http_version == '1.1')
1106
+ end
1107
+
1108
+ #
1109
+ # utils
1110
+ #
1111
+
1112
+ private
1113
+
1114
+ def addr_port
1115
+ if use_ssl?
1116
+ address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
1117
+ else
1118
+ address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
1119
+ end
1120
+ end
1121
+
1122
+ def D(msg)
1123
+ return unless @debug_output
1124
+ @debug_output << msg
1125
+ @debug_output << "\n"
1126
+ end
1127
+
1128
+ end
1129
+
1130
+ HTTPSession = HTTP
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
1279
+
1280
+ def capitalize(name)
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
1339
+
1340
+ # Returns an Integer object which represents the Content-Length: header field
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
1430
+
1431
+ # Set header fields and a body from HTML form data.
1432
+ # +params+ should be a Hash containing HTML form data.
1433
+ # Optional argument +sep+ means data record separator.
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
1441
+
1442
+ alias form_data= set_form_data
1443
+
1444
+ def urlencode(str)
1445
+ str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
1446
+ end
1447
+ private :urlencode
1448
+
1449
+ # Set the Authorization: header for "Basic" authorization.
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
2280
+
2281
+ HTTPInformationCode = HTTPInformation
2282
+ HTTPSuccessCode = HTTPSuccess
2283
+ HTTPRedirectionCode = HTTPRedirection
2284
+ HTTPRetriableCode = HTTPRedirection
2285
+ HTTPClientErrorCode = HTTPClientError
2286
+ HTTPFatalErrorCode = HTTPClientError
2287
+ HTTPServerErrorCode = HTTPServerError
2288
+ HTTPResponceReceiver = HTTPResponse
2289
+
2290
+ end # module Net