class CouchRest::Connection

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

proxy[RW]

Default proxy URL to use in all connections.

http[R]
last_response[R]
options[R]
uri[R]

Public Class Methods

new(uri, options = {}) click to toggle source
# 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

copy(path, destination, options = {}) click to toggle source

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
delete(path, options = {}) click to toggle source

Send a DELETE request.

# File lib/couchrest/connection.rb, line 81
def delete(path, options = {})
  execute('DELETE', path, options)
end
get(path, options = {}, &block) click to toggle source

Send a GET request.

# File lib/couchrest/connection.rb, line 66
def get(path, options = {}, &block)
  execute('GET', path, options, nil, &block)
end
head(path, options = {}) click to toggle source

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
post(path, doc = nil, options = {}, &block) click to toggle source

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
put(path, doc = nil, options = {}) click to toggle source

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

clean_uri(uri) click to toggle source

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
convert_content_type(type) click to toggle source
# 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
execute(method, path, options, payload = nil, &block) click to toggle source
# 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
handle_response_code(response) click to toggle source
# File lib/couchrest/connection.rb, line 182
def handle_response_code(response)
  raise_response_error(response) unless SUCCESS_RESPONSE_CODES.include?(response.status)
end
mime_for(path) click to toggle source
# 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
parse_and_convert_request_headers(options) click to toggle source
# 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
parse_body(body, opts) click to toggle source
# 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
parse_response(response, opts) click to toggle source
# 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
payload_from_doc(req, doc, opts = {}) click to toggle source

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
prepare_http_connection() click to toggle source

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
prepare_json_load_options(opts = {}) click to toggle source
# 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
raise_response_error(response) click to toggle source
# 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
send_and_parse_response(req, opts, &block) click to toggle source
# 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(req, &block) click to toggle source

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
set_http_connection_options(conn, opts) click to toggle source

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