2018-12-31 20:04:05 +01:00
|
|
|
//
|
2024-01-01 01:07:21 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2024
|
2018-12-31 20:04:05 +01:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
#include "td/net/Wget.h"
|
|
|
|
|
|
|
|
#include "td/net/HttpHeaderCreator.h"
|
|
|
|
#include "td/net/HttpOutboundConnection.h"
|
2018-08-15 14:41:42 +02:00
|
|
|
#include "td/net/SslStream.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
#include "td/utils/buffer.h"
|
2021-11-04 10:46:08 +01:00
|
|
|
#include "td/utils/BufferedFd.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/utils/HttpUrl.h"
|
|
|
|
#include "td/utils/logging.h"
|
2018-04-28 10:56:10 +02:00
|
|
|
#include "td/utils/misc.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
#include "td/utils/port/IPAddress.h"
|
|
|
|
#include "td/utils/port/SocketFd.h"
|
|
|
|
#include "td/utils/Slice.h"
|
2021-05-17 14:21:11 +02:00
|
|
|
#include "td/utils/SliceBuilder.h"
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
namespace td {
|
2018-05-18 15:15:01 +02:00
|
|
|
|
2019-06-17 18:12:54 +02:00
|
|
|
Wget::Wget(Promise<unique_ptr<HttpQuery>> promise, string url, std::vector<std::pair<string, string>> headers,
|
2022-11-11 12:23:43 +01:00
|
|
|
int32 timeout_in, int32 ttl, bool prefer_ipv6, SslCtx::VerifyPeer verify_peer, string content,
|
2019-11-19 13:11:19 +01:00
|
|
|
string content_type)
|
2018-12-31 20:04:05 +01:00
|
|
|
: promise_(std::move(promise))
|
|
|
|
, input_url_(std::move(url))
|
|
|
|
, headers_(std::move(headers))
|
|
|
|
, timeout_in_(timeout_in)
|
|
|
|
, ttl_(ttl)
|
2018-07-01 03:45:25 +02:00
|
|
|
, prefer_ipv6_(prefer_ipv6)
|
2019-11-19 13:11:19 +01:00
|
|
|
, verify_peer_(verify_peer)
|
|
|
|
, content_(std::move(content))
|
|
|
|
, content_type_(std::move(content_type)) {
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Status Wget::try_init() {
|
2019-08-05 11:56:28 +02:00
|
|
|
TRY_RESULT(url, parse_url(input_url_));
|
2019-12-08 07:57:33 +01:00
|
|
|
TRY_RESULT_ASSIGN(url.host_, idn_to_ascii(url.host_));
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
HttpHeaderCreator hc;
|
2019-11-19 13:11:19 +01:00
|
|
|
if (content_.empty()) {
|
|
|
|
hc.init_get(url.query_);
|
|
|
|
} else {
|
|
|
|
hc.init_post(url.query_);
|
|
|
|
hc.set_content_size(content_.size());
|
|
|
|
if (!content_type_.empty()) {
|
|
|
|
hc.set_content_type(content_type_);
|
|
|
|
}
|
|
|
|
}
|
2018-03-13 20:31:37 +01:00
|
|
|
bool was_host = false;
|
2018-04-19 14:23:54 +02:00
|
|
|
bool was_accept_encoding = false;
|
2018-12-31 20:04:05 +01:00
|
|
|
for (auto &header : headers_) {
|
2018-04-19 14:23:54 +02:00
|
|
|
auto header_lower = to_lower(header.first);
|
|
|
|
if (header_lower == "host") {
|
2018-03-13 20:31:37 +01:00
|
|
|
was_host = true;
|
|
|
|
}
|
2018-04-19 14:23:54 +02:00
|
|
|
if (header_lower == "accept-encoding") {
|
|
|
|
was_accept_encoding = true;
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
hc.add_header(header.first, header.second);
|
|
|
|
}
|
2018-03-13 20:31:37 +01:00
|
|
|
if (!was_host) {
|
|
|
|
hc.add_header("Host", url.host_);
|
|
|
|
}
|
2018-04-19 14:23:54 +02:00
|
|
|
if (!was_accept_encoding) {
|
|
|
|
hc.add_header("Accept-Encoding", "gzip, deflate");
|
|
|
|
}
|
2019-11-19 13:11:19 +01:00
|
|
|
TRY_RESULT(header, hc.finish(content_));
|
2018-06-01 22:45:34 +02:00
|
|
|
|
|
|
|
IPAddress addr;
|
2018-07-01 03:45:25 +02:00
|
|
|
TRY_STATUS(addr.init_host_port(url.host_, url.port_, prefer_ipv6_));
|
2018-06-01 22:45:34 +02:00
|
|
|
|
|
|
|
TRY_RESULT(fd, SocketFd::open(addr));
|
2021-03-27 09:02:32 +01:00
|
|
|
if (fd.empty()) {
|
2021-04-09 01:35:50 +02:00
|
|
|
return Status::Error("Sockets are not supported");
|
2021-03-27 09:02:32 +01:00
|
|
|
}
|
2020-06-15 03:23:47 +02:00
|
|
|
if (url.protocol_ == HttpUrl::Protocol::Http) {
|
2021-10-27 19:29:01 +02:00
|
|
|
connection_ = create_actor<HttpOutboundConnection>("Connect", BufferedFd<SocketFd>(std::move(fd)), SslStream{},
|
2018-08-15 14:41:42 +02:00
|
|
|
std::numeric_limits<std::size_t>::max(), 0, 0,
|
|
|
|
ActorOwn<HttpOutboundConnection::Callback>(actor_id(this)));
|
2018-06-01 22:45:34 +02:00
|
|
|
} else {
|
2022-11-11 12:51:22 +01:00
|
|
|
TRY_RESULT(ssl_ctx, SslCtx::create(CSlice() /* certificate */, verify_peer_));
|
|
|
|
TRY_RESULT(ssl_stream, SslStream::create(url.host_, std::move(ssl_ctx)));
|
2021-10-27 19:29:01 +02:00
|
|
|
connection_ = create_actor<HttpOutboundConnection>(
|
|
|
|
"Connect", BufferedFd<SocketFd>(std::move(fd)), std::move(ssl_stream), std::numeric_limits<std::size_t>::max(),
|
|
|
|
0, 0, ActorOwn<HttpOutboundConnection::Callback>(actor_id(this)));
|
2018-06-01 22:45:34 +02:00
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-06-01 22:45:34 +02:00
|
|
|
send_closure(connection_, &HttpOutboundConnection::write_next, BufferSlice(header));
|
2018-12-31 20:04:05 +01:00
|
|
|
send_closure(connection_, &HttpOutboundConnection::write_ok);
|
|
|
|
return Status::OK();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::loop() {
|
|
|
|
if (connection_.empty()) {
|
|
|
|
auto status = try_init();
|
|
|
|
if (status.is_error()) {
|
|
|
|
return on_error(std::move(status));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-17 18:12:54 +02:00
|
|
|
void Wget::handle(unique_ptr<HttpQuery> result) {
|
2018-12-31 20:04:05 +01:00
|
|
|
on_ok(std::move(result));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::on_connection_error(Status error) {
|
|
|
|
on_error(std::move(error));
|
|
|
|
}
|
|
|
|
|
2019-06-17 18:12:54 +02:00
|
|
|
void Wget::on_ok(unique_ptr<HttpQuery> http_query_ptr) {
|
2018-12-31 20:04:05 +01:00
|
|
|
CHECK(promise_);
|
2019-06-17 18:12:54 +02:00
|
|
|
CHECK(http_query_ptr);
|
2018-05-18 01:03:21 +02:00
|
|
|
if ((http_query_ptr->code_ == 301 || http_query_ptr->code_ == 302 || http_query_ptr->code_ == 307 ||
|
|
|
|
http_query_ptr->code_ == 308) &&
|
|
|
|
ttl_ > 0) {
|
2018-12-31 20:04:05 +01:00
|
|
|
LOG(DEBUG) << *http_query_ptr;
|
2018-04-19 14:23:54 +02:00
|
|
|
input_url_ = http_query_ptr->get_header("location").str();
|
2018-12-31 20:04:05 +01:00
|
|
|
LOG(DEBUG) << input_url_;
|
|
|
|
ttl_--;
|
|
|
|
connection_.reset();
|
|
|
|
yield();
|
|
|
|
} else if (http_query_ptr->code_ >= 200 && http_query_ptr->code_ < 300) {
|
|
|
|
promise_.set_value(std::move(http_query_ptr));
|
|
|
|
stop();
|
|
|
|
} else {
|
2018-04-19 14:23:54 +02:00
|
|
|
on_error(Status::Error(PSLICE() << "HTTP error: " << http_query_ptr->code_));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::on_error(Status error) {
|
|
|
|
CHECK(error.is_error());
|
|
|
|
CHECK(promise_);
|
|
|
|
promise_.set_error(std::move(error));
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::start_up() {
|
|
|
|
set_timeout_in(timeout_in_);
|
|
|
|
loop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::timeout_expired() {
|
2019-07-23 00:13:20 +02:00
|
|
|
on_error(Status::Error("Response timeout expired"));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Wget::tear_down() {
|
|
|
|
if (promise_) {
|
2021-05-31 20:51:48 +02:00
|
|
|
on_error(Status::Error("Canceled"));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}
|
2018-05-18 15:15:01 +02:00
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
} // namespace td
|