Net version 1.1.3

o  http.rb rd
o  Session -> Protocol


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@596 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 1999-12-29 11:14:04 +00:00
parent 0c00050f84
commit a1d1b15167
4 changed files with 419 additions and 372 deletions

View File

@ -5,6 +5,9 @@
maintained by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This file is derived from http-access.rb
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
=end
require 'net/session'
@ -17,253 +20,296 @@ class HTTPError < ProtocolError; end
class HTTPBadResponse < HTTPError; end
class HTTPSession < Session
=begin
Version = '1.1.2'
= HTTP class
session_setvar :port, '80'
session_setvar :command_type, 'Net::HTTPCommand'
== Class Methods
def get( path = '/', header = nil, ret = '' )
confirm_connection
@proto.get edit_path(path), header, ret
end
def head( path = '/', header = nil )
confirm_connection
@proto.head edit_path(path), header
end
private
def confirm_connection
if @socket.closed? then
@socket.reopen
end
end
: new( address, port = 80 )
def do_finish
unless @proto.error_occured or @socket.closed? then
head '/', { 'Connection' => 'Close' }
create new HTTP object.
: port
returns HTTP default port, 80
: command_type
returns Command class, HTTPCommand
== Methods
: get( path, header = nil, ret = '' )
get data from "path" on connecting host.
"header" is a Hash like { 'Accept' => '*/*', ... }.
The data will be written to "ret" using "<<" method.
This method returns response header (Hash) and "ret".
: head( path, header = nil )
get only header from "path" on connecting host.
"header" is a Hash like { 'Accept' => '*/*', ... }.
This method returns header as a Hash like
{ 'content-length' => 'Content-Length: 2554',
'content-type' => 'Content-Type: text/html',
... }
=end
class HTTP < Protocol
Version = '1.1.3'
protocol_param :port, '80'
protocol_param :command_type, '::Net::HTTPCommand'
def get( path, u_header = nil, ret = '' )
header = connecting {
@command.get ret, edit_path(path), u_header
}
return header, ret
end
def head( path, u_header = nil )
connecting {
@command.head edit_path(path), u_header
}
end
end
def edit_path( path )
path
end
private
class << self
def Proxy( addr, port )
klass = super
klass.module_eval %-
def edit_path( path )
'http://' + address + (port == self.port ? '' : ":\#{port}") + path
def connecting
if @socket.closed? then
@socket.reopen
end
header = yield
@socket.close unless keep_alive? header
header
end
def keep_alive?( header )
if str = header[ 'connection' ] then
if /\Aconnection:\s*keep-alive/i === str then
return true
end
else
if @http_version == '1.1' then
return true
end
-
klass
end
end
end
HTTP = HTTPSession
class HTTPCommand < Command
HTTPVersion = '1.1'
def initialize( sock )
@http_version = HTTPVersion
@in_header = {}
@in_header[ 'Host' ] = sock.addr
#@in_header[ 'User-Agent' ] = "Ruby http version #{HTTPSession::Version}"
@in_header[ 'Connection' ] = 'keep-alive'
@in_header[ 'Accept' ] = '*/*'
super sock
end
attr :http_version
def get( path, u_header = nil, ret = '' )
header = get_response(
sprintf( 'GET %s HTTP/%s', path, HTTPVersion ), u_header )
if chunked? header then
clen = read_chunked_body( ret )
header.delete 'transfer-encoding'
header[ 'content-length' ] = "Content-Length: #{clen}"
else
@socket.read content_length( header ), ret
end
@socket.close unless keep_alive? header
return header, ret
end
def head( path, u_header = nil )
header = get_response(
sprintf( 'HEAD %s HTTP/%s', path, HTTPVersion ), u_header )
@socket.close unless keep_alive? header
header
end
# def put
# def delete
# def trace
# def options
private
def do_quit
unless @socket.closed? then
@socket.close
end
end
def get_response( line, u_header )
@socket.writeline line
write_header u_header
rep = get_reply
header = read_header
reply_must rep, SuccessCode
header
end
def get_reply
str = @socket.readline
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
raise HTTPBadResponse, "wrong status line format: #{str}"
end
@http_version = $1
status = $2
discrip = $3
klass = case status[0]
when ?1 then
case status[2]
when ?0 then ContinueCode
when ?1 then SuccessCode
else UnknownCode
end
when ?2 then SuccessCode
when ?3 then RetryCode
when ?4 then ServerBusyCode
when ?5 then FatalErrorCode
else UnknownCode
end
klass.new( status, discrip )
end
def content_length( header )
unless str = header[ 'content-length' ] then
raise HTTPBadResponce, "content-length not given"
end
unless /\Acontent-length:\s*(\d+)/i === str then
raise HTTPBadResponce, "content-length format error"
end
$1.to_i
end
def keep_alive?( header )
if str = header[ 'connection' ] then
if /\Aconnection:\s*keep-alive/i === str then
return true
end
else
if @http_version == '1.1' then
return true
end
end
false
end
def chunked?( header )
if str = header[ 'transfer-encoding' ] then
if /\Atransfer-encoding:\s*chunked/i === str then
return true
end
end
false
end
def read_header
header = {}
while true do
line = @socket.readline
break if line.empty?
/\A[^:]+/ === line
nm = $&
nm.strip!
nm.downcase!
header[ nm ] = line
end
header
end
def write_header( user )
if user then
header = @in_header.dup.update user
else
header = @in_header
end
header.each do |n,v|
@socket.writeline n + ': ' + v
end
@socket.writeline ''
if tmp = header['Connection'] then
/close/i === tmp
else
false
end
end
def read_chunked_body( ret )
line = nil
len = nil
total = 0
while true do
line = @socket.readline
unless /[0-9a-hA-H]+/ === line then
raise HTTPBadResponce, "chunk size not given"
def do_finish
unless @command.error_occured or @socket.closed? then
head '/', { 'Connection' => 'Close' }
end
end
def edit_path( path )
path
end
class << self
def Proxy( p_addr, p_port )
klass = super
klass.module_eval %-
def edit_path( path )
'http://' + address +
(@port == #{self.port} ? '' : ':' + @port.to_s) + path
end
-
klass
end
len = $&.hex
break if len == 0
@socket.read( len, ret ); total += len
@socket.read 2 # \r\n
end
while true do
line = @socket.readline
break if line.empty?
end
total
end
end
HTTPSession = HTTP
class HTTPCommand < Command
HTTPVersion = '1.1'
def initialize( sock )
@http_version = HTTPVersion
@in_header = {}
@in_header[ 'Host' ] = sock.addr
@in_header[ 'Connection' ] = 'keep-alive'
@in_header[ 'Accept' ] = '*/*'
super sock
end
attr :http_version
def get( ret, path, u_header = nil )
header = get_response(
sprintf( 'GET %s HTTP/%s', path, HTTPVersion ), u_header )
if chunked? header then
clen = read_chunked_body( ret )
header.delete 'transfer-encoding'
header[ 'content-length' ] = "Content-Length: #{clen}"
else
@socket.read content_length( header ), ret
end
header
end
def head( path, u_header = nil )
get_response sprintf( 'HEAD %s HTTP/%s', path, HTTPVersion ), u_header
end
# def put
# def delete
# def trace
# def options
private
def do_quit
unless @socket.closed? then
@socket.close
end
end
def get_response( line, u_header )
@socket.writeline line
write_header u_header
rep = get_reply
header = read_header
reply_must rep, SuccessCode
header
end
def get_reply
str = @socket.readline
unless /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i === str then
raise HTTPBadResponse, "wrong status line format: #{str}"
end
@http_version = $1
status = $2
discrip = $3
klass = case status[0]
when ?1 then
case status[2]
when ?0 then ContinueCode
when ?1 then SuccessCode
else UnknownCode
end
when ?2 then SuccessCode
when ?3 then RetryCode
when ?4 then ServerBusyCode
when ?5 then FatalErrorCode
else UnknownCode
end
klass.new( status, discrip )
end
def content_length( header )
unless str = header[ 'content-length' ] then
raise HTTPBadResponce, "content-length not given"
end
unless /\Acontent-length:\s*(\d+)/i === str then
raise HTTPBadResponce, "content-length format error"
end
$1.to_i
end
def chunked?( header )
if str = header[ 'transfer-encoding' ] then
if /\Atransfer-encoding:\s*chunked/i === str then
return true
end
end
false
end
def read_header
header = {}
while true do
line = @socket.readline
break if line.empty?
/\A[^:]+/ === line
nm = $&
nm.strip!
nm.downcase!
header[ nm ] = line
end
header
end
def write_header( user )
if user then
header = @in_header.dup.update user
else
header = @in_header
end
header.each do |n,v|
@socket.writeline n + ': ' + v
end
@socket.writeline ''
if tmp = header['Connection'] then
/close/i === tmp
else
false
end
end
def read_chunked_body( ret )
line = nil
len = nil
total = 0
while true do
line = @socket.readline
unless /[0-9a-hA-H]+/ === line then
raise HTTPBadResponce, "chunk size not given"
end
len = $&.hex
break if len == 0
@socket.read( len, ret ); total += len
@socket.read 2 # \r\n
end
while true do
line = @socket.readline
break if line.empty?
end
total
end
end
end # module Net

View File

@ -4,8 +4,8 @@
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby license.
You can freely distribute/modify this file.
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
=end
@ -19,17 +19,17 @@ module Net
=begin
== Net::POP3Session
== Net::POP3
=== Super Class
Net::Session
Net::Protocol
=== Class Methods
: new( address = 'localhost', port = 110 )
This method create a new POP3Session object.
This method create a new POP3 object.
This will not open connection yet.
@ -37,11 +37,11 @@ Net::Session
: start( account, password )
This method start POP session.
This method start POP3.
: each{|popmail| ...}
This method is equals to "pop3session.mails.each"
This method is equals to "pop3.mails.each"
: mails
@ -50,12 +50,19 @@ Net::Session
=end
class POP3Session < Session
class POP3 < Protocol
Version = '1.1.2'
Version = '1.1.3'
session_setvar :port, '110'
session_setvar :command_type, 'Net::POP3Command'
protocol_param :port, '110'
protocol_param :command_type, '::Net::POP3Command'
protocol_param :mail_type, '::Net::POPMail'
def initialize( addr = nil, port = nil )
super
@mails = [].freeze
end
attr :mails
@ -68,25 +75,23 @@ Net::Session
private
def proto_initialize
@mails = [].freeze
end
def do_start( acnt, pwd )
@proto.auth( acnt, pwd )
@command.auth( acnt, pwd )
t = self.type.mail_type
@mails = []
@proto.list.each_with_index do |size,idx|
@command.list.each_with_index do |size,idx|
if size then
@mails.push POPMail.new( idx, size, @proto )
@mails.push t.new( idx, size, @command )
end
end
@mails.freeze
end
end # POP3Session
end
POPSession = POP3Session
POP3 = POP3Session
POP = POP3
POPSession = POP3
POP3Session = POP3
=begin
@ -133,10 +138,10 @@ Object
class POPMail
def initialize( idx, siz, pro )
@num = idx
@size = siz
@proto = pro
def initialize( n, s, cmd )
@num = n
@size = s
@command = cmd
@deleted = false
end
@ -145,13 +150,13 @@ Object
attr :size
def all( dest = '' )
@proto.retr( @num, dest )
@command.retr( @num, dest )
end
alias pop all
alias mail all
def top( lines, dest = '' )
@proto.top( @num, lines, dest )
@command.top( @num, lines, dest )
end
def header( dest = '' )
@ -159,7 +164,7 @@ Object
end
def delete
@proto.dele( @num )
@command.dele( @num )
@deleted = true
end
alias delete! delete
@ -169,7 +174,7 @@ Object
end
def uidl
@proto.uidl @num
@command.uidl @num
end
end
@ -177,30 +182,30 @@ Object
=begin
== Net::APOP3Session
== Net::APOP
This class has no new methods. Only way of authetication is changed.
=== Super Class
Net::POP3Session
Net::POP3
=end
class APOPSession < POP3Session
class APOP < POP3
session_setvar :command_type, 'Net::APOPCommand'
protocol_param :command_type, 'Net::APOPCommand'
end
APOP = APOPSession
APOPSession = APOP
=begin
== Net::POP3Command
POP3 protocol class.
POP3 command class.
=== Super Class
@ -240,7 +245,7 @@ Net::Command
: quit
This method finishes POP3 session.
This method ends POP using 'QUIT' commmand.
: rset
@ -276,7 +281,7 @@ Net::Command
def auth( acnt, pass )
@socket.writeline( 'USER ' + acnt )
@socket.writeline 'USER ' + acnt
check_reply_auth
@socket.writeline( 'PASS ' + pass )
@ -287,8 +292,7 @@ Net::Command
def list
@socket.writeline( 'LIST' )
check_reply( SuccessCode )
getok 'LIST'
arr = []
@socket.read_pendlist do |line|
@ -301,36 +305,29 @@ Net::Command
def rset
@socket.writeline( 'RSET' )
check_reply( SuccessCode )
getok 'RSET'
end
def top( num, lines = 0, dest = '' )
@socket.writeline( sprintf( 'TOP %d %d', num, lines ) )
check_reply( SuccessCode )
return @socket.read_pendstr( dest )
getok sprintf( 'TOP %d %d', num, lines )
@socket.read_pendstr( dest )
end
def retr( num, dest = '', &block )
@socket.writeline( sprintf( 'RETR %d', num ) )
check_reply( SuccessCode )
return @socket.read_pendstr( dest, &block )
getok sprintf( 'RETR %d', num )
@socket.read_pendstr( dest, &block )
end
def dele( num )
@socket.writeline( 'DELE ' + num.to_s )
check_reply( SuccessCode )
getok sprintf( 'DELE %d', num )
end
def uidl( num )
@socket.writeline( 'UIDL ' + num.to_s )
rep = check_reply( SuccessCode )
rep = getok( sprintf 'UIDL %d', num )
uid = rep.msg.split(' ')[1]
uid
@ -341,8 +338,7 @@ Net::Command
def do_quit
@socket.writeline( 'QUIT' )
check_reply( SuccessCode )
getok 'QUIT'
end

View File

@ -1,11 +1,11 @@
=begin
= net/session.rb version 1.1.2
= net/session.rb version 1.1.3
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby style license.
You can freely distribute/modify this file.
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
=end
@ -18,9 +18,9 @@ module Net
=begin
== Net::Session
== Net::Protocol
the abstruct class for Internet protocol session
the abstruct class for Internet protocol
=== Super Class
@ -30,22 +30,27 @@ Object
: Version
The version of Session class. It is a string like "1.1.2".
The version of Session class. It is a string like "1.1.3".
=== Class Methods
: new( address = 'localhost', port = nil )
This method Create a new Session object.
This method Creates a new Session object.
: start( address = 'localhost', port = nil, *args )
: start( address = 'localhost', port = nil, *args ){|session| .... }
This method create a new Session object and start 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
@ -59,14 +64,14 @@ Object
: start( *args )
This method start session. If you call this method when the session
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 finish session. If you call this method before session starts,
This method ends protocol. If you call this method before protocol starts,
it only return false without doing anything.
: active?
@ -75,20 +80,20 @@ Object
=end
class Session
class Protocol
Version = '1.1.2'
Version = '1.1.3'
class << self
def start( address = 'localhost', port = nil, *args )
session = new( address, port )
instance = new( address, port )
if iterator? then
session.start( *args ) { yield session }
instance.start( *args ) { yield instance }
else
session.start *args
session
instance.start *args
instance
end
end
@ -104,11 +109,8 @@ Object
@port = port
end
def connect
tmpa, tmpp = @address, @port
@address, @port = @proxyaddr, @proxyport
super
@address, @port = tmpa, tmpp
def connect( addr, port )
super @proxyaddr, @proxyport
end
private :connect
@ -129,7 +131,7 @@ Object
private
def session_setvar( name, val )
def protocol_param( name, val )
module_eval %-
def self.#{name.id2name}
#{val}
@ -143,26 +145,26 @@ Object
#
# sub-class requirements
#
# session_setvar command_type
# session_setvar port
# protocol_param command_type
# protocol_param port
#
# private method do_start (optional)
# private method do_finish (optional)
#
session_setvar :port, 'nil'
session_setvar :command_type, 'nil'
session_setvar :socket_type, 'Net::ProtocolSocket'
protocol_param :port, 'nil'
protocol_param :command_type, 'nil'
protocol_param :socket_type, '::Net::ProtocolSocket'
def initialize( addr = 'localhost', port = nil )
@address = addr
def initialize( addr = nil, port = nil )
@address = addr || 'localhost'
@port = port || self.type.port
@active = false
@pipe = nil
@proto = nil
@command = nil
@socket = nil
end
@ -170,6 +172,7 @@ Object
attr :address
attr :port
attr :command
attr :socket
@ -178,7 +181,7 @@ Object
@active = true
begin
connect
connect @address, @port
do_start *args
yield if iterator?
ensure
@ -187,7 +190,7 @@ Object
end
def finish
if @proto then
if @command then
do_finish
disconnect
end
@ -225,19 +228,21 @@ Object
end
def connect
@socket = self.type.socket_type.open( @address, @port, @pipe )
@proto = self.type.command_type.new( @socket )
def connect( addr, port )
@socket = self.type.socket_type.open( addr, port, @pipe )
@command = self.type.command_type.new( @socket )
end
def disconnect
@proto.quit
@proto = nil
@socket = nil
@command.quit
@command = nil
@socket = nil
end
end
Session = Protocol
=begin
@ -259,7 +264,7 @@ Object
: quit
This method finishes protocol.
This method dispatch command which ends the protocol.
=end
@ -301,6 +306,11 @@ Object
@error_occured = true
rep.error! @socket.sending
end
def getok( line, ok = SuccessCode )
@socket.writeline line
check_reply ok
end
end

View File

@ -4,8 +4,8 @@
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby license.
You can freely distribute/modify this file.
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
=end
@ -18,57 +18,57 @@ module Net
=begin
== Net::SMTPSession
== Net::SMTP
=== Super Class
Net::Session
Net::Protocol
=== Class Methods
: new( address = 'localhost', port = 25 )
This method create new SMTPSession object.
This method create new SMTP object.
=== Methods
: start( helo_domain = ENV['HOSTNAME'] )
This method opens TCP connection and start SMTP session.
If session had been started, do nothing and return false.
This method opens TCP connection and start SMTP.
If protocol had been started, do nothing and return false.
: sendmail( mailsrc, from_domain, to_addrs )
This method sends 'mailsrc' as mail. SMTPSession read strings from 'mailsrc'
by calling 'each' iterator, and convert them into "\r\n" terminated string when write.
SMTPSession's Exceptions are:
* Protocol::ProtoSyntaxError: syntax error (errno.500)
* Protocol::ProtoFatalError: fatal error (errno.550)
* Protocol::ProtoUnknownError: unknown error
* Protocol::ProtoServerBusy: temporary error (errno.420/450)
Exceptions which SMTP raises are:
* Net::ProtoSyntaxError: syntax error (errno.500)
* Net::ProtoFatalError: fatal error (errno.550)
* Net::ProtoUnknownError: unknown error
* Net::ProtoServerBusy: temporary error (errno.420/450)
: finish
This method closes SMTP session.
If session had not started, do nothind and return false.
This method ends SMTP.
If protocol had not started, do nothind and return false.
=end
class SMTPSession < Session
class SMTP < Protocol
Version = '1.1.2'
Version = '1.1.3'
session_setvar :port, '25'
session_setvar :command_type, 'Net::SMTPCommand'
protocol_param :port, '25'
protocol_param :command_type, '::Net::SMTPCommand'
def sendmail( mailsrc, fromaddr, toaddrs )
@proto.mailfrom fromaddr
@proto.rcpt toaddrs
@proto.data
@proto.sendmail mailsrc
@command.mailfrom fromaddr
@command.rcpt toaddrs
@command.data
@command.sendmail mailsrc
end
@ -79,12 +79,12 @@ Net::Session
unless helodom then
raise ArgumentError, "cannot get hostname"
end
@proto.helo helodom
@command.helo helodom
end
end
SMTP = SMTPSession
SMTPSession = SMTP
=begin
@ -99,14 +99,14 @@ Net::Command
: new( socket )
This method creates new SMTPCommand object, and open SMTP session.
This method creates new SMTPCommand object, and open SMTP.
=== Methods
: helo( helo_domain )
This method send "HELO" command and start SMTP session.
This method send "HELO" command and start SMTP.
helo_domain is localhost's FQDN.
: mailfrom( from_addr )
@ -139,34 +139,30 @@ Net::Command
def helo( fromdom )
@socket.writeline( 'HELO ' << fromdom )
check_reply( SuccessCode )
getok sprintf( 'HELO %s', fromdom )
end
def mailfrom( fromaddr )
@socket.writeline( 'MAIL FROM:<' + fromaddr + '>' )
check_reply( SuccessCode )
getok sprintf( 'MAIL FROM:<%s>', fromaddr )
end
def rcpt( toaddrs )
toaddrs.each do |i|
@socket.writeline( 'RCPT TO:<' + i + '>' )
check_reply( SuccessCode )
getok sprintf( 'RCPT TO:<%s>', i )
end
end
def data
@socket.writeline( 'DATA' )
check_reply( ContinueCode )
getok 'DATA', ContinueCode
end
def writemail( mailsrc )
@socket.write_pendstr( mailsrc )
check_reply( SuccessCode )
@socket.write_pendstr mailsrc
check_reply SuccessCode
end
alias sendmail writemail
@ -175,8 +171,7 @@ Net::Command
def do_quit
@socket.writeline( 'QUIT' )
check_reply( SuccessCode )
getok 'QUIT'
end
@ -184,19 +179,19 @@ Net::Command
arr = read_reply
stat = arr[0][0,3]
cls = UnknownCode
case stat[0]
when ?2 then cls = SuccessCode
when ?3 then cls = ContinueCode
when ?4 then cls = ServerBusyCode
when ?5 then
case stat[1]
when ?0 then cls = SyntaxErrorCode
when ?5 then cls = FatalErrorCode
end
end
klass = UnknownCode
klass = case stat[0]
when ?2 then SuccessCode
when ?3 then ContinueCode
when ?4 then ServerBusyCode
when ?5 then
case stat[1]
when ?0 then SyntaxErrorCode
when ?5 then FatalErrorCode
end
end
return cls.new( stat, arr.join('') )
klass.new( stat, arr.join('') )
end