Gunicorn: إعادة إنتاج خطأ قديم : كائن "الاستجابة" ليس له سمة "status_code" في wsgi.py مع مآخذ ويب

تم إنشاؤها على ٩ أغسطس ٢٠١٨  ·  33تعليقات  ·  مصدر: benoitc/gunicorn

تمامًا كما قال هذا الإصدار القديم 1210 ، خطأ في سجلات gunicorn عند قطع اتصال العميل ، وبيئتي هي :

  • دبيان جنو / لينكس 7.8.0

  • nginx

  • لغة البرمجة Python 3.4

  • gunicorn (19.8.1) (مع عامل واحد أو عدة عمال)

  • Flask-SocketIO ، يحدد العميل نقل Websocket

كل شيء يعمل بشكل جيد بما في ذلك العملاء ، باستثناء سجل الأخطاء هذا ، حيث يتم تسجيل اثنين من مثيلي الإنتاج المستقلين عن السحابة - ولكن لا يمكنني إعادة إنتاجه في جهاز التطوير الخاص بي ، وهو جهاز Mac.

شكرا جزيلا لمساعدتك.

خطأ في معالجة الطلب /socket.io/؟EIO=3&transport=websocket
Traceback (آخر مكالمة أخيرة):
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/workers/async.py" ، السطر 56 ، في المقبض
self.handle_request (listener_name، req، client، addr)
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/workers/async.py" ، السطر 116 ، في handle_request
Resp.close ()
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py" ، السطر 409 ، في الإغلاق
self.send_headers ()
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py" ، السطر 325 ، في send_headers
tosend = self.default_headers ()
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py" ، السطر 306 ، في default_headers
elif self.should_close ():
ملف "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py" ، السطر 229 ، في should_close
إذا كان self.status_code <200 أو self.status_code in (204، 304):
AttributeError: الكائن "Response" ليس له سمة "status_code"

Feedback Requested unconfirmed ThirdPartFlask

التعليق الأكثر فائدة

عافيهbenoitc

ال 33 كومينتر

هل لديك أي مثال بسيط لإعادة إنتاجه؟ يرجى أيضًا المحاولة مع أحدث معلم إذا أمكن.

لقد حاولت سابقًا عدة مرات في بيئة التطوير المحلية الخاصة بي - وهي نفس كود التطبيق مع بيئة الإنتاج - لكن لا يمكنني إعادة إنتاجه.

وقد تحققت من سجل إصدار الإصدار 19.9.0 , لم أجد شيئًا مرتبطًا به , سأحتفظ به
مشاهدة سجل الخطأ هذا إذا وجدت شيئًا جديدًا , سأقوم بنشره هنا.

لدي أيضًا هذه المشكلة ، خاصة عندما أقوم بفرض كل اتصالي من بروتوكول العميل إلى بروتوكول Websocket. إعداداتي هي نفسها BoWuGit. إذا تم السماح ببروتوكول الاقتراع قبل الترقية ، فلن يظهر هذا ، ولكن خطأ آخر:
"
[خطأ] خطأ في معالجة الطلب /socket.io/؟EIO=3&transport=polling&t=MPRHUoV&sid=cd64be7c940e474d8728b114c3fb9bbe

Traceback (آخر مكالمة أخيرة):
ملف "/usr/local/lib/python3.6/site-packages/gunicorn/workers/async.py" ، السطر 56 ، في المقبض
self.handle_request (listener_name، req، client، addr)

ملف "/usr/local/lib/python3.6/site-packages/gunicorn/workers/async.py" ، السطر 107 ، في handle_request
respiter = self.wsgi (بيئة ، resp.start_response)

ملف "/usr/local/lib/python3.6/site-packages/flask/app.py" ، سطر 1994 ، في __call__
إرجاع self.wsgi_app (بيئة ، start_response)

ملف "/usr/local/lib/python3.6/site-packages/flask_socketio/__init__.py" ، السطر 43 ، في __call__
start_response)

ملف "/usr/local/lib/python3.6/site-packages/engineio/middleware.
py "، السطر 47 ، في __call__
إرجاع self.engineio_app.handle_request (بيئة ، start_response)

ملف "/usr/local/lib/python3.6/site-packages/socketio/server.py" ، السطر 360 ، في handle_request
إرجاع self.eio.handle_request (بيئة ، start_response)

ملف "/usr/local/lib/python3.6/site-packages/engineio/server.py" ، السطر 279 ، في handle_request
مقبس = self._get_socket (sid)

ملف "/usr/local/lib/python3.6/site-packages/engineio/server.py" ، السطر 439 ، في _get_socket
رفع KeyError ("الجلسة غير متصلة")
"
لكنني أظن أنه قد يكون لها علاقة ببعضها البعض ، حيث أنني أجبرت الاتصال على أن يكون مقبس ويب ، لم يتم رؤية هذا الخطأ مرة أخرى.

وجود هذه المشكلة أيضًا مع Gunicorn 19.9.0 و Flask-socketIO 3.0.2 ، عند استخدام Eventlet 0.24.1

AttributeError: الكائن "Response" ليس له سمة "status_code"

تواجه أيضًا هذه المشكلة مع المتطلبات التالية:

Flask==1.0.2
gunicorn==19.5.0
python-socketio==2.0.0
eventlet==0.24.1

ظهور رسالة خطأ عند إغلاق متصفح الويب الذي به اتصال مقبس مفتوح:

 Error handling request /socket.io/?EIO=3&transport=websocket&sid=d43ec0ae0bb946debc51f1ca2e5b8a94
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/gunicorn/workers/async.py", line 52, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/usr/lib/python2.7/dist-packages/gunicorn/workers/async.py", line 114, in handle_request
    resp.close()
  File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 403, in close
    self.send_headers()
  File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 319, in send_headers
    tosend = self.default_headers()
  File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 300, in default_headers
    elif self.should_close():
  File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 233, in should_close
    if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'

يبدو أنه تم إصلاح هذه المشكلة في أحدث إصدار من python-engineio ..

تم اختباره باستخدام أحدث إصدار من برنامج python-engineio (2.3.2) ، وما زلت لا تعمل.

أي أخبار بشأن هذه المسألة؟ أحصل على نفس الخطأ عند استخدام Sentry-python

لدي نفس المشكلة

الحدث الصغير: 0.25.1
مقبس قارورة: 4.2.1
جونيكورن: 19.9.0

image

image

كيف تتكاثر؟ هل cna تقدم مثالا بسيطا؟

لست متأكدًا أيضًا من كيفية إعادة إنتاجه ، ولكنه يحدث غالبًا عندما أقوم بتحديث صفحة على تطبيق gunicorn الخاص بي

واجهت نفس المشكلة ، وبيئتي هي نفسها eazow بينما gunicorn == 20.0.4.
يبدو أن المشكلة حدثت بعد أن قمت بتثبيت الحارس لتتبع الأخطاء.
يمكن إعادة إنتاج القضايا من قبل

  1. تحديث الصفحة (وليس فتح صفحة جديدة)
  2. إغلاق الصفحة

من المثير للاهتمام أن فتح صفحة جديدة لن ينتج عنه المشكلة. غير متأكد من السبب. شكرا!

لدي نفس المشكلة مثل cowbonlin . نفس إصدار gunicorn أيضًا.

بعد تثبيت الحارس ، نتلقى قدرًا هائلاً من هذا الخطأ. على الرغم من أنني أجد صعوبة في معرفة ما إذا كان هذا يحدث دائمًا أم لا - لأننا لم نتتبع الأخطاء قبل الحراسة.

في حين أنه لا يبدو أنه يؤثر على الوظائف الفعلية لخادمنا ، فإن هذا مجرد عدد كبير من الرسائل غير المرغوب فيها.

نحن نواجه نفس الشيء. تم تثبيت Sentry لكنه معطل. أيه أفكار؟

تم تثبيت نفس المشكلة مع الحارس.

هل لديك أي مثال يعيد إنتاجه بدون حراسة (معطل أم لا)؟

بالإضافة إلى ذلك ، بدلاً من مساحة الاسم ، قمت بالضغط على / api يدويًا.

بالإضافة إلى ذلك ، بدلاً من مساحة الاسم ، قمت بالضغط على / api يدويًا.

ماذا يعني ؟ هل هذا الحارس مرتبط؟

بالإضافة إلى ذلك ، بدلاً من مساحة الاسم ، قمت بالضغط على / api يدويًا.

ماذا يعني ؟ هل هذا الحارس مرتبط؟

لا ، هذا متعلق بمساحات أسماء socket.io. حاولت إزالتها ، وحتى بعد إزالتها ، تلقيت الخطأ. أحصل على هذا الخطأ الآخر على الجهاز المحلي بدون gunicorn أو nginx ومع ذلك ، والذي قد يكون مرتبطًا.

هذه هي متطلباتي:

sentry_sdk == 0.14.3
Flask_SocketIO == 4.2.1
eventlet == 0.25.1

هذا هو كود flask-socketio الخاص بي على جانب الخادم:

socketio = SocketIO(engineio_logger=True, logger=True, debug=True, cors_allowed_origins="*", path='/socket.io')
...
socketio.init_app(app, async_mode="eventlet")

وهذا هو كود React socket io الخاص بي من جانب العميل:

          this.socket = io.connect(`http://localhost:5000?info=${someInfo}`, {
            transports: ['websocket', 'polling'] // an attempt to keep polling as a fallback but start on websockets
          });

اعلمني اذا كان هذا مفيدا لك. في Ubuntu ، يبدو الخطأ مثل الخطأ أعلاه ، ويبدو محليًا على Windows كما يلي:
`` Traceback (آخر مكالمة أخيرة):
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 599 ، في handle_one_response
اكتب (ب ")
الملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 491 ، كتابة
رفع AssertionError ("write () قبل start_response ()")
AssertionError: اكتب () قبل start_response ()

أثناء معالجة الاستثناء أعلاه ، حدث استثناء آخر:

Traceback (آخر مكالمة أخيرة):
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 357 ، في __init__
التعامل مع النفس ()
الملف "C: \ ProgramData \ Anaconda3 \ lib \ site -packs \ eventlet \ wsgi.py" ، السطر 390 ، في المقبض
طلب self.handle_one_request ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 466 ، في handle_one_request
self.handle_one_response ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 609 ، في handle_one_response
الكتابة (err_body)
الملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 538 ، كتابة
wfile.flush ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ socket.py" ، السطر 607 ، في الكتابة
إرجاع self._sock.send (ب)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ greenio \ base.py" ، السطر 397 ، في الإرسال
إرجاع self._send_loop (self.fd.send ، بيانات ، أعلام)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ greenio \ base.py" ، السطر 384 ، في _send_loop
إرجاع send_method (data، * args)
ConnectionAbortedError: [WinError 10053] تم إحباط اتصال مؤكد بواسطة البرنامج في الجهاز المضيف الخاص بك

أثناء معالجة الاستثناء أعلاه ، حدث استثناء آخر:

Traceback (آخر مكالمة أخيرة):
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ hubs \ hub.py" ، السطر 461 ، في fire_timers
مؤقت ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ hubs \ timer.py" ، السطر 59 ، في __call__
cb ( args ، * kw)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ semaphore.py" ، السطر 147 ، في _do_acquire
waiter.switch ()
الملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ greenthread.py" ، السطر 221 ، بشكل رئيسي
النتيجة = الوظيفة (أرغس ، * كارجس)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 818 ، في process_request
proto .__ init __ (conn_state، self)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ wsgi.py" ، السطر 359 ، في __init__
self.finish ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -packs \ eventlet \ wsgi.py" ، السطر 732 ، في النهاية
BaseHTTPServer.BaseHTTPRequestHandler.finish (ذاتي)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ socketserver.py" ، السطر 784 ، في النهاية
self.wfile.close ()
ملف "C: \ ProgramData \ Anaconda3 \ lib \ socket.py" ، السطر 607 ، في الكتابة
إرجاع self._sock.send (ب)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ greenio \ base.py" ، السطر 397 ، في الإرسال
إرجاع self._send_loop (self.fd.send ، بيانات ، أعلام)
ملف "C: \ ProgramData \ Anaconda3 \ lib \ site -pack \ eventlet \ greenio \ base.py" ، السطر 384 ، في _send_loop
إرجاع send_method (data، * args)
ConnectionAbortedError: [WinError 10053] تم إحباط اتصال مؤكد بواسطة البرنامج في الجهاز المضيف الخاص بك ""

يمكن تأكيد اختفاء هذا الخطأ عند تعطيل الحارس بالكامل. سيكون رائعًا إذا كان gunicorn قويًا بما يكفي للتعامل مع هذا.

عافيهbenoitc

يمكن تأكيد اختفاء هذا الخطأ عند تعطيل الحارس بالكامل. سيكون رائعًا إذا كان gunicorn قويًا بما يكفي للتعامل مع هذا.

لقد اكتشفت أن تعطيل جهاز Sentry FlaskIntegration يؤدي أيضًا إلى اختفاء الخطأ.

رؤية سلوك مشابه. يؤدي استخدام New Relic في الإنتاج إلى حدوث هذا الخطأ مع flask-socketio. في مرحلة التطوير ، يجب تحميل البرمجيات الوسيطة werkzeug debugger قبل تهيئة flask-socketio (لذلك لا يتم تطبيقها على تطبيق wsgi الخاص بـ engineio). المشكلة هي أن الإنتاج هو المكان الذي لا أريد فيه أن تتعثر الأخطاء.

لا يمكن استبدال الاستجابة في post_request لـ gunicorn config ، لكنني حاولت إجبار رمز الحالة على resp.status_code. لم يستغرق الأمر رغم ذلك.

يمكن تكرار هذا الخطأ باستخدام FlaskIntegration الخاص بـ Sentry مع Gunicorn و Flask-SocketIO. هل من الممكن حلها قريبا؟

Canicio فكرنا في محاولة ذلك للتخلص من الخطأ وحتى بعد تعطيل التكامل ، استمر الخطأ.

هل لدى أي شخص رمز قابل للمشاركة / مثال بسيط على benoitc ليخرج منه؟

بالتأكيد:

import sentry_sdk
from flask import Flask
from flask_socketio import SocketIO
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn="https://[email protected]/0",
    integrations=[FlaskIntegration()]
)

app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/')
def index():
    return '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
    var socket = io()
</script>

المتطلبات:

flask
sentry-sdk[flask]
flask-socketio
eventlet

مثال التكوين gunicorn:

bind = '[::]:4444'
worker_class = 'eventlet'
accesslog = '-'

عند تحميل / سيتصل بمقبس الويب. عند قطع الاتصال بمقبس الويب (على سبيل المثال ، التنقل بعيدًا والتحديث) ، سينتج استثناء مثل:

[2020-09-23 07:24:49 +0000] [16303] [ERROR] Error handling request /socket.io/?EIO=3&transport=websocket&sid=29f4c1adfac343d6bc6db56acf8fd0ee
Traceback (most recent call last):
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/workers/base_async.py", line 55, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/workers/base_async.py", line 115, in handle_request
    resp.close()
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 402, in close
    self.send_headers()
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 318, in send_headers
    tosend = self.default_headers()
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 299, in default_headers
    elif self.should_close():
  File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 219, in should_close
    if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'
2001:470:1f07:7eb:9dd4:254c:35d7:236c - - [23/Sep/2020:07:24:49 +0000] "GET /socket.io/?EIO=3&transport=websocket&sid=29f4c1adfac343d6bc6db56acf8fd0ee HTTP/1.1" 500 0 "-" "-"

ملاحظة: لم أستخدم الحارس مطلقًا في الواقع. هذا فقط من الحارس بدء الصفحة. المثال dsn يعمل بشكل جيد في الاختبار الخاص بنا.

سيؤدي التعليق integrations=[FlaskIntegration()] إلى إزالة الخطأ (بالطبع يؤدي إلى تعطيل الحارس بشكل فعال).

لما يستحق ، يمكن استخدام gevent-websocket بدلاً من الحدث الصغير بدون أخطاء. ومع ذلك ، يبدو بعد ذلك أنه يعالج جميع الطلبات ..

حسنًا ، فعل بعض اللعب. يبدو أن الحارس / newrelic يلف الاستجابة. بدون الحارس ، نحصل على <eventlet.wsgi._AlreadyHandled object at 0x7fd0f5b1c0d0> كما هو متوقع وسيوقف Gunicorn's EventletWorker.is_already_handled () التكرار.

ومع ذلك ، عند استخدام الحارس ، يصبح هذا شيئًا مثل <sentry_sdk.integrations.wsgi._ScopedResponse object at 0x7f30155a5100> بدلاً من ذلك ، مما يؤدي إلى فشل الشيك

بدلاً من ذلك ، يمكننا إلقاء نظرة خاطفة على جهاز التنفس لمعرفة ما إذا كان فارغًا. سوف ننظر أبعد غدا.

حسنًا ، هذا هو الحل الذي توصلت إليه:

eventlet_fix.py:
انظر التحرير أدناه

وفي ملف التكوين gunicorn الخاص بي: worker_class = 'eventlet_fix.EventletWorker .

المشكلة هي أن الحارس / newrelic يغلف الردود ، لذلك لا يمكننا ببساطة التحقق من ذلك مقابل ALREADY_HANDLED لـ eventlet. نظرًا لأن طبيعة الطلب الذي تمت معالجته بالفعل هو أن gunicorn start_response لا يتم الاتصال به ، يمكننا بدلاً من ذلك التحقق من وجود حالة استجابة.

لذا فقد اختطفت استدعاء wsgi للتحقق من حالة الاستجابة ثم اختراق قيم الاستجابة حسب الضرورة. هذا يسمح للطلب أن يستمر تسجيله بواسطة gunicorn. إذا رغبت بدلاً من ذلك في الاحتفاظ بالسلوك الأصلي ، فيمكن رفع StopIteration بدلاً من ذلك.

يعد اختراق الحالة إلى 101 مناسبًا لحالة الاستخدام الخاصة بنا هنا (flask-socketio websocket) ، ولكن بخلاف ذلك ، فإن تركها كـ None يعمل أيضًا نظرًا لأن headers_sent و should_close مضطران إلى True.

مرة أخرى ، هذا يفترض أنه إذا لم يتم تعيين start_response status فلن يتم استدعاء $ # $ 7 $ # $ ، وبالتالي يجب أن يكون الطلب قد "تمت معالجته بالفعل" خارجيًا.

تحرير: ليس جيدا. سوف تحتاج إلى إعادة التقييم. إذا استغرق تنفيذ الطلب وقتًا ، فلن يتم استدعاء $ # start_response قبل تحديد resp.status .

تحرير 2: إليك إصدار ثابت به مكرر استجابة مخترق:

from functools import wraps

from gunicorn.workers.geventlet import EventletWorker as _EventletWorker


class HackedResponse:
    def __init__(self, respiter, resp):
        self.respiter = iter(respiter)
        self.resp = resp
        if hasattr(respiter, "close"):
            self.close = respiter.close

    def __iter__(self):
        return self

    def __next__(self):
        try:
            return next(self.respiter)
        except StopIteration:
            if not self.resp.status:
                self.resp.status = "101"  # logger derives status code from status instead of using status_code
                self.resp.status_code = 101  # not actually needed since headers_sent/force_close result in status_code not being checked anymore
                self.resp.headers_sent = True
                self.resp.force_close()
            raise


def wsgi_decorator(wsgi):
    @wraps(wsgi)
    def wrapper(environ, start_response):
        respiter = wsgi(environ, start_response)
        resp = start_response.__self__
        return HackedResponse(respiter, resp)

    return wrapper


class EventletWorker(_EventletWorker):
    def load_wsgi(self):
        super().load_wsgi()
        self.wsgi = wsgi_decorator(self.wsgi)

من الواضح أن هذه مجرد رقعة قرد. من المحتمل أن يصل الإصلاح الفعلي إلى handle_request في base_async.py. قد يكون المفتاح هو التحقق (بشكل غير مباشر) مما إذا كان قد تم استدعاء start_response بعد التكرار خلال respiter ، إما عن طريق تحديد resp.status (فقط start_response يسمى) أو resp.headers_sent (تأكيد بأننا استجبنا بالفعل للطلب).

تضمين التغريدة
وجد ziddey طريقة لحل المشكلة.

ziddey أسئلة سريعة لمثالك (لأنني لا أستخدم الحارس).

  • هل يؤثر الخطأ فقط على الحارس أم أن الطلب متوقف أيضًا ، أي أن العامل يُنهي الخدمة (وهو ما أظن أنه يحدث إذا تم تغليف الاستجابة)؟
  • هل تتوقع أن يكون لديك شيء ملفوف هناك أو تنظيف ven الطلب إذا تم تغليف الاستجابة سيكون على ما يرام؟

benoitc غير قادر على الاختبار حاليًا ، لكن بالنظر إلى التتبع أعلاه https://github.com/benoitc/gunicorn/issues/1852#issuecomment -697189261 و https://github.com/benoitc/gunicorn/blob/4ae2a05c37b332773997f90ba754274b9b9b gunicorn / worker / base_async.py # L107 -L140

عادة ، is_already_handled سيعود True وينتهي هنا.

ومع ذلك ، نظرًا لأن الاستجابة ملفوفة ، فإن هذه الطريقة لا تعمل. بدلاً من ذلك ، يتقدم التنفيذ ، والفشل في السطر 115: يحاول resp.close() إرسال رؤوس ، ولكن لم يتم استدعاء start_response ، لذلك لا يوجد رمز حالة. حتى لو حدث ذلك ، فستظل تفشل في النهاية بشكل واضح.

ينتج عن هذا خطأ AttributeError يتم إعادة صياغته ومعالجته على الأرجح بواسطة handle_error . نظرًا لأنه تم التعامل مع الطلب خارجيًا بالفعل ، فلا ضرر هنا بخلاف سجل البريد العشوائي.

لا أستطيع أن أقول الكثير عن تطبيق Sentry - أنا لا أستخدمه أيضًا.

هناك تفصيل واحد: الآلية الحالية التي تمت معالجتها بالفعل لا تؤدي إلى تسجيل وصول. أفترض أن هذا منطقي من الناحية الفنية نظرًا لعدم وجود طريقة لمعرفة كيفية التعامل معه خارجيًا. في استجابتي المخترقة ، أجبرت رمز الحالة على 101 ، مع headers_sent كـ True حتى يتمكن المعالج من المتابعة ولا يزال يتم تسجيل الوصول للطلب.

التحقق resp.status اختبار نهائي لتحديد ما إذا كان قد تم استدعاء start_response .

benoitc إعادة النظر في هذا. للاستنتاج بشكل أكثر تحديدًا أن الطلب تمت معالجته بالفعل ، يمكن أن يكون environ['gunicorn.socket'] نوعًا من الوكيل للكائن الأساسي بدلاً من ذلك. بهذه الطريقة ، يمكن تسجيله عند الوصول إلى المقبس مباشرة (على سبيل المثال التفاف get_socket() للحدث الصغير) ، واستخدامه لشيء مثل is_already_handled

لا يزال يتطلب اختراق حالة الاستجابة على الرغم من أنه إذا كان تسجيل الوصول مطلوبًا.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات