Repository: kopf/httsleep

Issue:
Support `verify=False` usage as per requests library
It's not currently possible to use `httsleep` and ignore certificate errors, as you would with `requests.get(..., verify=False)`.

This looks fairly simple to add so I'll try to follow this with a PR and hopefully we can improve things.

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f6a9e74..a162d14 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,11 @@
 httsleep Changelog
 ==================
 
+Version NEXT
+-------------
+* The kwarg ``verify`` is now supported, allowing users of httsleep to specify
+  ``verify=False`` in the same way as when directly using the ``requests`` library.
+
 Version 0.2.0
 -------------
 * The shorthand kwargs (``status_code``, ``json``, ``jsonpath``, ``text``, ``callback``)
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index d7dbbc4..cbc5a7d 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,2 +1,3 @@
 Aengus Walton <ventolin@gmail.com>
 Patrick Mühlbauer <patrick@opentrash.org>
+Oliver Lockwood <oliver.lockwood@cantab.net>
diff --git a/httsleep/main.py b/httsleep/main.py
index fd607f1..649d5ed 100644
--- a/httsleep/main.py
+++ b/httsleep/main.py
@@ -34,6 +34,9 @@ class HttSleeper(object):
                      function that takes the response as an argument returning True.
     :param auth: a (username, password) tuple for HTTP authentication.
     :param headers: a dict of HTTP headers.
+    :param verify: Either a boolean, in which case it controls whether we verify the server's
+                   TLS certificate, or a string, in which case it must be a path to a CA
+                   bundle to use. Defaults to ``True``.
     :param polling_interval: how many seconds to sleep between requests.
     :param max_retries: the maximum number of retries to make, after which
                         a StopIteration exception is raised.
@@ -46,7 +49,7 @@ class HttSleeper(object):
     """
     def __init__(self, url_or_request, until=None, alarms=None,
                  status_code=None, json=None, jsonpath=None, text=None, callback=None,
-                 auth=None, headers=None,
+                 auth=None, headers=None, verify=True,
                  polling_interval=DEFAULT_POLLING_INTERVAL,
                  max_retries=DEFAULT_MAX_RETRIES,
                  ignore_exceptions=None,
@@ -84,6 +87,7 @@ class HttSleeper(object):
                          'jsonpath': jsonpath, 'text': text, 'callback': callback}
             until.append({k: v for k, v in condition.items() if v})
 
+        self.verify = verify
         self.until = until
         self.alarms = alarms
         self.polling_interval = int(polling_interval)
@@ -145,7 +149,7 @@ class HttSleeper(object):
         """
         while True:
             try:
-                response = self.session.send(self.request.prepare())
+                response = self.session.send(self.request.prepare(), verify=self.verify)
                 for condition in self.alarms:
                     if self.meets_condition(response, condition):
                         raise Alarm(response, condition)
@@ -192,9 +196,9 @@ class HttSleeper(object):
         return True
 
 
-def httsleep(url_or_request, until=None, alarms=None,
-             status_code=None, json=None, jsonpath=None, text=None, callback=None,
-             auth=None, headers=None,
+def httsleep(url_or_request, until=None, alarms=None, status_code=None,
+             json=None, jsonpath=None, text=None, callback=None,
+             auth=None, headers=None, verify=True,
              polling_interval=DEFAULT_POLLING_INTERVAL,
              max_retries=DEFAULT_MAX_RETRIES,
              ignore_exceptions=None,
@@ -207,7 +211,9 @@ def httsleep(url_or_request, until=None, alarms=None,
     return HttSleeper(
         url_or_request, until=until, alarms=alarms, status_code=status_code,
         json=json, jsonpath=jsonpath, text=text, callback=callback,
-        auth=auth, headers=headers, polling_interval=polling_interval,
-        max_retries=max_retries, ignore_exceptions=ignore_exceptions,
+        auth=auth, headers=headers, verify=verify,
+        polling_interval=polling_interval,
+        max_retries=max_retries,
+        ignore_exceptions=ignore_exceptions,
         loglevel=loglevel
     ).run()


Test patch:
diff --git a/tests/test_init.py b/tests/test_init.py
index 513fe3f..d367d77 100644
--- a/tests/test_init.py
+++ b/tests/test_init.py
@@ -35,6 +35,16 @@ def test_headers():
     assert obj.request.headers == headers
 
 
+def test_default_verify():
+    obj = HttSleeper(URL, CONDITION)
+    assert obj.verify == True
+
+
+def test_verify():
+    obj = HttSleeper(URL, CONDITION, verify=False)
+    assert obj.verify == False
+
+
 def test_ignore_exceptions_default_value():
     obj = HttSleeper(URL, CONDITION)
     assert obj.ignore_exceptions == tuple()
diff --git a/tests/test_run.py b/tests/test_run.py
index 7a708d1..24f59ff 100644
--- a/tests/test_run.py
+++ b/tests/test_run.py
@@ -5,6 +5,7 @@ from jsonpath_rw.jsonpath import Fields
 import mock
 import pytest
 from requests.exceptions import ConnectionError
+from requests import Response
 
 from httsleep.main import HttSleeper, Alarm, DEFAULT_POLLING_INTERVAL
 
@@ -22,6 +23,36 @@ def test_run_success():
         assert not mock_sleep.called
 
 
+@httpretty.activate
+def test_propagate_verify():
+    """Should tell requests to skip SSL verification if verify==False"""
+    resp = Response()
+    resp.status_code = 200
+    httsleep = HttSleeper(URL, {'status_code': 200}, verify=False)
+    with mock.patch('requests.sessions.Session.send') as mock_session_send:
+        mock_session_send.return_value = resp
+        httsleep.run()
+        assert mock_session_send.called
+        args, kwargs = mock_session_send.call_args
+    assert 'verify' in kwargs
+    assert kwargs['verify'] == False
+
+
+@httpretty.activate
+def test_default_sends_verify_true():
+    """Should not send a value for 'verify' to requests by default"""
+    resp = Response()
+    resp.status_code = 200
+    httsleep = HttSleeper(URL, {'status_code': 200})
+    with mock.patch('requests.sessions.Session.send') as mock_session_send:
+        mock_session_send.return_value = resp
+        httsleep.run()
+        assert mock_session_send.called
+        args, kwargs = mock_session_send.call_args
+    assert 'verify' in kwargs
+    assert kwargs['verify'] == True
+
+
 @httpretty.activate
 def test_run_alarm():
     """Should raise an Alarm when a failure criteria has been reached"""