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: http.rb 29865 2010-11-22 07:22:19Z shyouhei $
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: 29865 $.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 @header.each do |k,va|
1224 yield k, va.join(', ')
1225 end
1226 end
1227
1228 alias each each_header
1229
1230 # Iterates for each header names.
1231 def each_name(&block) #:yield: +key+
1232 @header.each_key(&block)
1233 end
1234
1235 alias each_key each_name
1236
1237 # Iterates for each capitalized header names.
1238 def each_capitalized_name(&block) #:yield: +key+
1239 @header.each_key do |k|
1240 yield capitalize(k)
1241 end
1242 end
1243
1244 # Iterates for each header values.
1245 def each_value #:yield: +value+
1246 @header.each_value do |va|
1247 yield va.join(', ')
1248 end
1249 end
1250
1251 # Removes a header field.
1252 def delete(key)
1253 @header.delete(key.downcase)
1254 end
1255
1256 # true if +key+ header exists.
1257 def key?(key)
1258 @header.key?(key.downcase)
1259 end
1260
1261 # Returns a Hash consist of header names and values.
1262 def to_hash
1263 @header.dup
1264 end
1265
1266 # As for #each_header, except the keys are provided in capitalized form.
1267 def each_capitalized
1268 @header.each do |k,v|
1269 yield capitalize(k), v.join(', ')
1270 end
1271 end
1272
1273 alias canonical_each each_capitalized
1274
1275 def capitalize(name)
1276 name.split(/-/).map {|s| s.capitalize }.join('-')
1277 end
1278 private :capitalize
1279
1280 # Returns an Array of Range objects which represents Range: header field,
1281 # or +nil+ if there is no such header.
1282 def range
1283 return nil unless @header['range']
1284 self['Range'].split(/,/).map {|spec|
1285 m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
1286 raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
1287 d1 = m[1].to_i
1288 d2 = m[2].to_i
1289 if m[1] and m[2] then d1..d2
1290 elsif m[1] then d1..-1
1291 elsif m[2] then -d2..-1
1292 else
1293 raise HTTPHeaderSyntaxError, 'range is not specified'
1294 end
1295 }
1296 end
1297
1298 # Set Range: header from Range (arg r) or beginning index and
1299 # length from it (arg idx&len).
1300 #
1301 # req.range = (0..1023)
1302 # req.set_range 0, 1023
1303 #
1304 def set_range(r, e = nil)
1305 unless r
1306 @header.delete 'range'
1307 return r
1308 end
1309 r = (r...r+e) if e
1310 case r
1311 when Numeric
1312 n = r.to_i
1313 rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
1314 when Range
1315 first = r.first
1316 last = r.last
1317 last -= 1 if r.exclude_end?
1318 if last == -1
1319 rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
1320 else
1321 raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
1322 raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
1323 raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
1324 rangestr = "#{first}-#{last}"
1325 end
1326 else
1327 raise TypeError, 'Range/Integer is required'
1328 end
1329 @header['range'] = ["bytes=#{rangestr}"]
1330 r
1331 end
1332
1333 alias range= set_range
1334
1335 # Returns an Integer object which represents the Content-Length: header field
1336 # or +nil+ if that field is not provided.
1337 def content_length
1338 return nil unless key?('Content-Length')
1339 len = self['Content-Length'].slice(/\d+/) or
1340 raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
1341 len.to_i
1342 end
1343
1344 def content_length=(len)
1345 unless len
1346 @header.delete 'content-length'
1347 return nil
1348 end
1349 @header['content-length'] = [len.to_i.to_s]
1350 end
1351
1352 # Returns "true" if the "transfer-encoding" header is present and
1353 # set to "chunked". This is an HTTP/1.1 feature, allowing the
1354 # the content to be sent in "chunks" without at the outset
1355 # stating the entire content length.
1356 def chunked?
1357 return false unless @header['transfer-encoding']
1358 field = self['Transfer-Encoding']
1359 (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
1360 end
1361
1362 # Returns a Range object which represents Content-Range: header field.
1363 # This indicates, for a partial entity body, where this fragment
1364 # fits inside the full entity body, as range of byte offsets.
1365 def content_range
1366 return nil unless @header['content-range']
1367 m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
1368 raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
1369 m[1].to_i .. m[2].to_i
1370 end
1371
1372 # The length of the range represented in Content-Range: header.
1373 def range_length
1374 r = content_range() or return nil
1375 r.end - r.begin + 1
1376 end
1377
1378 # Returns a content type string such as "text/html".
1379 # This method returns nil if Content-Type: header field does not exist.
1380 def content_type
1381 return nil unless main_type()
1382 if sub_type()
1383 then "#{main_type()}/#{sub_type()}"
1384 else main_type()
1385 end
1386 end
1387
1388 # Returns a content type string such as "text".
1389 # This method returns nil if Content-Type: header field does not exist.
1390 def main_type
1391 return nil unless @header['content-type']
1392 self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
1393 end
1394
1395 # Returns a content type string such as "html".
1396 # This method returns nil if Content-Type: header field does not exist
1397 # or sub-type is not given (e.g. "Content-Type: text").
1398 def sub_type
1399 return nil unless @header['content-type']
1400 main, sub = *self['Content-Type'].split(';').first.to_s.split('/')
1401 return nil unless sub
1402 sub.strip
1403 end
1404
1405 # Returns content type parameters as a Hash as like
1406 # {"charset" => "iso-2022-jp"}.
1407 def type_params
1408 result = {}
1409 list = self['Content-Type'].to_s.split(';')
1410 list.shift
1411 list.each do |param|
1412 k, v = *param.split('=', 2)
1413 result[k.strip] = v.strip
1414 end
1415 result
1416 end
1417
1418 # Set Content-Type: header field by +type+ and +params+.
1419 # +type+ must be a String, +params+ must be a Hash.
1420 def set_content_type(type, params = {})
1421 @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
1422 end
1423
1424 alias content_type= set_content_type
1425
1426 # Set header fields and a body from HTML form data.
1427 # +params+ should be a Hash containing HTML form data.
1428 # Optional argument +sep+ means data record separator.
1429 #
1430 # This method also set Content-Type: header field to
1431 # application/x-www-form-urlencoded.
1432 def set_form_data(params, sep = '&')
1433 self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
1434 self.content_type = 'application/x-www-form-urlencoded'
1435 end
1436
1437 alias form_data= set_form_data
1438
1439 def urlencode(str)
1440 str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
1441 end
1442 private :urlencode
1443
1444 # Set the Authorization: header for "Basic" authorization.
1445 def basic_auth(account, password)
1446 @header['authorization'] = [basic_encode(account, password)]
1447 end
1448
1449 # Set Proxy-Authorization: header for "Basic" authorization.
1450 def proxy_basic_auth(account, password)
1451 @header['proxy-authorization'] = [basic_encode(account, password)]
1452 end
1453
1454 def basic_encode(account, password)
1455 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
1456 end
1457 private :basic_encode
1458
1459 end
1460
1461
1462 #
1463 # Parent of HTTPRequest class. Do not use this directly; use
1464 # a subclass of HTTPRequest.
1465 #
1466 # Mixes in the HTTPHeader module.
1467 #
1468 class HTTPGenericRequest
1469
1470 include HTTPHeader
1471
1472 BUFSIZE = 16*1024
1473
1474 def initialize(m, reqbody, resbody, path, initheader = nil)
1475 @method = m
1476 @request_has_body = reqbody
1477 @response_has_body = resbody
1478 raise ArgumentError, "HTTP request path is empty" if path.empty?
1479 @path = path
1480 initialize_http_header initheader
1481 self['Accept'] ||= '*/*'
1482 @body = nil
1483 @body_stream = nil
1484 end
1485
1486 attr_reader :method
1487 attr_reader :path
1488
1489 def inspect
1490 "\#<#{self.class} #{@method}>"
1491 end
1492
1493 def request_body_permitted?
1494 @request_has_body
1495 end
1496
1497 def response_body_permitted?
1498 @response_has_body
1499 end
1500
1501 def body_exist?
1502 warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
1503 response_body_permitted?
1504 end
1505
1506 attr_reader :body
1507
1508 def body=(str)
1509 @body = str
1510 @body_stream = nil
1511 str
1512 end
1513
1514 attr_reader :body_stream
1515
1516 def body_stream=(input)
1517 @body = nil
1518 @body_stream = input
1519 input
1520 end
1521
1522 def set_body_internal(str) #:nodoc: internal use only
1523 raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
1524 self.body = str if str
1525 end
1526
1527 #
1528 # write
1529 #
1530
1531 def exec(sock, ver, path) #:nodoc: internal use only
1532 if @body
1533 send_request_with_body sock, ver, path, @body
1534 elsif @body_stream
1535 send_request_with_body_stream sock, ver, path, @body_stream
1536 else
1537 write_header sock, ver, path
1538 end
1539 end
1540
1541 private
1542
1543 def send_request_with_body(sock, ver, path, body)
1544 self.content_length = body.length
1545 delete 'Transfer-Encoding'
1546 supply_default_content_type
1547 write_header sock, ver, path
1548 sock.write body
1549 end
1550
1551 def send_request_with_body_stream(sock, ver, path, f)
1552 unless content_length() or chunked?
1553 raise ArgumentError,
1554 "Content-Length not given and Transfer-Encoding is not `chunked'"
1555 end
1556 supply_default_content_type
1557 write_header sock, ver, path
1558 if chunked?
1559 while s = f.read(BUFSIZE)
1560 sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
1561 end
1562 sock.write "0\r\n\r\n"
1563 else
1564 while s = f.read(BUFSIZE)
1565 sock.write s
1566 end
1567 end
1568 end
1569
1570 def supply_default_content_type
1571 return if content_type()
1572 warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
1573 set_content_type 'application/x-www-form-urlencoded'
1574 end
1575
1576 def write_header(sock, ver, path)
1577 buf = "#{@method} #{path} HTTP/#{ver}\r\n"
1578 each_capitalized do |k,v|
1579 buf << "#{k}: #{v}\r\n"
1580 end
1581 buf << "\r\n"
1582 sock.write buf
1583 end
1584
1585 end
1586
1587
1588 #
1589 # HTTP request class. This class wraps request header and entity path.
1590 # You *must* use its subclass, Net::HTTP::Get, Post, Head.
1591 #
1592 class HTTPRequest < HTTPGenericRequest
1593
1594 # Creates HTTP request object.
1595 def initialize(path, initheader = nil)
1596 super self.class::METHOD,
1597 self.class::REQUEST_HAS_BODY,
1598 self.class::RESPONSE_HAS_BODY,
1599 path, initheader
1600 end
1601 end
1602
1603
1604 class HTTP # reopen
1605 #
1606 # HTTP 1.1 methods --- RFC2616
1607 #
1608
1609 class Get < HTTPRequest
1610 METHOD = 'GET'
1611 REQUEST_HAS_BODY = false
1612 RESPONSE_HAS_BODY = true
1613 end
1614
1615 class Head < HTTPRequest
1616 METHOD = 'HEAD'
1617 REQUEST_HAS_BODY = false
1618 RESPONSE_HAS_BODY = false
1619 end
1620
1621 class Post < HTTPRequest
1622 METHOD = 'POST'
1623 REQUEST_HAS_BODY = true
1624 RESPONSE_HAS_BODY = true
1625 end
1626
1627 class Put < HTTPRequest
1628 METHOD = 'PUT'
1629 REQUEST_HAS_BODY = true
1630 RESPONSE_HAS_BODY = true
1631 end
1632
1633 class Delete < HTTPRequest
1634 METHOD = 'DELETE'
1635 REQUEST_HAS_BODY = false
1636 RESPONSE_HAS_BODY = true
1637 end
1638
1639 class Options < HTTPRequest
1640 METHOD = 'OPTIONS'
1641 REQUEST_HAS_BODY = false
1642 RESPONSE_HAS_BODY = false
1643 end
1644
1645 class Trace < HTTPRequest
1646 METHOD = 'TRACE'
1647 REQUEST_HAS_BODY = false
1648 RESPONSE_HAS_BODY = true
1649 end
1650
1651 #
1652 # WebDAV methods --- RFC2518
1653 #
1654
1655 class Propfind < HTTPRequest
1656 METHOD = 'PROPFIND'
1657 REQUEST_HAS_BODY = true
1658 RESPONSE_HAS_BODY = true
1659 end
1660
1661 class Proppatch < HTTPRequest
1662 METHOD = 'PROPPATCH'
1663 REQUEST_HAS_BODY = true
1664 RESPONSE_HAS_BODY = true
1665 end
1666
1667 class Mkcol < HTTPRequest
1668 METHOD = 'MKCOL'
1669 REQUEST_HAS_BODY = true
1670 RESPONSE_HAS_BODY = true
1671 end
1672
1673 class Copy < HTTPRequest
1674 METHOD = 'COPY'
1675 REQUEST_HAS_BODY = false
1676 RESPONSE_HAS_BODY = true
1677 end
1678
1679 class Move < HTTPRequest
1680 METHOD = 'MOVE'
1681 REQUEST_HAS_BODY = false
1682 RESPONSE_HAS_BODY = true
1683 end
1684
1685 class Lock < HTTPRequest
1686 METHOD = 'LOCK'
1687 REQUEST_HAS_BODY = true
1688 RESPONSE_HAS_BODY = true
1689 end
1690
1691 class Unlock < HTTPRequest
1692 METHOD = 'UNLOCK'
1693 REQUEST_HAS_BODY = true
1694 RESPONSE_HAS_BODY = true
1695 end
1696 end
1697
1698
1699 ###
1700 ### Response
1701 ###
1702
1703 # HTTP exception class.
1704 # You must use its subclasses.
1705 module HTTPExceptions
1706 def initialize(msg, res) #:nodoc:
1707 super msg
1708 @response = res
1709 end
1710 attr_reader :response
1711 alias data response #:nodoc: obsolete
1712 end
1713 class HTTPError < ProtocolError
1714 include HTTPExceptions
1715 end
1716 class HTTPRetriableError < ProtoRetriableError
1717 include HTTPExceptions
1718 end
1719 class HTTPServerException < ProtoServerError
1720 # We cannot use the name "HTTPServerError", it is the name of the response.
1721 include HTTPExceptions
1722 end
1723 class HTTPFatalError < ProtoFatalError
1724 include HTTPExceptions
1725 end
1726
1727
1728 # HTTP response class. This class wraps response header and entity.
1729 # Mixes in the HTTPHeader module, which provides access to response
1730 # header values both via hash-like methods and individual readers.
1731 # Note that each possible HTTP response code defines its own
1732 # HTTPResponse subclass. These are listed below.
1733 # All classes are
1734 # defined under the Net module. Indentation indicates inheritance.
1735 #
1736 # xxx HTTPResponse
1737 #
1738 # 1xx HTTPInformation
1739 # 100 HTTPContinue
1740 # 101 HTTPSwitchProtocol
1741 #
1742 # 2xx HTTPSuccess
1743 # 200 HTTPOK
1744 # 201 HTTPCreated
1745 # 202 HTTPAccepted
1746 # 203 HTTPNonAuthoritativeInformation
1747 # 204 HTTPNoContent
1748 # 205 HTTPResetContent
1749 # 206 HTTPPartialContent
1750 #
1751 # 3xx HTTPRedirection
1752 # 300 HTTPMultipleChoice
1753 # 301 HTTPMovedPermanently
1754 # 302 HTTPFound
1755 # 303 HTTPSeeOther
1756 # 304 HTTPNotModified
1757 # 305 HTTPUseProxy
1758 # 307 HTTPTemporaryRedirect
1759 #
1760 # 4xx HTTPClientError
1761 # 400 HTTPBadRequest
1762 # 401 HTTPUnauthorized
1763 # 402 HTTPPaymentRequired
1764 # 403 HTTPForbidden
1765 # 404 HTTPNotFound
1766 # 405 HTTPMethodNotAllowed
1767 # 406 HTTPNotAcceptable
1768 # 407 HTTPProxyAuthenticationRequired
1769 # 408 HTTPRequestTimeOut
1770 # 409 HTTPConflict
1771 # 410 HTTPGone
1772 # 411 HTTPLengthRequired
1773 # 412 HTTPPreconditionFailed
1774 # 413 HTTPRequestEntityTooLarge
1775 # 414 HTTPRequestURITooLong
1776 # 415 HTTPUnsupportedMediaType
1777 # 416 HTTPRequestedRangeNotSatisfiable
1778 # 417 HTTPExpectationFailed
1779 #
1780 # 5xx HTTPServerError
1781 # 500 HTTPInternalServerError
1782 # 501 HTTPNotImplemented
1783 # 502 HTTPBadGateway
1784 # 503 HTTPServiceUnavailable
1785 # 504 HTTPGatewayTimeOut
1786 # 505 HTTPVersionNotSupported
1787 #
1788 # xxx HTTPUnknownResponse
1789 #
1790 class HTTPResponse
1791 # true if the response has body.
1792 def HTTPResponse.body_permitted?
1793 self::HAS_BODY
1794 end
1795
1796 def HTTPResponse.exception_type # :nodoc: internal use only
1797 self::EXCEPTION_TYPE
1798 end
1799 end # reopened after
1800
1801 # :stopdoc:
1802
1803 class HTTPUnknownResponse < HTTPResponse
1804 HAS_BODY = true
1805 EXCEPTION_TYPE = HTTPError
1806 end
1807 class HTTPInformation < HTTPResponse # 1xx
1808 HAS_BODY = false
1809 EXCEPTION_TYPE = HTTPError
1810 end
1811 class HTTPSuccess < HTTPResponse # 2xx
1812 HAS_BODY = true
1813 EXCEPTION_TYPE = HTTPError
1814 end
1815 class HTTPRedirection < HTTPResponse # 3xx
1816 HAS_BODY = true
1817 EXCEPTION_TYPE = HTTPRetriableError
1818 end
1819 class HTTPClientError < HTTPResponse # 4xx
1820 HAS_BODY = true
1821 EXCEPTION_TYPE = HTTPServerException # for backward compatibility
1822 end
1823 class HTTPServerError < HTTPResponse # 5xx
1824 HAS_BODY = true
1825 EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
1826 end
1827
1828 class HTTPContinue < HTTPInformation # 100
1829 HAS_BODY = false
1830 end
1831 class HTTPSwitchProtocol < HTTPInformation # 101
1832 HAS_BODY = false
1833 end
1834
1835 class HTTPOK < HTTPSuccess # 200
1836 HAS_BODY = true
1837 end
1838 class HTTPCreated < HTTPSuccess # 201
1839 HAS_BODY = true
1840 end
1841 class HTTPAccepted < HTTPSuccess # 202
1842 HAS_BODY = true
1843 end
1844 class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
1845 HAS_BODY = true
1846 end
1847 class HTTPNoContent < HTTPSuccess # 204
1848 HAS_BODY = false
1849 end
1850 class HTTPResetContent < HTTPSuccess # 205
1851 HAS_BODY = false
1852 end
1853 class HTTPPartialContent < HTTPSuccess # 206
1854 HAS_BODY = true
1855 end
1856
1857 class HTTPMultipleChoice < HTTPRedirection # 300
1858 HAS_BODY = true
1859 end
1860 class HTTPMovedPermanently < HTTPRedirection # 301
1861 HAS_BODY = true
1862 end
1863 class HTTPFound < HTTPRedirection # 302
1864 HAS_BODY = true
1865 end
1866 HTTPMovedTemporarily = HTTPFound
1867 class HTTPSeeOther < HTTPRedirection # 303
1868 HAS_BODY = true
1869 end
1870 class HTTPNotModified < HTTPRedirection # 304
1871 HAS_BODY = false
1872 end
1873 class HTTPUseProxy < HTTPRedirection # 305
1874 HAS_BODY = false
1875 end
1876 # 306 unused
1877 class HTTPTemporaryRedirect < HTTPRedirection # 307
1878 HAS_BODY = true
1879 end
1880
1881 class HTTPBadRequest < HTTPClientError # 400
1882 HAS_BODY = true
1883 end
1884 class HTTPUnauthorized < HTTPClientError # 401
1885 HAS_BODY = true
1886 end
1887 class HTTPPaymentRequired < HTTPClientError # 402
1888 HAS_BODY = true
1889 end
1890 class HTTPForbidden < HTTPClientError # 403
1891 HAS_BODY = true
1892 end
1893 class HTTPNotFound < HTTPClientError # 404
1894 HAS_BODY = true
1895 end
1896 class HTTPMethodNotAllowed < HTTPClientError # 405
1897 HAS_BODY = true
1898 end
1899 class HTTPNotAcceptable < HTTPClientError # 406
1900 HAS_BODY = true
1901 end
1902 class HTTPProxyAuthenticationRequired < HTTPClientError # 407
1903 HAS_BODY = true
1904 end
1905 class HTTPRequestTimeOut < HTTPClientError # 408
1906 HAS_BODY = true
1907 end
1908 class HTTPConflict < HTTPClientError # 409
1909 HAS_BODY = true
1910 end
1911 class HTTPGone < HTTPClientError # 410
1912 HAS_BODY = true
1913 end
1914 class HTTPLengthRequired < HTTPClientError # 411
1915 HAS_BODY = true
1916 end
1917 class HTTPPreconditionFailed < HTTPClientError # 412
1918 HAS_BODY = true
1919 end
1920 class HTTPRequestEntityTooLarge < HTTPClientError # 413
1921 HAS_BODY = true
1922 end
1923 class HTTPRequestURITooLong < HTTPClientError # 414
1924 HAS_BODY = true
1925 end
1926 HTTPRequestURITooLarge = HTTPRequestURITooLong
1927 class HTTPUnsupportedMediaType < HTTPClientError # 415
1928 HAS_BODY = true
1929 end
1930 class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
1931 HAS_BODY = true
1932 end
1933 class HTTPExpectationFailed < HTTPClientError # 417
1934 HAS_BODY = true
1935 end
1936
1937 class HTTPInternalServerError < HTTPServerError # 500
1938 HAS_BODY = true
1939 end
1940 class HTTPNotImplemented < HTTPServerError # 501
1941 HAS_BODY = true
1942 end
1943 class HTTPBadGateway < HTTPServerError # 502
1944 HAS_BODY = true
1945 end
1946 class HTTPServiceUnavailable < HTTPServerError # 503
1947 HAS_BODY = true
1948 end
1949 class HTTPGatewayTimeOut < HTTPServerError # 504
1950 HAS_BODY = true
1951 end
1952 class HTTPVersionNotSupported < HTTPServerError # 505
1953 HAS_BODY = true
1954 end
1955
1956 # :startdoc:
1957
1958
1959 class HTTPResponse # reopen
1960
1961 CODE_CLASS_TO_OBJ = {
1962 '1' => HTTPInformation,
1963 '2' => HTTPSuccess,
1964 '3' => HTTPRedirection,
1965 '4' => HTTPClientError,
1966 '5' => HTTPServerError
1967 }
1968 CODE_TO_OBJ = {
1969 '100' => HTTPContinue,
1970 '101' => HTTPSwitchProtocol,
1971
1972 '200' => HTTPOK,
1973 '201' => HTTPCreated,
1974 '202' => HTTPAccepted,
1975 '203' => HTTPNonAuthoritativeInformation,
1976 '204' => HTTPNoContent,
1977 '205' => HTTPResetContent,
1978 '206' => HTTPPartialContent,
1979
1980 '300' => HTTPMultipleChoice,
1981 '301' => HTTPMovedPermanently,
1982 '302' => HTTPFound,
1983 '303' => HTTPSeeOther,
1984 '304' => HTTPNotModified,
1985 '305' => HTTPUseProxy,
1986 '307' => HTTPTemporaryRedirect,
1987
1988 '400' => HTTPBadRequest,
1989 '401' => HTTPUnauthorized,
1990 '402' => HTTPPaymentRequired,
1991 '403' => HTTPForbidden,
1992 '404' => HTTPNotFound,
1993 '405' => HTTPMethodNotAllowed,
1994 '406' => HTTPNotAcceptable,
1995 '407' => HTTPProxyAuthenticationRequired,
1996 '408' => HTTPRequestTimeOut,
1997 '409' => HTTPConflict,
1998 '410' => HTTPGone,
1999 '411' => HTTPLengthRequired,
2000 '412' => HTTPPreconditionFailed,
2001 '413' => HTTPRequestEntityTooLarge,
2002 '414' => HTTPRequestURITooLong,
2003 '415' => HTTPUnsupportedMediaType,
2004 '416' => HTTPRequestedRangeNotSatisfiable,
2005 '417' => HTTPExpectationFailed,
2006
2007 '500' => HTTPInternalServerError,
2008 '501' => HTTPNotImplemented,
2009 '502' => HTTPBadGateway,
2010 '503' => HTTPServiceUnavailable,
2011 '504' => HTTPGatewayTimeOut,
2012 '505' => HTTPVersionNotSupported
2013 }
2014
2015 class << HTTPResponse
2016 def read_new(sock) #:nodoc: internal use only
2017 httpv, code, msg = read_status_line(sock)
2018 res = response_class(code).new(httpv, code, msg)
2019 each_response_header(sock) do |k,v|
2020 res.add_field k, v
2021 end
2022 res
2023 end
2024
2025 private
2026
2027 def read_status_line(sock)
2028 str = sock.readline
2029 m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
2030 raise HTTPBadResponse, "wrong status line: #{str.dump}"
2031 m.captures
2032 end
2033
2034 def response_class(code)
2035 CODE_TO_OBJ[code] or
2036 CODE_CLASS_TO_OBJ[code[0,1]] or
2037 HTTPUnknownResponse
2038 end
2039
2040 def each_response_header(sock)
2041 while true
2042 line = sock.readuntil("\n", true).sub(/\s+\z/, '')
2043 break if line.empty?
2044 m = /\A([^:]+):\s*/.match(line) or
2045 raise HTTPBadResponse, 'wrong header line format'
2046 yield m[1], m.post_match
2047 end
2048 end
2049 end
2050
2051 # next is to fix bug in RDoc, where the private inside class << self
2052 # spills out.
2053 public
2054
2055 include HTTPHeader
2056
2057 def initialize(httpv, code, msg) #:nodoc: internal use only
2058 @http_version = httpv
2059 @code = code
2060 @message = msg
2061 initialize_http_header nil
2062 @body = nil
2063 @read = false
2064 end
2065
2066 # The HTTP version supported by the server.
2067 attr_reader :http_version
2068
2069 # HTTP result code string. For example, '302'. You can also
2070 # determine the response type by which response subclass the
2071 # response object is an instance of.
2072 attr_reader :code
2073
2074 # HTTP result message. For example, 'Not Found'.
2075 attr_reader :message
2076 alias msg message # :nodoc: obsolete
2077
2078 def inspect
2079 "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
2080 end
2081
2082 # For backward compatibility.
2083 # To allow Net::HTTP 1.1 style assignment
2084 # e.g.
2085 # response, body = Net::HTTP.get(....)
2086 #
2087 def to_ary
2088 warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
2089 res = self.dup
2090 class << res
2091 undef to_ary
2092 end
2093 [res, res.body]
2094 end
2095
2096 #
2097 # response <-> exception relationship
2098 #
2099
2100 def code_type #:nodoc:
2101 self.class
2102 end
2103
2104 def error! #:nodoc:
2105 raise error_type().new(@code + ' ' + @message.dump, self)
2106 end
2107
2108 def error_type #:nodoc:
2109 self.class::EXCEPTION_TYPE
2110 end
2111
2112 # Raises HTTP error if the response is not 2xx.
2113 def value
2114 error! unless self.kind_of?(HTTPSuccess)
2115 end
2116
2117 #
2118 # header (for backward compatibility only; DO NOT USE)
2119 #
2120
2121 def response #:nodoc:
2122 warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
2123 self
2124 end
2125
2126 def header #:nodoc:
2127 warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
2128 self
2129 end
2130
2131 def read_header #:nodoc:
2132 warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
2133 self
2134 end
2135
2136 #
2137 # body
2138 #
2139
2140 def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
2141 @socket = sock
2142 @body_exist = reqmethodallowbody && self.class.body_permitted?
2143 begin
2144 yield
2145 self.body # ensure to read body
2146 ensure
2147 @socket = nil
2148 end
2149 end
2150
2151 # Gets entity body. If the block given, yields it to +block+.
2152 # The body is provided in fragments, as it is read in from the socket.
2153 #
2154 # Calling this method a second or subsequent time will return the
2155 # already read string.
2156 #
2157 # http.request_get('/index.html') {|res|
2158 # puts res.read_body
2159 # }
2160 #
2161 # http.request_get('/index.html') {|res|
2162 # p res.read_body.object_id # 538149362
2163 # p res.read_body.object_id # 538149362
2164 # }
2165 #
2166 # # using iterator
2167 # http.request_get('/index.html') {|res|
2168 # res.read_body do |segment|
2169 # print segment
2170 # end
2171 # }
2172 #
2173 def read_body(dest = nil, &block)
2174 if @read
2175 raise IOError, "#{self.class}\#read_body called twice" if dest or block
2176 return @body
2177 end
2178 to = procdest(dest, block)
2179 stream_check
2180 if @body_exist
2181 read_body_0 to
2182 @body = to
2183 else
2184 @body = nil
2185 end
2186 @read = true
2187
2188 @body
2189 end
2190
2191 # Returns the entity body.
2192 #
2193 # Calling this method a second or subsequent time will return the
2194 # already read string.
2195 #
2196 # http.request_get('/index.html') {|res|
2197 # puts res.body
2198 # }
2199 #
2200 # http.request_get('/index.html') {|res|
2201 # p res.body.object_id # 538149362
2202 # p res.body.object_id # 538149362
2203 # }
2204 #
2205 def body
2206 read_body()
2207 end
2208
2209 alias entity body #:nodoc: obsolete
2210
2211 private
2212
2213 def read_body_0(dest)
2214 if chunked?
2215 read_chunked dest
2216 return
2217 end
2218 clen = content_length()
2219 if clen
2220 @socket.read clen, dest, true # ignore EOF
2221 return
2222 end
2223 clen = range_length()
2224 if clen
2225 @socket.read clen, dest
2226 return
2227 end
2228 @socket.read_all dest
2229 end
2230
2231 def read_chunked(dest)
2232 len = nil
2233 total = 0
2234 while true
2235 line = @socket.readline
2236 hexlen = line.slice(/[0-9a-fA-F]+/) or
2237 raise HTTPBadResponse, "wrong chunk size line: #{line}"
2238 len = hexlen.hex
2239 break if len == 0
2240 @socket.read len, dest; total += len
2241 @socket.read 2 # \r\n
2242 end
2243 until @socket.readline.empty?
2244 # none
2245 end
2246 end
2247
2248 def stream_check
2249 raise IOError, 'attempt to read body out of block' if @socket.closed?
2250 end
2251
2252 def procdest(dest, block)
2253 raise ArgumentError, 'both arg and block given for HTTP method' \
2254 if dest and block
2255 if block
2256 ReadAdapter.new(block)
2257 else
2258 dest || ''
2259 end
2260 end
2261
2262 end
2263
2264
2265 # :enddoc:
2266
2267 #--
2268 # for backward compatibility
2269 class HTTP
2270 ProxyMod = ProxyDelta
2271 end
2272 module NetPrivate
2273 HTTPRequest = ::Net::HTTPRequest
2274 end
2275
2276 HTTPInformationCode = HTTPInformation
2277 HTTPSuccessCode = HTTPSuccess
2278 HTTPRedirectionCode = HTTPRedirection
2279 HTTPRetriableCode = HTTPRedirection
2280 HTTPClientErrorCode = HTTPClientError
2281 HTTPFatalErrorCode = HTTPClientError
2282 HTTPServerErrorCode = HTTPServerError
2283 HTTPResponceReceiver = HTTPResponse
2284
2285 end # module Net