# HTTP

## Що це таке

HTTP, або Hypertext Transfer Protocol, — це набір правил, за якими браузер,
застосунок чи скрипт просить у сервера щось і отримує відповідь; коли ця розмова
йде всередині зашифрованого з'єднання TLS, той самий протокол називають HTTPS.
Кожен обмін складається з двох половин. Клієнт надсилає запит: метод на кшталт
`GET` чи `POST`, ціль, яку хоче отримати, набір полів заголовка і часом тіло.
Сервер відповідає тризначним кодом стану, власними полями заголовка й, зазвичай,
тілом. Завантаження сторінки, отримання зображення, виклик однієї програми з
іншої — це один і той самий обмін, повторений багато разів.

Специфікація називає HTTP протоколом без стану. Кожен запит мусить нести все,
що потрібно серверу, щоб його зрозуміти, а сам протокол нічого не пам'ятає між
обмінами. Вхід до облікового запису, кошик і сесії будують поверх нього,
здебільшого на cookie: це поле заголовка, яке сервер встановлює один раз, а
клієнт повертає з кожним наступним запитом.

Словник у HTTP невеликий, зате точний. `GET` і `HEAD` — безпечні методи: вони
просять представлення ресурсу й нічого не мають змінювати. `PUT` та `DELETE`
ідемпотентні, тобто той самий запит, надісланий двічі, лишає сервер у тому ж
стані, що й надісланий один раз. `POST` не обіцяє ні того, ні іншого. Коди стану
згруповано за першою цифрою: 2xx означає успіх, 3xx — перенаправлення, 4xx —
проблему із запитом, 5xx — проблему на сервері. Тож клієнт, який бачить
незнайомий код, усе одно приблизно розуміє, що сталося.

Значення всіх цих слів лишається сталим, хоча формат передавання вже двічі
замінили. HTTP/1.1 пише повідомлення текстовими рядками поверх з'єднання TCP, і
кожне з'єднання повертає відповіді по одній, у порядку запитів. HTTP/2 зберігає
ті самі методи й поля, але пакує їх у двійкові кадри та переплітає багато
запитів в одному з'єднанні. HTTP/3 передає ті самі кадри через QUIC, транспорт
на основі UDP, тож утрачений пакет затримує лише потік, якому він належав. Від
2022 року IETF публікує семантику в одному документі, а формат кожної версії —
в окремому.

Отже, HTTP чи HTTPS — це вибір не між двома протоколами. HTTPS — це HTTP, під
яким лежить TLS: він шифрує обмін і підтверджує ім'я сервера. WebSocket
починається як звичайний запит HTTP, а потім перемикає з'єднання на канал, де
писати можуть обидві сторони. gRPC — це домовленість про віддалені виклики, що
працює поверх HTTP/2.

## Чому це важливо

Майже кожна система, яка спілкується з іншими через інтернет, уже говорить
HTTP. Тому справжнє питання інше: користуватися тим, що протокол визначає, чи
сприймати його як трубу й будувати решту самотужки.

На користь першого варіанта є вагомий аргумент: довгий ланцюжок програм уже
розуміє цей словник. Клієнтська бібліотека може повторити `GET`, на який не
дочекалася відповіді, але `POST` мусить лишити як є, і так само має вчинити
будь-який проксі посередині. Кеш може зберігати відповідь із позначкою
`max-age=600` десять хвилин і віддавати її наступному читачеві, не питаючи
сервер. Моніторинг зараховує відповіді 5xx до помилок без додаткових підказок. API,
що надсилає кожен виклик як `POST` на одну адресу й повідомляє про збій усередині
відповіді 200, усього цього позбавляється, і кожен клієнт мусить вивчати його
приватні правила.

У моделі є межі. Запит і відповідь добре пасують клієнтові, який знає, чого
хоче, і значно гірше — серверу, якому є що повідомити першим. Через це живі
оновлення зазвичай ідуть через WebSocket, server-sent events або регулярні
повторні запити.

З відкритим HTTP у публічному інтернеті відповідь коротка. Браузери позначають
його як небезпечний, будь-хто на шляху може прочитати й змінити вміст, а
сертифікати безкоштовні, тож публічний трафік має йти через HTTPS. Вибір між
HTTP/1.1, HTTP/2 і HTTP/3 — здебільшого налаштування сервера або периферії
перед ним. Код застосунку лишається тим самим, бо кожен запит означає те саме в
усіх трьох версіях.

## Як це виглядає на практиці

Найвиразніший приклад на цьому сайті — API, яке перенесли на фреймворк,
побудований довкола самої форми HTTP.
[HTTP API переїхало з Fiber на Huma v2](/uk/portfolio/migrated-the-http-api-from-fiber-to-huma-60/),
що працює поверх Fiber. Кожен ендпоінт оголошено через структури входу й
виходу, і валідація випливає з типів, а не з написаних уручну перевірок. З тих
самих оголошень виходить опис OpenAPI, а перевірка в CI порівнює маршрути, які
реєструє сервер, з тими, що їх декларує опис. На 649 шляхах із 760 операціями
паритет становить 100%, і він тримається, бо маршрут без опису завалює білд.
Рівно два ендпоінти лишилися на чистому Fiber — ті, що працюють через
WebSocket, де модель запиту й відповіді Huma не підходить.

Сам сайт використовує узгодження вмісту. Кожну сторінку опубліковано і як HTML,
і як Markdown, і один зі способів отримати Markdown — це
[надіслати `Accept: text/markdown` і дати серверу вибрати](/uk/cli/). Та сама
сторінка пояснює, чому адреса onion навмисно має звичайний `http://`: адреса є
відкритим ключем самого сервісу, тож канал автентифіковано й зашифровано ще до
першого байта HTTP.

HTTP трапляється і як інструмент в інфраструктурному коді. Коли розгортали
другий сервер,
[брандмауери обох провайдерів написали безпосередньо проти їхніх REST API](/uk/portfolio/provisioned-a-second-server-from-code-123/)
через узагальнене HTTP-завдання, а не через колекцію постачальника. Так зникла
залежність разом з її версійним дрейфом, а платою стало ручне написання форми
кожного запиту. Сторінка називає й іншу ціну: узагальнене HTTP-завдання не
оголошує підтримки режиму перевірки, тож пробний прогін п'єси, яка створює
машину, пропускає в ній кожне завдання. Тому репетицію проводять на п'єсі
брандмауера.

## Де виникають проблеми

Більшість помилок із HTTP з'являється тоді, коли слово з його словника
вживають в іншому значенні, ніж те, яке чує решта ланцюжка.

### Помилка, що виглядає як успіх

API відповідає кодом 200 і кладе в тіло `"error": "card declined"`. Кеш може
таку відповідь зберегти, дашборди зараховують її як успіх, а логіка повторних
спроб у клієнті так і не спрацьовує. Надсилайте код, який відповідає ситуації,
4xx або 5xx, а суть проблеми описуйте в тілі. RFC 9457 визначає для цього
формат JSON із типом `application/problem+json`.

### Посилання, яке видаляє

Запит `GET` змінює стан, наприклад посилання для відписки чи видалення в
листі. Пошукові роботи, попередній перегляд посилань у месенджерах, сканери
безпеки й попереднє завантаження в браузері переходять за посиланнями самі, і
кожен із них може запустити дію. Усе, що змінює дані, має стояти за `POST`,
`PUT` або `DELETE`, а якщо посилання таки потрібне, — за сторінкою
підтвердження.

### Платіж, списаний двічі

Клієнт надсилає `POST`, з'єднання обривається до того, як прийшла відповідь, і
клієнт пробує ще раз. Сервер тим часом уже обробив перший запит. HTTP не дає
клієнтові способу розрізнити ці два випадки, тож це має робити API: приймати
ключ ідемпотентності в полі заголовка й повертати збережений результат, коли
ключ повторюється, або дозволити клієнтові самому вибрати ідентифікатор нового
ресурсу і створити його через `PUT`.

### Форма, що перетворюється на GET

Сервер відповідає на `POST` кодом 301 або 302, щоб відправити його деінде. З
історичних причин специфікація дозволяє агентові користувача змінити метод на
`GET` саме для цих двох кодів, і багато хто так робить, тож тіло губиться
дорогою. Якщо метод має пережити перенаправлення, потрібні коди 307 чи 308.

### Чужа версія з кешу

Одна URL-адреса віддає HTML або Markdown, українською чи англійською, залежно
від полів заголовка запиту. Кеш, якому про це не сказали, зберігає ту версію,
яку побачив першою, і віддає її наступному читачеві. Відповідь мусить назвати
поля, від яких залежить, наприклад `Vary: Accept`, щоб кеш тримав окрему копію
для кожного варіанта, а не одну на всю адресу.

## Що почитати далі

- [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110) — методи,
  коди стану й поля заголовка, визначені один раз для всіх версій.
- [RFC 9111: HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111) — коли
  збережену відповідь можна використати повторно і що кешу каже `Cache-Control`.
- [RFC 9112: HTTP/1.1](https://www.rfc-editor.org/rfc/rfc9112) — текстовий
  формат передавання, з'єднання і межі повідомлень.
- [RFC 9113: HTTP/2](https://www.rfc-editor.org/rfc/rfc9113) — двійкові кадри
  й багато потоків в одному з'єднанні.
- [RFC 9114: HTTP/3](https://www.rfc-editor.org/rfc/rfc9114) — та сама
  семантика поверх потоків QUIC.
- [RFC 9457: Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457)
  — стандартне тіло JSON для опису того, що пішло не так.

<https://engineer.company/uk/glossary/http/>
