diff options
Diffstat (limited to 'lib/net/session.rb')
-rw-r--r-- | lib/net/session.rb | 565 |
1 files changed, 430 insertions, 135 deletions
diff --git a/lib/net/session.rb b/lib/net/session.rb index b0977e7e14..7cf423ce3a 100644 --- a/lib/net/session.rb +++ b/lib/net/session.rb @@ -1,82 +1,197 @@ -# -# session.rb version 1.0.1 -# -# author: Minero Aoki <aamine@dp.u-netsurf.ne.jp> -# +=begin -require 'socket' += net/session.rb +written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> -class String +This library is distributed under the terms of the Ruby license. +You can freely distribute/modify this library. - def doquote - str = self.gsub( "\n", '\\n' ) - str.gsub!( "\r", '\\r' ) - str.gsub!( "\t", '\\t' ) - return str - end +=end -end +require 'socket' module Net - DEBUG = $DEBUG - # DEBUG = false + Version = '1.1.4' +=begin - class Session +== Net::Protocol - Version = '1.0.1' +the abstruct class for Internet protocol - def initialize( addr = 'localhost', port = nil ) - proto_initialize - @address = addr - @port = port if port - @active = false - end +=== Super Class + +Object + +=== Constants + +: Version + The version of Session class. It is a string like "1.1.4". + + +=== Class Methods + +: new( address = 'localhost', port = nil ) + This method Creates a new Session object. + +: start( address = 'localhost', port = nil, *args ) +: start( address = 'localhost', port = nil, *args ){|session| .... } + This method creates a new Session object and start session. + If you call this method with block, Session object give itself + to block and finish session when block returns. + +: Proxy( address, port ) + This method creates a proxy class of its protocol. + Arguments are address/port of proxy host. + + +=== Methods + +: address + the address of connecting server (FQDN). + +: port + connecting port number + +: start( *args ) + This method start protocol. If you call this method when the protocol + is already started, this only returns false without doing anything. + + '*args' are specified in subclasses. + +: 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 + + class Protocol class << self + def start( address = 'localhost', port = nil, *args ) - inst = new( address, port ) - ret = inst.start( *args ) + instance = new( address, port ) if iterator? then - ret = yield( inst ) - inst.finish + instance.start( *args ) { yield instance } + else + instance.start *args + instance end - return ret end + + def Proxy( p_addr, p_port ) + klass = Class.new( self ) + klass.module_eval %- + + def initialize( addr, port ) + @proxyaddr = '#{p_addr}' + @proxyport = '#{p_port}' + super @proxyaddr, @proxyport + @address = addr + @port = port + end + + def connect( addr, port ) + super @proxyaddr, @proxyport + end + private :connect + + attr :proxyaddr + attr :proxyport + - + def klass.proxy? + true + end + + klass + end + + def proxy? + false + end + + + private + + def protocol_param( name, val ) + module_eval %- + def self.#{name.id2name} + #{val} + 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::ProtocolSocket' + + + def initialize( addr = nil, port = nil ) + @address = addr || 'localhost' + @port = port || self.type.port + + @active = false + @pipe = nil + + @command = nil + @socket = nil end attr :address attr :port + attr :command attr :socket - attr :proto_type - attr :proto, true def start( *args ) return false if active? + @active = true - if ProtocolSocket === args[0] then - @socket = args.shift - else - @socket = ProtocolSocket.open( @address, @port ) + begin + connect @address, @port + do_start *args + yield if iterator? + ensure + finish if iterator? end - @proto = @proto_type.new( @socket ) - do_start( *args ) - - @active = true end def finish - if @proto then + if @command then do_finish - @proto = nil + disconnect + end + + if @socket and not @socket.closed? then + @socket.close + @socket = nil + end + + if active? then + @active = false return true else @@ -84,20 +199,72 @@ module Net end end - def active?() @active end + def active? + @active + end + + def set_pipe( arg ) # un-documented + @pipe = arg + end + + + private + + + def do_start + end + + def do_finish + end + + + def connect( addr, port ) + @socket = self.type.socket_type.open( addr, port, @pipe ) + @command = self.type.command_type.new( @socket ) + end + + def disconnect + @command.quit + @command = nil + @socket = nil + end end + Session = Protocol + + +=begin + +== Net::Command + +=== Super Class + +Object + +=== Class Methods + +: new( socket ) + This method create new Command object. 'socket' must be ProtocolSocket. + This method is abstract class. + + +=== Methods +: quit + This method dispatch command which ends the protocol. + +=end class Command def initialize( sock ) @socket = sock - check_reply( SuccessCode ) + @error_occured = false end attr :socket, true + attr :error_occured def quit if @socket and not @socket.closed? then @@ -107,21 +274,31 @@ module Net @socket.close unless @socket.closed? @socket = nil end + @error_occured = false end end private def check_reply( *oks ) - rep = get_reply + reply_must( get_reply, *oks ) + end + + def reply_must( rep, *oks ) oks.each do |i| if i === rep then return rep end end + @error_occured = true rep.error! @socket.sending end + + def getok( line, ok = SuccessCode ) + @socket.writeline line + check_reply ok + end end @@ -133,6 +310,7 @@ module Net class ProtoServerError < ProtocolError ; end class ProtoAuthError < ProtocolError ; end class ProtoCommandError < ProtocolError ; end + class ProtoRetryError < ProtocolError ; end class ReplyCode @@ -145,64 +323,157 @@ module Net attr :msg def error!( sending ) - err, tag = Errors[ self.type ] - mes = sprintf( <<MES, tag, @code, sending.doquote, @msg.doquote ) + mes = <<MES -%s: status %s +status %s writing string is: %s error message from server is: %s MES - raise err, mes + raise self.type::Error, + sprintf( mes, @code, Net.quote(sending), Net.quote(@msg) ) end end - class SuccessCode < ReplyCode ; end - class ContinueCode < SuccessCode ; end - class ErrorCode < ReplyCode ; end - class SyntaxErrorCode < ErrorCode ; end - class FatalErrorCode < ErrorCode ; end - class ServerBusyCode < ErrorCode ; end - class UnknownCode < ReplyCode ; end + class SuccessCode < ReplyCode + Error = ProtoUnknownError + end - class ReplyCode - Errors = { - SuccessCode => [ ProtoUnknownError, 'unknown error' ], - ContinueCode => [ ProtoUnknownError, 'unknown error' ], - ErrorCode => [ ProtocolError, 'protocol error' ], - SyntaxErrorCode => [ ProtoSyntaxError, 'syntax error' ], - FatalErrorCode => [ ProtoFatalError, 'fatal error' ], - ServerBusyCode => [ ProtoServerError, 'probably server busy' ], - UnknownCode => [ ProtoUnknownError, 'unknown error' ] - } + class ContinueCode < SuccessCode + Error = ProtoUnknownError + end + + class ErrorCode < ReplyCode + Error = ProtocolError + end + + class SyntaxErrorCode < ErrorCode + Error = ProtoSyntaxError + end + + class FatalErrorCode < ErrorCode + Error = ProtoFatalError + end + + class ServerBusyCode < ErrorCode + Error = ProtoServerError end + class RetryCode < ReplyCode + Error = ProtoRetryError + end + + class UnknownCode < ReplyCode + Error = ProtoUnknownError + end + + +=begin + +== Net::ProtocolSocket + +=== Super Class + +Object + +=== Class Methods + +: new( address = 'localhost', port = nil ) + This create new ProtocolSocket object, and connect to server. + + +=== Methods + +: close + This method closes socket. + +: address, addr + a FQDN address of server + +: ip_address, ipaddr + an IP address of server + +: port + connecting port number. + +: closed? + true if ProtocolSokcet have been closed already + + +: read( length ) + This method read 'length' bytes and return the string. + +: readuntil( target ) + This method read until find 'target'. Returns read string. + +: readline + read until "\r\n" and returns it without "\r\n". + +: read_pendstr + This method read until "\r\n.\r\n". + At the same time, delete period at line head and final line ("\r\n.\r\n"). + +: read_pendlist +: read_pendlist{|line| .... } + This method read until "\r\n.\r\n". This method resembles to 'read_pendstr', + but 'read_pendlist' don't check period at line head, and returns array which + each element is one line. + + When this method was called with block, evaluate it for each reading a line. + +: write( src ) + This method send 'src'. ProtocolSocket read strings from 'src' by 'each' + iterator. This method returns written bytes. + +: writebin( src ) + This method send 'src'. ProtocolSokcet read string from 'src' by 'each' + iterator. This method returns written bytes. + +: writeline( str ) + This method writes 'str'. There has not to be bare "\r" or "\n" in 'str'. + +: write_pendstr( src ) + This method writes 'src' as a mail. + ProtocolSocket reads strings from 'src' by 'each' iterator. + This returns written bytes. + +=end class ProtocolSocket - def initialize( addr, port ) - @address = addr - @port = port + def initialize( addr, port, pipe = nil ) + @addr = addr + @port = port + @pipe = pipe + @closed = true @ipaddr = '' - @closed = false @sending = '' @buffer = '' @socket = TCPsocket.new( addr, port ) + @closed = false @ipaddr = @socket.addr[3] - - @dout = Net::DEBUG end + attr :pipe, true + class << self alias open new end + def reopen + unless closed? then + @socket.close + @buffer = '' + end + @socket = TCPsocket.new( @addr, @port ) + end + attr :socket, true @@ -211,11 +482,21 @@ MES @closed = true end - def closed?() @closed end + def closed? + @closed + end + + def address + @addr.dup + end + alias addr address - def addr() @address.dup end - def port() @port end - def ipaddr() @ipaddr.dup end + attr :port + + def ip_address + @ipaddr.dup + end + alias ipaddr ip_address attr :sending @@ -226,16 +507,35 @@ MES def read( len, ret = '' ) - rsize = 0 + @pipe << "reading #{len} bytes...\n" if pre = @pipe ; @pipe = nil + rsize = 0 while rsize + @buffer.size < len do - rsize += @buffer.size - ret << fetch_rbuf( @buffer.size ) + rsize += writeinto( ret, @buffer.size ) fill_rbuf end - ret << fetch_rbuf( len - rsize ) + writeinto( ret, len - rsize ) - return ret + @pipe << "read #{len} bytes\n" if @pipe = pre + ret + end + + + def read_all( ret = '' ) + @pipe << "reading all...\n" if pre = @pipe; @pipe = nil + + rsize = 0 + begin + while true do + rsize += writeinto( ret, @buffer.size ) + fill_rbuf + end + rescue EOFError + ; + end + + @pipe << "read #{rsize} bytes\n" if @pipe = pre + ret end @@ -244,19 +544,21 @@ MES fill_rbuf end - return fetch_rbuf( idx + target.size ) + ret = '' + writeinto( ret, idx + target.size ) + ret end def readline ret = readuntil( CRLF ) ret.chop! - return ret + ret end def read_pendstr( dest = '' ) - $stderr.puts "reading pendstr" if pre = @dout ; @dout = false + @pipe << "reading text...\n" if pre = @pipe ; @pipe = nil rsize = 0 @@ -266,12 +568,14 @@ MES dest << str end - $stderr.puts "read pendstr #{rsize} bytes" if @dout = pre - return dest + @pipe << "read #{rsize} bytes\n" if @pipe = pre + dest end def read_pendlist + @pipe << "reading list...\n" if pre = @pipe ; @pipe = nil + arr = [] str = nil call = iterator? @@ -282,7 +586,8 @@ MES yield str if iterator? end - return arr + @pipe << "read #{arr.size} lines\n" if @pipe = pre + arr end @@ -295,22 +600,16 @@ MES @buffer << @socket.sysread( READ_BLOCK ) end - def fetch_rbuf( len ) + def writeinto( ret, len ) bsi = @buffer.size - ret = @buffer[ 0, len ] + ret << @buffer[ 0, len ] @buffer = @buffer[ len, bsi - len ] - if @dout then - $stderr.print 'read "' - debugout ret - $stderr.print "\"\n" - end - return ret + @pipe << %{read "#{Net.quote ret}"\n} if @pipe + len end - ### write - public @@ -319,7 +618,7 @@ MES each_crlf_line( src ) do |line| do_write_do line end - return do_write_fin + do_write_fin end @@ -328,7 +627,7 @@ MES src.each do |bin| do_write_do bin end - return do_write_fin + do_write_fin end @@ -336,13 +635,12 @@ MES do_write_beg do_write_do str do_write_do CRLF - return do_write_fin + do_write_fin end def write_pendstr( src ) - $stderr.puts "writing pendstr from #{src.type}" if pre = @dout - @dout = false + @pipe << "writing text from #{src.type}" if pre = @pipe ; @pipe = nil do_write_beg each_crlf_line( src ) do |line| @@ -352,8 +650,8 @@ MES do_write_do D_CRLF wsize = do_write_fin - $stderr.puts "wrote pendstr #{wsize} bytes" if @dout = pre - return wsize + @pipe << "wrote #{wsize} bytes text" if @pipe = pre + wsize end @@ -363,18 +661,18 @@ MES def each_crlf_line( src ) buf = '' beg = 0 - pos = nil + pos = s = bin = nil - src.each do |b| - buf << b + src.each do |bin| + buf << bin beg = 0 - while (pos = buf.index(TERMEXP, beg)) and (pos < buf.size - 2) do - pos += $&.size - tmp = buf[ beg, pos - beg ] - tmp.chop! - yield tmp << CRLF - beg = pos + while pos = buf.index( TERMEXP, beg ) do + s = $&.size + break if pos + s == buf.size - 1 and buf[-1] == ?\r + + yield buf[ beg, pos - beg ] << CRLF + beg = pos + s end buf = buf[ beg, buf.size - beg ] if beg != 0 end @@ -382,52 +680,49 @@ MES buf << "\n" unless /\n|\r/o === buf[-1,1] beg = 0 - while pos = buf.index(TERMEXP, beg) do - pos += $&.size - tmp = buf[ beg, pos - beg ] - tmp.chop! - yield tmp << CRLF - beg = pos + while pos = buf.index( TERMEXP, beg ) do + yield buf[ beg, pos - beg ] << CRLF + beg = pos + $&.size end end def do_write_beg - $stderr.print 'write "' if @dout - @writtensize = 0 @sending = '' end def do_write_do( arg ) - debugout arg if @dout - - if @sending.size < 128 then - @sending << arg + if @pipe or @sending.size < 128 then + @sending << Net.quote( arg ) else @sending << '...' unless @sending[-1] == ?. end + s = @socket.write( arg ) @writtensize += s - return s + s end def do_write_fin - $stderr.puts if @dout + if @pipe then + @pipe << 'write "' + @pipe << @sending + @pipe << "\"\n" + end @socket.flush - return @writtensize + @writtensize end + end - def debugout( ret ) - while ret and tmp = ret[ 0, 50 ] do - ret = ret[ 50, ret.size - 50 ] - tmp = tmp.inspect - $stderr.print tmp[ 1, tmp.size - 2 ] - end - end + def Net.quote( str ) + str = str.gsub( "\n", '\\n' ) + str.gsub!( "\r", '\\r' ) + str.gsub!( "\t", '\\t' ) + str end -end +end # module Net |