
Небольшая Python-библиотека, которая упрощает эксплуатацию состояний гонки в веб-приложениях с помощью Requests.
Requests-Racer — это небольшая библиотека на Python, которая позволяет использовать библиотеку Requests для отправки нескольких запросов, которые будут обработаны серверами назначения примерно в одно и то же время, даже если запросы имеют разные адресаты или разные размеры полезной нагрузки. Это может быть полезно для обнаружения и эксплуатации уязвимостей состояния гонки (race condition) в веб-приложениях. (Дополнительную информацию см. в motivation.md.)
Requests (и urllib3, который Requests использует внутри себя) никогда не предназначались для того, чтобы позволить вам делать что-то подобное. Поэтому Requests-Racer вынужден прибегать к довольно ужасным хакам, чтобы получить детальный контроль над тем, как отправляются запросы.
Эти хаки включают вмешательство в приватное состояние некоторых объектов urllib3, поэтому обновление urllib3, обратно совместимое с его публичным API, всё равно может сломать Requests-Racer. Поэтому я рекомендую использовать Requests-Racer в виртуальном окружении (virtualenv) и устанавливать его до Requests или urllib3, чтобы в качестве зависимости подтянулась известная совместимая версия этих библиотек.
Для использования Requests-Racer вам понадобится Python 3.5 или новее. Сначала создайте и активируйте виртуальное окружение Python:
python3 -m venv env
source env/bin/activate
Затем скачайте копию библиотеки и установите её:
git clone https://github.com/nccgroup/requests-racer.git
cd requests-racer
python setup.py install
Requests-Racer работает, предоставляя для Requests альтернативный транспортный адаптер под названием SynchronizedAdapter. Он собирает все запросы, которые вы отправляете через него, и завершает их только при вызове метода finish_all():
import requests
from requests_racer import SynchronizedAdapter
s = requests.Session()
sync = SynchronizedAdapter()
s.mount('http://', sync)
s.mount('https://', sync)
resp1 = s.get('http://example.com/a', params={'hello': 'world'})
resp2 = s.post('https://example.net/b', data={'one': 'two'})
# at this point, the requests have been started but not finished.
# resp1 and resp2 should *not* be used.
sync.finish_all()
print(resp1.status_code)
print(resp2.text)
Чтобы упростить ваш код, вы также можете использовать SynchronizedSession, который представляет собой просто объект requests.Session, который автоматически монтирует SynchronizedAdapter для HTTP[S] и проксирует метод finish_all(), поэтому приведённый выше код можно переписать следующим образом:
from requests_racer import SynchronizedSession
s = SynchronizedSession()
resp1 = s.get('http://example.com/a', params={'hello': 'world'})
resp2 = s.post('https://example.net/b', data={'one': 'two'})
# at this point, the requests have been started but not finished.
# resp1 and resp2 should *not* be used.
s.finish_all()
print(resp1.status_code)
print(resp2.text)
Вот некоторые предостережения, о которых следует помнить, и более интересные вещи, которые вы можете делать:
SynchronizedAdapter не потокобезопасен.SynchronizedAdapter, не будут обновлять объект сессии (например, cookie из заголовков Set-Cookie не будут добавлены в хранилище cookie сессии).999 и содержать трассировку в своём атрибуте .text.SynchronizedSession.from_regular_session создаёт SynchronizedSession из экземпляра requests.Session. Это полезно, если вам нужно сделать несколько простых запросов, например, для входа в сервис и получения сессионной cookie, которая понадобится для синхронизированных запросов.SynchronizedAdapter и SynchronizedSession принимают необязательный параметр под названием num_threads, который задаёт максимальное количество потоков, которые будет использовать адаптер. Если он не указан, адаптер будет использовать один поток на запрос.См. benchmark/ с заметками о производительности.
Copyright (C) 2019 Aleksejs Popovs, NCC Group
Эта программа является свободным программным обеспечением: вы можете распространять её и/или изменять её в соответствии с условиями Стандартной общественной лицензии GNU в том виде, в каком она была опубликована Free Software Foundation, либо версии 3 Лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; без даже подразумеваемой гарантии КОММЕРЧЕСКОЙ ПРИГОДНОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. См. Стандартную общественную лицензию GNU для получения дополнительных сведений.
Вы должны были получить копию Стандартной общественной лицензии GNU вместе с этой программой. Если вы её не получили, см. https://www.gnu.org/licenses/.
finish_all() принимает необязательный параметр под названием timeout, который задаёт максимальное время (в секундах), в течение которого адаптер будет ждать завершения потока.