summaryrefslogtreecommitdiff
path: root/lib/net/session.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net/session.rb')
-rw-r--r--lib/net/session.rb565
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