| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- /*
- 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
- */
- #include "mtproto/connection_http.h"
- #include "base/random.h"
- #include "base/qthelp_url.h"
- namespace MTP {
- namespace details {
- namespace {
- constexpr auto kForceHttpPort = 80;
- constexpr auto kFullConnectionTimeout = crl::time(8000);
- } // namespace
- HttpConnection::HttpConnection(QThread *thread, const ProxyData &proxy)
- : AbstractConnection(thread, proxy)
- , _checkNonce(base::RandomValue<MTPint128>()) {
- _manager.moveToThread(thread);
- _manager.setProxy(ToNetworkProxy(proxy));
- }
- ConnectionPointer HttpConnection::clone(const ProxyData &proxy) {
- return ConnectionPointer::New<HttpConnection>(thread(), proxy);
- }
- void HttpConnection::sendData(mtpBuffer &&buffer) {
- Expects(buffer.size() > 2);
- if (_status == Status::Finished) {
- return;
- }
- int32 requestSize = (buffer.size() - 2) * sizeof(mtpPrime);
- QNetworkRequest request(url());
- request.setHeader(
- QNetworkRequest::ContentLengthHeader,
- QVariant(requestSize));
- request.setHeader(
- QNetworkRequest::ContentTypeHeader,
- QVariant(u"application/x-www-form-urlencoded"_q));
- CONNECTION_LOG_INFO(u"Sending %1 len request."_q.arg(requestSize));
- _requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
- }
- void HttpConnection::disconnectFromServer() {
- if (_status == Status::Finished) return;
- _status = Status::Finished;
- const auto requests = base::take(_requests);
- for (const auto request : requests) {
- request->abort();
- request->deleteLater();
- }
- disconnect(
- &_manager,
- &QNetworkAccessManager::finished,
- this,
- &HttpConnection::requestFinished);
- }
- void HttpConnection::connectToServer(
- const QString &address,
- int port,
- const bytes::vector &protocolSecret,
- int16 protocolDcId,
- bool protocolForFiles) {
- _address = address;
- connect(
- &_manager,
- &QNetworkAccessManager::finished,
- this,
- &HttpConnection::requestFinished);
- auto buffer = preparePQFake(_checkNonce);
- if (Logs::DebugEnabled()) {
- _debugId = u"%1(dc:%2,%3)"_q
- .arg(_debugId.toInt())
- .arg(ProtocolDcDebugId(protocolDcId), url().toDisplayString());
- }
- _pingTime = crl::now();
- sendData(std::move(buffer));
- }
- mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
- QByteArray response = reply->readAll();
- CONNECTION_LOG_INFO(u"Read %1 bytes."_q.arg(response.size()));
- if (response.isEmpty()) return mtpBuffer();
- if (response.size() & 0x03 || response.size() < 8) {
- CONNECTION_LOG_ERROR(u"Bad response size %1."_q.arg(response.size()));
- return mtpBuffer(1, -500);
- }
- mtpBuffer data(response.size() >> 2);
- memcpy(data.data(), response.constData(), response.size());
- return data;
- }
- // Returns "maybe bad key".
- qint32 HttpConnection::handleError(QNetworkReply *reply) {
- auto result = qint32(kErrorCodeOther);
- QVariant statusCode = reply->attribute(
- QNetworkRequest::HttpStatusCodeAttribute);
- if (statusCode.isValid()) {
- int status = statusCode.toInt();
- result = -status;
- }
- switch (reply->error()) {
- case QNetworkReply::ConnectionRefusedError:
- CONNECTION_LOG_ERROR(u"Connection refused - %1."_q
- .arg(reply->errorString()));
- break;
- case QNetworkReply::RemoteHostClosedError:
- CONNECTION_LOG_ERROR(u"Remote host closed - %1."_q
- .arg(reply->errorString()));
- break;
- case QNetworkReply::HostNotFoundError:
- CONNECTION_LOG_ERROR(u"Host not found - %1."_q
- .arg(reply->errorString()));
- break;
- case QNetworkReply::TimeoutError:
- CONNECTION_LOG_ERROR(u"Timeout - %1."_q
- .arg(reply->errorString()));
- break;
- case QNetworkReply::OperationCanceledError:
- CONNECTION_LOG_ERROR(u"Cancelled - %1."_q
- .arg(reply->errorString()));
- break;
- case QNetworkReply::SslHandshakeFailedError:
- case QNetworkReply::TemporaryNetworkFailureError:
- case QNetworkReply::NetworkSessionFailedError:
- case QNetworkReply::BackgroundRequestNotAllowedError:
- case QNetworkReply::UnknownNetworkError:
- CONNECTION_LOG_ERROR(u"Network error %1 - %2."_q
- .arg(reply->error())
- .arg(reply->errorString()));
- break;
- // proxy errors (101-199):
- case QNetworkReply::ProxyConnectionRefusedError:
- case QNetworkReply::ProxyConnectionClosedError:
- case QNetworkReply::ProxyNotFoundError:
- case QNetworkReply::ProxyTimeoutError:
- case QNetworkReply::ProxyAuthenticationRequiredError:
- case QNetworkReply::UnknownProxyError:
- CONNECTION_LOG_ERROR(u"Proxy error %1 - %2."_q
- .arg(reply->error())
- .arg(reply->errorString()));
- break;
- // content errors (201-299):
- case QNetworkReply::ContentAccessDenied:
- case QNetworkReply::ContentOperationNotPermittedError:
- case QNetworkReply::ContentNotFoundError:
- case QNetworkReply::AuthenticationRequiredError:
- case QNetworkReply::ContentReSendError:
- case QNetworkReply::UnknownContentError:
- CONNECTION_LOG_ERROR(u"Content error %1 - %2."_q
- .arg(reply->error())
- .arg(reply->errorString()));
- break;
- // protocol errors
- case QNetworkReply::ProtocolUnknownError:
- case QNetworkReply::ProtocolInvalidOperationError:
- case QNetworkReply::ProtocolFailure:
- CONNECTION_LOG_ERROR(u"Protocol error %1 - %2."_q
- .arg(reply->error())
- .arg(reply->errorString()));
- break;
- };
- return result;
- }
- bool HttpConnection::isConnected() const {
- return (_status == Status::Ready);
- }
- void HttpConnection::requestFinished(QNetworkReply *reply) {
- if (_status == Status::Finished) return;
- reply->deleteLater();
- if (reply->error() == QNetworkReply::NoError) {
- _requests.remove(reply);
- mtpBuffer data = handleResponse(reply);
- if (data.size() == 1) {
- error(data[0]);
- } else if (!data.isEmpty()) {
- if (_status == Status::Ready) {
- _receivedQueue.push_back(data);
- receivedData();
- } else if (const auto res_pq = readPQFakeReply(data)) {
- const auto &data = res_pq->c_resPQ();
- if (data.vnonce() == _checkNonce) {
- CONNECTION_LOG_INFO(
- "HTTP-transport connected by pq-response.");
- _status = Status::Ready;
- _pingTime = crl::now() - _pingTime;
- connected();
- } else {
- CONNECTION_LOG_ERROR(
- "Wrong nonce in HTTP fake pq-response.");
- error(kErrorCodeOther);
- }
- } else {
- CONNECTION_LOG_ERROR(
- "Could not parse HTTP fake pq-response.");
- error(kErrorCodeOther);
- }
- }
- } else {
- if (!_requests.remove(reply)) {
- return;
- }
- error(handleError(reply));
- }
- }
- crl::time HttpConnection::pingTime() const {
- return isConnected() ? _pingTime : crl::time(0);
- }
- crl::time HttpConnection::fullConnectTimeout() const {
- return kFullConnectionTimeout;
- }
- bool HttpConnection::usingHttpWait() {
- return true;
- }
- bool HttpConnection::needHttpWait() {
- return _requests.isEmpty();
- }
- int32 HttpConnection::debugState() const {
- return -1;
- }
- QString HttpConnection::transport() const {
- if (!isConnected()) {
- return QString();
- }
- auto result = u"HTTP"_q;
- if (qthelp::is_ipv6(_address)) {
- result += u"/IPv6"_q;
- }
- return result;
- }
- QString HttpConnection::tag() const {
- auto result = u"HTTP"_q;
- if (qthelp::is_ipv6(_address)) {
- result += u"/IPv6"_q;
- } else {
- result += u"/IPv4"_q;
- }
- return result;
- }
- QUrl HttpConnection::url() const {
- const auto pattern = qthelp::is_ipv6(_address)
- ? u"http://[%1]:%2/api"_q
- : u"http://%1:%2/api"_q;
- // Not endpoint.port - always 80 port for http transport.
- return QUrl(pattern.arg(_address).arg(kForceHttpPort));
- }
- } // namespace details
- } // namespace MTP
|