كيفية استدعاء واجهة API داخلية من تطبيق iPhone عبر VPN
صنّف الطلب الفاشل حسب رمز NSURLError الخاص به قبل أن تمسّ النفق، ثم أصلح الطبقة التي تعطّلت فعلاً.
تستجيب واجهة API في تطبيق Safari على الهاتف نفسه، بينما ينتهي طلب التطبيق بمهلة. هذه الفجوة ليست النفق عادةً: التطبيق الذي يستدعي خدمة داخلية يرسل طلب HTTPS عاديًا، ولا يحدّد VPN الموجود على الجهاز إلا ما إذا كان لهذا الطلب مسار. كتابة نفق ثانٍ داخل التطبيق قرار من قرارات المنتج، وليست إصلاحًا لمهلة. ما يلي يصنّف الفشل حسب مكان حدوثه، مستعينًا بسطح الأخطاء الذي يملكه التطبيق أصلًا.
ابدأ من الخطأ، لا من النفق
URLSession يبلّغ عن أعطال النقل كقيم NSError داخل NSURLErrorDomain، والرمز هو أرخص دليل متاح. قبل تغيير أي إعداد، سجّل ذلك الرمز مع اسم المضيف الفاشل والوقت المنقضي للمهمة. تبدو المهلة وفشل الاسم وفشل الثقة متطابقة على الشاشة وتتطلب ثلاثة إصلاحات مختلفة. صنّف أولًا، ثم اقرأ القسم الخاص بتلك الفئة.
تجيب مقاييس المهمة عن السؤال التالي: هل أُنشئ اتصال أصلًا، وهذا يفصل الطلب الذي لم يكن له مسار قط عن الطلب الذي استغرق الخادم وقتًا طويلًا للرد عليه. لا شيء من هذا يتطلب تغيير النفق؛ بل يتطلب طباعة تشخيصات الطلب نفسه في مكان يمكنك قراءته.
يفشل تحليل الاسم قبل أن ينجح أي شيء آخر
الاسم المكتبي القصير مثل api.corp.example يعتمد عادةً على نطاق بحث لا يحمله الهاتف، فيحلّ التطبيق اسمًا موجودًا داخل المبنى فقط. NSURLErrorCannotFindHost هو البصمة. يجب أن يُحلّ اسم المضيف الكامل عبر أي محلّل يثبّته VPN، وعلى التطبيق أن يطلب الصيغة المؤهلة بالكامل كلما لم يكن نطاق البحث مضمونًا. استبدال الاسم بعنوان وتعطيل فحوص الشهادات ينقل الفشل بدلًا من إصلاحه.
سجل DNS خاص وشهادة صادرة لذلك الاسم تحديدًا هما الزوج المدعوم. كل ما عدا ذلك حلّ التفافي يُشحن إلى المستخدمين.
المهلة مسألة مسار، لا مسألة اسم
NSURLErrorTimedOut يعني أن الاسم حُلّ ولم يُجب شيء، وهو ما يشير إلى المسار أو سياسة الحزم أو الخدمة. عندما يُخصّص النفق لكل تطبيق، فإن تطبيقًا آخر يصل إلى الخادم نفسه لا يقول شيئًا عن نسختك، لأن هذا التعيين غير مشترك. تحقق من أن الحزمة التي تشغّلها داخل هذا التعيين، ثم تحقق من أن مسار الخدمة مسموح لها. نسخة تطوير مثبّتة خارج التعيين يمكن أن تقبع خارج النفق تمامًا وتبدو وكأن API معطّل.
مقارنة عبر المتصفح تُظهر أن أحد التطبيقات على الجهاز يملك مسارًا. لكنها لا تثبت أبدًا أن نسختك تملكه، ولهذا يجب إجراء الاختبارين منفصلين.
أعطال الثقة تتعلق بالاسم الموجود على الشهادة
NSURLErrorServerCertificateUntrusted يعني، إلى جانب خطأ TLS الكامن تحته، أن السلسلة أو فحص الهوية قد فشل. يجب الوثوق بمركز CA داخلي على الجهاز، والشهادة الصادرة لاسم مضيف مختلف تظل خاطئة مهما بلغ حجم إعادة تهيئة الشبكة. إرشادات أمان النقل في التطبيقات من آبل لا تزال تنطبق على اتصالات تحميل URL هذه، والنفق حول خدمة HTTP عادية لا يجعل تلك الخدمة آمنة من الطرف إلى الطرف.
أصلح الهوية أو مرساة الثقة وأبقِ قائمة الاستثناءات فارغة. استثناء التحميل العشوائي الشامل يخفي الفشل أثناء التطوير ويتركه في مكانه لكل مستخدم بعد ذلك.
رفض HTTP ينتمي إلى التطبيق، لا إلى الشبكة
يعني 401 أو 403 بعد مصافحة مكتملة أن الطلب وصل ورفضته الخدمة، فالسؤال المتبقي هو حساب أو جمهور رمز أو نطاق أو مفتاح. سجّل رمز الحالة واسم المضيف، ولا تسجّل أبدًا رمز الحامل أو جسم الاستجابة. عند هذه النقطة يؤدي النفق عمله، وأي تغييرات شبكية إضافية تؤخر الإصلاح الحقيقي فقط.
الجدول هو التصنيف الذي تحتفظ به بجانب الطرفية: صف واحد لكل فشل يستطيع التطبيق الإبلاغ عنه فعلًا. اطبع الرمز قبل أن تقرأه، لأن الصف الذي تختاره يحدد القسم الذي ستعمل خلاله.
| Where it fails | What the app sees | First check |
|---|---|---|
| name resolution | NSURLErrorCannotFindHost (-1003) | resolve the fully qualified name from the device itself |
| no path, or no answer | NSURLErrorTimedOut (-1001) | is this bundle inside the per-app mapping, and is the service path allowed |
| TLS trust | NSURLErrorServerCertificateUntrusted (-1202) | does the certificate name match the host, and is the internal CA trusted |
| service refusal | HTTP 401 or 403 after a completed handshake | account, token audience, scope or key |
كل صف أرخص في الاختبار من الصف الذي تحته، وكل صف يستبعد طبقة مختلفة من الطلب نفسه. انزل في الجدول حتى يطابق صف، ثم توقف عن تغيير الشبكة.