Version 1.1.0

o  http.rb
o  support class swap
o  Net.quote


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@588 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 1999-12-17 15:00:13 +00:00
parent 9d228b13de
commit 6f4751f5f6
4 changed files with 617 additions and 382 deletions

200
lib/net/http.rb Normal file
View File

@ -0,0 +1,200 @@
=begin
= net/http.rb
maintained by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This file is derived from http-access.rb
=end
require 'net/session'
module Net
class HTTPError < ProtocolError; end
class HTTPBadResponse < HTTPError; end
class HTTPSession < Session
Version = '1.1.0'
session_setvar :port, '80'
session_setvar :command_type, 'HTTPCommand'
def get( path = '/', header = nil, ret = '' )
confirm_connection
@proto.get path, header, ret
end
def head( path = '/', header = nil )
confirm_connection
@proto.head path, header
end
private
def confirm_connection
if @socket.closed? then
@socket.reopen
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 = '' )
@socket.writeline sprintf( 'GET %s HTTP/%s', path, HTTPVersion )
write_header u_header
check_reply SuccessCode
header = read_header
@socket.read content_length( header ), ret
@socket.close unless keep_alive? header
return header, ret
end
def head( path, u_header = nil )
@socket.writeline sprintf( 'HEAD %s HTTP/%s', path, HTTPVersion )
write_header u_header
check_reply SuccessCode
header = read_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
head '/', { 'Connection' => 'Close' }
end
end
def get_reply
str = @socket.readline
/\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s+(.*)\z/i === str
@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 /content-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 /connection:\s*keep-alive/i === str then
return true
end
else
if @http_version == '1.1' 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
end
end # module Net

View File

@ -1,11 +1,11 @@
=begin =begin
= Net module version 1.0.3 reference manual = net/pop.rb
pop.rb written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby style license. This library is distributed under the terms of Ruby license.
You can freely distribute/modify/copy this file. You can freely distribute/modify this file.
=end =end
@ -16,6 +16,7 @@ require 'md5'
module Net module Net
=begin =begin
== Net::POP3Session == Net::POP3Session
@ -28,14 +29,9 @@ Net::Session
: new( address = 'localhost', port = 110 ) : new( address = 'localhost', port = 110 )
This method create a new POP3Session object but this will not open connection. This method create a new POP3Session object.
This will not open connection yet.
=end
class POP3Session < Session
=begin
=== Methods === Methods
@ -45,30 +41,35 @@ Net::Session
: each{|popmail| ...} : each{|popmail| ...}
This method is equals to "POP3Session.mails.each" This method is equals to "pop3session.mails.each"
: mails : mails
This method returns an array of <a href="#popi">POP3Session::POPMail</a>. This method returns an array of ((URL:#POPMail)).
This array is renewed when login. This array is renewed when login.
=end =end
class POP3Session < Session
session_setvar :port, '110'
session_setvar :command_type, 'POP3Command'
attr :mails attr :mails
def each() @mails.each{|m| yield m} end def each
@mails.each {|m| yield m }
end
private private
def proto_initialize def proto_initialize
@proto_type = POP3Command
@port = 110
@mails = [].freeze @mails = [].freeze
end end
def do_start( acnt, pwd ) def do_start( acnt, pwd )
@proto.auth( acnt, pwd ) @proto.auth( acnt, pwd )
@mails = [] @mails = []
@ -80,15 +81,15 @@ Net::Session
@mails.freeze @mails.freeze
end end
end # POP3Session
def do_finish POPSession = POP3Session
@proto.quit POP3 = POP3Session
end
=begin =begin
== Net::POP3Session::POPMail == Net::POPMail
A class of mail which exists on POP server. A class of mail which exists on POP server.
@ -96,19 +97,6 @@ A class of mail which exists on POP server.
Object Object
=end
class POPMail
def initialize( idx, siz, pro )
@num = idx
@size = siz
@proto = pro
@deleted = false
end
=begin
=== Method === Method
@ -141,6 +129,17 @@ Object
=end =end
class POPMail
def initialize( idx, siz, pro )
@num = idx
@size = siz
@proto = pro
@deleted = false
end
attr :size attr :size
def all( dest = '' ) def all( dest = '' )
@ -173,11 +172,6 @@ Object
end end
end # POP3Session
POPSession = POP3Session
POP3 = POP3Session
=begin =begin
@ -193,10 +187,7 @@ Net::POP3Session
class APOPSession < POP3Session class APOPSession < POP3Session
def proto_initialize session_setvar :command_type, 'APOPCommand'
super
@proto_type = APOPCommand
end
end end
@ -219,17 +210,6 @@ Net::Command
This method creates new POP3Command object. 'socket' must be ProtocolSocket. This method creates new POP3Command object. 'socket' must be ProtocolSocket.
=end
class POP3Command < Command
def initialize( sock )
@uidl = nil
super
end
=begin
=== Methods === Methods
@ -284,6 +264,15 @@ Net::Command
=end =end
class POP3Command < Command
def initialize( sock )
super
check_reply SuccessCode
end
def auth( acnt, pass ) def auth( acnt, pass )
@socket.writeline( 'USER ' + acnt ) @socket.writeline( 'USER ' + acnt )
check_reply_auth check_reply_auth
@ -385,7 +374,7 @@ Net::Command
=== Super Class === Super Class
POP3 POP3Command
=== Methods === Methods
@ -428,9 +417,4 @@ POP3
end end
unless Session::Version == '1.0.3' then
$stderr.puts "WARNING: wrong version of session.rb & pop.rb"
end
end end

View File

@ -1,11 +1,11 @@
=begin =begin
= Net module version 1.0.3 reference manual = net/session.rb version 1.1.0
session.rb written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby style license. This library is distributed under the terms of Ruby style license.
You can freely distribute/modify/copy this file. You can freely distribute/modify this file.
=end =end
@ -13,16 +13,7 @@ You can freely distribute/modify/copy this file.
require 'socket' require 'socket'
class String module Net
def doquote
str = self.gsub( "\n", '\\n' )
str.gsub!( "\r", '\\r' )
str.gsub!( "\t", '\\t' )
return str
end
end
=begin =begin
@ -39,18 +30,8 @@ Object
: Version : Version
The version of Session class. It is a string like "1.0.3". The version of Session class. It is a string like "1.1.0".
=end
module Net
class Session
Version = '1.0.3'
=begin
=== Class Methods === Class Methods
@ -65,31 +46,6 @@ module Net
If you call this method with block, Session object give itself If you call this method with block, Session object give itself
to block and finish session when block returns. to block and finish session when block returns.
=end
def initialize( addr = 'localhost', port = nil )
proto_initialize
@address = addr
@port = port if port
@active = false
@pipe = nil
end
class << self
def start( address = 'localhost', port = nil, *args )
inst = new( address, port )
ret = inst.start( *args )
if iterator? then
ret = yield( inst )
inst.finish
end
return ret
end
end
=begin
=== Methods === Methods
@ -101,18 +57,6 @@ module Net
connecting port number connecting port number
=end
attr :address
attr :port
attr :socket
attr :proto_type
attr :proto, true
=begin
: start( *args ) : start( *args )
This method start session. If you call this method when the session This method start session. If you call this method when the session
@ -131,28 +75,96 @@ module Net
=end =end
class Session
Version = '1.1.0'
class << self
def start( address = 'localhost', port = nil, *args )
session = new( address, port )
if iterator? then
session.start( *args ) { yield session }
else
session.start *args
session
end
end
private
def session_setvar( name, val )
module_eval %-
def self.#{name.id2name}
#{val}
end
-
end
end
#
# sub-class requirements
#
# class method command_type
# class method port
#
# private method proto_initialize
# private method do_start (optional)
# private method do_finish (optional)
#
session_setvar :port, 'nil'
session_setvar :command_type, 'nil'
session_setvar :socket_type, 'ProtocolSocket'
def initialize( addr = 'localhost', port = nil )
@address = addr
@port = port || self.type.port
@active = false
@pipe = nil
@proto = nil
@socket = nil
end
attr :address
attr :port
attr :socket
def start( *args ) def start( *args )
return false if active? return false if active?
@active = true @active = true
if Class === args[0] then begin
c = args.shift connect
else do_start *args
c = ProtocolSocket yield if iterator?
ensure
finish if iterator?
end end
@socket = c.open( @address, @port, @pipe )
@pipe = nil
@proto = @proto_type.new( @socket )
do_start( *args )
end end
def finish def finish
@active = false
if @proto then if @proto then
do_finish 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 return true
else else
@ -160,12 +172,36 @@ module Net
end end
end end
def active?() @active end def active?
@active
end
def set_pipe( arg ) def set_pipe( arg ) # un-documented
@pipe = arg @pipe = arg
end end
private
def do_start
end
def do_finish
end
def connect
@socket = self.type.socket_type.open( @address, @port, @pipe )
@proto = self.type.command_type.new( @socket )
end
def disconnect
@proto.quit
@proto = nil
@socket = nil
end
end end
@ -197,7 +233,6 @@ Object
def initialize( sock ) def initialize( sock )
@socket = sock @socket = sock
check_reply( SuccessCode )
end end
attr :socket, true attr :socket, true
@ -248,39 +283,47 @@ Object
attr :msg attr :msg
def error!( sending ) def error!( sending )
err, tag = Errors[ self.type ] mes = <<MES
mes = sprintf( <<MES, tag, @code, sending.doquote, @msg.doquote )
%s: status %s status %s
writing string is: writing string is:
%s %s
error message from server is: error message from server is:
%s %s
MES MES
raise err, mes raise self.type::Error,
sprintf( mes, @code, Net.quote(sending), Net.quote(@msg) )
end end
end end
class SuccessCode < ReplyCode ; end class SuccessCode < ReplyCode
class ContinueCode < SuccessCode ; end Error = ProtoUnknownError
class ErrorCode < ReplyCode ; end end
class SyntaxErrorCode < ErrorCode ; end
class FatalErrorCode < ErrorCode ; end
class ServerBusyCode < ErrorCode ; end
class UnknownCode < ReplyCode ; end
class ReplyCode class ContinueCode < SuccessCode
Errors = { Error = ProtoUnknownError
SuccessCode => [ ProtoUnknownError, 'unknown error' ], end
ContinueCode => [ ProtoUnknownError, 'unknown error' ],
ErrorCode => [ ProtocolError, 'protocol error' ], class ErrorCode < ReplyCode
SyntaxErrorCode => [ ProtoSyntaxError, 'syntax error' ], Error = ProtocolError
FatalErrorCode => [ ProtoFatalError, 'fatal error' ], end
ServerBusyCode => [ ProtoServerError, 'probably server busy' ],
UnknownCode => [ ProtoUnknownError, 'unknown error' ] class SyntaxErrorCode < ErrorCode
} Error = ProtoSyntaxError
end
class FatalErrorCode < ErrorCode
Error = ProtoFatalError
end
class ServerBusyCode < ErrorCode
Error = ProtoServerError
end
class UnknownCode < ReplyCode
Error = ProtoUnknownError
end end
@ -298,31 +341,6 @@ Object
This create new ProtocolSocket object, and connect to server. This create new ProtocolSocket object, and connect to server.
=end
class ProtocolSocket
def initialize( addr, port, pipe = nil )
@address = addr
@port = port
@pipe = pipe
@ipaddr = ''
@closed = false
@sending = ''
@buffer = ''
@socket = TCPsocket.new( addr, port )
@ipaddr = @socket.addr[3]
end
attr :pipe, true
class << self
alias open new
end
=begin
=== Methods === Methods
@ -330,11 +348,11 @@ Object
This method closes socket. This method closes socket.
: addr : address, addr
a FQDN address of server a FQDN address of server
: ipaddr : ip_address, ipaddr
an IP address of server an IP address of server
@ -346,30 +364,6 @@ Object
true if ProtocolSokcet have been closed already true if ProtocolSokcet have been closed already
=end
attr :socket, true
def close
@socket.close
@closed = true
end
def closed?() @closed end
def addr() @address.dup end
def port() @port end
def ipaddr() @ipaddr.dup end
attr :sending
CRLF = "\r\n"
D_CRLF = ".\r\n"
TERMEXP = /\n|\r\n|\r/o
=begin
: read( length ) : read( length )
@ -397,92 +391,6 @@ Object
When this method was called with block, evaluate it for each reading a line. When this method was called with block, evaluate it for each reading a line.
=end
def read( len, ret = '' )
rsize = 0
while rsize + @buffer.size < len do
rsize += @buffer.size
ret << fetch_rbuf( @buffer.size )
fill_rbuf
end
ret << fetch_rbuf( len - rsize )
return ret
end
def readuntil( target )
until idx = @buffer.index( target ) do
fill_rbuf
end
return fetch_rbuf( idx + target.size )
end
def readline
ret = readuntil( CRLF )
ret.chop!
return ret
end
def read_pendstr( dest = '' )
@pipe << "reading text...\n" if pre = @pipe ; @pipe = nil
rsize = 0
while (str = readuntil( CRLF )) != D_CRLF do
rsize += str.size
str.gsub!( /\A\./o, '' )
dest << str
end
@pipe << "read #{rsize} bytes\n" if @pipe = pre
return dest
end
def read_pendlist
@pipe << "reading list...\n" if pre = @pipe ; @pipe = nil
arr = []
str = nil
call = iterator?
while (str = readuntil( CRLF )) != D_CRLF do
str.chop!
arr.push str
yield str if iterator?
end
@pipe << "read #{arr.size} lines\n" if @pipe = pre
return arr
end
private
READ_BLOCK = 1024 * 8
def fill_rbuf
@buffer << @socket.sysread( READ_BLOCK )
end
def fetch_rbuf( len )
bsi = @buffer.size
ret = @buffer[ 0, len ]
@buffer = @buffer[ len, bsi - len ]
@pipe << %{read "#{debugstr ret}"\n} if @pipe
return ret
end
=begin
: write( src ) : write( src )
@ -506,6 +414,159 @@ Object
=end =end
class ProtocolSocket
def initialize( addr, port, pipe = nil )
@addr = addr
@port = port
@pipe = pipe
@closed = true
@ipaddr = ''
@sending = ''
@buffer = ''
@socket = TCPsocket.new( addr, port )
@closed = false
@ipaddr = @socket.addr[3]
end
attr :pipe, true
class << self
alias open new
end
def reopen
unless closed? then
@socket.close
flush_rbuf
end
@socket = TCPsocket.new( @addr, @port )
end
attr :socket, true
def close
@socket.close
@closed = true
end
def closed?
@closed
end
def address
@addr.dup
end
alias addr address
attr :port
def ip_address
@ipaddr.dup
end
alias ipaddr ip_address
attr :sending
CRLF = "\r\n"
D_CRLF = ".\r\n"
TERMEXP = /\n|\r\n|\r/o
def read( len, ret = '' )
@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 )
fill_rbuf
end
ret << fetch_rbuf( len - rsize )
@pipe << "read #{len} bytes\n" if @pipe = pre
ret
end
def readuntil( target )
until idx = @buffer.index( target ) do
fill_rbuf
end
fetch_rbuf( idx + target.size )
end
def readline
ret = readuntil( CRLF )
ret.chop!
ret
end
def read_pendstr( dest = '' )
@pipe << "reading text...\n" if pre = @pipe ; @pipe = nil
rsize = 0
while (str = readuntil( CRLF )) != D_CRLF do
rsize += str.size
str.gsub!( /\A\./o, '' )
dest << str
end
@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?
while (str = readuntil( CRLF )) != D_CRLF do
str.chop!
arr.push str
yield str if iterator?
end
@pipe << "read #{arr.size} lines\n" if @pipe = pre
arr
end
private
READ_BLOCK = 1024 * 8
def fill_rbuf
@buffer << @socket.sysread( READ_BLOCK )
end
def fetch_rbuf( len )
bsi = @buffer.size
ret = @buffer[ 0, len ]
@buffer = @buffer[ len, bsi - len ]
@pipe << %{read "#{Net.quote ret}"\n} if @pipe
ret
end
def flush_rbuf
@buffer = ''
end
public public
@ -514,7 +575,7 @@ Object
each_crlf_line( src ) do |line| each_crlf_line( src ) do |line|
do_write_do line do_write_do line
end end
return do_write_fin do_write_fin
end end
@ -523,7 +584,7 @@ Object
src.each do |bin| src.each do |bin|
do_write_do bin do_write_do bin
end end
return do_write_fin do_write_fin
end end
@ -531,7 +592,7 @@ Object
do_write_beg do_write_beg
do_write_do str do_write_do str
do_write_do CRLF do_write_do CRLF
return do_write_fin do_write_fin
end end
@ -547,7 +608,7 @@ Object
wsize = do_write_fin wsize = do_write_fin
@pipe << "wrote #{wsize} bytes text" if @pipe = pre @pipe << "wrote #{wsize} bytes text" if @pipe = pre
return wsize wsize
end end
@ -587,48 +648,41 @@ Object
def do_write_beg def do_write_beg
@wtmp = 'write "' if @pipe
@writtensize = 0 @writtensize = 0
@sending = '' @sending = ''
end end
def do_write_do( arg ) def do_write_do( arg )
@wtmp << debugstr( arg ) if @pipe if @pipe or @sending.size < 128 then
@sending << Net.quote( arg )
if @sending.size < 128 then
@sending << arg
else else
@sending << '...' unless @sending[-1] == ?. @sending << '...' unless @sending[-1] == ?.
end end
s = @socket.write( arg ) s = @socket.write( arg )
@writtensize += s @writtensize += s
return s s
end end
def do_write_fin def do_write_fin
if @pipe then if @pipe then
@wtmp << "\n" @pipe << 'write "'
@pipe << @wtmp @pipe << @sending
@wtmp = nil @pipe << "\"\n"
end end
@socket.flush @socket.flush
return @writtensize @writtensize
end
def debugstr( str )
ret = ''
while str and tmp = str[ 0, 50 ] do
str = str[ 50, str.size - 50 ]
tmp = tmp.inspect
ret << tmp[ 1, tmp.size - 2 ]
end
ret
end end
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

View File

@ -1,11 +1,11 @@
=begin =begin
= Net module version 1.0.3 reference manual = net/smtp.rb
smtp.rb written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This library is distributed under the terms of Ruby style license. This library is distributed under the terms of Ruby license.
You can freely redistribute/modify/copy this file. You can freely distribute/modify this file.
=end =end
@ -13,6 +13,9 @@ You can freely redistribute/modify/copy this file.
require 'net/session' require 'net/session'
module Net
=begin =begin
== Net::SMTPSession == Net::SMTPSession
@ -27,18 +30,6 @@ Net::Session
This method create new SMTPSession object. This method create new SMTPSession object.
=end
module Net
class SMTPSession < Session
def proto_initialize
@proto_type = SMTPCommand
@port = 25
end
=begin
=== Methods === Methods
@ -65,26 +56,30 @@ module Net
=end =end
class SMTPSession < Session
Version = '1.1.0'
session_setvar :port, '25'
session_setvar :command_type, 'SMTPCommand'
def sendmail( mailsrc, fromaddr, toaddrs ) def sendmail( mailsrc, fromaddr, toaddrs )
@proto.mailfrom( fromaddr ) @proto.mailfrom fromaddr
@proto.rcpt( toaddrs ) @proto.rcpt toaddrs
@proto.data @proto.data
@proto.sendmail( mailsrc ) @proto.sendmail mailsrc
end end
private private
def do_start( helodom = nil ) def do_start( helodom = ENV['HOSTNAME'] )
unless helodom then unless helodom then
helodom = ENV[ 'HOSTNAME' ] raise ArgumentError, "cannot get hostname"
end end
@proto.helo( helodom ) @proto.helo helodom
end
def do_finish
@proto.quit
end end
end end
@ -111,17 +106,17 @@ Net::Command
: helo( helo_domain ) : helo( helo_domain )
This method send "HELO" command and start SMTP session.<br> This method send "HELO" command and start SMTP session.
helo_domain is localhost's FQDN. helo_domain is localhost's FQDN.
: mailfrom( from_addr ) : mailfrom( from_addr )
This method sends "MAIL FROM" command.<br> This method sends "MAIL FROM" command.
from_addr is your mail address(????@????). from_addr is your mail address(????@????).
: rcpt( to_addrs ) : rcpt( to_addrs )
This method sends "RCPT TO" command.<br> This method sends "RCPT TO" command.
to_addrs is array of mail address(???@???) of destination. to_addrs is array of mail address(???@???) of destination.
: data( mailsrc ) : data( mailsrc )
@ -137,6 +132,12 @@ Net::Command
class SMTPCommand < Command class SMTPCommand < Command
def initialize( sock )
super
check_reply SuccessCode
end
def helo( fromdom ) def helo( fromdom )
@socket.writeline( 'HELO ' << fromdom ) @socket.writeline( 'HELO ' << fromdom )
check_reply( SuccessCode ) check_reply( SuccessCode )
@ -163,10 +164,11 @@ Net::Command
end end
def sendmail( mailsrc ) def writemail( mailsrc )
@socket.write_pendstr( mailsrc ) @socket.write_pendstr( mailsrc )
check_reply( SuccessCode ) check_reply( SuccessCode )
end end
alias sendmail writemail
private private
@ -211,9 +213,4 @@ Net::Command
end end
unless Session::Version == '1.0.3' then
$stderr.puts "WARNING: wrong version of session.rb & smtp.rb"
end
end end