
مكتبة Python صغيرة تجعل من السهل استغلال حالات السباق (race conditions) في تطبيقات الويب باستخدام Requests.
Requests-Racer هي مكتبة بايثون صغيرة تتيح لك استخدام مكتبة Requests لإرسال طلبات متعددة ستتم معالجتها بواسطة خوادم وجهتها في الوقت نفسه تقريبًا، حتى لو كانت الطلبات ذات وجهات مختلفة أو حمولات بأحجام مختلفة. يمكن أن يكون هذا مفيدًا لاكتشاف واستغلال ثغرات حالة السباق في تطبيقات الويب. (لمزيد من المعلومات، انظر motivation.md.)
لم تكن Requests (و urllib3، التي تستخدمها Requests داخليًا) مخصصة أبدًا للسماح بفعل شيء مثل هذا. لذلك، تُجبر Requests-Racer على اللجوء إلى بعض الحيل الرديئة إلى حد ما للحصول على تحكم دقيق في كيفية إرسال الطلبات.
تتضمن هذه الحيل العبث بالحالة الخاصة لبعض كائنات urllib3، لذا فإن تحديث urllib3 المتوافق مع الإصدارات السابقة فيما يتعلق بواجهة برمجة التطبيقات العامة قد يكسر Requests-Racer. لذلك، أوصي باستخدام Requests-Racer في بيئة virtualenv وتثبيته قبل Requests أو urllib3، بحيث يتم سحب إصدار متوافق معروف من هذه المكتبات كاعتماد.
لاستخدام Requests-Racer، ستحتاج إلى بايثون 3.5 أو أحدث. أولاً، أنشئ بيئة بايثون افتراضية وفعّلها:
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 من خلال توفير محول نقل (Transport Adapter) بديل لـ 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 ليس آمنًا للخيوط (thread-safe).SynchronizedAdapter لن تقوم بتحديث كائن الجلسة (على سبيل المثال، لن تتم إضافة ملفات تعريف الارتباط من ترويسات Set-Cookie إلى وعاء ملفات تعريف الارتباط الخاص بالجلسة).999 وستحتوي على تتبع (traceback) في السمة .text الخاصة بها.SynchronizedSession.from_regular_session كائن SynchronizedSession من مثيل requests.Session. هذا مفيد إذا كنت بحاجة إلى إجراء بعض الطلبات البسيطة مثل تسجيل الدخول إلى خدمة والحصول على ملف تعريف ارتباط للجلسة ستحتاجه للطلبات المتزامنة.SynchronizedAdapter وSynchronizedSession معاملًا اختياريًا يسمى num_threads، وهو يحدد أقصى عدد من الخيوط التي سيستخدمها المحول. إذا لم يتم تحديده، سيستخدم المحول خيطًا واحدًا لكل طلب.انظر benchmark/ لملاحظات حول الأداء.
حقوق الطبع والنشر (C) 2019 Aleksejs Popovs, NCC Group
هذا البرنامج برمجيات حرة: يمكنك إعادة توزيعه و/أو تعديله بموجب شروط رخصة جنو العمومية العامة كما نشرتها مؤسسة البرمجيات الحرة، إما الإصدار الثالث من الرخصة أو (حسب اختيارك) أي إصدار لاحق.
يُوزَّع هذا البرنامج على أمل أن يكون مفيدًا، ولكن دون أي ضمان؛ حتى دون الضمان الضمني للقابلية للتسويق أو الملاءمة لغرض معين. راجع رخصة جنو العمومية العامة لمزيد من التفاصيل.
يجب أن تكون قد تلقيت نسخة من رخصة جنو العمومية العامة مع هذا البرنامج. إذا لم يكن الأمر كذلك، فانظر https://www.gnu.org/licenses/.
finish_all() معاملًا اختياريًا يسمى timeout، وهو يحدد أقصى مقدار من الوقت (بالثواني) الذي سينتظره المحول حتى ينتهي الخيط.