1# -*- coding: utf-8 -*-
2# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
3#
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import absolute_import, division, print_function
7__metaclass__ = type
8
9import json
10
11try:
12    from ssl import CertificateError
13except ImportError:
14    # This will never match the ssl exception, which will cause exception to
15    # bubble up the call stack.
16    class CertificateError(Exception):
17        pass
18
19from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
20from ansible.module_utils.urls import open_url
21
22from . import errors, debug
23
24
25class Response:
26    def __init__(self, status, data):
27        self.status = status
28        self.data = data
29        self._json = None
30
31    @property
32    def json(self):
33        if self._json is None:
34            try:
35                self._json = json.loads(self.data)
36            except ValueError:  # Cannot use JSONDecodeError here (python 2)
37                self._json = None
38
39        return self._json
40
41
42def request(method, url, payload=None, data=None, headers=None, **kwargs):
43    if payload is not None:
44        data = json.dumps(payload, separators=(",", ":"))
45        headers = dict(headers or {}, **{"content-type": "application/json"})
46
47    try:
48        raw_resp = open_url(
49            method=method, url=url, data=data, headers=headers, **kwargs
50        )
51        resp = Response(raw_resp.getcode(), raw_resp.read())
52        debug.log_request(method, url, payload, resp)
53        return resp
54    except HTTPError as e:
55        # This is not an error, since client consumers might be able to
56        # work around/expect non 20x codes.
57        resp = Response(e.code, e.reason)
58        debug.log_request(method, url, payload, resp)
59        return resp
60    except URLError as e:
61        debug.log_request(method, url, payload, comment=e.reason)
62        raise errors.HttpError(
63            "{0} request failed: {1}".format(method, e.reason),
64        )
65    except CertificateError as e:
66        raise errors.HttpError("Certificate error: {0}".format(e))
67