Bamdad
Bamdad
خواندن ۹ دقیقه·۶ سال پیش

از g تا گوگل

یه روز عادی، مرورگر رو باز میکنیم تا چیزی رو سرچ کنیم. از اولین کلیدی که توی url باکس مرورگر میزنیم، تا دیدن صفحه گوگل روی مانیتورمون کلی اتفاق میوفته. آماده‌ای دقیق ببینیم چه اتفاقاتی؟؟


کلید g روی کیبورد زده میشه، مرورگر یه event دریافت میکنه. اینکه چطور از فشار دادن فیزیکی یه دکمه، یا تپ کردن روی صفحه نمایش به ایونت توی مرورگر میرسیم رو جلوتر میگم. مرورگر با دریافت این ایونت تابع auto-complete رو صدا میزنه. بسته به الگوریتم مرورگرمون یا اینکه صفحه private/incognito هست یا نه، زیر باکس url تعدادی لینک پیشنهاد شده میبینیم. بیشتر این الگوریتم ها بر مبنای تاریخچه مرورگرمون، بوکمارک ها، کوکی ها یا جملات زیاد سرچ شده این پیشنهاد ها رو ترتیب بندی میکنن. حالا توی باکس url داریم google.com

دکمه enter زده میشه. مدار الکتریکی مخصوص این دکمه بسته میشه، این یعنی جریان خیلی کوچکی برقرار میشه و به مدار منطقی کیبورد وضعیت دکمه‌ها رو میگه. حالا این پالس آماده‌ی ارسال به کامپیوتر از یکی از راه های کابل یا بلوتوث به پورت usb، یا کیبورد مجازی مثل کیبورد صفحه‌های لمسی.

دریافت و تحلیل این ورودی روی سیستم عامل ها با هم تفاوت داره.

برای مثال روی مک OS X ایونت KeyDown از جنس NSEvent ارسال میشه. سیگنال ورودی I/O kit رو صدا میزنه و سیگنال رو به درایور کیبورد میفرسته. درایور این سیگنال رو به یه key code ترجمه میکنه و به صف ایونت های اپلیکیشنی که فعاله میفرسته، و اپلیکیشن از صف ایونت ها رو به ترتیب اجرا میکنه.

چیزی که توی باکس url نوشتیم، سرچ باید بشه یا یه url کامله؟ یه url شامل پروتوکل (مثل http) یا دامنه معتبره. مرورگر بررسی میکنه که هاست نیم ورودی شامل غیر از a-z, A-Z, 0-9, - یا . میشه یا نه، تو مثال ما که google.com این مشکل پیش نمیاد، اما اگه شامل بود punucode encoding انجام میده.

مرورگر اول لیست HSTS رو چک میکنه، اگه دامنه ورودی توی لیست بود با پروتوکل https بهش درخواست میزنه. این لیست روی خود مرورگر هست و لیست سایت هایی که فقط با https میشه بهشون درخواست زد. البته اگه اولین درخواست با http زده بشه، پاسخ سرور حاوی درخواستی از مرورگر خواهد بود که درخواست ها رو روی https بزنه. البته همین یک درخواست روی http میتونه کاربر رو در برابر downgrade attack آسیب پذیر کنه، به همین دلیل مرورگر های مدرن لیست HSTS دارن.

چک کردن DNS. مرورگر اول کش مرورگر رو برای دامنه جست و جو میکنه. اگه پیدا نکرد، تابع getHostByName رو برای جست و جو صدا میکنه. این تابع اول بررسی میکنه که این url مرجع local روی کامپیوتر داره یا نه. اگه url مرجع local نداشت، توی کش هم پیدا نشد، مرورگر سرور DNS درخواست میزنه، که معمولاً روتر local یا سرور کش IPS هست. اگه DNS server روی همون زیر شبکه ای (subnet) بود که شبکه ما دنبال میکنه، فرایند ARP برای اون DNS اجرا میشه. اگه روی اون subnet نبود، ARP برای geteway ip پیش فرض اجرا میشه.

برای ارسال ARP شبکه ما برای جست و جو به ip مقصد و MAC address اینترفیسی که قراره ازش APR ارسال بشه نیاز داره. اول بررسی میشه که ip مقصد توی کش هست یا نه، اگه بود ip همون MAC.

اگه ip ورودی توی کش ARP نبود، روتر چک میشه که ببینیم ip توی شبکه داخلی هست یا نه. اگه بود از اینترفیسی که با اون subnet در ارتباطه استفاده میکنه. اگه نبود از اینترفیسی استفاده میکنه که subnet پیش فرض geteway رو استفاده میکنه. بعد MAC address شبکه بررسی میشه و در نهایت یه درخواست ARP ارسال میشه.

یه ARP request به این شکله:

Sender MAC: interface:mac:address:here Sender IP: interface.ip.goes.here Target MAC: FF:FF:FF:FF:FF:FF (Broadcast) Target IP: target.ip.goes.here

حالا اگه کامپیوتر مستقیم به روتر وصل باشه، روتر یک ARP reply میفرسته.

اگه کامپیوتر به هاب وصل باشه، هاب ARP request رو میده. اگه روتر به همون کابل وصل باشه، یه ARP reply میفرسته.

اگه کامپیوتر به سویچ وصل باشه، سویچ جدول CAM/MAC داخلیشو چک میکنه که کدوم پورت اون MAC address ای که میخوایمو داره. اگه سویچ MAC address رو پیدا نکنه به تمام پورت ها ARP request رو میزنه. اگه پیدا بکنه به همون پورت که MAC address رو داره ARP request میزنه. اگه روتر روی همون کابل باشه ARP reply میده.

یه ARP reply به این شکله:

Sender MAC: target:mac:address:here Sender IP: target.ip.goes.here Target MAC: interface:mac:address:here Target IP: interface.ip.goes.here

حالا که شبکه ما ip ی DNS server یا geteway پیش فرض رو داره میتونه به فرایند DNS ادامه بده.

پورت ۵۳ باز میشه تا یک درخواست UDP به DNS server ارسال بشه (اگه اندازه پاسخ خیلی بزرگ باشه TCP استفاده میشه). اگه DNS سرور های local و ISP نداشتنش ، یه جست و جوی بازگشتی درخواست میشه و لیست DNS سرور ها بررسی میشه تا به SOA برسیم.

باز کردن یک سوکت. زمانی که مرورگر ip سرور مقصد رو پیدا کرد، با ip و پورت url (برای http پورت پیش فرض ۸۰ و برای https ۴۴۳) تابع socket رو صدا میزنه و یک TCP socket stream درخواست میکنه (AF_INET/AF_INET6 و SOCK_STREAM).

این درخواست اول به لایه‌ی transport که TCP ساخته شده فرستاده میشه. پورت مقصد به هدر اصافه شده، و پورت منبع از بین بازه‌ی داینامیک کرنل انتخاب میشه. این فرستاده میشه به لایه‌ی شبکه که یک ip header اضافه میکنه، و ip سرور مقصد اضافه میشه. این مجموعه به لایه‌ی لینک فرستاده میشه. یک هدر بهش اضافه میشه که شامل MAC address ماشین و geteway (روتر لوکال). اگه کرنل MAC address رو نمیشناخت، . ARP درخواست میده تا پیداش کنه.

حالا این مجموعه آمادست تا از یکی از راه های اترنت، وای‌فای یا شبکه cellular منتقل بشه.

TLS handshake

کامپیوتر کلاینت یک ClientHello میفرسته به سرور با TLS، که لیست الگوریتم های رمزنگاری و متد های فشرده‌ سازی داره. سرور با ServerHello جواب میده، با ‌TLS و الگوریتم رمزنگاری و متد فشرده سازی مورد نظر و سرتیفیکیت عمومی تایید شده توسط CA. این سرتیفیکیت شامل یک کلید عمومیه که توسط کلاینت برای رمزگذاری بقیه‌ی handshake استفاده میشه تا زمانی که هردو طرف کلید یکسان داشته باشن.

کلاینت با بررسی سرتیفیکیت از لیست سرتیفیکیت های مورد اعتماد CA، اعتبار سرتیفیکیت رو میسنجه. اگه اعتماد برقرار بود، کلاینت یک رشته psedu-random bytes تولید میکنه و با اون کلید رمزگذاریش میکنه. این رشته برای تعیین کردن یکسان بودن کلید به کار میره. سرور این رشته رو با کلید خصوصیش رمزگشایی میکنه و از اون برای تولید کلید اصلیش استفاده میکنه. کلاینت یک پیام finished به سرور میفرسته، hash رمزگذاری شده از انتقال تا این نقطه توسط کلید متقارن. سرور hash خودشو تولید میکنه و hash های فرستاده شده از کلاینت رو رمزگشایی میکنه تا نشون بده یکسانند. اگه بودن، سرور هم پیام finished خودشو میفرسته و کلید متقارن رو رمزگذاری میکنه. حالا دیگه اپلیکیشن دیتا رو رمزگذاری شده با کلید متقارن انتقال میده.

پروتوکل HTTP

اگه مرورگر توسط گوگل نوشته شده باشه، بجای http از پروتوکل SPDY استفاده میکنه تا اطلاعات رو بگیره.

اگه کلاینت از http استفاده بکنه و SPDY رو ساپورت نکنه، درخواستشو به شکل زیر میفرسته:

GET / HTTP/1.1 Host: google.com Connection: close [other headers]

که [other headers] به key-value pair هایی اشاره میکنه که سرتیفیکیت http دارن. HTTP/1.1 نشون میده که بعد از پایان پاسخ سرور کانکشن بسته بشه. بعد از فرستادن درخواست و هدر به سرور یک خط خالی ارسال میشه به معنی اینکه درخواست تمومه. سرور هم با دادن کد ریسپانس جواب میده:

200 OK [response headers]

بعدش یک خط خالی و سپس محتوای html صفحه‌ی google.com. سرور ممکنه کانکشن رو ببنده یا ممکنه طبق درخواست کلاینت توی هدر کانکشن رو باز نگه داره برای درخواست های بعدی.

بعد از پارس شدن html مرورگر همین فرایند رو برای فایل های دیگه (مثل عکس ها، CSS، آیکن و...) تکرار میکنه، فقط بجای GET /HTTP/1.1 از طریق GET /$(URL) HTTP/1.1 درخواست رو میفرسته.

مسئولیت رسیدگی به درخواست‌ها و پاسخ‌ها رو HTTPD (یا همون http Deamon) بر عهده داره. معروف ترین اونها Apache و nginx برای لینوکس و IIS برای ویندوز هستند. سرور درخواست هارو به چند قسمت تقسیم میکنه، متد درخواست (که یکی از GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE)، دامنه (تو مثال ما google.com) و درخواست یک زیر صفحه. سرور تایید میکنه که یک میزبان مجازی برای پاسخگویی به google.com هست و سرور تایید میکنه که google.com یک GET request رو میپذیره.سرور تایید میکنه که کلاینت اجازه‌ی دسترسی به این صفحه رو داره (با ip یا انواع روش های احراز هویت)

پشت مرورگر دو بخش هست، یکی پارسر، که کارش پارس کردن فایل های HTML, CSS, JS. یکی هم Rendering، که کارش تولید درخت DOM و رندر کردن اون و جا گذاریش تو صفحه و رنگ آمیزیشه.

کار مرورگر اینه که منبعی رو که انتخاب کردیم رو با درخواست زدن به سرور روی صفحه نمایش بده. این منابع معمولا HTML هستن ولی گاهی عکس، pdf یا داده های دیگه‌ای هستن. این منابع با url نشون داده میشن. اکثر مرورگر ها، المان های یکسیانی دارن، مثلا همه باکس url برای ورودی گرفتن دارن یا دکمه‌ی صفحه‌ی قبل/بعد، بوکمارک، ریفرش و... دارن.

بطور کلی مرورگر ها چند بخش دارن. رابط کاربری (تمام چیزی که میبینیم)، browser engine‌ (رابط کاربری رو به موتور رندرینگ وصل میکنه)، موتور رندرینگ (ورودی فایل های htmlو css رو میگیره و روی صفحه نمایش میده)، نتورکینگ (که کارش درخواست های سرویس هاست مثل http request و...)، UI backend (که ویجت ها مثل باکس ها رو میکشه)، موتور جاوااسکریپت (که فایل های js رو اجرا میکنه) و ذخیره سازی اطلاعات.

موتور رندرینگ از بخش نتورکینگ محتوای html رو در تکه های ۸ کیلو‌بایتی میگیره و اجرا میکنه. پارسر html المان های فایل رو به درخت المان ها ارسال میکنه. این درخت شامل المان های DOM و attribute nodes. DOM مختصر شده‌ی Document Object Model که شیوه‌ی ارایه المان های html به بقیه مثل js هست.

الگوریتم پارس html به شیوه‌ی نرمال بالا به پایین یا پایین به بالا نیست. این الگوریتم شامل دو بخش tokenization و tree construction.

وقتی پارس html تموم شد، مرورگر شروع میکنه به گرفتن منابع خارجی مثل عکس، css، فایل های جاوااسکریپت و...

فایل های css با تگ های <style> پارس میشن. فایل ها به styleSheet object پارس میشن، که هر آبجکت شامل قوانین css. پارسر css میتونه از بالا به پایین یا پایین به بالا باشه.

برای رندر صفحه درخت frame یا درخت رندر، از روی درخت DOM ساخته میشه و برای هر نود محاسبات css انجام میشه. توی درخت frame از پایین به بالا با جمع کردن طول المان ها، طول کامپوننت ها محاسبه میشه. همینطور برای محاسبه‌ی ارتفاع کامپوننت ها از پایین به بالا با جمع کردن ارتفاع نود ها، به ارتفاع کامپوننت میرسیم. با نتایج این محاسبات به مختصات هر نود میرسیم.

حین فرایند رندر کردن صفحه لایه‌ی گرافیکی میتونه از GPU استفاده کنه. زمانی که از GPU برای رندر کردن صفحه استفاده میشه، graphical software layer کار رو به چندین قسمت تقسیم میکنن تا از توانایی پارالل GPU برای محاسبات float point ها استفاده بشه.

بعد از اتمام رندرینگ مرورگر ممکنه اسکریپت های ظاهری یا تعاملی رو اجرا کنه. همچنین پلاگین های فلش یا جاوا ممکنه اجرا بشن. خود این اسکریپت ها ممکنه network request هایی بزنن که باز هم همین فرایند رندر شدن صفحه طی میشه.

شاید از این پست‌ها خوشتان بیاید