From cdc7602379c9d911983db2c044d69ac417869266 Mon Sep 17 00:00:00 2001 From: aamine Date: Tue, 6 Feb 2001 11:14:51 +0000 Subject: aamine * lib/net/http.rb: add HTTP#request. * lib/net/http.rb: take HTTP 1.0 server into account (incomplete). * lib/net/protocol.rb: timeout for open/read. * lib/net/protocol.rb: add Protocol#on_connect,on_disconnect. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1160 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/net/http.rb | 492 +++++++++++++++++++++++++--------------------------- lib/net/pop.rb | 2 +- lib/net/protocol.rb | 132 +++++++++----- lib/net/smtp.rb | 14 +- 4 files changed, 329 insertions(+), 311 deletions(-) (limited to 'lib/net') diff --git a/lib/net/http.rb b/lib/net/http.rb index 5b387f3a7e..8410497437 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -14,7 +14,7 @@ (Ruby Application Archive: http://www.ruby-lang.org/en/raa.html). -= class HTTP += class Net::HTTP == Class Methods @@ -49,7 +49,7 @@ HTTP default port (80). -== Methods +== Instance Methods : start : start {|http| .... } @@ -100,46 +100,31 @@ If called with block, gives a part of entity body string. -: new_get( path, header = nil ) {|req| .... } - creates a new GET request object and gives it to the block. - see also for Get class reference. - - # example - http.new_get( '/~foo/bar.html' ) do |req| - req['accept'] = 'text/html' - response = req.dispatch - p response['Content-Type'] - puts response.read_header - end -: new_head( path, header = nil ) {|req| .... } - creates a new HEAD request object and gives it to the block. - see also Head class reference. +: request( request, [src] ) +: request( request, [src] ) {|response| .... } + sends REQUEST to (remote) http server. This method also writes + string from SRC before it if REQUEST is a post/put request. + (giving SRC for get/head request causes ArgumentError.) -: new_post( path, header = nil ) {|req| .... } - creates a new POST request object and gives it to the block. - see also Post class reference. + If called with block, gives a HTTP response object to the block. -= class Get, Head, Post += class Net::HTTP::Get, Head, Post -HTTP request class. This class wraps request header and entity path. -All "key" is case-insensitive. +HTTP request classes. These classes wraps request header and +entity path. All "key" is case-insensitive. == Methods : self[ key ] returns header field for "key". -: dispatch [only Get, Head] - dispatches request. - This method returns HTTPResponse object. +: self[ key ] = val + set header to "val". -: dispatch( data = '' ) [only Post] -: dispatch {|adapter| .... } [only Post] - dispatches request. "data" is -= class HTTPResponse += class Net::HTTPResponse HTTP response class. This class wraps response header and entity. All "key" is case-insensitive. @@ -209,13 +194,31 @@ module Net class HTTP < Protocol - protocol_param :port, '80' - HTTPVersion = '1.1' + ### + ### connection + ### - def addr_port - address + (port == HTTP.port ? '' : ":#{port}") + protocol_param :port, '80' + + + def initialize( addr = nil, port = nil ) + super + + @proxy_address = nil + @proxy_port = nil + + @curr_http_version = HTTPVersion + @seems_1_0_server = false + end + + private + + def conn_command( sock ) + end + + def do_finish end @@ -223,11 +226,13 @@ module Net ### proxy ### + public + + class << self def Proxy( p_addr, p_port = nil ) - ::Net::NetPrivate::HTTPProxy.create_proxy_class( - p_addr, p_port || self.port ) + ProxyMod.create_proxy_class( p_addr, p_port || self.port ) end alias orig_new new @@ -274,9 +279,65 @@ module Net end - ### - ### for compatibility - ### + module ProxyMod + + class << self + + def create_proxy_class( p_addr, p_port ) + klass = Class.new( HTTP ) + klass.module_eval { + include HTTPProxy + @proxy_address = p_addr + @proxy_port = p_port + } + def klass.proxy_class? + true + end + + def klass.proxy_address + @proxy_address + end + + def klass.proxy_port + @proxy_port + end + + klass + end + + end + + def initialize( addr, port ) + super + @proxy_address = type.proxy_address + @proxy_port = type.proxy_port + end + + attr_reader :proxy_address, :proxy_port + + alias proxyaddr proxy_address + alias proxyport proxy_port + + def proxy? + true + end + + private + + def conn_socket( addr, port ) + super @proxy_address, @proxy_port + end + + def edit_path( path ) + 'http://' + addr_port + path + end + + end # module ProxyMod + + + # + # for backward compatibility + # @@newimpl = true @@ -300,23 +361,26 @@ module Net end - ### - ### http operations - ### + # + # http operations + # - def self.defrequest( nm, hasdest, hasdata ) + public + + def self.def_http_method( nm, hasdest, hasdata ) name = nm.id2name.downcase cname = nm.id2name lineno = __LINE__ + 2 - src = <" @@ -640,38 +614,42 @@ S @u_header.each_value( &block ) end - - def terminate - @response.terminate - end - - - private + private def canonical( k ) k.split('-').collect {|i| i.capitalize }.join('-') end + # + # write + # - # write request & header + def exec( sock, ver, path, ihead ) + ready( sock, ihead ) {|header| + request ver, path, header + } + end - def do_dispatch - if @response then - raise IOError, "#{type}\#dispatch called twice" - end - yield + def ready( sock, ihead ) + @response = nil + @socket = sock + ihead.update @u_header + yield ihead @response = read_response + @sock = nil end - def request( req ) - @socket.writeline req - @u_header.each do |n,v| + def request( ver, path, header ) + @socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver) + header.each do |n,v| @socket.writeline n + ': ' + v end @socket.writeline '' end - # read response & header + # + # read + # def read_response resp = rdresp0 @@ -683,13 +661,12 @@ S resp = get_resline while true do - line = @socket.readline + line = @socket.readuntil( "\n", true ) # ignore EOF + line.sub!( /\s+\z/, '' ) # don't use chop! break if line.empty? m = /\A([^:]+):\s*/.match( line ) - unless m then - raise HTTPBadResponse, 'wrong header line format' - end + m or raise HTTPBadResponse, 'wrong header line format' nm = m[1] line = m.post_match if resp.key? nm then @@ -704,7 +681,7 @@ S def get_resline str = @socket.readline - m = /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i.match( str ) + m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/i.match( str ) unless m then raise HTTPBadResponse, "wrong status line: #{str}" end @@ -712,52 +689,32 @@ S status = m[2] discrip = m[3] - HTTPResponse.new( status, discrip, @socket, type::HAS_BODY ) - end - - end - - class Get < HTTPRequest - - HAS_BODY = true - - def dispatch - do_dispatch { - request sprintf('GET %s HTTP/%s', @path, @http_version) - } + ::Net::NetPrivate::HTTPResponse.new( + status, discrip, @socket, type::HAS_BODY ) end end - class Head < HTTPRequest - - HAS_BODY = false - - def dispatch - do_dispatch { - request sprintf('HEAD %s HTTP/%s', @path, @http_version) - } - end - - end class HTTPRequestWithData < HTTPRequest + + private - def dispatch( str = nil ) + def exec( sock, ver, path, ihead, str = nil ) check_arg str, block_given? if block_given? then ac = Accumulator.new - yield ac # must be yield, not block.call + yield ac # must be yield, DO NOT USE block.call data = ac.terminate else data = str end - do_dispatch { - @u_header['Content-Length'] = data.size.to_s - @u_header.delete 'Transfer-Encoding' - request sprintf('%s %s HTTP/%s', type::METHOD, @path, @http_version) + ready( sock, ihead ) {|header| + header['Content-Length'] = data.size.to_s + header.delete 'Transfer-Encoding' + request ver, path, header @socket.write data } end @@ -773,21 +730,6 @@ S end - class Post < HTTPRequestWithData - - HAS_BODY = true - - METHOD = 'POST' - - end - - class Put < HTTPRequestWithData - - HAS_BODY = true - - METHOD = 'PUT' - - end class Accumulator @@ -810,6 +752,31 @@ S end + class Get < HTTPRequest + HAS_BODY = true + METHOD = 'GET' + end + + class Head < HTTPRequest + HAS_BODY = false + METHOD = 'HEAD' + end + + class Post < HTTPRequestWithData + HAS_BODY = true + METHOD = 'POST' + end + + class Put < HTTPRequestWithData + HAS_BODY = true + METHOD = 'PUT' + end + + end # HTTP:: + + + + module NetPrivate ### ### response @@ -926,7 +893,9 @@ S end + # # header (for backward compatibility) + # def read_header self @@ -935,8 +904,9 @@ S alias header read_header alias response read_header - + # # body + # def read_body( dest = nil, &block ) if @read and (dest or block) then @@ -963,22 +933,20 @@ S alias entity read_body - # internal use only + private + + def terminate read_body end - - private - - def read_body_0( dest ) if chunked? then read_chunked dest else clen = content_length if clen then - @socket.read clen, dest + @socket.read clen, dest, true # ignore EOF else clen = range_length if clen then @@ -1066,6 +1034,18 @@ S end + class Dummy + + def initialize( *args ) + end + + def critical? + false + end + + end + + end # module Net::NetPrivate end # module Net diff --git a/lib/net/pop.rb b/lib/net/pop.rb index 29a48f375f..00fff60824 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -65,7 +65,7 @@ Net::Protocol m.pop file end -=== Methods +=== Instance Methods : start( account, password ) : start( account, password ) {|pop| .... } diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index 7f99a64f97..9e97beec5a 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -15,7 +15,7 @@ You can get it from RAA == Net::Protocol -the abstract class for Internet protocol +the abstract class for some internet protocols === Super Class @@ -59,6 +59,7 @@ Object =end require 'socket' +require 'timeout' module Net @@ -116,8 +117,12 @@ module Net @command = nil @socket = nil - @active = false - @pipe = nil + @active = false + + @open_timeout = nil + @read_timeout = nil + + @pipe = nil end attr_reader :address @@ -126,10 +131,24 @@ module Net attr_reader :command attr_reader :socket + attr_accessor :open_timeout + attr_accessor :read_timeout + + def active? + @active + end + + def set_pipe( arg ) # un-documented + @pipe = arg + end + def inspect "#<#{type} #{address}:#{port} open=#{active?}>" end + # + # open session + # def start( *args ) return false if active? @@ -146,45 +165,54 @@ module Net end end + private + def _start( args ) connect do_start( *args ) @active = true end - private :_start - def finish - return false unless active? + def connect + conn_socket @address, @port + conn_command @socket + on_connect + end - do_finish unless @command.critical? - disconnect - @active = false - true + def conn_socket( addr, port ) + @socket = type.socket_type.open( + addr, port, @open_timeout, @read_timeout, @pipe ) end - def active? - @active + def conn_command( sock ) + @command = type.command_type.new( sock ) end - def set_pipe( arg ) # un-documented - @pipe = arg + def on_connect end + def do_start + end - private + # + # close session + # + public - def do_start - end + def finish + return false unless active? - def do_finish - @command.quit + do_finish if @command and not @command.critical? + disconnect + @active = false + true end + private - def connect( addr = @address, port = @port ) - @socket = type.socket_type.open( addr, port, @pipe ) - @command = type.command_type.new( @socket ) + def do_finish + @command.quit end def disconnect @@ -192,7 +220,11 @@ module Net if @socket and not @socket.closed? then @socket.close end - @socket = nil + @socket = nil + on_disconnect + end + + def on_disconnect end end @@ -311,6 +343,7 @@ module Net def write( str ) @sock.__send__ @mid, str end + alias << write end @@ -407,6 +440,7 @@ module Net @critical = false end + private def critical @@ -431,9 +465,12 @@ module Net class Socket - def initialize( addr, port, pipe = nil ) + def initialize( addr, port, otime = nil, rtime = nil, pipe = nil ) @addr = addr @port = port + + @read_timeout = rtime + @pipe = pipe @prepipe = nil @@ -442,7 +479,9 @@ module Net @sending = '' @buffer = '' - @socket = TCPsocket.new( addr, port ) + timeout( otime ) { + @socket = TCPsocket.new( addr, port ) + } @closed = false @ipaddr = @socket.addr[3] end @@ -494,13 +533,15 @@ module Net attr_reader :sending - ### - ### read - ### + # + # read + # + + public CRLF = "\r\n" - def read( len, dest = '' ) + def read( len, dest = '', ignerr = false ) @pipe << "reading #{len} bytes...\n" if @pipe; pipeoff rsize = 0 @@ -509,16 +550,15 @@ module Net rsize += writeinto( dest, @buffer.size ) fill_rbuf end + writeinto( dest, len - rsize ) rescue EOFError - len = rsize + raise unless igneof end - writeinto( dest, len - rsize ) @pipe << "read #{len} bytes\n" if pipeon dest end - def read_all( dest = '' ) @pipe << "reading all...\n" if @pipe; pipeoff @@ -536,8 +576,7 @@ module Net dest end - - def readuntil( target ) + def readuntil( target, igneof = false ) dest = '' begin while true do @@ -547,11 +586,11 @@ module Net end writeinto( dest, idx + target.size ) rescue EOFError + raise unless igneof writeinto( dest, @buffer.size ) end dest end - def readline ret = readuntil( "\n" ) @@ -559,7 +598,6 @@ module Net ret end - def read_pendstr( dest ) @pipe << "reading text...\n" if @pipe; pipeoff @@ -574,7 +612,6 @@ module Net dest end - # private use only (can not handle 'break') def read_pendlist @pipe << "reading list...\n" if @pipe; pipeoff @@ -594,10 +631,17 @@ module Net private - READ_BLOCK = 1024 * 8 + READ_SIZE = 1024 * 4 def fill_rbuf - @buffer << @socket.sysread( READ_BLOCK ) + unless IO.select [@socket], nil, nil, @read_timeout then + on_read_timeout + end + @buffer << @socket.sysread( READ_SIZE ) + end + + def on_read_timeout + raise TimeoutError, "socket read timeout (#{@read_timeout} sec)" end def writeinto( dest, len ) @@ -610,20 +654,18 @@ module Net end - ### - ### write - ### + # + # write interfece + # public - def write( str ) writing { do_write str } end - def writeline( str ) writing { do_write str @@ -631,7 +673,6 @@ module Net } end - def write_bin( src, block ) writing { if block then @@ -644,7 +685,6 @@ module Net } end - def write_pendstr( src, block ) @pipe << "writing text from #{src.type}\n" if @pipe; pipeoff diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 8dd2a3c811..78cfc23186 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -30,10 +30,8 @@ Net::Protocol === Methods -: start( helo_domain = Socket.gethostname, \ - account = nil, password = nil, authtype = nil ) -: start( helo_domain = Socket.gethostname, \ - account = nil, password = nil, authtype = nil ) {|smtp| .... } +: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) +: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... } opens TCP connection and starts SMTP session. If protocol had been started, do nothing and return false. @@ -53,10 +51,10 @@ Net::Protocol to_addrs must be a String(s) or an Array of String. 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) + * Net::ProtoSyntaxError: syntax error (errno.500) + * Net::ProtoFatalError: fatal error (errno.550) + * Net::ProtoUnknownError: unknown error + * Net::ProtoServerBusy: temporary error (errno.420/450) # usage example -- cgit v1.2.3