Клієнт-сервер і як працює веб
Зміст
Уяви прилавок у кавʼярні. Ти кажеш бариста, чого хочеш, і чекаєш. Бариста не подає тобі каву просто так, поки ти не попросив, і не знає наперед, хто зайде наступним. Ти теж не лізеш за стійку молоти : твоя справа — сформулювати замовлення, його справа — виконати. Веб побудований на цій самій асиметрії: одна сторона просить, інша обслуговує.
Навіщо це тестувальнику. Коли тест «не бачить» кнопку, коли запит падає з кодом 500, коли той самий сценарій зелений локально й червоний на CI — за кожним випадком стоїть конкретна ланка обміну між клієнтом і сервером. Ця глава — карта: вона називає всі ланки й пояснює, за що кожна відповідає. Деталі розбирають окремі глави розділу, і на них тут стоять лінки; завдання цієї глави — дати систему координат, у якій ті деталі матимуть сенс.
Клієнт і сервер: хто кого просить
Клієнт (client) — програма, яка ініціює запит і споживає відповідь. Найзвичніший клієнт у вебі — браузер, але клієнтом може бути будь-що, що вміє надіслати мережевий запит: утиліта curl у терміналі, Postman, мобільний застосунок, автотест.
Сервер (server) — програма, яка постійно слухає вхідні запити на певному порту й формує відповіді. Тут ховається перша плутанина: словом «сервер» називають і машину в дата-центрі, і програму-процес на ній. Далі в главі сервер — саме програма.
Головне в цій моделі — не визначення, а асиметрія ролей. Клієнт знає, до кого звертатись; сервер зазвичай не знає наперед, хто до нього прийде. У класичній схемі «один запит — одна відповідь» сервер не заговорює першим: він лише відповідає на те, що прийшло.
Чому взагалі так поділили? За поділом стоїть інженерний принцип розділення відповідальностей (separation of concerns): інтерфейс окремо від зберігання даних. Виграш подвійний — інтерфейс стає простіше переносити між платформами, а серверну частину простіше масштабувати, бо вона позбавлена турбот про відображення.
Рівні: коли клієнта й сервера вже недостатньо
Той самий поділ масштабують далі — на рівні (tiers). Рівнева архітектура (n-tier architecture) ділить застосунок на логічні шари (layers) і фізичні рівні (tiers): шар — це відповідальність (показати, порахувати, зберегти), рівень — машина, на якій та відповідальність виконується. Шари ходять в один бік: вищий користується послугами нижчого, а нижчий до вищого не звертається. Один шар — не обовʼязково один рівень: кілька шарів цілком можуть жити на одному.
Звідси три знайомі конфігурації. Дворівнева — клієнт плюс сервер із даними: у варіанті, який описує гайд Microsoft, увесь код застосунку лежить на клієнті, а окремий сервер тримає базу. Трирівнева — клієнт звертається до застосунку, розгорнутого на окремому сервері, а той уже до бази на ще одному; для веб-застосунків і веб-сервісів це дуже поширена схема. Багаторівнева — коли рівнів більше за три: складніший застосунок розносить різні ділянки функціональності по кількох середніх рівнях. Плата за рознесення — мережа поміж рівнями: фізичний поділ додає масштабованості й стійкості, але й .
Тестувальнику ця картина відповідає на питання «де шукати». Запити й дані, що проходять крізь кілька рівнів, ускладнюють тестування й моніторинг — червона перевірка на екрані показує симптом, а зламатися могло на будь-якому рівні за нею. Найчастіший приклад — перевірки введених даних: клієнтська валідація за MDN не є вичерпним заходом безпеки, її занадто просто обійти, а даним від клієнта довіряти не можна. Практичний висновок подвійний: баг «сервер прийняв те, чого форма не пускала» шукають не в UI, а рівнем нижче; і навпаки — правило, яке справді живе на сервері, перевіряється без UI взагалі, одним запитом до API.
Наша практика (не канон). Клієнта, який тримає логіку в себе, команди звуть товстим (thick, або fat), а того, що лише показує готовий результат, — тонким (thin): це робочі слова з практики, окремого стандарту чи специфікації під них немає. Сам поділ під цими назвами не домашній — він про те, на якому рівні лежить код застосунку.
Ключова думка діаграми: моделі різняться не кількістю прямокутників, а тим, по якому боку проходить межа відповідальності — товстий клієнт рахує й перевіряє сам, а тонкий лише показує те, що порахував сервер.
Автотест — це теж клієнт
Це найважливіший висновок глави для AQA. Твій тест не стоїть «збоку» від системи: у моделі він стоїть рівно там, де браузер чи curl, і має ті самі залежності від адрес, зʼєднань, статусів і оточення. Усе, що ламає браузер, ламає й тест.
// обидва рядки — це запити клієнта, просто різними шляхами
await page.goto('https://shop.example.com/cart'); // клієнт — браузер
const res = await request.get('https://api.example.com/cart/42'); // клієнт — сам тест
Різниця між рядками практична. Запит до API не залежить від верстки, анімацій і , тому ним зручно готувати : один POST замість десяти екранів клацання. А потім уже перевіряти інтерфейс на готовому стані.
Один запит — одна фінальна відповідь
Обмін має сталу форму. Запит складається з методу, шляху, заголовків і тіла (тіло є не завжди). Відповідь — зі , заголовків і тіла. Заголовки — це поля, які передаються перед тілом повідомлення.
GET /cart?id=42 HTTP/1.1
Host: shop.example.com
Accept: text/html
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5120
Статус-код — тризначне число від 100 до 599; клас відповіді задає перша цифра. Формула «один запит — одна відповідь» точна лише щодо фінальної відповіді: перед нею може прийти нуль або більше проміжних із кодами класу 1xx (наприклад, 100 Continue чи 103 Early Hints), а вже за ними — рівно одна фінальна. Це не , а передбачений протоколом порядок.
Методи, заголовки й тіло докладно розбирає глава «HTTP: методи, структура, заголовки», значення кодів — «HTTP статус-коди», а повний ланцюг від натискання Enter до сторінки на екрані — «Життєвий цикл запиту».
Від імені до адреси: роль DNS
Людина набирає shop.example.com, а мережа маршрутизує за числами. Перекладає одне в інше DNS (Domain Name System) — розподілена ієрархічна система, що складається з простору доменних імен, серверів імен і резолверів.
Резолвер (resolver) — програма-прошарок між застосунком і серверами імен: приймає запит програми й за потреби йде ланцюжком далі — до кореневих серверів, потім до серверів зони .com, потім до авторитетних серверів самого домену, які й повертають адресу.
Для тестів важлива не так механіка, як кеш. Кожен запис має TTL — час, скільки його дозволено тримати в кеші, перш ніж відкинути. Тому після переїзду стенда на нову адресу тести якийсь час стукають за старою: чекати доводиться саме TTL, а орієнтир джерела для типового вузла — порядок днів. Детально DNS розбирає глава «DNS, IP, порти та мережа».
Адреса, порт і чому localhost іноді підводить
IP-адреса (IP address) — числовий ідентифікатор вузла: за нею пакети знаходять дорогу до потрібної машини. Але машина одна, а програм на ній десятки, тому потрібен ще порт (port) — 16-бітне число від 0 до 65535, яке вказує, до якого саме сервісу йде зʼєднання. Пара «адреса + порт» називається сокетом, і саме пара сокетів (свій на кожному боці) однозначно визначає зʼєднання.
Для стандартних схем порт можна не писати — мається на увазі типовий: 80 для http, 443 для https. Нестандартний вказують явно, як :3000 у локальному стенді.
Окремий випадок — loopback: адреси 127.0.0.1 (IPv4) і ::1 (IPv6) означають «ця сама машина», і трафік на них нікуди не виходить у мережу. Звідси класична пастка: імʼя localhost веде на обидві ці адреси, тож якщо застосунок слухає лише 127.0.0.1, а клієнт пішов на ::1, зʼєднання «незрозуміло чому» не встановлюється. Рятує явна адреса замість імені.
Рукостискання: про що домовляються до обміну
Перш ніж піде корисний трафік, сторони узгоджують параметри — це (handshake). На шляху звичайного HTTPS-запиту їх щонайменше два.
Перше — транспортне, тристороннє рукостискання TCP: SYN → SYN-ACK → ACK. Кроків саме три, бо підтвердження й власний SYN сервера обʼєднані в одне повідомлення.
Друге — криптографічне, TLS. Воно автентифікує сторони, узгоджує параметри шифрування й встановлює спільні ключі; починається з ClientHello, на який сервер відповідає ServerHello. HTTPS і є звичайний HTTP, загорнутий у цей шар: спершу піднімається захищений канал, а вже всередині нього ходить знайомий трафік. Гарантій рівно три: автентифікація (сервер автентифікується завжди, клієнт — опційно), конфіденційність і .
Практичний наслідок: рукостискання платиться один раз на зʼєднання. Перший запит до нового хоста завжди дорожчий за наступні, бо keep-alive перевикористовує вже відкритий канал. Сертифікати, версії TLS і те, що ламається на цій фазі найчастіше, розбирає глава «HTTPS, TLS і безпека».
Затримка і пропускна здатність — це різні речі
Затримка (latency) — час, за який дані долають шлях від джерела до призначення; мережеву затримку зазвичай міряють туди й назад, як round-trip. (bandwidth) — міра того, скільки інформації може пройти крізь зʼєднання за заданий проміжок часу; міряють її кратними бітам за секунду (Mbps, Gbps).
Величини незалежні, і плутати їх дорого. Канал буває водночас «товстим» і «довгим»: у TCP існує добуток «пропускна здатність × затримка», і мережі з великим його значенням мають власні проблеми продуктивності — їх так і називають, long fat network.
Звідси різна діагностика. Сценарій із десятками дрібних API-викликів страждає від затримки — кожен виклик додає свій round-trip, і лікується це меншою кількістю походів у мережу. Сценарій із завантаженням великого файлу впирається в пропускну здатність, і лікується розміром корисного навантаження. Перший запит до хоста платить окремо: у його затримку входять DNS-пошук, рукостискання TCP і узгодження TLS.
Саме тому оптимізації вебу здебільшого борються з кількістю round-trips: зʼєднань, мультиплексування в HTTP/2, , CDN; HTTP/3 поверх QUIC додатково поєднує встановлення транспортного зʼєднання з узгодженням TLS.
Посередники на шляху: проксі, шлюз, тунель і CDN
Малюнок «клієнт → сервер» правдивий лише в підручнику. Насправді запит іде ланцюгом зʼєднань, і на ньому стоять (intermediary). Форм три, і розрізняють їх не за місцем, а за роллю:
- (proxy) — агент пересилання, якого обирає клієнт; типово його ставлять, щоб звести запити організації через спільного посередника заради безпеки або спільного кешу.
- (gateway), він же reverse proxy, — посередник, який поводиться як origin-сервер для зовнішнього зʼєднання, а всередину пересилає запити іншим машинам. Балансувальник навантаження — окремий випадок шлюзу: розділення навантаження між кількома машинами названо серед його типових застосувань.
- (tunnel) — сліпий ретранслятор, який повідомлень не змінює й учасником обміну не вважається.
CDN (Content Delivery Network) — мережа проміжних серверів географічно ближче до користувачів: вона кешує відповіді, тож частина запитів до origin-сервера просто не доходить. З погляду HTTP це спільний кеш — той, що обслуговує кількох користувачів, на відміну від приватного кешу браузера.
Два практичні наслідки. Коди 502 і 504 за означенням віддає сервер, який діє «as a gateway or proxy», — зламалася ланка за посередником, тоді як 503 означає, що сам сервер тимчасово не може обробити запит. І вузол, якого клієнт не обирав (корпоративний фаєрвол, публічний Wi-Fi), протокольно не відрізнити від на шляху — тож поведінка запиту здатна змінитися без жодної зміни в застосунку. Механіку кешу й посередників розбирає глава «Кешування».
Безстановість: чому сервер тебе не памʼятає
HTTP — безстановий (stateless) протокол: сервер за замовчуванням не памʼятає нічого про попередні запити, і кожен запит для нього чистий аркуш. Специфікація формулює це точніше: семантику кожного запиту можна зрозуміти ізольовано, а звʼязок між зʼєднанням і повідомленнями на ньому не впливає на їх інтерпретацію. Простіше кажучи, «ми ж щойно спілкувалися тим самим зʼєднанням» для сервера не аргумент.
Навіщо так зробили: заради масштабованості й простоти. Якщо сервер не мусить памʼятати клієнтів, будь-який із десятка однакових серверів може обробити будь-який запит.
Ключове уточнення, на якому валяться найчастіше: безстановий тут протокол, а не застосунок. Застосунок майже завжди має стан — твій кошик, твій профіль, — просто цей стан живе в базі даних і в тому, що клієнт приносить із кожним запитом.
Звідси три наслідки для тестів. Кожен запит має нести все потрібне сам: токен чи кукі прикладаються до кожного звернення, а не «один раз при логіні». — той, у якого кілька однакових запитів дають той самий ефект, що й один; такими є PUT, DELETE і всі , тому наївний POST-запиту здатен створити дубль. І третій: оскільки протокол не тримає стану між запитами, ізольовані тести робляться легко — але й навпаки, будь-який спільний стан (дані в базі, кукі, глобальні ) створюєш ти сам, і саме він потім тече між тестами.
Сесія: як застосунок памʼятає логін
Якщо протокол не памʼятає нічого, як сайт памʼятає, що ти залогінений? Відповідь: клієнт сам нагадує про себе в кожному запиті. Класична схема така — сервер тримає запис сесії у себе (памʼять, Redis, база даних), а браузеру віддає лише ключ, ідентифікатор сесії.
Заголовки Set-Cookie і Cookie для того й існують: сервер зберігає стан на боці браузера, а браузер за збереженими метаданими сам вирішує, чи додати кукі до наступного запиту. Код сторінки цього не робить — і саме тому кукі-сесія переживає перезавантаження без жодних зусиль.
Альтернатива — токен (token): стан зашитий у сам токен, і сервер запису не тримає. Токен кладуть або в кукі, або у сховище браузера — і в другому випадку він летить не автоматично, а вручну в заголовку Authorization. Ця різниця практична: токен зі сховища доїде до сервера рівно тоді, коли клієнтський код сам його туди поклав.
Для тестів звідси випливає найкорисніша звичка розділу: не проклацувати форму логіну щоразу, а один раз отримати сесію й перевикористовувати . Кукі, їхні атрибути й сховище браузера — глава «Кукі, сесії та сховище браузера».
SPA і MPA: де живе логіка складання сторінки
Одна й та сама модель «запит — відповідь» дає дві різні архітектури, і від того, яку обрали в проєкті, залежить, як писати очікування.
SPA (single-page application) завантажує лише один документ, а далі змінює вміст його тіла через JavaScript-API на кшталт fetch(). Переходи «між сторінками» документ не перезавантажують: адресу міняє History API — виклики history.pushState() і history.replaceState() змінюють URL без запиту на сервер. Протилежний полюс — MPA (multi-page application), де кожен екран є окремим документом: перехід означає, що браузер іде на сервер по новий документ і будує сторінку з нуля.
Для тесту різниця в одному: чи існує момент «сторінка завантажилась». У MPA дочекатися завершення навігації відносно просто — є чіткі навігаційні події. У SPA старі сигнали завантаження не спрацьовують, і чекати доводиться на появу самого контенту. Стратегії рендерингу докладно розбирає глава «Асинхронне завантаження: SPA/MPA, CSR/SSR», а робота з DOM — «DOM, селектори та події».
Наша практика (не канон). Термін MPA і означення «кожен екран — окремий документ» канонічного джерела не мають: так цей полюс називають команди, з якими ми працювали, і так його називатимуть на співбесіді. Сам механізм — повна навігація за новим документом — випливає з моделі «запит — відповідь», описаної вище.
WebSocket: коли модель «запит — відповідь» не підходить
Є клас функцій, де сервер мусить заговорити першим: чат, нотифікації, живі оновлення. У базовому HTTP такого способу немає, і до появи WebSocket двобічний обмін імітували опитуванням сервера — (polling); специфікація прямо називає це зловживанням HTTP.
WebSocket — протокол двобічного обміну поверх одного TCP-зʼєднання. Відкривається він звичайним HTTP-запитом GET із заголовками Upgrade: websocket і Connection: Upgrade, а успіх підтверджує рівно один статус — 101 Switching Protocols. Далі це вже самостійний протокол, повʼязаний із HTTP лише способом відкриття: заголовки й повторна автентифікація на кожне повідомлення більше не потрібні. Схеми адрес — ws і wss (другий поверх TLS).
Поруч живе простіший інструмент — Server-Sent Events (SSE): однобічний канал сервер → клієнт через інтерфейс EventSource і відповідь типу text/event-stream, із вбудованим автоперепідключенням.
Для тестів головне те, що тут немає кореляції «запит — відповідь»: чекати треба на подію-наслідок — появу елемента, конкретний кадр, — а не на фіксований час. Реалтайм розбирає глава «WebSockets і реалтайм».
REST: архітектурний стиль, а не протокол
REST (Representational State Transfer) — не протокол і не стандарт, а архітектурний стиль: набір обмежень на взаємодію компонентів, описаний Роєм Філдінгом. Три з них ти вже прочитав у цій главі під іншими іменами: client-server (розділення відповідальностей), stateless (запит містить усе потрібне) і layered system (компонент не бачить далі сусіднього шару — саме тому на шляху можуть стояти посередники). Є ще кешованість, необовʼязковий code on demand і головне — єдиний інтерфейс (uniform interface): ідентифікація ресурсів, маніпуляція через представлення, самоописові повідомлення й гіпермедіа як рушій стану.
Практично це дає простий поділ: що — задає адреса ресурсу, що з ним робимо — задає метод. GET запитує представлення ресурсу, POST просить сервер обробити передані дані, PUT створює або повністю замінює його стан, DELETE просить розірвати звʼязок ресурсу з його функціональністю. Формати даних і перевірки контракту — глава «REST API та формати даних».
Версії HTTP: що змінюється, а що ні
Версій протоколу кілька, і новачка це лякає дарма. Семантика — метод, шлях, заголовки, статус — від версії не залежить: специфікація навмисно задає вимоги так, щоб повідомлення однієї версії можна було ретранслювати іншими без зміни його змісту. Тому в панелі мережі ти бачиш ті самі поля незалежно від того, яка версія на дроті.
Змінюється кодування. HTTP/1.1 текстовий — його можна прочитати очима, як у прикладі вище. У HTTP/2 ті самі повідомлення вкладені у двійкову структуру — кадр (frame); MDN називає три відмінності від HTTP/1.1: двійковість, мультиплексування (паралельні запити в одному зʼєднанні) і стиснення заголовків. HTTP/3 працює поверх QUIC і використовує на кожному потоці двійкове кадрування, подібне до кадрування HTTP/2.
Наскільки це вже реальність: за даними, на які посилається MDN, вживання HTTP/2 досягло піку в січні 2022 — 46,9 % усіх сайтів; HTTP/3 у жовтні 2022 користувалися 26 % сайтів. Цифру подавай саме так — з датою й атрибуцією. Важливіше інше: перехід на нову версію не потребує змін у коді застосунку, тому в команді про нього можуть просто не сказати — інфраструктура оновилася, а тести побачили іншу поведінку мережі. Еволюцію протоколу розбирає глава «HTTP/2 і HTTP/3: еволюція протоколу».
Типові помилки
- Виглядає як «HTTP без стану, отже застосунок без стану», а насправді безстановий тут протокол: стан застосунку живе в базі даних і в тому, що клієнт приносить із кожним запитом.
- Виглядає як «сервер памʼятає, що я залогінений», а насправді памʼятає застосунок за ключем, який браузер сам додає до кожного запиту; без ключа сервер бачить тебе вперше.
- Виглядає як «сервер повільний», а насправді сценарій із десятків дрібних викликів упирається в затримку каналу, і ширша смуга його не прискорить.
- Виглядає як «
502— це баг застосунку», а насправді код віддає посередник у ролі шлюзу чи проксі: зламалася ланка за ним, тоді як503каже про сам сервер. - Виглядає як «
localhostі127.0.0.1— те саме», а насправді імʼя веде на обидві loopback-адреси, і застосунок, що слухає лише IPv4, для клієнта на::1недосяжний. - Виглядає як «сторінка завантажилась, дані мають бути», а насправді у SPA документ прийшов один раз, а вміст приносять фонові запити — чекати треба контент, а не навігацію.
Підсумок
- Клієнт ініціює обмін, сервер відповідає; за поділом стоїть розділення відповідальностей — інтерфейс окремо від зберігання даних.
- Автотест у цій моделі — такий самий клієнт, як браузер чи
curl, із тими самими залежностями від адрес, зʼєднань, статусів і оточення. - На запит приходить рівно одна фінальна відповідь, якій можуть передувати проміжні
1xx; семантика повідомлення не залежить від версії протоколу. - HTTP безстановий: кожен запит зрозумілий ізольовано й мусить нести все потрібне сам, а стан застосунку тримає база даних плюс те, що приносить клієнт.
- Між клієнтом і сервером стоять ланки, яких у коді не видно, — DNS, порти, рукостискання, посередники й CDN; більшість «незрозумілих» падінь живе саме там.
Можливі питання
- «Що відбувається після того, як я натиснув Enter в адресному рядку?» Слухають не завчений список, а розуміння порядку: імʼя в адресу, зʼєднання й рукостискання, запит, відповідь, побудова сторінки — і що на кожній ланці може зламатися.
- «Що означає, що HTTP stateless?» Перевіряють головне уточнення: безстановий протокол, а не застосунок; стан живе в базі й у тому, що клієнт приносить із кожним запитом.
- «Як сайт памʼятає, що я залогінений?» Дивляться на механізм, а не назву: сервер віддає ключ, браузер сам повертає його наступними запитами, сервер за ключем знаходить свій запис.
- «Чим затримка відрізняється від пропускної здатності?» Питання на точність. Очікують дві різні величини й приклад, де вони дають різний діагноз: багато дрібних запитів проти одного великого файлу.
- «Чим проксі відрізняється від reverse proxy?» Перевіряють, чи не переказуєш «стоїть зліва / стоїть справа»: проксі обирає клієнт, шлюз назовні поводиться як origin-сервер, а балансувальник — окремий випадок шлюзу.
- «Коли WebSocket, а коли звичайний запит?» Очікують межу: якщо першим має заговорити сервер, модель «запит — відповідь» не підходить, і зʼєднання відкривається апгрейдом зі статусом
101.
Джерела
Клієнт і сервер: хто кого просить
- RFC 9110 — HTTP Semantics — означення клієнта й сервера, асиметрія ролей і схема «запит — відповідь».
- Roy T. Fielding — Architectural Styles and the Design of Network-based Software Architectures, ch.5 (REST) — розділення відповідальностей як мотив клієнт-серверного обмеження: інтерфейсу й масштабованість сервера.
Рівні: коли клієнта й сервера вже недостатньо
- Microsoft Learn — N-tier architecture style — шари vs рівні, односпрямованість викликів, кілька шарів на одному рівні, трирівневий склад, ціна фізичного поділу (масштабованість/стійкість проти затримок), ускладнення тестування й моніторингу.
- Microsoft Application Architecture Guide, 2nd ed., ch.19 — склад дво- і трирівневої конфігурацій; трирівнева як дуже поширена для веб-застосунків і веб-сервісів.
- MDN — Client-side form validation — клієнтська/серверна валідація, «не вичерпний захід безпеки», заборона довіри даним від клієнта.
- RFC 9110 — HTTP Semantics — клієнт як програма, що встановлює зʼєднання й надсилає запити; автотест підпадає під те саме означення.
- Playwright — API testing — запит до API поза UI і підготовка стану одним запитом.
Один запит — одна фінальна відповідь
- MDN — HTTP messages — склад запиту (метод, шлях, заголовки, тіло) і відповіді (статус, заголовки, тіло).
- RFC 9110 — HTTP Semantics — заголовки як поля перед тілом, діапазон і клас статус-кодів, проміжні
1xxперед рівно однією фінальною відповіддю.
- RFC 1034 — Domain Names: Concepts and Facilities — складники DNS, роль резолвера, кеш і TTL разом з орієнтиром «порядок днів» для типового вузла.
Адреса, порт і чому localhost іноді підводить
- RFC 791 — Internet Protocol — IP-адреса як ідентифікатор вузла в мережі.
- RFC 9293 — Transmission Control Protocol (TCP) — порт як ідентифікатор сервісу, сокет і пара сокетів як ідентифікатор зʼєднання.
- RFC 9110 — HTTP Semantics — типові порти схем
httpіhttps, які можна не вказувати. - RFC 1122 — Requirements for Internet Hosts: Communication Layers — loopback-адреса як «ця сама машина».
- RFC 4291 — IP Version 6 Addressing Architecture — loopback
::1у IPv6. - RFC 6761 — Special-Use Domain Names — імена
localhostрезолвляться у відповідну loopback-адресу обох сімейств.
Рукостискання: про що домовляються до обміну
- RFC 9293 — Transmission Control Protocol (TCP) — тристороннє рукостискання й чому кроків саме три.
- RFC 8446 — TLS 1.3 — рукостискання як компонент TLS,
ClientHello/ServerHelloі три гарантії протоколу. - RFC 6797 — HTTP Strict Transport Security (HSTS) — HTTPS як HTTP поверх захищеного транспорту.
- MDN — Connection management in HTTP/1.x — перевикористання відкритого зʼєднання й ціна першого запиту.
Затримка і пропускна здатність — це різні речі
- MDN — Understanding latency — означення затримки, ціна першого запиту й чому дрібні запити страждають саме від неї.
- MDN Glossary — Bandwidth — пропускна здатність як обсяг інформації за одиницю часу.
- RFC 7323 — TCP Extensions for High Performance — добуток «пропускна здатність × затримка» і long fat network як доказ незалежності величин.
- MDN — Connection management in HTTP/1.x — перевикористання зʼєднань і мультиплексування як боротьба з кількістю round-trips.
- RFC 9000 — QUIC: A UDP-Based Multiplexed and Secure Transport — обʼєднання встановлення транспорту з узгодженням TLS.
Посередники на шляху: проксі, шлюз, тунель і CDN
- RFC 9110 — HTTP Semantics — три форми посередника та їхні визначальні ознаки, балансування навантаження як застосування шлюзу, різниця
502/504і503, перехоплювальний вузол як нерозрізнимий від атаки на шляху. - MDN Glossary — CDN — CDN як мережа проміжних серверів ближче до користувача, що розвантажує origin.
- RFC 9111 — HTTP Caching — поділ кешів на спільні й приватні.
Безстановість: чому сервер тебе не памʼятає
- RFC 9110 — HTTP Semantics — HTTP як безстановий протокол, ізольована семантика запиту, мотив масштабованості, означення ідемпотентного й безпечного методу.
- Roy T. Fielding — Architectural Styles and the Design of Network-based Software Architectures, ch.5 (REST) — обмеження stateless: запит містить усе потрібне, стан сесії лишається на клієнті.
- MDN — An overview of HTTP — протоколу проти стану застосунку.
- Playwright — Best Practices — ізоляція тестів і спільний стан, який доводиться створювати самому.
Сесія: як застосунок памʼятає логін
- RFC 6265 — HTTP State Management Mechanism —
Set-CookieіCookieяк механізм стану поверх HTTP; агент користувача сам вирішує, чи повернути кукі. - MDN — Using HTTP cookies — сесійна кукі як ключ до запису сесії на сервері.
- MDN — Authorization header — токен, який надсилають заголовком, а не автоматично.
- Playwright — Authentication — одноразовий логін і збережений стан автентифікації замість форми в кожному тесті.
SPA і MPA: де живе логіка складання сторінки
- MDN Glossary — SPA (Single-page application) — SPA як застосунок, що завантажує один документ і оновлює його вміст через JavaScript.
- MDN — History API —
pushState()іreplaceState()міняють URL без запиту на сервер. - Playwright — class Page — очікування навігації проти очікування появи контенту.
- MDN — Window: load event — подія завантаження документа як сигнал, на який спираються навігаційні очікування.
WebSocket: коли модель «запит — відповідь» не підходить
- RFC 6455 — The WebSocket Protocol — полінг як зловживання HTTP, відкривальне рукостискання через
Upgrade, статус101, самостійність протоколу після переходу, схемиwsіwss. - MDN — Using server-sent events — SSE як однобічний канал через
EventSourceіtext/event-stream. - Playwright — Auto-waiting (actionability) — очікування конкретної умови замість фіксованої паузи.
REST: архітектурний стиль, а не протокол
- Roy T. Fielding — Architectural Styles and the Design of Network-based Software Architectures, ch.5 (REST) — REST як архітектурний стиль, перелік обмежень і склад єдиного інтерфейсу.
- RFC 9110 — HTTP Semantics — означення методів
GET,POST,PUTіDELETE.
Версії HTTP: що змінюється, а що ні
- RFC 9110 — HTTP Semantics — вимоги, незалежні від версії протоколу: повідомлення ретранслюється іншою версією без зміни змісту.
- MDN — An overview of HTTP — двійкове кадрування повідомлень у HTTP/2.
- RFC 9114 — HTTP/3 — двійкове кадрування на потоці, подібне до HTTP/2, поверх QUIC.
- MDN — Evolution of HTTP — три відмінності HTTP/2, частки поширення з датами й нульова ціна переходу для застосунку.
Хто такий клієнт і хто сервер — і чому це питання не про залізо?
Клієнт — той бік, який починає обмін: складає запит і далі працює з тим, що прийшло назад. Сервер — програма, яка постійно тримає відкритим певний порт, приймає вхідні запити й формує відповіді. Перша плутанина ховається саме в слові «сервер»: ним називають і машину в дата-центрі, і процес на ній, а в розмові про HTTP мають на увазі процес. Клієнтом при цьому може бути будь-що, здатне надіслати мережевий запит: браузер, curl, Postman, мобільний застосунок, автотест. Головне тут не визначення, а асиметрія: ініціатива завжди на боці клієнта, і в класичній схемі сервер сам першим не озивається. Мотив такого поділу — розділення відповідальностей (separation of concerns): інтерфейс відокремлено від зберігання даних, тому його легше переносити між платформами, а серверну частину легше масштабувати.
Чому кажуть, що автотест — теж клієнт?
Бо в моделі обміну він займає те саме місце, що браузер чи curl, і успадковує ті самі залежності: адреси, зʼєднання, статуси, оточення. Це не метафора, а робочий висновок: усе, що ламає браузер на стенді — недоступний DNS-запис, зайнятий порт, протухлий сертифікат, в корпоративній мережі, — так само ламає й тест. Практичний бонус із того самого факту: тест може бути клієнтом двома різними способами. Через page.goto() він працює як браузер і залежить від верстки, анімацій і ; через прямий HTTP-запит до API — не залежить від них узагалі. Звідси звичка готувати дешевим шляхом: створити замовлення одним POST, а вже потім перевіряти екран на готовому стані замість того, щоб проклацувати десять форм.
З чого складається HTTP-запит і HTTP-відповідь?
Форма обміну стала. У запиті — метод, шлях, заголовки й тіло, причому тіло є не завжди. У відповіді — , заголовки й тіло. Заголовки в обох випадках — це поля, які їдуть попереду тіла повідомлення; саме в них живе більшість того, що потім дивиться тестувальник: Content-Type, Cookie, Authorization, Location. Статус-код — тризначне число в межах від 100 до 599, і перша його цифра задає клас відповіді, тобто підказує стратегію реакції ще до читання тіла. Для QA це найдешевша діагностика: за методом і шляхом видно, чого хотіли, за статусом — чим це скінчилося, а тіло вже уточнює деталі.
Чи правда, що на один запит завжди приходить рівно одна відповідь?
Формула точна, але тільки щодо фінальної відповіді. Перед нею протокол дозволяє нуль або більше проміжних повідомлень класу 1xx — наприклад, 100 Continue або 103 Early Hints, — і вже після них приходить рівно одна фінальна. Це не й не баг сервера, а передбачений порядок. Практично це означає дві речі. По-перше, побачивши 1xx у , не варто заводити тікет: обмін ще не завершився. По-друге, ставлять на статус саме фінальної відповіді — вона на запит одна, і решта класів кодів живе тільки в ній.
Що відбувається після того, як ти натиснув Enter в адресному рядку?
На співбесіді тут слухають не завчений список, а розуміння порядку й того, що на кожній ланці може зламатися. Спершу імʼя перетворюється на адресу — працює DNS. Далі піднімається зʼєднання: тристороннє TCP, а для https ще й узгодження TLS. Тільки після цього їде сам запит із методом, шляхом і заголовками, а назад приходить статус, заголовки й тіло. Останній етап — браузер розбирає отримане й будує сторінку, дотягуючи дорогою стилі, скрипти й дані фоновими запитами. Сильна відповідь дописує до кожної ланки свій клас : не резолвиться імʼя, не встановлюється зʼєднання, не сходиться сертифікат, відповів замість застосунку, прийшла сторінка без даних. Повний ланцюг деталізує окрема глава — тут важливий сам скелет.
Навіщо в цьому ланцюгу DNS і чому після переїзду стенда тести ще довго стукають за старою адресою?
Людина оперує іменами, мережа маршрутизує за числами — DNS і є перекладачем між ними. Це розподілена ієрархічна система з простору імен, серверів імен і резолверів; резолвер — прошарок між застосунком і рештою системи: він приймає запит програми й за потреби йде далі ланцюжком до кореневих серверів, потім до серверів потрібної зони, потім до авторитетних серверів самого домену. Для тестів важливіша не механіка обходу, а кеш: кожен запис має TTL — дозволений строк зберігання, після якого його відкидають. Тому коли стенд переїхав на нову адресу, старий запис ще живий у кешах, і клієнти якийсь час ходять «у нікуди»; орієнтир строку для типового вузла — порядок днів. Висновок для баг-репорта простий: «нічого не працює одразу після зміни адреси» — це не завжди дефект, спершу перевіряють, що саме резолвиться зараз.
Навіщо потрібен порт, якщо є IP-адреса?
IP-адреса доводить пакети до потрібної машини, але на машині одночасно живуть десятки програм — треба ще сказати, кому саме. Це й робить порт: 16-бітне число в діапазоні від 0 до 65535, що вказує на конкретний сервіс. Пара «адреса + порт» називається сокетом, а зʼєднання однозначно визначається парою сокетів — своїм на кожному боці. Для стандартних схем порт можна не писати, бо мається на увазі типовий: 80 для http і 443 для https; будь-який інший доводиться дописувати руками — звідси й звичний :3000 на локальному стенді. Звідси дві дуже приземлені речі в роботі: «сервіс не піднявся» і «сервіс піднявся на іншому порту» дають однакову помилку зʼєднання, а фіксований порт у тесті, зайнятий попереднім незавершеним прогоном, дає address already in use — тому порт або підбирають динамічно, або вимагають чистого стану між прогонами.
Чому localhost іноді підводить, хоча «це ж та сама машина»?
Бо localhost — це імʼя, а не адреса, і резолвиться воно у loopback-адреси обох сімейств: 127.0.0.1 для IPv4 і ::1 для IPv6. Loopback означає «ця сама машина», трафік на нього в мережу взагалі не виходить. Пастка виникає, коли застосунок слухає лише IPv4-адресу, а клієнт пішов на ::1: адреса начебто правильна, а зʼєднання не встановлюється, і повідомлення про помилку виглядає безглуздо. Найшвидша перевірка — підставити явну адресу замість імені й побачити, чи зміниться поведінка; альтернатива з боку сервера — слухати обидва сімейства адрес.
Про що домовляються сторони, перш ніж піде корисний трафік?
Це фаза рукостискань, і на звичайному HTTPS-запиті їх щонайменше два. Спершу транспортне: TCP домовляється в три кроки — SYN, у відповідь SYN-ACK, потім ACK; четвертого кроку не треба, бо сервер укладає підтвердження й власний SYN в одне повідомлення. Друге — криптографічне, TLS: сторони автентифікуються, узгоджують параметри шифрування й отримують спільні ключі, а починається все з ClientHello, на який прилітає ServerHello. Ця ціна нараховується раз на зʼєднання, тому звернення до незнайомого хоста коштує більше за всі подальші: keep-alive далі працює вже у відкритому каналі. Практичний наслідок для тестів — падіння на цій фазі виглядають інакше за помилки застосунку: немає ні статус-коду, ні тіла, є лише помилка зʼєднання чи сертифіката, і шукати причину в коді продукту марно.
Що саме дає HTTPS порівняно з HTTP?
HTTPS — це той самий HTTP, загорнутий у захищений шар: спершу піднімається канал, а вже всередині нього ходить звичний трафік із тими самими методами, заголовками й кодами. Гарантій рівно три: автентифікація сторін, конфіденційність і . Важлива деталь, за яку дають бали: сервер автентифікується завжди, а клієнт — опційно, тому «HTTPS означає, що сервер знає, хто я» — хибне твердження. Для тестувальника з цього випливає межа відповідальності: усе, що ламається на етапі узгодження захищеного каналу (несходження імені в сертифікаті, прострочений сертифікат, непідтримувана версія протоколу), — це проблема інфраструктури, а не логіки застосунку.
Чим затримка відрізняється від пропускної здатності?
Це дві незалежні величини, і плутанина між ними дорого коштує. (latency) відповідає на питання «як довго дані добираються до місця призначення»; на практиці її міряють у два кінці, як round-trip. (bandwidth) — скільки інформації здатне пропустити зʼєднання за одиницю часу, і міряють її кратними бітам за секунду. Незалежність не теоретична: канал буває водночас «товстим» і «довгим» — у TCP є навіть добуток «пропускна здатність × затримка», а мережі з великим його значенням мають власний клас проблем продуктивності й окрему назву, long fat network. Діагноз від цього залежить прямо: довгий ланцюжок дрібних API-викликів гальмує саме затримка — кожен виклик оплачує власний round-trip, тож ширший канал тут нічого не змінить; натомість тягання великого файлу тримається на смузі, і рятує його зменшення обсягу даних. Саме тому оптимізації вебу здебільшого зменшують кількість походів у мережу: зʼєднань, мультиплексування в HTTP/2, , CDN, а HTTP/3 поверх QUIC ще й поєднує підняття транспорту з узгодженням TLS.
Чим проксі відрізняється від reverse proxy — і де тут балансувальник?
Посередників розрізняють за роллю, а не за тим, «з якого боку вони стоять», і саме на цьому валиться більшість відповідей. Проксі — агент пересилання, якого обирає клієнт; типово його ставлять, щоб звести трафік організації через спільний вузол заради безпеки або спільного кешу. (він же reverse proxy) грає протилежну роль: назовні він видає себе за origin-сервер, а всередину роздає запити іншим машинам; балансувальник навантаження — окремий випадок шлюзу, бо розподіл навантаження між машинами якраз і є типовим застосуванням цієї ролі. Третя форма — : ретранслятор наосліп, який у зміст повідомлень не втручається, і повноцінним учасником обміну його не рахують. І окремий нюанс, який цінують на співбесіді: вузол, якого клієнт не обирав — корпоративний фаєрвол, публічний Wi-Fi, — протокольно не відрізнити від на шляху, тому поведінка запиту може змінитися без жодної зміни в застосунку.
Прийшов 502. Де шукати причину і чим він відрізняється від 503?
502 і 504 за означенням віддає вузол, який діє як шлюз або проксі, тобто скарга йде на ланку за посередником: застосунок про такий запит міг узагалі не дізнатися. Шукати в його коді безглуздо — дивитися треба на балансувальник, шлюз і те, що стоїть за ними. 503 каже інше: сам сервіс тимчасово не здатен обробити запит — перевантаження або планові роботи. Практичний наслідок для команди: пачка однотипних 502 одразу після частіше означає, що середовище ще не піднялося, ніж що в продукті зʼявився дефект. Тому перший крок — перевірити стан стенда, і лише другий — заводити тікет на розробників.
Що означає «HTTP — безстановий протокол»?
Що за замовчуванням сервер не тримає в памʼяті нічого з попереднього спілкування: кожне нове звернення він читає з нуля. Строгіше формулювання таке — семантику окремого запиту можна зрозуміти ізольовано, а те, що повідомлення приїхали одним зʼєднанням, їхнє тлумачення не змінює. Мотив — масштабованість і простота: якщо серверу не треба памʼятати клієнтів, будь-яка з десятка однакових машин обробить будь-який запит. І тут головне уточнення, на якому найчастіше валяться: безстановий саме протокол, а не застосунок. Свій стан застосунок має майже завжди — кошик, профіль, права; зберігається він у базі, а решту доносить клієнт кожним новим запитом. Для тестів звідси випливає правило: токен чи кукі мусять їхати з кожним зверненням, а не «один раз при логіні».
Що таке ідемпотентний метод і чому наївний ретрай POST небезпечний?
означає, що повторення того самого запиту не додає нічого понад результат єдиного виклику; під це підпадають PUT, DELETE і всі . POST до них не належить, і це не формальність: він просить сервер обробити передані дані, тож повторне надсилання цілком законно створює ще один обʼєкт. Класична історія в CI виглядає так — запит відпав по , обгортка мовчки повторила його, сервер обробив обидва, і в базі зʼявився дубль, який потім ламає наступний тест на унікальності. Тому автоматичний безпечно вішати на , а для POST потрібен інший механізм — узгоджений із бекендом або перевірка перед повтором. Друга сторона тієї самої медалі: безстановий протокол сам собою не зберігає нічого між тестами, тож увесь спільний стан — записи в базі, кукі, глобальні — створюєш ти, і саме він потім тече між прогонами.
Як сайт памʼятає, що ти залогінений?
Памʼятає не протокол, а застосунок — за ключем, який клієнт сам приносить у кожному запиті. Класична схема: після входу серверна сторона лишає запис про сесію в себе — у памʼяті, Redis чи базі — і повертає браузеру самий лише ідентифікатор через Set-Cookie; далі рішення, чи причепити Cookie до чергового запиту, браузер ухвалює сам, спираючись на збережені метадані. Код сторінки в цьому не бере участі — саме тому кукі-сесія без жодних зусиль переживає перезавантаження вкладки. Альтернатива — токен: потрібні дані носить у собі він сам, тож серверу нема чого зберігати; лежати токен може або в кукі, або у сховищі браузера, і в другому разі жодної автоматики немає — заголовок Authorization формує код. Ця різниця дуже практична: зі сховища токен потрапляє в запит лише тоді, коли його туди поклав клієнтський код, тож «залогінився, а API каже 401» найчастіше означає саме це. Для тестів із цього народжується головна економія розділу: форму входу проходять один раз, а далі підставляють уже здобутий стан автентифікації.
Чим SPA відрізняється від MPA з погляду очікувань у тесті?
SPA завантажує один документ і далі перемальовує його вміст засобами JavaScript, підтягуючи дані фоновими запитами; адресою при цьому займається History API: history.pushState() і history.replaceState() переписують URL без походу на сервер. У MPA кожен екран є окремим документом, тож перехід означає, що браузер іде по новий документ і будує сторінку заново. Для тесту вся різниця зводиться до одного питання: чи існує тут момент «сторінка завантажилась». У MPA є чіткі навігаційні події, і дочекатися завершення переходу відносно просто. У SPA ці сигнали спрацьовують один раз на старті й потім більше нічого не означають, тому чекати доводиться на появу самого контенту — конкретного елемента чи стану. Звідси й типова помилка: «документ завантажився, отже дані на екрані» — у SPA хибний висновок, бо документ приїхав раніше за дані.
Коли потрібен WebSocket, а коли вистачить звичайного запиту?
Межа проходить по тому, хто має заговорити першим. Якщо ініціатива лишається за клієнтом, схема «запит — відповідь» покриває все. Якщо ж першим має озватися сервер — чат, нотифікації, живі оновлення статусу, — базового HTTP не вистачає, і до появи WebSocket це імітували постійним опитуванням сервера, (polling), який специфікація прямо називає зловживанням протоколом. WebSocket — двобічний обмін поверх одного TCP-зʼєднання; відкривають його звичайним GET, до якого додають Upgrade: websocket і Connection: Upgrade, а підтвердженням служить єдиний можливий статус 101 Switching Protocols. Після переходу перед нами вже окремий протокол зі схемами ws і wss, де ні заголовків, ні повторної автентифікації на кожне повідомлення не потрібно. Поруч живе простіший інструмент — Server-Sent Events: дані там рухаються лише в один бік, від сервера до клієнта, через інтерфейс EventSource і відповідь text/event-stream, а перепідключення вбудоване в сам механізм. Для тестів головне те, що кореляції «запит — відповідь» тут немає: чекати треба на подію-наслідок, а не на фіксовану паузу.
REST — це протокол?
Ні. REST — архітектурний стиль, тобто перелік обмежень, які Рой Філдінг наклав на взаємодію компонентів системи. Частину цих обмежень видно вже в базовій моделі вебу: client-server (розділення відповідальностей), stateless (запит несе все потрібне сам) і layered system — компонент не бачить далі сусіднього шару, тому на шляху й можуть стояти посередники. Решта — кешованість, необовʼязковий code on demand і, найголовніше, єдиний інтерфейс із чотирьох складників: ресурси ідентифікуються, змінюють їх через представлення, повідомлення самоописові, а рушієм стану служить гіпермедіа. Практично стиль дає простий поділ ролей: що — визначає адреса ресурсу, а що з ним робимо — метод. За GET закріплено отримання представлення, POST віддає надіслані дані серверові на обробку, PUT або створює ресурс, або повністю переписує його стан, а DELETE просить розірвати звʼязок між ресурсом і його функціональністю. Тому питання «чи RESTful цей API» коректно розбирати як питання про дотримання обмежень, а не про наявність JSON у відповіді.
Інфраструктура переїхала на HTTP/2 або HTTP/3. Що з цього побачать тести?
Семантика від версії не залежить: метод, шлях, заголовки й статус лишаються ті самі, бо специфікація навмисно вимагає, щоб повідомлення можна було ретранслювати іншою версією без зміни змісту. Тому в панелі мережі ти бачиш звичні поля незалежно від того, що на дроті, і переписувати асерти не доводиться. Змінюється кодування: HTTP/1.1 текстовий і читається очима, а в HTTP/2 ті самі повідомлення вкладені у двійкові кадри — MDN перелічує три відмінності: двійкова форма, мультиплексування (кілька запитів паралельно в одному зʼєднанні) і стиснення заголовків. HTTP/3 живе поверх QUIC, а кадрування на кожному його потоці зроблено схожим на те, що є в HTTP/2. Щодо поширення: за даними, на які посилається MDN, пік вживання HTTP/2 припав на січень 2022 і склав 46,9 % усіх сайтів, а частка сайтів на HTTP/3 у жовтні 2022 становила 26 %. Найважливіший для QA наслідок інший: перехід не потребує змін у коді застосунку, тому команда може про нього навіть не оголосити — а поведінка мережі під тестами зміниться сама, і плаваючі падіння звідти виглядатимуть як продукту.
Три кейси, які тримаються на одній думці глави: тест — такий самий клієнт, як браузер. Спершу карта ланок, за якою «незрозуміле» падіння на CI розкладається на конкретного підозрюваного; далі — як користуватися роллю клієнта свідомо й дешево готувати стан; наостанок — де модель «запит — відповідь» перестає працювати і на що тоді чекати.
Кейс 1. «Локально зелено, на CI червоно»: карта ланок
Між твоїм expect і кодом застосунку стоїть довгий ланцюг, якого в тесті не видно: імʼя, адреса, порт, зʼєднання, , . Більшість «містичних» падінь живе саме там, і впізнаються вони за симптомом ще до читання логів застосунку. Порядок перегляду — вздовж ланцюга, від імені до застосунку.
| Симптом | Підозрювана ланка | Дешева перевірка |
|---|---|---|
| Імʼя не резолвиться або тест ходить за старою адресою після переїзду стенда | DNS і TTL запису в кеші | резолвити імʼя вручну й порівняти адресу з очікуваною; памʼятати, що орієнтир джерела для типового вузла — порядок днів |
Відмова у зʼєднанні на localhost, хоча сервіс «точно піднятий» | адреса й порт: імʼя веде і на 127.0.0.1, і на ::1 | підставити явну адресу замість імені; звірити, який саме порт слухає процес |
| Помилка сертифіката: немає ні статус-коду, ні тіла | фаза TLS | відкрити той самий URL браузером і подивитись сертифікат; перевірити системний час агента |
Всі ендпоінти віддають 502 чи 504 одразу після деплою | ланка за посередником — шлюз, балансувальник, сервіс за ними | перевірити стан стенда й версію застосунку до старту сюїти, а не заводити тікет на продукт |
Ендпоінти віддають 503 | сам сервіс: перевантаження або планові роботи | звернутися тим самим запитом за хвилину; звірити вікно робіт на середовищі |
| З корпоративної мережі поведінка запиту інша, ніж з дому | посередник, якого клієнт не обирав | подивитися змінні оточення проксі на агенті; повторити запит з машини поза мережею |
| Перші звернення повільні, далі все швидко | ціна першого зʼєднання: DNS, TCP, TLS | порівняти час першого й наступних запитів до того самого хоста |
UI-логін пройшов, а API-запит віддає 401 | токен зі сховища не поїхав: у Authorization його кладе код, а не браузер | подивитись у панелі мережі, чи є заголовок у падаючому запиті |
Три висновки, які економлять години.
- Клас симптому вже називає винного. Відсутність означає, що до застосунку не дійшло — ламалося зʼєднання, імʼя або сертифікат. Наявність статусу означає, що хтось відповів; лишається зʼясувати хто — застосунок чи посередник перед ним.
502і504— не тікет на розробників за замовчуванням. Обидва коди за означенням віддає вузол у ролі чи , тобто скарга йде на ланку за ним, і застосунок про такий запит міг узагалі не дізнатися.503— інша історія: тут озвався сам сервіс і сказав, що зараз не може.- «У мене працює» майже завжди означає іншу ланку, а не інший код. Локальна машина й агент CI відрізняються резолвом імен, доступом крізь проксі, набором сертифікатів і тим, які порти вже зайняті. Тому перше питання до червоного — не «що змінили в застосунку», а «чим відрізняється шлях запиту».
Кейс 2. Тест як клієнт: дешева передумова й один логін
Якщо тест — клієнт, то він може ходити двома дорогами, і вибір дороги вирішує, наскільки він крихкий. Клацання формою залежить від верстки, анімацій і ; той самий результат через API від них не залежить взагалі. Звідси робочий поділ: стан готуємо запитом, перевіряємо — інтерфейсом.
import { test, expect } from '@playwright/test';
test('щойно створене замовлення видно на його сторінці', async ({ page, request }) => {
// передумова одним запитом замість проходу по формах
const created = await request.post('/api/orders', {
data: { sku: 'SOME-SKU-1', qty: 2 },
});
// передумова мусить падати голосно й окремо від самої перевірки
expect(created.status(), 'замовлення не створилося — це не баг UI').toBe(201);
const { id } = await created.json();
await page.goto(`/orders/${id}`);
await expect(page.getByTestId('order-status')).toHaveText('Нове');
});
Логін — окремий випадок тієї самої ідеї. Протокол не памʼятає нічого між запитами, тож стан автентифікації тримає клієнт: кукі або те, що лежить у сховищі браузера. Значить, його достатньо здобути один раз і покласти у файл.
// auth.setup.ts — відпрацьовує один раз перед сюїтою
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('здобути стан автентифікації', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill(process.env.USER_EMAIL!);
await page.getByLabel('Пароль').fill(process.env.USER_PASSWORD!);
await page.getByRole('button', { name: 'Увійти' }).click();
// чекаємо наслідок входу, а не факт навігації
await expect(page.getByTestId('user-menu')).toBeVisible();
// у файл їдуть і кукі, і сховище браузера
await page.context().storageState({ path: authFile });
});
Що дивитися і чому:
- Статус окремо. Без цього невдале створення замовлення виявиться аж на перевірці інтерфейсу й прочитається як дефект UI. Асерт на
201перетворює туманне падіння на точне: не створилося — і далі йти немає сенсу. POSTне ідемпотентний, тому передумову не можна наївно ретраїти. Повторення того самого створення законно дає другий обʼєкт, а не той самий; на нестабільній мережі це тихо породжує дублі, які потім валять перевірку унікальності в сусідньому тесті. Якщо потрібен — потрібен і узгоджений з бекендом ключ, за яким сервер розпізнає повтор.- Збережений стан покриває обидва механізми памʼяті про логін, і це не дрібниця. Кукі-сесія їде автоматично, бо її додає браузер; токен, покладений у сховище, летить лише тоді, коли клієнтський код сам поставив його в
Authorization. Якщо застосунок тримає токен у сховищі, а тест дивує401при живих кукі — дивитись треба саме сюди. - Дані для передумови роблять унікальними на прогін. Спільний стан між тестами створює не протокол, а ти: фіксований email чи артикул на другому прогоні законно впреться в конфлікт із тим, що вже лежить у базі.
Кейс 3. Коли «запит — відповідь» перестає працювати
Модель «клацнув — приїхала сторінка» описує MPA. У SPA документ приходить один раз, а далі вміст приносять фонові запити, і старий сигнал завантаження більше нічого не означає. У реалтаймі ще складніше: сервер озивається сам, і корелювати відповідь із запитом просто нема з чим.
// крихко: документ у SPA не перезавантажується, тож чекати завершення навігації нема чого
await page.getByRole('link', { name: 'Замовлення' }).click();
await page.waitForLoadState('load'); // спрацює миттєво й нічого не гарантує
await expect(page.getByTestId('order-row').first()).toBeVisible();
// надійно: чекаємо наслідок дії — потрібну відповідь і потрібний стан екрана
await Promise.all([
page.waitForResponse((r) => r.url().includes('/api/orders') && r.ok()),
page.getByRole('link', { name: 'Замовлення' }).click(),
]);
await expect(page.getByTestId('order-row').first()).toBeVisible();
У реалтаймі роль «наслідку» грає кадр, який прилетів сам, без жодного запиту з боку тесту.
test('статус замовлення оновлюється пушем із сервера', async ({ page }) => {
const wsPromise = page.waitForEvent('websocket');
await page.goto('/orders/42');
const ws = await wsPromise;
// чекаємо конкретну подію, а не фіксовану паузу
await ws.waitForEvent('framereceived', (frame) =>
String(frame.payload).includes('order.paid'),
);
await expect(page.getByTestId('order-status')).toHaveText('Оплачено');
});
Що дивитися і чому:
- «Сторінка завантажилась» у SPA не означає «дані на екрані». Документ приїхав раніше за них, тому чекати треба контент — конкретний елемент або конкретну відповідь, — а не подію завантаження, яка вже давно відбулася.
- Навігація в SPA може взагалі не породити запиту на сервер. Адресу міняє History API, тож URL у рядку оновився, а мережею не пішло нічого; асерт на URL тут доводить менше, ніж здається.
- У реалтаймі фіксована пауза — це ставка, а не очікування. Подія приходить тоді, коли її надіслав сервер;
waitForTimeoutабо дає flaky-тест, або дарма подовжує прогін. Чекають саме подію-наслідок. - Кадр із сокета доводить менше, ніж стан екрана. Він каже, що сервер повідомив, а не що застосунок це показав. Тому кадр — проміжна синхронізація, а фінальний асерт лишається на інтерфейсі.
Ролі й форма обміну
- Можу пояснити асиметрію моделі — ініціативу тримає клієнт, сервер у класичній схемі першим не озивається — і назвати мотив поділу: інтерфейс окремо від зберігання даних заради переносності й масштабування.
- Розумію, чому автотест стоїть у моделі там само, де браузер чи
curl, і вмію назвати наслідок: усе, що ламає браузер на стенді, ламає й тест. - Памʼятаю склад запиту (метод, шлях, заголовки, тіло) і відповіді (статус у межах 100–599, заголовки, тіло) і знаю точне формулювання «один запит — одна відповідь»: рівно одна фінальна, якій можуть передувати проміжні
1xxна кшталт100 Continueчи103 Early Hints.
Адреси, порти й зʼєднання
- Розумію, чому після переїзду стенда тести ще стукають за старою адресою: винен TTL запису в кеші DNS, а орієнтир для типового вузла — порядок днів.
- Знаю різницю IP vs порт (машина vs сервіс на ній), памʼятаю діапазон
0–65535і типові порти80/443, які можна не писати. - Розумію пастку
localhost: імʼя веде і на127.0.0.1, і на::1, тож сервіс, який слухає лише IPv4, для клієнта, який пішов на IPv6, просто не існує — рятує явна адреса замість імені. - Можу описати, що відбувається до першого байта корисного трафіку: TCP (
SYN→SYN-ACK→ACK) і TLS ізClientHello/ServerHello, а також три гарантії TLS — автентифікація (сервер завжди, клієнт опційно), конфіденційність, цілісність.
Затримка, смуга й посередники
- Знаю різницю vs пропускної здатності, вмію показати їхню незалежність (добуток «смуга × затримка», long fat network) і поставити діагноз: десятки дрібних викликів упираються в затримку, велике завантаження — у смугу; окремо памʼятаю, чому перший запит до хоста дорожчий за наступні і що саме економить keep-alive.
- Можу пояснити, чому оптимізації вебу зменшують саме кількість round-trips: зʼєднань, мультиплексування HTTP/2, , CDN, обʼєднання підняття транспорту з узгодженням TLS у QUIC.
- Знаю різницю vs за роллю, а не за місцем: проксі призначає клієнт, шлюз для зовнішнього світу виглядає як сам origin-сервер, балансувальник — окремий випадок шлюзу, а лише сліпо ретранслює.
- Не плутаю
502/504з503: перші два віддає вузол у ролі шлюзу чи проксі (зламалася ланка за ним),503каже про сам сервіс — і памʼятаю, що вузол, якого клієнт не обирав, протокольно не відрізнити від на шляху, тож поведінка запиту може змінитися без жодної зміни в застосунку.
Безстановість і стан застосунку
- Можу сформулювати stateless точно: семантика запиту зрозуміла ізольовано, а факт спільного зʼєднання на тлумачення повідомлень не впливає — і додати ключове уточнення, що безстановий тут протокол, а не застосунок.
- Знаю означення , памʼятаю, що
PUT,DELETEі такими є, і можу пояснити, чому наївнийPOSTстворює дубль, який потім ламає сусідній тест. - Памʼятаю зворотний бік ізоляції: спільний стан — записи в базі, кукі, глобальні — створюю я сам, і протікає між прогонами саме він.
- Можу описати механізм памʼяті про логін: сервер тримає запис сесії у себе, віддає ключ через
Set-Cookie, браузер сам повертає його заголовкомCookie. - Знаю різницю кукі-сесії vs токена у сховищі (перша їде автоматично й переживає перезавантаження, другий летить лише тоді, коли код сам поклав його в
Authorization) і логінюся один раз із перевикористанням збереженого стану.
SPA, реалтайм і версії протоколу
- Знаю різницю SPA vs MPA і наслідок для очікувань: у MPA є навігаційні події, у SPA документ приїхав раніше за дані, тому чекають появу контенту, а
history.pushState()міняє URL без запиту на сервер. - Можу назвати межу застосування WebSocket — потрібен, коли першим має заговорити сервер; відкривається
GET-запитом зUpgradeі підтверджується статусом101— і відрізнити його від SSE (однобічний канал черезEventSourceіtext/event-stream). - Можу пояснити, що REST — архітектурний стиль, а не протокол, назвати його обмеження й поділ ролей: адреса каже, що за ресурс, метод — що з ним робимо.
- Знаю, що семантика повідомлення не залежить від версії HTTP (змінюється кодування: текст у 1.1, двійкові кадри й мультиплексування у 2, QUIC у 3) і що перехід не потребує змін у коді застосунку — тому про нього можуть не попередити.
Квіз
Перед стартом
- Питань: 16
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Що робить сторону клієнтом у моделі клієнт-сервер?



