# Copyright (C) 2016-2019 Virgil Security Inc.
#
# Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# (1) Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# (2) Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# (3) Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import ssl
from virgil_sdk.utils import Utils
from virgil_sdk.client import ClientException, UnauthorizedClientException, ExpiredAuthorizationClientException
from .base_connection import BaseConnection
from .urllib import urllib2
from .urllib import RequestWithMethod
[docs]class ServiceConnection(BaseConnection):
def __init__(self, base_url, adapters=None):
# type: (str, List[HttpRequestAdapter])->None
self.__base_url = base_url
self.__adapters = adapters
[docs] def send(self, request):
# type: (Request) -> Tuple[dict, dict]
"""
Sends an HTTP request to the API.
Args:
request: The HTTP request details.
Returns:
Response.
Raises:
ClientException: Gets some connection or api errors.
UnauthorizedClientException: Request without or wrong access token.
"""
prepared_request = self._prepare_request(request)
if self.__adapters:
for adapter in self.__adapters:
prepared_request = adapter.adapt(prepared_request)
ctx = ssl.create_default_context()
try:
response = urllib2.urlopen(prepared_request, context=ctx)
result = response.read()
headers = dict()
for k, v in dict(response.info()).items():
headers.update({k.upper(): v})
return Utils.json_loads(result.decode()), headers
except urllib2.HTTPError as exception:
client_errors = {
400: "Request Error",
401: "Authorization Error",
404: "Entity Not Found",
405: "Method Not Allowed",
500: "Internal Server Error"
}
try:
error_res = exception.read()
if error_res:
error_body = Utils.json_loads(bytes(error_res))
else:
error_body = error_res
if isinstance(error_body, dict) and "message" in error_body.keys() and "code" in error_body.keys():
if exception.code in client_errors.keys() and exception.code == 401:
if int(error_body["code"]) == 20304:
Utils.raise_from(
ExpiredAuthorizationClientException(client_errors[exception.code], exception.code)
)
Utils.raise_from(UnauthorizedClientException(client_errors[exception.code], exception.code))
Utils.raise_from(ClientException(error_body["message"], error_body["code"]))
else:
Utils.raise_from(ClientException(client_errors[exception.code], exception.code))
except ValueError:
raise exception
def _prepare_request(self, request):
# type (http.Request) -> urllib.RequestWithMethod
"""Converts http request to urllib-compatible request.
Args:
request: http.Request object containing sending request data.
Returns:
urllib-compatible request object.
"""
url = self.base_url + request.endpoint
data = request.body
if data:
data = Utils.json_dumps(data).encode()
headers = request.headers or {}
prepared_request = RequestWithMethod(
url,
method=request.method,
data=data,
headers=headers
)
return prepared_request
@property
def base_url(self):
"""
Gets api url.
"""
return self.__base_url