diff --git a/elasticsearch/connection.py b/elasticsearch/connection.py new file mode 100644 index 00000000..71845ed8 --- /dev/null +++ b/elasticsearch/connection.py @@ -0,0 +1,46 @@ +import logging +import time +import requests +import json + +from .exceptions import TransportError, HTTP_EXCEPTIONS + +logger = logging.getLogger('elasticsearch') +tracer = logging.getLogger('elasticsearch.trace') +tracer.propagate = False + +class Connection(object): + transport_schema = 'http' + + def __init__(self, host='localhost', port=9200, **kwargs): + self.host = '%s://%s:%s' % (self.transport_schema, host, port) + + def __repr__(self): + return '<%s: %s>' % (self.__class__.__name__, self.host) + + +class RequestsHttpConnection(Connection): + def __init__(self, **kwargs): + super(RequestsHttpConnection, self).__init__(**kwargs) + self.session = requests.session() + + def perform_request(self, method, url, params=None, body=None): + url = self.host + url + + request = requests.Request(method, url, params=params or {}, data=body).prepare() + try: + response = self.session.send(request) + raw_data = response.text + except requests.ConnectionError as e: + raise TransportError(e) + + # raise errors based on http status codes, let the client handle those if needed + if response.status_code >= 300: + + if response.status_code in HTTP_EXCEPTIONS: + raise HTTP_EXCEPTIONS[response.status_code]() + + raise TransportError() + + return response.status_code, raw_data + diff --git a/elasticsearch/exceptions.py b/elasticsearch/exceptions.py new file mode 100644 index 00000000..9da7c71a --- /dev/null +++ b/elasticsearch/exceptions.py @@ -0,0 +1,15 @@ +class ElastiSearchException(Exception): + pass + + +class TransportError(ElastiSearchException): + pass + + +class NotFoundError(TransportError): + " 404 " + + +HTTP_EXCEPTIONS = { + 404: NotFoundError, +} diff --git a/test_elasticsearch/test_connection.py b/test_elasticsearch/test_connection.py new file mode 100644 index 00000000..d25fabaa --- /dev/null +++ b/test_elasticsearch/test_connection.py @@ -0,0 +1,53 @@ +from unittest import TestCase + +from mock import Mock + +from elasticsearch.connection import RequestsHttpConnection + +class TestRequestsConnection(TestCase): + def _get_mock_connection(self, connection_params={}, status_code=200, response_body=u'{}'): + con = RequestsHttpConnection(**connection_params) + + con.session.send = Mock() + + dummy_response = Mock() + con.session.send.return_value = dummy_response + dummy_response.status_code = status_code + dummy_response.text = response_body + return con + + def _get_request(self, connection, *args, **kwargs): + status, data = connection.perform_request(*args, **kwargs) + self.assertEquals(200, status) + self.assertEquals(u'{}', data) + + self.assertEquals(1, connection.session.send.call_count) + + args, kwargs = connection.session.send.call_args + self.assertEquals({}, kwargs) + self.assertEquals(1, len(args)) + return args[0] + + def test_defaults(self): + con = self._get_mock_connection() + request = self._get_request(con, 'GET', '/') + + self.assertEquals('http://localhost:9200/', request.url) + self.assertEquals('GET', request.method) + self.assertEquals(None, request.body) + + def test_params_properly_encoded(self): + con = self._get_mock_connection() + request = self._get_request(con, 'GET', '/', params={'param': 'value with spaces'}) + + self.assertEquals('http://localhost:9200/?param=value+with+spaces', request.url) + self.assertEquals('GET', request.method) + self.assertEquals(None, request.body) + + def test_body_attached(self): + con = self._get_mock_connection() + request = self._get_request(con, 'GET', '/', body='{"answer": 42}') + + self.assertEquals('http://localhost:9200/', request.url) + self.assertEquals('GET', request.method) + self.assertEquals('{"answer": 42}', request.body)