Summary
APIOperationBase.execute() calls requests.post() with no timeout, so an
unresponsive endpoint blocks the calling thread indefinitely.
Where
authorizenet/apicontrollersbase.py (current master):
self._httpResponse = requests.post(self.endpoint, data=xmlRequest,
headers=constants.headers,
proxies=proxyDictionary)
grep -c timeout authorizenet/apicontrollersbase.py returns 0. requests
has no default timeout, so this waits as long as the socket stays open.
Why it matters
In a web application this is a stuck worker rather than a slow request. We hit
it on a checkout path: the charge itself was approved and settled, and the
follow-up API call in the same request stalled long enough for the WSGI server
to kill the worker. The customer saw a 502 and re-purchased; the first charge
had settled and the account was never activated, because the code that would
have activated it never ran.
A timeout converts that into the None response the SDK's callers already
handle, which is recoverable — the post-charge steps still run.
Suggested fix
A conservative default with an override, e.g.:
# module level
DEFAULT_HTTP_TIMEOUT = (5, 30) # (connect, read)
self._httpResponse = requests.post(
self.endpoint, data=xmlRequest, headers=constants.headers,
proxies=proxyDictionary,
timeout=getattr(self, 'timeout', DEFAULT_HTTP_TIMEOUT))
Exposing it as a settable attribute on the controller (or via
utility.helper.getproperty, consistent with how the proxy settings are read)
would let integrators tune it without forking.
Note the read timeout must be generous enough for the slowest transactional
call, and integrators should be warned in the docs that a read timeout on a
payment request is not safe to retry blindly — the request may have been
processed.
Happy to open a PR.
Version
Observed on 1.1.4; unchanged on current master.
Summary
APIOperationBase.execute()callsrequests.post()with notimeout, so anunresponsive endpoint blocks the calling thread indefinitely.
Where
authorizenet/apicontrollersbase.py(currentmaster):grep -c timeout authorizenet/apicontrollersbase.pyreturns0.requestshas no default timeout, so this waits as long as the socket stays open.
Why it matters
In a web application this is a stuck worker rather than a slow request. We hit
it on a checkout path: the charge itself was approved and settled, and the
follow-up API call in the same request stalled long enough for the WSGI server
to kill the worker. The customer saw a 502 and re-purchased; the first charge
had settled and the account was never activated, because the code that would
have activated it never ran.
A timeout converts that into the
Noneresponse the SDK's callers alreadyhandle, which is recoverable — the post-charge steps still run.
Suggested fix
A conservative default with an override, e.g.:
Exposing it as a settable attribute on the controller (or via
utility.helper.getproperty, consistent with how the proxy settings are read)would let integrators tune it without forking.
Note the read timeout must be generous enough for the slowest transactional
call, and integrators should be warned in the docs that a read timeout on a
payment request is not safe to retry blindly — the request may have been
processed.
Happy to open a PR.
Version
Observed on 1.1.4; unchanged on current
master.