تخطَّ إلى المحتوى

استكشاف أخطاء Gotenberg وإصلاحها في NextPDF

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

فئات الاستثناءات هي:

  • GotenbergConvertException — فشل في طبقة التحويل (الإعداد أو النقل أو الاستجابة).
  • RuntimeException — فشل في طبقة التحقق تطلقه سياسة الأمان قبل أي حركة شبكية.
  • ValueError — امتداد ملف غير معروف.
  • InvalidSpkiPinException — سلسلة تثبيت ⁨Transport Layer Security⁩‏ (⁨TLS⁩) ⁨SubjectPublicKeyInfo⁩‏ (⁨SPKI⁩) غير سليمة التنسيق.

”⁨Invalid Gotenberg configuration⁩: ⁨apiUrl is empty⁩”

قسم بعنوان «”⁨Invalid Gotenberg configuration⁩: ⁨apiUrl is empty⁩”»
  • النوع: GotenbergConvertException
  • المُطلِق: تم استدعاء convertFile() أو convertString() بينما GotenbergConfig::isValid() تُرجع ⁨false.⁩ يحدث ذلك عندما تكون apiUrl سلسلة فارغة.
  • الإصلاح: زود عنوان ⁨HTTPS URL⁩ غير فارغ. إذا أنشأت الإعداد باستخدام fromArray()، فلاحظ أنها تستبدل '' ضمنيًا عندما تكون قيمة api_url مفقودة أو غير نصية. تحقق من مصدر إعدادك أثناء الإقلاع.

حالات فشل عنوان ⁨URL⁩ والعنوان (⁨SSRF⁩)

قسم بعنوان «حالات فشل عنوان ⁨URL⁩ والعنوان (⁨SSRF⁩)»

تأتي حالات الفشل هذه من سياسة الأمان التي تحمي من تزوير الطلب من جانب الخادم (⁨SSRF⁩). يطلقها الجسر قبل إرسال أي طلب. كل واحدة منها هي RuntimeException عادية.

”⁨Gotenberg API URL must use HTTPS⁩ (⁨got⁩: ⁨http⁩)”

قسم بعنوان «”⁨Gotenberg API URL must use HTTPS⁩ (⁨got⁩: ⁨http⁩)”»
  • المُطلِق: مخطط عنوان ⁨URL⁩ المهيأ ليس https. التحقق غير حساس لحالة الأحرف، لذلك يُقبل HTTPS://.
  • الإصلاح: ضع ⁨Gotenberg⁩ خلف ⁨TLS⁩ واضبط نقطة نهاية ⁨HTTPS.⁩ يُرفض ⁨HTTP⁩ العادي حتى في التطوير المحلي.

”⁨Invalid Gotenberg API URL⁩: ⁨unable to parse⁩”

قسم بعنوان «”⁨Invalid Gotenberg API URL⁩: ⁨unable to parse⁩”»
  • المُطلِق: تعذر تحليل عنوان ⁨URL⁩ إلى مخطط ومضيف.
  • الإصلاح: زود عنوان ⁨URL⁩ مطلقًا صحيحًا نحويًا، مثل https://gotenberg.example.com:3000.

”⁨Gotenberg API URL must not resolve to a private or reserved IP address⁩”

قسم بعنوان «”⁨Gotenberg API URL must not resolve to a private or reserved IP address⁩”»
  • المُطلِق: المضيف هو عنوان بروتوكول إنترنت (⁨IP⁩) خاص أو محجوز حرفيًا، أو اسم مضيف يُحل (عبر كل سجلات ⁨A/AAAA⁩) إلى أي عنوان خاص أو محجوز. يحجب هذا النطاقات الخاصة من طلب التعليقات (⁨RFC⁩) 1918، وعناوين الاسترجاع (⁨loopback⁩)، والعناوين المحلية للرابط (⁨link-local⁩).
  • الإصلاح: وجه الجسر إلى عنوان عام قابل للتوجيه أو إلى عنوان خدمة معزول بشكل سليم. إذا كان ⁨Gotenberg⁩ لديك على شبكة خاصة عمدًا، فإن حارس ⁨SSRF⁩ في الجسر يرفضه حسب التصميم. اكشفه عبر عنوان يقبله الحارس، ثم احم ذلك المسار بضوابط شبكية ومصادقة. راجع /⁨integrations/gotenberg/security-and-operations/.⁩

“⁨Gotenberg API URL DNS answer changed since validation⁩ — ⁨possible DNS rebinding attack⁩”

قسم بعنوان «“⁨Gotenberg API URL DNS answer changed since validation⁩ — ⁨possible DNS rebinding attack⁩”»
  • المُطلِق: بين التحقق الأولي والطلب، أعاد تحليل جديد لنظام أسماء النطاقات (⁨DNS⁩) عنوانًا لم يكن ضمن المجموعة التي تم التحقق منها أصلًا.
  • الإصلاح: هذا يعني أن حارس ⁨time-of-check/time-of-use⁩ قد فُعّل. افحص ⁨DNS⁩ للمضيف. قد يكون السبب المشروع موازن تحميل يدوّر العناوين، وقد يكون السبب الخبيث هجوم إعادة ربط. استخدم عنوانًا ثابتًا أو اسمًا بمجموعة سجلات ثابتة لنقطة نهاية ⁨Gotenberg.⁩

حالات فشل التحقق من المدخلات

قسم بعنوان «حالات فشل التحقق من المدخلات»

تطلق سياسة الأمان حالات الفشل هذه قبل الطلب. كل واحدة منها هي RuntimeException عادية ما لم يُذكر خلاف ذلك.

  • النوع: GotenbergConvertException
  • المُطلِق: تعذر على convertFile() تقييس المسار، أو أن المسار المحلول ليس ملفًا عاديًا قابلًا للقراءة. يُطلق هذا أيضًا عند تمرير دليل.
  • الإصلاح: مرر مسارًا إلى ملف موجود قابل للقراءة. يُقيَّس المسار باستخدام realpath() أولًا، وهذا يبطل أيضًا اجتياز المسارات.

”⁨File size⁩ ( ⁨bytes⁩) ⁨exceeds maximum allowed size⁩ ( ⁨bytes⁩)”

قسم بعنوان «”⁨File size⁩ ( ⁨bytes⁩) ⁨exceeds maximum allowed size⁩ ( ⁨bytes⁩)”»
  • المُطلِق: المُدخل أكبر من maxFileSize (الافتراضي 52,428,800 بايت = 50 ⁨MiB⁩).
  • الإصلاح: ارفع maxFileSize إذا كان المستند يحتاج إلى ذلك فعلًا، أو ارفض الرفع في مرحلة أبكر. أبق الحد الأقصى منخفضًا بقدر ما تسمح به مستنداتك الفعلية. إنه حد الموارد المدمج الوحيد في الجسر.

يتحقق الجسر من اسم الملف. في تحويلات الملفات، يكون اسم الملف هو الاسم الأساسي للمسار المحلول؛ أما في convertString() فهو الاسم الذي تمرره. كل واحدة من هذه الحالات هي RuntimeException:

جزء الرسالةالمُطلِق
must not be emptyاسم ملف فارغ
path traversal sequences (..)يحتوي الاسم على ..
forward slashesيحتوي الاسم على /
backslashesيحتوي الاسم على \
null bytesيحتوي الاسم على بايت ⁨NUL⁩
control charactersيحتوي الاسم على محرف تحكُّم ⁨ASCII⁩‏ (0–31)
  • الإصلاح: مرِّر اسمًا أساسيًا نظيفًا. بالنسبة لـ convertString()، زوِّد اسمًا عاديًا مثل report.docx. يُستخدم لاكتشاف التنسيق وبوصفه اسم ملف الرفع متعدد الأجزاء، وليس بوصفه مسارًا.
  • الإصلاح: مرر اسمًا أساسيًا نظيفًا. بالنسبة إلى convertString()، زود اسمًا عاديًا مثل report.docx. يُستخدم لاكتشاف التنسيق وبوصفه اسم ملف الرفع متعدد الأجزاء، لا بوصفه مسارًا.
  • النوع: ValueError
  • المُطلِق: امتداد الملف ليس واحدًا من docx، xlsx، pptx، odt، ods، odp (غير حساس لحالة الأحرف، ويتسامح مع النقطة البادئة).
  • الإصلاح: حول فقط التنسيقات الستة المعترف بها. لا يتعرف الجسر على التنسيقات الثنائية القديمة (.doc، .xls، .ppt)، أو .rtf، أو .csv، أو النص العادي، أو الصور. حول هذه المدخلات إلى تنسيق معترف به قبل استدعاء الجسر، أو وجهها عبر مسار مختلف.

حالات فشل النقل والاستجابة

قسم بعنوان «حالات فشل النقل والاستجابة»

كل هذه هي GotenbergConvertException.

  • المُطلِق: أطلق عميل توصية ⁨PHP⁩ المعيارية 18‏ (⁨PSR-18⁩) (أو نقل ⁨cURL⁩ المدمج) استثناءً أثناء إرسال الطلب. يكون السبب رفض الاتصال، أو انتهاء مهلة، أو فشل مصافحة ⁨TLS⁩، أو عدم تطابق تثبيت.
  • رمز الاستثناء: رمز استثناء العميل الأساسي.
  • السبب: يُرفق استثناء عميل ⁨PSR-18⁩ الأصلي بوصفه الاستثناء السابق.
  • الإصلاح: تحقق من أربعة أشياء. تحقق من إمكانية الوصول إلى الخدمة باستخدام isAvailable(). تحقق من مسار الشبكة. تحقق من سلسلة ⁨TLS.⁩ إذا كان التثبيت مهيأً، فتحقق من أن ⁨SubjectPublicKeyInfo⁩‏ (⁨SPKI⁩) الحالي للخادم يطابق تثبيتًا مهيأً. عدم تطابق التثبيت بعد تدوير الشهادة هو السبب المعتاد. راجع إجراء التدوير في /⁨integrations/gotenberg/security-and-operations/.⁩
  • المُطلِق: فشل curl_exec الخاص بنقل ⁨cURL⁩ المدمج برقم خطأ ⁨cURL⁩ غير صفري، أو أعاد جسمًا غير نصي.
  • الإصلاح: يحدد رقم خطأ ⁨cURL⁩ السبب (⁨TLS⁩، أو التحليل، أو انتهاء المهلة، أو التثبيت). يظهر فشل التثبيت هنا عندما يرفض CURLOPT_PINNEDPUBLICKEY الشهادة. تأكد من أن التثبيتات المهيأة والعنوان المحلول محدثة.

”⁨Gotenberg conversion failed with HTTP⁩ : ”

قسم بعنوان «”⁨Gotenberg conversion failed with HTTP⁩ : ”»
  • المُطلِق: لم تكن حالة الاستجابة 200. يُدرج الجسم مقتطعًا إلى أول 500 محرف، مع إلحاق علامة حذف عندما يكون أطول.
  • الإصلاح: اقرأ الجسم المدرج. تشرح رسالة خطأ ⁨Gotenberg⁩ سبب رفض التحويل: محتوى مستند غير مدعوم، أو فشل داخلي في ⁨LibreOffice⁩، أو رفض مصادقة على 401 أو 403. 401/403 يعني أن apiKey مفقود أو خاطئ. 5xx فشل من جانب الخدمة، وهو المرشح لإعادة محاولة محدودة.

”⁨Unexpected Content-Type from Gotenberg⁩: (⁨expected application/pdf⁩)”

قسم بعنوان «”⁨Unexpected Content-Type from Gotenberg⁩: (⁨expected application/pdf⁩)”»
  • المُطلِق: كانت الحالة 200، لكن Content-Type في الاستجابة لم يحتوِ على application/pdf.
  • الإصلاح: يعني هذا عادةً أن وكيلًا أو بوابة أعاد صفحة خطأ ⁨HTML⁩ أو صفحة إعادة توجيه بحالة 200. يعطل الجسر تتبع إعادة التوجيه على النقل المدمج عمدًا، لذلك لا يُتبع رد 3xx بصمت إلى مضيف غير متحقق منه. وصول جسم بـ Content-Type خاطئ يشير إلى أن شيئًا ما بينك وبين ⁨Gotenberg⁩ يتدخل. افحص مسار الشبكة.

”⁨Response body does not start with⁩ %⁨PDF header⁩ — ⁨invalid PDF data⁩”

قسم بعنوان «”⁨Response body does not start with⁩ %⁨PDF header⁩ — ⁨invalid PDF data⁩”»
  • المُطلِق: الحالة 200، وContent-Type مقبول، لكن الجسم لا يبدأ بتوقيع %PDF.
  • الإصلاح: أعاد المنبع شيئًا ليس ملف تنسيق المستند المحمول (⁨PDF⁩) رغم الترويسات. عامل الاستجابة بوصفها غير موثوقة وافحص الخدمة. لا تكتب الجسم إلى القرص. يرفض الجسر إعادته بوصفه نتيجة.

”⁨Invalid SPKI pin format⁩: (⁨expected sha256/⁩)”

قسم بعنوان «”⁨Invalid SPKI pin format⁩: (⁨expected sha256/⁩)”»
  • النوع: InvalidSpkiPinException
  • المُطلِق: سلسلة تثبيت مهيأة لا تبدأ بـ sha256/ أو sha256//.
  • الإصلاح: نسق كل تثبيت بالشكل sha256/<base64-encoded-spki-hash>. يقبل النقل أيضًا الشكل الأصلي لـ ⁨cURL⁩ sha256//<base64>. ولد القيمة من ⁨SubjectPublicKeyInfo⁩ لشهادة الخادم، لا من الشهادة بأكملها.

”⁨It says unavailable but the service is up⁩”

قسم بعنوان «”⁨It says unavailable but the service is up⁩”»

isAvailable() تُرجع false دون أي استدعاء شبكي عندما يكون عنوان ⁨URL⁩ فارغًا، أو ليس ⁨HTTPS⁩، أو يُحل إلى عنوان ⁨private/reserved.⁩ كما تُرجع false عند أي خطأ شبكي، أو عندما يُرجع /health الحالة 500 أو أعلى؛ في تلك الحالات، تلتقط الخطأ بدلًا من إطلاقه. تحقق، بالترتيب:

  1. عنوان ⁨URL⁩ المهيأ غير فارغ وهو ⁨HTTPS.⁩
  2. المضيف لا يُحل إلى عنوان ⁨private/reserved⁩ (حارس ⁨SSRF⁩ يرفضه حتى لأغراض الفحص).
  3. <apiUrl>/health قابل للوصول من مضيف التطبيق ويُرجع حالة أقل من 500.
  • /⁨integrations/gotenberg/configuration/⁩ — كل الخيارات وقواعد اختيار النقل.
  • /⁨integrations/gotenberg/production-usage/⁩ — سياسة إعادة المحاولة وعقد معالجة الفشل.
  • /⁨integrations/gotenberg/security-and-operations/⁩ — نموذج ⁨SSRF⁩ وتدوير التثبيت.
  • /⁨integrations/gotenberg/quickstart/⁩ — ترتيب الالتقاط الشامل ضمن سياقه.