class CouchRest::Connection
Handle connections to the CouchDB server and provide a set of HTTP based methods to perform requests.
All connections are persistent and thread safe. A connection cannot be re-used to connect to other servers once instantiated.
Six types of REST requests are supported: get, put, post, delete, copy and head.
Requests that do not have a payload like GET, DELETE and COPY, accept the URI and options parameters. PUT and POST both expect a document as the second parameter.
The API will share the options between the HTTP connection and JSON parser.
When initializing a connection, the following options are available:
* `:timeout` (or `:read_timeout`) and `:open_timeout` the time in miliseconds to wait for the request, see the [Net HTTP Persistent documentation](http://docs.seattlerb.org/net-http-persistent/Net/HTTP/Persistent.html#attribute-i-read_timeout) for more details. * `:verify_ssl` verify ssl certificates (or not) * `:ssl_client_cert`, `:ssl_client_key` parameters controlling ssl client certificate authentication * `:ssl_ca_file` load additional CA certificates from a file (or directory)
The following request options are supported:
* `:content_type`, type of content to be sent, especially useful when sending files as this will set the file type. The default is :json. * `:accept`, the content type to accept in the response. This should pretty much always be `:json`. * `:payload` override the document or data sent in the message body (only PUT or POST). * `:headers` any additional headers (overrides :content_type and :accept if provided).
When :raw is true in PUT and POST requests, no attempt will be made to convert the document payload to JSON. This is not normally necessary as IO and Tempfile objects will not be parsed anyway. The result of the request will always be parsed.
For all other requests, mainly GET, the :raw option will make no attempt to parse the result. This is useful for receiving files from the database.
Constants
- DEFAULT_HEADERS
- HEADER_CONTENT_SYMBOL_MAP
- KNOWN_PARSER_OPTIONS
- SUCCESS_RESPONSE_CODES
Attributes
Default proxy URL to use in all connections.
Public Class Methods
# File lib/couchrest/connection.rb, line 58 def initialize(uri, options = {}) raise "CouchRest::Connection.new requires URI::HTTP(S) parameter" unless uri.is_a?(URI::HTTP) @uri = clean_uri(uri) @options = options.dup @http = prepare_http_connection end
Public Instance Methods
Send a COPY request to the URI provided.
# File lib/couchrest/connection.rb, line 86 def copy(path, destination, options = {}) opts = options.nil? ? {} : options.dup opts[:headers] = options[:headers].nil? ? {} : options[:headers].dup opts[:headers]['Destination'] = destination execute('COPY', path, opts) end
Send a DELETE request.
# File lib/couchrest/connection.rb, line 81 def delete(path, options = {}) execute('DELETE', path, options) end
Send a GET request.
# File lib/couchrest/connection.rb, line 66 def get(path, options = {}, &block) execute('GET', path, options, nil, &block) end
Send a HEAD request.
# File lib/couchrest/connection.rb, line 94 def head(path, options = {}) options = options.merge(:head => true) # No parsing! execute('HEAD', path, options) end
Send a POST request.
# File lib/couchrest/connection.rb, line 76 def post(path, doc = nil, options = {}, &block) execute('POST', path, options, doc, &block) end
Send a PUT request.
# File lib/couchrest/connection.rb, line 71 def put(path, doc = nil, options = {}) execute('PUT', path, options, doc) end
Private Instance Methods
Duplicate and remove excess baggage from the provided URI
# File lib/couchrest/connection.rb, line 102 def clean_uri(uri) uri = uri.dup uri.path = "" uri.query = nil uri.fragment = nil uri end
# File lib/couchrest/connection.rb, line 252 def convert_content_type(type) if type.is_a?(Symbol) case type when :json 'application/json' end else type end end
# File lib/couchrest/connection.rb, line 140 def execute(method, path, options, payload = nil, &block) req = { :method => method, :uri => uri + path } # Prepare the request headers DEFAULT_HEADERS.merge(parse_and_convert_request_headers(options)).each do |key, value| req[:header] ||= {} req[:header][key] = value end # Prepare the request body, if provided unless payload.nil? req[:body] = payload_from_doc(req, payload, options) end send_and_parse_response(req, options, &block) end
# File lib/couchrest/connection.rb, line 182 def handle_response_code(response) raise_response_error(response) unless SUCCESS_RESPONSE_CODES.include?(response.status) end
# File lib/couchrest/connection.rb, line 220 def mime_for(path) mime = MIME::Types.type_for path mime.empty? ? 'text/plain' : mime[0].content_type end
# File lib/couchrest/connection.rb, line 242 def parse_and_convert_request_headers(options) headers = options.include?(:headers) ? options[:headers].dup : {} HEADER_CONTENT_SYMBOL_MAP.each do |sym, key| if options.include?(sym) headers[key] = convert_content_type(options[sym]) end end headers end
# File lib/couchrest/connection.rb, line 194 def parse_body(body, opts) if opts[:raw] # passthru body else MultiJson.load(body, prepare_json_load_options(opts)) end end
# File lib/couchrest/connection.rb, line 186 def parse_response(response, opts) if opts[:head] opts[:raw] ? response.http_header.dump : response.headers else parse_body(response.body, opts) end end
Check if the provided doc is nil or special IO device or temp file. If not, encode it into a string.
The options supported are:
-
:raw TrueClass, if true the payload will not be altered.
# File lib/couchrest/connection.rb, line 209 def payload_from_doc(req, doc, opts = {}) if doc.is_a?(IO) || doc.is_a?(StringIO) || doc.is_a?(Tempfile) # attachments req[:header]['Content-Type'] = mime_for(req[:uri].path) doc elsif opts[:raw] || doc.nil? doc else MultiJson.encode(doc.respond_to?(:as_couch_json) ? doc.as_couch_json : doc) end end
Take a look at the options povided and try to apply them to the HTTP conneciton.
# File lib/couchrest/connection.rb, line 111 def prepare_http_connection conn = HTTPClient.new(options[:proxy] || self.class.proxy) set_http_connection_options(conn, options) conn end
# File lib/couchrest/connection.rb, line 231 def prepare_json_load_options(opts = {}) options = { :create_additions => CouchRest.decode_json_objects, # For object conversion, if required :max_nesting => false } KNOWN_PARSER_OPTIONS.each do |k| options[k] = opts[k] if opts.include?(k) end options end
# File lib/couchrest/connection.rb, line 225 def raise_response_error(response) exp = CouchRest::Exceptions::EXCEPTIONS_MAP[response.status] exp ||= CouchRest::RequestFailed raise exp.new(response) end
# File lib/couchrest/connection.rb, line 160 def send_and_parse_response(req, opts, &block) if block_given? parser = CouchRest::StreamRowParser.new(opts[:continuous] ? :feed : :array) response = send_request(req) do |chunk| parser.parse(chunk) do |doc| block.call(parse_body(doc, opts)) end end handle_response_code(response) parse_body(parser.header, opts) else response = send_request(req) handle_response_code(response) parse_response(response, opts) end end
Send request, and leave a reference to the response for debugging purposes
# File lib/couchrest/connection.rb, line 178 def send_request(req, &block) @last_response = @http.request(req.delete(:method), req.delete(:uri), req, &block) end
Prepare the http connection options for HTTPClient. We try to maintain RestClient
compatability in option names as this is what we used before.
# File lib/couchrest/connection.rb, line 119 def set_http_connection_options(conn, opts) # Authentication unless uri.user.to_s.empty? conn.force_basic_auth = true conn.set_auth(uri.to_s, uri.user, uri.password) end # SSL Certificate option mapping if opts.include?(:verify_ssl) conn.ssl_config.verify_mode = opts[:verify_ssl] ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE end conn.ssl_config.client_cert = opts[:ssl_client_cert] if opts.include?(:ssl_client_cert) conn.ssl_config.client_key = opts[:ssl_client_key] if opts.include?(:ssl_client_key) conn.ssl_config.set_trust_ca(opts[:ssl_ca_file]) if opts.include?(:ssl_ca_file) # Timeout options conn.receive_timeout = opts[:timeout] if opts.include?(:timeout) conn.connect_timeout = opts[:open_timeout] if opts.include?(:open_timeout) conn.send_timeout = opts[:read_timeout] if opts.include?(:read_timeout) end