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 ) {|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' => '*/*', ... }.
Data is written to "dest" by using "<<" method.
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 )
: get2( path, header = nil ) {|writer| .... }
send GET request for "path".
"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 {|str| .... }
@ -78,7 +77,31 @@ class HTTPBadResponse < HTTPError; end
Data is written in "dest" by using "<<" method.
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
@ -87,20 +110,24 @@ class HTTPBadResponse < HTTPError; end
protocol_param :port, '80'
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 )
u_header = procheader( u_header )
if block then
dest = ReadAdapter.new( block )
ret = nil
else
dest = ret = ''
end
dest, ret = HTTP.procdest( dest, block )
resp = nil
connecting( u_header ) {
@command.get edit_path(path), u_header
resp = @command.get_response
@command.try_get_body( resp, dest )
@command.get_body( resp, dest )
}
return resp['http-header'], ret
@ -108,67 +135,82 @@ class HTTPBadResponse < HTTPError; end
def get2( path, u_header = nil )
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
=begin c
def get_body( dest = '', &block )
if block then
dest = ReadAdapter.new( block )
end
@command.try_get_body @response, dest
@command.get_body @response, dest
ensure_termination @u_header
dest
end
=end
def head( path, u_header = nil )
u_header = procheader( u_header )
header = only_header( :head, path, u_header )
ensure_termination u_header
header
resp = nil
connecting( u_header ) {
@command.head( edit_path(path), u_header )
resp = @command.get_response_no_body
}
resp['http-header']
end
def post( path, data, u_header = nil, dest = nil, &block )
u_header = procheader( u_header )
if block then
dest = ReadAdapter.new( block )
ret = nil
else
dest = ret = ''
end
dest, ret = HTTP.procdest( dest, block )
resp = nil
connecting( u_header, true ) {
@command.post path, u_header, data
connecting( u_header ) {
@command.post edit_path(path), u_header, data
resp = @command.get_response
@command.try_get_body( resp, dest )
@command.get_body( resp, dest )
}
return resp['http-header'], ret
end
def post2( path, data, u_header = {} )
def post2( path, data, u_header = nil )
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
# 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 )
connecting( u_header, true ) {
ret = ''
connecting( u_header ) {
@command.put path, u_header, src, dest
resp = @comman.get_response
@command.get_body( resp, ret )
}
header
return header, ret
end
private
=begin c
def only_header( mid, path, u_header, data = nil )
@u_header = u_header
@response = nil
ensure_connection u_header
connecting u_header
if data then
@command.send mid, edit_path(path), u_header, data
else
@ -177,6 +219,7 @@ class HTTPBadResponse < HTTPError; end
@response = @command.get_response
@response['http-header']
end
=end
# called when connecting
@ -189,19 +232,19 @@ class HTTPBadResponse < HTTPError; end
end
end
def connecting( u_header, putp = false )
ensure_connection u_header
yield
ensure_termination u_header
end
def ensure_connection( u_header )
def connecting( u_header )
if not @socket then
u_header['Connection'] = 'Close'
start
elsif @socket.closed? then
@socket.reopen
end
if iterator? then
ret = yield
ensure_termination u_header
ret
end
end
def ensure_termination( u_header )
@ -258,18 +301,82 @@ class HTTPBadResponse < HTTPError; end
HTTPSession = HTTP
class HTTPSuccessCode < SuccessCode; end
class HTTPCreatedCode < SuccessCode; end
class HTTPAcceptedCode < SuccessCode; end
class HTTPNoContentCode < SuccessCode; end
class HTTPResetContentCode < SuccessCode; end
class HTTPPartialContentCode < SuccessCode; end
class HTTPWriter
class HTTPMultipleChoiceCode < RetryCode; end
class HTTPMovedPermanentlyCode < RetryCode; end
class HTTPMovedTemporarilyCode < RetryCode; end
class HTTPNotModifiedCode < RetryCode; end
class HTTPUseProxyCode < RetryCode; end
def initialize( command )
@command = command
@response = @header = @entity = nil
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
@ -292,30 +399,36 @@ class HTTPBadResponse < HTTPError; end
def get( path, u_header )
return unless begin_critical
request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header
end
def head( path, u_header )
return unless begin_critical
request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header
end
def post( path, u_header, data )
return unless begin_critical
request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header
@socket.write data
end
def put( path, u_header, src )
return unless begin_critical
request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header
@socket.write_bin src
end
# def delete
# def trace
# def options
def quit
end
def get_response
rep = get_reply
@ -327,35 +440,47 @@ class HTTPBadResponse < HTTPError; end
nm = /\A[^:]+/.match( line )[0].strip.downcase
header[nm] = line
end
rep['http-header'] = header
reply_must rep, SuccessCode
rep
end
def get_body( rep, dest )
header = rep['http-header']
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
def check_response( resp )
reply_must resp, SuccessCode
end
def try_get_body( rep, dest )
rep = get_reply while ContinueCode === rep
return nil unless rep['body-exist']
def get_body( rep, dest )
header = rep['http-header']
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
@ -376,6 +501,50 @@ class HTTPBadResponse < HTTPError; 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
str = @socket.readline
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
@ -385,41 +554,9 @@ class HTTPBadResponse < HTTPError; end
status = $2
discrip = $3
be = false
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
klass, bodyexist = CODE_TO_CLASS[status] || [UnknownCode, true]
code = klass.new( status, discrip )
code['body-exist'] = be
code['body-exist'] = bodyexist
code
end

View File

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

View File

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

View File

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