summaryrefslogtreecommitdiff
path: root/lib/net/http.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net/http.rb')
-rw-r--r--lib/net/http.rb492
1 files changed, 236 insertions, 256 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 5b387f3a7e..8410497437 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -14,7 +14,7 @@
(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html).
-= class HTTP
+= class Net::HTTP
== Class Methods
@@ -49,7 +49,7 @@
HTTP default port (80).
-== Methods
+== Instance Methods
: start
: start {|http| .... }
@@ -100,46 +100,31 @@
If called with block, gives a part of entity body string.
-: new_get( path, header = nil ) {|req| .... }
- creates a new GET request object and gives it to the block.
- see also for Get class reference.
-
- # example
- http.new_get( '/~foo/bar.html' ) do |req|
- req['accept'] = 'text/html'
- response = req.dispatch
- p response['Content-Type']
- puts response.read_header
- end
-: new_head( path, header = nil ) {|req| .... }
- creates a new HEAD request object and gives it to the block.
- see also Head class reference.
+: request( request, [src] )
+: request( request, [src] ) {|response| .... }
+ sends REQUEST to (remote) http server. This method also writes
+ string from SRC before it if REQUEST is a post/put request.
+ (giving SRC for get/head request causes ArgumentError.)
-: new_post( path, header = nil ) {|req| .... }
- creates a new POST request object and gives it to the block.
- see also Post class reference.
+ If called with block, gives a HTTP response object to the block.
-= class Get, Head, Post
+= class Net::HTTP::Get, Head, Post
-HTTP request class. This class wraps request header and entity path.
-All "key" is case-insensitive.
+HTTP request classes. These classes wraps request header and
+entity path. All "key" is case-insensitive.
== Methods
: self[ key ]
returns header field for "key".
-: dispatch [only Get, Head]
- dispatches request.
- This method returns HTTPResponse object.
+: self[ key ] = val
+ set header to "val".
-: dispatch( data = '' ) [only Post]
-: dispatch {|adapter| .... } [only Post]
- dispatches request. "data" is
-= class HTTPResponse
+= class Net::HTTPResponse
HTTP response class. This class wraps response header and entity.
All "key" is case-insensitive.
@@ -209,13 +194,31 @@ module Net
class HTTP < Protocol
- protocol_param :port, '80'
-
HTTPVersion = '1.1'
+ ###
+ ### connection
+ ###
- def addr_port
- address + (port == HTTP.port ? '' : ":#{port}")
+ protocol_param :port, '80'
+
+
+ def initialize( addr = nil, port = nil )
+ super
+
+ @proxy_address = nil
+ @proxy_port = nil
+
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ end
+
+ private
+
+ def conn_command( sock )
+ end
+
+ def do_finish
end
@@ -223,11 +226,13 @@ module Net
### proxy
###
+ public
+
+
class << self
def Proxy( p_addr, p_port = nil )
- ::Net::NetPrivate::HTTPProxy.create_proxy_class(
- p_addr, p_port || self.port )
+ ProxyMod.create_proxy_class( p_addr, p_port || self.port )
end
alias orig_new new
@@ -274,9 +279,65 @@ module Net
end
- ###
- ### for compatibility
- ###
+ module ProxyMod
+
+ class << self
+
+ def create_proxy_class( p_addr, p_port )
+ klass = Class.new( HTTP )
+ klass.module_eval {
+ include HTTPProxy
+ @proxy_address = p_addr
+ @proxy_port = p_port
+ }
+ def klass.proxy_class?
+ true
+ end
+
+ def klass.proxy_address
+ @proxy_address
+ end
+
+ def klass.proxy_port
+ @proxy_port
+ end
+
+ klass
+ end
+
+ end
+
+ def initialize( addr, port )
+ super
+ @proxy_address = type.proxy_address
+ @proxy_port = type.proxy_port
+ end
+
+ attr_reader :proxy_address, :proxy_port
+
+ alias proxyaddr proxy_address
+ alias proxyport proxy_port
+
+ def proxy?
+ true
+ end
+
+ private
+
+ def conn_socket( addr, port )
+ super @proxy_address, @proxy_port
+ end
+
+ def edit_path( path )
+ 'http://' + addr_port + path
+ end
+
+ end # module ProxyMod
+
+
+ #
+ # for backward compatibility
+ #
@@newimpl = true
@@ -300,23 +361,26 @@ module Net
end
- ###
- ### http operations
- ###
+ #
+ # http operations
+ #
- def self.defrequest( nm, hasdest, hasdata )
+ public
+
+ def self.def_http_method( nm, hasdest, hasdata )
name = nm.id2name.downcase
cname = nm.id2name
lineno = __LINE__ + 2
- src = <<S
+ src = <<" ----"
def #{name}( path, #{hasdata ? 'data,' : ''}
u_header = nil #{hasdest ? ',dest = nil, &block' : ''} )
- resp = #{name}2( path,
- #{hasdata ? 'data,' : ''}
- u_header ) {|resp|
+ resp = nil
+ request(
+ #{cname}.new( path, u_header ) #{hasdata ? ',data' : ''}
+ ) do |resp|
resp.read_body( #{hasdest ? 'dest, &block' : ''} )
- }
+ end
if @newimpl then
resp
else
@@ -326,78 +390,63 @@ module Net
end
def #{name}2( path, #{hasdata ? 'data,' : ''}
- u_header = nil )
- new_#{name}( path, u_header ) do |req|
- resp = req.dispatch#{hasdata ? '(data)' : ''}
- yield resp if block_given?
- end
- end
-
- def new_#{name}( path, u_header = nil, &block )
- common_oper ::Net::NetPrivate::#{cname}, path, u_header, &block
+ u_header = nil, &block )
+ request( #{cname}.new(path, u_header),
+ #{hasdata ? 'data,' : ''} &block )
end
-S
- # puts src
+ ----
+#puts src
module_eval src, __FILE__, lineno
end
+ def_http_method :Get, true, false
+ def_http_method :Head, false, false
+ def_http_method :Post, true, true
+ def_http_method :Put, false, true
- defrequest :Get, true, false
- defrequest :Head, false, false
- defrequest :Post, true, true
- defrequest :Put, false, true
-
-
- private
-
-
- def initialize( addr = nil, port = nil )
- super
- @command = ::Net::NetPrivate::Switch.new
- @curr_http_version = HTTPVersion
- end
-
- def connect( addr = @address, port = @port )
- @socket = type.socket_type.open( addr, port, @pipe )
- end
-
- def disconnect
- if @socket and not @socket.closed? then
- @socket.close
- end
- @socket = nil
+ def request( req, *args )
+ common_oper( req ) {
+ req.__send__( :exec,
+ @socket, @curr_http_version,
+ edit_path(req.path),
+ header_defaults, *args )
+ yield req.response if block_given?
+ }
+ req.response
end
- def do_finish
- end
+ private
- def common_oper( reqc, path, u_header )
- req = nil
- @command.on
+ def common_oper( req )
if not @socket then
start
+ req['connection'] = 'close'
elsif @socket.closed? then
@socket.reopen
end
+ if @seems_1_0_server then
+ req['connection'] = 'close'
+ end
- req = reqc.new( @curr_http_version,
- @socket, inihead,
- edit_path(path), u_header )
- yield req if block_given?
- req.terminate
+ yield req
+ req.response.__send__ :terminate
@curr_http_version = req.http_version
- unless keep_alive? req, req.response then
+ if keep_alive? req, req.response then
+ if @socket.closed? then
+ @seems_1_0_server = true
+ @socket.close
+ end
+ else
@socket.close
end
- @command.off
req.response
end
- def inihead
+ def header_defaults
h = {}
h['Host'] = addr_port
h['Connection'] = 'Keep-Alive'
@@ -427,87 +476,14 @@ S
false
end
- end
-
- HTTPSession = HTTP
-
-
-
- module NetPrivate
-
- class Switch
- def initialize
- @critical = false
- end
-
- def critical?
- @critical
- end
-
- def on
- @critical = true
+ def addr_port
+ address + (port == HTTP.port ? '' : ":#{port}")
end
- def off
- @critical = false
- end
end
- module HTTPProxy
-
- class << self
-
- def create_proxy_class( p_addr, p_port )
- klass = Class.new( HTTP )
- klass.module_eval {
- include HTTPProxy
- @proxy_address = p_addr
- @proxy_port = p_port
- }
- def klass.proxy_class?
- true
- end
-
- def klass.proxy_address
- @proxy_address
- end
-
- def klass.proxy_port
- @proxy_port
- end
-
- klass
- end
-
- end
-
-
- def initialize( addr, port )
- super
- @proxy_address = type.proxy_address
- @proxy_port = type.proxy_port
- end
-
- attr_reader :proxy_address, :proxy_port
-
- alias proxyaddr proxy_address
- alias proxyport proxy_port
-
- def proxy?
- true
- end
-
- def connect( addr = nil, port = nil )
- super @proxy_address, @proxy_port
- end
-
- def edit_path( path )
- 'http://' + addr_port + path
- end
-
- end
+ HTTPSession = HTTP
- end # net private
class Code
@@ -575,8 +551,7 @@ S
- module NetPrivate
-
+ class HTTP
###
### request
@@ -584,15 +559,10 @@ S
class HTTPRequest
- def initialize( httpver, sock, inith, path, uhead )
- @http_version = httpver
- @socket = sock
+ def initialize( path, uhead = nil )
@path = path
- @response = nil
-
- @u_header = inith
+ @u_header = tmp = {}
return unless uhead
- tmp = {}
uhead.each do |k,v|
key = canonical(k)
if tmp.key? key then
@@ -600,13 +570,17 @@ S
end
tmp[ key ] = v.strip
end
- @u_header.update tmp
+
+ @socket = nil
+ @response = nil
+ @http_version = nil
end
- attr_reader :http_version
+ public
attr_reader :path
attr_reader :response
+ attr_reader :http_version
def inspect
"\#<#{type}>"
@@ -640,38 +614,42 @@ S
@u_header.each_value( &block )
end
-
- def terminate
- @response.terminate
- end
-
-
- private
+ private
def canonical( k )
k.split('-').collect {|i| i.capitalize }.join('-')
end
+ #
+ # write
+ #
- # write request & header
+ def exec( sock, ver, path, ihead )
+ ready( sock, ihead ) {|header|
+ request ver, path, header
+ }
+ end
- def do_dispatch
- if @response then
- raise IOError, "#{type}\#dispatch called twice"
- end
- yield
+ def ready( sock, ihead )
+ @response = nil
+ @socket = sock
+ ihead.update @u_header
+ yield ihead
@response = read_response
+ @sock = nil
end
- def request( req )
- @socket.writeline req
- @u_header.each do |n,v|
+ def request( ver, path, header )
+ @socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver)
+ header.each do |n,v|
@socket.writeline n + ': ' + v
end
@socket.writeline ''
end
- # read response & header
+ #
+ # read
+ #
def read_response
resp = rdresp0
@@ -683,13 +661,12 @@ S
resp = get_resline
while true do
- line = @socket.readline
+ line = @socket.readuntil( "\n", true ) # ignore EOF
+ line.sub!( /\s+\z/, '' ) # don't use chop!
break if line.empty?
m = /\A([^:]+):\s*/.match( line )
- unless m then
- raise HTTPBadResponse, 'wrong header line format'
- end
+ m or raise HTTPBadResponse, 'wrong header line format'
nm = m[1]
line = m.post_match
if resp.key? nm then
@@ -704,7 +681,7 @@ S
def get_resline
str = @socket.readline
- m = /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i.match( str )
+ m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/i.match( str )
unless m then
raise HTTPBadResponse, "wrong status line: #{str}"
end
@@ -712,52 +689,32 @@ S
status = m[2]
discrip = m[3]
- HTTPResponse.new( status, discrip, @socket, type::HAS_BODY )
- end
-
- end
-
- class Get < HTTPRequest
-
- HAS_BODY = true
-
- def dispatch
- do_dispatch {
- request sprintf('GET %s HTTP/%s', @path, @http_version)
- }
+ ::Net::NetPrivate::HTTPResponse.new(
+ status, discrip, @socket, type::HAS_BODY )
end
end
- class Head < HTTPRequest
-
- HAS_BODY = false
-
- def dispatch
- do_dispatch {
- request sprintf('HEAD %s HTTP/%s', @path, @http_version)
- }
- end
-
- end
class HTTPRequestWithData < HTTPRequest
+
+ private
- def dispatch( str = nil )
+ def exec( sock, ver, path, ihead, str = nil )
check_arg str, block_given?
if block_given? then
ac = Accumulator.new
- yield ac # must be yield, not block.call
+ yield ac # must be yield, DO NOT USE block.call
data = ac.terminate
else
data = str
end
- do_dispatch {
- @u_header['Content-Length'] = data.size.to_s
- @u_header.delete 'Transfer-Encoding'
- request sprintf('%s %s HTTP/%s', type::METHOD, @path, @http_version)
+ ready( sock, ihead ) {|header|
+ header['Content-Length'] = data.size.to_s
+ header.delete 'Transfer-Encoding'
+ request ver, path, header
@socket.write data
}
end
@@ -773,21 +730,6 @@ S
end
- class Post < HTTPRequestWithData
-
- HAS_BODY = true
-
- METHOD = 'POST'
-
- end
-
- class Put < HTTPRequestWithData
-
- HAS_BODY = true
-
- METHOD = 'PUT'
-
- end
class Accumulator
@@ -810,6 +752,31 @@ S
end
+ class Get < HTTPRequest
+ HAS_BODY = true
+ METHOD = 'GET'
+ end
+
+ class Head < HTTPRequest
+ HAS_BODY = false
+ METHOD = 'HEAD'
+ end
+
+ class Post < HTTPRequestWithData
+ HAS_BODY = true
+ METHOD = 'POST'
+ end
+
+ class Put < HTTPRequestWithData
+ HAS_BODY = true
+ METHOD = 'PUT'
+ end
+
+ end # HTTP::
+
+
+
+ module NetPrivate
###
### response
@@ -926,7 +893,9 @@ S
end
+ #
# header (for backward compatibility)
+ #
def read_header
self
@@ -935,8 +904,9 @@ S
alias header read_header
alias response read_header
-
+ #
# body
+ #
def read_body( dest = nil, &block )
if @read and (dest or block) then
@@ -963,22 +933,20 @@ S
alias entity read_body
- # internal use only
+ private
+
+
def terminate
read_body
end
-
- private
-
-
def read_body_0( dest )
if chunked? then
read_chunked dest
else
clen = content_length
if clen then
- @socket.read clen, dest
+ @socket.read clen, dest, true # ignore EOF
else
clen = range_length
if clen then
@@ -1066,6 +1034,18 @@ S
end
+ class Dummy
+
+ def initialize( *args )
+ end
+
+ def critical?
+ false
+ end
+
+ end
+
+
end # module Net::NetPrivate
end # module Net