1999-12-17 10:00:13 -05:00
|
|
|
=begin
|
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
= net/http.rb
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
Copyright (c) 1999-2002 Yukihiro Matsumoto
|
2001-02-23 23:53:50 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
written & maintained by Minero Aoki <aamine@loveruby.net>
|
|
|
|
This file is derived from "http-access.rb".
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
This program is free software. You can re-distribute and/or
|
|
|
|
modify this program under the same terms as Ruby itself,
|
|
|
|
Ruby Distribute License or GNU General Public License.
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2001-12-07 05:04:25 -05:00
|
|
|
NOTE: You can find Japanese version of this document in
|
|
|
|
the doc/net directory of the standard ruby interpreter package.
|
2000-09-21 02:58:01 -04:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
$Id$
|
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
== What is this module?
|
|
|
|
|
|
|
|
This module provide your program the functions to access WWW
|
|
|
|
documents via HTTP, Hyper Text Transfer Protocol version 1.1.
|
|
|
|
For details of HTTP, refer [RFC2616]
|
|
|
|
((<URL:http://www.ietf.org/rfc/rfc2616.txt>)).
|
|
|
|
|
|
|
|
== Examples
|
|
|
|
|
|
|
|
=== Getting Document From Server
|
|
|
|
|
|
|
|
Be care to ',' (comma) putted after "response".
|
2001-06-26 20:59:08 -04:00
|
|
|
This is required for compatibility.
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
Net::HTTP.start( 'some.www.server', 80 ) {|http|
|
|
|
|
response , = http.get('/index.html')
|
|
|
|
puts response.body
|
|
|
|
}
|
|
|
|
|
|
|
|
(shorter version)
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
Net::HTTP.get_print 'some.www.server', '/index.html'
|
|
|
|
|
|
|
|
=== Posting Form Data
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
Net::HTTP.start( 'some.www.server', 80 ) {|http|
|
2001-12-09 03:58:30 -05:00
|
|
|
response , = http.post( '/cgi-bin/any.rhtml',
|
|
|
|
'querytype=subject&target=ruby' )
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
=== Accessing via Proxy
|
|
|
|
|
|
|
|
Net::HTTP.Proxy() creates http proxy class. It has same
|
|
|
|
methods of Net::HTTP but its instances always connect to
|
|
|
|
proxy, instead of given host.
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
|
|
|
|
$proxy_addr = 'your.proxy.addr'
|
|
|
|
$proxy_port = 8080
|
|
|
|
:
|
|
|
|
Net::HTTP::Proxy($proxy_addr, $proxy_port).start( 'some.www.server' ) {|http|
|
|
|
|
# always connect to your.proxy.addr:8080
|
|
|
|
:
|
|
|
|
}
|
|
|
|
|
|
|
|
Since Net::HTTP.Proxy() returns Net::HTTP itself when $proxy_addr is nil,
|
|
|
|
there's no need to change code if there's proxy or not.
|
|
|
|
|
|
|
|
=== Redirect
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
Net::HTTP.version_1_1
|
|
|
|
|
|
|
|
host = 'www.ruby-lang.org'
|
2001-06-26 20:59:08 -04:00
|
|
|
path = '/'
|
2001-06-26 19:49:21 -04:00
|
|
|
begin
|
|
|
|
Net::HTTP.start( host, 80 ) {|http|
|
2001-06-26 20:59:08 -04:00
|
|
|
response , = http.get(path)
|
|
|
|
print response.body
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
|
|
|
rescue Net::ProtoRetriableError => err
|
2001-06-26 20:59:08 -04:00
|
|
|
if m = %r<http://([^/]+)>.match( err.response['location'] ) then
|
|
|
|
host = m[1].strip
|
|
|
|
path = m.post_match
|
|
|
|
retry
|
2001-06-26 19:49:21 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
NOTE: This code is using ad-hoc way to extract host name, but in future
|
|
|
|
URI class will be included in ruby standard library.
|
|
|
|
|
|
|
|
=== Basic Authentication
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
|
|
|
|
Net::HTTP.start( 'auth.some.domain' ) {|http|
|
2001-12-09 03:58:30 -05:00
|
|
|
response , = http.get( '/need-auth.cgi',
|
2002-02-19 07:33:52 -05:00
|
|
|
'Authorization' => 'Basic ' + ["#{account}:#{password}"].pack('m').strip )
|
2001-12-09 03:58:30 -05:00
|
|
|
print response.body
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
In version 1.2 (Ruby 1.7 or later), you can write like this:
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
|
|
|
|
req = Net::HTTP::Get.new('/need-auth.cgi')
|
|
|
|
req.basic_auth 'account', 'password'
|
|
|
|
Net::HTTP.start( 'auth.some.domain' ) {|http|
|
2001-12-09 03:58:30 -05:00
|
|
|
response = http.request(req)
|
|
|
|
print response.body
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
== Switching Net::HTTP versions
|
|
|
|
|
|
|
|
You can use old Net::HTTP (in Ruby 1.6) features by calling
|
|
|
|
HTTP.version_1_1. And calling Net::HTTP.version_1_2 allows
|
|
|
|
you to use 1.2 features again.
|
|
|
|
|
|
|
|
# example
|
|
|
|
Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
|
|
|
|
|
|
|
|
Net::HTTP.version_1_1
|
|
|
|
Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
|
|
|
|
|
|
|
|
Net::HTTP.version_1_2
|
|
|
|
Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
|
|
|
|
|
|
|
|
Yes, this is not thread-safe.
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
== class Net::HTTP
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
=== Class Methods
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-08-16 23:08:45 -04:00
|
|
|
: new( address, port = 80, proxy_addr = nil, proxy_port = nil )
|
2001-06-26 19:49:21 -04:00
|
|
|
creates a new Net::HTTP object.
|
2001-06-26 20:59:08 -04:00
|
|
|
If proxy_addr is given, creates an Net::HTTP object with proxy support.
|
2000-05-18 04:57:37 -04:00
|
|
|
|
2001-08-16 23:08:45 -04:00
|
|
|
: start( address, port = 80, proxy_addr = nil, proxy_port = nil )
|
|
|
|
: start( address, port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... }
|
2001-06-26 19:49:21 -04:00
|
|
|
is equals to
|
2001-02-22 18:23:57 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
Net::HTTP.new(address, port, proxy_addr, proxy_port).start(&block)
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
: get( address, path, port = 80 )
|
2001-06-26 19:49:21 -04:00
|
|
|
gets entity body from path and returns it.
|
|
|
|
return value is a String.
|
2001-02-22 18:23:57 -05:00
|
|
|
|
|
|
|
: get_print( address, path, port = 80 )
|
2001-06-26 20:59:08 -04:00
|
|
|
gets entity body from path and output it to $stdout.
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
: Proxy( address, port = 80 )
|
|
|
|
creates a HTTP proxy class.
|
|
|
|
Arguments are address/port of proxy host.
|
2001-06-26 20:59:08 -04:00
|
|
|
You can replace HTTP class with created proxy class.
|
2001-06-26 19:49:21 -04:00
|
|
|
|
2001-06-26 20:59:08 -04:00
|
|
|
If ADDRESS is nil, this method returns self (Net::HTTP).
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
# example
|
|
|
|
proxy_class = Net::HTTP::Proxy( 'proxy.foo.org', 8080 )
|
2001-12-09 03:58:30 -05:00
|
|
|
:
|
|
|
|
proxy_class.start( 'www.ruby-lang.org' ) {|http|
|
|
|
|
# connecting proxy.foo.org:8080
|
|
|
|
:
|
|
|
|
}
|
2000-11-10 18:31:32 -05:00
|
|
|
|
|
|
|
: proxy_class?
|
2001-06-26 19:49:21 -04:00
|
|
|
If self is HTTP, false.
|
|
|
|
If self is a class which was created by HTTP::Proxy(), true.
|
2000-11-10 18:31:32 -05:00
|
|
|
|
1999-12-29 06:14:04 -05:00
|
|
|
: port
|
2001-06-26 20:59:08 -04:00
|
|
|
default HTTP port (80).
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
=== Instance Methods
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2000-05-18 04:57:37 -04:00
|
|
|
: start
|
|
|
|
: start {|http| .... }
|
2001-06-26 19:49:21 -04:00
|
|
|
creates a new Net::HTTP object and starts HTTP session.
|
|
|
|
|
|
|
|
When this method is called with block, gives a HTTP object to block
|
|
|
|
and close the HTTP session after block call finished.
|
|
|
|
|
|
|
|
: active?
|
|
|
|
true if HTTP session is started.
|
|
|
|
|
|
|
|
: address
|
|
|
|
the address to connect
|
|
|
|
|
|
|
|
: port
|
|
|
|
the port number to connect
|
2000-05-18 04:57:37 -04:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
: open_timeout
|
|
|
|
: open_timeout=(n)
|
|
|
|
seconds to wait until connection is opened.
|
|
|
|
If HTTP object cannot open a conection in this seconds,
|
|
|
|
it raises TimeoutError exception.
|
|
|
|
|
|
|
|
: read_timeout
|
|
|
|
: read_timeout=(n)
|
|
|
|
seconds to wait until reading one block (by one read(1) call).
|
|
|
|
If HTTP object cannot open a conection in this seconds,
|
|
|
|
it raises TimeoutError exception.
|
|
|
|
|
|
|
|
: finish
|
|
|
|
finishes HTTP session.
|
2001-07-03 15:03:16 -04:00
|
|
|
If HTTP session had not started, raises an IOError.
|
2000-05-18 04:57:37 -04:00
|
|
|
|
2000-11-10 18:31:32 -05:00
|
|
|
: proxy?
|
2001-06-26 19:49:21 -04:00
|
|
|
true if self is a HTTP proxy class
|
2000-11-10 18:31:32 -05:00
|
|
|
|
|
|
|
: proxy_address
|
2001-06-26 20:59:08 -04:00
|
|
|
address of proxy host. If self does not use a proxy, nil.
|
2000-11-10 18:31:32 -05:00
|
|
|
|
|
|
|
: proxy_port
|
2001-06-26 20:59:08 -04:00
|
|
|
port number of proxy host. If self does not use a proxy, nil.
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
: get( path, header = nil )
|
2000-03-26 03:48:15 -05:00
|
|
|
: get( path, header = nil ) {|str| .... }
|
2001-06-26 20:59:08 -04:00
|
|
|
gets data from PATH on the connecting host.
|
|
|
|
HEADER must be a Hash like { 'Accept' => '*/*', ... }.
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
In version 1.1, this method returns a pair of objects,
|
|
|
|
a Net::HTTPResponse object and entity body string.
|
|
|
|
In version 1.2, this method returns a Net::HTTPResponse
|
|
|
|
object.
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
If called with block, gives entity body string to the block
|
|
|
|
little by little.
|
2000-03-26 03:48:15 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
In version 1.1, this method might raises exception for also
|
2001-06-26 20:59:08 -04:00
|
|
|
3xx (redirect). On the case you can get a HTTPResponse object
|
|
|
|
by "anException.response".
|
2001-06-26 19:49:21 -04:00
|
|
|
In version 1.2, this method never raises exception.
|
1999-12-22 08:49:13 -05:00
|
|
|
|
2001-06-26 20:59:08 -04:00
|
|
|
# version 1.1 (bundled with Ruby 1.6)
|
2001-06-26 19:49:21 -04:00
|
|
|
response, body = http.get( '/index.html' )
|
1999-12-22 08:49:13 -05:00
|
|
|
|
2001-06-26 20:59:08 -04:00
|
|
|
# version 1.2 (bundled with Ruby 1.7 or later)
|
2001-06-26 19:49:21 -04:00
|
|
|
response = http.get( '/index.html' )
|
2000-03-26 03:48:15 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
# compatible in both version
|
|
|
|
response , = http.get( '/index.html' )
|
|
|
|
response.body
|
|
|
|
|
|
|
|
# using block
|
|
|
|
File.open( 'save.txt', 'w' ) {|f|
|
2001-12-09 03:58:30 -05:00
|
|
|
http.get( '/~foo/', nil ) do |str|
|
|
|
|
f.write str
|
|
|
|
end
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
2000-03-26 03:48:15 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
: head( path, header = nil )
|
2001-06-26 20:59:08 -04:00
|
|
|
gets only header from PATH on the connecting host.
|
|
|
|
HEADER is a Hash like { 'Accept' => '*/*', ... }.
|
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
This method returns a Net::HTTPResponse object.
|
2001-06-26 20:59:08 -04:00
|
|
|
|
|
|
|
In version 1.1, this method might raises exception for also
|
|
|
|
3xx (redirect). On the case you can get a HTTPResponse object
|
|
|
|
by "anException.response".
|
2001-12-30 14:18:45 -05:00
|
|
|
In version 1.2, this method never raises exception.
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
response = nil
|
|
|
|
Net::HTTP.start( 'some.www.server', 80 ) {|http|
|
2001-12-09 03:58:30 -05:00
|
|
|
response = http.head( '/index.html' )
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
2001-07-08 03:00:23 -04:00
|
|
|
p response['content-type']
|
2000-04-22 03:29:53 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
: post( path, data, header = nil )
|
2001-06-26 19:49:21 -04:00
|
|
|
: post( path, data, header = nil ) {|str| .... }
|
2001-12-30 14:18:45 -05:00
|
|
|
posts DATA (must be String) to PATH. HEADER must be a Hash
|
|
|
|
like { 'Accept' => '*/*', ... }.
|
|
|
|
|
|
|
|
In version 1.1, this method returns a pair of objects, a
|
|
|
|
Net::HTTPResponse object and an entity body string.
|
|
|
|
In version 1.2, this method returns a Net::HTTPReponse object.
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
If called with block, gives a part of entity body string.
|
|
|
|
|
2001-06-26 20:59:08 -04:00
|
|
|
In version 1.1, this method might raises exception for also
|
|
|
|
3xx (redirect). On the case you can get a HTTPResponse object
|
|
|
|
by "anException.response".
|
2001-12-30 14:18:45 -05:00
|
|
|
In version 1.2, this method never raises exception.
|
2001-06-26 20:59:08 -04:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
# version 1.1
|
2001-12-30 14:18:45 -05:00
|
|
|
response, body = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
# version 1.2
|
2001-12-30 14:18:45 -05:00
|
|
|
response = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
|
|
|
|
|
|
|
# compatible in both version
|
|
|
|
response , = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
2001-06-26 19:49:21 -04:00
|
|
|
|
|
|
|
# using block
|
|
|
|
File.open( 'save.html', 'w' ) {|f|
|
2001-12-30 14:18:45 -05:00
|
|
|
http.post( '/cgi-bin/search.rb',
|
|
|
|
'query=subject&target=ruby' ) do |str|
|
2001-12-09 03:58:30 -05:00
|
|
|
f.write str
|
|
|
|
end
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
2001-07-08 03:00:23 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
: request_get( path, header = nil )
|
|
|
|
: request_get( path, header = nil ) {|response| .... }
|
2001-07-08 03:00:23 -04:00
|
|
|
gets entity from PATH. This method returns a HTTPResponse object.
|
|
|
|
|
|
|
|
When called with block, keep connection while block is executed
|
|
|
|
and gives a HTTPResponse object to the block.
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
This method never raises Net::* exceptions.
|
2001-07-08 03:00:23 -04:00
|
|
|
|
|
|
|
# example
|
2001-12-30 14:18:45 -05:00
|
|
|
response = http.request_get( '/index.html' )
|
2001-07-08 03:00:23 -04:00
|
|
|
p response['content-type']
|
|
|
|
puts response.body # body is already read
|
|
|
|
|
|
|
|
# using block
|
2001-12-30 14:18:45 -05:00
|
|
|
http.request_get( '/index.html' ) {|response|
|
2001-12-09 03:58:30 -05:00
|
|
|
p response['content-type']
|
|
|
|
response.read_body do |str| # read body now
|
|
|
|
print str
|
|
|
|
end
|
2001-07-08 03:00:23 -04:00
|
|
|
}
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
: request_post( path, data, header = nil )
|
|
|
|
: request_post( path, data, header = nil ) {|response| .... }
|
2001-07-08 03:00:23 -04:00
|
|
|
posts data to PATH. This method returns a HTTPResponse object.
|
|
|
|
|
|
|
|
When called with block, gives a HTTPResponse object to the block
|
|
|
|
before reading entity body, with keeping connection.
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
This method never raises Net::* exceptions.
|
|
|
|
|
2001-07-08 03:00:23 -04:00
|
|
|
# example
|
|
|
|
response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
|
|
|
|
p response.status
|
|
|
|
puts response.body # body is already read
|
|
|
|
|
|
|
|
# using block
|
|
|
|
http.post2( '/cgi-bin/nice.rb', 'datadatadata...' ) {|response|
|
2001-12-09 03:58:30 -05:00
|
|
|
p response.status
|
|
|
|
p response['content-type']
|
|
|
|
response.read_body do |str| # read body now
|
|
|
|
print str
|
|
|
|
end
|
2001-06-26 19:49:21 -04:00
|
|
|
}
|
2000-09-21 02:58:01 -04:00
|
|
|
|
2001-06-26 20:59:08 -04:00
|
|
|
: request( request [, data] )
|
|
|
|
: request( request [, data] ) {|response| .... }
|
2001-12-30 14:18:45 -05:00
|
|
|
sends a HTTPRequest object REQUEST to the HTTP server.
|
2001-07-08 03:00:23 -04:00
|
|
|
This method also writes DATA string if REQUEST is a post/put request.
|
|
|
|
Giving DATA for get/head request causes ArgumentError.
|
2000-09-21 02:58:01 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
If called with block, this method passes a HTTPResponse object to
|
|
|
|
the block, without reading entity body.
|
|
|
|
|
|
|
|
This method never raises Net::* exceptions.
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
== class Net::HTTP::Get, Head, Post
|
2000-03-27 10:52:27 -05:00
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
HTTP request classes. These classes wraps request header and
|
2001-03-08 03:39:40 -05:00
|
|
|
entity path. All arguments named "key" is case-insensitive.
|
|
|
|
|
|
|
|
=== Class Methods
|
|
|
|
|
|
|
|
: new
|
|
|
|
creats HTTP request object.
|
2000-12-24 15:01:44 -05:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
=== Instance Methods
|
2000-12-24 15:01:44 -05:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
: self[ key ]
|
2001-03-08 03:39:40 -05:00
|
|
|
returns the header field corresponding to the case-insensitive key.
|
|
|
|
For example, a key of "Content-Type" might return "text/html"
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
: self[ key ] = val
|
2001-03-08 03:39:40 -05:00
|
|
|
sets the header field corresponding to the case-insensitive key.
|
2000-09-21 02:58:01 -04:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
: each {|name, val| .... }
|
|
|
|
iterates for each field name and value pair.
|
|
|
|
|
|
|
|
: basic_auth( account, password )
|
|
|
|
set Authorization: header for basic auth.
|
|
|
|
|
|
|
|
: range
|
|
|
|
returns a Range object which represents Range: header field.
|
|
|
|
|
|
|
|
: range = r
|
|
|
|
: set_range( i, len )
|
|
|
|
set Range: header from Range (arg r) or beginning index and
|
|
|
|
length from it (arg i&len).
|
|
|
|
|
|
|
|
: content_length
|
|
|
|
returns a Integer object which represents Content-Length: header field.
|
|
|
|
|
|
|
|
: content_range
|
|
|
|
returns a Range object which represents Content-Range: header field.
|
2000-03-27 10:52:27 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
== class Net::HTTPResponse
|
2000-03-27 10:52:27 -05:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
HTTP response class. This class wraps response header and entity.
|
2001-02-23 23:53:50 -05:00
|
|
|
All arguments named KEY is case-insensitive.
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
=== Instance Methods
|
2000-03-31 08:02:40 -05:00
|
|
|
|
|
|
|
: self[ key ]
|
2001-03-08 03:39:40 -05:00
|
|
|
returns the header field corresponding to the case-insensitive key.
|
|
|
|
For example, a key of "Content-Type" might return "text/html".
|
|
|
|
A key of "Content-Length" might do "2045".
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
More than one fields which has same names are joined with ','.
|
2000-03-31 08:02:40 -05:00
|
|
|
|
|
|
|
: self[ key ] = val
|
2001-03-08 03:39:40 -05:00
|
|
|
sets the header field corresponding to the case-insensitive key.
|
2000-03-27 10:52:27 -05:00
|
|
|
|
2000-03-31 08:02:40 -05:00
|
|
|
: key?( key )
|
2001-02-23 23:53:50 -05:00
|
|
|
true if key exists.
|
|
|
|
KEY is case insensitive.
|
2000-03-31 08:02:40 -05:00
|
|
|
|
2000-04-18 05:39:02 -04:00
|
|
|
: each {|name,value| .... }
|
2001-02-23 23:53:50 -05:00
|
|
|
iterates for each field name and value pair.
|
|
|
|
|
|
|
|
: canonical_each {|name,value| .... }
|
2001-06-26 19:49:21 -04:00
|
|
|
iterates for each "canonical" field name and value pair.
|
2000-04-18 05:39:02 -04:00
|
|
|
|
2000-06-27 09:36:17 -04:00
|
|
|
: code
|
2001-02-23 23:53:50 -05:00
|
|
|
HTTP result code string. For example, '302'.
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2000-06-27 09:36:17 -04:00
|
|
|
: message
|
2001-02-23 23:53:50 -05:00
|
|
|
HTTP result message. For example, 'Not Found'.
|
2000-03-31 08:02:40 -05:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
: read_body( dest = '' )
|
2001-06-26 19:49:21 -04:00
|
|
|
gets entity body and write it into DEST using "<<" method.
|
2001-02-23 23:53:50 -05:00
|
|
|
If this method is called twice or more, nothing will be done
|
|
|
|
and returns first DEST.
|
2000-06-27 09:36:17 -04:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
: read_body {|str| .... }
|
2001-06-26 19:49:21 -04:00
|
|
|
gets entity body little by little and pass it to block.
|
2001-02-23 23:53:50 -05:00
|
|
|
|
|
|
|
: body
|
2001-06-26 19:49:21 -04:00
|
|
|
response body. If #read_body has been called, this method returns
|
|
|
|
arg of #read_body DEST. Else gets body as String and returns it.
|
2000-06-27 09:36:17 -04:00
|
|
|
|
1999-12-29 06:14:04 -05:00
|
|
|
=end
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2000-06-12 12:42:46 -04:00
|
|
|
require 'net/protocol'
|
|
|
|
|
|
|
|
|
|
|
|
module Net
|
|
|
|
|
|
|
|
class HTTPBadResponse < StandardError; end
|
2001-02-22 18:23:57 -05:00
|
|
|
class HTTPHeaderSyntaxError < StandardError; end
|
2000-06-12 12:42:46 -04:00
|
|
|
|
|
|
|
|
1999-12-29 06:14:04 -05:00
|
|
|
class HTTP < Protocol
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
HTTPVersion = '1.1'
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
|
|
|
|
#
|
2002-02-19 07:33:52 -05:00
|
|
|
# for backward compatibility
|
2002-02-07 15:22:39 -05:00
|
|
|
#
|
|
|
|
|
2002-02-22 07:10:58 -05:00
|
|
|
@@newimpl = true
|
2002-02-07 15:22:39 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def HTTP.version_1_2
|
|
|
|
@@newimpl = true
|
|
|
|
end
|
2002-02-07 15:22:39 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def HTTP.version_1_1
|
|
|
|
@@newimpl = false
|
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def HTTP.is_version_1_2?
|
|
|
|
@@newimpl
|
2001-02-06 06:14:51 -05:00
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def HTTP.setimplversion( obj )
|
|
|
|
f = @@newimpl
|
|
|
|
obj.instance_eval { @newimpl = f }
|
2001-12-30 14:18:45 -05:00
|
|
|
end
|
2002-02-19 07:33:52 -05:00
|
|
|
private_class_method :setimplversion
|
2001-12-30 14:18:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# short cut methods
|
|
|
|
#
|
|
|
|
|
|
|
|
def HTTP.get( addr, path, port = nil )
|
|
|
|
req = Get.new( path )
|
|
|
|
resp = nil
|
2002-02-22 07:10:58 -05:00
|
|
|
new( addr, port || HTTP.default_port ).start {|http|
|
2001-12-30 14:18:45 -05:00
|
|
|
resp = http.request( req )
|
|
|
|
}
|
|
|
|
resp.body
|
|
|
|
end
|
|
|
|
|
|
|
|
def HTTP.get_print( addr, path, port = nil )
|
|
|
|
new( addr, port || HTTP.port ).start {|http|
|
|
|
|
http.get path, nil, $stdout
|
|
|
|
}
|
|
|
|
nil
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
#
|
2002-02-19 07:33:52 -05:00
|
|
|
# connection
|
2001-02-23 23:53:50 -05:00
|
|
|
#
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
protocol_param :default_port, '80'
|
|
|
|
protocol_param :socket_type, '::Net::InternetMessageIO'
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
class << HTTP
|
|
|
|
def start( address, port = nil, p_addr = nil, p_port = nil, &block )
|
|
|
|
new( address, port, p_addr, p_port ).start( &block )
|
2000-06-27 09:36:17 -04:00
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
alias newobj new
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def new( address, port = nil, p_addr = nil, p_port = nil )
|
|
|
|
obj = Proxy(p_addr, p_port).newobj(address, port)
|
|
|
|
setimplversion obj
|
|
|
|
obj
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize( addr, port = nil )
|
|
|
|
super
|
|
|
|
@curr_http_version = HTTPVersion
|
|
|
|
@seems_1_0_server = false
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def do_start
|
|
|
|
conn_socket
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_finish
|
|
|
|
disconn_socket
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# proxy
|
|
|
|
#
|
|
|
|
|
|
|
|
public
|
|
|
|
|
|
|
|
# no proxy
|
|
|
|
@is_proxy_class = false
|
|
|
|
@proxy_addr = nil
|
|
|
|
@proxy_port = nil
|
|
|
|
|
|
|
|
def HTTP.Proxy( p_addr, p_port = nil )
|
|
|
|
p_addr or return self
|
|
|
|
|
|
|
|
p_port ||= port()
|
|
|
|
delta = ProxyDelta
|
|
|
|
proxyclass = Class.new(self)
|
|
|
|
proxyclass.module_eval {
|
|
|
|
include delta
|
|
|
|
# with proxy
|
|
|
|
@is_proxy_class = true
|
|
|
|
@proxy_address = p_addr
|
|
|
|
@proxy_port = p_port
|
|
|
|
}
|
|
|
|
proxyclass
|
|
|
|
end
|
|
|
|
|
|
|
|
class << HTTP
|
2001-06-26 19:49:21 -04:00
|
|
|
def proxy_class?
|
|
|
|
@is_proxy_class
|
2000-11-10 18:31:32 -05:00
|
|
|
end
|
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
attr_reader :proxy_address
|
|
|
|
attr_reader :proxy_port
|
2000-11-10 18:31:32 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def proxy?
|
2001-07-13 17:20:41 -04:00
|
|
|
type.proxy_class?
|
2000-11-10 18:31:32 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def proxy_address
|
2001-06-26 19:49:21 -04:00
|
|
|
type.proxy_address
|
2000-11-10 18:31:32 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def proxy_port
|
2001-06-26 19:49:21 -04:00
|
|
|
type.proxy_port
|
2000-11-10 18:31:32 -05:00
|
|
|
end
|
|
|
|
|
2001-06-26 19:49:21 -04:00
|
|
|
alias proxyaddr proxy_address
|
|
|
|
alias proxyport proxy_port
|
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
private
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
# no proxy
|
2000-06-27 09:36:17 -04:00
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
def conn_address
|
|
|
|
address
|
|
|
|
end
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
def conn_port
|
|
|
|
port
|
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
def edit_path( path )
|
|
|
|
path
|
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2002-02-07 15:22:39 -05:00
|
|
|
module ProxyDelta
|
2001-02-06 06:14:51 -05:00
|
|
|
private
|
2002-02-07 15:22:39 -05:00
|
|
|
|
|
|
|
# with proxy
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def conn_address
|
2002-02-07 15:22:39 -05:00
|
|
|
proxy_address
|
2001-12-30 14:18:45 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def conn_port
|
2002-02-07 15:22:39 -05:00
|
|
|
proxy_port
|
2001-02-06 06:14:51 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def edit_path( path )
|
2001-12-30 14:18:45 -05:00
|
|
|
'http://' + addr_port() + path
|
2001-02-06 06:14:51 -05:00
|
|
|
end
|
2002-02-07 15:22:39 -05:00
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# http operations
|
|
|
|
#
|
2000-11-10 18:31:32 -05:00
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
public
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def get( path, initheader = nil, dest = nil, &block )
|
|
|
|
res = nil
|
|
|
|
request( Get.new(path,initheader) ) {|res|
|
|
|
|
res.read_body dest, &block
|
|
|
|
}
|
|
|
|
unless @newimpl then
|
|
|
|
res.value
|
|
|
|
return res, res.body
|
|
|
|
end
|
2000-03-26 03:48:15 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
def head( path, initheader = nil )
|
|
|
|
res = request( Head.new(path,initheader) )
|
|
|
|
@newimpl or res.value
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
def post( path, data, initheader = nil, dest = nil, &block )
|
|
|
|
res = nil
|
|
|
|
request( Post.new(path,initheader), data ) {|res|
|
|
|
|
res.read_body dest, &block
|
|
|
|
}
|
|
|
|
unless @newimpl then
|
|
|
|
res.value
|
|
|
|
return res, res.body
|
|
|
|
end
|
|
|
|
|
|
|
|
res
|
2000-12-24 15:01:44 -05:00
|
|
|
end
|
2000-03-26 03:48:15 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def put( path, data, initheader = nil )
|
|
|
|
res = request( Put.new(path,initheader), data )
|
|
|
|
@newimpl or res.value
|
|
|
|
res
|
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def request_get( path, initheader = nil, &block )
|
|
|
|
request Get.new(path,initheader), &block
|
|
|
|
end
|
|
|
|
|
|
|
|
def request_head( path, initheader = nil, &block )
|
|
|
|
request Head.new(path,initheader), &block
|
|
|
|
end
|
|
|
|
|
|
|
|
def request_post( path, data, initheader = nil, &block )
|
|
|
|
request Post.new(path,initheader), data, &block
|
|
|
|
end
|
|
|
|
|
|
|
|
def request_put( path, data, initheader = nil, &block )
|
|
|
|
request Put.new(path,initheader), data, &block
|
|
|
|
end
|
|
|
|
|
|
|
|
alias get2 request_get
|
|
|
|
alias head2 request_head
|
|
|
|
alias post2 request_post
|
|
|
|
alias put2 request_put
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def send_request( name, path, body = nil, header = nil )
|
|
|
|
r = HTTPGenericRequest.new( name, (body ? true : false), true,
|
|
|
|
path, header )
|
|
|
|
request r, body
|
|
|
|
end
|
2000-04-18 05:39:02 -04:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
|
2001-12-07 05:04:25 -05:00
|
|
|
def request( req, body = nil, &block )
|
|
|
|
unless active? then
|
|
|
|
start {
|
2001-12-30 14:18:45 -05:00
|
|
|
req['connection'] = 'close'
|
|
|
|
return request(req, body, &block)
|
2001-12-07 05:04:25 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
begin_transport req
|
|
|
|
req.__send__(:exec,
|
|
|
|
@socket, @curr_http_version, edit_path(req.path), body)
|
|
|
|
begin
|
|
|
|
res = HTTPResponse.read_new(@socket, req.response_body_permitted?)
|
|
|
|
end while HTTPContinue === res
|
|
|
|
yield res if block_given?
|
|
|
|
end_transport req, res
|
|
|
|
|
|
|
|
res
|
2000-04-18 05:39:02 -04:00
|
|
|
end
|
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
private
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def begin_transport( req )
|
2001-12-07 05:04:25 -05:00
|
|
|
if @socket.closed? then
|
2001-12-30 14:18:45 -05:00
|
|
|
reconn_socket
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
2001-02-23 23:53:50 -05:00
|
|
|
if not req.body_exist? or @seems_1_0_server then
|
2001-02-06 06:14:51 -05:00
|
|
|
req['connection'] = 'close'
|
|
|
|
end
|
2001-07-03 15:03:16 -04:00
|
|
|
req['host'] = addr_port()
|
2002-02-19 07:33:52 -05:00
|
|
|
end
|
2000-03-27 10:52:27 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def end_transport( req, res )
|
|
|
|
res.__send__ :terminate
|
|
|
|
@curr_http_version = res.http_version
|
2000-09-21 02:58:01 -04:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
if not res.body then
|
2001-02-23 23:53:50 -05:00
|
|
|
@socket.close
|
2002-02-19 07:33:52 -05:00
|
|
|
elsif keep_alive? req, res then
|
2001-02-23 23:53:50 -05:00
|
|
|
D 'Conn keep-alive'
|
|
|
|
if @socket.closed? then # (only) read stream had been closed
|
|
|
|
D 'Conn (but seems 1.0 server)'
|
2001-02-06 06:14:51 -05:00
|
|
|
@seems_1_0_server = true
|
|
|
|
@socket.close
|
|
|
|
end
|
|
|
|
else
|
2001-02-23 23:53:50 -05:00
|
|
|
D 'Conn close'
|
2000-03-05 05:25:53 -05:00
|
|
|
@socket.close
|
|
|
|
end
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
def keep_alive?( req, res )
|
|
|
|
/close/i === req['connection'].to_s and return false
|
|
|
|
@seems_1_0_server and return false
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
/keep-alive/i === res['connection'].to_s and return true
|
|
|
|
/close/i === res['connection'].to_s and return false
|
|
|
|
/keep-alive/i === res['proxy-connection'].to_s and return true
|
|
|
|
/close/i === res['proxy-connection'].to_s and return false
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
@curr_http_version == '1.1' and return true
|
1999-12-29 06:14:04 -05:00
|
|
|
false
|
|
|
|
end
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
|
|
|
|
#
|
|
|
|
# utils
|
|
|
|
#
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def addr_port
|
2002-02-22 07:10:58 -05:00
|
|
|
address + (port == HTTP.default_port ? '' : ":#{port}")
|
2001-02-23 23:53:50 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def D( msg )
|
|
|
|
if @dout then
|
|
|
|
@dout << msg
|
|
|
|
@dout << "\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2000-12-24 15:01:44 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
HTTPSession = HTTP
|
aamine
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb: 1.1.26.
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb:
add module Net::NetPrivate and its inner classes
{Read,Write}Adapter, Command, Socket,
SMTPCommand, POP3Command, APOPCommand, HTTPCommand
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@826 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2000-07-12 02:04:40 -04:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
###
|
2001-02-23 23:53:50 -05:00
|
|
|
### header
|
2001-01-13 14:07:15 -05:00
|
|
|
###
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
module HTTPHeader
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def size
|
|
|
|
@header.size
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
alias length size
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def []( key )
|
2001-02-22 18:23:57 -05:00
|
|
|
@header[ key.downcase ]
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2000-10-11 01:27:56 -04:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def []=( key, val )
|
2001-02-22 18:23:57 -05:00
|
|
|
@header[ key.downcase ] = val
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2000-11-07 06:27:16 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
def each_header( &block )
|
2001-02-22 18:23:57 -05:00
|
|
|
@header.each( &block )
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2000-11-07 06:27:16 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
alias each each_header
|
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def each_key( &block )
|
|
|
|
@header.each_key( &block )
|
1999-12-17 10:00:13 -05:00
|
|
|
end
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def each_value( &block )
|
|
|
|
@header.each_value( &block )
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def delete( key )
|
|
|
|
@header.delete key.downcase
|
2000-02-21 10:25:37 -05:00
|
|
|
end
|
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def key?( key )
|
|
|
|
@header.key? key.downcase
|
2000-02-21 10:25:37 -05:00
|
|
|
end
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def to_hash
|
|
|
|
@header.dup
|
|
|
|
end
|
2001-02-07 02:23:09 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def canonical_each
|
|
|
|
@header.each do |k,v|
|
|
|
|
yield canonical(k), v
|
|
|
|
end
|
|
|
|
end
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def canonical( k )
|
|
|
|
k.split('-').collect {|i| i.capitalize }.join('-')
|
2000-03-27 10:52:27 -05:00
|
|
|
end
|
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def range
|
2002-02-22 07:10:58 -05:00
|
|
|
s = @header['range'] or return nil
|
|
|
|
s.split(',').collect {|spec|
|
|
|
|
m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
|
|
|
|
raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
|
|
|
|
d1 = m[1].to_i
|
|
|
|
d2 = m[2].to_i
|
|
|
|
if m[1] and m[2] then d1..d2
|
|
|
|
elsif m[1] then d1..-1
|
|
|
|
elsif m[2] then -d2..-1
|
|
|
|
else
|
|
|
|
raise HTTPHeaderSyntaxError, 'range is not specified'
|
|
|
|
end
|
|
|
|
}
|
2001-02-22 18:23:57 -05:00
|
|
|
end
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
def range=( r, fin = nil )
|
2001-12-30 14:18:45 -05:00
|
|
|
r = (r ... r + fin) if fin
|
2001-02-23 23:53:50 -05:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
case r
|
|
|
|
when Numeric
|
|
|
|
s = r > 0 ? "0-#{r - 1}" : "-#{-r}"
|
|
|
|
when Range
|
|
|
|
first = r.first
|
|
|
|
last = r.last
|
|
|
|
if r.exclude_end? then
|
|
|
|
last -= 1
|
|
|
|
end
|
|
|
|
|
|
|
|
if last == -1 then
|
|
|
|
s = first > 0 ? "#{first}-" : "-#{-first}"
|
|
|
|
else
|
|
|
|
first >= 0 or raise HTTPHeaderSyntaxError, 'range.first is negative'
|
|
|
|
last > 0 or raise HTTPHeaderSyntaxError, 'range.last is negative'
|
|
|
|
first < last or raise HTTPHeaderSyntaxError, 'must be .first < .last'
|
|
|
|
s = "#{first}-#{last}"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
raise TypeError, 'Range/Integer is required'
|
|
|
|
end
|
|
|
|
|
|
|
|
@header['range'] = "bytes=#{s}"
|
|
|
|
r
|
|
|
|
end
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
alias set_range range=
|
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
def content_length
|
|
|
|
s = @header['content-length']
|
|
|
|
s or return nil
|
|
|
|
|
|
|
|
m = /\d+/.match(s)
|
|
|
|
m or raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
|
|
|
|
m[0].to_i
|
|
|
|
end
|
|
|
|
|
|
|
|
def chunked?
|
|
|
|
s = @header['transfer-encoding']
|
2001-02-23 23:53:50 -05:00
|
|
|
(s and /(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i === s) ? true : false
|
2001-02-22 18:23:57 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def content_range
|
|
|
|
s = @header['content-range']
|
|
|
|
s or return nil
|
|
|
|
|
|
|
|
m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>i.match( s )
|
|
|
|
m or raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
|
|
|
|
|
|
|
|
m[1].to_i .. m[2].to_i + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def range_length
|
|
|
|
r = content_range
|
|
|
|
r and r.length
|
|
|
|
end
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
def basic_auth( acc, pass )
|
2001-03-13 00:48:58 -05:00
|
|
|
@header['authorization'] = 'Basic ' + ["#{acc}:#{pass}"].pack('m').strip
|
2001-02-23 23:53:50 -05:00
|
|
|
end
|
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
end
|
|
|
|
|
2001-02-23 23:53:50 -05:00
|
|
|
|
|
|
|
###
|
|
|
|
### request
|
|
|
|
###
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
class HTTPGenericRequest
|
2001-02-22 18:23:57 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
include HTTPHeader
|
2001-02-22 18:23:57 -05:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def initialize( m, reqbody, resbody, path, initheader = nil )
|
2001-07-03 14:13:13 -04:00
|
|
|
@method = m
|
|
|
|
@request_has_body = reqbody
|
|
|
|
@response_has_body = resbody
|
2001-02-22 18:23:57 -05:00
|
|
|
@path = path
|
2001-07-03 14:13:13 -04:00
|
|
|
|
2001-02-22 18:23:57 -05:00
|
|
|
@header = tmp = {}
|
2001-12-30 14:18:45 -05:00
|
|
|
return unless initheader
|
|
|
|
initheader.each do |k,v|
|
2001-02-22 18:23:57 -05:00
|
|
|
key = k.downcase
|
|
|
|
if tmp.key? key then
|
|
|
|
$stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
|
|
|
|
end
|
|
|
|
tmp[ key ] = v.strip
|
|
|
|
end
|
2001-02-23 23:53:50 -05:00
|
|
|
tmp['accept'] ||= '*/*'
|
2001-02-22 18:23:57 -05:00
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
attr_reader :method
|
2001-02-22 18:23:57 -05:00
|
|
|
attr_reader :path
|
|
|
|
|
|
|
|
def inspect
|
2002-02-19 07:33:52 -05:00
|
|
|
"\#<#{self.type} #{@method}>"
|
2001-02-22 18:23:57 -05:00
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
def request_body_permitted?
|
|
|
|
@request_has_body
|
|
|
|
end
|
|
|
|
|
|
|
|
def response_body_permitted?
|
|
|
|
@response_has_body
|
2001-02-23 23:53:50 -05:00
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
alias body_exist? response_body_permitted?
|
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
#
|
|
|
|
# write
|
|
|
|
#
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def exec( sock, ver, path, body )
|
2001-07-03 14:13:13 -04:00
|
|
|
if body then
|
|
|
|
check_body_premitted
|
2002-02-19 07:33:52 -05:00
|
|
|
send_request_with_body sock, ver, path, body
|
2001-07-03 14:13:13 -04:00
|
|
|
else
|
2002-02-19 07:33:52 -05:00
|
|
|
request sock, ver, path
|
2000-11-07 06:27:16 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
def check_body_premitted
|
|
|
|
request_body_permitted? or
|
|
|
|
raise ArgumentError, 'HTTP request body is not premitted'
|
2000-11-07 06:27:16 -05:00
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def send_request_with_body( sock, ver, path, body )
|
2001-01-13 14:07:15 -05:00
|
|
|
if block_given? then
|
|
|
|
ac = Accumulator.new
|
2001-02-06 06:14:51 -05:00
|
|
|
yield ac # must be yield, DO NOT USE block.call
|
2001-01-13 14:07:15 -05:00
|
|
|
data = ac.terminate
|
|
|
|
else
|
2001-07-03 14:13:13 -04:00
|
|
|
data = body
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2001-02-23 23:53:50 -05:00
|
|
|
@header['content-length'] = data.size.to_s
|
|
|
|
@header.delete 'transfer-encoding'
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
unless @header['content-type'] then
|
|
|
|
$stderr.puts 'Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
|
|
|
|
@header['content-type'] = 'application/x-www-form-urlencoded'
|
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
request sock, ver, path
|
|
|
|
sock.write data
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
def request( sock, ver, path )
|
|
|
|
sock.writeline sprintf('%s %s HTTP/%s', @method, path, ver)
|
|
|
|
canonical_each do |k,v|
|
|
|
|
sock.writeline k + ': ' + v
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2001-07-03 14:13:13 -04:00
|
|
|
sock.writeline ''
|
|
|
|
end
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
class HTTPRequest < HTTPGenericRequest
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
def initialize( path, initheader = nil )
|
2001-07-03 14:13:13 -04:00
|
|
|
super type::METHOD,
|
|
|
|
type::REQUEST_HAS_BODY,
|
|
|
|
type::RESPONSE_HAS_BODY,
|
2001-12-30 14:18:45 -05:00
|
|
|
path, initheader
|
2001-07-03 14:13:13 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2001-02-07 02:23:09 -05:00
|
|
|
class HTTP
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
class Get < HTTPRequest
|
2001-02-07 02:23:09 -05:00
|
|
|
METHOD = 'GET'
|
2001-07-03 14:13:13 -04:00
|
|
|
REQUEST_HAS_BODY = false
|
|
|
|
RESPONSE_HAS_BODY = true
|
2001-02-07 02:23:09 -05:00
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
class Head < HTTPRequest
|
2001-02-07 02:23:09 -05:00
|
|
|
METHOD = 'HEAD'
|
2001-07-03 14:13:13 -04:00
|
|
|
REQUEST_HAS_BODY = false
|
|
|
|
RESPONSE_HAS_BODY = false
|
2001-02-07 02:23:09 -05:00
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
class Post < HTTPRequest
|
2001-02-07 02:23:09 -05:00
|
|
|
METHOD = 'POST'
|
2001-07-03 14:13:13 -04:00
|
|
|
REQUEST_HAS_BODY = true
|
|
|
|
RESPONSE_HAS_BODY = true
|
2001-02-07 02:23:09 -05:00
|
|
|
end
|
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
class Put < HTTPRequest
|
2001-02-07 02:23:09 -05:00
|
|
|
METHOD = 'PUT'
|
2001-07-03 14:13:13 -04:00
|
|
|
REQUEST_HAS_BODY = true
|
|
|
|
RESPONSE_HAS_BODY = true
|
2001-02-07 02:23:09 -05:00
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
2001-02-07 12:17:51 -05:00
|
|
|
end
|
2001-02-06 06:14:51 -05:00
|
|
|
|
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
###
|
|
|
|
### response
|
|
|
|
###
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
class HTTPResponse
|
|
|
|
# predefine HTTPResponse class to allow inheritance
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def self.body_permitted?
|
|
|
|
self::HAS_BODY
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.exception_type
|
|
|
|
self::EXCEPTION_TYPE
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
class HTTPUnknownResponse < HTTPResponse
|
|
|
|
HAS_BODY = true
|
|
|
|
EXCEPTION_TYPE = ProtocolError
|
|
|
|
end
|
|
|
|
class HTTPInformation < HTTPResponse
|
|
|
|
HAS_BODY = false
|
|
|
|
EXCEPTION_TYPE = ProtocolError
|
|
|
|
end
|
|
|
|
class HTTPSuccess < HTTPResponse
|
|
|
|
HAS_BODY = true
|
|
|
|
EXCEPTION_TYPE = ProtocolError
|
|
|
|
end
|
|
|
|
class HTTPRedirection < HTTPResponse
|
|
|
|
HAS_BODY = true
|
|
|
|
EXCEPTION_TYPE = ProtoRetriableError
|
|
|
|
end
|
|
|
|
class HTTPClientError < HTTPResponse
|
|
|
|
HAS_BODY = true
|
|
|
|
EXCEPTION_TYPE = ProtoFatalError
|
|
|
|
end
|
|
|
|
class HTTPServerError < HTTPResponse
|
|
|
|
HAS_BODY = true
|
|
|
|
EXCEPTION_TYPE = ProtoServerError
|
|
|
|
end
|
|
|
|
class HTTPContinue < HTTPInformation
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPSwitchProtocol < HTTPInformation
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPOK < HTTPSuccess
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPCreated < HTTPSuccess
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPAccepted < HTTPSuccess
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNonAuthoritativeInformation < HTTPSuccess
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNoContent < HTTPSuccess
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPResetContent < HTTPSuccess
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPPartialContent < HTTPSuccess
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPMultipleChoice < HTTPRedirection
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPMovedPermanently < HTTPRedirection
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPMovedTemporarily < HTTPRedirection
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNotModified < HTTPRedirection
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPUseProxy < HTTPRedirection
|
|
|
|
HAS_BODY = false
|
|
|
|
end
|
|
|
|
class HTTPBadRequest < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPUnauthorized < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPPaymentRequired < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPForbidden < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNotFound < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPMethodNotAllowed < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNotAcceptable < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPProxyAuthenticationRequired < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPRequestTimeOut < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPConflict < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPGone < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPLengthRequired < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPPreconditionFailed < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPRequestEntityTooLarge < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPRequestURITooLarge < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPUnsupportedMediaType < HTTPClientError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPInternalServerError < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPNotImplemented < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPBadGateway < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPServiceUnavailable < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPGatewayTimeOut < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
class HTTPVersionNotSupported < HTTPServerError
|
|
|
|
HAS_BODY = true
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
class HTTPResponse # redefine
|
2001-02-22 18:23:57 -05:00
|
|
|
|
2001-02-07 12:17:51 -05:00
|
|
|
CODE_CLASS_TO_OBJ = {
|
2002-02-19 07:33:52 -05:00
|
|
|
'1' => HTTPInformation,
|
|
|
|
'2' => HTTPSuccess,
|
|
|
|
'3' => HTTPRedirection,
|
|
|
|
'4' => HTTPClientError,
|
|
|
|
'5' => HTTPServerError
|
2000-10-11 01:27:56 -04:00
|
|
|
}
|
2001-02-07 12:17:51 -05:00
|
|
|
CODE_TO_OBJ = {
|
2002-02-19 07:33:52 -05:00
|
|
|
'100' => HTTPContinue,
|
2000-10-12 05:54:32 -04:00
|
|
|
'101' => HTTPSwitchProtocol,
|
2000-10-11 01:27:56 -04:00
|
|
|
|
|
|
|
'200' => HTTPOK,
|
|
|
|
'201' => HTTPCreated,
|
|
|
|
'202' => HTTPAccepted,
|
|
|
|
'203' => HTTPNonAuthoritativeInformation,
|
|
|
|
'204' => HTTPNoContent,
|
|
|
|
'205' => HTTPResetContent,
|
|
|
|
'206' => HTTPPartialContent,
|
|
|
|
|
|
|
|
'300' => HTTPMultipleChoice,
|
|
|
|
'301' => HTTPMovedPermanently,
|
|
|
|
'302' => HTTPMovedTemporarily,
|
|
|
|
'304' => HTTPNotModified,
|
|
|
|
'305' => HTTPUseProxy,
|
|
|
|
|
|
|
|
'400' => HTTPBadRequest,
|
|
|
|
'401' => HTTPUnauthorized,
|
|
|
|
'402' => HTTPPaymentRequired,
|
|
|
|
'403' => HTTPForbidden,
|
|
|
|
'404' => HTTPNotFound,
|
|
|
|
'405' => HTTPMethodNotAllowed,
|
|
|
|
'406' => HTTPNotAcceptable,
|
|
|
|
'407' => HTTPProxyAuthenticationRequired,
|
|
|
|
'408' => HTTPRequestTimeOut,
|
|
|
|
'409' => HTTPConflict,
|
|
|
|
'410' => HTTPGone,
|
2002-02-19 07:33:52 -05:00
|
|
|
'411' => HTTPLengthRequired,
|
2000-10-11 01:27:56 -04:00
|
|
|
'412' => HTTPPreconditionFailed,
|
|
|
|
'413' => HTTPRequestEntityTooLarge,
|
|
|
|
'414' => HTTPRequestURITooLarge,
|
|
|
|
'415' => HTTPUnsupportedMediaType,
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
'501' => HTTPInternalServerError,
|
2000-10-11 01:27:56 -04:00
|
|
|
'501' => HTTPNotImplemented,
|
|
|
|
'502' => HTTPBadGateway,
|
|
|
|
'503' => HTTPServiceUnavailable,
|
|
|
|
'504' => HTTPGatewayTimeOut,
|
|
|
|
'505' => HTTPVersionNotSupported
|
2000-03-27 10:52:27 -05:00
|
|
|
}
|
|
|
|
|
2001-07-03 14:13:13 -04:00
|
|
|
|
|
|
|
class << self
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def read_new( sock, hasbody )
|
2002-02-22 07:10:58 -05:00
|
|
|
httpv, code, msg = read_status_line(sock)
|
2002-02-19 07:33:52 -05:00
|
|
|
res = response_class(code).new( httpv, code, msg, sock, hasbody )
|
2002-02-22 07:10:58 -05:00
|
|
|
each_response_header(sock) do |k,v|
|
|
|
|
if res.key? k then
|
|
|
|
res[k] << ', ' << v
|
|
|
|
else
|
|
|
|
res[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
res
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2002-02-22 07:10:58 -05:00
|
|
|
def read_status_line( sock )
|
2002-02-19 07:33:52 -05:00
|
|
|
str = sock.readline
|
|
|
|
m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
|
|
|
|
raise HTTPBadResponse, "wrong status line: #{str.dump}"
|
2002-02-22 07:10:58 -05:00
|
|
|
m.to_a[1,3]
|
2002-02-19 07:33:52 -05:00
|
|
|
end
|
2001-07-03 14:13:13 -04:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def response_class( code )
|
|
|
|
CODE_TO_OBJ[code] or
|
|
|
|
CODE_CLASS_TO_OBJ[code[0,1]] or
|
|
|
|
HTTPUnknownResponse
|
|
|
|
end
|
|
|
|
|
2002-02-22 07:54:11 -05:00
|
|
|
def each_response_header( sock )
|
2001-07-03 14:13:13 -04:00
|
|
|
while true do
|
|
|
|
line = sock.readuntil( "\n", true ) # ignore EOF
|
2001-12-30 14:18:45 -05:00
|
|
|
line.sub!( /\s+\z/, '' ) # don't use chop!
|
2001-07-03 14:13:13 -04:00
|
|
|
break if line.empty?
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
m = /\A([^:]+):\s*/.match(line) or
|
|
|
|
raise HTTPBadResponse, 'wrong header line format'
|
2002-02-22 07:10:58 -05:00
|
|
|
yield m[1], m.post_match
|
2001-07-03 14:13:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
include HTTPHeader
|
|
|
|
|
|
|
|
def initialize( httpv, code, msg, sock, hasbody )
|
|
|
|
@http_version = httpv
|
|
|
|
@code = code
|
|
|
|
@message = msg
|
|
|
|
@socket = sock
|
|
|
|
@body_exist = hasbody
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
@header = {}
|
|
|
|
@body = nil
|
|
|
|
@read = false
|
|
|
|
end
|
|
|
|
|
2001-02-07 12:17:51 -05:00
|
|
|
attr_reader :http_version
|
2002-02-19 07:33:52 -05:00
|
|
|
attr_reader :code
|
|
|
|
attr_reader :message
|
|
|
|
alias msg message
|
2001-02-07 12:17:51 -05:00
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def inspect
|
2002-02-19 07:33:52 -05:00
|
|
|
"#<#{type} #{@code} readbody=#{@read}>"
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
#
|
|
|
|
# response <-> exception relationship
|
|
|
|
#
|
|
|
|
|
|
|
|
def code_type
|
|
|
|
self.type
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
def error!
|
|
|
|
raise error_type.new(@code + ' ' + @message.dump, self)
|
|
|
|
end
|
|
|
|
|
|
|
|
def error_type
|
|
|
|
type::EXCEPTION_TYPE
|
|
|
|
end
|
|
|
|
|
|
|
|
def value
|
|
|
|
HTTPSuccess === self or error!
|
|
|
|
end
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
#
|
2001-01-13 14:07:15 -05:00
|
|
|
# header (for backward compatibility)
|
2001-02-06 06:14:51 -05:00
|
|
|
#
|
2000-12-24 14:39:15 -05:00
|
|
|
|
2001-03-08 03:39:40 -05:00
|
|
|
def response
|
2001-01-13 14:07:15 -05:00
|
|
|
self
|
|
|
|
end
|
2000-12-24 14:39:15 -05:00
|
|
|
|
2001-03-08 03:39:40 -05:00
|
|
|
alias header response
|
|
|
|
alias read_header response
|
2001-01-13 14:07:15 -05:00
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
#
|
2001-01-13 14:07:15 -05:00
|
|
|
# body
|
2001-02-06 06:14:51 -05:00
|
|
|
#
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
def read_body( dest = nil, &block )
|
2001-12-30 14:18:45 -05:00
|
|
|
if @read then
|
|
|
|
(dest or block) and
|
|
|
|
raise IOError, "#{type}\#read_body called twice with argument"
|
|
|
|
return @body
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
to = procdest(dest, block)
|
|
|
|
stream_check
|
2002-02-19 07:33:52 -05:00
|
|
|
if @body_exist and self.type.body_permitted? then
|
2001-12-30 14:18:45 -05:00
|
|
|
read_body_0 to
|
|
|
|
@body = to
|
|
|
|
else
|
|
|
|
@body = nil
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
2001-12-30 14:18:45 -05:00
|
|
|
@read = true
|
2001-01-13 14:07:15 -05:00
|
|
|
|
|
|
|
@body
|
|
|
|
end
|
|
|
|
|
|
|
|
alias body read_body
|
|
|
|
alias entity read_body
|
|
|
|
|
2001-02-06 06:14:51 -05:00
|
|
|
private
|
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def terminate
|
|
|
|
read_body
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_body_0( dest )
|
|
|
|
if chunked? then
|
2000-11-16 09:03:20 -05:00
|
|
|
read_chunked dest
|
2000-11-07 06:27:16 -05:00
|
|
|
else
|
2001-01-13 14:07:15 -05:00
|
|
|
clen = content_length
|
2000-11-07 06:27:16 -05:00
|
|
|
if clen then
|
2001-02-06 06:14:51 -05:00
|
|
|
@socket.read clen, dest, true # ignore EOF
|
2000-11-07 06:27:16 -05:00
|
|
|
else
|
2001-01-13 14:07:15 -05:00
|
|
|
clen = range_length
|
2000-11-07 06:27:16 -05:00
|
|
|
if clen then
|
|
|
|
@socket.read clen, dest
|
|
|
|
else
|
2000-11-16 09:03:20 -05:00
|
|
|
@socket.read_all dest
|
2000-11-07 06:27:16 -05:00
|
|
|
end
|
|
|
|
end
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
2000-11-07 06:27:16 -05:00
|
|
|
end
|
|
|
|
|
2000-11-16 09:03:20 -05:00
|
|
|
def read_chunked( dest )
|
1999-12-29 06:14:04 -05:00
|
|
|
len = nil
|
|
|
|
total = 0
|
1999-12-20 15:48:49 -05:00
|
|
|
|
1999-12-29 06:14:04 -05:00
|
|
|
while true do
|
|
|
|
line = @socket.readline
|
2001-12-30 14:18:45 -05:00
|
|
|
m = /[0-9a-fA-F]+/.match(line)
|
2001-02-22 18:23:57 -05:00
|
|
|
m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
2000-03-31 08:02:40 -05:00
|
|
|
len = m[0].hex
|
1999-12-29 06:14:04 -05:00
|
|
|
break if len == 0
|
2001-12-30 14:18:45 -05:00
|
|
|
@socket.read len, dest; total += len
|
1999-12-29 06:14:04 -05:00
|
|
|
@socket.read 2 # \r\n
|
1999-12-20 15:48:49 -05:00
|
|
|
end
|
2000-04-14 06:41:35 -04:00
|
|
|
until @socket.readline.empty? do
|
2002-02-19 07:33:52 -05:00
|
|
|
# none
|
1999-12-29 06:14:04 -05:00
|
|
|
end
|
2000-03-26 03:48:15 -05:00
|
|
|
end
|
|
|
|
|
2001-01-13 14:07:15 -05:00
|
|
|
def stream_check
|
2001-02-23 23:53:50 -05:00
|
|
|
@socket.closed? and raise IOError, 'try to read body out of block'
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def procdest( dest, block )
|
2002-02-19 07:33:52 -05:00
|
|
|
(dest and block) and
|
|
|
|
raise ArgumentError, 'both of arg and block are given for HTTP method'
|
2001-01-13 14:07:15 -05:00
|
|
|
if block then
|
2002-02-19 07:33:52 -05:00
|
|
|
ReadAdapter.new(block)
|
2001-01-13 14:07:15 -05:00
|
|
|
else
|
2001-02-17 15:40:50 -05:00
|
|
|
dest || ''
|
2001-01-13 14:07:15 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
1999-12-20 15:48:49 -05:00
|
|
|
end
|
|
|
|
|
aamine
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb: 1.1.26.
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb:
add module Net::NetPrivate and its inner classes
{Read,Write}Adapter, Command, Socket,
SMTPCommand, POP3Command, APOPCommand, HTTPCommand
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@826 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2000-07-12 02:04:40 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
|
2002-02-19 07:33:52 -05:00
|
|
|
# for backward compatibility
|
2001-12-30 14:18:45 -05:00
|
|
|
|
2001-12-13 14:15:21 -05:00
|
|
|
module NetPrivate
|
|
|
|
HTTPResponse = ::Net::HTTPResponse
|
|
|
|
HTTPGenericRequest = ::Net::HTTPGenericRequest
|
|
|
|
HTTPRequest = ::Net::HTTPRequest
|
|
|
|
HTTPHeader = ::Net::HTTPHeader
|
|
|
|
end
|
2002-02-19 07:33:52 -05:00
|
|
|
HTTPInformationCode = HTTPInformation
|
|
|
|
HTTPSuccessCode = HTTPSuccess
|
|
|
|
HTTPRedirectionCode = HTTPRedirection
|
|
|
|
HTTPRetriableCode = HTTPRedirection
|
|
|
|
HTTPClientErrorCode = HTTPClientError
|
|
|
|
HTTPFatalErrorCode = HTTPClientError
|
|
|
|
HTTPServerErrorCode = HTTPServerError
|
2001-12-13 14:15:21 -05:00
|
|
|
HTTPResponceReceiver = HTTPResponse
|
2001-06-26 19:49:21 -04:00
|
|
|
|
1999-12-17 10:00:13 -05:00
|
|
|
end # module Net
|