summaryrefslogtreecommitdiff
path: root/lib/net/protocol.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net/protocol.rb')
-rw-r--r--lib/net/protocol.rb1098
1 files changed, 391 insertions, 707 deletions
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 343721add3..8c81298c0e 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -1,279 +1,73 @@
-=begin
-
-= net/protocol.rb version 1.1.34
-
-written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
-
-This program is free software.
-You can distribute/modify this program under
-the terms of the Ruby Distribute License.
-
-Japanese version of this document is in "net" full package.
-You can get it from RAA
-(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html).
-
-
-== Net::Protocol
-
-the abstract class for Internet protocol
-
-=== Super Class
-
-Object
-
-=== Class Methods
-
-: new( address = 'localhost', port = nil )
- This method Creates a new protocol object.
-
-: start( address = 'localhost', port = nil, *protoargs )
-: start( address = 'localhost', port = nil, *protoargs ) {|proto| .... }
- This method creates a new Protocol object and opens a session.
- equals to Net::Protocol.new( address, port ).start( *protoargs )
-
-=== Methods
-
-: address
- the address of connecting server (FQDN).
-
-: port
- connecting port number
-
-: start( *args )
-: start( *args ) {|proto| .... }
- This method starts protocol. If protocol was already started,
- do nothing and returns false.
-
- '*args' are specified in subclasses.
-
- When is called with block, gives Protocol object to block and
- close session when block finished.
-
-: finish
- This method ends protocol. If you call this method before protocol starts,
- it only return false without doing anything.
-
-: active?
- true if session have been started
-
-=end
+# frozen_string_literal: true
+#
+# = net/protocol.rb
+#
+#--
+# Copyright (c) 1999-2004 Yukihiro Matsumoto
+# Copyright (c) 1999-2004 Minero Aoki
+#
+# written and maintained by Minero Aoki <aamine@loveruby.net>
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms as Ruby itself,
+# Ruby Distribute License or GNU General Public License.
+#
+# $Id$
+#++
+#
+# WARNING: This file is going to remove.
+# Do not rely on the implementation written in this file.
+#
require 'socket'
require 'timeout'
+require 'io/wait'
+module Net # :nodoc:
-module Net
+ class Protocol #:nodoc: internal use only
+ VERSION = "0.2.2"
- module NetPrivate
- end
-
- def self.net_private( &block )
- ::Net::NetPrivate.module_eval( &block )
- end
-
-
- class Protocol
-
- Version = '1.1.34'
-
-
- class << self
-
- def start( address = 'localhost', port = nil, *args )
- instance = new( address, port )
-
- if block_given? then
- instance.start( *args ) { yield instance }
- else
- instance.start( *args )
- instance
+ private
+ def Protocol.protocol_param(name, val)
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def #{name}
+ #{val}
end
- end
-
- private
-
- def protocol_param( name, val )
- module_eval %-
- def self.#{name.id2name}
- #{val}
+ End
+ end
+
+ def ssl_socket_connect(s, timeout)
+ if timeout
+ while true
+ raise Net::OpenTimeout if timeout <= 0
+ start = Process.clock_gettime Process::CLOCK_MONOTONIC
+ # to_io is required because SSLSocket doesn't have wait_readable yet
+ case s.connect_nonblock(exception: false)
+ when :wait_readable; s.to_io.wait_readable(timeout)
+ when :wait_writable; s.to_io.wait_writable(timeout)
+ else; break
end
- -
- end
-
- end
-
-
- #
- # sub-class requirements
- #
- # protocol_param command_type
- # protocol_param port
- #
- # private method do_start (optional)
- # private method do_finish (optional)
- #
-
- protocol_param :port, 'nil'
- protocol_param :command_type, 'nil'
- protocol_param :socket_type, '::Net::NetPrivate::Socket'
-
-
- def initialize( addr = nil, port = nil )
- @address = addr || 'localhost'
- @port = port || type.port
-
- @command = nil
- @socket = nil
-
- @active = false
-
- @open_timeout = nil
- @read_timeout = nil
-
- @dout = nil
- end
-
- attr_reader :address
- attr_reader :port
-
- attr_reader :command
- attr_reader :socket
-
- attr_accessor :open_timeout
- attr_accessor :read_timeout
-
- def active?
- @active
- end
-
- def set_debug_output( arg ) # un-documented
- @dout = arg
- end
-
- alias set_pipe set_debug_output
-
- def inspect
- "#<#{type} #{address}:#{port} open=#{active?}>"
- end
-
- #
- # open session
- #
-
- def start( *args )
- return false if active?
-
- if block_given? then
- begin
- _start args
- yield self
- ensure
- finish
+ timeout -= Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
end
else
- _start args
- end
- end
-
- private
-
- def _start( args )
- connect
- do_start( *args )
- @active = true
- end
-
- def connect
- conn_socket @address, @port
- conn_command @socket
- on_connect
- end
-
- def re_connect
- @socket.reopen @open_timeout
- on_connect
- end
-
- def conn_socket( addr, port )
- @socket = type.socket_type.open(
- addr, port, @open_timeout, @read_timeout, @dout )
- end
-
- def conn_command( sock )
- @command = type.command_type.new( sock )
- end
-
- def on_connect
- end
-
- def do_start
- end
-
- #
- # close session
- #
-
- public
-
- def finish
- return false unless active?
-
- do_finish if @command and not @command.critical?
- disconnect
- @active = false
- true
- end
-
- private
-
- def do_finish
- @command.quit
- end
-
- def disconnect
- @command = nil
- if @socket and not @socket.closed? then
- @socket.close
+ s.connect
end
- @socket = nil
- on_disconnect
- end
-
- def on_disconnect
- end
-
- end
-
- Session = Protocol
-
-
- net_private {
-
- class Response
-
- def initialize( ctype, cno, msg )
- @code_type = ctype
- @code = cno
- @message = msg
- super()
end
- attr_reader :code_type, :code, :message
- alias msg message
-
- def inspect
- "#<#{type} #{code}>"
- end
-
- def error!( data = nil )
- raise code_type.error_type.new( code + ' ' + Net.quote(msg), data )
+ tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters
+ TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]]
+ tcp_socket_parameters.include?([:key, :open_timeout])
+ else
+ # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize
+ # See discussion in https://github.com/ruby/net-http/pull/224
+ Socket.method(:tcp).parameters.include?([:key, :open_timeout])
end
-
+ private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT
end
- }
-
+ # :stopdoc:
class ProtocolError < StandardError; end
class ProtoSyntaxError < ProtocolError; end
class ProtoFatalError < ProtocolError; end
@@ -283,593 +77,483 @@ module Net
class ProtoCommandError < ProtocolError; end
class ProtoRetriableError < ProtocolError; end
ProtocRetryError = ProtoRetriableError
+ # :startdoc:
- class ProtocolError
-
- def initialize( msg, data = nil )
- super msg
- @data = data
- end
-
- attr :data
-
- def inspect
- "#<#{type}>"
- end
-
- end
-
+ ##
+ # OpenTimeout, a subclass of Timeout::Error, is raised if a connection cannot
+ # be created within the open_timeout.
- class Code
+ class OpenTimeout < Timeout::Error; end
- def initialize( paren, err )
- @parents = paren
- @err = err
+ ##
+ # ReadTimeout, a subclass of Timeout::Error, is raised if a chunk of the
+ # response cannot be read within the read_timeout.
- @parents.push self
+ class ReadTimeout < Timeout::Error
+ # :stopdoc:
+ def initialize(io = nil)
+ @io = io
end
+ attr_reader :io
- attr_reader :parents
-
- def inspect
- "#<#{type}>"
- end
-
- def error_type
- @err
- end
-
- def ===( response )
- response.code_type.parents.reverse_each {|i| return true if i == self }
- false
- end
-
- def mkchild( err = nil )
- type.new( @parents + [self], err || @err )
- end
-
- end
-
- ReplyCode = Code.new( [], ProtoUnknownError )
- InformationCode = ReplyCode.mkchild( ProtoUnknownError )
- SuccessCode = ReplyCode.mkchild( ProtoUnknownError )
- ContinueCode = ReplyCode.mkchild( ProtoUnknownError )
- ErrorCode = ReplyCode.mkchild( ProtocolError )
- SyntaxErrorCode = ErrorCode.mkchild( ProtoSyntaxError )
- FatalErrorCode = ErrorCode.mkchild( ProtoFatalError )
- ServerErrorCode = ErrorCode.mkchild( ProtoServerError )
- AuthErrorCode = ErrorCode.mkchild( ProtoAuthError )
- RetriableCode = ReplyCode.mkchild( ProtoRetriableError )
- UnknownCode = ReplyCode.mkchild( ProtoUnknownError )
-
-
-
- net_private {
-
- class WriteAdapter
-
- def initialize( sock, mid )
- @sock = sock
- @mid = mid
- end
-
- def inspect
- "#<#{type}>"
- end
-
- def write( str )
- @sock.__send__ @mid, str
- end
-
- def <<( str )
- @sock.__send__ @mid, str
- self
- end
-
- end
-
- class ReadAdapter
-
- def initialize( block )
- @block = block
- end
-
- def inspect
- "#<#{type}>"
- end
-
- def <<( str )
- callblock( str, &@block ) if @block
- end
-
- private
-
- def callblock( str )
- begin
- user_break = true
- yield str
- user_break = false
- rescue Exception
- user_break = false
- raise
- ensure
- if user_break then
- @block = nil
- return # stop break
- end
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
end
+ msg
end
-
end
+ ##
+ # WriteTimeout, a subclass of Timeout::Error, is raised if a chunk of the
+ # response cannot be written within the write_timeout. Not raised on Windows.
-
- class Command
-
- def initialize( sock )
- @socket = sock
- @last_reply = nil
- @critical = false
+ class WriteTimeout < Timeout::Error
+ # :stopdoc:
+ def initialize(io = nil)
+ @io = io
end
+ attr_reader :io
- attr_accessor :socket
- attr_reader :last_reply
-
- def inspect
- "#<#{type}>"
- end
-
- # abstract quit
-
-
- private
-
- # abstract get_reply()
-
- def check_reply( *oks )
- @last_reply = get_reply
- reply_must( @last_reply, *oks )
- end
-
- def reply_must( rep, *oks )
- oks.each do |i|
- if i === rep then
- return rep
- end
+ def message
+ msg = super
+ if @io
+ msg = "#{msg} with #{@io.inspect}"
end
- rep.error!
- end
-
- def getok( line, expect = SuccessCode )
- @socket.writeline line
- check_reply expect
- end
-
-
- #
- # error handle
- #
-
- public
-
- def critical?
- @critical
- end
-
- def error_ok
- @critical = false
- end
-
-
- private
-
- def critical
- @critical = true
- ret = yield
- @critical = false
- ret
+ msg
end
-
- def begin_critical
- ret = @critical
- @critical = true
- not ret
- end
-
- def end_critical
- @critical = false
- end
-
end
- class Socket
-
- def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
- @addr = addr
- @port = port
-
- @read_timeout = rtime
-
- @debugout = dout
-
- @socket = nil
- @sending = ''
- @buffer = ''
-
- connect otime
- D 'opened'
+ class BufferedIO #:nodoc: internal use only
+ def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, debug_output: nil)
+ @io = io
+ @read_timeout = read_timeout
+ @write_timeout = write_timeout
+ @continue_timeout = continue_timeout
+ @debug_output = debug_output
+ @rbuf = ''.b
+ @rbuf_empty = true
+ @rbuf_offset = 0
end
- def connect( otime )
- D "opening connection to #{@addr}..."
- timeout( otime ) {
- @socket = TCPsocket.new( @addr, @port )
- }
- end
- private :connect
-
- attr :pipe, true
-
- class << self
- alias open new
- end
+ attr_reader :io
+ attr_accessor :read_timeout
+ attr_accessor :write_timeout
+ attr_accessor :continue_timeout
+ attr_accessor :debug_output
def inspect
- "#<#{type} #{closed? ? 'closed' : 'opened'}>"
+ "#<#{self.class} io=#{@io}>"
end
- def reopen( otime = nil )
- D 'reopening...'
- close
- connect otime
- D 'reopened'
- end
-
- attr :socket, true
-
- def close
- if @socket then
- @socket.close
- D 'closed'
- else
- D 'close call for already closed socket'
- end
- @socket = nil
- @buffer = ''
+ def eof?
+ @io.eof?
end
def closed?
- not @socket
- end
-
- def address
- @addr.dup
+ @io.closed?
end
- alias addr address
-
- attr_reader :port
-
- def ip_address
- @socket or return ''
- @socket.addr[3]
+ def close
+ @io.close
end
- alias ipaddr ip_address
-
- attr_reader :sending
-
-
#
- # read
+ # Read
#
public
- CRLF = "\r\n"
-
- def read( len, dest = '', ignerr = false )
- D_off "reading #{len} bytes..."
-
- rsize = 0
+ def read(len, dest = ''.b, ignore_eof = false)
+ LOG "reading #{len} bytes..."
+ read_bytes = 0
begin
- while rsize + @buffer.size < len do
- rsize += rbuf_moveto( dest, @buffer.size )
+ while read_bytes + rbuf_size < len
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
- rbuf_moveto dest, len - rsize
+ s = rbuf_consume(len - read_bytes)
+ read_bytes += s.bytesize
+ dest << s
rescue EOFError
- raise unless igneof
+ raise unless ignore_eof
end
-
- D_on "read #{len} bytes"
+ LOG "read #{read_bytes} bytes"
dest
end
- def read_all( dest = '' )
- D_off 'reading all...'
-
- rsize = 0
+ def read_all(dest = ''.b)
+ LOG 'reading all...'
+ read_bytes = 0
begin
- while true do
- rsize += rbuf_moveto( dest, @buffer.size )
+ while true
+ if s = rbuf_consume_all
+ read_bytes += s.bytesize
+ dest << s
+ end
rbuf_fill
end
rescue EOFError
;
end
-
- D_on "read #{rsize} bytes"
+ LOG "read #{read_bytes} bytes"
dest
end
- def readuntil( target, igneof = false )
- dest = ''
+ def readuntil(terminator, ignore_eof = false)
+ offset = @rbuf_offset
begin
- while true do
- idx = @buffer.index( target )
- break if idx
+ until idx = @rbuf.index(terminator, offset)
+ offset = @rbuf.bytesize
rbuf_fill
end
- rbuf_moveto dest, idx + target.size
+ return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
rescue EOFError
- raise unless igneof
- rbuf_moveto dest, @buffer.size
- end
- dest
- end
-
- def readline
- ret = readuntil( "\n" )
- ret.chop!
- ret
- end
-
- def read_pendstr( dest )
- D_off 'reading text...'
-
- rsize = 0
- while (str = readuntil("\r\n")) != ".\r\n" do
- rsize += str.size
- str.gsub!( /\A\./, '' )
- dest << str
+ raise unless ignore_eof
+ return rbuf_consume
end
-
- D_on "read #{rsize} bytes"
- dest
end
- # private use only (can not handle 'break')
- def read_pendlist
- D_off 'reading list...'
-
- str = nil
- i = 0
- while (str = readuntil("\r\n")) != ".\r\n" do
- i += 1
- str.chop!
- yield str
- end
-
- D_on "read #{i} items"
+ def readline
+ readuntil("\n").chop
end
-
private
-
- READ_SIZE = 1024 * 4
+ BUFSIZE = 1024 * 16
def rbuf_fill
- unless IO.select [@socket], nil, nil, @read_timeout then
- on_read_timeout
+ tmp = @rbuf_empty ? @rbuf : nil
+ case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
+ when String
+ @rbuf_empty = false
+ if rv.equal?(tmp)
+ @rbuf_offset = 0
+ else
+ @rbuf << rv
+ rv.clear
+ end
+ return
+ when :wait_readable
+ (io = @io.to_io).wait_readable(@read_timeout) or raise Net::ReadTimeout.new(io)
+ # continue looping
+ when :wait_writable
+ # OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
+ # http://www.openssl.org/support/faq.html#PROG10
+ (io = @io.to_io).wait_writable(@read_timeout) or raise Net::ReadTimeout.new(io)
+ # continue looping
+ when nil
+ raise EOFError, 'end of file reached'
+ end while true
+ end
+
+ def rbuf_flush
+ if @rbuf_empty
+ @rbuf.clear
+ @rbuf_offset = 0
end
- @buffer << @socket.sysread( READ_SIZE )
+ nil
end
- def on_read_timeout
- raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
+ def rbuf_size
+ @rbuf.bytesize - @rbuf_offset
end
- def rbuf_moveto( dest, len )
- bsi = @buffer.size
- s = @buffer[ 0, len ]
- dest << s
- @buffer = @buffer[ len, bsi - len ]
-
- @debugout << %<read "#{Net.quote s}"\n> if @debugout
- len
+ def rbuf_consume_all
+ rbuf_consume if rbuf_size > 0
end
+ def rbuf_consume(len = nil)
+ if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
+ s = @rbuf
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ elsif len.nil?
+ s = @rbuf.byteslice(@rbuf_offset..-1)
+ @rbuf = ''.b
+ @rbuf_offset = 0
+ @rbuf_empty = true
+ else
+ s = @rbuf.byteslice(@rbuf_offset, len)
+ @rbuf_offset += len
+ @rbuf_empty = @rbuf_offset == @rbuf.bytesize
+ rbuf_flush
+ end
+
+ @debug_output << %Q[-> #{s.dump}\n] if @debug_output
+ s
+ end
#
- # write interfece
+ # Write
#
public
- def write( str )
+ def write(*strs)
writing {
- do_write str
+ write0(*strs)
}
end
- def writeline( str )
+ alias << write
+
+ def writeline(str)
writing {
- do_write str + "\r\n"
+ write0 str + "\r\n"
}
end
- def write_bin( src, block )
- writing {
- if block then
- block.call ::Net::NetPrivate::WriteAdapter.new( self, :do_write )
- else
- src.each do |bin|
- do_write bin
+ private
+
+ def writing
+ @written_bytes = 0
+ @debug_output << '<- ' if @debug_output
+ yield
+ @debug_output << "\n" if @debug_output
+ bytes = @written_bytes
+ @written_bytes = nil
+ bytes
+ end
+
+ def write0(*strs)
+ @debug_output << strs.map(&:dump).join if @debug_output
+ orig_written_bytes = @written_bytes
+ strs.each_with_index do |str, i|
+ need_retry = true
+ case len = @io.write_nonblock(str, exception: false)
+ when Integer
+ @written_bytes += len
+ len -= str.bytesize
+ if len == 0
+ if strs.size == i+1
+ return @written_bytes - orig_written_bytes
+ else
+ need_retry = false
+ # next string
+ end
+ elsif len < 0
+ str = str.byteslice(len, -len)
+ else # len > 0
+ need_retry = false
+ # next string
end
- end
- }
+ # continue looping
+ when :wait_writable
+ (io = @io.to_io).wait_writable(@write_timeout) or raise Net::WriteTimeout.new(io)
+ # continue looping
+ end while need_retry
+ end
end
- def write_pendstr( src, block )
- D_off "writing text from #{src.type}"
+ #
+ # Logging
+ #
- wsize = use_each_crlf_line {
- if block then
- block.call ::Net::NetPrivate::WriteAdapter.new( self, :wpend_in )
- else
- wpend_in src
- end
- }
+ private
- D_on "wrote #{wsize} bytes text"
- wsize
+ def LOG_off
+ @save_debug_out = @debug_output
+ @debug_output = nil
end
+ def LOG_on
+ @debug_output = @save_debug_out
+ end
- private
+ def LOG(msg)
+ return unless @debug_output
+ @debug_output << msg + "\n"
+ end
+ end
- def wpend_in( src )
- line = nil
- pre = @writtensize
- each_crlf_line( src ) do |line|
- do_write '.' if line[0] == ?.
- do_write line
+ class InternetMessageIO < BufferedIO #:nodoc: internal use only
+ def initialize(*, **)
+ 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.delete_prefix('.')
end
+ LOG_on()
+ LOG "read message (#{read_bytes} bytes)"
+ end
- @writtensize - pre
+ # *library private* (cannot handle 'break')
+ def each_list_item
+ while (str = readuntil("\r\n")) != ".\r\n"
+ yield str.chop
+ end
end
- def use_each_crlf_line
- writing {
- @wbuf = ''
+ def write_message_0(src)
+ prev = @written_bytes
+ each_crlf_line(src) do |line|
+ write0 dot_stuff(line)
+ end
+ @written_bytes - prev
+ end
- yield
+ #
+ # Write
+ #
- if not @wbuf.empty? then # un-terminated last line
- if @wbuf[-1] == ?\r then
- @wbuf.chop!
- end
- @wbuf.concat "\r\n"
- do_write @wbuf
- elsif @writtensize == 0 then # empty src
- do_write "\r\n"
- end
- do_write ".\r\n"
+ 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
- @wbuf = nil
+ 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.method(:write_message_0)))
+ rescue LocalJumpError
+ # allow `break' from writer block
+ end
+ }
}
+ LOG_on()
+ LOG "wrote #{len} bytes"
+ len
end
- def each_crlf_line( src )
- str = m = beg = nil
+ private
- adding( src ) do
- beg = 0
- buf = @wbuf
- while buf.index( /\n|\r\n|\r/, beg ) do
- 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) ]
- str.concat "\r\n"
- yield str
- beg = m.end(0)
- end
- @wbuf = buf[ beg ... buf.size ]
+ def dot_stuff(s)
+ s.sub(/\A\./, '..')
+ end
+
+ def using_each_crlf_line
+ @wbuf = ''.b
+ yield
+ if not @wbuf.empty? # unterminated last line
+ write0 dot_stuff(@wbuf.chomp) + "\r\n"
+ elsif @written_bytes == 0 # empty src
+ write0 "\r\n"
end
+ write0 ".\r\n"
+ @wbuf = nil
end
- def adding( src )
- i = nil
+ def each_crlf_line(src)
+ buffer_filling(@wbuf, src) do
+ while line = @wbuf.slice!(/\A[^\r\n]*(?:\n|\r(?:\n|(?!\z)))/)
+ yield line.chomp("\n") + "\r\n"
+ end
+ end
+ end
+ def buffer_filling(buf, src)
case src
- when String
- 0.step( src.size - 1, 2048 ) do |i|
- @wbuf << src[i,2048]
+ when String # for speeding up.
+ 0.step(src.size - 1, 1024) do |i|
+ buf << src[i, 1024]
yield
end
-
- when File
- while true do
- i = src.read( 2048 )
- break unless i
- i[0,0] = @wbuf
- @wbuf = i
+ when File # for speeding up.
+ while s = src.read(1024)
+ buf << s
yield
end
-
- else
- src.each do |i|
- @wbuf << i
- if @wbuf.size > 2048 then
- yield
- end
+ else # generic reader
+ src.each do |str|
+ buf << str
+ yield if buf.size > 1024
end
- yield unless @wbuf.empty?
+ yield unless buf.empty?
end
end
+ end
- def writing
- @writtensize = 0
- @sending = ''
+ #
+ # The writer adapter class
+ #
+ class WriteAdapter
+ # :stopdoc:
+ def initialize(writer)
+ @writer = writer
+ end
- yield
+ def inspect
+ "#<#{self.class} writer=#{@writer.inspect}>"
+ end
- if @debugout then
- @debugout << 'write "'
- @debugout << @sending
- @debugout << "\"\n"
- end
- @socket.flush
- @writtensize
+ def write(str)
+ @writer.call(str)
end
- def do_write( arg )
- if @debugout or @sending.size < 128 then
- @sending << Net.quote( arg )
- else
- @sending << '...' unless @sending[-1] == ?.
- end
+ alias print write
- s = @socket.write( arg )
- @writtensize += s
- s
+ def <<(str)
+ write str
+ self
+ end
+
+ def puts(str = '')
+ write str.chomp("\n") + "\n"
end
+ def printf(*args)
+ write sprintf(*args)
+ end
+ end
- def D_off( msg )
- D msg
- @savedo, @debugout = @debugout, nil
+
+ class ReadAdapter #:nodoc: internal use only
+ def initialize(block)
+ @block = block
end
- def D_on( msg )
- @debugout = @savedo
- D msg
+ def inspect
+ "#<#{self.class}>"
end
- def D( msg )
- @debugout or return
- @debugout << msg
- @debugout << "\n"
+ def <<(str)
+ call_block(str, &@block) if @block
end
- 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)
+ yield str
+ end
+ end
- def Net.quote( str )
- str = str.gsub( "\n", '\\n' )
- str.gsub!( "\r", '\\r' )
- str.gsub!( "\t", '\\t' )
- str
+ module NetPrivate #:nodoc: obsolete
+ Socket = ::Net::InternetMessageIO
end
end # module Net