aamine
* 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:
parent
d633fc6b5b
commit
acce0b7ec4
@ -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>
|
||||
|
||||
* process.c: add W* macro if not available.
|
||||
|
285
lib/net/http.rb
285
lib/net/http.rb
@ -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 ) {|http| .... }
|
||||
is equals to
|
||||
|
||||
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 )
|
||||
creates a HTTP proxy class.
|
||||
Arguments are address/port of proxy host.
|
||||
@ -190,6 +199,7 @@ require 'net/protocol'
|
||||
module Net
|
||||
|
||||
class HTTPBadResponse < StandardError; end
|
||||
class HTTPHeaderSyntaxError < StandardError; end
|
||||
|
||||
|
||||
class HTTP < Protocol
|
||||
@ -396,7 +406,6 @@ module Net
|
||||
#{hasdata ? 'data,' : ''} &block )
|
||||
end
|
||||
----
|
||||
#puts src
|
||||
module_eval src, __FILE__, lineno
|
||||
end
|
||||
|
||||
@ -449,9 +458,9 @@ module Net
|
||||
|
||||
def header_defaults
|
||||
h = {}
|
||||
h['Host'] = addr_port
|
||||
h['Connection'] = 'Keep-Alive'
|
||||
h['Accept'] = '*/*'
|
||||
h['host'] = addr_port
|
||||
h['connection'] = 'Keep-Alive'
|
||||
h['accept'] = '*/*'
|
||||
h
|
||||
end
|
||||
|
||||
@ -481,6 +490,24 @@ module Net
|
||||
address + (port == HTTP.port ? '' : ":#{port}")
|
||||
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
|
||||
|
||||
HTTPSession = HTTP
|
||||
@ -558,14 +585,147 @@ module Net
|
||||
|
||||
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
|
||||
|
||||
include ::Net::NetPrivate::HTTPHeader
|
||||
|
||||
def initialize( path, uhead = nil )
|
||||
@path = path
|
||||
@u_header = tmp = {}
|
||||
@header = tmp = {}
|
||||
return unless uhead
|
||||
uhead.each do |k,v|
|
||||
key = canonical(k)
|
||||
key = k.downcase
|
||||
if tmp.key? key then
|
||||
$stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
|
||||
end
|
||||
@ -583,41 +743,9 @@ module Net
|
||||
"\#<#{type}>"
|
||||
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
|
||||
|
||||
def canonical( k )
|
||||
k.split('-').collect {|i| i.capitalize }.join('-')
|
||||
end
|
||||
|
||||
#
|
||||
# write
|
||||
#
|
||||
@ -632,7 +760,7 @@ module Net
|
||||
def ready( sock, ihead )
|
||||
@response = nil
|
||||
@socket = sock
|
||||
ihead.update @u_header
|
||||
ihead.update @header
|
||||
yield ihead
|
||||
@response = get_response
|
||||
@sock = nil
|
||||
@ -641,7 +769,7 @@ module Net
|
||||
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
|
||||
@socket.writeline canonical(n) + ': ' + v
|
||||
end
|
||||
@socket.writeline ''
|
||||
end
|
||||
@ -682,9 +810,7 @@ module Net
|
||||
def get_resline
|
||||
str = @socket.readline
|
||||
m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/i.match( str )
|
||||
unless m then
|
||||
raise HTTPBadResponse, "wrong status line: #{str}"
|
||||
end
|
||||
m or raise HTTPBadResponse, "wrong status line: #{str}"
|
||||
httpver = m[1]
|
||||
status = m[2]
|
||||
discrip = m[3]
|
||||
@ -789,6 +915,8 @@ module Net
|
||||
|
||||
class HTTPResponse < Response
|
||||
|
||||
include ::Net::NetPrivate::HTTPHeader
|
||||
|
||||
CODE_CLASS_TO_OBJ = {
|
||||
'1' => HTTPInformationCode,
|
||||
'2' => HTTPSuccessCode,
|
||||
@ -841,7 +969,6 @@ module Net
|
||||
'505' => HTTPVersionNotSupported
|
||||
}
|
||||
|
||||
|
||||
def initialize( stat, msg, sock, be, hv )
|
||||
code = CODE_TO_OBJ[stat] ||
|
||||
CODE_CLASS_TO_OBJ[stat[0,1]] ||
|
||||
@ -862,38 +989,6 @@ module Net
|
||||
"#<#{type} #{code}>"
|
||||
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
|
||||
unless SuccessCode === self then
|
||||
error! self
|
||||
@ -973,9 +1068,7 @@ module Net
|
||||
while true do
|
||||
line = @socket.readline
|
||||
m = /[0-9a-fA-F]+/.match( line )
|
||||
unless m then
|
||||
raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
||||
end
|
||||
m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
||||
len = m[0].hex
|
||||
break if len == 0
|
||||
@socket.read( len, dest ); total += len
|
||||
@ -986,37 +1079,6 @@ module Net
|
||||
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
|
||||
if @socket.closed? then
|
||||
raise IOError, 'try to read body out of block'
|
||||
@ -1025,8 +1087,7 @@ module Net
|
||||
|
||||
def procdest( dest, block )
|
||||
if dest and block then
|
||||
raise ArgumentError,
|
||||
'both of arg and block are given for HTTP method'
|
||||
raise ArgumentError, 'both of arg and block are given for HTTP method'
|
||||
end
|
||||
if block then
|
||||
::Net::NetPrivate::ReadAdapter.new block
|
||||
|
@ -490,18 +490,22 @@ module Net
|
||||
|
||||
@debugout = dout
|
||||
|
||||
@closed = true
|
||||
@ipaddr = ''
|
||||
@socket = nil
|
||||
@sending = ''
|
||||
@buffer = ''
|
||||
|
||||
timeout( otime ) {
|
||||
@socket = TCPsocket.new( addr, port )
|
||||
}
|
||||
@closed = false
|
||||
@ipaddr = @socket.addr[3]
|
||||
connect otime
|
||||
D 'opened'
|
||||
end
|
||||
|
||||
def connect( otime )
|
||||
D "opening connection to #{@addr}..."
|
||||
timeout( otime ) {
|
||||
@socket = TCPsocket.new( @addr, @port )
|
||||
}
|
||||
end
|
||||
private :connect
|
||||
|
||||
attr :pipe, true
|
||||
|
||||
class << self
|
||||
@ -509,29 +513,31 @@ module Net
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{type} open=#{!@closed}>"
|
||||
"#<#{type} #{closed? ? 'closed' : 'opened'}>"
|
||||
end
|
||||
|
||||
def reopen( otime = nil )
|
||||
unless closed? then
|
||||
close
|
||||
@buffer = ''
|
||||
end
|
||||
timeout( otime ) {
|
||||
@socket = TCPsocket.new( @addr, @port )
|
||||
}
|
||||
@closed = false
|
||||
D 'reopening...'
|
||||
close
|
||||
connect otime
|
||||
D 'reopened'
|
||||
end
|
||||
|
||||
attr :socket, true
|
||||
|
||||
def close
|
||||
@socket.close
|
||||
@closed = true
|
||||
if @socket then
|
||||
@socket.close
|
||||
D 'closed'
|
||||
else
|
||||
D 'close call for already closed socket'
|
||||
end
|
||||
@socket = nil
|
||||
@buffer = ''
|
||||
end
|
||||
|
||||
def closed?
|
||||
@closed
|
||||
not @socket
|
||||
end
|
||||
|
||||
def address
|
||||
@ -543,7 +549,8 @@ module Net
|
||||
attr_reader :port
|
||||
|
||||
def ip_address
|
||||
@ipaddr.dup
|
||||
@socket or return ''
|
||||
@socket.addr[3]
|
||||
end
|
||||
|
||||
alias ipaddr ip_address
|
||||
@ -560,7 +567,7 @@ module Net
|
||||
CRLF = "\r\n"
|
||||
|
||||
def read( len, dest = '', ignerr = false )
|
||||
D_off "reading #{len} bytes...\n"
|
||||
D_off "reading #{len} bytes..."
|
||||
|
||||
rsize = 0
|
||||
begin
|
||||
@ -573,12 +580,12 @@ module Net
|
||||
raise unless igneof
|
||||
end
|
||||
|
||||
D_on "read #{len} bytes\n"
|
||||
D_on "read #{len} bytes"
|
||||
dest
|
||||
end
|
||||
|
||||
def read_all( dest = '' )
|
||||
D_off "reading all...\n"
|
||||
D_off 'reading all...'
|
||||
|
||||
rsize = 0
|
||||
begin
|
||||
@ -590,7 +597,7 @@ module Net
|
||||
;
|
||||
end
|
||||
|
||||
D_on "read #{rsize} bytes\n"
|
||||
D_on "read #{rsize} bytes"
|
||||
dest
|
||||
end
|
||||
|
||||
@ -617,7 +624,7 @@ module Net
|
||||
end
|
||||
|
||||
def read_pendstr( dest )
|
||||
D_off "reading text...\n"
|
||||
D_off 'reading text...'
|
||||
|
||||
rsize = 0
|
||||
while (str = readuntil("\r\n")) != ".\r\n" do
|
||||
@ -626,13 +633,13 @@ module Net
|
||||
dest << str
|
||||
end
|
||||
|
||||
D_on "read #{rsize} bytes\n"
|
||||
D_on "read #{rsize} bytes"
|
||||
dest
|
||||
end
|
||||
|
||||
# private use only (can not handle 'break')
|
||||
def read_pendlist
|
||||
D_off "reading list...\n"
|
||||
D_off 'reading list...'
|
||||
|
||||
str = nil
|
||||
i = 0
|
||||
@ -642,7 +649,7 @@ module Net
|
||||
yield str
|
||||
end
|
||||
|
||||
D_on "read #{i} items\n"
|
||||
D_on "read #{i} items"
|
||||
end
|
||||
|
||||
|
||||
@ -705,7 +712,7 @@ module Net
|
||||
end
|
||||
|
||||
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 {
|
||||
if block then
|
||||
@ -715,7 +722,7 @@ module Net
|
||||
end
|
||||
}
|
||||
|
||||
D_on "wrote #{wsize} bytes text\n"
|
||||
D_on "wrote #{wsize} bytes text"
|
||||
wsize
|
||||
end
|
||||
|
||||
@ -762,17 +769,17 @@ module Net
|
||||
beg = 0
|
||||
buf = @wbuf
|
||||
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
|
||||
# "...\r" : can follow "\n..."
|
||||
break
|
||||
end
|
||||
str = buf[ beg, m.begin(0) - beg ]
|
||||
str = buf[ beg ... m.begin(0) ]
|
||||
str.concat "\r\n"
|
||||
yield str
|
||||
beg = m.end(0)
|
||||
end
|
||||
@wbuf = buf[ beg, buf.size - beg ]
|
||||
@wbuf = buf[ beg ... buf.size ]
|
||||
end
|
||||
end
|
||||
|
||||
@ -836,14 +843,19 @@ module Net
|
||||
|
||||
|
||||
def D_off( msg )
|
||||
@debugout << msg if @debugout
|
||||
D msg
|
||||
@savedo, @debugout = @debugout, nil
|
||||
end
|
||||
|
||||
def D_on( msg )
|
||||
@debugout = @savedo
|
||||
@savedo = nil
|
||||
@debugout << msg if @debugout
|
||||
D msg
|
||||
end
|
||||
|
||||
def D( msg )
|
||||
@debugout or return
|
||||
@debugout << msg
|
||||
@debugout << "\n"
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user