پرتکل http2
Last updated
Last updated
فکر میکنم بحث درمورد پیشزمینه و اتفاقات گذشته بس باشد. برویم به سراغ استانداردهای پرتکل: چیزهایی که http2 را ساختند.
http2 یک پرتکل باینری است.
اگر شما با پرتکلهای اینترنتی قبلا کار کرده باشید، احتمالا نسبت به این مورد واکنش نشان میدهید و شروع به ارائهی دلایل مبنی بر این که چرا پرتکلهای متنی (text/ascii) بهترند چون انسانها میتوانند آنها را بخوانند، در telnet از آنها استفاده کنند و ...
http2 باینری است تا فریمبندی را راحتتر کند. تشخیص اول و آخر یک فریم در HTTP 1.1 و بقیهی پرتکلهای متنی، کار پیچیدهای بود. با دورشدن از فضاهای خالی اختیاری و راههای متفاوت برای پیادهسازی یک چیز، پیادهسازی حالا راحتتر خواهد شد.
همچنین، جداکردن فریمهای مربوط به قرارداد و پرتکل از فریمهای مربوط به دادههای پاسخ، راحتتر خواهد شد - قبلا این کار در HTTP1 بسیار گیجکننده بود.
در حقیقت، پرتکل قابلیت فشردهسازی را دارد و اجرا شدنش روی TLS، متن را مخفی میکند، بنابراین شخص ثالثی نمیتواند متن را از روی ترافیک جابجاشده بخواند. در واقع، باید عادت کنیم که از برنامههایی مثل Wireshark برای خواندن دادههای مبادلهشده در سطح پرتکل http2 استفاده کنیم.
دیباگکردن این پرتکل به ابزارهایی مثل curl یا برای آنالیز به Wireshark و مشابههای آن نیاز دارد.
http2 فریمها را به صورت باینری میفرستد. نوع فریمها ممکن است مختلف باشد، ولی همهی آنها یکنوع مشخصات دارند: اندازه (Length)، نوع (Type)، نشانها (Flags)، شناسهی استریم (Stream Identifier) و دادههای فریم.
در قرارداد http2، ده نوع فریم مختلف تعریف شده و اساسیترین آنها که به قابلیتهای HTTP 1.1 نیز مربوط هستند، DATA و HEADERS است. بعضی از این نوعها را در ادامه بررسی میکنیم.
شناسهی استریم که در فریم قبلی به آن اشاره شد، با یک «استریم» در هر فریم همراه است. یک استریم، مجموعهای از فریمهای داده به طور مستقل و دوطرفه است که کلاینت و سرور میتوانند در یک کانکشن http2 آنها را مبادله کنند.
یک کانکشن مستقل http2 میتواند دارای چندین استریمهای باز به طور همزمان باشد، و یا یکی از طرفین، استریمهای مختلف را با هم ترکیب کند تا چندین فریم را بسازد.
تسهیمسازی استریم به این معنی است که بستههای چندین استریم با هم ترکیب شده و در قالب یک کانکشن مبادله میشودن. دو (یا بیشتر) قطار از دادهها، تبدیل به یک قطار میشوند و در طرف دیگر، دوباره جدا میشوند. مثلا:
حالا اگر این دو قطار با هم ترکیب شوند یا اصطلاحا تسهیمسازی (Multiplex) شوند:
هر استریم دارای اولویت است (همچنین آن را به عنوان weight هم میشناسند)، که به طرف مقابل اطلاع میدهد که کدام استریم مهمتر است تا با توجه به محدودیت منابع، کدام را زودتر دریافت کند یا کدام را زودتر درخواست کند.
با استفاده از فریم PRIORITY، کلاینت میتواند به سرور اطلاع دهد که یک استریم، به چه استریمهای دیگری وابسته است. این قابلیت به کلاینت اجازه میدهد که یک درخت از وابستگیها ایجاد کند که هر «استریم فرزند» به «استریمهای پدر» وابسته است.
اولویتها و وابستگیها میتوانند در زمان اجرا به صورت پویا عوض شوند، که باعث میشود مرورگرها این توانایی را داشته باشند که هنگامی که کاربر در یک صفحهی پر از عکس، پیمایش (Scroll) میکند، مرورگر به سرور اطلاع دهد که کدام عکسها مهمتر هستند، یا اگر به یک Tab دیگر بروید، مرورگر به استریمهای دیگری اولویت بالاتر میدهد که مربوط به صفحهی جدید هستند.
HTTP یک پرتکل Stateless است، یعنی هر درخواست باید هدرهایی را همراه داشته باشد تا سرور بتواند به آن پاسخ دهد، بدون اینکه نیاز باشد سرور اطلاعات بسیاری را از درخواستهای قبلی ذخیره کند. از آنجایی که http2 این پارادایم را تغییر نمیدهد، همچنان به همین صورت کار میکند.
این روش باعث تکراریشدن درخواستهای HTTP میشود. وقتی یک کلاینت از همان سرور، چندین بار درخواست عکس برای یک صفحه میکند، چندین درخواست تقریبا یکسان را میفرستند. این نوع دادههایی که تقریبا یکسان هستند، مناسب فشردهسازی هستند.
همانطور که قبلا هم اشاره کردیم، تعداد فایلهای موردنیاز هر صفحه بالاتر رفته، همچنین اندازهی Cookies و درخواستها هم به مرور زمان در حال افزایش هستند. Cookies باید در هر درخواست فرستاده شوند، معمولا در چندین درخواست یکی هستند.
اندازهی درخواستهای HTTP 1.1 در واقع اینقدر بزرگ شدهاند که گاهی وقتها بیش از حجم مقبول TCP میشوند که باعث میشود ارسالشان بسیار کندتر شود، چون یکبار ناقص فرستاده میشوند تا از سرور ACK بگیرند و بعد از آن، به طور کامل ارسال میشود. این دلیل دیگری برای نیازداشتن به فشردهسازی است.
فشردهسازیهای HTTPS و SPDY دربرابر حملات BREACH و CRIME آسیبپذیر بودند. با قراردادن یک متن شناخته در استریم و یافتن اینکه چگونه تغییر میکند، حملهکننده میتوانست بفهمد که چیزی به صورت Encrypt شده ارسال میشده است.
فشردهسازی محتوای پویا در یک پرتکل - بدون اینکه به یکی از این حملات آسیبپذیر باشد - به فکر و تصمیمهای محتاطانه نیاز دارد. این همان چیزی است که کارگروه HTTPbis سعی کردند انجام دهند.
[HPACK] وارد میشود!(https://www.rfc-editor.org/rfc/rfc7541.txt). Header Compression for HTTP/2 یا فشردهسازی هدرها برای HTTP/2، یک فرمت فشردهسازی است که مخصوصا برای هدرهای http2 طراحی شده است و در یک پیشنویس اینترنتی جداگانه تبیین شده.
به گفتهی Roberto Peon (یکی از سازندگان HPACK): (مترجم: این نقلقول به طور تحتاللفظی ترجمه نشده).
HPACK احتمال ناامنبودن پیادهسازی و در نتیجه نشت اطلاعات را کاهش میدهد روند رمزنگاری/رمزگشایی را آسانتر و بهصرفهتر میکند، و به دریافتکننده این امکان را میدهد که بر Context Size فشردهسازی کنترل داشته باشد یا اجازهی ایجاد پراکسی برای Re-Indexing (مثل وضعیت مشترک بین فرانتاند و بکاند در یک پراکسی) را بدهد و همچنین امکان مقایسهی سریع رشتههایی که به روش هافمن رمزنگاری شدهاند را میدهد.
یکی دیگر از معایب HTTP 1.1، عدم وجود پشیمانی است! یعنی وقتی یک درخواست با Content-Length مشخص فرستاده شد، نمیتوانید به این راحتی متوقفش کنید. البته، شما میتوانید کانکشن TCP را قطع کنید، ولی باید دوباره یک ارتباط TCP جدید ایجاد کنید.
راهحل بهتر این است که درخواست قبلی را متوقف کنید و یک درخواست جدید بفرستید. این کار در http2 با ارسال فریم RST_STREAM ممکن است که از هدررفت پهنای باند و همچنین از بین رفتن کانکشنها جلوگیری میکند.
این ویژگی را با نام «cache push» نیز یاد میکنند. ایدهی اصلی این است که هنگامی که کلاینت درخواست فایل X را میکند، سرور میداند که احتمالا فایل Z را هم میخواهد و آن را به کلاینت، بدون اینکه درخواست کرده باشد، میفرستد. این ویژگی باعث میشود که کلاینت ، فایل Z را هم در Cache قرار دهد و هنگامی که نیاز است از آن استفاده کند.
Server push ویژگیای است که کلاینت باید به سرور اجازهی انجام آن را بدهد. در آن صورت، کلاینت میتواند یک استریم پوششده را در هر زمانی با RST_STREAM کنسل کند.
هر استریم http2، ظرفیت جریان را تعیین میکند تا سمت دیگر مطلع باشد که چه مقدار میتواند داده بفرستید. اگر با پرتکل SSH و چگونگی کار آن آشنا باشید، میدانید که این ویژگی بسیار شبیه به SSH است.
در هر استریم، هر دو طرف باید به طرف دیگر اطلاع دهند که فضای کافی برای دریافت دادهها را دارند و طرف دیگر تنها در صورتی اجازهی ارسال دارد که ظرفیت اجازه میدهد. تنها فریمهای DATA کنترل میشوند.