aboutsummaryrefslogtreecommitdiffstats
path: root/lib/net
diff options
context:
space:
mode:
authoraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-03-06 17:08:21 +0000
committeraamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-03-06 17:08:21 +0000
commit3eedf9156cf01751423a99ef2939ec81964155d2 (patch)
tree9cc29f6a4e68821dcdaa57058bf44871bcd4fd8c /lib/net
parentdd53813e38e2839b08cc540df6296c392dbf3c25 (diff)
downloadruby-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/net')
-rw-r--r--lib/net/http.rb117
-rw-r--r--lib/net/https.rb177
-rw-r--r--lib/net/pop.rb25
-rw-r--r--lib/net/protocol.rb227
-rw-r--r--lib/net/smtp.rb30
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