1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
o  all:  use "critical" to avoid duplicated command dispatch
o  http.rb:  change get2, post2 usage (HTTPWriter)
o  http.rb:  entity reading algorithm is better
o  http.rb:  more reply code (4xx, 5xx)
o  protocol.rb:  arguments of "connect" can be omitted
o  protocol.rb:  "quit" is not template method (now do_quit is removed)
o  protocol.rb:  ReplyCode.error_type was not work: using module_eval


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@657 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 2000-03-27 15:52:27 +00:00
parent 0dcf7498b1
commit 9fd5174ef3
4 changed files with 355 additions and 178 deletions

View file

@ -58,19 +58,18 @@ class HTTPBadResponse < HTTPError; end
: 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" (and get entity body). post "data"(must be String now) to "path".
If body exists, also get entity body.
It is written to "dest" by using "<<" method.
"header" must be a Hash like { 'Accept' => '*/*', ... }. "header" must be a Hash like { 'Accept' => '*/*', ... }.
Data is written to "dest" by using "<<" method.
This method returns response header (Hash) and "dest". This method returns response header (Hash) and "dest".
If called as iterator, give a part String of entity body. If called as iterator, gives a part String of entity body.
ATTENTION: entity body could be empty : get2( path, header = nil ) {|writer| .... }
: get2( path, header = nil )
send GET request for "path". send GET request for "path".
"header" must be a Hash like { 'Accept' => '*/*', ... }. "header" must be a Hash like { 'Accept' => '*/*', ... }.
This method returns response header (Hash). This method gives HTTPWriter object to block.
: get_body( dest = '' ) : get_body( dest = '' )
: get_body {|str| .... } : get_body {|str| .... }
@ -78,7 +77,31 @@ class HTTPBadResponse < HTTPError; end
Data is written in "dest" by using "<<" method. Data is written in "dest" by using "<<" method.
This method returns "dest". This method returns "dest".
If called as iterator, give a part String of entity body. If called as iterator, gives a part String of entity body.
: post2( path, data, header = nil ) {|writer| .... }
post "data"(must be String now) to "path".
"header" must be a Hash like { 'Accept' => '*/*', ... }.
This method gives HTTPWriter object to block.
= class HTTPWriter
== Methods
: header
HTTP header.
: response
ReplyCode object.
: entity( dest = '' )
: body( dest = '' )
entity body.
: entity {|str| ... }
get entity body by using iterator.
If this method is called twice, block is not called.
=end =end
@ -87,20 +110,24 @@ class HTTPBadResponse < HTTPError; end
protocol_param :port, '80' protocol_param :port, '80'
protocol_param :command_type, '::Net::HTTPCommand' protocol_param :command_type, '::Net::HTTPCommand'
def HTTP.procdest( dest, block )
if block then
return ReadAdapter.new( block ), nil
else
dest ||= ''
return dest, dest
end
end
def get( path, u_header = nil, dest = nil, &block ) def get( path, u_header = nil, dest = nil, &block )
u_header = procheader( u_header ) u_header = procheader( u_header )
if block then dest, ret = HTTP.procdest( dest, block )
dest = ReadAdapter.new( block )
ret = nil
else
dest = ret = ''
end
resp = nil resp = nil
connecting( u_header ) { connecting( u_header ) {
@command.get edit_path(path), u_header @command.get edit_path(path), u_header
resp = @command.get_response resp = @command.get_response
@command.try_get_body( resp, dest ) @command.get_body( resp, dest )
} }
return resp['http-header'], ret return resp['http-header'], ret
@ -108,67 +135,82 @@ class HTTPBadResponse < HTTPError; end
def get2( path, u_header = nil ) def get2( path, u_header = nil )
u_header = procheader( u_header ) u_header = procheader( u_header )
only_header( :get, path, u_header ) connecting( u_header ) {
@command.get edit_path(path), u_header
tmp = HTTPWriter.new( @command )
yield tmp
tmp.off
}
end end
=begin c
def get_body( dest = '', &block ) def get_body( dest = '', &block )
if block then if block then
dest = ReadAdapter.new( block ) dest = ReadAdapter.new( block )
end end
@command.try_get_body @response, dest @command.get_body @response, dest
ensure_termination @u_header ensure_termination @u_header
dest dest
end end
=end
def head( path, u_header = nil ) def head( path, u_header = nil )
u_header = procheader( u_header ) u_header = procheader( u_header )
header = only_header( :head, path, u_header ) resp = nil
ensure_termination u_header connecting( u_header ) {
header @command.head( edit_path(path), u_header )
resp = @command.get_response_no_body
}
resp['http-header']
end end
def post( path, data, u_header = nil, dest = nil, &block ) def post( path, data, u_header = nil, dest = nil, &block )
u_header = procheader( u_header ) u_header = procheader( u_header )
if block then dest, ret = HTTP.procdest( dest, block )
dest = ReadAdapter.new( block )
ret = nil
else
dest = ret = ''
end
resp = nil resp = nil
connecting( u_header, true ) { connecting( u_header ) {
@command.post path, u_header, data @command.post edit_path(path), u_header, data
resp = @command.get_response resp = @command.get_response
@command.try_get_body( resp, dest ) @command.get_body( resp, dest )
} }
return resp['http-header'], ret return resp['http-header'], ret
end end
def post2( path, data, u_header = {} ) def post2( path, data, u_header = nil )
u_header = procheader( u_header ) u_header = procheader( u_header )
only_header :post, path, u_header, data connecting( u_header ) {
@command.post edit_path(path), u_header, data
tmp = HTTPWriter.new( @command )
yield tmp
tmp.off
}
end end
# not tested because I could not setup apache (__;;; # not tested because I could not setup apache (__;;;
def put( path, src = nil, u_header = nil, &block ) def put( path, src, u_header = nil )
u_header = procheader( u_header ) u_header = procheader( u_header )
connecting( u_header, true ) { ret = ''
connecting( u_header ) {
@command.put path, u_header, src, dest @command.put path, u_header, src, dest
resp = @comman.get_response
@command.get_body( resp, ret )
} }
header return header, ret
end end
private private
=begin c
def only_header( mid, path, u_header, data = nil ) def only_header( mid, path, u_header, data = nil )
@u_header = u_header @u_header = u_header
@response = nil @response = nil
ensure_connection u_header connecting u_header
if data then if data then
@command.send mid, edit_path(path), u_header, data @command.send mid, edit_path(path), u_header, data
else else
@ -177,6 +219,7 @@ class HTTPBadResponse < HTTPError; end
@response = @command.get_response @response = @command.get_response
@response['http-header'] @response['http-header']
end end
=end
# called when connecting # called when connecting
@ -189,19 +232,19 @@ class HTTPBadResponse < HTTPError; end
end end
end end
def connecting( u_header, putp = false ) def connecting( u_header )
ensure_connection u_header
yield
ensure_termination u_header
end
def ensure_connection( u_header )
if not @socket then if not @socket then
u_header['Connection'] = 'Close' u_header['Connection'] = 'Close'
start start
elsif @socket.closed? then elsif @socket.closed? then
@socket.reopen @socket.reopen
end end
if iterator? then
ret = yield
ensure_termination u_header
ret
end
end end
def ensure_termination( u_header ) def ensure_termination( u_header )
@ -258,18 +301,82 @@ class HTTPBadResponse < HTTPError; end
HTTPSession = HTTP HTTPSession = HTTP
class HTTPSuccessCode < SuccessCode; end class HTTPWriter
class HTTPCreatedCode < SuccessCode; end
class HTTPAcceptedCode < SuccessCode; end
class HTTPNoContentCode < SuccessCode; end
class HTTPResetContentCode < SuccessCode; end
class HTTPPartialContentCode < SuccessCode; end
class HTTPMultipleChoiceCode < RetryCode; end def initialize( command )
class HTTPMovedPermanentlyCode < RetryCode; end @command = command
class HTTPMovedTemporarilyCode < RetryCode; end @response = @header = @entity = nil
class HTTPNotModifiedCode < RetryCode; end end
class HTTPUseProxyCode < RetryCode; end
def response
unless @resp then
@resp = @command.get_response
end
@resp
end
def header
unless @header then
@header = response['http-header']
end
@header
end
def entity( dest = nil, &block )
dest, ret = HTTP.procdest( dest, block )
unless @entity then
@entity = @command.get_body( response, dest )
end
@entity
end
alias body entity
def off
entity
@command = nil
end
end
class HTTPSwitchProtocol < SuccessCode; end
class HTTPOK < SuccessCode; end
class HTTPCreated < SuccessCode; end
class HTTPAccepted < SuccessCode; end
class HTTPNonAuthoritativeInformation < SuccessCode; end
class HTTPNoContent < SuccessCode; end
class HTTPResetContent < SuccessCode; end
class HTTPPartialContent < SuccessCode; end
class HTTPMultipleChoice < RetryCode; end
class HTTPMovedPermanently < RetryCode; end
class HTTPMovedTemporarily < RetryCode; end
class HTTPNotModified < RetryCode; end
class HTTPUseProxy < RetryCode; end
class HTTPBadRequest < RetryCode; end
class HTTPUnauthorized < RetryCode; end
class HTTPPaymentRequired < RetryCode; end
class HTTPForbidden < FatalErrorCode; end
class HTTPNotFound < FatalErrorCode; end
class HTTPMethodNotAllowed < FatalErrorCode; end
class HTTPNotAcceptable < FatalErrorCode; end
class HTTPProxyAuthenticationRequired < RetryCode; end
class HTTPRequestTimeOut < FatalErrorCode; end
class HTTPConflict < FatalErrorCode; end
class HTTPGone < FatalErrorCode; end
class HTTPLengthRequired < FatalErrorCode; end
class HTTPPreconditionFailed < FatalErrorCode; end
class HTTPRequestEntityTooLarge < FatalErrorCode; end
class HTTPRequestURITooLarge < FatalErrorCode; end
class HTTPUnsupportedMediaType < FatalErrorCode; end
class HTTPNotImplemented < FatalErrorCode; end
class HTTPBadGateway < FatalErrorCode; end
class HTTPServiceUnavailable < FatalErrorCode; end
class HTTPGatewayTimeOut < FatalErrorCode; end
class HTTPVersionNotSupported < FatalErrorCode; end
class HTTPCommand < Command class HTTPCommand < Command
@ -292,30 +399,36 @@ class HTTPBadResponse < HTTPError; end
def get( path, u_header ) def get( path, u_header )
return unless begin_critical
request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header
end end
def head( path, u_header ) def head( path, u_header )
return unless begin_critical
request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header
end end
def post( path, u_header, data ) def post( path, u_header, data )
return unless begin_critical
request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header
@socket.write data @socket.write data
end end
def put( path, u_header, src ) def put( path, u_header, src )
return unless begin_critical
request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header
@socket.write_bin src @socket.write_bin src
end end
# def delete # def delete
# def trace # def trace
# def options # def options
def quit
end
def get_response def get_response
rep = get_reply rep = get_reply
@ -327,35 +440,47 @@ class HTTPBadResponse < HTTPError; end
nm = /\A[^:]+/.match( line )[0].strip.downcase nm = /\A[^:]+/.match( line )[0].strip.downcase
header[nm] = line header[nm] = line
end end
rep['http-header'] = header rep['http-header'] = header
reply_must rep, SuccessCode
rep rep
end end
def get_body( rep, dest ) def check_response( resp )
header = rep['http-header'] reply_must resp, SuccessCode
if chunked? header then
read_chunked( dest, header )
else
if clen = content_length( header ) then
@socket.read clen, dest
else
##### "multipart/byteranges" check should be done here ...
# now, length is designated by closing socket
@socket.read_all dest
@socket.close
end
end
end end
def try_get_body( rep, dest ) def get_body( rep, dest )
rep = get_reply while ContinueCode === rep header = rep['http-header']
return nil unless rep['body-exist']
get_body rep, dest if rep['body-exist'] then
if chunked? header then
read_chunked( dest, header )
else
if clen = content_length( header ) then
@socket.read clen, dest
else
if false then # "multipart/byteranges" check should be done
else
if header['Connection'] and
/connection:\s*close/i === header['Connection'] then
@socket.read_all dest
@socket.close
end
end
end
end
end
end_critical
reply_must rep, SuccessCode
dest
end
def get_response_no_body
resp = get_response
end_critical
reply_must resp, SuccessCode
resp
end end
@ -376,6 +501,50 @@ class HTTPBadResponse < HTTPError; end
end end
CODE_TO_CLASS = {
'100' => [ContinueCode, false],
'100' => [HTTPSwitchProtocol, false],
'200' => [HTTPOK, true],
'201' => [HTTPCreated, true],
'202' => [HTTPAccepted, true],
'203' => [HTTPNonAuthoritativeInformation, true],
'204' => [HTTPNoContent, false],
'205' => [HTTPResetContent, false],
'206' => [HTTPPartialContent, true],
'300' => [HTTPMultipleChoice, true],
'301' => [HTTPMovedPermanently, true],
'302' => [HTTPMovedTemporarily, true],
'303' => [HTTPMovedPermanently, true],
'304' => [HTTPNotModified, false],
'305' => [HTTPUseProxy, false],
'400' => [HTTPBadRequest, true],
'401' => [HTTPUnauthorized, true],
'402' => [HTTPPaymentRequired, true],
'403' => [HTTPForbidden, true],
'404' => [HTTPNotFound, true],
'405' => [HTTPMethodNotAllowed, true],
'406' => [HTTPNotAcceptable, true],
'407' => [HTTPProxyAuthenticationRequired, true],
'408' => [HTTPRequestTimeOut, true],
'409' => [HTTPConflict, true],
'410' => [HTTPGone, true],
'411' => [FatalErrorCode, true],
'412' => [HTTPPreconditionFailed, true],
'413' => [HTTPRequestEntityTooLarge, true],
'414' => [HTTPRequestURITooLarge, true],
'415' => [HTTPUnsupportedMediaType, true],
'500' => [FatalErrorCode, true],
'501' => [HTTPNotImplemented, true],
'502' => [HTTPBadGateway, true],
'503' => [HTTPServiceUnavailable, true],
'504' => [HTTPGatewayTimeOut, true],
'505' => [HTTPVersionNotSupported, true]
}
def get_reply def get_reply
str = @socket.readline str = @socket.readline
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
@ -385,41 +554,9 @@ class HTTPBadResponse < HTTPError; end
status = $2 status = $2
discrip = $3 discrip = $3
be = false klass, bodyexist = CODE_TO_CLASS[status] || [UnknownCode, true]
klass = case status[0]
when ?1 then
case status[2]
when ?0 then ContinueCode
when ?1 then HTTPSuccessCode
else UnknownCode
end
when ?2 then
case status[2]
when ?0 then be = true; HTTPSuccessCode
when ?1 then be = false; HTTPSuccessCode
when ?2 then be = true; HTTPSuccessCode
when ?3 then be = true; HTTPSuccessCode
when ?4 then be = false; HTTPNoContentCode
when ?5 then be = false; HTTPResetContentCode
when ?6 then be = true; HTTPPartialContentCode
else UnknownCode
end
when ?3 then
case status[2]
when ?0 then be = true; HTTPMultipleChoiceCode
when ?1 then be = true; HTTPMovedPermanentryCode
when ?2 then be = true; HTTPMovedTemporarilyCode
when ?3 then be = true; HTTPMovedPermanentryCode
when ?4 then be = false; HTTPNotModifiedCode
when ?5 then be = false; HTTPUseProxyCode
else UnknownCode
end
when ?4 then ServerBusyCode
when ?5 then FatalErrorCode
else UnknownCode
end
code = klass.new( status, discrip ) code = klass.new( status, discrip )
code['body-exist'] = be code['body-exist'] = bodyexist
code code
end end

View file

@ -71,8 +71,9 @@ Net::Protocol
def do_start( acnt, pwd ) def do_start( acnt, pwd )
@command.auth( acnt, pwd ) @command.auth( acnt, pwd )
t = self.type.mail_type
@mails = [] @mails = []
t = type.mail_type
@command.list.each_with_index do |size,idx| @command.list.each_with_index do |size,idx|
if size then if size then
@mails.push t.new( idx, size, @command ) @mails.push t.new( idx, size, @command )
@ -221,72 +222,83 @@ Net::POP3
def initialize( sock ) def initialize( sock )
super super
check_reply SuccessCode critical {
check_reply SuccessCode
}
end end
def auth( acnt, pass ) def auth( acnt, pass )
@socket.writeline 'USER ' + acnt critical {
check_reply_auth @socket.writeline 'USER ' + acnt
check_reply_auth
@socket.writeline( 'PASS ' + pass ) @socket.writeline 'PASS ' + pass
ret = check_reply_auth check_reply_auth
}
return ret
end end
def list def list
getok 'LIST'
arr = [] arr = []
@socket.read_pendlist do |line| critical {
num, siz = line.split( / +/o ) getok 'LIST'
arr[ num.to_i ] = siz.to_i @socket.read_pendlist do |line|
end num, siz = line.split( / +/o )
arr[ num.to_i ] = siz.to_i
return arr end
}
arr
end end
def rset def rset
getok 'RSET' critical {
getok 'RSET'
}
end end
def top( num, lines = 0, dest = '' ) def top( num, lines = 0, dest = '' )
getok sprintf( 'TOP %d %d', num, lines ) critical {
@socket.read_pendstr( dest ) getok sprintf( 'TOP %d %d', num, lines )
@socket.read_pendstr( dest )
}
end end
def retr( num, dest = '', &block ) def retr( num, dest = '', &block )
getok sprintf( 'RETR %d', num ) critical {
@socket.read_pendstr( dest, &block ) getok sprintf( 'RETR %d', num )
@socket.read_pendstr( dest, &block )
}
end end
def dele( num ) def dele( num )
getok sprintf( 'DELE %d', num ) critical {
getok sprintf( 'DELE %d', num )
}
end end
def uidl( num ) def uidl( num )
rep = getok( sprintf 'UIDL %d', num ) critical {
uid = rep.msg.split(' ')[1] getok( sprintf 'UIDL %d', num ).msg.split(' ')[1]
}
end
uid
def quit
critical {
getok 'QUIT'
}
end end
private private
def do_quit
getok 'QUIT'
end
def check_reply_auth def check_reply_auth
begin begin
cod = check_reply( SuccessCode ) cod = check_reply( SuccessCode )
@ -326,19 +338,17 @@ Net::POP3
def auth( acnt, pass ) def auth( acnt, pass )
@socket.writeline( "APOP #{acnt} #{digest(@stamp + pass)}" ) critical {
return check_reply_auth @socket.writeline( "APOP #{acnt} #{digest(@stamp + pass)}" )
check_reply_auth
}
end end
def digest( str ) def digest( str )
temp = MD5.new( str ).digest
ret = '' ret = ''
temp.each_byte do |i| MD5.new( str ).digest.each_byte {|i| ret << sprintf('%02x', i) }
ret << sprintf( '%02x', i ) ret
end
return ret
end end
end end

View file

@ -15,7 +15,7 @@ require 'socket'
module Net module Net
Version = '1.1.10' Version = '1.1.11'
=begin =begin
@ -144,7 +144,7 @@ Object
def initialize( addr = nil, port = nil ) def initialize( addr = nil, port = nil )
@address = addr || 'localhost' @address = addr || 'localhost'
@port = port || self.type.port @port = port || type.port
@active = false @active = false
@pipe = nil @pipe = nil
@ -160,11 +160,11 @@ Object
def start( *args ) def start( *args )
return false if active? return false if active?
@active = true
begin begin
connect @address, @port connect
do_start *args do_start *args
@active = true
yield if iterator? yield if iterator?
ensure ensure
finish if iterator? finish if iterator?
@ -174,7 +174,7 @@ Object
def finish def finish
ret = active? ret = active?
do_finish if @command do_finish
disconnect disconnect
@active = false @active = false
@ -201,9 +201,9 @@ Object
end end
def connect( addr, port ) def connect( addr = @address, port = @port )
@socket = self.type.socket_type.open( addr, port, @pipe ) @socket = type.socket_type.open( addr, port, @pipe )
@command = self.type.command_type.new( @socket ) @command = type.command_type.new( @socket )
end end
def disconnect def disconnect
@ -213,7 +213,7 @@ Object
end end
@socket = nil @socket = nil
end end
end end
Session = Protocol Session = Protocol
@ -226,24 +226,17 @@ Object
@socket = sock @socket = sock
@error_occured = false @error_occured = false
@last_reply = nil @last_reply = nil
@critical = false
end end
attr_reader :socket, :error_occured, :last_reply attr_reader :socket, :error_occured, :last_reply
attr_writer :socket attr_writer :socket
def quit # abstract quit
if @socket and not @socket.closed? then
do_quit
@error_occured = false
end
end
private private
def do_quit
end
# abstract get_reply() # abstract get_reply()
def check_reply( *oks ) def check_reply( *oks )
@ -266,7 +259,30 @@ Object
@socket.writeline line @socket.writeline line
check_reply ok check_reply ok
end end
def critical
return if @critical
@critical = true
r = yield
@critical = false
r
end
def critical?
@critical
end
def begin_critical
ret = @critical
@critical = true
not ret
end
def end_critical
@critical = false
end
end end
@ -284,11 +300,11 @@ Object
class << self class << self
def error_type( err ) def error_type( err )
@err = err module_eval "def self.get_error_type() #{err.name} end"
end end
def error!( mes ) def error!( mes )
raise @err, mes raise get_error_type, mes
end end
end end

View file

@ -121,33 +121,44 @@ Net::Protocol
def initialize( sock ) def initialize( sock )
super super
check_reply SuccessCode critical {
check_reply SuccessCode
}
end end
def helo( fromdom ) def helo( fromdom )
getok sprintf( 'HELO %s', fromdom ) critical {
getok sprintf( 'HELO %s', fromdom )
}
end end
def ehlo( fromdom ) def ehlo( fromdom )
getok sprintf( 'EHLO %s', fromdom ) critical {
getok sprintf( 'EHLO %s', fromdom )
}
end end
def mailfrom( fromaddr ) def mailfrom( fromaddr )
getok sprintf( 'MAIL FROM:<%s>', fromaddr ) critical {
getok sprintf( 'MAIL FROM:<%s>', fromaddr )
}
end end
def rcpt( toaddrs ) def rcpt( toaddrs )
toaddrs.each do |i| toaddrs.each do |i|
getok sprintf( 'RCPT TO:<%s>', i ) critical {
getok sprintf( 'RCPT TO:<%s>', i )
}
end end
end end
def data def data
return unless begin_critical
getok 'DATA', ContinueCode getok 'DATA', ContinueCode
end end
@ -155,18 +166,21 @@ Net::Protocol
def write_mail( mailsrc = nil, &block ) def write_mail( mailsrc = nil, &block )
@socket.write_pendstr mailsrc, &block @socket.write_pendstr mailsrc, &block
check_reply SuccessCode check_reply SuccessCode
end_critical
end end
alias sendmail write_mail alias sendmail write_mail
private def quit
critical {
getok 'QUIT'
def do_quit }
getok 'QUIT'
end end
private
def get_reply def get_reply
arr = read_reply arr = read_reply
stat = arr[0][0,3] stat = arr[0][0,3]