diff --git a/lib/aws/s3/connection.rb b/lib/aws/s3/connection.rb index 1b91127..7d860c5 100644 --- a/lib/aws/s3/connection.rb +++ b/lib/aws/s3/connection.rb @@ -1,50 +1,51 @@ module AWS module S3 class Connection #:nodoc: + @@conn_name = nil class << self def connect(options = {}) new(options) end - + def prepare_path(path) path = path.remove_extended unless path.valid_utf8? URI.escape(path) end end - + attr_reader :access_key_id, :secret_access_key, :http, :options - - # Creates a new connection. Connections make the actual requests to S3, though these requests are usually + + # Creates a new connection. Connections make the actual requests to S3, though these requests are usually # called from subclasses of Base. - # + # # For details on establishing connections, check the Connection::Management::ClassMethods. def initialize(options = {}) @options = Options.new(options) connect end - + def request(verb, path, headers = {}, body = nil, attempts = 0, &block) - body.rewind if body.respond_to?(:rewind) unless attempts.zero? - - requester = Proc.new do + body.rewind if body.respond_to?(:rewind) unless attempts.zero? + + requester = Proc.new do path = self.class.prepare_path(path) if attempts.zero? # Only escape the path once request = request_method(verb).new(path, headers) ensure_content_type!(request) add_user_agent!(request) authenticate!(request) if body - if body.respond_to?(:read) - request.body_stream = body - else - request.body = body + if body.respond_to?(:read) + request.body_stream = body + else + request.body = body end - request.content_length = body.respond_to?(:lstat) ? body.stat.size : body.size + request.content_length = body.respond_to?(:lstat) ? body.stat.size : body.size else - request.content_length = 0 + request.content_length = 0 end http.request(request, &block) end - + if persistent? http.start unless http.started? requester.call @@ -55,11 +56,11 @@ def request(verb, path, headers = {}, body = nil, attempts = 0, &block) @http = create_connection attempts == 3 ? raise : (attempts += 1; retry) end - + def url_for(path, options = {}) authenticate = options.delete(:authenticated) # Default to true unless explicitly false - authenticate = true if authenticate.nil? + authenticate = true if authenticate.nil? path = self.class.prepare_path(path) request = request_method(:get).new(path, {}) query_string = query_string_authentication(request, options) @@ -67,18 +68,18 @@ def url_for(path, options = {}) url << "?#{query_string}" if authenticate end end - + def subdomain http.address[/^([^.]+).#{DEFAULT_HOST}$/, 1] end - + def persistent? options[:persistent] end - + def protocol(options = {}) # This always trumps http.use_ssl? - if options[:use_ssl] == false + if options[:use_ssl] == false 'http://' elsif options[:use_ssl] || http.use_ssl? 'https://' @@ -86,7 +87,7 @@ def protocol(options = {}) 'http://' end end - + private def extract_keys! missing_keys = [] @@ -95,14 +96,14 @@ def extract_keys! @secret_access_key = extract_key[:secret_access_key] raise MissingAccessKey.new(missing_keys) unless missing_keys.empty? end - + def create_connection http = http_class.new(options[:server], options[:port]) http.use_ssl = !options[:use_ssl].nil? || options[:port] == 443 http.verify_mode = OpenSSL::SSL::VERIFY_NONE http end - + def http_class if options.connecting_through_proxy? Net::HTTP::Proxy(*options.proxy_settings) @@ -110,7 +111,7 @@ def http_class Net::HTTP end end - + def connect extract_keys! @http = create_connection @@ -124,16 +125,16 @@ def port_string def ensure_content_type!(request) request['Content-Type'] ||= 'binary/octet-stream' end - + # Just do Header authentication for now def authenticate!(request) request['Authorization'] = Authentication::Header.new(request, access_key_id, secret_access_key) end - + def add_user_agent!(request) request['User-Agent'] ||= "AWS::S3/#{Version}" end - + def query_string_authentication(request, options = {}) Authentication::QueryString.new(request, access_key_id, secret_access_key, options) end @@ -141,29 +142,29 @@ def query_string_authentication(request, options = {}) def request_method(verb) Net::HTTP.const_get(verb.to_s.capitalize) end - + def method_missing(method, *args, &block) options[method] || super end - + module Management #:nodoc: def self.included(base) base.cattr_accessor :connections base.connections = {} base.extend ClassMethods end - + # Manage the creation and destruction of connections for AWS::S3::Base and its subclasses. Connections are # created with establish_connection!. module ClassMethods # Creates a new connection with which to make requests to the S3 servers for the calling class. - # + # # AWS::S3::Base.establish_connection!(:access_key_id => '...', :secret_access_key => '...') # # You can set connections for every subclass of AWS::S3::Base. Once the initial connection is made on # Base, all subsequent connections will inherit whatever values you don't specify explictly. This allows you to - # customize details of the connection, such as what server the requests are made to, by just specifying one - # option. + # customize details of the connection, such as what server the requests are made to, by just specifying one + # option. # # AWS::S3::Bucket.established_connection!(:use_ssl => true) # @@ -185,23 +186,23 @@ module ClassMethods # argument is set. # * :use_ssl - Whether requests should be made over SSL. If set to true, the :port argument # will be implicitly set to 443, unless specified otherwise. Defaults to false. - # * :persistent - Whether to use a persistent connection to the server. Having this on provides around a two fold + # * :persistent - Whether to use a persistent connection to the server. Having this on provides around a two fold # performance increase but for long running processes some firewalls may find the long lived connection suspicious and close the connection. # If you run into connection errors, try setting :persistent to false. Defaults to false. # * :proxy - If you need to connect through a proxy, you can specify your proxy settings by specifying a :host, :port, :user, and :password # with the :proxy option. - # The :host setting is required if specifying a :proxy. - # + # The :host setting is required if specifying a :proxy. + # # AWS::S3::Bucket.established_connection!(:proxy => { # :host => '...', :port => 8080, :user => 'marcel', :password => 'secret' # }) def establish_connection!(options = {}) - # After you've already established the default connection, just specify + # After you've already established the default connection, just specify # the difference for subsequent connections - options = default_connection.options.merge(options) if connected? + @@conn_name = options[:connection_name] ? options[:connection_name] : nil connections[connection_name] = Connection.connect(options) end - + # Returns the connection for the current class, or Base's default connection if the current class does not # have its own connection. # @@ -213,29 +214,29 @@ def connection raise NoConnectionEstablished end end - + # Returns true if a connection has been made yet. def connected? !connections.empty? end - + # Removes the connection for the current class. If there is no connection for the current class, the default # connection will be removed. def disconnect(name = connection_name) - name = default_connection unless connections.has_key?(name) + name = default_connection unless connections.has_key?(@@conn_name) connection = connections[name] connection.http.finish if connection.persistent? connections.delete(name) end - - # Clears *all* connections, from all classes, with prejudice. + + # Clears *all* connections, from all classes, with prejudice. def disconnect! connections.each_key {|connection| disconnect(connection)} end private def connection_name - name + @@conn_name || name end def default_connection_name @@ -247,10 +248,10 @@ def default_connection end end end - + class Options < Hash #:nodoc: - VALID_OPTIONS = [:access_key_id, :secret_access_key, :server, :port, :use_ssl, :persistent, :proxy].freeze - + VALID_OPTIONS = [:access_key_id, :secret_access_key, :server, :port, :use_ssl, :persistent, :proxy, :connection_name].freeze + def initialize(options = {}) super() validate(options) @@ -261,11 +262,11 @@ def initialize(options = {}) def connecting_through_proxy? !self[:proxy].nil? end - + def proxy_settings self[:proxy].values_at(:host, :port, :user, :password) end - + private def validate(options) invalid_options = options.keys - VALID_OPTIONS @@ -275,4 +276,5 @@ def validate(options) end end end -end \ No newline at end of file +end +