Repository: mozilla-services/requests-hawk

Issue:
Allow skipping content checks and hash for GET requests
mohawk allows for skipping content checks and (and subsequently not creating a hash) when dealing with either very large or empty content. This is achieved by setting `always_hash_content=False` and removing the `content` and `content_type` parameters from `mohawk.Sender()`. https://mohawk.readthedocs.io/en/latest/usage.html#skipping-content-checks

It would be great to also reflect this in requests-hawk, as it currently prevents us from authenticating to an endpoint without resorting using the lower-level mohawk and requests

Discussion:

Patch:
diff --git a/requests_hawk/__init__.py b/requests_hawk/__init__.py
index 6bd4f10..4b3110b 100644
--- a/requests_hawk/__init__.py
+++ b/requests_hawk/__init__.py
@@ -8,6 +8,7 @@ from six.moves.urllib.parse import urlparse
 from six import text_type
 
 import mohawk
+from mohawk.base import EmptyValue
 from requests.auth import AuthBase
 
 
@@ -38,7 +39,8 @@ class HawkAuth(AuthBase):
     You should use either `hawk_session` or both `id` and 'key'.
     """
     def __init__(self, hawk_session=None, id=None, key=None, algorithm='sha256',
-                 credentials=None, server_url=None, _timestamp=None):
+                 credentials=None, server_url=None, _timestamp=None,
+                 always_hash_content=True):
         if credentials is not None:
             raise AttributeError("The 'credentials' param has been removed. "
                                  "Pass 'id' and 'key' instead, or '**credentials_dict'.")
@@ -65,6 +67,7 @@ class HawkAuth(AuthBase):
         }
         self._timestamp = _timestamp
         self.host = urlparse(server_url).netloc if server_url else None
+        self.always_hash_content = always_hash_content
 
     def __call__(self, r):
         if self.host is not None:
@@ -74,8 +77,9 @@ class HawkAuth(AuthBase):
             self.credentials,
             r.url,
             r.method,
-            content=r.body or '',
-            content_type=r.headers.get('Content-Type', ''),
+            content=r.body or EmptyValue,
+            content_type=r.headers.get("Content-Type", EmptyValue),
+            always_hash_content=self.always_hash_content,
             _timestamp=self._timestamp
         )
 


Test patch:
diff --git a/requests_hawk/tests/test_hawkauth.py b/requests_hawk/tests/test_hawkauth.py
index a97e284..3d9a251 100644
--- a/requests_hawk/tests/test_hawkauth.py
+++ b/requests_hawk/tests/test_hawkauth.py
@@ -95,3 +95,9 @@ class TestHawkAuth(unittest.TestCase):
         self.assertTrue('ts="1431698847"' in r.headers['Authorization'],
                         "Timestamp doesn't match")
         self.assertEqual(r.body, b'{"foo": "bar"}')
+
+    def test_hawk_auth_supports_empty_body(self):
+        auth = HawkAuth(id='test_id', key='test_key', always_hash_content=False)
+        request = Request('GET', 'http://www.example.com', auth=auth)
+        r = request.prepare()
+        self.assertNotIn('hash="', r.headers['Authorization'])