خطای ProxyError در Requests روی API خصوصی؟ ابتدا محیط را بررسی کنید
Requests تنظیمات پروکسی، احراز هویت و CA را از محیط ادغام میکند. ببینید فرایند چه میبیند، میزبان خصوصی را استثنا کنید و بررسی TLS را روشن نگه دارید.
VPN روشن است، داشبورد داخلی در مرورگر باز میشود و اسکریپت Python همچنان خطای پروکسی میدهد. در این تصویر هیچ مشکلی با تونل نیست: مفسر یک نشانی پروکسی از محیط خودش میخواند و درخواست را به آنجا میفرستد. این راهنما محیطی را میخواند که فرایند واقعاً میبیند، تنگترین استثنا را اعمال میکند و تنها پس از آن یک سیاست نشست صریح را در نظر میگیرد. هر گام با فرمانی تمام میشود که خروجیاش تعیین میکند ادامه میدهید یا نه.
محیطی را بخوانید که فرایند واقعاً میبیند
تنظیمات پروکسی میزکار، خروجیهای شل و خطوط Environment= یک واحد systemd سه منبع متفاوتاند و Requests فقط آخرین منبعی را که به ارث میبرد میخواند. تشخیص زیر را در همان شل، کانتینر یا واحدی اجرا کنید که اسکریپت ناموفق را اجرا میکند، نه در ترمینال تعاملی خودتان. متغیری که در شل ورود شما صادر شده برای فرایند کانتینر نامرئی است و همین شکاف بسیاری از گزارشها درباره پروکسیای را توضیح میدهد که از ناکجا ظاهر میشود. دو فرمان همان داده را با سطوح جزئیات متفاوت چاپ میکنند.
env | grep -i -E 'proxy|requests_ca_bundle|curl_ca_bundle'
python3 -c "import os;[print(k,'=',v) for k,v in sorted(os.environ.items()) if 'proxy' in k.lower()]"
هر دو املای هر متغیر خوانده میشود و ورودیهای تکراری به همان ترتیبی اعمال میشوند که فرایند آنها را دریافت کرده است، بنابراین یک HTTP_PROXY کهنه میتواند بر مقداری که تازه صادر کردهاید چیره شود. متغیرهای بسته CA هم اینجا مهماند، چون تغییر میدهند که verify به چه چیزی حل شود، بدون آنکه جایی در کد ظاهر شوند. اگر خروجی خالی است، محیط مشکل شما نیست و میتوانید خواندن این بخش را متوقف کنید. اگر پروکسیای فهرست میکند که نمیشناسید، پیش از تغییر هر چیزی فایل یا واحدی را که آن را تنظیم میکند پیدا کنید.
از Requests بپرسید با یک URL مشخص چه میکند. این فراخوانی مقادیر ادغامشده پروکسی، verify و cert را برای آن درخواست بدون فرستادن بسته نشان میدهد.
python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"
ورودی غیرخالی proxies برای میزبان خصوصی پاسخ شماست و مقدار چاپشده verify میگوید درخواست به کدام بسته CA اعتماد میکرد. اگر هر دو درست به نظر میرسند، خرابی در ادامه پشته است و باید به بخش بررسی بروید. از کد حدس نزنید: ادغام برای هر درخواست رخ میدهد، به همین دلیل یک نشست میتواند برای دو میزبان رفتار متفاوتی داشته باشد.
پیش از غیرفعال کردن هر چیزی، میزبان خصوصی را استثنا کنید
وقتی پروکسی واقعاً برای مقاصد دیگر لازم است، اصلاح باریک، استثنا کردن این میزبان است. به فهرست موجود بیفزایید و آن را جایگزین نکنید، چون پاک کردن NO_PROXY هر مقصد داخلی دیگری را که به آن تکیه داشت میشکند. فرمانهای زیر ورودی را اضافه میکنند و سپس از Requests میپرسند آیا عبور اعمال میشود.
export NO_PROXY="${NO_PROXY:+$NO_PROXY,}api.corp.example,.corp.example"
python3 -c "import requests;print(requests.utils.should_bypass_proxies('https://api.corp.example/status',None))"
تطبیق بر اساس پسوند میزبان است، پس .corp.example کل دامنه را پوشش میدهد و api.corp.example یک نام را. ستاره خالی پروکسی را برای همه میزبانها غیرفعال میکند و تقریباً هرگز آن چیزی نیست که یک کار تولیدی میخواهد. اینکه ورودی CIDR رعایت شود به نسخههای Requests و urllib3 در ایمیج بستگی دارد، پس ورودی دقیق را آزمایش کنید و فرض نکنید کار میکند. نتیجه should_bypass_proxies را برای URL واقعی بررسی کنید، نه برای نام میزبانی که از حافظه تایپ کردهاید.
دو لایه را در ذهن جدا نگه دارید. عبور از پروکسی HTTP به معنای عبور از VPN نیست، چون تصمیم پروکسی در کتابخانه گرفته میشود و تصمیم تونل در جدول مسیریابی هسته.
trust_env را فقط بهعنوان یک سیاست نشست آگاهانه غیرفعال کنید
اگر کل کار باید پیکربندی محیطی را نادیده بگیرد، نشست را صریح بسازید و تصمیم را در کد ثبت کنید. این یک سیاست است، نه راهحل موقت، و جای آن در diffی است که بازبین میبیند. مثال زیر یک مهلت، مسیر صریح CA و بررسی وضعیت را نگه میدارد.
import requests
with requests.Session() as session:
session.trust_env = False
response = session.get(
"https://api.corp.example/status",
timeout=(5, 20),
verify="/etc/ssl/certs/corp-ca.pem",
)
response.raise_for_status()
خاموش کردن trust_env سه رفتار را همزمان تغییر میدهد و تنها یکی از آنها درباره پروکسی است. Requests خواندن اعتبارنامههای .netrc را متوقف میکند و متغیرهای REQUESTS_CA_BUNDLE و CURL_CA_BUNDLE دیگر بر verify اثر نمیگذارند، که آنگاه به انبار گواهی همراه بازمیگردد. اگر سازمان شما TLS را با CA خودش پایان میدهد، آن بازگشت خطای بررسیای ایجاد میکند که بیربط با تغییری که تازه دادهاید به نظر میرسد. همانا ارسال صریح verify است که دو اثر را جدا نگه میدارد.
کاری که نباید بکنید این است که verify=False را تنظیم کنید تا خطای حاصل را خاموش کنید. این کار پیکربندی غلط پروکسی را با اتصالی تأییدنشده معاوضه میکند و مشکل گواهیای را که استثنا باید آشکار میکرد پنهان میسازد.
لایه خراب را بررسی کنید و امکان بازگشت را نگه دارید
سه خرابی در گزارش یکسان به نظر میرسند و هیچ اشتراکی ندارند: پروکسیای که به مقصد نمیرسد ProxyError پرتاب میکند یا 502 برمیگرداند، مشکل اعتماد SSLError پرتاب میکند و رد درخواست برنامه 401 یا 403 برمیگرداند. درخواست را بیرون از Requests بازتولید کنید تا ببینید کدام را دارید. فرمانهای زیر پروکسی را حذف میکنند، زنجیره گواهی را میآزمایند و سپس فراخوانی را از طریق کتابخانه تکرار میکنند.
curl -sv --noproxy '*' https://api.corp.example/status -o /dev/null
openssl s_client -connect api.corp.example:443 -servername api.corp.example </dev/null
python3 -c "import requests;r=requests.get('https://api.corp.example/status',timeout=10);print(r.status_code)"
اگر فراخوانی curl موفق شود و فراخوانی Python شکست بخورد، محیط هنوز تفاوت است. اگر هر دو در مرحله TLS شکست بخورند، پروکسی هرگز مشکل نبود و زنجیره گواهی مشکل است. اگر هر دو 401 برگردانند، درخواست به برنامه رسیده و کار باقیمانده احراز هویت است. ثبت اینکه کدام یک از سه را مشاهده کردهاید یک ساعت برای نفر بعدی صرفهجویی میکند.
بازگشت یک خط است در هر جهت: استثنا را بردارید یا trust_env را برگردانید. مقدار قبلی NO_PROXY را پیش از افزودن به آن ذخیره کنید و اسکریپت پوششی یا ورودی Environment= در سطح واحد را به خروجیای که فقط در تاریخ ترمینال شما زندگی میکند ترجیح دهید. هر دو تغییر در محیط فرایند دیده میشوند، بنابراین راهاندازی مجدد معمولاً شما را به رفتار قبلی برمیگرداند.
با تأیید چهار چیز تمام کنید: تنظیمات ادغامشده برای URL خصوصی هیچ پروکسی نشان نمیدهد، گواهی در برابر CA موردنظر معتبر است، درخواست همان وضعیتی را که انتظار دارید برمیگرداند و تغییر در جایی قابل بازتولید کامیت شده است. اسکریپت سبز با محیطی توضیحنشده اصلاحی است که روی ماشین بعدی دوباره میشکند. فرمانهای این راهنما همانهایی هستند که باید در تیکت بچسبانید.