diff options
author | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-03-06 17:08:21 +0000 |
---|---|---|
committer | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2004-03-06 17:08:21 +0000 |
commit | 3eedf9156cf01751423a99ef2939ec81964155d2 (patch) | |
tree | 9cc29f6a4e68821dcdaa57058bf44871bcd4fd8c /lib | |
parent | dd53813e38e2839b08cc540df6296c392dbf3c25 (diff) | |
download | ruby-3eedf9156cf01751423a99ef2939ec81964155d2.tar.gz |
* lib/net/http.rb: spin off https code again.
* lib/net/https.rb: new file.
* ext/openssl/lib/net/https.rb: removed. moved to net/https with modifications.
* ext/openssl/lib/net/protocol.rb: removed. merged with net/http.
* lib/net/protocol.rb: new class BufferedIO.
* lib/net/protocol.rb: InternetMessageIO < BufferedIO.
* lib/net/protocol.rb: BufferedIO.new takes an IO.
* lib/net/smtp.rb: follow InternetMessageIO's change.
* lib/net/pop.rb: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5908 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r-- | lib/net/http.rb | 117 | ||||
-rw-r--r-- | lib/net/https.rb | 177 | ||||
-rw-r--r-- | lib/net/pop.rb | 25 | ||||
-rw-r--r-- | lib/net/protocol.rb | 227 | ||||
-rw-r--r-- | lib/net/smtp.rb | 30 |
5 files changed, 350 insertions, 226 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb index 540c878c01..a4891f1c49 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1,10 +1,11 @@ # # = net/http.rb # -# Copyright (c) 1999-2003 Yukihiro Matsumoto -# Copyright (c) 1999-2003 Minero Aoki +# Copyright (C) 1999-2003 Yukihiro Matsumoto +# Copyright (C) 1999-2003 Minero Aoki # -# Written & maintained by Minero Aoki <aamine@loveruby.net>. +# Written and maintained by Minero Aoki <aamine@loveruby.net>. +# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>. # # This file is derived from "http-access.rb". # @@ -25,11 +26,6 @@ require 'net/protocol' require 'uri' -begin - require 'net/protocols' -rescue LoadError -end - module Net # :nodoc: @@ -355,30 +351,16 @@ module Net # :nodoc: def initialize(address, port = nil) @address = address @port = (port || HTTP.default_port) - @curr_http_version = HTTPVersion @seems_1_0_server = false @close_on_empty_response = false @socket = nil @started = false - @open_timeout = 30 @read_timeout = 60 - @debug_output = nil - - # ssl @use_ssl = false - @key = nil - @cert = nil - @ca_file = nil - @ca_path = nil - @verify_mode = nil - @verify_callback = nil - @verify_depth = nil - @ssl_timeout = nil - @cert_store = nil - @peer_cert = nil + @ssl_context = nil end def inspect @@ -432,29 +414,9 @@ module Net # :nodoc: # returns true if use SSL/TLS with HTTP. def use_ssl? - @use_ssl - end - - alias use_ssl use_ssl? #:nodoc: - - # turn on/off SSL. - # This flag must be set before starting session. - # If you change use_ssl value after session started, - # a Net::HTTP object raises IOError. - def use_ssl=(flag) - flag = (flag ? true : false) - raise IOError, "use_ssl value changed but session already started" if started? and @use_ssl != flag - @use_ssl = flag + false # redefined in net/https end - attr_writer :key, :cert - attr_writer :ca_file, :ca_path - attr_writer :verify_mode, :verify_callback, :verify_depth - attr_writer :cert_store, :ssl_timeout - attr_reader :peer_cert - - alias timeout= ssl_timeout= # for backward compatibility - # Opens TCP connection and HTTP session. # # When this method is called with block, gives a HTTP object @@ -479,38 +441,34 @@ module Net # :nodoc: end def do_start + connect + @started = true + end + private :do_start + + def connect + s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) } if use_ssl? - require 'net/protocols' - sockclass = SSLIO - else - sockclass = InternetMessageIO - end - @socket = sockclass.open(conn_address(), conn_port(), - @open_timeout, @read_timeout, @debug_output) - if use_ssl? - if proxy? - @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', - @address, @port, HTTP_VERSION) - @socket.writeline '' - res = HTTPResponse.read_new(@socket) - res.value + unless @ssl_context.verify_mode + warn "warning: peer certificate won't be verified in this SSL session" + @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE end - @socket.key = @key if @key - @socket.cert = @cert if @cert - @socket.ca_file = @ca_file - @socket.ca_path = @ca_path - @socket.verify_mode = @verify_mode - @socket.verify_callback = @verify_callback - @socket.verify_depth = @verify_depth - @socket.timeout = @ssl_timeout - @socket.cert_store = @cert_store - @socket.ssl_connect - @peer_cert = @socket.peer_cert + s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) + s.sync_close = true + s.connect + end + @socket = BufferedIO.new(s) + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output + if use_ssl? and proxy? + @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', + @address, @port, HTTP_VERSION) + @socket.writeline '' + HTTPResponse.read_new(@socket).value end on_connect - @started = true end - private :do_start + private :connect def on_connect end @@ -966,7 +924,7 @@ module Net # :nodoc: req.exec @socket, @curr_http_version, edit_path(req.path), body begin res = HTTPResponse.read_new(@socket) - end while HTTPContinue === res + end while res.kind_of?(HTTPContinue) res.reading_body(@socket, req.response_body_permitted?) { yield res if block_given? } @@ -979,8 +937,7 @@ module Net # :nodoc: def begin_transport(req) if @socket.closed? - @socket.reopen @open_timeout - on_connect + connect end if @seems_1_0_server req['connection'] = 'close' @@ -1042,6 +999,7 @@ module Net # :nodoc: HTTPSession = HTTP + # # Header module. # @@ -1219,6 +1177,7 @@ module Net # :nodoc: end + # # Parent of HTTPRequest class. Do not use this directly; use # a subclass of HTTPRequest. @@ -1319,7 +1278,6 @@ module Net # :nodoc: class HTTP - class Get < HTTPRequest METHOD = 'GET' REQUEST_HAS_BODY = false @@ -1397,7 +1355,6 @@ module Net # :nodoc: REQUEST_HAS_BODY = true RESPONSE_HAS_BODY = true end - end @@ -1717,9 +1674,7 @@ module Net # :nodoc: '505' => HTTPVersionNotSupported } - - class << self - + class << HTTPResponse def read_new(sock) #:nodoc: internal use only httpv, code, msg = read_status_line(sock) res = response_class(code).new(httpv, code, msg) @@ -1730,7 +1685,6 @@ module Net # :nodoc: res[k] = v end end - res end @@ -1758,7 +1712,6 @@ module Net # :nodoc: yield m[1], m.post_match end end - end # next is to fix bug in RDoc, where the private inside class << self @@ -1825,7 +1778,7 @@ module Net # :nodoc: # Raises HTTP error if the response is not 2xx. def value - error! unless HTTPSuccess === self + error! unless self.kind_of?(HTTPSuccess) end # diff --git a/lib/net/https.rb b/lib/net/https.rb new file mode 100644 index 0000000000..4200be3d62 --- /dev/null +++ b/lib/net/https.rb @@ -0,0 +1,177 @@ +=begin + += $RCSfile$ -- SSL/TLS enhancement for Net::HTTP. + +== Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU Yuuzou <gotoyuzo@notwork.org> + All rights reserved. + +== Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + +== Requirements + This program requires Net 1.2.0 or higher version. + You can get it from RAA or Ruby's CVS repository. + +== Version + $Id$ + + 2001-11-06: Contiributed to Ruby/OpenSSL project. + 2004-03-06: Some code is merged in to net/http. + +== Example + +Here is a simple HTTP client: + + require 'net/http' + require 'uri' + + uri = URI.parse(ARGV[0] || 'http://localhost/') + http = Net::HTTP.new(uri.host, uri.port) + http.start { + http.request_get(uri.path) {|res| + print res.body + } + } + +It can be replaced by the following code: + + require 'net/https' + require 'uri' + + uri = URI.parse(ARGV[0] || 'https://localhost/') + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true if uri.scheme == "https" # enable SSL/TLS + http.start { + http.request_get(uri.path) {|res| + print res.body + } + } + +== class Net::HTTP + +=== Instance Methods + +: use_ssl? + returns true if use SSL/TLS with HTTP. + +: use_ssl=((|true_or_false|)) + sets use_ssl. + +: peer_cert + return the X.509 certificates the server presented. + +: key, key=((|key|)) + Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + (This method is appeared in Michal Rokos's OpenSSL extention.) + +: key_file, key_file=((|path|)) + Sets a private key file to use in PEM format. + +: cert, cert=((|cert|)) + Sets an OpenSSL::X509::Certificate object as client certificate + (This method is appeared in Michal Rokos's OpenSSL extention). + +: cert_file, cert_file=((|path|)) + Sets pathname of a X.509 certification file in PEM format. + +: ca_file, ca_file=((|path|)) + Sets path of a CA certification file in PEM format. + The file can contrain several CA certificats. + +: ca_path, ca_path=((|path|)) + Sets path of a CA certification directory containing certifications + in PEM format. + +: verify_mode, verify_mode=((|mode|)) + Sets the flags for server the certification verification at + begining of SSL/TLS session. + OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable. + +: verify_callback, verify_callback=((|proc|)) + Sets the verify callback for the server certification verification. + +: verify_depth, verify_depth=((|num|)) + Sets the maximum depth for the certificate chain verification. + +: cert_store, cert_store=((|store|)) + Sets the X509::Store to verify peer certificate. + +: ssl_timeout, ssl_timeout=((|sec|)) + Sets the SSL timeout seconds. + +=end + +require 'net/http' +require 'openssl' + +module Net + + class HTTP + remove_method :use_ssl? + def use_ssl? + @use_ssl + end + + alias use_ssl use_ssl? # for backward compatibility + + # Turn on/off SSL. + # This flag must be set before starting session. + # If you change use_ssl value after session started, + # a Net::HTTP object raises IOError. + def use_ssl=(flag) + flag = (flag ? true : false) + raise IOError, "use_ssl value changed but session already started" \ + if started? and @use_ssl != flag + if flag and not @ssl_context + @ssl_context = OpenSSL::SSL::SSLContext.new + end + @use_ssl = flag + end + + def self.ssl_context_accessor(name) + module_eval(<<-End, __FILE__, __LINE__ + 1) + def #{name} + return nil unless @ssl_context + @ssl_context.#{name} + end + + def #{name}=(val) + @ssl_context ||= OpenSSL::SSL::SSLContext.new + @ssl_context.#{name} = val + end + End + end + + ssl_context_accessor :key + ssl_context_accessor :cert + ssl_context_accessor :ca_file + ssl_context_accessor :ca_path + ssl_context_accessor :verify_mode + ssl_context_accessor :verify_callback + ssl_context_accessor :verify_depth + ssl_context_accessor :cert_store + + def ssl_timeout + return nil unless @ssl_context + @ssl_context.timeout + end + + def ssl_timeout=(sec) + raise ArgumentError, 'Net::HTTP#ssl_timeout= called but use_ssl=false' \ + unless use_ssl? + @ssl_context ||= OpenSSL::SSL::SSLContext.new + @ssl_context.timeout = sec + end + + alias timeout= ssl_timeout= # for backward compatibility + + def peer_cert + return nil if not use_ssl? or not @socket + @socket.io.peer_cert + end + end + +end diff --git a/lib/net/pop.rb b/lib/net/pop.rb index 5ada68fd4d..278b949395 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -1,17 +1,17 @@ # = net/pop.rb # #-- -# Copyright (c) 1999-2003 Yukihiro Matsumoto -# Copyright (c) 1999-2003 Minero Aoki +# Copyright (c) 1999-2004 Yukihiro Matsumoto +# Copyright (c) 1999-2004 Minero Aoki # -# written & maintained by Minero Aoki <aamine@loveruby.net> +# written and maintained by Minero Aoki <aamine@loveruby.net> # # This program is free software. You can re-distribute and/or # modify this program under the same terms as Ruby itself, -# Ruby Distribute License or GNU General Public License. +# Ruby Distribute License. # -# NOTE: You can find Japanese version of this document in -# the doc/net directory of the standard ruby interpreter package. +# NOTE: You can find Japanese version of this document at: +# http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=net%2Fpop.rb # # $Id$ #++ @@ -168,6 +168,7 @@ require 'net/protocol' require 'digest/md5' +require 'timeout' module Net @@ -424,8 +425,12 @@ module Net end def do_start( account, password ) - @socket = self.class.socket_type.open(@address, @port, - @open_timeout, @read_timeout, @debug_output) + @socket = InternetMessageIO.new(timeout(@open_timeout) { + TCPSocket.open(@address, @port) + }) + logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})" + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output on_connect @command = POP3Command.new(@socket) if apop? @@ -558,6 +563,10 @@ module Net end end + def logging(msg) + @debug_output << msg if @debug_output + end + end # class POP3 # class aliases diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index 78fd8dc3d8..aaed060a51 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -46,61 +46,33 @@ module Net # :nodoc: ProtocRetryError = ProtoRetriableError - class InternetMessageIO #:nodoc: internal use only - class << self - alias open new - end - - def initialize(addr, port, open_timeout = nil, read_timeout = nil, debug_output = nil) - @address = addr - @port = port - @read_timeout = read_timeout - @debug_output = debug_output - @socket = nil - @rbuf = nil # read buffer - @wbuf = nil # write buffer - connect open_timeout - LOG 'opened' - end - - def connect(open_timeout) - LOG "opening connection to #{@address}..." - timeout(open_timeout) { - @socket = TCPsocket.new(@address, @port) - } + class BufferedIO #:nodoc: internal use only + def initialize(io) + @io = io + @read_timeout = 60 + @debug_output = nil @rbuf = '' end - private :connect - def close - if @socket - @socket.close - LOG 'closed' - else - LOG 'close call for already closed socket' - end - @socket = nil - @rbuf = '' - end + attr_reader :io + attr_accessor :read_timeout + attr_accessor :debug_output - def reopen(open_timeout = nil) - LOG 'reopening...' - close - connect open_timeout - LOG 'reopened' + def inspect + "#<#{self.class} io=#{@io}>" end def closed? - not @socket + @io.closed? end - def inspect - "#<#{self.class} #{closed?() ? 'closed' : 'opened'}>" + def close + @io.close end - ### - ### READ - ### + # + # Read + # public @@ -154,32 +126,13 @@ module Net # :nodoc: readuntil("\n").chop end - def each_message_chunk - LOG 'reading message...' - LOG_off() - read_bytes = 0 - while (line = readuntil("\r\n")) != ".\r\n" - read_bytes += line.size - yield line.sub(/\A\./, '') - end - LOG_on() - LOG "read message (#{read_bytes} bytes)" - end - - # *library private* (cannot handle 'break') - def each_list_item - while (str = readuntil("\r\n")) != ".\r\n" - yield str.chop - end - end - private def rbuf_fill - until IO.select([@socket], nil, nil, @read_timeout) + until IO.select([@io], nil, nil, @read_timeout) raise TimeoutError, "socket read timeout (#{@read_timeout} sec)" end - @rbuf << @socket.sysread(1024) + @rbuf << @io.sysread(1024) end def rbuf_consume(len) @@ -188,9 +141,9 @@ module Net # :nodoc: s end - ### - ### WRITE - ### + # + # Write + # public @@ -206,6 +159,88 @@ module Net # :nodoc: } end + private + + def writing + @written_bytes = 0 + @debug_output << '<- ' if @debug_output + yield + @debug_output << "\n" if @debug_output + bytes = @written_bytes + @written_bytes = nil + bytes + end + + def write0(str) + @debug_output << str.dump if @debug_output + len = @io.write(str) + @written_bytes += len + len + end + + # + # Logging + # + + private + + def LOG_off + @save_debug_out = @debug_output + @debug_output = nil + end + + def LOG_on + @debug_output = @save_debug_out + end + + def LOG(msg) + return unless @debug_output + @debug_output << msg + "\n" + end + end + + + class InternetMessageIO < BufferedIO #:nodoc: internal use only + def initialize(io) + super + @wbuf = nil + end + + # + # Read + # + + def each_message_chunk + LOG 'reading message...' + LOG_off() + read_bytes = 0 + while (line = readuntil("\r\n")) != ".\r\n" + read_bytes += line.size + yield line.sub(/\A\./, '') + end + LOG_on() + LOG "read message (#{read_bytes} bytes)" + end + + # *library private* (cannot handle 'break') + def each_list_item + while (str = readuntil("\r\n")) != ".\r\n" + yield str.chop + end + end + + def write_message_0(src) + prev = @written_bytes + each_crlf_line(src) do |line| + write0 line.sub(/\A\./, '..') + end + @written_bytes - prev + end + + # + # Write + # + def write_message(src) LOG "writing message from #{src.class}" LOG_off() @@ -238,42 +273,6 @@ module Net # :nodoc: private - def writing - @written_bytes = 0 - @debug_output << '<- ' if @debug_output - yield - @socket.flush - @debug_output << "\n" if @debug_output - bytes = @written_bytes - @written_bytes = nil - bytes - end - - def write0(str) - @debug_output << str.dump if @debug_output - len = @socket.write(str) - @written_bytes += len - len - end - - # - # Reads string from src calling :each, and write to @socket. - # Escapes '.' on the each line head. - # - def write_message_0(src) - prev = @written_bytes - each_crlf_line(src) do |line| - if line[0] == ?. - then write0 '.' + line - else write0 line - end - end - @written_bytes - prev - end - - # - # setup @wbuf for each_crlf_line. - # def using_each_crlf_line @wbuf = '' yield @@ -315,26 +314,6 @@ module Net # :nodoc: yield unless buf.empty? end end - - ### - ### DEBUG - ### - - private - - def LOG_off - @save_debug_out = @debug_output - @debug_output = nil - end - - def LOG_on - @debug_output = @save_debug_out - end - - def LOG(msg) - return unless @debug_output - @debug_output << msg + "\n" - end end diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 21c04812fe..123d31d205 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -1,17 +1,16 @@ # = net/smtp.rb -# +# #-- -# Copyright (c) 1999-2003 Yukihiro Matsumoto -# Copyright (c) 1999-2003 Minero Aoki +# Copyright (C) 1999-2004 Yukihiro Matsumoto +# Copyright (C) 1999-2004 Minero Aoki # -# written & maintained by Minero Aoki <aamine@loveruby.net> +# written and maintained by Minero Aoki <aamine@loveruby.net> # # This program is free software. You can re-distribute and/or -# modify this program under the same terms as Ruby itself, -# Ruby Distribute License or GNU General Public License. +# modify this program under the same terms as Ruby itself. # -# NOTE: You can find Japanese version of this document in -# the doc/net directory of the standard ruby interpreter package. +# NOTE: You can find Japanese version of this document at: +# http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=net%2Fsmtp.rb # # $Id$ #++ @@ -120,7 +119,7 @@ require 'net/protocol' require 'digest/md5' - +require 'timeout' module Net # :nodoc: @@ -343,9 +342,12 @@ module Net # :nodoc: raise IOError, 'SMTP session already started' if @started check_auth_args user, secret, authtype if user or secret - @socket = InternetMessageIO.open(@address, @port, - @open_timeout, @read_timeout, - @debug_output) + @socket = InternetMessageIO.new(timeout(@open_timeout) { + TCPSocket.open(@address, @port) + }) + logging "SMTP session opened: #{@address}:#{@port}" + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output check_response(critical { recv_response() }) begin if @esmtp @@ -629,6 +631,10 @@ module Net # :nodoc: end end + def logging(msg) + @debug_output << msg if @debug_output + end + end # class SMTP SMTPSession = SMTP |