diff options
author | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-09-13 07:27:18 +0000 |
---|---|---|
committer | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-09-13 07:27:18 +0000 |
commit | e99c067d8eabedf3210ab7531f0ca9b910809b1b (patch) | |
tree | 9cfb8e7bc1fc6ba3f772a3528552a8834f37466c /lib/net/protocol.rb | |
parent | bf20e3b82ef1271d4648e6db0abd2237fc490247 (diff) |
* lib/net/http.rb: backported from trunk, rev 1.128. [ruby-dev:25673] [ruby-dev:26617]
* lib/net/protocol.rb: backported from trunk, rev 1.78.
* lib/net/protocol.rb: new method #old_open to support net/smtp and net/pop.
* lib/net/smtp.rb: use #old_open.
* lib/net/pop.rb: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@9146 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/net/protocol.rb')
-rw-r--r-- | lib/net/protocol.rb | 392 |
1 files changed, 167 insertions, 225 deletions
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index b527956c98..d722fdcbd4 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -2,8 +2,8 @@ # = net/protocol.rb # #-- -# Copyright (c) 1999-2004 Yukihiro Matsumoto -# Copyright (c) 1999-2004 Minero Aoki +# Copyright (c) 1999-2005 Yukihiro Matsumoto +# Copyright (c) 1999-2005 Minero Aoki # # written and maintained by Minero Aoki <aamine@loveruby.net> # @@ -25,7 +25,7 @@ module Net # :nodoc: class Protocol #:nodoc: internal use only private - def Protocol.protocol_param( name, val ) + def Protocol.protocol_param(name, val) module_eval(<<-End, __FILE__, __LINE__ + 1) def #{name} #{val} @@ -34,6 +34,7 @@ module Net # :nodoc: end end + class ProtocolError < StandardError; end class ProtoSyntaxError < ProtocolError; end class ProtoFatalError < ProtocolError; end @@ -45,335 +46,282 @@ module Net # :nodoc: ProtocRetryError = ProtoRetriableError - class InternetMessageIO #:nodoc: internal use only - - class << self - alias open new - end - - def initialize( addr, port, - open_timeout = nil, read_timeout = nil, - debug_output = nil ) - @address = addr - @port = port - @read_timeout = read_timeout - @debug_output = debug_output - @socket = nil - @rbuf = nil # read buffer - @wbuf = nil # write buffer - connect open_timeout - LOG 'opened' - end - - attr_reader :address - attr_reader :port - - def ip_address - return '' unless @socket - @socket.addr[3] - end - - attr_accessor :read_timeout - - attr_reader :socket - - def connect( open_timeout ) - LOG "opening connection to #{@address}..." - timeout(open_timeout) { - @socket = TCPsocket.new(@address, @port) - } + class BufferedIO #:nodoc: internal use only + def initialize(io) + @io = io + @read_timeout = 60 + @debug_output = nil @rbuf = '' end - private :connect - def close - if @socket - @socket.close - LOG 'closed' - else - LOG 'close call for already closed socket' - end - @socket = nil - @rbuf = '' - end + attr_reader :io + attr_accessor :read_timeout + attr_accessor :debug_output - def reopen( open_timeout = nil ) - LOG 'reopening...' - close - connect open_timeout - LOG 'reopened' + def inspect + "#<#{self.class} io=#{@io}>" end def closed? - not @socket + @io.closed? end - def inspect - "#<#{self.class} #{closed?() ? 'closed' : 'opened'}>" + def close + @io.close end - ### - ### READ - ### + # + # Read + # public - def read( len, dest = '', ignore_eof = false ) + def read(len, dest = '', ignore_eof = false) LOG "reading #{len} bytes..." - # LOG_off() # experimental: [ruby-list:38800] read_bytes = 0 begin while read_bytes + @rbuf.size < len - read_bytes += rbuf_moveto(dest, @rbuf.size) + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size rbuf_fill end - rbuf_moveto dest, len - read_bytes + dest << (s = rbuf_consume(len - read_bytes)) + read_bytes += s.size rescue EOFError raise unless ignore_eof end - # LOG_on() LOG "read #{read_bytes} bytes" dest end - def read_all( dest = '' ) + def read_all(dest = '') LOG 'reading all...' - # LOG_off() # experimental: [ruby-list:38800] read_bytes = 0 begin while true - read_bytes += rbuf_moveto(dest, @rbuf.size) + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size rbuf_fill end rescue EOFError ; end - # LOG_on() LOG "read #{read_bytes} bytes" dest end - def readuntil( terminator, ignore_eof = false ) - dest = '' + def readuntil(terminator, ignore_eof = false) begin until idx = @rbuf.index(terminator) rbuf_fill end - rbuf_moveto dest, idx + terminator.size + return rbuf_consume(idx + terminator.size) rescue EOFError raise unless ignore_eof - rbuf_moveto dest, @rbuf.size + return rbuf_consume(@rbuf.size) end - dest end def readline readuntil("\n").chop end - def each_message_chunk - LOG 'reading message...' - LOG_off() - read_bytes = 0 - while (line = readuntil("\r\n")) != ".\r\n" - read_bytes += line.size - yield line.sub(/\A\./, '') - end - LOG_on() - LOG "read message (#{read_bytes} bytes)" - end - - # *library private* (cannot handle 'break') - def each_list_item - while (str = readuntil("\r\n")) != ".\r\n" - yield str.chop - end - end - private def rbuf_fill timeout(@read_timeout) { - @rbuf << @socket.sysread(1024) + @rbuf << @io.sysread(1024) } end - def rbuf_moveto( dest, len ) - dest << (s = @rbuf.slice!(0, len)) + def rbuf_consume(len) + s = @rbuf.slice!(0, len) @debug_output << %Q[-> #{s.dump}\n] if @debug_output - len + s end - ### - ### WRITE - ### + # + # Write + # public - def write( str ) + def write(str) writing { write0 str } end - def writeline( str ) + def writeline(str) writing { write0 str + "\r\n" } end - def write_message( src ) - LOG "writing message from #{src.class}" - LOG_off() - len = using_each_crlf_line { - write_message_0 src - } - LOG_on() - LOG "wrote #{len} bytes" - len - end - - def write_message_by_block( &block ) - LOG 'writing message from block' - LOG_off() - len = using_each_crlf_line { - begin - block.call(WriteAdapter.new(self, :write_message_0)) - rescue LocalJumpError - # allow `break' from writer block - end - } - LOG_on() - LOG "wrote #{len} bytes" - len - end - private def writing @written_bytes = 0 @debug_output << '<- ' if @debug_output yield - @socket.flush @debug_output << "\n" if @debug_output bytes = @written_bytes @written_bytes = nil bytes end - def write0( str ) + def write0(str) @debug_output << str.dump if @debug_output - len = @socket.write(str) + len = @io.write(str) @written_bytes += len len end # - # Reads string from src calling :each, and write to @socket. - # Escapes '.' on the each line head. + # Logging # - def write_message_0( src ) + + private + + def LOG_off + @save_debug_out = @debug_output + @debug_output = nil + end + + def LOG_on + @debug_output = @save_debug_out + end + + def LOG(msg) + return unless @debug_output + @debug_output << msg + "\n" + end + end + + + class InternetMessageIO < BufferedIO #:nodoc: internal use only + def InternetMessageIO.old_open(addr, port, + open_timeout = nil, read_timeout = nil, debug_output = nil) + debug_output << "opening connection to #{addr}...\n" if debug_output + s = timeout(open_timeout) { TCPsocket.new(addr, port) } + io = new(s) + io.read_timeout = read_timeout + io.debug_output = debug_output + io + end + + def initialize(io) + super + @wbuf = nil + end + + # + # Read + # + + def each_message_chunk + LOG 'reading message...' + LOG_off() + read_bytes = 0 + while (line = readuntil("\r\n")) != ".\r\n" + read_bytes += line.size + yield line.sub(/\A\./, '') + end + LOG_on() + LOG "read message (#{read_bytes} bytes)" + end + + # *library private* (cannot handle 'break') + def each_list_item + while (str = readuntil("\r\n")) != ".\r\n" + yield str.chop + end + end + + def write_message_0(src) prev = @written_bytes each_crlf_line(src) do |line| - if line[0] == ?. - then write0 '.' + line - else write0 line - end + write0 line.sub(/\A\./, '..') end @written_bytes - prev end # - # setup @wbuf for each_crlf_line. + # Write # - def using_each_crlf_line - writing { - @wbuf = '' - yield - if not @wbuf.empty? # unterminated last line - if @wbuf[-1] == ?\r - @wbuf.chop! - end - @wbuf.concat "\r\n" - write0 @wbuf - elsif @written_bytes == 0 # empty src - write0 "\r\n" - end - write0 ".\r\n" - @wbuf = nil + + def write_message(src) + LOG "writing message from #{src.class}" + LOG_off() + len = writing { + using_each_crlf_line { + write_message_0 src + } } + LOG_on() + LOG "wrote #{len} bytes" + len end - # - # extract a CR-LF-terminating-line from @wbuf and yield it. - # - def each_crlf_line( src ) - adding(src) do - beg = 0 - buf = @wbuf - while buf.index(/\n|\r\n|\r/, beg) - m = Regexp.last_match - if (m.begin(0) == buf.length - 1) and buf[-1] == ?\r - # "...\r" : can follow "\n..." - break + def write_message_by_block(&block) + LOG 'writing message from block' + LOG_off() + len = writing { + using_each_crlf_line { + begin + block.call(WriteAdapter.new(self, :write_message_0)) + rescue LocalJumpError + # allow `break' from writer block end - str = buf[beg ... m.begin(0)] - str.concat "\r\n" - yield str - beg = m.end(0) + } + } + LOG_on() + LOG "wrote #{len} bytes" + len + end + + private + + def using_each_crlf_line + @wbuf = '' + yield + if not @wbuf.empty? # unterminated last line + write0 @wbuf.chomp + "\r\n" + elsif @written_bytes == 0 # empty src + write0 "\r\n" + end + write0 ".\r\n" + @wbuf = nil + end + + def each_crlf_line(src) + buffer_filling(@wbuf, src) do + while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n) + yield line.chomp("\n") + "\r\n" end - @wbuf = buf[beg ... buf.length] end end - # - # Reads strings from SRC and add to @wbuf, then yield. - # - def adding( src ) + def buffer_filling(buf, src) case src when String # for speeding up. - 0.step(src.size - 1, 2048) do |i| - @wbuf << src[i,2048] + 0.step(src.size - 1, 1024) do |i| + buf << src[i, 1024] yield end - when File # for speeding up. - while s = src.read(2048) - s[0,0] = @wbuf - @wbuf = s + while s = src.read(1024) + buf << s yield end - else # generic reader src.each do |s| - @wbuf << s - yield if @wbuf.size > 2048 + buf << s + yield if buf.size > 1024 end - yield unless @wbuf.empty? + yield unless buf.empty? end end - - ### - ### DEBUG - ### - - private - - def LOG_off - @save_debug_out = @debug_output - @debug_output = nil - end - - def LOG_on - @debug_output = @save_debug_out - end - - def LOG( msg ) - return unless @debug_output - @debug_output << msg - @debug_output << "\n" - end - end @@ -381,41 +329,38 @@ module Net # :nodoc: # The writer adapter class # class WriteAdapter - - def initialize( sock, mid ) - @socket = sock - @method_id = mid + def initialize(socket, method) + @socket = socket + @method_id = method end def inspect "#<#{self.class} socket=#{@socket.inspect}>" end - def write( str ) + def write(str) @socket.__send__(@method_id, str) end alias print write - def <<( str ) + def <<(str) write str self end - def puts( str = '' ) + def puts(str = '') write str.chomp("\n") + "\n" end - def printf( *args ) + def printf(*args) write sprintf(*args) end - end class ReadAdapter #:nodoc: internal use only - - def initialize( block ) + def initialize(block) @block = block end @@ -423,21 +368,18 @@ module Net # :nodoc: "#<#{self.class}>" end - def <<( str ) + def <<(str) call_block(str, &@block) if @block end private - # # This method is needed because @block must be called by yield, # not Proc#call. You can see difference when using `break' in # the block. - # - def call_block( str ) + def call_block(str) yield str end - end |