FlowingDev

تشريح رسائل HTTP: بناء حزم الويب الأولية

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

جرّب الأداة: منشئ رسائل HTTP

في جملة واحدة

رسالة HTTP هي كتلة نصية مُنسّقة يتبادلها متصفح الويب والخادم، وهي بمثابة مزيج من ملصق الشحن، ودليل التعليمات، ومحتويات الطرد لكل تفاعل يجري على الويب.

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

في الفوضى البدائية للويب في أوائل التسعينيات، كانت الأمور بسيطة. كان المتصفح يحتاج إلى طريقة ليسأل الخادم: "مرحباً، هل يمكنني الحصول على ملف science.html؟" وكان الخادم يحتاج إلى طريقة للرد: "بالتأكيد، تفضل"، أو "عذراً، لم أجده". هذه المحادثة كانت بحاجة إلى قواعد—بروتوكول. هذا البروتوكول أصبح HTTP، وهو اختصار لـ Hypertext Transfer Protocol (بروتوكول نقل النص التشعبي).

"المشكلة" التي حلها هي إنشاء لغة عالمية وواضحة للويب. بدون تنسيق قياسي، قد يتوقع أحد الخوادم الطلب في سطر واحد، بينما قد يحتاج خادم آخر إلى قصيدة هايكو. كانت الأمور ستتحول إلى فوضى عارمة. الإصدار الأولي HTTP/0.9 كان بسيطًا جدًا: GET /the-page-i-want.html. وكان الخادم يرسل كود الـ HTML مباشرة.

لكن الويب لم يبقَ بسيطًا. احتجنا إلى إرسال بيانات إلى الخادم لملء النماذج. احتجنا إلى التعامل مع أنواع محتوى مختلفة مثل الصور، ولاحقًا، JSON. احتجنا إلى الأمان، والتخزين المؤقت (caching)، وطريقة للمتصفحات لتصف نفسها. الطلب البسيط المكون من سطر واحد تطور إلى "رسالة" منظمة ومتعددة الأجزاء تحتوي على سطر بداية، وكتلة من البيانات الوصفية (headers)، ومحتوى اختياري للحمولة الفعلية (payload). أصبح صياغة هذه الرسائل يدويًا المهارة الأساسية لأي شخص يعمل مباشرة مع البنية التحتية للويب أو واجهات برمجة التطبيقات (APIs) أو الأمن، مما حل مشكلة كيفية إجراء أعمال متزايدة التعقيد عبر حوار الطلب-الاستجابة البسيط للويب.

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

في جوهرها، رسالة HTTP هي مجرد نص. يمكنك حرفيًا كتابتها في الطرفية (terminal) وإرسالها إلى خادم إذا كنت ترغب في ذلك. هذا النص مقسم إلى ثلاثة أجزاء: سطر بداية، وكتلة من الـ headers، ومحتوى اختياري، وكلها مفصولة بفواصل أسطر محددة (\r\n، أو CRLF اختصارًا لـ "Carriage Return, Line Feed").

هناك نوعان من الرسائل: الطلبات (من العميل إلى الخادم) والاستجابات (من الخادم إلى العميل). تبدو متطابقة تقريبًا ولكن لها سطر أول مختلف.

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

هذا هو متصفحك وهو يطلب شيئًا ما.

GET /documentation/guides/http-builder HTTP/1.1
Host: flowing.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/117.0
Accept: text/html,*/*
Accept-Language: en-US,en;q=0.5
Connection: keep-alive

<-- المحتوى يوضع هنا، لكن طلبات GET عادة لا تحتوي على محتوى -->
  1. سطر البداية (Start-Line): GET /documentation/guides/http-builder HTTP/1.1

    • GET: الـ method (أو الفعل) الخاص بـ HTTP. وهو ما تريد فعله. GET يجلب البيانات، POST يقدم بيانات جديدة، PUT يحدّث بيانات موجودة، DELETE يحذف البيانات.
    • /documentation/...: مسار المورد (resource path). عند دمجه مع Host header، فإنه يشكل الـ URL الكامل.
    • HTTP/1.1: إصدار البروتوكول.
  2. الـ Headers: قائمة من أزواج "مفتاح-قيمة" (key-value pairs) توفر بيانات وصفية (metadata) حيوية حول الطلب.

    • Host: flowing.dev: لمن هذا الطلب؟ هذا الـ header إلزامي في HTTP/1.1.
    • User-Agent: Mozilla/5.0...: من يرسل هذا الطلب؟ المتصفح يعرّف عن نفسه.
    • Accept: text/html,*/*: ما نوع تنسيق الاستجابة الذي يمكنني فهمه؟ هنا، يفضل المتصفح HTML ولكنه سيقبل أي شيء.
  3. السطر الفارغ: بعد آخر header، سطر فارغ واحد (\r\n) يشير إلى "انتهت الـ headers، والمحتوى هو التالي". هذا الجزء غير قابل للتفاوض. حذفه سيفسد كل شيء.

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

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

هذه هي استجابة الخادم للطلب.

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 15328
Server: Vercel
Date: Mon, 25 Sep 2023 10:30:00 GMT
Cache-Control: public, max-age=0, must-revalidate

<!DOCTYPE html>
<html>
  <head>...</head>
  <body>...</body>
</html>
  1. سطر الحالة (Status-Line): HTTP/1.1 200 OK

    • HTTP/1.1: إصدار البروتوكول، نفس إصدار الطلب.
    • 200: رمز الحالة (Status Code). رقم من ثلاثة أرقام يلخص النتيجة. 2xx يعني النجاح، 3xx يعني إعادة التوجيه، 4xx يعني أنك (العميل) أخطأت، و 5xx يعني أنني (الخادم) أخطأت.
    • OK: عبارة السبب (Reason Phrase). ملخص يمكن قراءته من قبل الإنسان لرمز الحالة.
  2. الـ Headers: بيانات وصفية حول الاستجابة.

    • Content-Type: text/html: "المحتوى الذي أرسله لك هو HTML." هذا أمر بالغ الأهمية ليعرف المتصفح كيفية عرض الحمولة.
    • Content-Length: 15328: "طول المحتوى هو 15,328 بايت بالضبط."
    • Set-Cookie: ...: كيف تطلب الخوادم من المتصفحات تخزين ملفات تعريف الارتباط (cookies).
    • Cache-Control: ...: تعليمات لكيفية قيام المتصفح أو الوكلاء (proxies) الوسيطين بتخزين هذه الاستجابة مؤقتًا.
  3. المحتوى (Body): المورد الذي طلبه العميل—HTML، CSS، كائن JSON، بيانات صورة، إلخ.

مهرجان المحتوى: تشفير الـ Payload

عندما يحتوي الطلب على محتوى، فإنه يحتاج إلى Content-Type header لشرح تنسيقه. الأنواع الثلاثة الأكثر شيوعًا هي:

  • application/x-www-form-urlencoded: التنسيق الافتراضي لنماذج HTML التقليدية. هو مجرد سلسلة استعلام (query string) في المحتوى.

    name=Grace+Hopper&title=Rear+Admiral
    
  • application/json: ملك واجهات برمجة التطبيقات (APIs) الحديثة. المحتوى هو نص JSON.

    {
      "name": "Grace Hopper",
      "title": "Rear Admiral"
    }
    
  • multipart/form-data: التنسيق المستخدم لإرسال النماذج التي تتضمن رفع ملفات. إنه مثل رسالة داخل رسالة. يتم تقسيم المحتوى إلى أجزاء، كل جزء مفصول بسلسلة نصية تسمى "boundary" (حد). يمكن أن يكون لكل جزء headers مصغرة خاصة به (مثل Content-Disposition و Content-Type) ومحتواه الخاص.

    POST /profiles/edit HTTP/1.1
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
    
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="username"
    
    ada_lovelace
    ----WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name="avatar"; filename="portrait.jpg"
    Content-Type: image/jpeg
    
    <...البيانات الثنائية الأولية للصورة توضع هنا...>
    ----WebKitFormBoundary7MA4YWxkTrZu0gW--
    

قصص من الواقع

قضية الـ Content-Type المفقود

كان مطور يبني أول REST API له. كان من المفترض أن تقبل نقطة النهاية (endpoint) حمولة JSON لإنشاء مستخدم جديد. كتب كود الخادم واختبره باستخدام أداة سطر أوامر، وأرسل كائن JSON صالحًا تمامًا. لكن الخادم استمر في الرد بـ 400 Bad Request. قضى ساعتين يحدق في ملف JSON الخاص به، مقتنعًا بأنه أغفل فاصلة. في يأس، طلب المساعدة من مطور أقدم. ألقى المطور الأقدم نظرة واحدة على الطلب وسأل: "أين الـ Content-Type header؟" كان المطور قد أرسل بيانات JSON، لكنه لم يخبر الخادم أبدًا أنها كانت JSON. حاول إطار عمل الخادم، الذي كان يتوقع التنسيق الافتراضي x-www-form-urlencoded، تحليل JSON كسلسلة استعلام، وفشل فشلاً ذريعاً، ورفض الطلب.

الدرس: محتوى الرسالة لا معنى له بدون Content-Type header لإعطائه سياقًا. يجب عليك تسمية طردك بشكل صحيح.

لخبطة الـ Multipart

كان فريق يعمل على صفحة "إعدادات" حيث يمكن للمستخدم تغيير اسمه ورفع صورة ملف شخصي جديدة اختياريًا. قام مطور الواجهة الأمامية المبتدئ بتنفيذ ذلك من خلال استدعاءين منفصلين للـ API: طلب PUT مع اسم المستخدم في محتوى JSON، ثم، إذا تم تحديد صورة، طلب POST مع بيانات الصورة. لقد نجح الأمر، لكنه كان معقدًا وأنشأ حالات تسابق (race conditions). ماذا لو نجح تغيير الاسم ولكن فشل تحميل الصورة؟ سيترك المستخدم في حالة غير متسقة. رأى مهندس خلفية (backend) حركة مرور الشبكة وأخذه جانبًا. وأوضح له قائلاً: "هذه حالة استخدام مثالية لـ multipart/form-data". أعادوا كتابة الكود لبناء طلب POST واحد من جزأين: جزء لحقل الاسم وجزء لملف الصورة. أدى ذلك إلى تبسيط الكود وجعل التحديث بأكمله عملية ذرية (atomic).

الدرس: الـ multipart ليس فقط للملفات. إنه لإرسال مزيج من البيانات—حقول نصية، ملفات، أنواع محتوى مختلفة—في طلب واحد موثوق.

الشبح في الـ Cache

كان موقع للتجارة الإلكترونية يجري تخفيضات سريعة، لكن المستخدمين اشتكوا من رؤية أسعار قديمة. كان فريق العمليات في حيرة من أمره؛ فقد تم تكوين التخزين المؤقت من جانب الخادم بشكل صحيح. تم استدعاء خبير في أداء الويب. بدلاً من استخدام أدوات المطور في المتصفح، استخدم أداة لفحص استجابة HTTP الأولية لصفحة منتج. وجد الجاني على الفور. كان موازن أحمال (load balancer) تم تكوينه بشكل خاطئ أمام خوادم الويب يقوم بحقن Cache-Control: public, max-age=3600 header خاص به، متجاوزًا بذلك الـ Cache-Control: no-cache الذي كان الخادم يهدف إلى إرساله. هذا الـ header الدخيل كان يخبر المتصفحات وشبكات توصيل المحتوى (CDNs) بتخزين الأسعار لمدة ساعة، بغض النظر عما يقوله خادم التطبيق.

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

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

  • نسيان السطر الفارغ. يجب أن تحتوي رسالة HTTP على CRLF (\r\n) بين الـ headers والمحتوى. إذا كان مفقودًا، ستعتقد المحللات (parsers) أن المحتوى الخاص بك هو مجرد header آخر سيئ التنسيق وسيفشل الطلب.
  • عدم تطابق Content-Length. إذا أعلنت عن Content-Length header، يجب أن تكون قيمته هي الحجم الدقيق بالبايت للمحتوى. إذا كان أصغر من اللازم، سيتم اقتطاع بياناتك. إذا كان أكبر من اللازم، سينتظر الخادم إلى الأبد بايتات لن تصل أبدًا.
  • Content-Type خاطئ. إرسال محتوى JSON ولكن تسميته كـ text/plain هو وصفة لخطأ 4xx. يجب أن يتفق الـ header والمحتوى.
  • CRLF مقابل LF. تتطلب المواصفات الرسمية \r\n لفواصل الأسطر. معظم الخوادم الحديثة متسامحة وستقبل \n (Line Feed) بسيطة. ومع ذلك، الاعتماد على هذا يمكن أن يتسبب في فشل طلبك مع الخوادم أو الوكلاء أو جدران الحماية الأقدم والأكثر صرامة.
  • ترميز الأحرف الخاصة. نسيان ترميز البيانات بنظام URL (URL-encoding) في سلسلة استعلام أو محتوى x-www-form-urlencoded هو خطأ برمجي كلاسيكي. يجب أن تصبح المسافة %20، و & يجب أن تصبح %26، وهكذا، وإلا فإنك تخاطر بإفساد بياناتك.

ليش لازم تهتم بهذا الموضوع

في معظم الأحيان، يتولى متصفحك أو إطار عملك أو مكتبتك (مثل axios أو requests) التفاصيل الفوضوية لبناء رسائل HTTP نيابة عنك. ولكن يجب أن تعرف كيف تفعل ذلك يدويًا عندما:

  • تكون في عمق جلسة تصحيح أخطاء (debugging). عندما لا يعمل استدعاء API ورسالة الخطأ غامضة، فإن فحص أو إعادة إنشاء رسالة HTTP الأولية هو الحكم الفصل. يتيح لك رؤية ما يتم إرساله عبر الشبكة بالضبط، بعيدًا عن أي طبقة تجريد.
  • تقوم ببناء أو اختبار API. فهم بنية الرسالة أمر أساسي لتصميم نقاط نهاية API جيدة وكتابة اختبارات تكامل فعالة. يقضي مختبرو الأمان أيامهم في صياغة رسائل مشوهة للعثور على نقاط الضعف.
  • تقوم بكشط موقع ويب (scraping). لتقليد متصفح حقيقي بنجاح وتجاوز تدابير مكافحة الروبوتات، تحتاج غالبًا إلى بناء طلب بمجموعة محددة جدًا من الـ headers (User-Agent، Referer، Accept-*، إلخ).
  • تعمل مع webhooks. عندما يتلقى تطبيقك webhook من خدمة مثل Stripe أو GitHub، فأنت في الطرف المتلقي لطلب HTTP أولي. ستحتاج إلى تحليل الـ headers (على سبيل المثال، للتحقق من التوقيعات الأمنية) والمحتوى لاتخاذ إجراء بشأن الحدث.

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

تعمق أكثر

  • An overview of HTTP on MDN - أفضل مكان للبدء للحصول على دليل عالي المستوى وسهل القراءة.
  • RFC 9112: HTTP/1.1 - المواصفات الفنية الأساسية لبناء جملة رسائل HTTP/1.1. إنها كثيفة ولكنها حاسمة.
  • HTTP headers on MDN - مرجع شامل وقابل للبحث لكل header قياسي في HTTP.
  • POST on MDN - دليل عملي يتضمن تفاصيل حول أنواع المحتوى Content-Type المختلفة لطلبات POST.
  • Wikipedia: Hypertext Transfer Protocol - نظرة عامة قوية على تاريخ وسياق HTTP.

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

جرّب الأداة: منشئ رسائل HTTP