connection_http.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "mtproto/connection_http.h"
  8. #include "base/random.h"
  9. #include "base/qthelp_url.h"
  10. namespace MTP {
  11. namespace details {
  12. namespace {
  13. constexpr auto kForceHttpPort = 80;
  14. constexpr auto kFullConnectionTimeout = crl::time(8000);
  15. } // namespace
  16. HttpConnection::HttpConnection(QThread *thread, const ProxyData &proxy)
  17. : AbstractConnection(thread, proxy)
  18. , _checkNonce(base::RandomValue<MTPint128>()) {
  19. _manager.moveToThread(thread);
  20. _manager.setProxy(ToNetworkProxy(proxy));
  21. }
  22. ConnectionPointer HttpConnection::clone(const ProxyData &proxy) {
  23. return ConnectionPointer::New<HttpConnection>(thread(), proxy);
  24. }
  25. void HttpConnection::sendData(mtpBuffer &&buffer) {
  26. Expects(buffer.size() > 2);
  27. if (_status == Status::Finished) {
  28. return;
  29. }
  30. int32 requestSize = (buffer.size() - 2) * sizeof(mtpPrime);
  31. QNetworkRequest request(url());
  32. request.setHeader(
  33. QNetworkRequest::ContentLengthHeader,
  34. QVariant(requestSize));
  35. request.setHeader(
  36. QNetworkRequest::ContentTypeHeader,
  37. QVariant(u"application/x-www-form-urlencoded"_q));
  38. CONNECTION_LOG_INFO(u"Sending %1 len request."_q.arg(requestSize));
  39. _requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
  40. }
  41. void HttpConnection::disconnectFromServer() {
  42. if (_status == Status::Finished) return;
  43. _status = Status::Finished;
  44. const auto requests = base::take(_requests);
  45. for (const auto request : requests) {
  46. request->abort();
  47. request->deleteLater();
  48. }
  49. disconnect(
  50. &_manager,
  51. &QNetworkAccessManager::finished,
  52. this,
  53. &HttpConnection::requestFinished);
  54. }
  55. void HttpConnection::connectToServer(
  56. const QString &address,
  57. int port,
  58. const bytes::vector &protocolSecret,
  59. int16 protocolDcId,
  60. bool protocolForFiles) {
  61. _address = address;
  62. connect(
  63. &_manager,
  64. &QNetworkAccessManager::finished,
  65. this,
  66. &HttpConnection::requestFinished);
  67. auto buffer = preparePQFake(_checkNonce);
  68. if (Logs::DebugEnabled()) {
  69. _debugId = u"%1(dc:%2,%3)"_q
  70. .arg(_debugId.toInt())
  71. .arg(ProtocolDcDebugId(protocolDcId), url().toDisplayString());
  72. }
  73. _pingTime = crl::now();
  74. sendData(std::move(buffer));
  75. }
  76. mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
  77. QByteArray response = reply->readAll();
  78. CONNECTION_LOG_INFO(u"Read %1 bytes."_q.arg(response.size()));
  79. if (response.isEmpty()) return mtpBuffer();
  80. if (response.size() & 0x03 || response.size() < 8) {
  81. CONNECTION_LOG_ERROR(u"Bad response size %1."_q.arg(response.size()));
  82. return mtpBuffer(1, -500);
  83. }
  84. mtpBuffer data(response.size() >> 2);
  85. memcpy(data.data(), response.constData(), response.size());
  86. return data;
  87. }
  88. // Returns "maybe bad key".
  89. qint32 HttpConnection::handleError(QNetworkReply *reply) {
  90. auto result = qint32(kErrorCodeOther);
  91. QVariant statusCode = reply->attribute(
  92. QNetworkRequest::HttpStatusCodeAttribute);
  93. if (statusCode.isValid()) {
  94. int status = statusCode.toInt();
  95. result = -status;
  96. }
  97. switch (reply->error()) {
  98. case QNetworkReply::ConnectionRefusedError:
  99. CONNECTION_LOG_ERROR(u"Connection refused - %1."_q
  100. .arg(reply->errorString()));
  101. break;
  102. case QNetworkReply::RemoteHostClosedError:
  103. CONNECTION_LOG_ERROR(u"Remote host closed - %1."_q
  104. .arg(reply->errorString()));
  105. break;
  106. case QNetworkReply::HostNotFoundError:
  107. CONNECTION_LOG_ERROR(u"Host not found - %1."_q
  108. .arg(reply->errorString()));
  109. break;
  110. case QNetworkReply::TimeoutError:
  111. CONNECTION_LOG_ERROR(u"Timeout - %1."_q
  112. .arg(reply->errorString()));
  113. break;
  114. case QNetworkReply::OperationCanceledError:
  115. CONNECTION_LOG_ERROR(u"Cancelled - %1."_q
  116. .arg(reply->errorString()));
  117. break;
  118. case QNetworkReply::SslHandshakeFailedError:
  119. case QNetworkReply::TemporaryNetworkFailureError:
  120. case QNetworkReply::NetworkSessionFailedError:
  121. case QNetworkReply::BackgroundRequestNotAllowedError:
  122. case QNetworkReply::UnknownNetworkError:
  123. CONNECTION_LOG_ERROR(u"Network error %1 - %2."_q
  124. .arg(reply->error())
  125. .arg(reply->errorString()));
  126. break;
  127. // proxy errors (101-199):
  128. case QNetworkReply::ProxyConnectionRefusedError:
  129. case QNetworkReply::ProxyConnectionClosedError:
  130. case QNetworkReply::ProxyNotFoundError:
  131. case QNetworkReply::ProxyTimeoutError:
  132. case QNetworkReply::ProxyAuthenticationRequiredError:
  133. case QNetworkReply::UnknownProxyError:
  134. CONNECTION_LOG_ERROR(u"Proxy error %1 - %2."_q
  135. .arg(reply->error())
  136. .arg(reply->errorString()));
  137. break;
  138. // content errors (201-299):
  139. case QNetworkReply::ContentAccessDenied:
  140. case QNetworkReply::ContentOperationNotPermittedError:
  141. case QNetworkReply::ContentNotFoundError:
  142. case QNetworkReply::AuthenticationRequiredError:
  143. case QNetworkReply::ContentReSendError:
  144. case QNetworkReply::UnknownContentError:
  145. CONNECTION_LOG_ERROR(u"Content error %1 - %2."_q
  146. .arg(reply->error())
  147. .arg(reply->errorString()));
  148. break;
  149. // protocol errors
  150. case QNetworkReply::ProtocolUnknownError:
  151. case QNetworkReply::ProtocolInvalidOperationError:
  152. case QNetworkReply::ProtocolFailure:
  153. CONNECTION_LOG_ERROR(u"Protocol error %1 - %2."_q
  154. .arg(reply->error())
  155. .arg(reply->errorString()));
  156. break;
  157. };
  158. return result;
  159. }
  160. bool HttpConnection::isConnected() const {
  161. return (_status == Status::Ready);
  162. }
  163. void HttpConnection::requestFinished(QNetworkReply *reply) {
  164. if (_status == Status::Finished) return;
  165. reply->deleteLater();
  166. if (reply->error() == QNetworkReply::NoError) {
  167. _requests.remove(reply);
  168. mtpBuffer data = handleResponse(reply);
  169. if (data.size() == 1) {
  170. error(data[0]);
  171. } else if (!data.isEmpty()) {
  172. if (_status == Status::Ready) {
  173. _receivedQueue.push_back(data);
  174. receivedData();
  175. } else if (const auto res_pq = readPQFakeReply(data)) {
  176. const auto &data = res_pq->c_resPQ();
  177. if (data.vnonce() == _checkNonce) {
  178. CONNECTION_LOG_INFO(
  179. "HTTP-transport connected by pq-response.");
  180. _status = Status::Ready;
  181. _pingTime = crl::now() - _pingTime;
  182. connected();
  183. } else {
  184. CONNECTION_LOG_ERROR(
  185. "Wrong nonce in HTTP fake pq-response.");
  186. error(kErrorCodeOther);
  187. }
  188. } else {
  189. CONNECTION_LOG_ERROR(
  190. "Could not parse HTTP fake pq-response.");
  191. error(kErrorCodeOther);
  192. }
  193. }
  194. } else {
  195. if (!_requests.remove(reply)) {
  196. return;
  197. }
  198. error(handleError(reply));
  199. }
  200. }
  201. crl::time HttpConnection::pingTime() const {
  202. return isConnected() ? _pingTime : crl::time(0);
  203. }
  204. crl::time HttpConnection::fullConnectTimeout() const {
  205. return kFullConnectionTimeout;
  206. }
  207. bool HttpConnection::usingHttpWait() {
  208. return true;
  209. }
  210. bool HttpConnection::needHttpWait() {
  211. return _requests.isEmpty();
  212. }
  213. int32 HttpConnection::debugState() const {
  214. return -1;
  215. }
  216. QString HttpConnection::transport() const {
  217. if (!isConnected()) {
  218. return QString();
  219. }
  220. auto result = u"HTTP"_q;
  221. if (qthelp::is_ipv6(_address)) {
  222. result += u"/IPv6"_q;
  223. }
  224. return result;
  225. }
  226. QString HttpConnection::tag() const {
  227. auto result = u"HTTP"_q;
  228. if (qthelp::is_ipv6(_address)) {
  229. result += u"/IPv6"_q;
  230. } else {
  231. result += u"/IPv4"_q;
  232. }
  233. return result;
  234. }
  235. QUrl HttpConnection::url() const {
  236. const auto pattern = qthelp::is_ipv6(_address)
  237. ? u"http://[%1]:%2/api"_q
  238. : u"http://%1:%2/api"_q;
  239. // Not endpoint.port - always 80 port for http transport.
  240. return QUrl(pattern.arg(_address).arg(kForceHttpPort));
  241. }
  242. } // namespace details
  243. } // namespace MTP