diff options
Diffstat (limited to 'resources/libraries/python/HTTPRequest.py')
-rw-r--r-- | resources/libraries/python/HTTPRequest.py | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/resources/libraries/python/HTTPRequest.py b/resources/libraries/python/HTTPRequest.py new file mode 100644 index 0000000000..7b21f5a761 --- /dev/null +++ b/resources/libraries/python/HTTPRequest.py @@ -0,0 +1,258 @@ +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implements HTTP requests GET, PUT, POST, DELETE used in communication with +honeycomb. +""" + +from requests import request, RequestException, Timeout, TooManyRedirects, \ + HTTPError, ConnectionError +from requests.auth import HTTPBasicAuth + +from robot.api import logger +from robot.api.deco import keyword + + +HTTP_CODES = {"OK": 200, + "UNAUTHORIZED": 401, + "FORBIDDEN": 403, + "NOT_FOUND": 404, + "SERVICE_UNAVAILABLE": 503} + + +class HTTPRequestError(Exception): + """Exception raised by HTTPRequest objects.""" + + def __init__(self, msg, enable_logging=True): + """Sets the exception message and enables / disables logging + + It is not wanted to log errors when using these keywords together + with keywords like "Wait until keyword succeeds". + + :param msg: Message to be displayed and logged + :param enable_logging: When True, logging is enabled, otherwise + logging is disabled. + :type msg: str + :type enable_logging: bool + """ + super(HTTPRequestError, self).__init__() + self._msg = msg + self._repr_msg = self.__module__ + '.' + \ + self.__class__.__name__ + ": " + self._msg + + if enable_logging: + logger.error(self._msg) + logger.debug(self._repr_msg) + + def __repr__(self): + return repr(self._repr_msg) + + def __str__(self): + return str(self._repr_msg) + + +class HTTPRequest(object): + """A class implementing HTTP requests.""" + + def __init__(self): + pass + + @staticmethod + def create_full_url(ip_addr, port, path): + """Creates full url including IP, port, and path to data. + + :param ip_addr: Server IP + :param port: Communication port + :param path: Path to data + :type ip_addr: str + :type port: str or int + :type path: str + :return: full url + :rtype: str + """ + return "http://{ip}:{port}{path}".format(ip=ip_addr, port=port, + path=path) + + @staticmethod + def _http_request(method, node, path, enable_logging=True, **kwargs): + """Sends specified HTTP request and returns status code and + response content + + :param method: The method to be performed on the resource identified by + the given request URI + :param node: honeycomb node + :param path: URL path, e.g. /index.html + :param enable_logging: used to suppress errors when checking + honeycomb state during suite setup and teardown + :param kwargs: named parameters accepted by request.request: + params -- (optional) Dictionary or bytes to be sent in the query + string for the Request. + data -- (optional) Dictionary, bytes, or file-like object to + send in the body of the Request. + json -- (optional) json data to send in the body of the Request. + headers -- (optional) Dictionary of HTTP Headers to send with + the Request. + cookies -- (optional) Dict or CookieJar object to send with the + Request. + files -- (optional) Dictionary of 'name': file-like-objects + (or {'name': ('filename', fileobj)}) for multipart encoding upload. + timeout (float or tuple) -- (optional) How long to wait for the + server to send data before giving up, as a float, or a (connect + timeout, read timeout) tuple. + allow_redirects (bool) -- (optional) Boolean. Set to True if POST/ + PUT/DELETE redirect following is allowed. + proxies -- (optional) Dictionary mapping protocol to the URL of + the proxy. + verify -- (optional) whether the SSL cert will be verified. + A CA_BUNDLE path can also be provided. Defaults to True. + stream -- (optional) if False, the response content will be + immediately downloaded. + cert -- (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :type method: str + :type node: dict + :type path: str + :type enable_logging: bool + :type kwargs: dict + :return: Status code and content of response + :rtype: tuple + :raises HTTPRequestError: If + 1. it is not possible to connect + 2. invalid HTTP response comes from server + 3. request exceeded the configured number of maximum re-directions + 4. request timed out + 5. there is any other unexpected HTTP request exception + """ + timeout = kwargs["timeout"] + url = HTTPRequest.create_full_url(node['host'], + node['honeycomb']['port'], + path) + try: + auth = HTTPBasicAuth(node['honeycomb']['user'], + node['honeycomb']['passwd']) + rsp = request(method, url, auth=auth, **kwargs) + return rsp.status_code, rsp.content + + except ConnectionError as err: + # Switching the logging on / off is needed only for + # "requests.ConnectionError" + if enable_logging: + raise HTTPRequestError("Not possible to connect to {0}\n". + format(url) + repr(err)) + else: + raise HTTPRequestError("Not possible to connect to {0}\n". + format(url) + repr(err), + enable_logging=False) + except HTTPError as err: + raise HTTPRequestError("Invalid HTTP response from {0}\n". + format(url) + repr(err)) + except TooManyRedirects as err: + raise HTTPRequestError("Request exceeded the configured number " + "of maximum re-directions\n" + repr(err)) + except Timeout as err: + raise HTTPRequestError("Request timed out. Timeout is set to " + "{0}\n".format(timeout) + repr(err)) + except RequestException as err: + raise HTTPRequestError("Unexpected HTTP request exception.\n" + + repr(err)) + + @staticmethod + @keyword(name="HTTP Get") + def get(node, path, headers=None, timeout=10, enable_logging=True): + """Sends a GET request and returns the response and status code. + + :param node: honeycomb node + :param path: URL path, e.g. /index.html + :param headers: Dictionary of HTTP Headers to send with the Request. + :param timeout: How long to wait for the server to send data before + giving up, as a float, or a (connect timeout, read timeout) tuple. + :param enable_logging: Used to suppress errors when checking + honeycomb state during suite setup and teardown. When True, logging + is enabled, otherwise logging is disabled. + :type node: dict + :type path: str + :type headers: dict + :type timeout: float or tuple + :type enable_logging: bool + :return: Status code and content of response + :rtype: tuple + """ + return HTTPRequest._http_request('GET', node, path, + enable_logging=enable_logging, + headers=headers, timeout=timeout) + + @staticmethod + @keyword(name="HTTP Put") + def put(node, path, headers=None, payload=None, timeout=10): + """Sends a PUT request and returns the response and status code. + + :param node: honeycomb node + :param path: URL path, e.g. /index.html + :param headers: Dictionary of HTTP Headers to send with the Request. + :param payload: Dictionary, bytes, or file-like object to send in + the body of the Request. + :param timeout: How long to wait for the server to send data before + giving up, as a float, or a (connect timeout, read timeout) tuple. + :type node: dict + :type path: str + :type headers: dict + :type payload: dict, bytes, or file-like object + :type timeout: float or tuple + :return: Status code and content of response + :rtype: tuple + """ + return HTTPRequest._http_request('PUT', node, path, headers=headers, + data=payload, timeout=timeout) + + @staticmethod + @keyword(name="HTTP Post") + def post(node, path, headers=None, payload=None, json=None, timeout=10): + """Sends a POST request and returns the response and status code. + + :param node: honeycomb node + :param path: URL path, e.g. /index.html + :param headers: Dictionary of HTTP Headers to send with the Request. + :param payload: Dictionary, bytes, or file-like object to send in + the body of the Request. + :param json: json data to send in the body of the Request + :param timeout: How long to wait for the server to send data before + giving up, as a float, or a (connect timeout, read timeout) tuple. + :type node: dict + :type path: str + :type headers: dict + :type payload: dict, bytes, or file-like object + :type json: str + :type timeout: float or tuple + :return: Status code and content of response + :rtype: tuple + """ + return HTTPRequest._http_request('POST', node, path, headers=headers, + data=payload, json=json, + timeout=timeout) + + @staticmethod + @keyword(name="HTTP Delete") + def delete(node, path, timeout=10): + """Sends a DELETE request and returns the response and status code. + + :param node: honeycomb node + :param path: URL path, e.g. /index.html + :param timeout: How long to wait for the server to send data before + giving up, as a float, or a (connect timeout, read timeout) tuple. + :type node: dict + :type path: str + :type timeout: float or tuple + :return: Status code and content of response + :rtype: tuple + """ + return HTTPRequest._http_request('DELETE', node, path, timeout=timeout) |