Merge branch 'api-doc-improvements'
[pyi2ncommon] / src / web_interface.py
CommitLineData
11cbb815
PD
1# The software in this package is distributed under the GNU General
2# Public License version 2 (with a special exception described below).
3#
4# A copy of GNU General Public License (GPL) is included in this distribution,
5# in the file COPYING.GPL.
6#
7# As a special exception, if other files instantiate templates or use macros
8# or inline functions from this file, or you compile this file and link it
9# with other works to produce a work based on this file, this file
10# does not by itself cause the resulting work to be covered
11# by the GNU General Public License.
12#
13# However the source code for this file must still be made available
14# in accordance with section (3) of the GNU General Public License.
15#
16# This exception does not invalidate any other reasons why a work based
17# on this file might be covered by the GNU General Public License.
18#
19# Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
20
f49f6323 21"""
f49f6323
PD
22Utility for HTTP based interaction with the arnied web page.
23
24Copyright: Intra2net AG
f49f6323
PD
25"""
26
27import re
1ba3b378 28import ssl
f49f6323
PD
29import http.client as client
30import urllib.parse as parse
c16498f5 31import socket
f49f6323 32import logging
7628bc48 33
3de8b4d8 34log = logging.getLogger('pyi2ncommon.web_interface')
f49f6323 35
f49f6323 36
c16498f5
CH
37#: FQDN of local machine
38LOCALHOST = socket.gethostname()
39
f49f6323 40
897c6e50 41def find_in_form(regex, form="status", escape=False, check_certs=True):
f49f6323 42 """
7628bc48 43 Find a regex in given I2N web page form.
f49f6323
PD
44
45 :param str regex: regular expression to find
7628bc48 46 :param str form: form name to open
f49f6323 47 :param bool escape: whether to escape the regex
897c6e50 48 :param check_certs: forwarded to :py:func:`web_page_request`, see doc there
f49f6323
PD
49 :returns: whether the regex was found
50 :rtype: bool
51 """
897c6e50
CH
52 data = web_page_request(method="GET", url="/arnie?form=" + form,
53 check_certs=check_certs)
f49f6323
PD
54 if escape:
55 regex = re.escape(regex)
56 if re.search(regex, data):
57 return True
58 else:
59 log.debug("'%s' could not be found in:\n%s", regex, data)
60 return False
61
62
897c6e50 63def web_page_request(method="GET", url="/", body=None, check_certs=True):
f49f6323
PD
64 """
65 Send an HTTPS request and return any response data.
66
897c6e50
CH
67 SSL certificates are checked against a default set of certificates
68 installed on you system. This can be disabled (not recommended, security
69 implications!) by setting `check_certs` to `False`. To allow a secure
70 connection to host with e.g. a self-signed certificate, the caller can
71 load this certificate by specifying `check_certs=/path/to/cert.pem`.
c16498f5
CH
72 (see also: :py:meth:`ssl.SSLContext.load_verify_locations`). Note that the
73 certificate has to be issued for the same server name that we try to
74 access, i.e. :py:data:`LOCALHOST`.
897c6e50 75
f49f6323
PD
76 :param str method: GET or POST method for the request
77 :param str url: url location within the remote host
78 :param body: dictionary to be parsed and added to the url
79 :type body: {str, str} or None
897c6e50
CH
80 :param check_certs: Whether or not to check ssl certificates for connection
81 or file name to a certificate file
82 :type check_certs: bool or str
f49f6323
PD
83 :returns: data from the response if any
84 :rtype: str
85 """
86 body = parse.urlencode(body) if body is not None else ""
87 headers = {"Content-Type": "application/x-www-form-urlencoded",
88 "Accept": "text/plain"}
89
897c6e50
CH
90 if isinstance(check_certs, str):
91 context = ssl.create_default_context(cafile=check_certs)
92 elif check_certs:
93 context = ssl.create_default_context()
94 else:
95 # disable certificate checks
96 context = ssl._create_unverified_context()
c16498f5 97 conn = client.HTTPSConnection(LOCALHOST, context=context)
f49f6323
PD
98 conn.request(method, url, body, headers)
99 resp = conn.getresponse()
100 logging.info("Request status %s and response %s",
101 resp.status, resp.reason)
102 if resp.status != 200:
eed487df 103 raise client.HTTPException("POST request failed.")
f49f6323
PD
104 data = resp.read().decode()
105 conn.close()
106
107 return data