summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--lib/net/http.rb285
-rw-r--r--lib/net/protocol.rb80
3 files changed, 227 insertions, 146 deletions
diff --git a/ChangeLog b/ChangeLog
index de4cea5..eda1af4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 402987c..75021da 100644
--- a/lib/net/http.rb
+++ b/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,66 +585,167 @@ module Net
net_private {
- class HTTPRequest
+ module HTTPHeader
- def initialize( path, uhead = nil )
- @path = path
- @u_header = tmp = {}
- return unless uhead
- uhead.each do |k,v|
- key = canonical(k)
- if tmp.key? key then
- $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
- end
- tmp[ key ] = v.strip
- end
-
- @socket = nil
- @response = nil
+ def size
+ @header.size
end
- attr_reader :path
- attr_reader :response
-
- def inspect
- "\#<#{type}>"
- end
+ alias length size
def []( key )
- @u_header[ canonical key ]
+ @header[ key.downcase ]
end
def []=( key, val )
- @u_header[ canonical key ] = val
+ @header[ key.downcase ] = val
end
- def key?( key )
- @u_header.key? canonical(key)
+ def each( &block )
+ @header.each( &block )
end
- def delete( key )
- @u_header.delete canonical(key)
+ def each_key( &block )
+ @header.each_key( &block )
end
- def each( &block )
- @u_header.each( &block )
+ def each_value( &block )
+ @header.each_value( &block )
end
- def each_key( &block )
- @u_header.each_key( &block )
+ def delete( key )
+ @header.delete key.downcase
end
- def each_value( &block )
- @u_header.each_value( &block )
+ def key?( key )
+ @header.key? key.downcase
end
+ def to_hash
+ @header.dup
+ end
- private
+ 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
+ @header = tmp = {}
+ return unless uhead
+ uhead.each do |k,v|
+ key = k.downcase
+ if tmp.key? key then
+ $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
+ end
+ tmp[ key ] = v.strip
+ end
+
+ @socket = nil
+ @response = nil
+ end
+
+ attr_reader :path
+ attr_reader :response
+
+ def inspect
+ "\#<#{type}>"
+ end
+
+
+ private
+
#
# 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
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index f936e5c..2ebdcb9 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -490,17 +490,21 @@ module Net
@debugout = dout
- @closed = true
- @ipaddr = ''
+ @socket = nil
@sending = ''
@buffer = ''
+ connect otime
+ D 'opened'
+ end
+
+ def connect( otime )
+ D "opening connection to #{@addr}..."
timeout( otime ) {
- @socket = TCPsocket.new( addr, port )
+ @socket = TCPsocket.new( @addr, @port )
}
- @closed = false
- @ipaddr = @socket.addr[3]
end
+ private :connect
attr :pipe, true
@@ -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