Что на самом деле происходит, когда пользователь вбивает в браузер адрес google.com

Эта статья является попыткой ответа на старый вопрос для собеседований: «Что же случается, когда вы печатаете в адресной строке google.com и нажимаете Enter?» Мы попробуем разобраться в этом максимально подробно, не пропуская ни одной детали.

1. Нажата клавиша «g»

Далее в статье содержится информация о работе физической клавиатуры и прерывания операционной системы. Но много чего происходит и помимо этого — когда вы нажимаете клавишу «g», браузер получает событие и запускается механизм автоподстановки. В зависимости от алгоритма браузера и его режима (включена ли функция «инкогнито») в выпадающем окне под строкой URL пользователю будет предложено определённое количество вариантов для автоподстановки.

Большинство алгоритмов автоподстановки ранжируют рекомендации в зависимости от истории поиска и оставленных закладках. Некоторые браузеры (например, Rockmelt) даже предлагают профили друзей на Facebook. Когда пользователь планирует напечатать в адресной строке «google.com», ничего из вышеперечисленного не играет роли, но тем не менее выполнится большое количество кода, а рекомендации будут обновляться с каждой новой напечатанной буквой. Возможно, браузер предложит перейти на google.com, до того, как пользователь вобьёт адрес целиком.

2. Клавиша «enter» нажата до конца

В качестве некой нулевой точки можно выбрать момент, когда клавиша Enter на клавиатуре нажата до конца и находится в нижнем положении. В этой точке замыкается электрическая цепь этой клавиши и небольшое количество тока отправляется по электросхеме клавиатуры, которая сканирует состояние каждого переключателя клавиши и конвертирует сигнал в целочисленный код клавиши (в данном случае — 13). Затем контроллер клавиатуры конвертирует код клавиши для передачи его компьютеру. Как правило, сейчас передача происходит через USB или Bluetooth, а раньше клавиатура подключалась к компьютеру с помощью коннекторов PS/2 или ADB.

В случае USB-клавиатуры:

В случае виртуальной клавиатуры (тачскрин):

2.1 Возникло прерывание [не для USB-клавиатур]

Клавиатура отправляет сигналы в свою «линию запросов прерываний» (IRQ), которая затем сопоставляется с «вектором прерывания» (целое число) контроллером прерываний. Процессор использует «таблицу дескрипторов прерываний» (IDT) для сопоставления векторов прерываний с функциями («обработчики прерываний») ядра. Когда появляется прерывание, процессор (CPU) обновляет IDT вектором прерывания и запускает соответствующий обработчик. Таким образом, в дело вступает ядро.

2.2 (На Windows) Сообщение WM_KEYDOWN отправлено приложению

HID передаёт событие нажатой клавиши драйверу KBDHID.sys, который конвертирует его в скан-код (scancode). В данном конкретном случае скан-код — VK_RETURN (0x0D). Драйвер KDBHID.sys связывается с драйвером KBDCLASS.sys (драйвер классов клавиатуры). Он отвечает за безопасную обработку всего ввода с клавиатуры. В дальнейшем этот драйвер вызывает Win32K.sys (после возможной передачи сообщения через установленные сторонние клавиатурные фильтры). Все это происходит в режиме ядра.

Win32K.sys определяет, какое окно активно в данный момент, с помощью функции GetForegroundWindow(). Этот API обеспечивает обработку окна адресной строки в браузере. Затем главный «насос сообщений» Windows вызывает SendMessage(hWnd, WM_KEYDOWN, VK_RETURN, lParam)lParam — это битовая маска, которая указывает на дальнейшую информацию о нажатии клавиши: счётчик повторов (в этом случае 0), актуальный скан-код (может зависеть от OEM, но VK_RETURN обычно не зависит от этого), информацию о том, были ли нажаты дополнительные клавиши (например, Alt, Shift, Ctrl — в нашем случае не были) и некоторые другие данные.

В API Windows есть функция SendMessage, которая помещает сообщение в очередь для конкретного обработчика окон (hWnd). После этого для обработки всех сообщений очереди вызывается главная функция обработки сообщений (WindowProc), присвоенная обработчику hWnd.

Окно (hWnd), активное в данный момент, представляет из себя контрол обработки и в этом случае у WindowsProc есть обработчик для сообщений WM_KEYDOWN. Этот код изучает третий параметр, который поступил в SendMessage (wParam) и, поскольку это VK_RETURN, понимает, что пользователь нажал клавишу ENTER.

2.3 (В OS X) Событие NSEVent KeyDown отправлено приложению

Сигнал прерывания активирует событие прерывания в драйвере I/O Kit клавиатуры. Драйвер переводит сигнал в код клавиатуры, который затем передаётся процессу OS X под названием WindowServer. В результате, WindowsServer передаёт событие любому подходящему (активному или «слушающему») приложению через Mach-порт, в котором событие помещается в очередь. Затем события могут быть прочитаны из этой очереди потоками с достаточными привилегиями, чтобы вызывать функцию mach_ipc_dispatch. Чаще всего это происходит и обрабатывается с помощью основного цикла NSApplication через NSEvent в NSEventype KeyDown.

2.4 (В GNU/Linux) Сервер Xorg слушает клавиатурные коды

В случае графического X server, для получения нажатия клавиши будет использован общий драйвер событий evdev. Переназначение клавиатурных кодов скан-кодам осуществляется с помощью специальных правил и карт X Server. Когда маппинг скан-кода нажатой клавиши завершён, X server посылает символ в window manager (DWM, metacity, i3), который затем отправляет его в активное окно. Графический API окна, получившего символ, печатает соответствующий символ шрифта в нужном поле.

3. Парсинг URL

Теперь у браузера есть следующая информация об URL:

Protocol «HTTP»
Использовать «Hyper Text Transfer Protocol»

Resource «/»
Показать главную (индексную) страницу

3.1 Это URL или поисковый запрос?

Когда пользователь не вводит протокол или доменное имя, то браузер «скармливает» то, что человек напечатал, поисковой машине, установленной по умолчанию. Часто к URL добавляется специальный текст, который позволяет поисковой машине понять, что информация передана из URL-строки определённого браузера.

3.2 Список проверки HSTS

Механизм:

Сервер сообщает о политиках HSTS с помощью специального заголовка при подключении через шифрованный HTTPS (заголовок HSTS при подключении по нешифрованному HTTP игнорируется). Например, (Поле max-age указывает срок действия в секундах, величина 31536000 приблизительно соответствует одному году): Strict-Transport-Security: max-age=31536000; includeSubDomains; preload.

Когда сайт применяет политику HSTS, пользовательские браузеры, корректно воспринимающие заголовок HSTS, должны:

  1. Автоматически автономно преобразовывать все http-ссылки на данный сайт в https-ссылки. (Например, вместо http://ru.wikipedia.org/wiki/HSTS браузер будет использовать https://ru.wikipedia.org/wiki/HSTS, преобразование произойдет до реального обращения к серверу.)
  2. Если безопасность соединения https не может быть проверена (в частности, если TLS-сертификат сервера не подписан доверенным ключом), будет показано сообщение об ошибке, и пользователь будет лишен доступа к сайту.

Действующие политики HSTS помогают защитить пользователей сайта от части пассивных и активных атак. Атаки класса MiTM значительно усложняются.

3.3 Конвертация не-ASCII Unicode символов в название хоста

4. Определение DNS

4.1 Процесс отправки ARP-запроса

Для того, чтобы отправить широковещательный ARP-запрос необходимо отыскать целевой IP-адрес, а также знать MAC-адрес интерфейса, который будет использоваться для отправки ARP-запроса.

Кэш ARP проверяется для каждого целевого IP-адреса — если адрес есть в кэше, то библиотечная функция возвращает результат: Target IP = MAC.

Если же записи в кэше нет:

ARP-запрос:

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-ответ:

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-сервера либо шлюза по умолчанию, который можно использовать для разрешения доменного имени:

5. Открытие сокета

Когда браузер получает IP-адрес конечного сервера, то он берёт эту информацию и данные об используемом порте из URL (80 порт для HTTP, 443 для HTTPS) и осуществляет вызов функции socket системной библиотеки и запрашивает поток TCP сокета — AF_INET и SOCK_STREAM.

На этой точке пакет готов к передаче через:

В случае интернет-соединения большинства частных пользователей или небольших компаний пакет будет отправлен с компьютера, через локальную сеть, а затем через модем (MOdulator/DEModulator), который транслирует цифровые единицы и нули в аналоговый сигнал, подходящий для передачи по телефонной линии, кабелю или беспроводным телефонным соединениям. На другой стороне соединения расположен другой модем, который конвертирует аналоговый сигнал в цифровые данные и передаёт их следующему сетевому узлу, где происходит дальнейший анализ данных об отправителе и получателе.

В конечном итоге пакет доберётся до маршрутизатора, управляющего локальной подсетью. Затем он продолжит путешествовать от одного роутера к другому, пока не доберётся до сервера назначения. Каждый маршрутизатор на пути будет извлекать адрес назначения из IP-заголовка и отправлять пакет на следующий хоп. Значение поля TTL (time to live) в IP-заголовке будет каждый раз уменьшаться после прохождения каждого роутера. Если значение поля TTL достигнет нуля, пакет будет отброшен (это произойдёт также если у маршрутизатора не будет места в текущей очереди — например, из-за перегрузки сети).

Во время TCP-соединения происходит множество подобных запросов и ответов.

5.1 Жизненный цикл TCP-соединения

a. Клиент выбирает номер начальной последовательности (ISN) и отправляет пакет серверу с установленным битом SYN для открытия соединения.

b. Сервер получает пакет с битом SYN и, если готов к установлению соединения, то:

c. Клиент подтверждает соединение путём отправки пакета:

d. Данные передаются следующим образом:

e. Закрытие соединения:

6. TLS handshake

7. Протокол HTTP

Если используемый браузер был создан Google, то вместо отправки HTTP-запроса для получения страницы, он отправит запрос, чтобы попытаться «договориться» с сервером об «апгрейде» протокола с HTTP до SPDY («спиди»).

Если клиент использует HTTP-протокол и не поддерживает SPDY, то отправляет серверу запрос следующей формы:

GET / HTTP/1.1
Host: google.com
Connection: close
[другие заголовки]

где [другие заголовки] — это серия пар «ключ: значение», разбитых переносом строки. (Здесь предполагается, что в использованном браузере нет никаких ошибок, нарушающих спецификацию HTTP. Также предполагается, что браузер использует HTTP/1.1, в противном случае он может не включать заголовок Host в запрос и версия, отданная в ответ на GET-запрос может быть HTTP/1.0 или HTTP/0.9).

HTTP/1.1 определяет опцию закрытия соединения («close») для отправителя — с её помощью происходит уведомление о закрытии соединения после завершения ответа. К примеру:

Connection: close

Приложения HTTP/1.1, которые не поддерживают постоянные соединения, обязаны включать опцию «close» в каждое сообщение.

После отправки запроса и заголовков, браузер отправляет серверу единичную пустую строку, сигнализируя о том, что содержимое сообщения закончилось.

Сервер отвечает специальным кодом, который обозначает статус запроса и включает ответ следующей формы:

200 OK
[заголовки ответа]

После этого посылается пустая строка, а затем оставшийся контент HTML-страницы www.google.com. Сервер может затем закрыть соединение, или, если того требуют отправленные клиентом заголовки, сохранять соединение открытым для его использования следующими запросами.

Если HTTP-заголовки отправленные веб-браузером включают информацию, которой серверу достаточно для определения версии файла, закэшированного в браузере и этот файл не менялся со времени последнего запроса, то ответ может принять следующую форму:

304 Not Modified
[заголовки ответа]

и, соответственно, клиенту не посылается никакого контента, вместо этого браузер «достаёт» HTML из кэша.

После разбора HTML, браузер (и сервер) повторяет процесс загрузки для каждого ресурса (изображения, стили, скрипты, favicon.ico и так далее), на который ссылается HTML-страница, но при этом изменяется адрес каждого запроса c GET / HTTP/1.1 на GET /$(относительный URL ресурса www.google.com) HTTP/1.1.

Если HTML ссылается на ресурс, размещённый на домене, отличном от google.com, то браузер возвращается к шагам, включающим разрешение доменного имени, а затем заново проходит процесс до текущего состояния, но уже для другого домена. Заголовок Host в запросе вместо google.com будет установлен на нужное доменное имя.

7.1 Обработка HTTP-запросов на сервере

HTTPD (HTTP Daemon) является одним из инструментов обработки запросов/ответов на стороне сервера. Наиболее популярные HTTPD-серверы это Apache или Nginx для Linux и IIS для Windows.

— HTTPD (HTTP Daemon) получает запрос.

— Сервер разбирает запрос по следующим параметрам:

— Сервер проверяет существование виртуального хоста, который соответствует google.com.

— Сервер проверяет, что google.com может принимать GET-запросы.

— Сервер проверяет, имеет ли клиент право использовать этот метод (на основе IP-адреса, аутентификации и прочее).

— Если на сервере установлен модуль перезаписи (mod_rewrite для Apache или URL Rewrite для IIS), то он сопоставляет запрос с одним из сконфигурированных правил. Если находится совпадающее правило, то сервер использует его, чтобы переписать запрос.

— Сервер находит контент, который соответствует запросу, в нашем случае он изучит индексный файл.

— Далее сервер разбирает («парсит») файл с помощью обработчика. Если Google работает на PHP, то сервер использует PHP для интерпретации индексного файла и направляет результат клиенту.

8. За кулисами браузера

Задача браузера заключается в том, чтобы показывать пользователю выбранные им веб-ресурсы, запрашивая их с сервера и отображая в окне просмотра. Как правило такими ресурсами являются HTML-документы, но это может быть и PDF, изображения или контент другого типа. Расположение ресурсов определяется с помощью URL.

Способ, который браузер использует для интерпретации и отображения HTML-файлов описан в спецификациях HTML и CSS. Эти документы разработаны и поддерживаются консорциумом W3C (World Wide Wib Consortium), которая занимается стандартизацией веба.

Интерфейсы браузеров сильно похожи между собой. У них есть большое количество одинаковых элементов:

Высокоуровневая структура браузера

Браузер включает следующие компоненты:

9. Парсинг HTML

Движок рендеринга начинает получать содержимое запрашиваемого документа от сетевого механизма браузера. Как правило, контент поступает кусками по 8Кб. Главной задачей HTML-парсера является разбор разметки в специальное дерево.

Получающееся на выходе дерево («parse tree») — это дерево DOM-элементов и узлов атрибутов. DOM — сокращение от Document Object Model. Это модель объектного представления HTML-документа и интерфейс для взаимодействия HTML-элементов с «внешним миром» (например, JavaScript-кодом). Корнем дерева является объект «Документ».

Алгоритм разбора

HTML-нельзя «распарсить» с помощью обычных анализаторов (нисходящих или восходящих). Тому есть несколько причин:

Невозможность использования привычных технологий парсинга приводит к тому, что разработчики браузеров реализуют собственные механизмы разбора HTML. Алгоритм парсинга подробно описан в спецификации HTML5.

Алгоритм состоит из двух этапов: токенизации и создания дерева.

Действия после завершения парсинга

После этого браузер начинает подгружать внешние ресурсы, связанные со страницей (стили, изображения, скрипты и так далее).

На этом этапе браузер помечает документ, как интерактивный и начинает разбирать скрипты, находящиеся в «отложенном» состоянии: то есть те из них, что должны быть исполнены после парсинга. После этого статус документа устанавливается в состояние «complete» и инициируется событие загрузки («load»).

Важный момент: ошибки «Invalid Syntax» при разборе не может быть, поскольку браузеры исправляют любой «невалидный» контент и продолжают работу.

10. Интерпретация CSS

11. Рендеринг страниц

12. Рендеринг GPU

13. Вызванное пользователем и пост-рендеринговое исполнение

После завершения рендеринга, браузер исполняет JavaScript-код в результате срабатывания некоего часового механизма (так работают дудлы на странице Google) или в результате действий пользователя (ввод поискового запроса в строку и получение рекомендаций в ответ). Также могут срабатывать плагины вроде Flash или Java (но не в рассматриваемом примере с домашней страницей Google). Скрипты могут потребовать обработки дополнительных сетевых запросов, изменять страницу или её шаблон, что приведёт к следующему этапу рендеринга и отрисовки.

Scholokov Denis

Автор: Scholokov Denis