Веб, HTTP и устройство веб-приложения
Курс посвящён веб-приложениям: программам, которые пользователь открывает в браузере, а данные которых живут на сервере. Прежде чем написать первую строку разметки, разберём механику, на которой держится всё остальное: что происходит между вводом адреса и появлением страницы на экране. Адрес, запрос, ответ, разбор документа, запросы за ресурсами, отрисовка — эта цепочка повторяется при каждом открытии любой страницы, и каждое её звено в дальнейшем получит отдельную тему. Здесь мы пройдём цепочку целиком, чтобы дальше было видно, какое место занимает HTML, где вступает в дело CSS и зачем странице скрипт.
Гипертекст и Всемирная паутина
Гипертекст и гипермедиа
Гипертекст — текст, содержащий ссылки на другие тексты, по которым читатель переходит в выбранном им порядке. Обычная книга читается от начала к концу; гипертекст не имеет единственного порядка чтения, его задаёт сам читатель, выбирая ссылку за ссылкой. Термин предложил Тед Нельсон в 1965 году, описывая файловую структуру для документов, которые меняются и связаны друг с другом 1. Он же ввёл слово «гипермедиа» — то же самое, но для изображений, звука и видео; в обиходе закрепился первый термин, хотя современный веб гипермедийный по существу.
Ссылка — основная операция гипертекста, и на ней держится вся дальнейшая конструкция. Если связанные документы лежат на одной машине, ссылке достаточно имени файла. Если на разных, потребуются две вещи: способ назвать документ так, чтобы имя было понятно на любой машине сети, и способ доставить документ по этому имени. Первая задача решается адресом, вторая — протоколом передачи.
От идеи до современного состояния
В 1989 году Тим Бернерс-Ли, работавший в Европейской организации по ядерным исследованиям (CERN), предложил систему для хранения служебной документации, в которой документы были бы связаны ссылками и доступны с любого компьютера сети 2. Из этого предложения выросли три составляющие, определяющие веб до сих пор: единообразный адрес ресурса (URL), протокол передачи гипертекста (HTTP) и язык разметки гипертекста (HTML). К концу 1990 года были написаны первый сервер и первый браузер, а в 1991 году система стала доступна за пределами CERN.
Веб часто отождествляют с интернетом, и это неточно. Интернет — сеть сетей, обеспечивающая доставку пакетов между машинами; он существовал за двадцать лет до веба и обслуживает электронную почту, передачу файлов и многое другое. Веб — один из сервисов поверх интернета: набор соглашений о том, как называть документы, как их запрашивать и как записывать. Именно эти соглашения мы будем изучать.
Базовая механика веба за тридцать лет не изменилась: пользователь называет адрес, браузер отправляет запрос, сервер возвращает документ, браузер его отображает. Изменилось содержание документа. Первые страницы были статичными: сервер отдавал файл как есть. С появлением форм страница научилась отправлять данные обратно, и сервер стал собирать документы на лету. С появлением скриптов страница научилась меняться прямо в браузере и запрашивать данные, не перезагружаясь. Так документ превратился в приложение: программу, которая выполняется в браузере и разговаривает с сервером. Механика запроса и ответа осталась прежней, и потому начинать надо с неё.
Адресация и протокол
URL
Единообразный указатель ресурса (Uniform Resource Locator, URL) — строка, по которой браузер находит ресурс в сети. Правила её записи закреплены стандартом 3. Рассмотрим адрес, в котором присутствуют все составляющие:
https://tasks.example.ru:8080/api/tasks?status=active&page=2#top
Схема https называет протокол, по которому следует обращаться к ресурсу. Хост tasks.example.ru — имя машины в сети; перед отправкой запроса браузер переводит его в числовой адрес, обращаясь к системе доменных имён (DNS). Порт 8080 — номер, по которому операционная система сервера различает программы, принимающие соединения; если порт не указан, он подразумевается схемой: 80 для http, 443 для https. Путь /api/tasks указывает ресурс внутри хоста; исторически он совпадал с путём к файлу на диске, у приложения же путь — имя, которое сервер истолковывает сам. Параметры запроса после ? — пары «имя — значение», соединённые знаком &; так странице передают уточнения: фильтр, номер страницы, поисковую строку. Фрагмент после # адресует часть документа и на сервер не отправляется: с ним работает только браузер, прокручивая страницу к нужному месту.
В адресе допустимы латинские буквы, цифры и небольшой набор знаков. Всё остальное — кириллица, пробел, сами служебные знаки внутри значений — передаётся в закодированном виде: каждый байт представления UTF-8 записывается как знак % и два шестнадцатеричных разряда. Слово «задачи» превращается в %D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8, по два байта на букву. Адресная строка браузера показывает адрес в раскодированном виде для удобства чтения, но на сервер уходит закодированный.
Адрес может быть относительным: ссылка tasks.html в документе https://example.ru/app/index.html разрешается в https://example.ru/app/tasks.html, а /tasks.html — в https://example.ru/tasks.html, от корня хоста. Разметка документа почти целиком состоит из относительных адресов, и правила их разрешения понадобятся уже в теме 2.
HTTP: запрос и ответ
Протокол передачи гипертекста (HyperText Transfer Protocol, HTTP) устроен по схеме «запрос — ответ»: клиент отправляет запрос, сервер возвращает ответ, после чего обмен завершён. Актуальное описание семантики протокола содержится в стандарте 4. Сообщения обоих направлений имеют одинаковое строение: стартовая строка, заголовки, пустая строка, тело.
Стартовая строка запроса содержит метод, адрес ресурса и версию протокола: GET /api/tasks?status=active HTTP/1.1. Метод называет действие. GET запрашивает ресурс и не имеет тела; такой запрос не должен ничего менять на сервере, поэтому его можно повторять и кешировать. POST передаёт данные в теле запроса: отправка формы, создание записи. PUT заменяет ресурс целиком, PATCH изменяет его часть, DELETE удаляет. Заголовки запроса уточняют, кто и что просит: Host называет хост из адреса, User-Agent — программу-клиент, Accept и Accept-Language — предпочтительные формат и язык ответа, Cookie возвращает серверу ранее выданные им данные.
Стартовая строка ответа содержит версию протокола, код состояния и его словесное пояснение: HTTP/1.1 200 OK. Код — трёхзначное число, первая цифра которого задаёт класс. Класс 2xx означает успех: 200 — ресурс в теле ответа, 201 — ресурс создан, 204 — выполнено, тела нет. Класс 3xx — перенаправление: 301 и 302 сообщают, что ресурс находится по адресу из заголовка Location, и браузер переходит по нему сам; 304 означает, что ресурс не изменился и браузер может взять копию из своего кеша. Класс 4xx — ошибка на стороне клиента: 400 — запрос составлен неверно, 401 и 403 — требуется вход или доступ запрещён, 404 — по такому адресу ничего нет. Класс 5xx — ошибка на стороне сервера: 500 — сбой программы, 502 и 503 — сервер недоступен. Важная особенность: код 404 или 500 — это полноценный ответ с заголовками и, как правило, с телом; с точки зрения протокола обмен состоялся, просто результат отрицательный. Заголовки ответа описывают тело: Content-Type называет формат (text/html, text/css, application/json, image/svg+xml), Content-Length — размер, Cache-Control — как долго ответ можно хранить в кеше; Set-Cookie передаёт клиенту данные, которые тот будет возвращать в последующих запросах.
Последний заголовок отвечает на вопрос, который протокол иначе оставляет открытым. HTTP не хранит состояния: сервер не помнит предыдущих запросов клиента, и каждый запрос для него самостоятелен. Чтобы сервер узнавал вернувшегося пользователя, состояние переносится в самом обмене: сервер выдаёт клиенту метку в Set-Cookie, клиент прикладывает её к каждому следующему запросу в Cookie. На этом устроены сеансы и вход в систему, к чему мы вернёмся в теме 9.
Схема https означает тот же HTTP, но поверх шифрованного соединения (TLS). Шифрование скрывает содержимое обмена от посредников, а сертификат сервера подтверждает, что клиент разговаривает именно с тем хостом, который назван в адресе. Строение сообщений при этом не меняется. Не меняется оно и в новых версиях протокола: HTTP/2 и HTTP/3 передают те же методы, коды и заголовки в двоичном виде и эффективнее используют соединение, но для автора приложения выглядят так же, как текстовый HTTP/1.1.
Цикл загрузки страницы
Соединим адрес и протокол в последовательность действий, которую браузер выполняет при открытии страницы. Получив адрес, браузер узнаёт у системы доменных имён числовой адрес хоста, устанавливает с ним соединение — для https ещё и согласует шифрование — и отправляет запрос GET с путём из адреса. Сервер отвечает документом. Браузер начинает разбирать документ, не дожидаясь его конца, и по мере разбора обнаруживает ссылки на ресурсы: таблицы стилей, скрипты, изображения, шрифты. За каждым ресурсом отправляется отдельный запрос, нередко к другим хостам: библиотеки и шрифты часто лежат в сетях доставки содержимого, счётчики посещений — на серверах аналитики. Получив стили, браузер вычисляет раскладку и отрисовывает страницу; получив скрипты, выполняет их, и скрипты, в свою очередь, могут отправить новые запросы уже за данными.
Из этой последовательности следуют два наблюдения. Первое: одна страница — это десятки запросов, и перечень их определяет документ: браузер запрашивает ровно то, на что документ ссылается. Второе: повторное открытие той же страницы обходится дешевле. Ресурсы, для которых сервер разрешил кеширование, браузер берёт из локальной копии, не обращаясь к сети, либо спрашивает сервер, изменились ли они, и получает короткий ответ 304 вместо тела. Так протокол без состояния обеспечивает быстрый повторный доступ, не храня ничего о клиенте.
Браузер как среда выполнения
Архитектура браузера
Браузер — программа, которая получает документы по HTTP и превращает их в изображение на экране и в среду для выполнения скриптов. Внутри он состоит из нескольких подсистем, и их разделение объясняет многое из того, что встретится в курсе 5.
Сетевой слой выполняет запросы и принимает ответы: именно он реализует протокол, разобранный выше. Разборщик HTML превращает текст документа в дерево элементов — объектную модель документа (DOM), с которой затем работают остальные подсистемы и скрипты. Разборщик CSS строит из таблиц стилей набор правил и сопоставляет их элементам дерева. Движок отрисовки вычисляет по дереву и стилям положение и размер каждого элемента (раскладку) и рисует результат. Интерпретатор JavaScript выполняет скрипты, которые читают и изменяют дерево документа, отчего движок перерисовывает страницу. Хранилище держит cookies и данные, которые страница сохраняет между посещениями.
Ключевые подсистемы объединяют в движок, и движков в современных браузерах три. Blink с интерпретатором V8 лежит в основе Chrome, а также Edge, Opera и Яндекс Браузера; WebKit с JavaScriptCore — в основе Safari; Gecko со SpiderMonkey — в основе Firefox. Для автора приложения это означает, что проверять страницу достаточно в трёх браузерах разных семейств, а не во всех существующих.
Каждая вкладка современного браузера выполняется в отдельном процессе, изолированном от других вкладок и от операционной системы. Страница не может читать файлы на диске пользователя, обращаться к другим вкладкам или к произвольным программам: всё, что ей доступно, — это набор интерфейсов, предоставленных браузером. Ограничение распространяется и на сеть: скрипт со страницы одного хоста по умолчанию не может читать ответы другого хоста. Это правило одного источника ещё встретится в теме 8, когда клиент и сервер данных окажутся на разных портах.
Инструменты разработчика
Все перечисленные подсистемы браузер показывает изнутри через инструменты разработчика, встроенные в каждый современный браузер и открываемые клавишей F12. Панель Elements отображает дерево документа — то, что построил разборщик, а не исходный текст; дерево живое, его можно править на месте и видеть результат. Панель Console выполняет команды JavaScript в контексте страницы и показывает ошибки скриптов. Панель Network перечисляет все запросы страницы с методом, кодом ответа, типом и размером, а по щелчку раскрывает заголовки и тело; здесь протокол виден без посредников. Панель Application показывает хранилище: cookies и сохранённые страницей данные.
Инструменты разработчика — рабочий инструмент курса на всём его протяжении. Проверять разметку, отлаживать стили, исследовать поведение скриптов и наблюдать обмен с сервером мы будем в них, а не догадками.
Стандартизация
Соглашения, из которых состоит веб, закреплены в открытых стандартах, и знать, где они лежат, полезно с первого дня. Адрес и протокол описаны документами RFC, которые выпускает Инженерный совет интернета (IETF): URL — в RFC 3986, семантика HTTP — в RFC 9110. Язык HTML и объектная модель документа развиваются в группе WHATWG как «живой стандарт» без номеров версий: спецификация обновляется непрерывно, а привычное обозначение HTML5 осталось от последней нумерованной редакции 6. Консорциум W3C, основанный в 1994 году Бернерсом-Ли, с 2019 года отвечает за CSS, доступность (WCAG) и ряд программных интерфейсов. Язык JavaScript стандартизирован под именем ECMAScript организацией Ecma International, редакции выходят ежегодно.
Стандарты не гарантируют одинакового поведения: браузеры реализуют их с разной полнотой и в разное время. Поэтому рядом со спецификациями существуют таблицы совместимости, показывающие, какая возможность в каком браузере поддерживается, и справочники, пересказывающие стандарты человеческим языком, — среди них Mozilla Developer Network (MDN). В курсе мы будем опираться на спецификации и MDN как на источники, которым можно верить, и с осторожностью относиться к случайным руководствам, часть которых описывает веб десятилетней давности.
Веб-приложение и его части
Клиент и сервер
Вернёмся к тому, с чего начали: веб-приложение — программа, разделённая на две части, работающие на разных машинах. Клиентская часть выполняется в браузере пользователя: она отображает данные, принимает ввод и выполняет ту часть логики, которую можно доверить браузеру. Серверная часть принимает запросы, применяет правила, хранит данные и отвечает документами или данными. Между ними — только HTTP: клиент ничего не знает об устройстве сервера, кроме адресов и формата ответов, сервер ничего не знает о клиенте, кроме содержания запросов.
Разделение обязанностей удобно рассмотреть на трёх поколениях веб-приложений. У статического сайта сервер — хранилище файлов: он отдаёт документы как есть, все данные записаны в самих документах, а любое изменение требует правки файлов. Приложение первого поколения отправляет ввод пользователя формой, сервер собирает новый документ на каждый запрос и возвращает его целиком; данные живут в базе на сервере, а каждое действие перезагружает страницу. Приложение с интерфейсом данных (API) разделяет обязанности иначе: сервер отдаёт данные в формате JSON, а страницу строит скрипт в браузере, запрашивая данные по мере надобности и меняя страницу без перезагрузки. Данные при этом по-прежнему хранятся на сервере, у клиента — только копия, которую он может дополнительно сохранить в браузере между посещениями.
Из разделения следует правило, к которому курс будет возвращаться. Клиентская часть работает на машине пользователя и целиком ему подконтрольна: он может изменить скрипт, отправить запрос в обход страницы, подставить любые значения. Всё, что важно для целостности данных, проверяет сервер; клиент делает проверки ради удобства, а не ради защиты. В теме 9 эта граница между доверенной и недоверенной частью будет рассмотрена подробно.
Ориентиры курса
Курс охватывает клиентскую часть веб-приложения. Мы будем писать документы на HTML, оформлять их на CSS, программировать поведение на JavaScript и обмениваться данными с сервером по HTTP. Серверную часть мы получим готовой: программа, отдающая данные в формате JSON по заранее известным адресам, будет запускаться одной командой, а её устройство рассмотрим обзорно в заключительной теме. Программирование серверной части, развёртывание и клиентские фреймворки остаются за рамками курса и лишь упоминаются там, где без этого не обойтись.
Учебное приложение курса — список задач. Оно пройдёт три состояния, повторяющие три поколения веб-приложений: сначала страница, размеченная и оформленная; затем приложение, работающее целиком в браузере и хранящее задачи локально; наконец, клиент, получающий и изменяющий задачи на сервере по HTTP. Инструменты на всём пути одни и те же: браузер с инструментами разработчика, текстовый редактор и локальный сервер, отдающий файлы по HTTP так же, как это делает настоящий.
Итоги темы
Веб — сервис поверх интернета, построенный на трёх соглашениях: адресе ресурса, протоколе передачи и языке разметки. Адрес состоит из схемы, хоста, порта, пути, параметров запроса и фрагмента; всё, что выходит за пределы латиницы и цифр, кодируется побайтно. HTTP — протокол без состояния по схеме «запрос — ответ»; сообщения состоят из стартовой строки, заголовков и тела, метод называет действие, код состояния — результат, а ответ с кодом ошибки остаётся полноценным ответом.
Загрузка страницы — не один запрос, а последовательность: документ, затем ресурсы, на которые он ссылается, позже данные, запрошенные скриптом. Браузер разбирает документ в дерево, применяет стили, отрисовывает результат и выполняет скрипты в изолированном процессе; инструменты разработчика показывают каждую из этих подсистем.
Веб-приложение разделено на клиент в браузере и сервер, связанные только HTTP. Клиент отображает и принимает ввод, сервер хранит данные и проверяет всё, что важно. В курсе мы строим клиентскую часть: от документа на HTML до приложения, обменивающегося данными с готовым сервером.
Литература
- Nelson T. H. Complex Information Processing: A File Structure for the Complex, the Changing and the Indeterminate. — Proceedings of the 1965 20th National Conference, 1965, С. 84–100, DOI: 10.1145/800197.806036.
- Berners-Lee T. Information Management: A Proposal. — 1989, https://www.w3.org/History/1989/proposal.html.
- Berners-Lee T., Fielding R. T., Masinter L. Uniform Resource Identifier (URI): Generic Syntax. — 2005, DOI: 10.17487/RFC3986.
- Fielding R. T., Nottingham M., Reschke J. HTTP Semantics. — 2022, DOI: 10.17487/RFC9110.
- Kosaka M. Inside look at modern web browser. — 2018, https://developer.chrome.com/blog/inside-browser-part1.
- {WHATWG}. HTML Living Standard. — 2026, https://html.spec.whatwg.org/multipage/.