From 9fd5174ef3aebe3d862289b4c646e65b2400b2f9 Mon Sep 17 00:00:00 2001 From: aamine Date: Mon, 27 Mar 2000 15:52:27 +0000 Subject: [PATCH] v1.1.11 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 --- lib/net/http.rb | 355 ++++++++++++++++++++++++++++++-------------- lib/net/pop.rb | 86 ++++++----- lib/net/protocol.rb | 58 +++++--- lib/net/smtp.rb | 34 +++-- 4 files changed, 355 insertions(+), 178 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 14f9da4003..009850abbc 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -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 diff --git a/lib/net/pop.rb b/lib/net/pop.rb index 616f537f52..7a41e44350 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -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 diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index 4f2bf17fd4..776dba7359 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -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 diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index b2ac287c06..a1a2f5370f 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -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]