Skip to content
This repository has been archived by the owner on Dec 11, 2023. It is now read-only.

(GITHUB-89) Do not throw ValueError on authentication error during POST #132

Merged
merged 1 commit into from
Aug 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions librato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,7 @@ def _process_response(self, resp, backoff):
not_a_server_error = resp.status < 500

if not_a_server_error:
body = resp.read()
if body:
resp_data = json.loads(body.decode(_getcharset(resp)))
log.info("body(<-): %s" % body)
resp_data = _decode_body(resp)
a_client_error = resp.status >= 400
if a_client_error:
raise exceptions.get(resp.status, resp_data)
Expand Down Expand Up @@ -568,6 +565,25 @@ def connect(username, api_key, hostname=HOSTNAME, base_path=BASE_PATH, sanitizer
return LibratoConnection(username, api_key, hostname, base_path, sanitizer=sanitizer, protocol=protocol)


def _decode_body(resp):
"""
Read and decode HTTPResponse body based on charset and content-type
"""
body = resp.read()
log.info("body(<-): %s" % body)
if not body:
return None

decoded_body = body.decode(_getcharset(resp))
content_type = _get_content_type(resp)

if content_type == "application/json":
resp_data = json.loads(decoded_body)
else:
resp_data = decoded_body

return resp_data

def _getcharset(resp, default='utf-8'):
"""
Extract the charset from an HTTPResponse.
Expand All @@ -581,3 +597,10 @@ def _getcharset(resp, default='utf-8'):
m = email.message.Message()
m['content-type'] = resp.getheader('content-type')
return m.get_content_charset(default)

def _get_content_type(resp):
"""
Get Content-Type header ignoring parameters
"""
parts = resp.getheader('content-type', "application/json").split(";")
return parts[0]
4 changes: 4 additions & 0 deletions tests/mock_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,12 +446,16 @@ class MockResponse(object):
def __init__(self, request, fake_failure=False):
self.request = request
self.status = 500 if fake_failure else 200
self._headers = {'content-type': "application/json;charset=utf-8"}

class headers(object):
@staticmethod
def get_content_charset(default):
return 'utf-8'

def getheader(self, name, default=None):
return self._headers.get(name.lower(), default)

def read(self):
return self._json_body_based_on_request()

Expand Down
46 changes: 46 additions & 0 deletions tests/test_process_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import logging
import unittest
try:
from unittest.mock import create_autospec, PropertyMock
except ImportError:
from mock import create_autospec, PropertyMock
import librato
from mock_connection import MockConnect, server
from six.moves.http_client import HTTPResponse

#logging.basicConfig(level=logging.DEBUG)
# Mock the server
librato.HTTPSConnection = MockConnect

class TestLibrato(unittest.TestCase):
def setUp(self):
self.conn = librato.connect('user_test', 'key_test')
server.clean()

def test_get_authentication_failure(self):
"""
fails with Unauthorized on 401 during GET
"""
mock_response = create_autospec(HTTPResponse, spec_set=True, instance=True)
mock_response.mock_add_spec(['status'], spec_set=True)
mock_response.status = 401
# GET 401 responds with JSON
mock_response.getheader.return_value = "application/json;charset=utf-8"
mock_response.read.return_value = '{"errors":{"request":["Authorization Required"]}}'.encode('utf-8')

with self.assertRaises(librato.exceptions.Unauthorized):
self.conn._process_response(mock_response, 1)

def test_post_authentication_failure(self):
"""
fails with Unauthorized on 401 during POST
"""
mock_response = create_autospec(HTTPResponse, spec_set=True, instance=True)
mock_response.mock_add_spec(['status'], spec_set=True)
mock_response.status = 401
# POST 401 responds with text
mock_response.getheader.return_value = "text/plain"
mock_response.read.return_value = 'Credentials are required to access this resource.'.encode('utf-8')

with self.assertRaises(librato.exceptions.Unauthorized):
self.conn._process_response(mock_response, 1)