ZaStoGram/TMessagesProj/jni/tgnet/wss/WssPool.h
loop-uh 4669d9cc02
Some checks failed
ZaStoGram source guards / guards (push) Failing after 45s
Build three ZaStoGram APKs / build (armeabi-v7a, ZaStoGram-standalone-armeabi-v7a, Armv7, armv7) (push) Failing after 3m17s
Build three ZaStoGram APKs / build (arm64-v8a, ZaStoGram-standalone-arm64-v8a, Arm64, arm64) (push) Failing after 3m19s
Build three ZaStoGram APKs / build (x86, ZaStoGram-standalone-x86, X86, x86) (push) Failing after 3m20s
Держать WSS на релее и открывать соединения из пула
Короткая пачка TCP-таймаутов на старте отключала kws домашнего DC на
10 минут и уводила его в туннель Cloudflare, где на мобильной сети
TCP замерзает после ~16 КБ. Теперь таймаут при недавнем успешном TCP
к тому же адресу считается потерей потока, провалы в пределах 2 с
считаются одним, подавление длится 2 минуты, а включение WSS на
старте не рвёт соединения, если ни одно ещё не открыто.

Пул держит по одному готовому (TCP+TLS+upgrade) сокету на маршрут
kwsN, который соединения недавно запрашивали; неудачи его пробных
сокетов не влияют на выбор маршрута.
2026-09-24 19:39:26 +03:00

83 lines
2.5 KiB
C++

/*
* This is the source code of tgnet library v. 1.1
* It is licensed under GNU GPL v. 2 or later.
*/
#ifndef TGNET_WSS_POOL_H
#define TGNET_WSS_POOL_H
#include "WssSocket.h"
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
class EventObject;
namespace tgnet {
namespace wss {
// Keeps WebSocket relay sockets that already finished TCP, TLS and the HTTP
// upgrade, so a connection that needs a relay starts sending at once instead
// of paying the handshake again — and instead of losing seconds to a provider
// that swallows the SYN of a single flow. One pool per ConnectionsManager: it
// lives on that account's network thread and its epoll, so nothing is shared
// across threads.
//
// Only routes a connection actually asked for are warmed, one spare each, and
// only for a while after the last request. A pooled socket has not sent the
// 64-byte obfuscation header yet, so the relay has not bound it to anything
// and the taker's first frame still carries the whole init.
class Pool {
public:
Pool();
~Pool();
void attach(int epollFd, std::function<int64_t()> clock);
// A ready socket for exactly this route, or nullptr. Every call also marks
// the route as wanted, so the pool keeps a spare for the next one.
std::unique_ptr<Socket> take(const Route &route, int64_t now);
// Handshake timeouts, expiry and refill. With allowed == false the pool is
// emptied and stops opening sockets (paused, offline, WSS or proxy off).
void tick(int64_t now, bool allowed);
void clear(const char *reason);
// Frees event objects of retired sockets. Call only outside of an epoll
// batch: a stale event of the current batch may still point at them.
void collectGarbage();
struct Entry;
void onEntryEvent(Entry *entry, uint32_t events);
private:
struct Demand {
Route route;
int64_t lastWanted = 0;
int64_t nextOpenAt = 0;
int64_t backoffMs = 0;
};
void open(const std::string &key, Demand &demand, int64_t now);
void retire(Entry *entry, bool backoff, const char *reason);
void updateInterest(Entry *entry);
size_t countFor(const std::string &key) const;
int epollFd = -1;
std::function<int64_t()> clock;
std::vector<std::unique_ptr<Entry>> entries;
std::map<std::string, Demand> demands;
std::vector<EventObject *> graveyard;
};
// Called by EventObject for EventObjectTypeWssPool.
void DispatchPoolEvent(void *entry, uint32_t events);
} // namespace wss
} // namespace tgnet
#endif