ZaStoGram_desktop/Telegram/SourceFiles/mtproto/web_proxy/web_proxy_webview.h
loop-uh 84e2237558
All checks were successful
Desktop source guards / guards (push) Successful in 6s
Rework the WEB proxy data path around measured limits
A stand (test relay with a sink/echo backend, WAN emulator, env-gated
self-test in the client) showed the dev-17 limits, not the relay, capping
throughput: a fixed 1 MiB upload window and 2 MiB download credit, plus
2 file sessions per DC, while every bridge write waited for its own
round trip through the WebView.

- Bridge: frames written in one carrier turn go to the page as one
  batch, up to 4 page calls are in flight, and the injected script
  joins the frames the page posts in one task into one message.
- Windows: the upload window and the shared download credit follow the
  bandwidth-delay product of the credit loop plus 100 ms of queue
  (AdaptiveWindow), with a periodic drain to keep the base honest, a
  per-direction share when both are busy, and a hold while new streams'
  initial credit floods the relay's downlink.
- Upload and download session counts are upstream's again; upload
  frames shrink with a small window.
- Media sessions over MTProxy and WEB drop a regular temporary key
  borrowed from their DC and use the media cluster key: the regular key
  sent to a -N DC was answered with -404 and destroyed in a loop.
- web_carrier summaries report windows, rates, delays and bridge stats.
2026-09-24 03:18:20 +03:00

122 lines
3.2 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include "mtproto/proxy/data.h"
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <crl/crl_time.h>
#include <atomic>
#include <deque>
#include <memory>
#include <string>
class QTimer;
namespace Webview {
class Window;
} // namespace Webview
namespace MTP::WebProxy {
// What crossed the native <-> page boundary, for diagnostics and the
// self-test. Written on the main thread, read anywhere.
struct BridgeCounters {
std::atomic<int64> upWrites = 0;
std::atomic<int64> upBytes = 0;
std::atomic<int64> upAckMsTotal = 0;
std::atomic<int64> upAckMsMax = 0;
std::atomic<int64> upQueuedMax = 0;
std::atomic<int64> downMessages = 0;
std::atomic<int64> downBytes = 0;
};
[[nodiscard]] BridgeCounters &Bridge();
class WebviewCarrier final : public QObject {
public:
struct Callbacks {
Fn<void(uint64)> ready;
Fn<void(uint64, QByteArray)> payload;
// Bytes and items (send() calls) the page has taken over.
Fn<void(uint64, int, int)> written;
Fn<void(uint64)> failed;
};
WebviewCarrier(
const ProxyData &proxy,
uint64 generation,
Callbacks callbacks);
~WebviewCarrier();
[[nodiscard]] static bool Supported();
[[nodiscard]] bool valid() const;
// One or more whole frames. Consecutive sends are coalesced into one
// page call and several page calls are kept in flight, so the bridge
// costs one round trip per batch, not per frame.
void send(QByteArray frames);
// Asks the bridge page to close its relay session and stops reacting
// to the page. The object should be kept alive shortly afterwards so
// that the asynchronous close script gets a chance to run.
void close();
private:
struct Pending {
QByteArray frame;
bool notifyWritten = false;
};
struct InFlight {
uint64 sequence = 0;
int bytes = 0;
int items = 0;
bool notifyWritten = false;
crl::time since = 0;
};
void handleMessage(std::string message, std::string sourceUrl);
void handleControl(const QByteArray &control);
void handleBinary(QByteArray frame);
[[nodiscard]] bool validNavigation(const QString &url) const;
[[nodiscard]] bool validSource(const std::string &sourceUrl) const;
void extendHandshake();
void probeRestrictions();
void enqueue(Pending pending);
void drain();
void heartbeat();
void fail(const char *reason);
const ProxyData _proxy;
const uint64 _generation = 0;
const QString _nonce;
const QString _url;
Callbacks _callbacks;
std::unique_ptr<Webview::Window> _window;
std::unique_ptr<QTimer> _handshakeTimer;
std::unique_ptr<QTimer> _healthTimer;
std::unique_ptr<QTimer> _probeTimer;
std::unique_ptr<QTimer> _writeTimer;
std::deque<Pending> _pending;
std::deque<InFlight> _inFlight;
int _inFlightBytes = 0;
uint64 _writeSequence = 0;
crl::time _handshakeStarted = 0;
int _pendingBytes = 0;
bool _bridgeInitialized = false;
bool _adopted = false;
bool _failed = false;
bool _closing = false;
#ifndef NDEBUG
bool _probed = false;
#endif // !NDEBUG
};
} // namespace MTP::WebProxy