FlowingDev

HTTP Messages का पोस्टमॉर्टम: वेब के रॉ पैकेट्स बनाना

रॉ HTTP messages की एनाटॉमी सीखें, स्टार्ट-लाइन और headers से लेकर कॉम्प्लेक्स multipart बॉडीज़ तक, यह समझने के लिए कि वेब क्लाइंट और सर्वर कैसे कम्यूनिकेट करते हैं।

टूल आज़माएँ: HTTP मैसेज बिल्डर

एक लाइन में

एक HTTP message, प्लेन टेक्स्ट का एक फ़ॉर्मैटेड ब्लॉक होता है जिसे वेब ब्राउज़र और सर्वर एक दूसरे को भेजते हैं, और यह हर वेब इंटरैक्शन के लिए एक शिपिंग लेबल, एक इंस्ट्रक्शन मैनुअल, और पैकेज के अंदर के सामान का काम करता है।

यह कौनसी समस्या हल करता है

1990 के दशक की शुरुआत में जब वेब अपनी शुरुआती अवस्था में था, तो चीजें सिंपल थीं। एक ब्राउज़र को सर्वर से पूछने का एक तरीका चाहिए था, "अरे, क्या मुझे वह science.html फ़ाइल मिल सकती है?" और सर्वर को जवाब देने का एक तरीका चाहिए था, "ज़रूर, यह रही," या "सॉरी, नहीं मिली।" इस बातचीत के लिए नियमों की ज़रूरत थी—एक प्रोटोकॉल की। वही प्रोटोकॉल HTTP, यानी हाइपरटेक्स्ट ट्रांसफर प्रोटोकॉल बना।

इसने जो "समस्या" हल की, वह थी वेब के लिए एक यूनिवर्सल, स्पष्ट भाषा बनाना। एक स्टैंडर्ड फ़ॉर्मैट के बिना, हो सकता है कि एक सर्वर एक ही लाइन में रिक्वेस्ट की उम्मीद करे, जबकि दूसरा एक हाइकू चाहे। पूरी तरह से अराजकता (chaos) फैल जाती। शुरुआती HTTP/0.9 एकदम सिंपल था: GET /the-page-i-want.html। सर्वर बस HTML वापस भेज देता था।

लेकिन वेब सिंपल नहीं रहा। हमें फ़ॉर्म भरने के लिए सर्वर पर डेटा भेजने की ज़रूरत पड़ी। हमें इमेज और बाद में, JSON जैसे अलग-अलग कंटेंट टाइप को संभालने की ज़रूरत पड़ी। हमें सिक्योरिटी, कैशिंग, और ब्राउज़रों के लिए खुद को डिस्क्राइब करने का एक तरीका चाहिए था। सिंपल एक-लाइन वाली रिक्वेस्ट एक स्ट्रक्चर्ड, मल्टी-पार्ट "मैसेज" में विकसित हुई जिसमें एक स्टार्ट-लाइन, मेटाडेटा का एक ब्लॉक (हेडर्स), और असली पेलोड के लिए एक ऑप्शनल बॉडी होती है। इन मैसेजों को हाथ से बनाना उन सभी के लिए एक ज़रूरी स्किल बन गया जो सीधे वेब इंफ्रास्ट्रक्चर, APIs, या सिक्योरिटी के साथ काम कर रहे थे, जिससे वेब के सिंपल रिक्वेस्ट-रिस्पॉन्स डायलॉग पर लगातार कॉम्प्लेक्स होते जा रहे बिजनेस को करने की समस्या का समाधान हुआ।

अंदर की कहानी: यह कैसे काम करता है

मूल रूप से, एक HTTP message सिर्फ टेक्स्ट होता है। अगर आपका मन करे तो आप सचमुच एक टर्मिनल में इसे टाइप करके सर्वर पर भेज सकते हैं। यह टेक्स्ट तीन हिस्सों में बंटा होता है: एक स्टार्ट-लाइन, हेडर्स का एक ब्लॉक, और एक ऑप्शनल बॉडी, जो सभी स्पेसिफिक लाइन ब्रेक्स (\r\n, या CRLF यानी "कैरेज रिटर्न, लाइन फ़ीड") से अलग होते हैं।

दो तरह के मैसेज होते हैं: रिक्वेस्ट (क्लाइंट से सर्वर) और रिस्पॉन्स (सर्वर से क्लाइंट)। वे लगभग एक जैसे दिखते हैं लेकिन उनकी पहली लाइन अलग होती है।

एक 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

<-- The body would go here, but GET requests usually don't have one -->
  1. द स्टार्ट-लाइन (The Start-Line): GET /documentation/guides/http-builder HTTP/1.1

    • GET: यह HTTP method (या verb) है। यह बताता है कि आप क्या करना चाहते हैं। GET डेटा प्राप्त करता है, POST नया डेटा सबमिट करता है, PUT मौजूदा डेटा को अपडेट करता है, DELETE डेटा हटाता है।
    • /documentation/...: यह रिसोर्स पाथ (resource path) है। Host हेडर के साथ मिलकर, यह पूरा URL बनाता है।
    • HTTP/1.1: यह प्रोटोकॉल वर्ज़न है।
  2. द हेडर्स (The Headers): की-वैल्यू पेयर्स की एक लिस्ट जो रिक्वेस्ट के बारे में ज़रूरी मेटाडेटा देती है।

    • Host: flowing.dev: यह रिक्वेस्ट किसके लिए है? यह हेडर HTTP/1.1 में अनिवार्य है।
    • User-Agent: Mozilla/5.0...: यह रिक्वेस्ट कौन भेज रहा है? ब्राउज़र अपनी पहचान बताता है।
    • Accept: text/html,*/*: मैं किस तरह का रिस्पॉन्स फ़ॉर्मैट समझ सकता हूँ? यहाँ, ब्राउज़र HTML को प्राथमिकता देता है लेकिन कुछ भी स्वीकार कर लेगा।
  3. द ब्लैंक लाइन (The Blank Line): आखिरी हेडर के बाद, एक सिंगल खाली लाइन (\r\n) यह संकेत देती है कि "हेडर्स खत्म हो गए हैं, अब बॉडी आएगी।" यह ज़रूरी है। इसे छोड़ने से सब कुछ टूट जाएगा।

  4. द बॉडी (The Body): असली डेटा पेलोड। 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. द स्टेटस-लाइन (The Status-Line): HTTP/1.1 200 OK

    • HTTP/1.1: प्रोटोकॉल वर्ज़न, रिक्वेस्ट जैसा ही।
    • 200: यह स्टेटस कोड (Status Code) है। एक तीन-अंकीय संख्या जो रिजल्ट का सार बताती है। 2xx का मतलब सफलता, 3xx का मतलब रीडायरेक्शन, 4xx का मतलब आपने (क्लाइंट ने) गड़बड़ की, और 5xx का मतलब मैंने (सर्वर ने) गड़बड़ की।
    • OK: यह रीज़न फ्रेज़ (Reason Phrase) है। स्टेटस कोड का इंसानों के पढ़ने लायक सारांश।
  2. द हेडर्स (The Headers): रिस्पॉन्स के बारे में मेटाडेटा।

    • Content-Type: text/html: "जो बॉडी मैं आपको भेज रहा हूँ वह HTML है।" यह ब्राउज़र के लिए यह जानने के लिए महत्वपूर्ण है कि पेलोड को कैसे रेंडर करना है।
    • Content-Length: 15328: "बॉडी ठीक 15,328 बाइट्स लंबी है।"
    • Set-Cookie: ...: सर्वर ब्राउज़र को कुकीज़ स्टोर करने के लिए कैसे बताते हैं।
    • Cache-Control: ...: ब्राउज़र या बीच के प्रॉक्सी को इस रिस्पॉन्स को कैसे कैश करना चाहिए, इसके लिए निर्देश।
  3. द बॉडी (The Body): वह रिसोर्स जो क्लाइंट ने मांगा था—HTML, CSS, एक JSON ऑब्जेक्ट, इमेज डेटा, आदि।

बॉडी का खज़ाना: पेलोड को एनकोड करना

जब किसी रिक्वेस्ट में बॉडी होती है, तो उसे अपना फ़ॉर्मैट समझाने के लिए Content-Type हेडर की ज़रूरत होती है। तीन सबसे आम हैं:

  • application/x-www-form-urlencoded: पुराने ज़माने के HTML फ़ॉर्म्स के लिए डिफ़ॉल्ट। यह बस बॉडी में एक क्वेरी स्ट्रिंग होती है।

    name=Grace+Hopper&title=Rear+Admiral
    
  • application/json: मॉडर्न APIs का राजा। बॉडी एक JSON स्ट्रिंग होती है।

    {
      "name": "Grace Hopper",
      "title": "Rear Admiral"
    }
    
  • multipart/form-data: उन फ़ॉर्म को सबमिट करने का फ़ॉर्मैट जिनमें फ़ाइल अपलोड शामिल हैं। यह एक मैसेज के अंदर मैसेज जैसा है। बॉडी को हिस्सों में तोड़ा जाता है, हर हिस्सा एक "बाउंड्री" स्ट्रिंग से अलग होता है। हर हिस्से के अपने मिनी-हेडर्स (जैसे 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
    
    <...raw binary data of the image goes here...>
    ----WebKitFormBoundary7MA4YWxkTrZu0gW--
    

असल दुनिया की कहानियाँ

गुम हुए Content-Type का मामला

एक डेवलपर अपनी पहली REST API बना रहा था। एंडपॉइंट को एक नया यूज़र बनाने के लिए JSON पेलोड एक्सेप्ट करना था। उसने सर्वर कोड लिखा और उसे एक कमांड-लाइन टूल से टेस्ट किया, एक बिल्कुल सही JSON ऑब्जेक्ट भेजकर। लेकिन सर्वर बार-बार 400 Bad Request का जवाब देता रहा। उसने दो घंटे तक अपने JSON को घूरते हुए बिताए, यह सोचते हुए कि उसने कहीं कॉमा छोड़ दिया है। हताश होकर, उसने एक सीनियर डेवलपर से मदद मांगी। सीनियर डेवलपर ने रिक्वेस्ट पर एक नज़र डाली और पूछा, "तुम्हारा Content-Type हेडर कहाँ है?" डेवलपर ने JSON डेटा तो भेजा था, लेकिन उसने सर्वर को कभी बताया ही नहीं कि यह JSON है। सर्वर का फ्रेमवर्क, जो डिफ़ॉल्ट x-www-form-urlencoded की उम्मीद कर रहा था, उसने JSON को क्वेरी स्ट्रिंग के रूप में पार्स करने की कोशिश की, बुरी तरह विफल रहा, और रिक्वेस्ट को रिजेक्ट कर दिया।

सबक: Content-Type हेडर के बिना मैसेज की बॉडी का कोई मतलब नहीं होता, जो उसे संदर्भ देता है। आपको अपने पैकेज पर सही लेबल लगाना होगा।

Multipart की गड़बड़ी

एक टीम एक "सेटिंग्स" पेज बना रही थी जहाँ यूज़र अपना नाम बदल सकता था और वैकल्पिक रूप से एक नई प्रोफ़ाइल पिक्चर अपलोड कर सकता था। जूनियर फ्रंटएंड डेवलपर ने इसे दो अलग-अलग API कॉल्स के साथ लागू किया: एक PUT रिक्वेस्ट जिसमें JSON बॉडी में यूज़र का नाम था, और फिर, अगर कोई पिक्चर चुनी गई थी, तो इमेज डेटा के साथ एक POST रिक्वेस्ट। यह काम कर रहा था, लेकिन यह अजीब था और रेस कंडीशंस पैदा कर रहा था। क्या होता अगर नाम बदलने में सफलता मिलती लेकिन इमेज अपलोड विफल हो जाता? यूज़र एक असंगत स्थिति में रह जाता। एक बैकएंड इंजीनियर ने नेटवर्क ट्रैफ़िक देखा और उन्हें एक तरफ खींचा। "यह multipart/form-data के लिए एक परफेक्ट यूज़ केस है," उसने समझाया। उन्होंने कोड को रिफैक्टर करके एक सिंगल POST रिक्वेस्ट बनाई जिसमें दो पार्ट थे: एक नाम फ़ील्ड के लिए और एक इमेज फ़ाइल के लिए। इसने कोड को सरल बनाया और पूरे अपडेट को एक एटॉमिक ऑपरेशन बना दिया।

सबक: multipart सिर्फ़ फ़ाइलों के लिए नहीं है। यह एक ही, भरोसेमंद रिक्वेस्ट में मिली-जुली डेटा—टेक्स्ट फ़ील्ड्स, फ़ाइलें, अलग-अलग कंटेंट टाइप—भेजने के लिए है।

कैश (Cache) का भूत

एक ई-कॉमर्स साइट पर एक फ्लैश सेल चल रही थी, लेकिन यूज़र्स शिकायत कर रहे थे कि उन्हें पुरानी कीमतें दिख रही हैं। ऑप्स टीम हैरान थी; उनका सर्वर-साइड कैशिंग सही ढंग से कॉन्फ़िगर किया गया था। एक वेब परफॉरमेंस एक्सपर्ट को बुलाया गया। ब्राउज़र डेवलपर टूल्स का उपयोग करने के बजाय, उसने एक प्रोडक्ट पेज के रॉ HTTP रिस्पॉन्स का निरीक्षण करने के लिए एक टूल का उपयोग किया। उसे तुरंत अपराधी मिल गया। वेब सर्वर के सामने एक गलत कॉन्फ़िगर किया गया लोड बैलेंसर अपना खुद का Cache-Control: public, max-age=3600 हेडर इंजेक्ट कर रहा था, जो सर्वर के इच्छित Cache-Control: no-cache हेडर को ओवरराइड कर रहा था। यह अनचाहा हेडर ब्राउज़रों और CDNs को बता रहा था कि कीमतों को एक घंटे के लिए कैश करें, भले ही एप्लिकेशन सर्वर कुछ भी कहे।

सबक: रॉ HTTP मैसेज ही सच्चाई का अंतिम स्रोत है। हाई-लेवल टूल्स कभी-कभी उन डिटेल्स को छिपा सकते हैं या गलत व्याख्या कर सकते हैं जो टेक्स्ट में ही साफ़-साफ़ दिखाई देती हैं।

आम गलतियाँ और जाल

  • खाली लाइन (blank line) को भूल जाना। एक HTTP मैसेज में हेडर्स और बॉडी के बीच एक CRLF (\r\n) होना ही चाहिए। यदि यह गायब है, तो पार्सर सोचेंगे कि आपकी बॉडी सिर्फ एक और खराब हेडर है और रिक्वेस्ट विफल हो जाएगी।
  • Content-Length का मेल न खाना। यदि आप एक Content-Length हेडर घोषित करते हैं, तो उसका मान बॉडी के एकदम सही बाइट आकार का होना चाहिए। यदि यह बहुत छोटा है, तो आपका डेटा कट जाएगा। यदि यह बहुत बड़ा है, तो सर्वर उन बाइट्स के लिए हमेशा इंतजार करता रहेगा जो कभी नहीं आएंगी।
  • गलत Content-Type। JSON बॉडी भेजना लेकिन उसे text/plain के रूप में लेबल करना 4xx एरर का नुस्खा है। हेडर और बॉडी सहमत होने चाहिए।
  • CRLF बनाम LF। आधिकारिक स्पेसिफिकेशन लाइन ब्रेक के लिए \r\n की मांग करता है। अधिकांश आधुनिक सर्वर सहनशील होते हैं और एक सिंपल \n (लाइन फ़ीड) स्वीकार करेंगे। हालांकि, इस पर भरोसा करने से आपकी रिक्वेस्ट पुराने, सख्त सर्वर, प्रॉक्सी या फ़ायरवॉल के साथ विफल हो सकती है।
  • स्पेशल कैरेक्टर्स को एनकोड करना। क्वेरी स्ट्रिंग या x-www-form-urlencoded बॉडी में डेटा को URL-एनकोड करना भूल जाना एक क्लासिक बग है। एक स्पेस %20 बनना चाहिए, एक & %26 बनना चाहिए, और इसी तरह, वरना आप अपने डेटा को करप्ट करने का जोखिम उठाते हैं।

यह आपके रडार पर क्यों होना चाहिए

ज़्यादातर समय, आपका ब्राउज़र, फ्रेमवर्क, या लाइब्रेरी (जैसे axios या requests) आपके लिए HTTP मैसेज बनाने की मुश्किल डिटेल्स को संभालता है। लेकिन आपको पता होना चाहिए कि इसे मैन्युअल रूप से कब करना है जब:

  • आप किसी डीबगिंग सेशन में गहरे उतरे हुए हैं। जब कोई API कॉल काम नहीं कर रहा हो और एरर मैसेज अस्पष्ट हो, तो रॉ HTTP मैसेज का निरीक्षण करना या उसे फिर से बनाना अंतिम निर्णायक होता है। यह आपको यह देखने देता है कि नेटवर्क पर असल में क्या जा रहा है, किसी भी एब्स्ट्रेक्शन से मुक्त।
  • आप कोई API बना रहे हैं या टेस्ट कर रहे हैं। अच्छे API एंडपॉइंट डिज़ाइन करने और प्रभावी इंटीग्रेशन टेस्ट लिखने के लिए मैसेज स्ट्रक्चर को समझना मौलिक है। सिक्योरिटी टेस्टर्स अपना दिन खराब मैसेज बनाकर कमजोरियों को खोजने में बिताते हैं।
  • आप किसी वेबसाइट को स्क्रैप कर रहे हैं। एक असली ब्राउज़र की सफलतापूर्वक नकल करने और एंटी-बॉट उपायों को बायपास करने के लिए, आपको अक्सर हेडर्स (User-Agent, Referer, Accept-*, आदि) के एक बहुत ही स्पेसिफिक कॉम्बिनेशन के साथ एक रिक्वेस्ट बनानी पड़ती है।
  • आप वेबहुक के साथ काम कर रहे हैं। जब आपका एप्लिकेशन Stripe या GitHub जैसी सर्विस से वेबहुक प्राप्त करता है, तो आप एक रॉ HTTP रिक्वेस्ट के रिसीविंग एंड पर होते हैं। आपको इवेंट पर कार्रवाई करने के लिए इसके हेडर्स (जैसे, सिक्योरिटी सिग्नेचर के लिए) और इसकी बॉडी को पार्स करने की आवश्यकता होगी।

शुरू से एक HTTP मैसेज को असेंबल करना जानना वैसा ही है जैसे एक मैकेनिक जानता है कि एक इंटरनल कम्बशन इंजन कैसे काम करता है। आप इसे हर दिन नहीं करते हैं, लेकिन जब कुछ गलत हो जाता है, तो वह मौलिक ज्ञान अनमोल होता है।

और गहराई में जाएँ

  • An overview of HTTP on MDN - एक हाई-लेवल और आसानी से पढ़ी जा सकने वाली गाइड के लिए यह सबसे अच्छी शुरुआती जगह है।
  • RFC 9112: HTTP/1.1 - HTTP/1.1 मैसेज सिंटैक्स के लिए प्राथमिक तकनीकी स्पेसिफिकेशन। यह घना है लेकिन निर्णायक है।
  • HTTP headers on MDN - हर स्टैंडर्ड HTTP हेडर के लिए एक व्यापक और खोजने योग्य रेफरेंस।
  • POST on MDN - एक प्रैक्टिकल गाइड जिसमें POST रिक्वेस्ट के लिए विभिन्न Content-Type बॉडीज़ पर विवरण शामिल हैं।
  • Wikipedia: Hypertext Transfer Protocol - HTTP के इतिहास और संदर्भ का एक ठोस अवलोकन।

थ्योरी हो गई। अब हाथ आज़माइए — 100% आपके ब्राउज़र में।

टूल आज़माएँ: HTTP मैसेज बिल्डर