* lib/net/http.rb: can call {old,new}_implementation any times.
* lib/net/http.rb: HTTP#connecting, receive ->
  common_oper, connecting.
* lib/net/http.rb: output warning if u_header includes
  duplicated header.
* lib/net/http.rb: not check Connection:/Proxy-Connection;
  always read until eof.
* lib/net/protocol: detects and catches "break" from block.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1041 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 2000-11-16 14:03:20 +00:00
parent 074203d270
commit 0d11c322d1
3 changed files with 181 additions and 137 deletions

View File

@ -1,3 +1,18 @@
Thu Nov 16 23:06:07 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
* lib/net/http.rb: can call {old,new}_implementation any times.
* lib/net/http.rb: HTTP#connecting, receive ->
common_oper, connecting.
* lib/net/http.rb: output warning if u_header includes
duplicated header.
* lib/net/http.rb: not check Connection:/Proxy-Connection;
always read until eof.
* lib/net/protocol: detects and catches "break" from block.
Thu Nov 16 14:58:00 2000 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp> Thu Nov 16 14:58:00 2000 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
* ext/socket/socket.c (sock_new): duplicates file descriptor * ext/socket/socket.c (sock_new): duplicates file descriptor

View File

@ -20,12 +20,13 @@ You can get it from RAA
: new( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) : new( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil )
creates a new Net::HTTP object. creates a new Net::HTTP object.
if proxy_addr is given, this method is equals to If proxy_addr is given, this method is equals to
Net::HTTP::Proxy(proxy_addr,proxy_port). Net::HTTP::Proxy(proxy_addr,proxy_port).
: start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) : start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil )
: start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... } : start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... }
is equals to Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block) is equals to
Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block)
: Proxy( address, port ) : Proxy( address, port )
creates a HTTP proxy class. creates a HTTP proxy class.
@ -86,15 +87,15 @@ You can get it from RAA
# example # example
begin begin
response, body = http.get( '/index.html' ) response, body = http.get( '/index.html' )
rescue Net::ProtoRetriableError rescue Net::ProtoRetriableError => err
response = $!.data response = err.data
... ...
end end
: head( path, header = nil ) : head( path, header = nil )
get only header from "path" on connecting host. gets only header from "path" on connecting host.
"header" is a Hash like { 'Accept' => '*/*', ... }. "header" is a Hash like { 'Accept' => '*/*', ... }.
This method returns Net::HTTPResponse object. This method returns a Net::HTTPResponse object.
You can http header from this object like: You can http header from this object like:
response['content-length'] #-> '2554' response['content-length'] #-> '2554'
@ -104,13 +105,13 @@ You can get it from RAA
: post( path, data, header = nil, dest = '' ) : post( path, data, header = nil, dest = '' )
: post( path, data, header = nil ) {|str| .... } : post( path, data, header = nil ) {|str| .... }
post "data"(must be String now) to "path". posts "data" (must be String now) to "path".
If body exists, also get entity body. If the body exists, also gets entity body.
It is written to "dest" by using "<<" method. Data is written to "dest" by using "<<" method.
"header" must be a Hash like { 'Accept' => '*/*', ... }. "header" must be a Hash like { 'Accept' => '*/*', ... }.
This method returns Net::HTTPResponse object and "dest". This method returns Net::HTTPResponse object and "dest".
If called with block, gives a part String of entity body. If called with block, gives a part of entity body string.
: get2( path, header = nil ) : get2( path, header = nil )
: get2( path, header = nil ) {|recv| .... } : get2( path, header = nil ) {|recv| .... }
@ -145,7 +146,7 @@ You can get it from RAA
: head2( path, header = nil ) : head2( path, header = nil )
: head2( path, header = nil ) {|recv| .... } : head2( path, header = nil ) {|recv| .... }
send HEAD request for "path". send HEAD request for "path".
"header" must be a Hash like { 'Accept' => '*/*', ... }. "header" must be a Hash like { 'Accept' => 'text/html', ... }.
The difference between "head" method is that The difference between "head" method is that
"head2" does not raise exceptions. "head2" does not raise exceptions.
@ -162,7 +163,7 @@ You can get it from RAA
: post2( path, data, header = nil ) : post2( path, data, header = nil )
: post2( path, data, header = nil ) {|recv| .... } : post2( path, data, header = nil ) {|recv| .... }
post "data"(must be String now) to "path". posts "data" (must be String now) to "path".
"header" must be a Hash like { 'Accept' => '*/*', ... }. "header" must be a Hash like { 'Accept' => '*/*', ... }.
If this method is called with block, one gives If this method is called with block, one gives
a HTTPResponseReceiver object to block. a HTTPResponseReceiver object to block.
@ -200,10 +201,10 @@ All "key" is case-insensitive.
set field value for "key". set field value for "key".
: key?( key ) : key?( key )
true if key is exist true if key exists
: each {|name,value| .... } : each {|name,value| .... }
iterate for each field name and value pair iterates for each field name and value pair
: code : code
HTTP result code string. For example, '302' HTTP result code string. For example, '302'
@ -232,33 +233,33 @@ All "key" is case-insensitive.
= http.rb version 1.2 features = http.rb version 1.2 features
You can use these 1.2 features by calling method You can use 1.2 features by calling HTTP.new_implementation. And
Net::HTTP.new_implementation. Or you want to use 1.1 feature, calling Net::HTTP.old_implementation allows to use 1.1 features.
call Net::HTTP.old_implementation.
Now old_impl is default and if new_impl was called then Net::HTTP # example
changes self into new implementation. In 1.2, new_impl is default HTTP.start {|http1| ...(http1 has 1.1 features)... }
and if old_impl was called then changes self into old implementation.
== Warning!!! HTTP.new_implementation
HTTP.start {|http2| ...(http2 has 1.2 features)... }
You can call new_implementation/old_implementation any times HTTP.old_implementation
but CANNOT call both of them at the same time. HTTP.start {|http3| ...(http3 has 1.1 features)... }
You must use one implementation in one application (process).
== Method == Method (only diff to 1.1)
: get( path, u_header = nil ) : get( path, u_header = nil )
: get( path, u_header = nil ) {|str| .... } : get( path, u_header = nil ) {|str| .... }
get document from "path" and returns HTTPResponse object. gets document from "path".
returns HTTPResponse object.
: head( path, u_header = nil ) : head( path, u_header = nil )
get only document header from "path" and returns HTTPResponse object. gets only document header from "path".
returns HTTPResponse object.
: post( path, data, u_header = nil ) : post( path, data, u_header = nil )
: post( path, data, u_header = nil ) {|str| .... } : post( path, data, u_header = nil ) {|str| .... }
post "data" to "path" entity and get document, posts "data" to "path" entity and gets document.
then returns HTTPResponse object. returns HTTPResponse object.
=end =end
@ -290,7 +291,10 @@ module Net
alias orig_new new alias orig_new new
def new( address = nil, port = nil, p_addr = nil, p_port = nil ) def new( address = nil, port = nil, p_addr = nil, p_port = nil )
(p_addr ? self::Proxy(p_addr, p_port) : self).orig_new( address, port ) c = p_addr ? self::Proxy(p_addr, p_port) : self
i = c.orig_new( address, port )
setvar i
i
end end
def start( address = nil, port = nil, p_addr = nil, p_port = nil, &block ) def start( address = nil, port = nil, p_addr = nil, p_port = nil, &block )
@ -332,47 +336,26 @@ module Net
### 1.2 implementation ### 1.2 implementation
### ###
@new_impl = false @@newimpl = false
class << self #class << self
def new_implementation def self.new_implementation
return if @new_impl @@newimpl = true
@new_impl = true
module_eval %^
undef head
alias head head2
undef get
def get( path, u_header = nil, dest = nil, &block )
get2( path, u_header ) {|f| f.body( dest, &block ) }
end end
undef post def self.old_implementation
@@newimpl = false
def post( path, data, u_header = nil, dest = nil, &block )
post2( path, data, u_header ) {|f| f.body( dest, &block ) }
end end
undef put #private
def put( path, src, u_header = nil ) def self.setvar( obj )
put2( path, src, u_header ) {|f| f.body } f = @@newimpl
obj.instance_eval { @newimpl = f }
end end
^ #end
end
def old_implementation
if @new_impl then
raise RuntimeError,
'http.rb is already switched to new implementation'
end
end
end
### ###
@ -381,42 +364,49 @@ module Net
def get( path, u_header = nil, dest = nil, &block ) def get( path, u_header = nil, dest = nil, &block )
resp = get2( path, u_header ) {|f| f.body( dest, &block ) } resp = get2( path, u_header ) {|f| f.body( dest, &block ) }
if @newimpl then
resp
else
resp.value resp.value
return resp, resp.body return resp, resp.body
end end
end
def get2( path, u_header = nil, &block ) def get2( path, u_header = nil, &block )
connecting( u_header ) {|uh| common_oper( u_header, true, block ) {|uh|
@command.get edit_path(path), uh @command.get edit_path(path), uh
receive true, block
} }
end end
def head( path, u_header = nil ) def head( path, u_header = nil )
resp = head2( path, u_header ) resp = head2( path, u_header )
unless @newimpl then
resp.value resp.value
end
resp resp
end end
def head2( path, u_header = nil, &block ) def head2( path, u_header = nil, &block )
connecting( u_header ) {|uh| common_oper( u_header, false, block ) {|uh|
@command.head edit_path(path), uh @command.head edit_path(path), uh
receive false, block
} }
end end
def post( path, data, u_header = nil, dest = nil, &block ) def post( path, data, u_header = nil, dest = nil, &block )
resp = post2( path, data, u_header ) {|f| f.body( dest, &block ) } resp = post2( path, data, u_header ) {|f| f.body( dest, &block ) }
if @newimpl then
resp
else
resp.value resp.value
return resp, resp.body return resp, resp.body
end end
end
def post2( path, data, u_header = nil, &block ) def post2( path, data, u_header = nil, &block )
connecting( u_header ) {|uh| common_oper( u_header, true, block ) {|uh|
@command.post edit_path(path), uh, data @command.post edit_path(path), uh, data
receive true, block
} }
end end
@ -424,14 +414,17 @@ module Net
# not tested because I could not setup apache (__;;; # not tested because I could not setup apache (__;;;
def put( path, src, u_header = nil ) def put( path, src, u_header = nil )
resp = put2( path, src, u_header ) {|f| f.body } resp = put2( path, src, u_header ) {|f| f.body }
if @newimpl then
resp
else
resp.value resp.value
return resp, resp.body return resp, resp.body
end end
end
def put2( path, src, u_header = nil, &block ) def put2( path, src, u_header = nil, &block )
connecting( u_header ) {|uh| common_oper( u_header, true, block ) {|uh|
@command.put path, uh, src @command.put path, uh, src
receive true, block
} }
end end
@ -439,8 +432,28 @@ module Net
private private
def connecting( u_header ) def common_oper( u_header, body_exist, block )
u_header = procheader( u_header ) u_header = procheader( u_header )
recv = err = nil
connecting( u_header ) {
recv = HTTPResponseReceiver.new( @command, body_exist )
yield u_header
begin
block.call recv if block
rescue Exception => err
;
end
recv.terminate
recv.response
}
raise err if err
recv.response
end
def connecting( u_header )
if not @socket then if not @socket then
u_header['Connection'] = 'close' u_header['Connection'] = 'close'
start start
@ -448,13 +461,11 @@ module Net
@socket.reopen @socket.reopen
end end
resp = yield( u_header ) resp = yield
unless keep_alive? u_header, resp then unless keep_alive? u_header, resp then
@socket.close @socket.close
end end
resp
end end
def keep_alive?( header, resp ) def keep_alive?( header, resp )
@ -487,24 +498,20 @@ module Net
ret[ 'Accept' ] = '*/*' ret[ 'Accept' ] = '*/*'
return ret unless h return ret unless h
tmp = {}
h.each do |k,v| h.each do |k,v|
arr = k.split('-') key = k.split('-').collect {|i| i.capitalize }.join('-')
arr.each {|i| i.capitalize! } if tmp[key] then
ret[ arr.join('-') ] = v $stderr.puts "'#{key}' http header appered twice" if $VERBOSE
end end
tmp[key] = v
end
ret.update tmp
ret ret
end end
def receive( body_exist, block )
recv = HTTPResponseReceiver.new( @command, body_exist )
block.call recv if block
recv.terminate
recv.header
end
# called when connecting # called when connecting
def do_finish def do_finish
unless @socket.closed? then unless @socket.closed? then
@ -600,29 +607,31 @@ module Net
alias header read_header alias header read_header
alias response read_header alias response read_header
def body( dest = nil, &block ) def read_body( dest = nil, &block )
unless @body then unless @body then
self.read_header read_header
to = procdest( dest, block ) to = procdest( dest, block )
stream_check stream_check
if @body_exist and header.code_type.body_exist? then
@command.get_body header, to if @body_exist and @header.code_type.body_exist? then
header.body = @body = to @command.get_body @header, to
@header.body = @body = to
else else
@command.no_body @command.no_body
header.body = nil @header.body = nil
@body = 1 @body = 1
end end
end end
@body == 1 ? nil : @body @body == 1 ? nil : @body
end end
alias entity body alias body read_body
alias entity read_body
def terminate def terminate
header read_header
body read_body
@command = nil @command = nil
end end
@ -955,7 +964,7 @@ module Net
def get_body( resp, dest ) def get_body( resp, dest )
if chunked? resp then if chunked? resp then
read_chunked( dest, resp ) read_chunked dest
else else
clen = content_length( resp ) clen = content_length( resp )
if clen then if clen then
@ -965,15 +974,7 @@ module Net
if clen then if clen then
@socket.read clen, dest @socket.read clen, dest
else else
tmp = resp['connection']
if tmp and /close/i === tmp then
@socket.read_all dest @socket.read_all dest
else
tmp = resp['proxy-connection']
if tmp and /close/i === tmp then
@socket.read_all dest
end
end
end end
end end
end end
@ -987,7 +988,7 @@ module Net
private private
def read_chunked( ret, header ) def read_chunked( dest )
len = nil len = nil
total = 0 total = 0
@ -999,7 +1000,7 @@ module Net
end end
len = m[0].hex len = m[0].hex
break if len == 0 break if len == 0
@socket.read( len, ret ); total += len @socket.read( len, dest ); total += len
@socket.read 2 # \r\n @socket.read 2 # \r\n
end end
until @socket.readline.empty? do until @socket.readline.empty? do
@ -1007,9 +1008,9 @@ module Net
end end
end end
def content_length( header ) def content_length( resp )
if header.key? 'content-length' then if resp.key? 'content-length' then
m = /\d+/.match( header['content-length'] ) m = /\d+/.match( resp['content-length'] )
unless m then unless m then
raise HTTPBadResponse, 'wrong Content-Length format' raise HTTPBadResponse, 'wrong Content-Length format'
end end
@ -1019,14 +1020,14 @@ module Net
end end
end end
def chunked?( header ) def chunked?( resp )
str = header[ 'transfer-encoding' ] tmp = resp['transfer-encoding']
str and /(?:\A|\s+)chunked(?:\s+|\z)/i === str tmp and /(?:\A|\s+)chunked(?:\s+|\z)/i === tmp
end end
def range_length( header ) def range_length( resp )
if header.key? 'content-range' then if resp.key? 'content-range' then
m = %r<bytes\s+(\d+)-(\d+)/\d+>.match( header['content-range'] ) m = %r<bytes\s+(\d+)-(\d+)/\d+>.match( resp['content-range'] )
unless m then unless m then
raise HTTPBadResponse, 'wrong Content-Range format' raise HTTPBadResponse, 'wrong Content-Range format'
end end

View File

@ -314,12 +314,31 @@ module Net
end end
def <<( str ) def <<( str )
@block.call str callblock( str, &@block ) if @block
end
private
def callblock( str )
begin
user_break = true
yield str
user_break = false
rescue Exception
user_break = false
raise
ensure
if user_break then
@block = nil
return # stop break
end
end
end end
end end
class Command class Command
def initialize( sock ) def initialize( sock )
@ -442,6 +461,7 @@ module Net
def address def address
@addr.dup @addr.dup
end end
alias addr address alias addr address
attr_reader :port attr_reader :port
@ -449,11 +469,16 @@ module Net
def ip_address def ip_address
@ipaddr.dup @ipaddr.dup
end end
alias ipaddr ip_address alias ipaddr ip_address
attr_reader :sending attr_reader :sending
###
### read
###
CRLF = "\r\n" CRLF = "\r\n"
def read( len, dest = '' ) def read( len, dest = '' )
@ -514,7 +539,7 @@ module Net
rsize = 0 rsize = 0
while (str = readuntil( "\r\n" )) != ".\r\n" do while (str = readuntil("\r\n")) != ".\r\n" do
rsize += str.size rsize += str.size
str.gsub!( /\A\./, '' ) str.gsub!( /\A\./, '' )
dest << str dest << str
@ -525,20 +550,19 @@ module Net
end end
# private use only (can not handle 'break')
def read_pendlist def read_pendlist
@pipe << "reading list...\n" if @pipe; pipeoff @pipe << "reading list...\n" if @pipe; pipeoff
arr = []
str = nil str = nil
i = 0
while (str = readuntil( "\r\n" )) != ".\r\n" do while (str = readuntil("\r\n")) != ".\r\n" do
str.chop! str.chop!
arr.push str yield str
yield str if block_given? i += 1
end end
@pipe << "read #{arr.size} lines\n" if pipeon @pipe << "read #{i} items\n" if pipeon
arr
end end
@ -561,6 +585,10 @@ module Net
end end
###
### write
###
public public