mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-11 09:19:35 +00:00
08167a6a91
Currently the build without implicitly included precompiled header is not supported anyway (because Qt MOC source files do not include stdafx.h, they include plain headers). So when we decide to support building without implicitly included precompiled headers we'll have to fix all the headers anyway.
214 lines
7.4 KiB
C++
214 lines
7.4 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
It is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#include "mtproto/connection_http.h"
|
|
|
|
namespace MTP {
|
|
namespace internal {
|
|
|
|
mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) {
|
|
QByteArray response = reply->readAll();
|
|
TCP_LOG(("HTTP Info: read %1 bytes").arg(response.size()));
|
|
|
|
if (response.isEmpty()) return mtpBuffer();
|
|
|
|
if (response.size() & 0x03 || response.size() < 8) {
|
|
LOG(("HTTP Error: bad response size %1").arg(response.size()));
|
|
return mtpBuffer(1, -500);
|
|
}
|
|
|
|
mtpBuffer data(response.size() >> 2);
|
|
memcpy(data.data(), response.constData(), response.size());
|
|
|
|
return data;
|
|
}
|
|
|
|
qint32 HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key"
|
|
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: LOG(("HTTP Error: connection refused - %1").arg(reply->errorString())); break;
|
|
case QNetworkReply::RemoteHostClosedError: LOG(("HTTP Error: remote host closed - %1").arg(reply->errorString())); break;
|
|
case QNetworkReply::HostNotFoundError: LOG(("HTTP Error: host not found - %2").arg(reply->error()).arg(reply->errorString())); break;
|
|
case QNetworkReply::TimeoutError: LOG(("HTTP Error: timeout - %2").arg(reply->error()).arg(reply->errorString())); break;
|
|
case QNetworkReply::OperationCanceledError: LOG(("HTTP Error: cancelled - %2").arg(reply->error()).arg(reply->errorString())); break;
|
|
case QNetworkReply::SslHandshakeFailedError:
|
|
case QNetworkReply::TemporaryNetworkFailureError:
|
|
case QNetworkReply::NetworkSessionFailedError:
|
|
case QNetworkReply::BackgroundRequestNotAllowedError:
|
|
case QNetworkReply::UnknownNetworkError: LOG(("HTTP Error: network error %1 - %2").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:LOG(("HTTP Error: proxy error %1 - %2").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: LOG(("HTTP Error: content error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
|
|
|
|
// protocol errors
|
|
case QNetworkReply::ProtocolUnknownError:
|
|
case QNetworkReply::ProtocolInvalidOperationError:
|
|
case QNetworkReply::ProtocolFailure: LOG(("HTTP Error: protocol error %1 - %2").arg(reply->error()).arg(reply->errorString())); break;
|
|
};
|
|
TCP_LOG(("HTTP Error %1, restarting! - %2").arg(reply->error()).arg(reply->errorString()));
|
|
|
|
return result;
|
|
}
|
|
|
|
HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread)
|
|
, status(WaitingHttp)
|
|
, httpNonce(rand_value<MTPint128>())
|
|
, _flags(0) {
|
|
manager.moveToThread(thread);
|
|
App::setProxySettings(manager);
|
|
}
|
|
|
|
void HTTPConnection::sendData(mtpBuffer &buffer) {
|
|
if (status == FinishedWork) return;
|
|
|
|
if (buffer.size() < 3) {
|
|
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
|
|
TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
|
|
emit error(kErrorCodeOther);
|
|
return;
|
|
}
|
|
|
|
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
|
|
|
|
QNetworkRequest request(address);
|
|
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
|
|
|
|
TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
|
|
requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
|
|
}
|
|
|
|
void HTTPConnection::disconnectFromServer() {
|
|
if (status == FinishedWork) return;
|
|
status = FinishedWork;
|
|
|
|
Requests copy = requests;
|
|
requests.clear();
|
|
for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) {
|
|
(*i)->abort();
|
|
(*i)->deleteLater();
|
|
}
|
|
|
|
disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
|
|
|
|
address = QUrl();
|
|
}
|
|
|
|
void HTTPConnection::connectHttp(const DcOptions::Endpoint &endpoint) {
|
|
_flags = endpoint.flags;
|
|
auto addr = QString::fromStdString(endpoint.ip);
|
|
|
|
// not endpoint.port - always 80 port for http transport
|
|
address = QUrl(((_flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));
|
|
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
|
|
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
|
|
|
|
mtpBuffer buffer(preparePQFake(httpNonce));
|
|
|
|
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
|
|
|
|
sendData(buffer);
|
|
}
|
|
|
|
bool HTTPConnection::isConnected() const {
|
|
return (status == UsingHttp);
|
|
}
|
|
|
|
void HTTPConnection::requestFinished(QNetworkReply *reply) {
|
|
if (status == FinishedWork) return;
|
|
|
|
reply->deleteLater();
|
|
if (reply->error() == QNetworkReply::NoError) {
|
|
requests.remove(reply);
|
|
|
|
mtpBuffer data = handleResponse(reply);
|
|
if (data.size() == 1) {
|
|
emit error(data[0]);
|
|
} else if (!data.isEmpty()) {
|
|
if (status == UsingHttp) {
|
|
_receivedQueue.push_back(data);
|
|
emit receivedData();
|
|
} else {
|
|
try {
|
|
auto res_pq = readPQFakeReply(data);
|
|
const auto &res_pq_data(res_pq.c_resPQ());
|
|
if (res_pq_data.vnonce == httpNonce) {
|
|
DEBUG_LOG(("Connection Info: HTTP/%1-transport connected by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
|
|
status = UsingHttp;
|
|
emit connected();
|
|
}
|
|
} catch (Exception &e) {
|
|
DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what()));
|
|
emit error(kErrorCodeOther);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (!requests.remove(reply)) {
|
|
return;
|
|
}
|
|
|
|
emit error(handleError(reply));
|
|
}
|
|
}
|
|
|
|
bool HTTPConnection::usingHttpWait() {
|
|
return true;
|
|
}
|
|
|
|
bool HTTPConnection::needHttpWait() {
|
|
return requests.isEmpty();
|
|
}
|
|
|
|
int32 HTTPConnection::debugState() const {
|
|
return -1;
|
|
}
|
|
|
|
QString HTTPConnection::transport() const {
|
|
if (status == UsingHttp) {
|
|
return qsl("HTTP");
|
|
} else {
|
|
return QString();
|
|
}
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace MTP
|