76 lines
2.2 KiB
Python
76 lines
2.2 KiB
Python
import requests
|
|
from asn1crypto import tsp
|
|
from pyhanko_certvalidator._asyncio_compat import to_thread
|
|
|
|
from .api import TimeStamper
|
|
from .common_utils import TimestampRequestError, set_tsp_headers
|
|
|
|
__all__ = ['HTTPTimeStamper']
|
|
|
|
|
|
class HTTPTimeStamper(TimeStamper):
|
|
"""
|
|
Standard HTTP-based timestamp client.
|
|
"""
|
|
|
|
def __init__(self, url, https=False, timeout=5, auth=None, headers=None):
|
|
"""
|
|
Initialise the timestamp client.
|
|
|
|
:param url:
|
|
URL where the server listens for timestamp requests.
|
|
:param https:
|
|
Enforce HTTPS.
|
|
:param timeout:
|
|
Timeout (in seconds)
|
|
:param auth:
|
|
Value of HTTP ``Authorization`` header
|
|
:param headers:
|
|
Other headers to include.
|
|
"""
|
|
if https and not url.startswith('https:'): # pragma: nocover
|
|
raise ValueError('Timestamp URL is not HTTPS.')
|
|
self.url = url
|
|
self.timeout = timeout
|
|
self.auth = auth
|
|
self.headers = headers
|
|
super().__init__()
|
|
|
|
def request_headers(self) -> dict:
|
|
"""
|
|
Format the HTTP request headers.
|
|
|
|
:return:
|
|
Header dictionary.
|
|
"""
|
|
return set_tsp_headers(self.headers or {})
|
|
|
|
async def async_request_tsa_response(
|
|
self, req: tsp.TimeStampReq
|
|
) -> tsp.TimeStampResp:
|
|
def task():
|
|
try:
|
|
raw_res = requests.post(
|
|
self.url,
|
|
req.dump(),
|
|
headers=self.request_headers(),
|
|
auth=self.auth,
|
|
timeout=self.timeout,
|
|
)
|
|
except IOError as e:
|
|
raise TimestampRequestError(
|
|
'Error in communication with timestamp server',
|
|
) from e
|
|
|
|
if (
|
|
raw_res.headers.get('Content-Type')
|
|
!= 'application/timestamp-reply'
|
|
):
|
|
raise TimestampRequestError(
|
|
'Timestamp server response is malformed.', raw_res
|
|
)
|
|
return tsp.TimeStampResp.load(raw_res.content)
|
|
|
|
response = await to_thread(task)
|
|
return response
|