Transport: WebSocket, tryb awaryjny HTTP i ponowne łączenie
Jak widżet rozmawia z serwerem czatu – adresy, podprotokół rc.v1, format ramek, kody błędów i kody zamknięcia, limity, ponowne łączenie z seq i deduplikacją oraz tryb awaryjny HTTP przez Server-Sent Events i long polling.
Ta strona opisuje, co dzieje się między widżetem w przeglądarce a serwerem czatu. Przyda ci się przy szukaniu błędów (zakładka „Sieć” narzędzi deweloperskich), przy odblokowaniach w proxy i firewallu oraz przy twojej Content Security Policy.
Ważne: protokół sieciowy jest interfejsem między naszym widżetem a naszym serwerem. Własne klienty, które mówią tym protokołem bezpośrednio, nie są oficjalnie wspierane; pola mogą się rozszerzać w ramach wersji protokołu 1. Do własnych interfejsów użyj API JavaScript, do integracji serwerowych webhooków i API serwera.
Adresy
- WebSocket:
wss://ws.reactive.chat/rc_wss/ - Tryb awaryjny HTTP, formularz offline, przesyłanie plików: ten sam adres z
https://, czylihttps://ws.reactive.chat/rc_wss/h/…,https://ws.reactive.chat/api/offline/<SITE_KEY>ihttps://ws.reactive.chat/rc_wss/upload/… - Pliki widżetu:
https://cdn.reactive.chat/
Jak widżet znajduje adres WebSocket:
data-rc-ws-urlw tagu loadera,window.ReactiveChatSettings.wsUrlalboReactiveChat('boot', { wsUrl })– jeśli ustawiono, obowiązuje ta wartość.- W przeciwnym razie adres jest wyprowadzany z miejsca, z którego pochodzi
loader.js:wss://ws.+ ostatnie dwa człony domeny +/rc_wss/. Zcdn.reactive.chatpowstajewss://ws.reactive.chat/rc_wss/.
Wynikają z tego dwie pułapki:
- Jeśli serwujesz
loader.jsz własnej domeny, widżet wyprowadza adres w twojej domenie. Ustaw wtedydata-rc-ws-url(zobacz Szybki start). - Przy dwuczłonowych końcówkach takich jak
.co.ukwyprowadzenie daje zły wynik. Ustawdata-rc-ws-urlwyraźnie.
Parametrów query droga WebSocket nie analizuje.
Nawiązywanie połączenia
- Moment połączenia: widżet nie łączy się już podczas ładowania strony, lecz przy pierwszej rzeczywistej interakcji (ruch myszy, kliknięcie, naciśnięcie klawisza, kółko myszy, dotknięcie, fokus). Łączy się od razu, gdy okno czatu jest otwarte lub właśnie otwierane, znana jest rozmowa odwiedzającego albo czekają niewysłane wiadomości. Atrybut
data-rc-connect="load"na dowolnym elemencie (zwykle na tagu loadera) lubwindow.ReactiveChatSettings.connect = 'load'przywraca łączenie przy ładowaniu. Reguły uruchamiane określony czas po wyświetleniu strony liczą czas od pierwszej interakcji. - Podprotokół: widżet otwiera
new WebSocket(url, 'rc.v1'), serwer odpowiada tym samymrc.v1. Wersja protokołu nie jest w adresie, tylko w każdej ramce (v: 1). - Tylko ramki tekstowe w UTF-8, jedna ramka = jedna wiadomość. Bez kompresji (
permessage-deflatejest wyłączone po stronie serwera). - Uzgadnianie: po
openwidżet wysyłainitnajpóźniej po 5 sekundach; serwer odpowiadainit_ok. Jeśliopennie nastąpi w ciągu 8 sekund, próba uchodzi za nieudaną; jeśliinit_oknie przyjdzie w ciągu 10 sekund, widżet nawiązuje połączenie od nowa. - Kontrola pochodzenia: serwer porównuje nazwę hosta z nagłówka
Originz domenami projektu (Dozwolone źródła w koncie). Schemat i port się nie liczą; subdomeny pasują (www.deine-firma.depasuje dodeine-firma.de,nichtdeine-firma.denie). Przy niepowodzeniu: błądorigin_denied, potem Close 1008, bez ponownej próby. Jeśli w projekcie nie wpisano żadnej domeny, kontrola pochodzenia się nie odbywa – dlatego zawsze wpisuj swoje domeny.
Format ramki
Każda wiadomość w obu kierunkach ma taką postać:
{ "v": 1, "t": "msg", "id": "01JAB3C4D5E6F7G8H9JKMNPQRS", "ts": 1792500000000, "d": { } }
- v (liczba całkowita, obowiązkowe) – wersja protokołu, obecnie
1. - t (String, obowiązkowe) – typ wiadomości,
[a-z0-9_]{1,24}. - id (String, obowiązkowe) – ULID: 26 znaków Crockford Base32, tylko wielkie litery. Jest zarazem kluczem deduplikacji.
- ts (liczba całkowita, obowiązkowe) – milisekundy od 1970, tylko do diagnostyki.
- d (obiekt, obowiązkowe) – dane, nigdy
null, puste jako{}.
Nieznane typy i nieznane pola są po cichu ignorowane – dzięki temu starsze widżety pozostają zgodne z nowszymi serwerami.
Typy wiadomości w skrócie
Od widżetu do serwera:
- init – rejestracja połączenia: klucz witryny, identyfikator sesji, odwiedzającego, karty i rozmowy, język, stan zgody, ewentualnie podpisana tożsamość (
user), kontekst strony (ctx) i punkt wznowienia (resume). - msg – wiadomość odwiedzającego (tekst, do 5 załączników, odpowiedź na szybkie odpowiedzi lub formularze).
- typing – wskaźnik pisania (
start/stop). Treść pola wiadomości nigdy nie jest przesyłana. - read – przeczytane do
seq. - ctx – zmiana kontekstu, np. przy nawigacji w SPA albo po
identify. - csat – ocena (kciuk w górę/w dół, komentarz do 1000 znaków).
- upload_req – prośba o zgodę na przesłanie pliku (nazwa, typ MIME, rozmiar, SHA-256).
- transcript, forget, consent – historia rozmowy e-mailem, usunięcie własnych danych, udzielenie/wycofanie zgody.
- ping – sygnał podtrzymujący.
Od serwera do widżetu:
- init_ok – potwierdzone identyfikatory, historia, wygląd, funkcje i limity (zobacz niżej), wynik weryfikacji tożsamości (
identity.verified). - msg – wiadomości (także echo własnej wiadomości z
client_id). - bot_typing, bot_stream – asystent AI myśli lub pisze;
bot_streamprzesyła zawsze cały dotąd widoczny tekst, najwyżej co 150 ms. - typing, read, handoff, queue_pos, proactive, conv_list, error, pong.
Kody błędów
Błąd przychodzi jako ramka t: "error" z d.code, d.message (po angielsku, tylko dla programistów), d.retry (no, immediate, backoff, after_reinit) i w razie potrzeby d.retry_after_ms oraz d.detail. Lista jest zamknięta; nieznany kod jest traktowany jak internal.
- bad_json – nieprawidłowy JSON; po trzech przypadkach Close 1008.
- bad_envelope – ramka narusza format (także: ULID odbiega o ponad 24 h,
detail.reason: stale_or_future). - unsupported_version – wersji nie da się wynegocjować; Close 4001.
- unknown_site_key – nieznany klucz witryny, projekt wstrzymany lub zarchiwizowany; Close 4002.
- origin_denied – pochodzenie spoza domen projektu; Close 1008.
- visitor_blocked – odwiedzający zablokowany; Close 1008.
- auth_failed – weryfikacja tożsamości nie powiodła się,
detail.reason:no_hash,bad_hash,bad_exp,expired,exp_too_far. Sesja trwa dalej anonimowo. - not_initialized / already_initialized – wiadomość przed inicjalizacją albo drugie
init. - init_timeout – brak
initw ciągu 5 s; Close 4004. - conversation_not_found / conversation_closed
- rate_limited – ograniczenie częstotliwości; z
retry_after_ms. Jeśli powtarza się na tym samym połączeniu, serwer zamyka je kodem 4008. - payload_too_large – ramka powyżej 64 KiB.
- msg_too_long – tekst dłuższy niż limit planu,
detail.max. - unsupported_kind, attachment_invalid
- upload_disabled, upload_too_large, upload_mime_denied, upload_expired
- consent_required – akcja wymaga zgody.
- dnd_active – aktywne jest „Nie przeszkadzać”.
- session_conflict – za dużo połączeń tej samej sesji (ponad 3); Close 4003, karta przechodzi w tryb pomocniczy.
- internal, unavailable – przejściowe; ponowna próba z odczekaniem.
Kody zamknięcia
- 1000, 1001, 1006, 1011, 1013, 4004 – widżet łączy się ponownie (z odczekaniem).
- 1008 – odrzucone pochodzenie, powtarzający się nieprawidłowy JSON, blokada przez ochronę przed nadużyciami. Brak ponownej próby na tej stronie.
- 4001 – wersji nie da się wynegocjować. Brak ponownej próby.
- 4002 – nieznany klucz witryny albo nieaktywny projekt. Brak ponownej próby.
- 4008 – powtarzające się ograniczenie częstotliwości. Widżet czeka
retry_after_msi łączy się ponownie. - 4003 – konflikt sesji; karta działa jako pomocnicza dla innej karty.
Limity na połączenie
Wiążące wartości serwer wysyła w init_ok.limits i init_ok.features:
- heartbeat_ms 25 000 – widżet wysyła
ping25 s po ostatniej wychodzącej wiadomości. - pong_timeout_ms 10 000 – dwa brakujące
pongprowadzą do ponownego nawiązania połączenia. - idle_timeout_ms 70 000 – serwer zamyka połączenie po 70 s bez przychodzącej ramki (1001).
- max_frame_bytes 65 536 – większe ramki:
payload_too_large. - msg_per_10s 20 i frames_per_10s 120 – kubełek tokenów na połączenie; powyżej
rate_limited. - reconnect_cap_ms 30 000 – najdłuższe odczekanie między ponownymi próbami.
- dedup_window_ms 86 400 000 – 24 godziny deduplikacji.
- features.max_msg_len – znaki (punkty kodowe Unicode) na wiadomość odwiedzającego: 1000 w planie Free, 4000 w planach płatnych. Widżet nie wysyła zbyt długich tekstów.
- features.max_upload_bytes, features.allowed_mime – zobacz Limity i kwoty.
Ponowne łączenie, kolejność, duplikaty
- Odczekanie:
min(30000, 500 × 2^(Versuch−1))ms, z czego losowo górna połowa (próba 1: 250–500 ms, od próby 7: 15–30 s). W kartach w tle trzy razy dłużej.retry_after_msserwera jest dolną granicą. Liczba prób jest nieograniczona; licznik wraca do zera dopiero przyinit_ok. Gdy przeglądarka zgłosionline, widżet próbuje od razu; gdy jest offline, czeka. - Kolejność: każda wiadomość rozmowy ma ciągły numer
seqod 1. Wyświetlanie odbywa się wedługseq, a nie według czasu. Przy ponownym połączeniu widżet wysyłaresume.last_seqi do 50 otwartych identyfikatorów; serwer dostarcza wszystko, co przyszło później (najwyżej 100 wiadomości, w przeciwnym raziehas_more), i powtarza potwierdzenia dla wiadomości, które już dotarły. - Bez duplikatów: serwer pamięta każde
idwiadomości przez 24 godziny. Powtórzona wiadomość nie jest zapisywana ponownie, tylko dostaje to samo potwierdzenie. „Wyślij ponownie” w widżecie używa tego samegoid. - Stany wychodzącej wiadomości:
pending→sent→delivered(echo z serwera) →read;failedpo 5 próbach, po 24 h albo przyretry: "no". - Bufor offline: gdy połączenie się zerwie, wiadomości trafiają do bufora (najwyżej 50 wpisów, 128 KiB, 24 h) – za zgodą w
localStorage, bez zgody wsessionStorage– i wychodzą przy następnyminit_ok. - Wiele kart, jedno połączenie: karty jednej strony wybierają lidera (Web Locks, w przeciwnym razie sygnał podtrzymujący w
localStorage), tylko on utrzymuje połączenie; pozostałe komunikują się przezBroadcastChannel.
Tryb awaryjny HTTP
Jeśli sieć nie przepuszcza WebSocket, czat działa dalej przez zwykłe żądania HTTPS – z tymi samymi ramkami i tymi samymi limitami. Korzysta z niego tylko widżet; interfejs agentów łączy się zawsze przez WebSocket. Wersję skróconą dla redaktorów znajdziesz w Tryb awaryjny HTTP.
Kiedy się włącza:
- przeglądarka nie obsługuje WebSocket;
- dwie kolejne próby nawiązania WebSocket nie powiodą się, zanim na tej stronie w ogóle doszło do połączenia WebSocket (próba bez wyniku po 8 sekundach uchodzi za nieudaną);
- w
sessionStoragejest wpisrc_httpmłodszy niż 30 minut (kolejne strony w tej samej karcie); - wymusisz go przez
data-rc-transport="http"(przy dowolnym elemencie, który jest w dokumencie w chwili startu) albo przezwindow.ReactiveChatSettings = { transport: 'http' }przed loaderem.ReactiveChat('boot', { transport: 'http' })nie działa – rodzaj połączenia jest ustalany przy starcie.
Bez fetch w przeglądarce trybu awaryjnego nie ma. Jeśli istniejące połączenie WebSocket zostanie przerwane, widżet łączy się ponownie przez WebSocket. Po przejściu na tryb awaryjny raz na stronę sprawdza po cichu, czy WebSocket już przechodzi; jeśli tak, usuwa rc_http, a następna strona znowu używa WebSocket.
Żądania (pod https://ws.reactive.chat/rc_wss/h/):
- POST h/auf – otwiera połączenie. Odpowiedź
{"v":1,"cid":"…","tok":"…","poll_ms":25000,"idle_ms":60000}.tokto krótkotrwały token połączenia (32 znaki). - GET h/<cid>/sse?tok=… – Server-Sent Events, dla każdej ramki
id:idata:, co 15 s sygnał podtrzymujący. Dostarczanie zaległych przezLast-Event-ID. - GET h/<cid>/poll?tok=…&nach=<n> – long polling; serwer trzyma żądanie otwarte do 25 s i odpowiada
{"v":1,"n":…,"rahmen":[…]}. - POST h/<cid>/tx?tok=… – wysyłanie,
text/plain, jedna ramka albo tablica z najwyżej 20 ramkami; odpowiedź 204. - POST h/<cid>/zu?tok=… – zamknięcie (przez
sendBeacon).
Widżet najpierw próbuje SSE; jeśli w ciągu 5 sekund nie przyjdzie pierwsza ramka (np. dlatego, że proxy buforuje), przechodzi na long polling. Błędy takie jak 404 (unbekannt), 403, 413 czy 429 traktuje jak zerwanie połączenia i próbuje ponownie z odczekaniem. Po 60 sekundach bez pobrania serwer zamyka połączenie. CORS: Access-Control-Allow-Origin odzwierciedla pochodzenie, bez credentials; kontrola pochodzenia odbywa się tak jak przy WebSocket w init.
Wymagania po twojej stronie:
connect-srctwojej CSP zawiera adres czatu zwss://ihttps://.- Firewalle i proxy dopuszczają żądania trwające co najmniej 30 sekund.
- Własne proxy przekazuje
/rc_wss/bez buforowania (nginxproxy_buffering offiproxy_cache off, Apacheflushpackets=on, Caddyflush_interval -1) i nie zapisuje/rc_wss/h/w logu dostępu (token w query). Zobacz Własna domena.
Zapisywane w przeglądarce: tylko wpis rc_http w sessionStorage z czasem nawiązania połączenia HTTPS, bez danych osobowych.
Która droga jest aktywna
W zakładce „Sieć” narzędzi deweloperskich:
- WebSocket: połączenie typu WebSocket do
wss://ws.reactive.chat/rc_wss/(albo do twojej subdomeny). W ramkach widziszinit,init_ok,ping/pongi wiadomości. - Tryb awaryjny HTTP: najpierw
h/auf, potem długo trwające…/sse(albo powtarzające się…/poll) i dla każdej wysłanej wiadomości jedno…/tx.
Dalej: Limity i kwoty · Rozwiązywanie problemów
آخرین بهروزرسانی: