FlowingDev

HTTP: البطاقات البريدية التي تشغّل الويب

تعلم كيف يتم بناء طلبات واستجابات HTTP الخام، الرسائل النصية الأساسية للويب، باستخدام الترويسات (headers)، المحتوى (body)، وسطر الحالة.

جرّب الأداة: عارض رسائل HTTP

في جملة واحدة

رسائل HTTP هي ببساطة كتل نصية منسقة بشكل خاص يستخدمها العملاء (مثل متصفحك) والخوادم للتحدث مع بعضهم البعض، لطلب وإرسال صفحات الويب والبيانات وصور القطط عبر الإنترنت.

المشكلة التي يحلها

في العصر الحجري الرقمي (أواخر الثمانينات وأوائل التسعينات)، كان الإنترنت أشبه بالغرب المتوحش. كان لديك بروتوكولات مختلفة لمهام مختلفة: FTP للملفات، Gopher لقوائم المستندات، ومجموعة من الأنظمة المتخصصة الأخرى. لم تكن هذه الأنظمة تتحدث مع بعضها البعض. كان الأمر أشبه بحاجتك إلى ساعي بريد ومغلف مختلف لكل شخص تريد التواصل معه.

ثم جاء السير تيم بيرنرز لي برؤية "شبكة عنكبوتية عالمية" (World Wide Web) — نظام موحد من مستندات النص التشعبي المترابطة. ولكي ينجح هذا الأمر، كان بحاجة إلى لغة بسيطة وعالمية يمكن لأي كمبيوتر استخدامها لطلب مستند واستلامه. كان يجب أن تكون "عديمة الحالة" (stateless)، بمعنى أن كل طلب هو حدث قائم بذاته، لا يتطلب من الخادم تذكر المحادثات السابقة. والأهم من ذلك، كان يجب أن تكون قابلة للقراءة من قبل البشر، على الأقل من حيث المبدأ، لتسهيل عملية تصحيح الأخطاء (debugging).

وهنا يأتي دور بروتوكول نقل النص التشعبي، أو HTTP. لقد حل المشكلة عن طريق تحديد تنسيق رسائل قياسي، "بطاقة بريدية" عالمية للويب. هذه البطاقة البريدية تحتوي على أماكن مخصصة لعنوان المستلم (الخادم والمسار)، معلومات المرسل، ملاحظة سريعة حول ما بداخلها (الترويسات أو headers)، والمحتوى الفعلي (body). هذا الهيكل الموحد يعني أن أي عميل يمكنه التحدث إلى أي خادم، مما أدى إلى إنشاء الويب القابل للتشغيل البيني الذي نعرفه ونحبه اليوم.

كيف يعمل من الداخل

في جوهره، رسالة HTTP هي مجرد سيل من النصوص. لكنها ليست مجرد نصوص عادية؛ فلها بنية صارمة. لا يمكنك فقط أن تخربش "أعطني الصفحة الرئيسية!" على منديل رقمي وترميه على الخادم. تنقسم الرسالة إلى نكهتين رئيسيتين: الطلب (ما تطلبه) والاستجابة (ما تحصل عليه).

تشريح رسالة الطلب (Request)

هذا ما يرسله متصفحك عندما تكتب عنوان URL أو تنقر على رابط. يتكون من ما يصل إلى ثلاثة أجزاء، مفصولة بأسطر جديدة محددة (CRLF، أو \r\n في الكود).

  1. سطر البداية (Start-Line): سطر واحد يحدد ماذا تريد، أين هو، وبأي إصدار من اللغة تتحدث. METHOD /path/to/resource HTTP/Version

    GET /documentation/guides/http HTTP/1.1
    
    • GET هي الطريقة (Method). إنها فعل الطلب.
    • /documentation/guides/http هو مسار المورد (Resource Path).
    • HTTP/1.1 هو إصدار البروتوكول (Protocol Version).
    الطريقة الشائعة (Method) ماذا تعني هل لها محتوى (Body)؟
    GET "من فضلك أعطني هذا المورد." لا
    POST "هذه بعض البيانات؛ قم بإنشاء شيء ما." نعم
    PUT "هذه بعض البيانات؛ قم بتحديث/استبدال." نعم
    DELETE "من فضلك احذف هذا المورد." لا
    HEAD "أعطني الترويسات فقط، بدون المحتوى." لا
  2. الترويسات (Headers): سلسلة من أزواج Key: Value التي توفر بيانات وصفية (metadata) حول الطلب. فكر فيها على أنها مربعات الاختيار والملاحظات الموجودة على ظهر البطاقة البريدية.

    Host: flowing.dev
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-US,en;q=0.5
    
    • Host: الأهم على الإطلاق. يخبر الخادم بأي موقع ويب تحاول الوصول إليه، وهو أمر ضروري للخوادم التي تستضيف مواقع متعددة على عنوان IP واحد.
    • User-Agent: "هذا هو المتصفح/الأداة التي أستخدمها."
    • Accept: "أفضل تلقي المحتوى بهذه التنسيقات."
  3. السطر الفارغ فائق الأهمية: بعد آخر ترويسة، يوجد سطر واحد فارغ تمامًا (CRLF). هذا هو الفاصل الذي لا يمكن التفاوض عليه. إنه يشير إلى "نهاية الترويسات، المحتوى (إن وجد) يبدأ تالياً".

  4. المحتوى (Body) (اختياري): الحمولة (payload). بالنسبة لطلبات GET أو HEAD، يكون فارغًا. أما بالنسبة لـ POST أو PUT، فهذا هو المكان الذي توجد فيه البيانات التي ترسلها — حمولة JSON لاستدعاء API، محتويات نموذج تم إرساله، إلخ.

    {
      "username": "dev-guru",
      "email": "guru@example.com"
    }
    

تشريح رسالة الاستجابة (Response)

هذا ما يرسله الخادم ردًا على طلبك. يعكس هيكل الطلب ولكنه يؤدي وظيفة مختلفة.

  1. سطر الحالة (Status-Line): سطر واحد يخبرك بما إذا كان الطلب قد نجح ولماذا. HTTP/Version StatusCode StatusText

    HTTP/1.1 200 OK
    
    • StatusCode (رمز الحالة) هو الجزء الأكثر أهمية. وهو رقم مكون من ثلاثة أرقام يلخص النتيجة.
    عائلة الرمز المعنى مثال
    2xx نجاح! كل شيء سار على ما يرام. 200 OK
    3xx إعادة توجيه. عليك البحث في مكان آخر. 301 Moved Permanently
    4xx خطأ من العميل. أنت من أخطأت. 404 Not Found
    5xx خطأ من الخادم. أنا من أخطأت. 500 Internal Server Error
  2. الترويسات (Headers): أزواج Key: Value تصف الاستجابة.

    Date: Fri, 24 May 2024 12:00:00 GMT
    Content-Type: text/html; charset=utf-8
    Content-Length: 4096
    Cache-Control: max-age=600
    
    • Content-Type: "هذا ما أرسله لك. في هذه الحالة، هو مستند HTML بترميز UTF-8."
    • Content-Length: "محتوى استجابتي يبلغ طوله 4096 بايت بالضبط."
    • Cache-Control: "يمكنك (أو أي بروكسي بيننا) تخزين نسخة من هذا لمدة 600 ثانية."
  3. السطر الفارغ: نعم، إنه هنا أيضًا. يفصل الترويسات عن المحتوى.

  4. المحتوى (Body): الشيء الفعلي الذي طلبته! كود HTML لصفحة الويب، بيانات JSON من الـ API، ملف الصورة، إلخ. هذا هو "المحتوى" في Content-Type.

قصص من الواقع

قضية خطأ 401 الغامض

كانت مطورة تقوم بالتكامل مع API لطرف ثالث. كانت متأكدة من أنها ترسل مفتاح API الصحيح، لكن كل طلب كان يعود بخطأ 401 Unauthorized. كودها بدا مثاليًا: api.setAuth('my-secret-key'). محبطة، قامت بالتقاط طلب HTTP الخام الذي يرسله إطار عملها (framework).

كشفت الرسالة الخام الحقيقة:

POST /v1/widgets HTTP/1.1
Host: api.thirdparty.com
Content-Type: application/json
Api-Key: my-secret-key

{ "name": "New Widget" }

راجعت توثيق الـ API مرة أخرى. كان من المفترض أن يكون header المصادقة هو Authorization، وليس Api-Key، وكان يجب أن تبدأ القيمة بـ Bearer . كان التجريد (abstraction) الذي يوفره إطار عملها بسيطًا جدًا ويستخدم اسم header خاطئ. تجاوزت الدالة المساعدة، وقامت بتعيين الـ header يدويًا، ونجح الطلب التالي مع استجابة 201 Created.

الدرس: أطر العمل والمكتبات هي تجريدات مفيدة، لكن رسالة HTTP الخام هي الحقيقة المطلقة. عندما تشعر أن هناك شيئًا خاطئًا، افحص الرسالة الخام لترى ما يتم إرساله فعليًا عبر الشبكة.

معضلة التخزين المؤقت (Caching)

أطلق فريق تسويق صفحة هبوط جديدة، لكن نصف موظفي الشركة كانوا لا يزالون يرون الصفحة القديمة "قريبًا"، حتى بعد الضغط بجنون على Ctrl+F5. أصر المطور على أن المشكلة ليست في كود الخادم. اشتبه في وجود مشكلة في التخزين المؤقت (caching)، فاستخدم أداة لفحص ترويسات استجابة HTTP الخام للصفحة.

بدت الاستجابة من الخادم هكذا:

HTTP/1.1 200 OK
Content-Type: text/html
...
Cache-Control: public, max-age=86400
Age: 34500

كانت ترويسة Cache-Control تخبر كل متصفح وخادم بروكسي في السلسلة بالاحتفاظ بهذه الصفحة لمدة 86,400 ثانية (يوم كامل!). وأظهرت ترويسة Age أن الإصدار الذي يتم تقديمه كان عمره بالفعل أكثر من 9 ساعات. كان هناك إعداد خاطئ على شبكة توصيل المحتوى (CDN) الخاصة بهم يطبق سياسة تخزين مؤقت صارمة على جميع صفحات HTML. بمجرد أن أصلحوا قاعدة الـ CDN، ظهرت الصفحة الجديدة للجميع على الفور.

الدرس: ترويسات الاستجابة ليست مجرد بيانات وصفية؛ إنها تعليمات تتحكم في المتصفحات والبروكسيات وشبكات CDN. فهم Cache-Control وExpires وETag أمر بالغ الأهمية لإدارة كيفية توصيل المحتوى الخاص بك.

سارق المحتوى الصامت

قام مطور مبتدئ ببناء نقطة نهاية API بسيطة لقبول ملاحظات المستخدمين. كانت تعمل بشكل مثالي على جهازه المحلي. ولكن في بيئة الاختبار (staging)، كانت عمليات إرسال النماذج تفشل. أظهرت سجلات الخادم أن طلبات POST /feedback كانت تصل، لكن محتوى الطلب (body) كان دائمًا فارغًا. كانت بيانات المستخدم تختفي في الهواء.

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

POST /feedback HTTP/1.1
Host: staging.myapp.com
Content-Type: application/json
Content-Length: 0

{}

لكنه كان يعلم أن الكود من جانب العميل يرسل كائن JSON كامل! كان Content-Length يساوي 0، والمحتوى فارغًا. بالعودة إلى الوراء، اكتشف قاعدة أمنية في جدار الحماية لتطبيقات الويب (WAF) في بيئة الاختبار تم تكوينها عن طريق الخطأ لتجريد المحتوى من أي طلب POST إلى مسار غير معروف. نظرًا لأن /feedback كانت نقطة نهاية جديدة، كان الـ WAF "يحمي" الخادم عن طريق التهام البيانات بصمت.

الدرس: ترويستا Content-Length وContent-Type هما عقد بين العميل والخادم. إذا لم يصفا المحتوى بدقة، ستتعطل الأمور بطرق محيرة. تحقق منهما دائمًا عند تصحيح أخطاء نقل البيانات.

أخطاء وفخاخ شائعة

  • نسيان السطر الفارغ. هذا السطر الفارغ (CRLFCRLF) بين الترويسات والمحتوى ليس مساحة بيضاء اختيارية. إنه الفاصل الأساسي. بدونه، تكون الرسالة بأكملها مشوهة، ولن يعرف الخادم أين تنتهي الترويسات وأين تبدأ الحمولة.
  • عدم تطابق Content-Length. إذا كانت ترويستك تقول Content-Length: 100 ولكنك أرسلت فقط محتوى بحجم 50 بايت، فسوف يعلق الخادم، في انتظار الـ 50 بايت الأخرى حتى انتهاء المهلة (timeout). إذا أرسلت 150 بايت، فقد يتم تفسير الـ 50 بايت الإضافية على أنها بداية طلب جديد مشوه.
  • نهايات الأسطر CRLF مقابل LF. مواصفات HTTP صارمة: يجب أن تنتهي الأسطر بـ Carriage Return متبوعًا بـ Line Feed (\r\n). في حين أن العديد من الخوادم الحديثة متسامحة وتقبل Line Feed بسيط (\n)، فإن بعض الخوادم القديمة أو الأكثر صرامة سترفض الرسالة أو تحللها بشكل غير صحيح.
  • تجاهل Content-Type. قد ترسل كائن JSON صالحًا تمامًا في محتوى POST الخاص بك، ولكن إذا لم تقم بتضمين ترويسة Content-Type: application/json، فقد يفترض الخادم أنه application/x-www-form-urlencoded (الافتراضي للنماذج) ويفشل في تحليله.
  • الخلط في حالة الأحرف للترويسات. أسماء الترويسات غير حساسة لحالة الأحرف (Content-Type هي نفسها content-type). ومع ذلك، قيم الترويسات يمكن أن تكون، وغالبًا ما تكون، حساسة لحالة الأحرف. مفتاح API أو قيمة بترميز Base64 هي مثال ممتاز على ذلك.

لماذا يجب أن يكون على رادارك

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

يجب أن تفكر في رسالة HTTP الخام كلما كنت:

  • تصحح أي خطأ متعلق بالشبكة (رموز 4xx أو 5xx).
  • تحاول تحسين أداء الويب (التخزين المؤقت، الضغط).
  • تبني أو تستهلك API.
  • تقوم بإعداد عمليات إعادة التوجيه، أو البروكسيات، أو موازنات التحميل (load balancers).
  • تحقق في ثغرات أمن الويب (مثل حقن الترويسات - header injection).

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

تعمق أكثر

  • MDN: An overview of HTTP - أفضل نقطة انطلاق، تجمع بين الوضوح والدقة التقنية.
  • RFC 9110: HTTP Semantics - المواصفات الحديثة لمفاهيم HTTP الأساسية مثل الطرق ورموز الحالة والترويسات.
  • RFC 9112: HTTP/1.1 - المواصفات التي تحدد بناء جملة الرسائل النصية التي نوقشت هنا.
  • Wikipedia: Hypertext Transfer Protocol - ملخص جيد عالي المستوى لتاريخ ومكونات HTTP.
  • HTTP/2 Explained - كتاب مجاني على الإنترنت من تأليف دانيال ستينبرغ (مبتكر cURL) يشرح كيف تم تكييف المفاهيم الأساسية لرسائل HTTP لبروتوكول HTTP/2 الثنائي الحديث.

انتهينا من النظرية. حان وقت التطبيق — 100% في متصفحك.

جرّب الأداة: عارض رسائل HTTP