1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

HTTP tunnel in rldp-http-proxy

This commit is contained in:
SpyCheese 2022-06-27 19:38:11 +03:00
parent d11580dfb3
commit c55b6f84a5
7 changed files with 432 additions and 100 deletions

View file

@ -58,9 +58,7 @@ void HttpConnection::loop() {
} }
TRY_STATUS(buffered_fd_.flush_write()); TRY_STATUS(buffered_fd_.flush_write());
if (writing_payload_ && buffered_fd_.left_unwritten() < fd_high_watermark()) { if (writing_payload_ && buffered_fd_.left_unwritten() < fd_high_watermark()) {
auto w = buffered_fd_.left_unwritten(); written = continue_payload_write();
continue_payload_write();
written = buffered_fd_.left_unwritten() > w;
} }
if (close_after_write_ && !writing_payload_ && !buffered_fd_.left_unwritten()) { if (close_after_write_ && !writing_payload_ && !buffered_fd_.left_unwritten()) {
LOG(INFO) << "close after write"; LOG(INFO) << "close after write";
@ -120,7 +118,7 @@ void HttpConnection::write_payload(std::shared_ptr<HttpPayload> payload) {
class Cb : public HttpPayload::Callback { class Cb : public HttpPayload::Callback {
public: public:
Cb(td::actor::ActorId<HttpConnection> conn) : conn_(conn) { Cb(td::actor::ActorId<HttpConnection> conn, size_t watermark) : conn_(conn), watermark_(watermark) {
} }
void run(size_t ready_bytes) override { void run(size_t ready_bytes) override {
if (!reached_ && ready_bytes >= watermark_) { if (!reached_ && ready_bytes >= watermark_) {
@ -135,23 +133,23 @@ void HttpConnection::write_payload(std::shared_ptr<HttpPayload> payload) {
} }
private: private:
size_t watermark_ = chunk_size();
bool reached_ = false;
td::actor::ActorId<HttpConnection> conn_; td::actor::ActorId<HttpConnection> conn_;
size_t watermark_;
bool reached_ = false;
}; };
writing_payload_->add_callback(std::make_unique<Cb>(actor_id(this))); writing_payload_->add_callback(std::make_unique<Cb>(
actor_id(this), writing_payload_->payload_type() == HttpPayload::PayloadType::pt_tunnel ? 1 : chunk_size()));
continue_payload_write(); continue_payload_write();
} }
void HttpConnection::continue_payload_write() { bool HttpConnection::continue_payload_write() {
if (!writing_payload_) { if (!writing_payload_) {
return; return false;
} }
if (writing_payload_->is_error()) { if (writing_payload_->is_error()) {
stop(); stop();
return; return false;
} }
auto t = writing_payload_->payload_type(); auto t = writing_payload_->payload_type();
@ -159,19 +157,24 @@ void HttpConnection::continue_payload_write() {
t = HttpPayload::PayloadType::pt_chunked; t = HttpPayload::PayloadType::pt_chunked;
} }
bool wrote = false;
while (!writing_payload_->written()) { while (!writing_payload_->written()) {
if (buffered_fd_.left_unwritten() > fd_high_watermark()) { if (buffered_fd_.left_unwritten() > fd_high_watermark()) {
return; return wrote;
} }
if (!writing_payload_->parse_completed() && writing_payload_->ready_bytes() < chunk_size()) { bool is_tunnel = writing_payload_->payload_type() == HttpPayload::PayloadType::pt_tunnel;
return; if (!is_tunnel && !writing_payload_->parse_completed() && writing_payload_->ready_bytes() < chunk_size()) {
return wrote;
} }
writing_payload_->store_http(buffered_fd_.output_buffer(), chunk_size(), t); if (is_tunnel && writing_payload_->ready_bytes() == 0) {
return wrote;
}
wrote |= writing_payload_->store_http(buffered_fd_.output_buffer(), chunk_size(), t);
} }
if (writing_payload_->parse_completed() && writing_payload_->written()) { if (writing_payload_->parse_completed() && writing_payload_->written()) {
payload_written(); payload_written();
return;
} }
return wrote;
} }
td::Status HttpConnection::read_payload(HttpResponse *response) { td::Status HttpConnection::read_payload(HttpResponse *response) {

View file

@ -65,9 +65,7 @@ class HttpConnection : public td::actor::Actor, public td::ObserverBase {
void send_request(std::unique_ptr<HttpRequest> request, std::shared_ptr<HttpPayload> payload); void send_request(std::unique_ptr<HttpRequest> request, std::shared_ptr<HttpPayload> payload);
void send_response(std::unique_ptr<HttpResponse> response, std::shared_ptr<HttpPayload> payload); void send_response(std::unique_ptr<HttpResponse> response, std::shared_ptr<HttpPayload> payload);
void write_payload(std::shared_ptr<HttpPayload> payload); void write_payload(std::shared_ptr<HttpPayload> payload);
void continue_payload_write(); bool continue_payload_write();
td::Status receive_request();
td::Status receive_response();
td::Status read_payload(HttpRequest *request); td::Status read_payload(HttpRequest *request);
td::Status read_payload(HttpResponse *response); td::Status read_payload(HttpResponse *response);
td::Status read_payload(std::shared_ptr<HttpPayload> payload); td::Status read_payload(std::shared_ptr<HttpPayload> payload);

View file

@ -35,7 +35,8 @@ class HttpInboundConnection : public HttpConnection {
td::Status receive_eof() override { td::Status receive_eof() override {
found_eof_ = true; found_eof_ = true;
if (reading_payload_) { if (reading_payload_) {
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) { if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof &&
reading_payload_->payload_type() != HttpPayload::PayloadType::pt_tunnel) {
return td::Status::Error("unexpected EOF"); return td::Status::Error("unexpected EOF");
} else { } else {
reading_payload_->complete_parse(); reading_payload_->complete_parse();

View file

@ -44,7 +44,8 @@ class HttpOutboundConnection : public HttpConnection {
td::Status receive_eof() override { td::Status receive_eof() override {
found_eof_ = true; found_eof_ = true;
if (reading_payload_) { if (reading_payload_) {
if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) { if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof &&
reading_payload_->payload_type() != HttpPayload::PayloadType::pt_tunnel) {
return td::Status::Error("unexpected EOF"); return td::Status::Error("unexpected EOF");
} else { } else {
LOG(INFO) << "stopping (EOF payload)"; LOG(INFO) << "stopping (EOF payload)";

View file

@ -164,6 +164,8 @@ td::Result<std::shared_ptr<HttpPayload>> HttpRequest::create_empty_payload() {
if (!need_payload()) { if (!need_payload()) {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty); return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
} else if (method_ == "CONNECT") {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_tunnel, low_watermark(), high_watermark());
} else if (found_content_length_) { } else if (found_content_length_) {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(), return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
content_length_); content_length_);
@ -175,7 +177,7 @@ td::Result<std::shared_ptr<HttpPayload>> HttpRequest::create_empty_payload() {
} }
bool HttpRequest::need_payload() const { bool HttpRequest::need_payload() const {
return found_content_length_ || found_transfer_encoding_; return found_content_length_ || found_transfer_encoding_ || method_ == "CONNECT";
} }
td::Status HttpRequest::add_header(HttpHeader header) { td::Status HttpRequest::add_header(HttpHeader header) {
@ -284,7 +286,8 @@ td::Status HttpPayload::parse(td::ChainBufferReader &input) {
case PayloadType::pt_empty: case PayloadType::pt_empty:
UNREACHABLE(); UNREACHABLE();
case PayloadType::pt_eof: case PayloadType::pt_eof:
cur_chunk_size_ = 1 << 30; case PayloadType::pt_tunnel:
cur_chunk_size_ = 1LL << 60;
break; break;
case PayloadType::pt_chunked: case PayloadType::pt_chunked:
state_ = ParseState::reading_crlf; state_ = ParseState::reading_crlf;
@ -480,17 +483,18 @@ void HttpPayload::run_callbacks() {
} }
} }
void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type) { bool HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type) {
if (store_type == PayloadType::pt_empty) { if (store_type == PayloadType::pt_empty) {
return; return false;
} }
slice_gc(); slice_gc();
bool wrote = false;
while (chunks_.size() > 0 && max_size > 0) { while (chunks_.size() > 0 && max_size > 0) {
auto cur_state = state_.load(std::memory_order_consume); auto cur_state = state_.load(std::memory_order_consume);
auto s = get_slice(max_size); auto s = get_slice(max_size);
if (s.size() == 0) { if (s.size() == 0) {
if (cur_state != ParseState::reading_trailer && cur_state != ParseState::completed) { if (cur_state != ParseState::reading_trailer && cur_state != ParseState::completed) {
return; return wrote;
} else { } else {
break; break;
} }
@ -500,28 +504,33 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
if (store_type == PayloadType::pt_chunked) { if (store_type == PayloadType::pt_chunked) {
char buf[64]; char buf[64];
::sprintf(buf, "%lx\r\n", s.size()); ::sprintf(buf, "%lx\r\n", s.size());
output.append(td::Slice(buf, strlen(buf))); auto slice = td::Slice(buf, strlen(buf));
wrote |= !slice.empty();
output.append(slice);
} }
wrote |= !s.empty();
output.append(std::move(s)); output.append(std::move(s));
if (store_type == PayloadType::pt_chunked) { if (store_type == PayloadType::pt_chunked) {
output.append(td::Slice("\r\n", 2)); output.append(td::Slice("\r\n", 2));
wrote = true;
} }
} }
if (chunks_.size() != 0) { if (chunks_.size() != 0) {
return; return wrote;
} }
if (!written_zero_chunk_) { if (!written_zero_chunk_) {
if (store_type == PayloadType::pt_chunked) { if (store_type == PayloadType::pt_chunked) {
output.append(td::Slice("0\r\n", 3)); output.append(td::Slice("0\r\n", 3));
wrote = true;
} }
written_zero_chunk_ = true; written_zero_chunk_ = true;
} }
if (store_type != PayloadType::pt_chunked) { if (store_type != PayloadType::pt_chunked) {
written_trailer_ = true; written_trailer_ = true;
return; return wrote;
} }
while (max_size > 0) { while (max_size > 0) {
@ -529,15 +538,16 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
HttpHeader h = get_header(); HttpHeader h = get_header();
if (h.empty()) { if (h.empty()) {
if (cur_state != ParseState::completed) { if (cur_state != ParseState::completed) {
return; return wrote;
} else { } else {
break; break;
} }
} }
auto s = h.size(); auto s = h.size();
h.store_http(output); h.store_http(output);
wrote = true;
if (max_size <= s) { if (max_size <= s) {
return; return wrote;
} }
max_size -= s; max_size -= s;
} }
@ -545,7 +555,9 @@ void HttpPayload::store_http(td::ChainBufferWriter &output, size_t max_size, Htt
if (!written_trailer_) { if (!written_trailer_) {
output.append(td::Slice("\r\n", 2)); output.append(td::Slice("\r\n", 2));
written_trailer_ = true; written_trailer_ = true;
wrote = true;
} }
return wrote;
} }
tl_object_ptr<ton_api::http_payloadPart> HttpPayload::store_tl(size_t max_size) { tl_object_ptr<ton_api::http_payloadPart> HttpPayload::store_tl(size_t max_size) {
@ -729,17 +741,18 @@ td::Result<std::unique_ptr<HttpResponse>> HttpResponse::parse(std::unique_ptr<Ht
} }
HttpResponse::HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload, HttpResponse::HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload,
bool keep_alive) bool keep_alive, bool is_tunnel)
: proto_version_(std::move(proto_version)) : proto_version_(std::move(proto_version))
, code_(code) , code_(code)
, reason_(std::move(reason)) , reason_(std::move(reason))
, force_no_payload_(force_no_payload) , force_no_payload_(force_no_payload)
, force_no_keep_alive_(!keep_alive) { , force_no_keep_alive_(!keep_alive)
, is_tunnel_(is_tunnel) {
} }
td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto_version, td::uint32 code, td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto_version, td::uint32 code,
std::string reason, bool force_no_payload, std::string reason, bool force_no_payload,
bool keep_alive) { bool keep_alive, bool is_tunnel) {
if (proto_version != "HTTP/1.0" && proto_version != "HTTP/1.1") { if (proto_version != "HTTP/1.0" && proto_version != "HTTP/1.1") {
return td::Status::Error(PSTRING() << "unsupported http version '" << proto_version << "'"); return td::Status::Error(PSTRING() << "unsupported http version '" << proto_version << "'");
} }
@ -749,7 +762,7 @@ td::Result<std::unique_ptr<HttpResponse>> HttpResponse::create(std::string proto
} }
return std::make_unique<HttpResponse>(std::move(proto_version), code, std::move(reason), force_no_payload, return std::make_unique<HttpResponse>(std::move(proto_version), code, std::move(reason), force_no_payload,
keep_alive); keep_alive, is_tunnel);
} }
td::Status HttpResponse::complete_parse_header() { td::Status HttpResponse::complete_parse_header() {
@ -767,6 +780,8 @@ td::Result<std::shared_ptr<HttpPayload>> HttpResponse::create_empty_payload() {
if (!need_payload()) { if (!need_payload()) {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty); return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_empty);
} else if (is_tunnel_) {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_tunnel, low_watermark(), high_watermark());
} else if (found_content_length_) { } else if (found_content_length_) {
return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(), return std::make_shared<HttpPayload>(HttpPayload::PayloadType::pt_content_length, low_watermark(), high_watermark(),
content_length_); content_length_);
@ -828,10 +843,12 @@ void HttpResponse::store_http(td::ChainBufferWriter &output) {
for (auto &x : options_) { for (auto &x : options_) {
x.store_http(output); x.store_http(output);
} }
if (keep_alive_) { if (!is_tunnel_) {
HttpHeader{"Connection", "Keep-Alive"}.store_http(output); if (keep_alive_) {
} else { HttpHeader{"Connection", "Keep-Alive"}.store_http(output);
HttpHeader{"Connection", "Close"}.store_http(output); } else {
HttpHeader{"Connection", "Close"}.store_http(output);
}
} }
output.append(td::Slice("\r\n", 2)); output.append(td::Slice("\r\n", 2));
} }
@ -893,7 +910,9 @@ void answer_error(HttpStatusCode code, std::string reason,
} }
auto response = HttpResponse::create("HTTP/1.0", code, reason, false, false).move_as_ok(); auto response = HttpResponse::create("HTTP/1.0", code, reason, false, false).move_as_ok();
response->add_header(HttpHeader{"Content-Length", "0"}); response->add_header(HttpHeader{"Content-Length", "0"});
response->complete_parse_header();
auto payload = response->create_empty_payload().move_as_ok(); auto payload = response->create_empty_payload().move_as_ok();
payload->complete_parse();
CHECK(payload->parse_completed()); CHECK(payload->parse_completed());
promise.set_value(std::make_pair(std::move(response), std::move(payload))); promise.set_value(std::make_pair(std::move(response), std::move(payload)));
} }

View file

@ -64,7 +64,7 @@ td::Result<HttpHeader> get_header(std::string line);
class HttpPayload { class HttpPayload {
public: public:
enum class PayloadType { pt_empty, pt_eof, pt_chunked, pt_content_length }; enum class PayloadType { pt_empty, pt_eof, pt_chunked, pt_content_length, pt_tunnel };
HttpPayload(PayloadType t, size_t low_watermark, size_t high_watermark, td::uint64 size) HttpPayload(PayloadType t, size_t low_watermark, size_t high_watermark, td::uint64 size)
: type_(t), low_watermark_(low_watermark), high_watermark_(high_watermark), cur_chunk_size_(size) { : type_(t), low_watermark_(low_watermark), high_watermark_(high_watermark), cur_chunk_size_(size) {
CHECK(t == PayloadType::pt_content_length); CHECK(t == PayloadType::pt_content_length);
@ -75,17 +75,15 @@ class HttpPayload {
CHECK(t != PayloadType::pt_content_length); CHECK(t != PayloadType::pt_content_length);
CHECK(t != PayloadType::pt_empty); CHECK(t != PayloadType::pt_empty);
switch (t) { switch (t) {
case PayloadType::pt_empty:
UNREACHABLE();
case PayloadType::pt_eof: case PayloadType::pt_eof:
case PayloadType::pt_tunnel:
state_ = ParseState::reading_chunk_data; state_ = ParseState::reading_chunk_data;
break; break;
case PayloadType::pt_chunked: case PayloadType::pt_chunked:
state_ = ParseState::reading_chunk_header; state_ = ParseState::reading_chunk_header;
break; break;
case PayloadType::pt_content_length: default:
state_ = ParseState::reading_chunk_data; UNREACHABLE();
break;
} }
} }
HttpPayload(PayloadType t) : type_(t) { HttpPayload(PayloadType t) : type_(t) {
@ -136,7 +134,7 @@ class HttpPayload {
void slice_gc(); void slice_gc();
HttpHeader get_header(); HttpHeader get_header();
void store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type); bool store_http(td::ChainBufferWriter &output, size_t max_size, HttpPayload::PayloadType store_type);
tl_object_ptr<ton_api::http_payloadPart> store_tl(size_t max_size); tl_object_ptr<ton_api::http_payloadPart> store_tl(size_t max_size);
bool written() const { bool written() const {
@ -267,9 +265,11 @@ class HttpResponse {
} }
static td::Result<std::unique_ptr<HttpResponse>> create(std::string proto_version, td::uint32 code, static td::Result<std::unique_ptr<HttpResponse>> create(std::string proto_version, td::uint32 code,
std::string reason, bool force_no_payload, bool keep_alive); std::string reason, bool force_no_payload, bool keep_alive,
bool is_tunnel = false);
HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload, bool keep_alive); HttpResponse(std::string proto_version, td::uint32 code, std::string reason, bool force_no_payload, bool keep_alive,
bool is_tunnel = false);
bool check_parse_header_completed() const; bool check_parse_header_completed() const;
bool keep_alive() const { bool keep_alive() const {
@ -323,6 +323,7 @@ class HttpResponse {
bool keep_alive_ = false; bool keep_alive_ = false;
std::vector<HttpHeader> options_; std::vector<HttpHeader> options_;
bool is_tunnel_ = false;
}; };
void answer_error(HttpStatusCode code, std::string reason, void answer_error(HttpStatusCode code, std::string reason,

View file

@ -52,6 +52,8 @@
#include <list> #include <list>
#include <set> #include <set>
#include "git.h" #include "git.h"
#include "td/utils/BufferedFd.h"
#include "common/delay.h"
#if TD_DARWIN || TD_LINUX #if TD_DARWIN || TD_LINUX
#include <unistd.h> #include <unistd.h>
@ -128,8 +130,15 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
public: public:
HttpRldpPayloadReceiver(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id, HttpRldpPayloadReceiver(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp) td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp,
: payload_(std::move(payload)), id_(transfer_id), src_(src), local_id_(local_id), adnl_(adnl), rldp_(rldp) { bool is_tunnel = false)
: payload_(std::move(payload))
, id_(transfer_id)
, src_(src)
, local_id_(local_id)
, adnl_(adnl)
, rldp_(rldp)
, is_tunnel_(is_tunnel) {
} }
void start_up() override { void start_up() override {
@ -178,13 +187,14 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>( auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>(
id_, seqno_++, static_cast<td::int32>(chunk_size())); id_, seqno_++, static_cast<td::int32>(chunk_size()));
auto timeout = td::Timestamp::in(is_tunnel_ ? 60.0 : 15.0);
td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P), td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P),
td::Timestamp::in(15.0), std::move(f), 2 * chunk_size() + 1024); timeout, std::move(f), 2 * chunk_size() + 1024);
} }
void add_data(td::BufferSlice data) { void add_data(td::BufferSlice data) {
LOG(INFO) << "HttpPayloadReceiver: received answer (size " << data.size() << ")"; LOG(INFO) << "HttpPayloadReceiver: received answer (size " << data.size() << ")";
auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(std::move(data), true); auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(data, true);
if (F.is_error()) { if (F.is_error()) {
abort_query(F.move_as_error()); abort_query(F.move_as_error());
return; return;
@ -243,14 +253,20 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
bool sent_ = false; bool sent_ = false;
td::int32 seqno_ = 0; td::int32 seqno_ = 0;
bool is_tunnel_;
}; };
class HttpRldpPayloadSender : public td::actor::Actor { class HttpRldpPayloadSender : public td::actor::Actor {
public: public:
HttpRldpPayloadSender(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id, HttpRldpPayloadSender(std::shared_ptr<ton::http::HttpPayload> payload, td::Bits256 transfer_id,
ton::adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ton::adnl::Adnl> adnl, ton::adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ton::adnl::Adnl> adnl,
td::actor::ActorId<ton::rldp::Rldp> rldp) td::actor::ActorId<ton::rldp::Rldp> rldp, bool is_tunnel = false)
: payload_(std::move(payload)), id_(transfer_id), local_id_(local_id), adnl_(adnl), rldp_(rldp) { : payload_(std::move(payload))
, id_(transfer_id)
, local_id_(local_id)
, adnl_(adnl)
, rldp_(rldp)
, is_tunnel_(is_tunnel) {
} }
std::string generate_prefix() const { std::string generate_prefix() const {
@ -287,32 +303,36 @@ class HttpRldpPayloadSender : public td::actor::Actor {
class Cb : public ton::http::HttpPayload::Callback { class Cb : public ton::http::HttpPayload::Callback {
public: public:
Cb(td::actor::ActorId<HttpRldpPayloadSender> id) : self_id_(id) { Cb(td::actor::ActorId<HttpRldpPayloadSender> id, size_t watermark) : self_id_(id), watermark_(watermark) {
} }
void run(size_t ready_bytes) override { void run(size_t ready_bytes) override {
if (!reached_ && ready_bytes >= watermark_) { if (!reached_ && ready_bytes >= watermark_) {
reached_ = true; reached_ = true;
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query); td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query, false);
} else if (reached_ && ready_bytes < watermark_) { } else if (reached_ && ready_bytes < watermark_) {
reached_ = false; reached_ = false;
} }
} }
void completed() override { void completed() override {
td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query); td::actor::send_closure(self_id_, &HttpRldpPayloadSender::try_answer_query, false);
} }
private: private:
size_t watermark_ = ton::http::HttpRequest::low_watermark();
bool reached_ = false; bool reached_ = false;
td::actor::ActorId<HttpRldpPayloadSender> self_id_; td::actor::ActorId<HttpRldpPayloadSender> self_id_;
size_t watermark_;
}; };
payload_->add_callback(std::make_unique<Cb>(actor_id(this))); payload_->add_callback(std::make_unique<Cb>(actor_id(this),
is_tunnel_ ? 1 : ton::http::HttpRequest::low_watermark()));
alarm_timestamp() = td::Timestamp::in(10.0); alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 60.0 : 10.0);
} }
void try_answer_query() { void try_answer_query(bool from_timer = false) {
if (from_timer) {
active_timer_ = false;
}
if (!cur_query_promise_) { if (!cur_query_promise_) {
return; return;
} }
@ -321,6 +341,15 @@ class HttpRldpPayloadSender : public td::actor::Actor {
} }
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) { if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) {
answer_query(); answer_query();
} else if (!is_tunnel_ || payload_->ready_bytes() == 0) {
return;
} else if (from_timer) {
answer_query();
} else if (!active_timer_) {
active_timer_ = true;
ton::delay_action([SelfId = actor_id(this)]() {
td::actor::send_closure(SelfId, &HttpRldpPayloadSender::try_answer_query, true);
}, td::Timestamp::in(0.001));
} }
} }
@ -348,16 +377,12 @@ class HttpRldpPayloadSender : public td::actor::Actor {
LOG(INFO) << "received request. size=" << cur_query_size_ << " parse_completed=" << payload_->parse_completed() LOG(INFO) << "received request. size=" << cur_query_size_ << " parse_completed=" << payload_->parse_completed()
<< " ready_bytes=" << payload_->ready_bytes(); << " ready_bytes=" << payload_->ready_bytes();
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) { alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 50.0 : 10.0);
answer_query(); try_answer_query(false);
return;
}
alarm_timestamp() = td::Timestamp::in(10.0);
} }
void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) { void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(std::move(data), true); auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(data, true);
if (F.is_error()) { if (F.is_error()) {
LOG(INFO) << "failed to parse query: " << F.move_as_error(); LOG(INFO) << "failed to parse query: " << F.move_as_error();
return; return;
@ -367,6 +392,10 @@ class HttpRldpPayloadSender : public td::actor::Actor {
void alarm() override { void alarm() override {
if (cur_query_promise_) { if (cur_query_promise_) {
if (is_tunnel_) {
answer_query();
return;
}
LOG(INFO) << "timeout on inbound connection. closing http transfer"; LOG(INFO) << "timeout on inbound connection. closing http transfer";
} else { } else {
LOG(INFO) << "timeout on RLDP connection. closing http transfer"; LOG(INFO) << "timeout on RLDP connection. closing http transfer";
@ -382,7 +411,7 @@ class HttpRldpPayloadSender : public td::actor::Actor {
} }
seqno_++; seqno_++;
alarm_timestamp() = td::Timestamp::in(30.0); alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 60.0 : 30.0);
} }
void abort_query(td::Status error) { void abort_query(td::Status error) {
@ -412,6 +441,7 @@ class HttpRldpPayloadSender : public td::actor::Actor {
size_t cur_query_size_; size_t cur_query_size_;
td::Promise<td::BufferSlice> cur_query_promise_; td::Promise<td::BufferSlice> cur_query_promise_;
bool is_tunnel_, active_timer_ = false;
}; };
class RldpHttpProxy; class RldpHttpProxy;
@ -452,7 +482,8 @@ class TcpToRldpRequestSender : public td::actor::Actor {
} }
}); });
td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender", request_payload_, id_, local_id_, adnl_, rldp_) td::actor::create_actor<HttpRldpPayloadSender>("HttpPayloadSender", request_payload_, id_, local_id_, adnl_, rldp_,
is_tunnel())
.release(); .release();
auto f = ton::serialize_tl_object(request_->store_tl(id_), true); auto f = ton::serialize_tl_object(request_->store_tl(id_), true);
@ -461,13 +492,14 @@ class TcpToRldpRequestSender : public td::actor::Actor {
} }
void got_result(td::BufferSlice data) { void got_result(td::BufferSlice data) {
auto F = ton::fetch_tl_object<ton::ton_api::http_response>(std::move(data), true); auto F = ton::fetch_tl_object<ton::ton_api::http_response>(data, true);
if (F.is_error()) { if (F.is_error()) {
abort_query(F.move_as_error()); abort_query(F.move_as_error());
return; return;
} }
auto f = F.move_as_ok(); auto f = F.move_as_ok();
auto R = ton::http::HttpResponse::create(f->http_version_, f->status_code_, f->reason_, false, true); auto R = ton::http::HttpResponse::create(
f->http_version_, f->status_code_, f->reason_, false, true, is_tunnel() && f->status_code_ == 200);
if (R.is_error()) { if (R.is_error()) {
abort_query(R.move_as_error()); abort_query(R.move_as_error());
return; return;
@ -498,7 +530,7 @@ class TcpToRldpRequestSender : public td::actor::Actor {
} }
}); });
td::actor::create_actor<HttpRldpPayloadReceiver>("HttpPayloadReceiver", response_payload_, id_, dst_, local_id_, td::actor::create_actor<HttpRldpPayloadReceiver>("HttpPayloadReceiver", response_payload_, id_, dst_, local_id_,
adnl_, rldp_) adnl_, rldp_, is_tunnel())
.release(); .release();
promise_.set_value(std::make_pair(std::move(response_), std::move(response_payload_))); promise_.set_value(std::make_pair(std::move(response_), std::move(response_payload_)));
@ -515,6 +547,10 @@ class TcpToRldpRequestSender : public td::actor::Actor {
} }
protected: protected:
bool is_tunnel() const {
return request_->method() == "CONNECT";
}
td::Bits256 id_; td::Bits256 id_;
ton::adnl::AdnlNodeIdShort local_id_; ton::adnl::AdnlNodeIdShort local_id_;
@ -535,6 +571,208 @@ class TcpToRldpRequestSender : public td::actor::Actor {
std::shared_ptr<ton::http::HttpPayload> response_payload_; std::shared_ptr<ton::http::HttpPayload> response_payload_;
}; };
class RldpTcpTunnel : public td::actor::Actor, private td::ObserverBase {
public:
RldpTcpTunnel(td::Bits256 transfer_id, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
td::actor::ActorId<ton::adnl::Adnl> adnl, td::actor::ActorId<ton::rldp::Rldp> rldp, td::SocketFd fd)
: id_(transfer_id)
, src_(src)
, local_id_(local_id)
, adnl_(std::move(adnl))
, rldp_(std::move(rldp))
, fd_(std::move(fd)) {
}
void start_up() override {
self_ = actor_id(this);
td::actor::SchedulerContext::get()->get_poll().subscribe(fd_.get_poll_info().extract_pollable_fd(this),
td::PollFlags::ReadWrite());
class Cb : public ton::adnl::Adnl::Callback {
public:
explicit Cb(td::actor::ActorId<RldpTcpTunnel> id) : self_id_(std::move(id)) {
}
void receive_message(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst,
td::BufferSlice data) override {
LOG(INFO) << "rldp tcp tunnel: dropping message";
}
void receive_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) override {
td::actor::send_closure(self_id_, &RldpTcpTunnel::receive_query, std::move(data), std::move(promise));
}
private:
td::actor::ActorId<RldpTcpTunnel> self_id_;
};
td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, local_id_, generate_prefix(),
std::make_unique<Cb>(actor_id(this)));
process();
}
void tear_down() override {
LOG(INFO) << "RldpTcpTunnel: tear_down";
td::actor::send_closure(adnl_, &ton::adnl::Adnl::unsubscribe, local_id_, generate_prefix());
td::actor::SchedulerContext::get()->get_poll().unsubscribe(fd_.get_poll_info().get_pollable_fd_ref());
}
void notify() override {
td::actor::send_closure(self_, &RldpTcpTunnel::process);
}
void request_data() {
if (close_ || sent_request_) {
return;
}
sent_request_ = true;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
td::actor::send_closure(SelfId, &RldpTcpTunnel::got_data_from_rldp, std::move(R));
});
auto f = ton::create_serialize_tl_object<ton::ton_api::http_getNextPayloadPart>(id_, out_seqno_++, 1 << 17);
td::actor::send_closure(rldp_, &ton::rldp::Rldp::send_query_ex, local_id_, src_, "payload part", std::move(P),
td::Timestamp::in(60.0), std::move(f), (1 << 18) + 1024);
}
void receive_query(td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
auto F = ton::fetch_tl_object<ton::ton_api::http_getNextPayloadPart>(data, true);
if (F.is_error()) {
LOG(INFO) << "failed to parse query: " << F.error();
promise.set_error(F.move_as_error());
return;
}
auto f = F.move_as_ok();
if (cur_promise_) {
LOG(INFO) << "failed to process query: previous query is active";
promise.set_error(td::Status::Error("previous query is active"));
return;
}
if (f->seqno_ != cur_seqno_) {
LOG(INFO) << "failed to process query: seqno mismatch";
promise.set_error(td::Status::Error("seqno mismatch"));
return;
}
LOG(INFO) << "RldpTcpTunnel: received query, seqno=" << cur_seqno_;
cur_promise_ = std::move(promise);
cur_max_chunk_size_ = f->max_chunk_size_;
alarm_timestamp() = td::Timestamp::in(50.0);
process();
}
void got_data_from_rldp(td::Result<td::BufferSlice> R) {
if (R.is_error()) {
abort(R.move_as_error());
return;
}
td::BufferSlice data = R.move_as_ok();
LOG(INFO) << "RldpTcpTunnel: received data from rldp: size=" << data.size();
sent_request_ = false;
auto F = ton::fetch_tl_object<ton::ton_api::http_payloadPart>(data, true);
if (F.is_error()) {
abort(F.move_as_error());
return;
}
auto f = F.move_as_ok();
fd_.output_buffer().append(std::move(f->data_));
if (f->last_) {
got_last_part_ = true;
}
process();
}
void process() {
if (!close_) {
auto status = [&] {
TRY_STATUS(fd_.flush_read());
TRY_STATUS(fd_.flush_write());
close_ = td::can_close(fd_);
return td::Status::OK();
}();
if (status.is_error()) {
abort(std::move(status));
return;
}
}
if (got_last_part_) {
close_ = true;
}
answer_query();
request_data();
}
void answer_query(bool allow_empty = false, bool from_timer = false) {
if (from_timer) {
active_timer_ = false;
}
auto &input = fd_.input_buffer();
if (cur_promise_ && (!input.empty() || close_ || allow_empty)) {
if (!from_timer && !close_ && !allow_empty && input.size() < ton::http::HttpRequest::low_watermark()) {
if (!active_timer_) {
active_timer_ = true;
ton::delay_action([SelfId = actor_id(this)]() {
td::actor::send_closure(SelfId, &RldpTcpTunnel::answer_query, false, true);
}, td::Timestamp::in(0.001));
}
return;
}
size_t s = std::min<size_t>(input.size(), cur_max_chunk_size_);
td::BufferSlice data(s);
LOG(INFO) << "RldpTcpTunnel: sending data to rldp: size=" << data.size();
input.advance(s, td::as_mutable_slice(data));
cur_promise_.set_result(ton::create_serialize_tl_object<ton::ton_api::http_payloadPart>(
std::move(data), std::vector<ton::tl_object_ptr<ton::ton_api::http_header>>(), close_));
++cur_seqno_;
cur_promise_.reset();
alarm_timestamp() = td::Timestamp::never();
if (close_) {
stop();
return;
}
}
}
void alarm() override {
answer_query(true, false);
}
void abort(td::Status status) {
LOG(INFO) << "RldpTcpTunnel error: " << status;
if (cur_promise_) {
cur_promise_.set_error(status.move_as_error());
}
stop();
}
private:
std::string generate_prefix() const {
std::string x(static_cast<size_t>(36), '\0');
auto S = td::MutableSlice{x};
CHECK(S.size() == 36);
auto id = ton::ton_api::http_getNextPayloadPart::ID;
S.copy_from(td::Slice(reinterpret_cast<const td::uint8 *>(&id), 4));
S.remove_prefix(4);
S.copy_from(id_.as_slice());
return x;
}
td::Bits256 id_;
ton::adnl::AdnlNodeIdShort src_;
ton::adnl::AdnlNodeIdShort local_id_;
td::actor::ActorId<ton::adnl::Adnl> adnl_;
td::actor::ActorId<ton::rldp::Rldp> rldp_;
td::BufferedFd<td::SocketFd> fd_;
td::actor::ActorId<RldpTcpTunnel> self_;
td::int32 cur_seqno_ = 0, cur_max_chunk_size_ = 0;
td::Promise<td::BufferSlice> cur_promise_;
td::int32 out_seqno_ = 0;
bool close_ = false, sent_request_ = false, got_last_part_ = false;
bool active_timer_ = false;
};
class RldpToTcpRequestSender : public td::actor::Actor { class RldpToTcpRequestSender : public td::actor::Actor {
public: public:
RldpToTcpRequestSender(td::Bits256 id, ton::adnl::AdnlNodeIdShort local_id, ton::adnl::AdnlNodeIdShort dst, RldpToTcpRequestSender(td::Bits256 id, ton::adnl::AdnlNodeIdShort local_id, ton::adnl::AdnlNodeIdShort dst,
@ -627,8 +865,8 @@ class RldpHttpProxy : public td::actor::Actor {
client_port_ = port; client_port_ = port;
} }
void set_local_host(std::string name, td::IPAddress remote) { void set_local_host(std::string host, td::uint16 port, td::IPAddress remote) {
local_hosts_.emplace_back(std::move(name), std::move(remote)); hosts_[host].ports_[port].remote_addr_ = remote;
} }
void receive_request_result(td::uint64 id, td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) { void receive_request_result(td::uint64 id, td::Result<tonlib_api::object_ptr<tonlib_api::Object>> R) {
@ -688,7 +926,7 @@ class RldpHttpProxy : public td::actor::Actor {
} }
void store_dht() { void store_dht() {
for (auto &serv : local_hosts_) { for (auto &serv : hosts_) {
if (serv.first != "*") { if (serv.first != "*") {
for (auto &serv_id : server_ids_) { for (auto &serv_id : server_ids_) {
ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first}; ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first};
@ -765,7 +1003,7 @@ class RldpHttpProxy : public td::actor::Actor {
} }
void run_cont() { void run_cont() {
if (is_client_ && local_hosts_.size() > 0) { if (is_client_ && hosts_.size() > 0) {
LOG(ERROR) << "client-only node cannot be server"; LOG(ERROR) << "client-only node cannot be server";
std::_Exit(2); std::_Exit(2);
} }
@ -876,9 +1114,6 @@ class RldpHttpProxy : public td::actor::Actor {
ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID), ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID),
std::make_unique<AdnlCb>(actor_id(this))); std::make_unique<AdnlCb>(actor_id(this)));
} }
for (auto &serv : local_hosts_) {
servers_.emplace(serv.first, td::actor::create_actor<HttpRemote>("remote", serv.second));
}
rldp_ = ton::rldp::Rldp::create(adnl_.get()); rldp_ = ton::rldp::Rldp::create(adnl_.get());
td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, local_id_); td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, local_id_);
@ -938,7 +1173,7 @@ class RldpHttpProxy : public td::actor::Actor {
void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) { td::Promise<td::BufferSlice> promise) {
LOG(INFO) << "got HTTP request over rldp from " << src; LOG(INFO) << "got HTTP request over rldp from " << src;
TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object<ton::ton_api::http_request>(std::move(data), true)); TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object<ton::ton_api::http_request>(data, true));
TRY_RESULT_PROMISE(promise, request, ton::http::HttpRequest::create(f->method_, f->url_, f->http_version_)); TRY_RESULT_PROMISE(promise, request, ton::http::HttpRequest::create(f->method_, f->url_, f->http_version_));
for (auto &x : f->headers_) { for (auto &x : f->headers_) {
ton::http::HttpHeader h{x->name_, x->value_}; ton::http::HttpHeader h{x->name_, x->value_};
@ -947,7 +1182,8 @@ class RldpHttpProxy : public td::actor::Actor {
} }
TRY_STATUS_PROMISE(promise, request->complete_parse_header()); TRY_STATUS_PROMISE(promise, request->complete_parse_header());
auto host = request->host(); auto host = request->host();
if (host.size() == 0) { td::uint16 port = 80;
if (host.empty()) {
host = request->url(); host = request->url();
if (host.size() >= 7 && host.substr(0, 7) == "http://") { if (host.size() >= 7 && host.substr(0, 7) == "http://") {
host = host.substr(7); host = host.substr(7);
@ -972,29 +1208,60 @@ class RldpHttpProxy : public td::actor::Actor {
{ {
auto p = host.find(':'); auto p = host.find(':');
if (p != std::string::npos) { if (p != std::string::npos) {
try {
port = (td::uint16)std::stoul(host.substr(p + 1));
} catch (const std::logic_error &) {
port = 80;
promise.set_error(td::Status::Error(PSLICE() << "Invalid port '" << host.substr(p + 1) << "'"));
return;
}
host = host.substr(0, p); host = host.substr(0, p);
} }
} }
std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); }); std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); });
auto it = servers_.find(host); auto it = hosts_.find(host);
if (it == servers_.end()) { if (it == hosts_.end()) {
it = servers_.find("*"); it = hosts_.find("*");
if (it == servers_.end()) { if (it == hosts_.end()) {
promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown server name")); promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown server name"));
return; return;
} }
} }
auto it2 = it->second.ports_.find(port);
if (it2 == it->second.ports_.end()) {
promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown host:port"));
return;
}
auto &server = it2->second;
if (request->method() == "CONNECT") {
LOG(INFO) << "starting HTTP tunnel over RLDP to " << server.remote_addr_;
start_tcp_tunnel(f->id_, src, dst, server.remote_addr_, std::move(promise));
return;
}
if (server.http_remote_.empty()) {
server.http_remote_ = td::actor::create_actor<HttpRemote>("remote", server.remote_addr_);
}
TRY_RESULT_PROMISE(promise, payload, request->create_empty_payload()); TRY_RESULT_PROMISE(promise, payload, request->create_empty_payload());
LOG(INFO) << "starting HTTP over RLDP request"; LOG(INFO) << "starting HTTP over RLDP request";
td::actor::create_actor<RldpToTcpRequestSender>("inboundreq", f->id_, dst, src, std::move(request), td::actor::create_actor<RldpToTcpRequestSender>("inboundreq", f->id_, dst, src, std::move(request),
std::move(payload), std::move(promise), adnl_.get(), rldp_.get(), std::move(payload), std::move(promise), adnl_.get(), rldp_.get(),
it->second.get()) server.http_remote_.get())
.release(); .release();
} }
void start_tcp_tunnel(td::Bits256 id, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort local_id,
td::IPAddress ip, td::Promise<td::BufferSlice> promise) {
TRY_RESULT_PROMISE(promise, fd, td::SocketFd::open(ip));
td::actor::create_actor<RldpTcpTunnel>(td::actor::ActorOptions().with_name("tunnel").with_poll(), id, src, local_id,
adnl_.get(), rldp_.get(), std::move(fd)).release();
promise.set_result(ton::create_serialize_tl_object<ton::ton_api::http_response>(
"HTTP/1.1", 200, "Connection Established", std::vector<ton::tl_object_ptr<ton::ton_api::http_header>>()));
}
void add_adnl_addr(ton::adnl::AdnlNodeIdShort id) { void add_adnl_addr(ton::adnl::AdnlNodeIdShort id) {
server_ids_.insert(id); server_ids_.insert(id);
} }
@ -1008,10 +1275,17 @@ class RldpHttpProxy : public td::actor::Actor {
} }
private: private:
struct Host {
struct Server {
td::IPAddress remote_addr_;
td::actor::ActorOwn<HttpRemote> http_remote_;
};
std::map<td::uint16, Server> ports_;
};
td::uint16 port_{0}; td::uint16 port_{0};
td::IPAddress addr_; td::IPAddress addr_;
std::string global_config_; std::string global_config_;
std::vector<std::pair<std::string, td::IPAddress>> local_hosts_;
bool is_client_{false}; bool is_client_{false};
td::uint16 client_port_{0}; td::uint16 client_port_{0};
@ -1023,7 +1297,7 @@ class RldpHttpProxy : public td::actor::Actor {
td::actor::ActorOwn<ton::http::HttpServer> server_; td::actor::ActorOwn<ton::http::HttpServer> server_;
std::map<std::string, ton::adnl::AdnlNodeIdShort> dns_; std::map<std::string, ton::adnl::AdnlNodeIdShort> dns_;
std::map<std::string, td::actor::ActorOwn<HttpRemote>> servers_; std::map<std::string, Host> hosts_;
td::actor::ActorOwn<ton::keyring::Keyring> keyring_; td::actor::ActorOwn<ton::keyring::Keyring> keyring_;
td::actor::ActorOwn<ton::adnl::AdnlNetworkManager> adnl_network_manager_; td::actor::ActorOwn<ton::adnl::AdnlNetworkManager> adnl_network_manager_;
@ -1123,6 +1397,42 @@ int main(int argc, char *argv[]) {
td::log_interface = td::default_log_interface; td::log_interface = td::default_log_interface;
}; };
auto add_local_host = [&](const std::string& local, const std::string& remote) -> td::Status {
std::string host;
std::vector<td::uint16> ports;
auto p = local.find(':');
if (p == std::string::npos) {
host = local;
ports = {80, 443};
} else {
host = local.substr(0, p);
++p;
while (p < local.size()) {
auto p2 = local.find(',', p);
if (p2 == std::string::npos) {
p2 = local.size();
}
try {
ports.push_back((td::uint16)std::stoul(local.substr(p, p2 - p)));
} catch (const std::logic_error& e) {
return td::Status::Error(PSLICE() << "Invalid port: " << local.substr(p, p2 - p));
}
p = p2 + 1;
}
}
for (td::uint16 port : ports) {
std::string cur_remote = remote;
if (cur_remote.find(':') == std::string::npos) {
cur_remote += ':';
cur_remote += std::to_string(port);
}
td::IPAddress addr;
TRY_STATUS(addr.init_host_port(cur_remote));
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, host, port, addr);
}
return td::Status::OK();
};
td::OptionParser p; td::OptionParser p;
p.set_description( p.set_description(
"A simple rldp-to-http and http-to-rldp proxy for running and accessing ton sites\n" "A simple rldp-to-http and http-to-rldp proxy for running and accessing ton sites\n"
@ -1136,7 +1446,8 @@ int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(v); SET_VERBOSITY_LEVEL(v);
}); });
p.add_option('V', "version", "shows rldp-http-proxy build information", [&]() { p.add_option('V', "version", "shows rldp-http-proxy build information", [&]() {
std::cout << "rldp-http-proxy build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; std::cout << "rldp-http-proxy build information: [ Commit: " << GitMetadata::CommitSHA1()
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0); std::exit(0);
}); });
p.add_option('h', "help", "prints a help message", [&]() { p.add_option('h', "help", "prints a help message", [&]() {
@ -1170,27 +1481,25 @@ int main(int argc, char *argv[]) {
}); });
p.add_option('C', "global-config", "global TON configuration file", p.add_option('C', "global-config", "global TON configuration file",
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_global_config, arg.str()); }); [&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_global_config, arg.str()); });
p.add_checked_option('L', "local", "http hostname that will be proxied to http server at localhost:80", p.add_checked_option('L', "local",
"<hosthame>:<ports>, hostname that will be proxied to localhost\n"
"<ports> is a comma-separated list of ports (may be omitted, default: 80, 443)\n",
[&](td::Slice arg) -> td::Status { [&](td::Slice arg) -> td::Status {
td::IPAddress addr; return add_local_host(arg.str(), "127.0.0.1");
TRY_STATUS(addr.init_ipv4_port("127.0.0.1", 80));
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, arg.str(), addr);
return td::Status::OK();
}); });
p.add_option('D', "db", "db root", p.add_option('D', "db", "db root",
[&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_db_root, arg.str()); }); [&](td::Slice arg) { td::actor::send_closure(x, &RldpHttpProxy::set_db_root, arg.str()); });
p.add_checked_option( p.add_checked_option(
'R', "remote", 'R', "remote",
"<hostname>@<ip>:<port>, indicates a http hostname that will be proxied to remote http server at <ip>:<port>", "<hostname>:<ports>@<ip>:<port>, indicates a hostname that will be proxied to remote server at <ip>:<port>\n"
"<ports> is a comma-separated list of ports (may be omitted, default: 80,433)\n"
"<port> is a remote port (may be omitted, default: same as host's port)",
[&](td::Slice arg) -> td::Status { [&](td::Slice arg) -> td::Status {
auto ch = arg.find('@'); auto ch = arg.find('@');
if (ch == td::Slice::npos) { if (ch == td::Slice::npos) {
return td::Status::Error("bad format for --remote"); return td::Status::Error("bad format for --remote");
} }
td::IPAddress addr; return add_local_host(arg.substr(0, ch).str(), arg.substr(ch + 1).str());
TRY_STATUS(addr.init_host_port(arg.substr(ch + 1).str()));
td::actor::send_closure(x, &RldpHttpProxy::set_local_host, arg.substr(0, ch).str(), addr);
return td::Status::OK();
}); });
p.add_option('d', "daemonize", "set SIGHUP", [&]() { p.add_option('d', "daemonize", "set SIGHUP", [&]() {
td::set_signal_handler(td::SignalType::HangUp, [](int sig) { td::set_signal_handler(td::SignalType::HangUp, [](int sig) {