lib/net/protocol.rb


DEFINITIONS

This source file includes following functions.


   1  =begin
   2  
   3  = net/protocol.rb
   4  
   5  Copyright (c) 1999-2002 Yukihiro Matsumoto
   6  
   7  written & maintained by Minero Aoki <aamine@loveruby.net>
   8  
   9  This program is free software. You can re-distribute and/or
  10  modify this program under the same terms as Ruby itself,
  11  Ruby Distribute License or GNU General Public License.
  12  
  13  NOTE: You can find Japanese version of this document in
  14  the doc/net directory of the standard ruby interpreter package.
  15  
  16  $Id: protocol.rb,v 1.63 2002/07/11 08:22:16 matz Exp $
  17  
  18  =end
  19  
  20  require 'socket'
  21  require 'timeout'
  22  
  23  
  24  module Net
  25  
  26    class Protocol
  27  
  28      Version = '1.2.3'
  29      Revision = %q$Revision: 1.63 $.split(/\s+/)[1]
  30  
  31  
  32      class << self
  33  
  34        def port
  35          default_port
  36        end
  37  
  38        private
  39  
  40        def protocol_param( name, val )
  41          module_eval <<-End, __FILE__, __LINE__ + 1
  42              def self.#{name.id2name}
  43                #{val}
  44              end
  45          End
  46        end
  47          
  48      end
  49  
  50  
  51      #
  52      # --- Configuration Staffs for Sub Classes ---
  53      #
  54      #   class method default_port
  55      #   class method command_type
  56      #   class method socket_type
  57      #
  58      #   private method do_start
  59      #   private method do_finish
  60      #
  61      #   private method conn_address
  62      #   private method conn_port
  63      #
  64  
  65  
  66      def Protocol.start( address, port = nil, *args )
  67        instance = new(address, port)
  68  
  69        if block_given? then
  70          instance.start(*args) { return yield(instance) }
  71        else
  72          instance.start(*args)
  73          instance
  74        end
  75      end
  76  
  77      def initialize( addr, port = nil )
  78        @address = addr
  79        @port    = port || self.class.default_port
  80  
  81        @command = nil
  82        @socket  = nil
  83  
  84        @started = false
  85  
  86        @open_timeout = 30
  87        @read_timeout = 60
  88  
  89        @debug_output = nil
  90      end
  91  
  92      attr_reader :address
  93      attr_reader :port
  94  
  95      attr_reader :command
  96      attr_reader :socket
  97  
  98      attr_accessor :open_timeout
  99  
 100      attr_reader :read_timeout
 101  
 102      def read_timeout=( sec )
 103        @socket.read_timeout = sec if @socket
 104        @read_timeout = sec
 105      end
 106  
 107      def started?
 108        @started
 109      end
 110  
 111      alias active? started?
 112  
 113      def set_debug_output( arg )   # un-documented
 114        @debug_output = arg
 115      end
 116  
 117      def inspect
 118        "#<#{self.class} #{@address}:#{@port} open=#{active?}>"
 119      end
 120  
 121      #
 122      # open
 123      #
 124  
 125      def start( *args )
 126        @started and raise IOError, 'protocol has been opened already'
 127  
 128        if block_given? then
 129          begin
 130            do_start( *args )
 131            @started = true
 132            return yield(self)
 133          ensure
 134            finish if @started
 135          end
 136        end
 137  
 138        do_start( *args )
 139        @started = true
 140        self
 141      end
 142  
 143      private
 144  
 145      # abstract do_start()
 146  
 147      def conn_socket
 148        @socket = self.class.socket_type.open(
 149                conn_address(), conn_port(),
 150                @open_timeout, @read_timeout, @debug_output )
 151        on_connect
 152      end
 153  
 154      alias conn_address address
 155      alias conn_port    port
 156  
 157      def reconn_socket
 158        @socket.reopen @open_timeout
 159        on_connect
 160      end
 161  
 162      def conn_command
 163        @command = self.class.command_type.new(@socket)
 164      end
 165  
 166      def on_connect
 167      end
 168  
 169      #
 170      # close
 171      #
 172  
 173      public
 174  
 175      def finish
 176        @started or raise IOError, 'closing already closed protocol'
 177        do_finish
 178        @started = false
 179        nil
 180      end
 181  
 182      private
 183  
 184      # abstract do_finish()
 185  
 186      def disconn_command
 187        @command.quit if @command and not @command.critical?
 188        @command = nil
 189      end
 190  
 191      def disconn_socket
 192        if @socket and not @socket.closed? then
 193          @socket.close
 194        end
 195        @socket = nil
 196      end
 197      
 198    end
 199  
 200    Session = Protocol
 201  
 202  
 203    class Response
 204  
 205      def initialize( ctype, code, msg )
 206        @code_type = ctype
 207        @code      = code
 208        @message   = msg
 209        super()
 210      end
 211  
 212      attr_reader :code_type
 213      attr_reader :code
 214      attr_reader :message
 215      alias msg message
 216  
 217      def inspect
 218        "#<#{self.class} #{@code}>"
 219      end
 220  
 221      def error!
 222        raise error_type().new(code + ' ' + @message.dump, self)
 223      end
 224  
 225      def error_type
 226        @code_type.error_type
 227      end
 228  
 229    end
 230  
 231  
 232    class ProtocolError          < StandardError; end
 233    class ProtoSyntaxError       < ProtocolError; end
 234    class ProtoFatalError        < ProtocolError; end
 235    class ProtoUnknownError      < ProtocolError; end
 236    class ProtoServerError       < ProtocolError; end
 237    class ProtoAuthError         < ProtocolError; end
 238    class ProtoCommandError      < ProtocolError; end
 239    class ProtoRetriableError    < ProtocolError; end
 240    ProtocRetryError = ProtoRetriableError
 241  
 242    class ProtocolError
 243    
 244      def initialize( msg, resp )
 245        super msg
 246        @response = resp
 247      end
 248  
 249      attr_reader :response
 250      alias data response
 251  
 252      def inspect
 253        "#<#{self.class} #{self.message}>"
 254      end
 255    
 256    end
 257  
 258  
 259    class Code
 260  
 261      def initialize( paren, err )
 262        @parents = [self] + paren
 263        @error_type = err
 264      end
 265  
 266      def parents
 267        @parents.dup
 268      end
 269  
 270      attr_reader :error_type
 271  
 272      def inspect
 273        "#<#{self.class} #{sprintf '0x%x', __id__}>"
 274      end
 275  
 276      def ===( response )
 277        response.code_type.parents.each {|c| c == self and return true }
 278        false
 279      end
 280  
 281      def mkchild( err = nil )
 282        self.class.new(@parents, err || @error_type)
 283      end
 284    
 285    end
 286    
 287    ReplyCode       = Code.new( [], ProtoUnknownError )
 288    InformationCode = ReplyCode.mkchild( ProtoUnknownError )
 289    SuccessCode     = ReplyCode.mkchild( ProtoUnknownError )
 290    ContinueCode    = ReplyCode.mkchild( ProtoUnknownError )
 291    ErrorCode       = ReplyCode.mkchild( ProtocolError )
 292    SyntaxErrorCode = ErrorCode.mkchild( ProtoSyntaxError )
 293    FatalErrorCode  = ErrorCode.mkchild( ProtoFatalError )
 294    ServerErrorCode = ErrorCode.mkchild( ProtoServerError )
 295    AuthErrorCode   = ErrorCode.mkchild( ProtoAuthError )
 296    RetriableCode   = ReplyCode.mkchild( ProtoRetriableError )
 297    UnknownCode     = ReplyCode.mkchild( ProtoUnknownError )
 298  
 299  
 300    class Command
 301  
 302      def initialize( sock )
 303        @socket = sock
 304        @last_reply = nil
 305        @atomic = false
 306      end
 307  
 308      attr_accessor :socket
 309      attr_reader :last_reply
 310  
 311      def inspect
 312        "#<#{self.class} socket=#{@socket.inspect} critical=#{@atomic}>"
 313      end
 314  
 315      # abstract quit()
 316  
 317      private
 318  
 319      def check_reply( *oks )
 320        @last_reply = get_reply()
 321        reply_must @last_reply, *oks
 322      end
 323  
 324      # abstract get_reply()
 325  
 326      def reply_must( rep, *oks )
 327        oks.each do |i|
 328          return rep if i === rep
 329        end
 330        rep.error!
 331      end
 332  
 333      def getok( line, expect = SuccessCode )
 334        @socket.writeline line
 335        check_reply expect
 336      end
 337  
 338      #
 339      # critical session
 340      #
 341  
 342      public
 343  
 344      def critical?
 345        @atomic
 346      end
 347  
 348      def error_ok
 349        @atomic = false
 350      end
 351  
 352      private
 353  
 354      def atomic
 355        @atomic = true
 356        ret = yield
 357        @atomic = false
 358        ret
 359      end
 360  
 361    end
 362  
 363  
 364    class InternetMessageIO
 365  
 366      class << self
 367        alias open new
 368      end
 369  
 370      def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
 371        @address      = addr
 372        @port         = port
 373        @read_timeout = rtime
 374        @debug_output = dout
 375  
 376        @socket  = nil
 377        @rbuf    = nil
 378  
 379        connect otime
 380        D 'opened'
 381      end
 382  
 383      attr_reader :address
 384      attr_reader :port
 385  
 386      def ip_address
 387        @socket or return ''
 388        @socket.addr[3]
 389      end
 390  
 391      attr_accessor :read_timeout
 392  
 393      attr_reader :socket
 394  
 395      def connect( otime )
 396        D "opening connection to #{@address}..."
 397        timeout( otime ) {
 398            @socket = TCPSocket.new( @address, @port )
 399        }
 400        @rbuf = ''
 401      end
 402      private :connect
 403  
 404      def close
 405        if @socket then
 406          @socket.close
 407          D 'closed'
 408        else
 409          D 'close call for already closed socket'
 410        end
 411        @socket = nil
 412        @rbuf = ''
 413      end
 414  
 415      def reopen( otime = nil )
 416        D 'reopening...'
 417        close
 418        connect otime
 419        D 'reopened'
 420      end
 421  
 422      def closed?
 423        not @socket
 424      end
 425  
 426      def inspect
 427        "#<#{type} #{closed? ? 'closed' : 'opened'}>"
 428      end
 429  
 430      ###
 431      ###  READ
 432      ###
 433  
 434      public
 435  
 436      def read( len, dest = '', ignore = false )
 437        D_off "reading #{len} bytes..."
 438  
 439        rsize = 0
 440        begin
 441          while rsize + @rbuf.size < len do
 442            rsize += rbuf_moveto(dest, @rbuf.size)
 443            rbuf_fill
 444          end
 445          rbuf_moveto dest, len - rsize
 446        rescue EOFError
 447          raise unless ignore
 448        end
 449  
 450        D_on "read #{len} bytes"
 451        dest
 452      end
 453  
 454      def read_all( dest = '' )
 455        D_off 'reading all...'
 456  
 457        rsize = 0
 458        begin
 459          while true do
 460            rsize += rbuf_moveto(dest, @rbuf.size)
 461            rbuf_fill
 462          end
 463        rescue EOFError
 464          ;
 465        end
 466  
 467        D_on "read #{rsize} bytes"
 468        dest
 469      end
 470  
 471      def readuntil( target, ignore = false )
 472        dest = ''
 473        begin
 474          while true do
 475            idx = @rbuf.index(target)
 476            break if idx
 477            rbuf_fill
 478          end
 479          rbuf_moveto dest, idx + target.size
 480        rescue EOFError
 481          raise unless ignore
 482          rbuf_moveto dest, @rbuf.size
 483        end
 484        dest
 485      end
 486          
 487      def readline
 488        ret = readuntil("\n")
 489        ret.chop!
 490        ret
 491      end
 492  
 493      private
 494  
 495      BLOCK_SIZE = 1024
 496  
 497      def rbuf_fill
 498        until IO.select [@socket], nil, nil, @read_timeout do
 499          on_read_timeout
 500        end
 501        @rbuf << @socket.sysread(BLOCK_SIZE)
 502      end
 503  
 504      def on_read_timeout
 505        raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
 506      end
 507  
 508      def rbuf_moveto( dest, len )
 509        dest << (s = @rbuf.slice!(0, len))
 510        @debug_output << %Q[-> #{s.dump}\n] if @debug_output
 511        len
 512      end
 513  
 514      #
 515      # message read
 516      #
 517  
 518      public
 519  
 520      def read_message_to( dest )
 521        D_off 'reading text...'
 522  
 523        rsize = 0
 524        while (str = readuntil("\r\n")) != ".\r\n" do
 525          rsize += str.size
 526          dest << str.sub(/\A\./, '')
 527        end
 528  
 529        D_on "read #{rsize} bytes"
 530        dest
 531      end
 532    
 533      # private use only (cannot handle 'break')
 534      def each_list_item
 535        while (str = readuntil("\r\n")) != ".\r\n" do
 536          yield str.chop
 537        end
 538      end
 539  
 540  
 541      ###
 542      ###  WRITE
 543      ###
 544  
 545      #
 546      # basic write
 547      #
 548  
 549      public
 550  
 551      def write( str )
 552        writing {
 553            do_write str
 554        }
 555      end
 556  
 557      def writeline( str )
 558        writing {
 559            do_write str + "\r\n"
 560        }
 561      end
 562  
 563      private
 564  
 565      def writing
 566        @writtensize = 0
 567        @debug_output << '<- ' if @debug_output
 568        yield
 569        @socket.flush
 570        @debug_output << "\n" if @debug_output
 571        @writtensize
 572      end
 573  
 574      def do_write( str )
 575        @debug_output << str.dump if @debug_output
 576        @writtensize += (n = @socket.write(str))
 577        n
 578      end
 579  
 580      #
 581      # message write
 582      #
 583  
 584      public
 585  
 586      def write_message( src )
 587        D_off "writing text from #{src.type}"
 588  
 589        wsize = using_each_crlf_line {
 590            wpend_in src
 591        }
 592  
 593        D_on "wrote #{wsize} bytes text"
 594        wsize
 595      end
 596  
 597      def through_message
 598        D_off 'writing text from block'
 599  
 600        wsize = using_each_crlf_line {
 601            yield WriteAdapter.new(self, :wpend_in)
 602        }
 603  
 604        D_on "wrote #{wsize} bytes text"
 605        wsize
 606      end
 607  
 608      private
 609  
 610      def wpend_in( src )
 611        line = nil
 612        pre = @writtensize
 613        each_crlf_line( src ) do |line|
 614          do_write '.' if line[0] == ?.
 615          do_write line
 616        end
 617  
 618        @writtensize - pre
 619      end
 620  
 621      def using_each_crlf_line
 622        writing {
 623            @wbuf = ''
 624  
 625            yield
 626  
 627            if not @wbuf.empty? then       # unterminated last line
 628              if @wbuf[-1] == ?\r then
 629                @wbuf.chop!
 630              end
 631              @wbuf.concat "\r\n"
 632              do_write @wbuf
 633            elsif @writtensize == 0 then   # empty src
 634              do_write "\r\n"
 635            end
 636            do_write ".\r\n"
 637  
 638            @wbuf = nil
 639        }
 640      end
 641  
 642      def each_crlf_line( src )
 643        str = m = beg = nil
 644  
 645        adding( src ) do
 646          beg = 0
 647          buf = @wbuf
 648          while buf.index( /\n|\r\n|\r/, beg ) do
 649            m = Regexp.last_match
 650            if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then
 651              # "...\r" : can follow "\n..."
 652              break
 653            end
 654            str = buf[ beg ... m.begin(0) ]
 655            str.concat "\r\n"
 656            yield str
 657            beg = m.end(0)
 658          end
 659          @wbuf = buf[ beg ... buf.size ]
 660        end
 661      end
 662  
 663      def adding( src )
 664        i = nil
 665  
 666        case src
 667        when String
 668          0.step( src.size - 1, 2048 ) do |i|
 669            @wbuf << src[i,2048]
 670            yield
 671          end
 672  
 673        when File
 674          while true do
 675            i = src.read(2048)
 676            break unless i
 677            i[0,0] = @wbuf
 678            @wbuf = i
 679            yield
 680          end
 681  
 682        else
 683          src.each do |i|
 684            @wbuf << i
 685            if @wbuf.size > 2048 then
 686              yield
 687            end
 688          end
 689          yield unless @wbuf.empty?
 690        end
 691      end
 692  
 693      ###
 694      ### DEBUG
 695      ###
 696  
 697      private
 698  
 699      def D_off( msg )
 700        D msg
 701        @savedo, @debug_output = @debug_output, nil
 702      end
 703  
 704      def D_on( msg )
 705        @debug_output = @savedo
 706        D msg
 707      end
 708  
 709      def D( msg )
 710        @debug_output or return
 711        @debug_output << msg
 712        @debug_output << "\n"
 713      end
 714    
 715    end
 716  
 717  
 718    class WriteAdapter
 719  
 720      def initialize( sock, mid )
 721        @socket = sock
 722        @mid = mid
 723      end
 724  
 725      def inspect
 726        "#<#{type} socket=#{@socket.inspect}>"
 727      end
 728  
 729      def write( str )
 730        @socket.__send__ @mid, str
 731      end
 732  
 733      alias print write
 734  
 735      def <<( str )
 736        write str
 737        self
 738      end
 739  
 740      def puts( str = '' )
 741        write str.sub(/\n?/, "\n")
 742      end
 743  
 744      def printf( *args )
 745        write sprintf(*args)
 746      end
 747    
 748    end
 749  
 750  
 751    class ReadAdapter
 752  
 753      def initialize( block )
 754        @block = block
 755      end
 756  
 757      def inspect
 758        "#<#{type}>"
 759      end
 760  
 761      def <<( str )
 762        call_block str, &@block if @block
 763      end
 764  
 765      private
 766  
 767      def call_block( str )
 768        yield str
 769      end
 770    
 771    end
 772  
 773  
 774    # for backward compatibility
 775    module NetPrivate
 776      Response = ::Net::Response
 777      Command = ::Net::Command
 778      Socket = ::Net::InternetMessageIO
 779      BufferedSocket = ::Net::InternetMessageIO
 780      WriteAdapter = ::Net::WriteAdapter
 781      ReadAdapter = ::Net::ReadAdapter
 782    end
 783    BufferedSocket = ::Net::InternetMessageIO
 784  
 785  end   # module Net