Часть первая, GraphQL
Часть вторая,(вы здесь)
Часть третья, gRPC
Привет! Продолжаем цикл статей про тестирование не-REST-бэкенда, в прошлый раз мы говорили о GraphQL, теперь пришло время WebSocket.
Итак, что такое WebSocket?
![](https://habrastorage.org/getpro/habr/upload_files/9fb/8f0/635/9fb8f0635306ca448dd1235f7326e9f2.jpg)
Википедия сообщает, что это «протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером, использующий постоянное соединение».
Что тут важно — что это протокол (со всеми вытекающими последствиями для протокола), который использует постоянное соединение.
Работу по WebSocket в обычной жизни можно представить примерно так.
Вы живете в квартире вместе с семьей, решили поработать, ушли в отдельную комнату и закрыли за собой звуконепроницаемую дверь (ну а как еще работать-то). В общем, сидите, работаете, и вообще не слышите происходящего в квартире. Заметите только, если вам кто-то постучить в дверь.
И тут к вам в дверь стучит, скажем, жена, вы открываете, и она говорит, что ей надо уехать по делам вместе с остальными членами семьи, вы в доме за старшего, и надо будет встретить курьера, который скоро приедет.
ОК, что вам делать в такой ситуации? Как минимум, открыть свою крутую звуконепроницаемую дверь, чтобы услышать звонок курьера в дверь квартиры, и продолжать работать. Когда он придет и позвонит, вы услышите и среагируете.
Собственно, вот так работает WebSocket.
А если бы мы взаимодействовали через REST-HTTP, всё выглядело бы немного иначе.
Вы бы подходили к закрытой звуконепроницаемой двери, открывали ее, убеждались, что курьер не обрывает дверной звонок, возвращались обратно поработать 20 секунд, снова вставали и подходили к двери, слушали, как там дела с курьером, и снова уходили работать на еще одни 20 секунд. И так бегали бы туда-сюда, пока бы не пришел курьер или пока не протоптали бы ламинат.
У меня, кстати, был случай — мне надо было через мобильное приложение срочно обратиться в медицинское учреждение по страховке. Я открыл приложеньку, вошел в чат с оператором, оператор попросил уточнить подробности. Сижу, пишу подробности — и у меня моргает чат, вижу вместо своего сообщения пустое поле. Думаю — ну ладно, бывает, пишу снова. Опять через полминуты то же самое. И в третий раз тоже.
Я в этот момент начинаю выглядеть как-то так.
![](https://habrastorage.org/getpro/habr/upload_files/9f1/186/c8c/9f1186c8c4469ea58b3cb2b2fe4498d2.jpeg)
Но потом вспоминаю, что я в первую очередь инженер, и мне интересно понять «А почему так?». Сижу и думаю — неужели они реально используют HTTP? Подключаю мобилку к компу, смотрю трафик — и правда, оказывается, мобильный клиент раз в 25 секунд шлет HTTP-запрос на сервер и уточняет, есть ли в чате новый ответ от оператора. Если нет, то он зачем-то перерисовывает фронт. Заодно дропая все введенные мною данные. Видимо, ребята не тестировали это. И зря.
Так вот, WebSocket, помогает системам, которые хотят работать в режиме реального времени. Если есть какие-то данные, которые нужно отправить клиенту, сервер знает, что есть вот сокет соединения, их можно туда отправить и они успешно туда улетят. Клиент же спокойно работает и знает, что если прилетят данные через конкретный сокет, надо будет с ними выполнить операции. В общем, все спокойно работают и, если надо, обмениваются сообщениями, никто никого не ждет.
Как устанавливается это соединение
Процесс работы по Websocket выглядит так. Сперва посылается обычный HTTP-запрос на установку постоянного соединения— это вот когда к вам в дверь постучала жена. Но у запроса должно быть два очень важных технических заголовка.
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Первый – это connection upgrade. Тут мы говорим серверу, что готовы перенести наши отношения на новый уровень, более постоянный и вообще хороший. Второй — мы указываем, что мы хотим сделать апгрейд на WebSocket.
Если сервер может, он отвечает — хорошо, это будет код ответа 101, и служебные заголовки смены протокола.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMl YUkAGmm50PpG2HaGWk=
Sec-WebSocket-Protocol: chat'
Укажет, на какой протокол поменялся, какой сокет будет использоваться и прочую информацию. Это аналог того, что вы открыли дверь и начинаете слушать звонок курьера, продолжая при этом делать свои дела.
После этого между клиентом и сервером устанавливается тип соединения connection, они начинают общаться.
HTTP Connect is upgrading to WS
TYPE: Connection
Если вдруг кто-то там отвалился или закрыл соединение, тип коннекта меняется на дисконнект.
WS connection close
TYPE: Disconnect
И здесь сразу возникает вопрос — а как сервер поймет, что клиент все еще там?
Представьте себе пневмопочту.
Откуда вы знаете, что по ту сторону прямо сейчас еще сидит человек, который сразу принимает ваши посылки? А вдруг он ушел уже, и вы впустую все это шлете.
![](https://habrastorage.org/getpro/habr/upload_files/7a8/3af/169/7a83af169ca30001573cc9873d689e1a.jpeg)
Вот на этот случай у WebSocket-протокола есть специальный механизм пинг-понгов — он всегда может уточнить, “слышно” ли его и стоит ли продолжать работу. Если вдруг кто-то не ответил, происходит несколько попыток (с увеличивающимся таймаутом), и после этого соединение считается закрытым. У WebSocket для этого есть свои коды ответов, которые все так или иначе завязаны именно на закрытие соединения.
Status Code | Meaning |
1000 | Normal Closure |
1001 | Going Away |
1002 | Protocol error |
1003 | Unsupported Data |
1004 | Reserved |
1005 | No Status Rcvd |
1006 | Abnormal Closure |
1007 | Invalid frame payload data |
1008 | Policy Violation |
1009 | Message Too Big |
1010 | Mandatory Ext. |
1011 | Internal Error |
1012 | Service Restart |
1013 | Try Again Later |
1014 | The server was acting as a gateway or proxy and received an invalid response from the upstream server. This is similar to 502 HTTP Status Code. |
1015 | TLS handshake |
1016-2999 | Unassigned |
3000 | Unauthorized |
3001-3002 | Unassigned |
3003 | Forbidden |
3004-3999 | Unassigned |
4000-4999 | Reserved for Private Use |
Как с ним работать в Postman
На одном из своих прошлых проектов я как раз тестировал поиск на WebSocket, так что на этом примере и разберемся.
Запустим Google Chrome, откроем консоль разработчика (F12), перейдем на сайт https://www.onetwotrip.com/ru/bus/ и поищем билеты Москва-Тверь на дату в будущем. Дожидаемся окончания загрузки и переходим во вкладку Network
Увидим следующую картину. Как видите, везде коды 200, то есть обычные HTTP-запросы.
![](https://habrastorage.org/getpro/habr/upload_files/0a7/fb2/0bc/0a7fb20bcb4bd6169bae60a609588ef2.png)
Где ловить WebSocket запрос? Он находится на вкладке WS, WebSocket
![](https://habrastorage.org/getpro/habr/upload_files/e09/b1f/629/e09b1f629653d318c4a9493c2515876e.png)
Тут видно, что протокол SSL-ный, защищенный и есть вся информация.
![](https://habrastorage.org/getpro/habr/upload_files/fa6/da0/05c/fa6da005cd2a52495c668956aad59437.png)
Есть и вкладка payload — показывает, что мы отправили какие-то данные.
![](https://habrastorage.org/getpro/habr/upload_files/6f5/541/cd7/6f5541cd791039c1cb22c2a340145668.png)
Есть вкладка Messages, где мы видим все сообщения, которыми обменивались клиент и сервер.
![](https://habrastorage.org/getpro/habr/upload_files/21b/0a0/0c5/21b0a00c5d83ad61c00eec6ca92f7271.png)
Здесь мы сначала сделали connection, потом мы отослали на сервер ОК, и он вернул нам данные в четыре захода
Как это все делать и протестировать в Postman?
Нажимаете в Postman кнопку New, видите WebSocket.
![](https://habrastorage.org/getpro/habr/upload_files/426/d4e/d86/426d4ed8632369b1a54cc22f92bc3b0e.png)
Выбираете его, и у вас появляется такой экран. Тут есть уже месседжи, параметры, хедеры и прочее.
![](https://habrastorage.org/getpro/habr/upload_files/92f/7f8/7af/92f7f87af3cacdfa066128c0c4549d2f.png)
Давайте потестируем.
В адрес вводим wss://www.onetwotrip.com/_bus/ws/run/search?startGeoId=16&endGeoId=100&date=2023-08-10&adults=1&children=0
Если дата в параметре date устарела, меняем на будущую. Postman сам проставит параметры и заголовки, но в Headers все же надо самим добавить еще и Origin — https://www.onetwotrip.com
![](https://habrastorage.org/getpro/habr/upload_files/226/1c4/c11/2261c4c1166b6d614dc14ce388979c26.png)
Нажимаем, как всегда, кнопочку connect, и видим, что все остановилось. Почему?
Смотрим вниз окна и форму Responses, где нас уже ждет ответ.
![](https://habrastorage.org/getpro/habr/upload_files/ad5/4c4/097/ad54c409730655be691e7efb00762291.png)
Раскрываем его
![](https://habrastorage.org/getpro/habr/upload_files/0cd/a2d/b7d/0cda2db7dad99b620eb78fbf76cb2ddd.png)
Видим, что сперва был послан сначала обычный HTTPS-запрос на установку Websocket-соединения со всеми служебными заголовками: connection upgrade, WebSocket, все как надо.
![](https://habrastorage.org/getpro/habr/upload_files/44e/e63/989/44ee639897aa54c9fe0eb3df869d1880.png)
Сервер прислал мне в ответ 101, мол, я согласен, давай проапгрейдим наши отношения на WebSocket. Он выдал мне сокет и все остальное, включая куки.
![](https://habrastorage.org/getpro/habr/upload_files/55a/8c7/34a/55a8c734a63de9f7b62f577bfe5c6fad.png)
При этом статус нашего соединения connected.
![](https://habrastorage.org/getpro/habr/upload_files/992/a9f/a27/992a9fa276936db6d584bbbc7c52eb4f.png)
Теперь с ним можно работать, сервер ждет от нас сообщение
Пошлем ему ОК. Для этого во вкладке Messages напишем “ОК” и нажмем кнопку Send.
![](https://habrastorage.org/getpro/habr/upload_files/bf1/868/f5e/bf1868f5e7f31e79b40ae4fdd40e2366.png)
В ответ увидим несколько ответов от сервера, в рамках которых он отгружал нам данные, и в конце, когда данные закончились, он закрыл соединение.
![](https://habrastorage.org/getpro/habr/upload_files/98f/c2b/132/98fc2b1326cb3c3371882a4ab6dce018.png)
Это сигнал для клиента к отключению соединения. Если раскрыть это сообщение, увидим, что мы завершились с кодом 1000 — это нормальное завершение.
![](https://habrastorage.org/getpro/habr/upload_files/3f9/4d9/ee8/3f94d9ee857ecb3f15368d5f9d30519f.png)
Собственно, в этом и заключается вся магия вебсокета. Во-первых, нам нужно установить вебсокет-соединение: посылаем обычный http-запрос (Postman это, кстати, делает автоматически). Далее посылаем данные в том формате, который ждет сервер, и получаем от него информацию в ответ. В конце еще получим какие-то определенные коды, связанные с закрытием коннекта.
![](https://habrastorage.org/getpro/habr/upload_files/3ed/d34/01f/3edd3401ffd41e9372e78e4b8df74647.png)
Что можно тестировать?
Прежде всего — сам факт установки соединения. Потому что, может быть, сервер откажется соединяться, или он отключен, или умеет только в http.
Обязательно не забудьте проверить security, SSL никто не отменял и установку по WSS тоже нужно проверить.
Далее, как в классическом тестировании backend api, — проверяем формат ответа и сами данные, которые внутри.
Как написано выше, у вебсокета есть свои коды ответов. Тестировать или нет их — зависит от обстоятельств. Если вам это действительно нужно, ведь у вас есть такой функционал завязанный на это, — то вперед! В своей практике я с тестированием кодов ответа почти не сталкивался.
Ну, и, наконец, доступность метода. Потому что метод может быть на самом деле закрыт авторизацией, и нужно будет какой-нибудь токен прицепить к запросу.
С вебсокетом на этом всё. Для тех кто еще с нами — в третьей части поговорим про самое сложное из цикла. Про gRPC.