File: net/protocol.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: Net#24
has properties
constant: ProtocRetryError #46
  class: Protocol#26
inherits from
  Object ( Builtin-Module )
has properties
class method: protocol_param / 2 #28
  class: ProtocolError#38
inherits from
  StandardError ( Builtin-Module )
  class: ProtoSyntaxError#39
inherits from
  ProtocolError ( Net )
  class: ProtoFatalError#40
inherits from
  ProtocolError ( Net )
  class: ProtoUnknownError#41
inherits from
  ProtocolError ( Net )
  class: ProtoServerError#42
inherits from
  ProtocolError ( Net )
  class: ProtoAuthError#43
inherits from
  ProtocolError ( Net )
  class: ProtoCommandError#44
inherits from
  ProtocolError ( Net )
  class: ProtoRetriableError#45
inherits from
  ProtocolError ( Net )
  class: BufferedIO#49
inherits from
  Object ( Builtin-Module )
has properties
method: initialize / 1 #50
attribute: io [R] #57
attribute: read_timeout [RW] #58
attribute: debug_output [RW] #59
method: inspect #61
method: closed? #65
method: close #69
method: read / 3 #79
method: read_all / 1 #97
method: readuntil / 2 #113
method: readline #125
constant: BUFSIZE #131
method: rbuf_fill #133
method: rbuf_consume / 1 #139
method: write / 1 #151
method: writeline / 1 #157
method: writing #165
method: write0 / 1 #175
method: LOG_off #188
method: LOG_on #193
method: LOG / 1 #197
  class: InternetMessageIO#204
inherits from
  BufferedIO ( Net )
has properties
class method: old_open / 5 #205
method: initialize / 1 #215
method: each_message_chunk #224
method: each_list_item #237
method: write_message_0 / 1 #243
method: write_message / 1 #255
method: write_message_by_block / 1 #268
method: using_each_crlf_line #287
method: each_crlf_line / 1 #299
method: buffer_filling / 2 #307
  class: WriteAdapter#333
inherits from
  Object ( Builtin-Module )
has properties
method: initialize / 2 #334
method: inspect #339
method: write / 1 #343
alias: print write #347
method: << / 1 #349
method: puts / 1 #354
method: printf / 1 #358
  class: ReadAdapter#364
inherits from
  Object ( Builtin-Module )
has properties
method: initialize / 1 #365
method: inspect #369
method: << / 1 #373
method: call_block / 1 #382
  module: NetPrivate#388
has properties
constant: Socket #389

Class Hierarchy

Object ( Builtin-Module )
Exception ( Builtin-Module )
StandardError ( Builtin-Module )
ProtocolError ( Net ) — #38
  ProtoSyntaxError    #39
  ProtoFatalError    #40
  ProtoUnknownError    #41
  ProtoServerError    #42
  ProtoAuthError    #43
  ProtoCommandError    #44
  ProtoRetriableError    #45
Protocol ( Net ) — #26
BufferedIO ( Net ) — #49
  InternetMessageIO    #204
WriteAdapter ( Net ) — #333
ReadAdapter ( Net ) — #364

Code

   1  #
   2  # = net/protocol.rb
   3  #
   4  #--
   5  # Copyright (c) 1999-2005 Yukihiro Matsumoto
   6  # Copyright (c) 1999-2005 Minero Aoki
   7  #
   8  # written and maintained by Minero Aoki <aamine@loveruby.net>
   9  #
  10  # This program is free software. You can re-distribute and/or
  11  # modify this program under the same terms as Ruby itself,
  12  # Ruby Distribute License or GNU General Public License.
  13  #
  14  # $Id: protocol.rb 12092 2007-03-19 02:39:22Z aamine $
  15  #++
  16  #
  17  # WARNING: This file is going to remove.
  18  # Do not rely on the implementation written in this file.
  19  #
  20 
  21  require 'socket'
  22  require 'timeout'
  23 
  24  module Net # :nodoc:
  25 
  26    class Protocol   #:nodoc: internal use only
  27      private
  28      def Protocol.protocol_param(name, val)
  29        module_eval(<<-End, __FILE__, __LINE__ + 1)
  30          def #{name}
  31            #{val}
  32          end
  33        End
  34      end
  35    end
  36 
  37 
  38    class ProtocolError          < StandardError; end
  39    class ProtoSyntaxError       < ProtocolError; end
  40    class ProtoFatalError        < ProtocolError; end
  41    class ProtoUnknownError      < ProtocolError; end
  42    class ProtoServerError       < ProtocolError; end
  43    class ProtoAuthError         < ProtocolError; end
  44    class ProtoCommandError      < ProtocolError; end
  45    class ProtoRetriableError    < ProtocolError; end
  46    ProtocRetryError = ProtoRetriableError
  47 
  48 
  49    class BufferedIO   #:nodoc: internal use only
  50      def initialize(io)
  51        @io = io
  52        @read_timeout = 60
  53        @debug_output = nil
  54        @rbuf = ''
  55      end
  56 
  57      attr_reader :io
  58      attr_accessor :read_timeout
  59      attr_accessor :debug_output
  60 
  61      def inspect
  62        "#<#{self.class} io=#{@io}>"
  63      end
  64 
  65      def closed?
  66        @io.closed?
  67      end
  68 
  69      def close
  70        @io.close
  71      end
  72 
  73      #
  74      # Read
  75      #
  76 
  77      public
  78 
  79      def read(len, dest = '', ignore_eof = false)
  80        LOG "reading #{len} bytes..."
  81        read_bytes = 0
  82        begin
  83          while read_bytes + @rbuf.size < len
  84            dest << (s = rbuf_consume(@rbuf.size))
  85            read_bytes += s.size
  86            rbuf_fill
  87          end
  88          dest << (s = rbuf_consume(len - read_bytes))
  89          read_bytes += s.size
  90        rescue EOFError
  91          raise unless ignore_eof
  92        end
  93        LOG "read #{read_bytes} bytes"
  94        dest
  95      end
  96 
  97      def read_all(dest = '')
  98        LOG 'reading all...'
  99        read_bytes = 0
 100        begin
 101          while true
 102            dest << (s = rbuf_consume(@rbuf.size))
 103            read_bytes += s.size
 104            rbuf_fill
 105          end
 106        rescue EOFError
 107          ;
 108        end
 109        LOG "read #{read_bytes} bytes"
 110        dest
 111      end
 112 
 113      def readuntil(terminator, ignore_eof = false)
 114        begin
 115          until idx = @rbuf.index(terminator)
 116            rbuf_fill
 117          end
 118          return rbuf_consume(idx + terminator.size)
 119        rescue EOFError
 120          raise unless ignore_eof
 121          return rbuf_consume(@rbuf.size)
 122        end
 123      end
 124          
 125      def readline
 126        readuntil("\n").chop
 127      end
 128 
 129      private
 130 
 131      BUFSIZE = 1024 * 16
 132 
 133      def rbuf_fill
 134        timeout(@read_timeout) {
 135          @rbuf << @io.sysread(BUFSIZE)
 136        }
 137      end
 138 
 139      def rbuf_consume(len)
 140        s = @rbuf.slice!(0, len)
 141        @debug_output << %Q[-> #{s.dump}\n] if @debug_output
 142        s
 143      end
 144 
 145      #
 146      # Write
 147      #
 148 
 149      public
 150 
 151      def write(str)
 152        writing {
 153          write0 str
 154        }
 155      end
 156 
 157      def writeline(str)
 158        writing {
 159          write0 str + "\r\n"
 160        }
 161      end
 162 
 163      private
 164 
 165      def writing
 166        @written_bytes = 0
 167        @debug_output << '<- ' if @debug_output
 168        yield
 169        @debug_output << "\n" if @debug_output
 170        bytes = @written_bytes
 171        @written_bytes = nil
 172        bytes
 173      end
 174 
 175      def write0(str)
 176        @debug_output << str.dump if @debug_output
 177        len = @io.write(str)
 178        @written_bytes += len
 179        len
 180      end
 181 
 182      #
 183      # Logging
 184      #
 185 
 186      private
 187 
 188      def LOG_off
 189        @save_debug_out = @debug_output
 190        @debug_output = nil
 191      end
 192 
 193      def LOG_on
 194        @debug_output = @save_debug_out
 195      end
 196 
 197      def LOG(msg)
 198        return unless @debug_output
 199        @debug_output << msg + "\n"
 200      end
 201    end
 202 
 203 
 204    class InternetMessageIO < BufferedIO   #:nodoc: internal use only
 205      def InternetMessageIO.old_open(addr, port,
 206          open_timeout = nil, read_timeout = nil, debug_output = nil)
 207        debug_output << "opening connection to #{addr}...\n" if debug_output
 208        s = timeout(open_timeout) { TCPsocket.new(addr, port) }
 209        io = new(s)
 210        io.read_timeout = read_timeout
 211        io.debug_output = debug_output
 212        io
 213      end
 214 
 215      def initialize(io)
 216        super
 217        @wbuf = nil
 218      end
 219 
 220      #
 221      # Read
 222      #
 223 
 224      def each_message_chunk
 225        LOG 'reading message...'
 226        LOG_off()
 227        read_bytes = 0
 228        while (line = readuntil("\r\n")) != ".\r\n"
 229          read_bytes += line.size
 230          yield line.sub(/\A\./, '')
 231        end
 232        LOG_on()
 233        LOG "read message (#{read_bytes} bytes)"
 234      end
 235    
 236      # *library private* (cannot handle 'break')
 237      def each_list_item
 238        while (str = readuntil("\r\n")) != ".\r\n"
 239          yield str.chop
 240        end
 241      end
 242 
 243      def write_message_0(src)
 244        prev = @written_bytes
 245        each_crlf_line(src) do |line|
 246          write0 line.sub(/\A\./, '..')
 247        end
 248        @written_bytes - prev
 249      end
 250 
 251      #
 252      # Write
 253      #
 254 
 255      def write_message(src)
 256        LOG "writing message from #{src.class}"
 257        LOG_off()
 258        len = writing {
 259          using_each_crlf_line {
 260            write_message_0 src
 261          }
 262        }
 263        LOG_on()
 264        LOG "wrote #{len} bytes"
 265        len
 266      end
 267 
 268      def write_message_by_block(&block)
 269        LOG 'writing message from block'
 270        LOG_off()
 271        len = writing {
 272          using_each_crlf_line {
 273            begin
 274              block.call(WriteAdapter.new(self, :write_message_0))
 275            rescue LocalJumpError
 276              # allow `break' from writer block
 277            end
 278          }
 279        }
 280        LOG_on()
 281        LOG "wrote #{len} bytes"
 282        len
 283      end
 284 
 285      private
 286 
 287      def using_each_crlf_line
 288        @wbuf = ''
 289        yield
 290        if not @wbuf.empty?   # unterminated last line
 291          write0 @wbuf.chomp + "\r\n"
 292        elsif @written_bytes == 0   # empty src
 293          write0 "\r\n"
 294        end
 295        write0 ".\r\n"
 296        @wbuf = nil
 297      end
 298 
 299      def each_crlf_line(src)
 300        buffer_filling(@wbuf, src) do
 301          while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n)
 302            yield line.chomp("\n") + "\r\n"
 303          end
 304        end
 305      end
 306 
 307      def buffer_filling(buf, src)
 308        case src
 309        when String    # for speeding up.
 310          0.step(src.size - 1, 1024) do |i|
 311            buf << src[i, 1024]
 312            yield
 313          end
 314        when File    # for speeding up.
 315          while s = src.read(1024)
 316            buf << s
 317            yield
 318          end
 319        else    # generic reader
 320          src.each do |s|
 321            buf << s
 322            yield if buf.size > 1024
 323          end
 324          yield unless buf.empty?
 325        end
 326      end
 327    end
 328 
 329 
 330    #
 331    # The writer adapter class
 332    #
 333    class WriteAdapter
 334      def initialize(socket, method)
 335        @socket = socket
 336        @method_id = method
 337      end
 338 
 339      def inspect
 340        "#<#{self.class} socket=#{@socket.inspect}>"
 341      end
 342 
 343      def write(str)
 344        @socket.__send__(@method_id, str)
 345      end
 346 
 347      alias print write
 348 
 349      def <<(str)
 350        write str
 351        self
 352      end
 353 
 354      def puts(str = '')
 355        write str.chomp("\n") + "\n"
 356      end
 357 
 358      def printf(*args)
 359        write sprintf(*args)
 360      end
 361    end
 362 
 363 
 364    class ReadAdapter   #:nodoc: internal use only
 365      def initialize(block)
 366        @block = block
 367      end
 368 
 369      def inspect
 370        "#<#{self.class}>"
 371      end
 372 
 373      def <<(str)
 374        call_block(str, &@block) if @block
 375      end
 376 
 377      private
 378 
 379      # This method is needed because @block must be called by yield,
 380      # not Proc#call.  You can see difference when using `break' in
 381      # the block.
 382      def call_block(str)
 383        yield str
 384      end
 385    end
 386 
 387 
 388    module NetPrivate   #:nodoc: obsolete
 389      Socket = ::Net::InternetMessageIO
 390    end
 391 
 392  end   # module Net