* lib/net/protocol.rb: clear read buffer after reopen.
* lib/net/protocol.rb: refactoring.
* lib/net/http.rb: split module HTTPHeader from HTTPResponse.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1209 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 2001-02-22 23:23:57 +00:00
parent d633fc6b5b
commit acce0b7ec4
3 changed files with 229 additions and 148 deletions

View File

@ -1,3 +1,11 @@
Fri Feb 23 08:28:58 2001 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
* lib/net/protocol.rb: clear read buffer after reopen.
* lib/net/protocol.rb: refactoring.
* lib/net/http.rb: split module HTTPHeader from HTTPResponse.
Tue Feb 20 23:45:35 2001 WATANABE Hirofumi <eban@ruby-lang.org> Tue Feb 20 23:45:35 2001 WATANABE Hirofumi <eban@ruby-lang.org>
* process.c: add W* macro if not available. * process.c: add W* macro if not available.

View File

@ -26,8 +26,17 @@
: 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 is equals to
Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block) Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block)
: get( address, path, port = 80 )
gets entity body from path and returns it.
return value is a String.
: get_print( address, path, port = 80 )
gets entity body from path and print it.
return value is an entity body (a String).
: Proxy( address, port ) : Proxy( address, port )
creates a HTTP proxy class. creates a HTTP proxy class.
Arguments are address/port of proxy host. Arguments are address/port of proxy host.
@ -190,6 +199,7 @@ require 'net/protocol'
module Net module Net
class HTTPBadResponse < StandardError; end class HTTPBadResponse < StandardError; end
class HTTPHeaderSyntaxError < StandardError; end
class HTTP < Protocol class HTTP < Protocol
@ -396,7 +406,6 @@ module Net
#{hasdata ? 'data,' : ''} &block ) #{hasdata ? 'data,' : ''} &block )
end end
---- ----
#puts src
module_eval src, __FILE__, lineno module_eval src, __FILE__, lineno
end end
@ -449,9 +458,9 @@ module Net
def header_defaults def header_defaults
h = {} h = {}
h['Host'] = addr_port h['host'] = addr_port
h['Connection'] = 'Keep-Alive' h['connection'] = 'Keep-Alive'
h['Accept'] = '*/*' h['accept'] = '*/*'
h h
end end
@ -481,6 +490,24 @@ module Net
address + (port == HTTP.port ? '' : ":#{port}") address + (port == HTTP.port ? '' : ":#{port}")
end end
#
# utils
#
def self.get( addr, path, port = nil )
req = Get.new( path )
resp = nil
new( addr, port || HTTP.port ).start {|http|
resp = http.request( req )
}
resp.body
end
def self.get_print( addr, path, port = nil )
print get( addr, path, port )
end
end end
HTTPSession = HTTP HTTPSession = HTTP
@ -558,14 +585,147 @@ module Net
net_private { net_private {
module HTTPHeader
def size
@header.size
end
alias length size
def []( key )
@header[ key.downcase ]
end
def []=( key, val )
@header[ key.downcase ] = val
end
def each( &block )
@header.each( &block )
end
def each_key( &block )
@header.each_key( &block )
end
def each_value( &block )
@header.each_value( &block )
end
def delete( key )
@header.delete key.downcase
end
def key?( key )
@header.key? key.downcase
end
def to_hash
@header.dup
end
def canonical_each
@header.each do |k,v|
yield canonical(k), v
end
end
def canonical( k )
k.split('-').collect {|i| i.capitalize }.join('-')
end
def range
s = @header['range']
s or return nil
arr = []
s.split(',').each do |spec|
m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match( spec )
m or raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
d1 = m[1].to_i
d2 = m[2].to_i
if m[1] and m[2] then arr.push d1 .. d2
elsif m[1] then arr.push d1 .. -1
elsif m[2] then arr.push -d2 .. -1
else
raise HTTPHeaderSyntaxError, 'range is not specified'
end
end
return *arr
end
def range=( r )
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
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']
s and /(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i === s
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
end
class HTTPRequest class HTTPRequest
include ::Net::NetPrivate::HTTPHeader
def initialize( path, uhead = nil ) def initialize( path, uhead = nil )
@path = path @path = path
@u_header = tmp = {} @header = tmp = {}
return unless uhead return unless uhead
uhead.each do |k,v| uhead.each do |k,v|
key = canonical(k) key = k.downcase
if tmp.key? key then if tmp.key? key then
$stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
end end
@ -583,41 +743,9 @@ module Net
"\#<#{type}>" "\#<#{type}>"
end end
def []( key )
@u_header[ canonical key ]
end
def []=( key, val )
@u_header[ canonical key ] = val
end
def key?( key )
@u_header.key? canonical(key)
end
def delete( key )
@u_header.delete canonical(key)
end
def each( &block )
@u_header.each( &block )
end
def each_key( &block )
@u_header.each_key( &block )
end
def each_value( &block )
@u_header.each_value( &block )
end
private private
def canonical( k )
k.split('-').collect {|i| i.capitalize }.join('-')
end
# #
# write # write
# #
@ -632,7 +760,7 @@ module Net
def ready( sock, ihead ) def ready( sock, ihead )
@response = nil @response = nil
@socket = sock @socket = sock
ihead.update @u_header ihead.update @header
yield ihead yield ihead
@response = get_response @response = get_response
@sock = nil @sock = nil
@ -641,7 +769,7 @@ module Net
def request( ver, path, header ) def request( ver, path, header )
@socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver) @socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver)
header.each do |n,v| header.each do |n,v|
@socket.writeline n + ': ' + v @socket.writeline canonical(n) + ': ' + v
end end
@socket.writeline '' @socket.writeline ''
end end
@ -682,9 +810,7 @@ module Net
def get_resline def get_resline
str = @socket.readline 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 m or raise HTTPBadResponse, "wrong status line: #{str}"
raise HTTPBadResponse, "wrong status line: #{str}"
end
httpver = m[1] httpver = m[1]
status = m[2] status = m[2]
discrip = m[3] discrip = m[3]
@ -789,6 +915,8 @@ module Net
class HTTPResponse < Response class HTTPResponse < Response
include ::Net::NetPrivate::HTTPHeader
CODE_CLASS_TO_OBJ = { CODE_CLASS_TO_OBJ = {
'1' => HTTPInformationCode, '1' => HTTPInformationCode,
'2' => HTTPSuccessCode, '2' => HTTPSuccessCode,
@ -841,7 +969,6 @@ module Net
'505' => HTTPVersionNotSupported '505' => HTTPVersionNotSupported
} }
def initialize( stat, msg, sock, be, hv ) def initialize( stat, msg, sock, be, hv )
code = CODE_TO_OBJ[stat] || code = CODE_TO_OBJ[stat] ||
CODE_CLASS_TO_OBJ[stat[0,1]] || CODE_CLASS_TO_OBJ[stat[0,1]] ||
@ -862,38 +989,6 @@ module Net
"#<#{type} #{code}>" "#<#{type} #{code}>"
end end
def []( key )
@header[ key.downcase ]
end
def []=( key, val )
@header[ key.downcase ] = val
end
def each( &block )
@header.each( &block )
end
def each_key( &block )
@header.each_key( &block )
end
def each_value( &block )
@header.each_value( &block )
end
def delete( key )
@header.delete key.downcase
end
def key?( key )
@header.key? key.downcase
end
def to_hash
@header.dup
end
def value def value
unless SuccessCode === self then unless SuccessCode === self then
error! self error! self
@ -973,9 +1068,7 @@ module Net
while true do while true do
line = @socket.readline line = @socket.readline
m = /[0-9a-fA-F]+/.match( line ) m = /[0-9a-fA-F]+/.match( line )
unless m then m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
raise HTTPBadResponse, "wrong chunk size line: #{line}"
end
len = m[0].hex len = m[0].hex
break if len == 0 break if len == 0
@socket.read( len, dest ); total += len @socket.read( len, dest ); total += len
@ -986,37 +1079,6 @@ module Net
end end
end end
def content_length
if @header.key? 'content-length' then
m = /\d+/.match( @header['content-length'] )
unless m then
raise HTTPBadResponse, 'wrong Content-Length format'
end
m[0].to_i
else
nil
end
end
def chunked?
tmp = @header['transfer-encoding']
tmp and /\bchunked\b/i === tmp
end
def range_length
s = @header['content-range']
s or return nil
m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>.match( s )
m or raise HTTPBadResponse, 'wrong Content-Range format'
low = m[1].to_i
up = m[2].to_i
return nil if low > up
up - low + 1
end
def stream_check def stream_check
if @socket.closed? then if @socket.closed? then
raise IOError, 'try to read body out of block' raise IOError, 'try to read body out of block'
@ -1025,8 +1087,7 @@ module Net
def procdest( dest, block ) def procdest( dest, block )
if dest and block then if dest and block then
raise ArgumentError, raise ArgumentError, 'both of arg and block are given for HTTP method'
'both of arg and block are given for HTTP method'
end end
if block then if block then
::Net::NetPrivate::ReadAdapter.new block ::Net::NetPrivate::ReadAdapter.new block

View File

@ -490,18 +490,22 @@ module Net
@debugout = dout @debugout = dout
@closed = true @socket = nil
@ipaddr = ''
@sending = '' @sending = ''
@buffer = '' @buffer = ''
timeout( otime ) { connect otime
@socket = TCPsocket.new( addr, port ) D 'opened'
}
@closed = false
@ipaddr = @socket.addr[3]
end end
def connect( otime )
D "opening connection to #{@addr}..."
timeout( otime ) {
@socket = TCPsocket.new( @addr, @port )
}
end
private :connect
attr :pipe, true attr :pipe, true
class << self class << self
@ -509,29 +513,31 @@ module Net
end end
def inspect def inspect
"#<#{type} open=#{!@closed}>" "#<#{type} #{closed? ? 'closed' : 'opened'}>"
end end
def reopen( otime = nil ) def reopen( otime = nil )
unless closed? then D 'reopening...'
close close
@buffer = '' connect otime
end D 'reopened'
timeout( otime ) {
@socket = TCPsocket.new( @addr, @port )
}
@closed = false
end end
attr :socket, true attr :socket, true
def close def close
@socket.close if @socket then
@closed = true @socket.close
D 'closed'
else
D 'close call for already closed socket'
end
@socket = nil
@buffer = ''
end end
def closed? def closed?
@closed not @socket
end end
def address def address
@ -543,7 +549,8 @@ module Net
attr_reader :port attr_reader :port
def ip_address def ip_address
@ipaddr.dup @socket or return ''
@socket.addr[3]
end end
alias ipaddr ip_address alias ipaddr ip_address
@ -560,7 +567,7 @@ module Net
CRLF = "\r\n" CRLF = "\r\n"
def read( len, dest = '', ignerr = false ) def read( len, dest = '', ignerr = false )
D_off "reading #{len} bytes...\n" D_off "reading #{len} bytes..."
rsize = 0 rsize = 0
begin begin
@ -573,12 +580,12 @@ module Net
raise unless igneof raise unless igneof
end end
D_on "read #{len} bytes\n" D_on "read #{len} bytes"
dest dest
end end
def read_all( dest = '' ) def read_all( dest = '' )
D_off "reading all...\n" D_off 'reading all...'
rsize = 0 rsize = 0
begin begin
@ -590,7 +597,7 @@ module Net
; ;
end end
D_on "read #{rsize} bytes\n" D_on "read #{rsize} bytes"
dest dest
end end
@ -617,7 +624,7 @@ module Net
end end
def read_pendstr( dest ) def read_pendstr( dest )
D_off "reading text...\n" D_off 'reading text...'
rsize = 0 rsize = 0
while (str = readuntil("\r\n")) != ".\r\n" do while (str = readuntil("\r\n")) != ".\r\n" do
@ -626,13 +633,13 @@ module Net
dest << str dest << str
end end
D_on "read #{rsize} bytes\n" D_on "read #{rsize} bytes"
dest dest
end end
# private use only (can not handle 'break') # private use only (can not handle 'break')
def read_pendlist def read_pendlist
D_off "reading list...\n" D_off 'reading list...'
str = nil str = nil
i = 0 i = 0
@ -642,7 +649,7 @@ module Net
yield str yield str
end end
D_on "read #{i} items\n" D_on "read #{i} items"
end end
@ -705,7 +712,7 @@ module Net
end end
def write_pendstr( src, block ) def write_pendstr( src, block )
D_off "writing text from #{src.type}\n" D_off "writing text from #{src.type}"
wsize = use_each_crlf_line { wsize = use_each_crlf_line {
if block then if block then
@ -715,7 +722,7 @@ module Net
end end
} }
D_on "wrote #{wsize} bytes text\n" D_on "wrote #{wsize} bytes text"
wsize wsize
end end
@ -762,17 +769,17 @@ module Net
beg = 0 beg = 0
buf = @wbuf buf = @wbuf
while buf.index( /\n|\r\n|\r/, beg ) do while buf.index( /\n|\r\n|\r/, beg ) do
m = $~ m = Regexp.last_match
if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then
# "...\r" : can follow "\n..." # "...\r" : can follow "\n..."
break break
end end
str = buf[ beg, m.begin(0) - beg ] str = buf[ beg ... m.begin(0) ]
str.concat "\r\n" str.concat "\r\n"
yield str yield str
beg = m.end(0) beg = m.end(0)
end end
@wbuf = buf[ beg, buf.size - beg ] @wbuf = buf[ beg ... buf.size ]
end end
end end
@ -836,14 +843,19 @@ module Net
def D_off( msg ) def D_off( msg )
@debugout << msg if @debugout D msg
@savedo, @debugout = @debugout, nil @savedo, @debugout = @debugout, nil
end end
def D_on( msg ) def D_on( msg )
@debugout = @savedo @debugout = @savedo
@savedo = nil D msg
@debugout << msg if @debugout end
def D( msg )
@debugout or return
@debugout << msg
@debugout << "\n"
end end
end end