Race condition in send()
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 45/100
- Type d'issue
- Bug
- Clarté
- Plutôt claire
- Activité
- Calme
- Stack technique
- python
- Domaine
- backend, networking
Piste de recherche
Suivez adapter.send(), conditional_headers(), build_response(), update_cached_response() et _load_from_cache() dans le flux du cache et de l’adaptateur. Reproduisez la séquence à deux processus décrite avec des requêtes concurrentes, puis vérifiez qu’une requête qui a reçu un 304 renvoie toujours la réponse mise en cache ou un cache miss normal, au lieu d’exposer le 304 à l’appelant.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
I spotted this while using tox -vvv -p all, so via pip, since pip==26.1.
The issue with tox -p all is that it can run multiple versions of Python (in my config from 3.10 to 3.14), in parallel, I bet some with zstd some without.
TL;DR:
- pip wants to query /simple/setuptools/
- Cache is checked, an etag is found in the cache.
- The HTTP request with an If-None-Match is sent.
- In the meantime the cache gets modified, from
Accept-Encoding: gzip, deflatetoAccept-Encoding: gzip, deflate, zstdor the other way around, by another process. - The HTTP response arrives, it's a
304 Not Modified, the cache has to be updated, but this time it does no longer loads, and the 304 itself is returned.
In details:
It all starts in those two lines of adapter.send():
request.headers.update(self.controller.conditional_headers(request))
resp = super().send(request, stream, timeout, verify, cert, proxies)
Here conditional_headers does a _load_from_cache which can succeed, let say it succeed.
Then super().send() will eventually call adapter.build_response() with a 304 Not Modified so build_response calls self.controller.update_cached_response which runs another _load_from_cache, which can fail if the cache was modified by another process. Let's say it fails it runs this code:
if not cached_response:
# we didn't have a cached response
return response
So the 304 response is returned, up to the user. In case of pip an exception is raised because the answer don't have a Content-Type. But pip or not, the user just did a get with no If-None-Match so he does not expect a 304, either a cache hit (hopefully 200 OK) or a cache miss (hopefully 200 OK) but never a 304.
The chronology with two processes
It think it looks like this, [1] and [2] denoting two distinct processes:
- [1] runs a
requests.get("https://pypi.org/simple/setuptools/") - [2] runs a
requests.get("https://pypi.org/simple/setuptools/") - [1] Read the cache, extract an ETag from the cache
- [2] Read the cache, extract an ETag from the cache
- [1] Sends the HTTP request with If-None-Match
- [2] Sends the HTTP request with If-None-Match
- [1] Gets a response,
adapter.build_responsecallsself.controller.update_cached_response(), changing the headers on disk. TheAccept-Encodingin my case. - [1] Returns the cached response.
- [2] Gets a response,
adapter.build_responsecallsself.controller.update_cached_response()which calls_load_from_cachewhich refuses to load because the headers has changed (Varytells the answer varies onAccept-Encodingand theAccept-Encodingchanged),update_cached_responsereturns the 304 response.build_responsereturns the 304 response. The user gets a304, which is unexpected.
- Langage dominant
- Python
- Étoiles
- 505
- Forks
- 153
- Merge moyen
- 5 j 6 h
- PR mergées (30 j)
- 5
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Aucun modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de psf/cachecontrol
-
Cache Deserialization (part two) -- warning message incorrect?Peut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
psf/cachecontrol#209 · 5 commentaires ·
-
HTTP 302 Found responses with Cache Headers not being cachedPeut-être pris @cburroughs l’a pris il y a 281 jours. Ouverte
Difficulté 3/5 1-2 jours Accessibilité débutants 42/100
psf/cachecontrol#384 · 18 commentaires ·
-
Head request is not cached even with cacheable_methods=("GET", HEAD") without a previous GETPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouverte
Difficulté 3/5 1-2 jours Accessibilité débutants 35/100
psf/cachecontrol#337 · 6 commentaires ·
-
Investigate failure to load caches above `2^32 - 1` bytesPeut-être à nouveau libre @woodruffw l’a pris il y a 854 jours, et aucune pull request n’est ouverte. Ouverte
psf/cachecontrol#336 · 4 commentaires · 1 réaction · 1 personne assignée ·
-
The new SeparateBodyFileCache design makes updates/reads non-atomicPeut-être à nouveau libre Une pull request pour cette issue a été fermée sans être fusionnée. Ouvertebug
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 28/100
psf/cachecontrol#324 · 4 commentaires ·
Toutes les issues de psf/cachecontrol
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
aws-samples/sample-per-model-bedrock#243 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
FuRongJun-1999/dsh-memory#404 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Les mainteneurs répondent en général sous 1 jour
-
bug frontend
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
PennyLaneAI/catalyst#3337 ·
Les mainteneurs répondent en général sous 1 jour
-
Workflow.schedule stops when a cron deadline passes before sleepPeut-être pris @HuaTNA l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour