From 6f4751f5f63d177534f34e0c278967883e11c035 Mon Sep 17 00:00:00 2001 From: aamine Date: Fri, 17 Dec 1999 15:00:13 +0000 Subject: [PATCH] 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 --- lib/net/http.rb | 200 ++++++++++++++++ lib/net/pop.rb | 156 ++++++------ lib/net/session.rb | 576 +++++++++++++++++++++++++-------------------- lib/net/smtp.rb | 67 +++--- 4 files changed, 617 insertions(+), 382 deletions(-) create mode 100644 lib/net/http.rb diff --git a/lib/net/http.rb b/lib/net/http.rb new file mode 100644 index 0000000000..d1ac411188 --- /dev/null +++ b/lib/net/http.rb @@ -0,0 +1,200 @@ +=begin + += net/http.rb + +maintained by Minero Aoki +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 diff --git a/lib/net/pop.rb b/lib/net/pop.rb index 547dafbbcc..1a0c61b51c 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -1,11 +1,11 @@ =begin -= Net module version 1.0.3 reference manual += net/pop.rb -pop.rb written by Minero Aoki +written by Minero Aoki -This library is distributed under the terms of Ruby style license. -You can freely distribute/modify/copy this file. +This library is distributed under the terms of Ruby license. +You can freely distribute/modify this file. =end @@ -16,6 +16,7 @@ require 'md5' module Net + =begin == Net::POP3Session @@ -28,14 +29,9 @@ Net::Session : 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 @@ -45,30 +41,35 @@ Net::Session : each{|popmail| ...} - This method is equals to "POP3Session.mails.each" + This method is equals to "pop3session.mails.each" : mails - This method returns an array of POP3Session::POPMail. + This method returns an array of ((URL:#POPMail)). This array is renewed when login. =end + class POP3Session < Session + + session_setvar :port, '110' + session_setvar :command_type, 'POP3Command' + + attr :mails - def each() @mails.each{|m| yield m} end + def each + @mails.each {|m| yield m } + end private def proto_initialize - @proto_type = POP3Command - @port = 110 @mails = [].freeze end - def do_start( acnt, pwd ) @proto.auth( acnt, pwd ) @mails = [] @@ -80,15 +81,15 @@ Net::Session @mails.freeze end + end # POP3Session - def do_finish - @proto.quit - end + POPSession = POP3Session + POP3 = POP3Session =begin -== Net::POP3Session::POPMail +== Net::POPMail A class of mail which exists on POP server. @@ -96,19 +97,6 @@ A class of mail which exists on POP server. Object -=end - - class POPMail - - def initialize( idx, siz, pro ) - @num = idx - @size = siz - @proto = pro - - @deleted = false - end - -=begin === Method @@ -141,42 +129,48 @@ Object =end - attr :size + class POPMail - def all( dest = '' ) - @proto.retr( @num, dest ) - end - alias pop all - alias mail all - - def top( lines, dest = '' ) - @proto.top( @num, lines, dest ) - end - - def header( dest = '' ) - top( 0, dest ) - end - - def delete - @proto.dele( @num ) - @deleted = true - end - alias delete! delete - - def deleted? - @deleted - end - - def uidl - @proto.uidl @num - end + def initialize( idx, siz, pro ) + @num = idx + @size = siz + @proto = pro + @deleted = false end - end # POP3Session - POPSession = POP3Session - POP3 = POP3Session + attr :size + + def all( dest = '' ) + @proto.retr( @num, dest ) + end + alias pop all + alias mail all + + def top( lines, dest = '' ) + @proto.top( @num, lines, dest ) + end + + def header( dest = '' ) + top( 0, dest ) + end + + def delete + @proto.dele( @num ) + @deleted = true + end + alias delete! delete + + def deleted? + @deleted + end + + def uidl + @proto.uidl @num + end + + end =begin @@ -193,10 +187,7 @@ Net::POP3Session class APOPSession < POP3Session - def proto_initialize - super - @proto_type = APOPCommand - end + session_setvar :command_type, 'APOPCommand' end @@ -219,17 +210,6 @@ Net::Command This method creates new POP3Command object. 'socket' must be ProtocolSocket. -=end - - class POP3Command < Command - - def initialize( sock ) - @uidl = nil - super - end - - -=begin === Methods @@ -284,6 +264,15 @@ Net::Command =end + + class POP3Command < Command + + def initialize( sock ) + super + check_reply SuccessCode + end + + def auth( acnt, pass ) @socket.writeline( 'USER ' + acnt ) check_reply_auth @@ -385,7 +374,7 @@ Net::Command === Super Class -POP3 +POP3Command === Methods @@ -428,9 +417,4 @@ POP3 end - - unless Session::Version == '1.0.3' then - $stderr.puts "WARNING: wrong version of session.rb & pop.rb" - end - end diff --git a/lib/net/session.rb b/lib/net/session.rb index 3863d8506e..5965d4d280 100644 --- a/lib/net/session.rb +++ b/lib/net/session.rb @@ -1,11 +1,11 @@ =begin -= Net module version 1.0.3 reference manual += net/session.rb version 1.1.0 -session.rb written by Minero Aoki +written by Minero Aoki 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 @@ -13,16 +13,7 @@ You can freely distribute/modify/copy this file. require 'socket' -class String - - def doquote - str = self.gsub( "\n", '\\n' ) - str.gsub!( "\r", '\\r' ) - str.gsub!( "\t", '\\t' ) - return str - end - -end +module Net =begin @@ -39,18 +30,8 @@ Object : 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 @@ -65,31 +46,6 @@ module Net If you call this method with block, Session object give itself 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 @@ -101,18 +57,6 @@ module Net connecting port number -=end - - attr :address - attr :port - - attr :socket - - attr :proto_type - attr :proto, true - -=begin - : start( *args ) This method start session. If you call this method when the session @@ -131,28 +75,96 @@ module Net =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 ) return false if active? @active = true - if Class === args[0] then - c = args.shift - else - c = ProtocolSocket + begin + connect + do_start *args + yield if iterator? + ensure + finish if iterator? end - @socket = c.open( @address, @port, @pipe ) - @pipe = nil - - @proto = @proto_type.new( @socket ) - do_start( *args ) end def finish - @active = false - if @proto 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 @@ -160,12 +172,36 @@ module Net end end - def active?() @active end + def active? + @active + end - def set_pipe( arg ) + def set_pipe( arg ) # un-documented @pipe = arg 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 @@ -197,7 +233,6 @@ Object def initialize( sock ) @socket = sock - check_reply( SuccessCode ) end attr :socket, true @@ -248,39 +283,47 @@ Object attr :msg def error!( sending ) - err, tag = Errors[ self.type ] - mes = sprintf( < [ 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 UnknownCode < ReplyCode + Error = ProtoUnknownError end @@ -298,31 +341,6 @@ Object 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 @@ -330,11 +348,11 @@ Object This method closes socket. -: addr +: address, addr a FQDN address of server -: ipaddr +: ip_address, ipaddr an IP address of server @@ -346,30 +364,6 @@ Object 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 ) @@ -397,92 +391,6 @@ Object 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 ) @@ -506,6 +414,159 @@ Object =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 @@ -514,7 +575,7 @@ Object each_crlf_line( src ) do |line| do_write_do line end - return do_write_fin + do_write_fin end @@ -523,7 +584,7 @@ Object src.each do |bin| do_write_do bin end - return do_write_fin + do_write_fin end @@ -531,7 +592,7 @@ Object do_write_beg do_write_do str do_write_do CRLF - return do_write_fin + do_write_fin end @@ -547,7 +608,7 @@ Object wsize = do_write_fin @pipe << "wrote #{wsize} bytes text" if @pipe = pre - return wsize + wsize end @@ -587,48 +648,41 @@ Object def do_write_beg - @wtmp = 'write "' if @pipe - @writtensize = 0 @sending = '' end def do_write_do( arg ) - @wtmp << debugstr( arg ) if @pipe - - 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 if @pipe then - @wtmp << "\n" - @pipe << @wtmp - @wtmp = nil + @pipe << 'write "' + @pipe << @sending + @pipe << "\"\n" end @socket.flush - return @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 + @writtensize end end -end + + def Net.quote( str ) + str = str.gsub( "\n", '\\n' ) + str.gsub!( "\r", '\\r' ) + str.gsub!( "\t", '\\t' ) + str + end + +end # module Net diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 6294512ffc..dd74cdcdb4 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -1,11 +1,11 @@ =begin -= Net module version 1.0.3 reference manual += net/smtp.rb -smtp.rb written by Minero Aoki +written by Minero Aoki -This library is distributed under the terms of Ruby style license. -You can freely redistribute/modify/copy this file. +This library is distributed under the terms of Ruby license. +You can freely distribute/modify this file. =end @@ -13,6 +13,9 @@ You can freely redistribute/modify/copy this file. require 'net/session' +module Net + + =begin == Net::SMTPSession @@ -27,18 +30,6 @@ Net::Session This method create new SMTPSession object. -=end - -module Net - - class SMTPSession < Session - - def proto_initialize - @proto_type = SMTPCommand - @port = 25 - end - -=begin === Methods @@ -65,26 +56,30 @@ module Net =end + class SMTPSession < Session + + Version = '1.1.0' + + session_setvar :port, '25' + session_setvar :command_type, 'SMTPCommand' + + def sendmail( mailsrc, fromaddr, toaddrs ) - @proto.mailfrom( fromaddr ) - @proto.rcpt( toaddrs ) + @proto.mailfrom fromaddr + @proto.rcpt toaddrs @proto.data - @proto.sendmail( mailsrc ) + @proto.sendmail mailsrc end private - def do_start( helodom = nil ) + def do_start( helodom = ENV['HOSTNAME'] ) unless helodom then - helodom = ENV[ 'HOSTNAME' ] + raise ArgumentError, "cannot get hostname" end - @proto.helo( helodom ) - end - - def do_finish - @proto.quit + @proto.helo helodom end end @@ -111,17 +106,17 @@ Net::Command : helo( helo_domain ) - This method send "HELO" command and start SMTP session.
+ This method send "HELO" command and start SMTP session. helo_domain is localhost's FQDN. : mailfrom( from_addr ) - This method sends "MAIL FROM" command.
+ This method sends "MAIL FROM" command. from_addr is your mail address(????@????). : rcpt( to_addrs ) - This method sends "RCPT TO" command.
+ This method sends "RCPT TO" command. to_addrs is array of mail address(???@???) of destination. : data( mailsrc ) @@ -137,6 +132,12 @@ Net::Command class SMTPCommand < Command + def initialize( sock ) + super + check_reply SuccessCode + end + + def helo( fromdom ) @socket.writeline( 'HELO ' << fromdom ) check_reply( SuccessCode ) @@ -163,10 +164,11 @@ Net::Command end - def sendmail( mailsrc ) + def writemail( mailsrc ) @socket.write_pendstr( mailsrc ) check_reply( SuccessCode ) end + alias sendmail writemail private @@ -211,9 +213,4 @@ Net::Command end - - unless Session::Version == '1.0.3' then - $stderr.puts "WARNING: wrong version of session.rb & smtp.rb" - end - end