psf / psf/requests

HTTPDigestAuth fails from secondary thread

Open
#5,178 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
54.3k
Forks
10.4k
Avg merge
16h 43m
Merged PRs (30d)
3

Description

Hi,

I am looking into this Home Assistant issue 13524 where a request is prepared once at startup (on an initialisation thread), and then updated at regular interval from a thread pool. This is working fine with HTTPBasicAuth, but failing with HTTPDigestAuth.

Is this a bug? or is this by design? If it is by design, can you help me find a workaround for Home Assistant?

Expected Result

{
  "authenticated": true,
  "user": "user"
}

Actual Result

Traceback (most recent call last):
  File ".../python3.7/threading.py", line 926, in _bootstrap_inner
    self.run()
  File ".../python3.7/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "test.py", line 8, in do_update
    request
  File ".../python3.7/site-packages/requests/sessions.py", line 653, in send
    r = dispatch_hook('response', hooks, r, **kwargs)
  File ".../python3.7/site-packages/requests/hooks.py", line 31, in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
  File ".../python3.7/site-packages/requests/auth.py", line 247, in handle_401
    if self._thread_local.pos is not None:
AttributeError: '_thread._local' object has no attribute 'pos'

Reproduction Steps

import requests
import threading

def do_update(name, request):
    print("do_update %s: starting", name)
    with requests.Session() as sess:
        response = sess.send(
            request
        )
    print(response.text)
    print("do_update %s: finishing", name)
    
method = 'GET'
resource = 'http://httpbin.org/digest-auth/auth/user/passwd'
auth = requests.auth.HTTPDigestAuth('user', 'passwd')
request = requests.Request(
    method, resource, auth=auth
).prepare()

#Works on main thread
do_update("main thread", request)

#Doesn't work on secondary thread
x = threading.Thread(target=do_update, args=("other thread",request,))
x.start()
x.join()

System Information

$ python -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": ""
  },
  "idna": {
    "version": "2.8"
  },
  "implementation": {
    "name": "CPython",
    "version": "3.7.4"
  },
  "platform": {
    "release": "4.19.57+",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "",
    "version": null
  },
  "requests": {
    "version": "2.22.0"
  },
  "system_ssl": {
    "version": "101000bf"
  },
  "urllib3": {
    "version": "1.25.3"
  },
  "using_pyopenssl": false
}

This command is only available on Requests v2.16.4 and greater. Otherwise,
please provide some basic information about your system (Python version,
operating system, &c).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the HTTPDigestAuth traceback in requests/auth.py and run the provided reproduction script with the secondary thread. Compare the main-thread and secondary-thread results; done means the secondary-thread request completes without the '_thread._local' AttributeError and produces the expected authenticated response.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.