Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

Race condition in send()

Ouverte
#424 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

@JulienPalard y travaille déjà.

Depuis le 19/5/2026.

  • #425 par @JulienPalard — ouverte

É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, deflate to Accept-Encoding: gzip, deflate, zstd or 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_response calls self.controller.update_cached_response(), changing the headers on disk. The Accept-Encoding in my case.
  • [1] Returns the cached response.
  • [2] Gets a response, adapter.build_response calls self.controller.update_cached_response() which calls _load_from_cache which refuses to load because the headers has changed (Vary tells the answer varies on Accept-Encoding and the Accept-Encoding changed), update_cached_response returns the 304 response. build_response returns the 304 response. The user gets a 304, 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

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de psf/cachecontrol

Toutes les issues de psf/cachecontrol

Issues similaires

Plus d'issues Python

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.