2
This file is part of TON Blockchain source code.
4
TON Blockchain is free software; you can redistribute it and/or
5
modify it under the terms of the GNU General Public License
6
as published by the Free Software Foundation; either version 2
7
of the License, or (at your option) any later version.
9
TON Blockchain is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
17
In addition, as a special exception, the copyright holders give permission
18
to link the code of portions of this program with the OpenSSL library.
19
You must obey the GNU General Public License in all respects for all
20
of the code used other than OpenSSL. If you modify file(s) with this
21
exception, you may extend this exception to your version of the file(s),
22
but you are not obligated to do so. If you do not wish to do so, delete this
23
exception statement from your version. If you delete this exception statement
24
from all source files in the program, then also delete it here.
26
Copyright 2019-2020 Telegram Systems LLP
28
#include "http/http-server.h"
29
#include "http/http-client.h"
31
#include "td/utils/port/signals.h"
32
#include "td/utils/OptionParser.h"
33
#include "td/utils/FileLog.h"
39
#if TD_DARWIN || TD_LINUX
45
class HttpRemote : public td::actor::Actor {
48
std::unique_ptr<ton::http::HttpRequest> request;
49
std::shared_ptr<ton::http::HttpPayload> payload;
50
td::Timestamp timeout;
51
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>> promise;
53
HttpRemote(std::string domain, td::actor::ActorId<HttpProxy> proxy) : domain_(std::move(domain)), proxy_(proxy) {
55
void start_up() override {
56
class Cb : public ton::http::HttpClient::Callback {
58
Cb(td::actor::ActorId<HttpRemote> id) : id_(id) {
60
void on_ready() override {
61
td::actor::send_closure(id_, &HttpRemote::set_ready, true);
63
void on_stop_ready() override {
64
td::actor::send_closure(id_, &HttpRemote::set_ready, false);
68
td::actor::ActorId<HttpRemote> id_;
70
client_ = ton::http::HttpClient::create_multi(domain_, td::IPAddress(), 1, 1, std::make_shared<Cb>(actor_id(this)));
71
fail_at_ = td::Timestamp::in(10.0);
72
close_at_ = td::Timestamp::in(60.0);
74
void set_ready(bool ready) {
75
if (ready == ready_) {
80
fail_at_ = td::Timestamp::in(10.0);
81
alarm_timestamp().relax(fail_at_);
83
fail_at_ = td::Timestamp::never();
84
while (list_.size() > 0) {
85
auto q = std::move(list_.front());
87
td::actor::send_closure(client_, &ton::http::HttpClient::send_request, std::move(q.request),
88
std::move(q.payload), q.timeout, std::move(q.promise));
89
close_at_ = td::Timestamp::in(60.0);
94
std::unique_ptr<ton::http::HttpRequest> request, std::shared_ptr<ton::http::HttpPayload> payload,
95
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>>
97
bool keep = request->keep_alive();
98
auto P = td::PromiseCreator::lambda(
99
[promise = std::move(promise),
100
keep](td::Result<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>>
103
promise.set_error(R.move_as_error());
105
auto v = R.move_as_ok();
106
v.first->set_keep_alive(keep);
107
if (v.second->payload_type() != ton::http::HttpPayload::PayloadType::pt_empty &&
108
!v.first->found_content_length() && !v.first->found_transfer_encoding()) {
109
v.first->add_header(ton::http::HttpHeader{"Transfer-Encoding", "Chunked"});
111
promise.set_value(std::move(v));
115
td::actor::send_closure(client_, &ton::http::HttpClient::send_request, std::move(request), std::move(payload),
116
td::Timestamp::in(3.0), std::move(P));
117
close_at_ = td::Timestamp::in(60.0);
119
list_.push_back(Query{std::move(request), std::move(payload), td::Timestamp::in(3.0), std::move(P)});
123
void alarm() override;
128
td::Timestamp fail_at_;
129
td::Timestamp close_at_;
130
td::actor::ActorOwn<ton::http::HttpClient> client_;
132
std::list<Query> list_;
134
td::actor::ActorId<HttpProxy> proxy_;
137
class HttpProxy : public td::actor::Actor {
142
void set_port(td::uint16 port) {
144
LOG(ERROR) << "duplicate port";
152
LOG(ERROR) << "no port specified";
156
class Cb : public ton::http::HttpServer::Callback {
158
Cb(td::actor::ActorId<HttpProxy> proxy) : proxy_(proxy) {
160
void receive_request(
161
std::unique_ptr<ton::http::HttpRequest> request, std::shared_ptr<ton::http::HttpPayload> payload,
162
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>>
164
td::actor::send_closure(proxy_, &HttpProxy::receive_request, std::move(request), std::move(payload),
169
td::actor::ActorId<HttpProxy> proxy_;
172
server_ = ton::http::HttpServer::create(port_, std::make_shared<Cb>(actor_id(this)));
175
void receive_request(
176
std::unique_ptr<ton::http::HttpRequest> request, std::shared_ptr<ton::http::HttpPayload> payload,
177
td::Promise<std::pair<std::unique_ptr<ton::http::HttpResponse>, std::shared_ptr<ton::http::HttpPayload>>>
179
auto host = request->host();
180
if (host.size() == 0) {
181
host = request->url();
182
if (host.size() >= 7 && host.substr(0, 7) == "http://") {
183
host = host.substr(7);
184
} else if (host.size() >= 8 && host.substr(0, 8) == "https://") {
185
host = host.substr(7);
187
auto p = host.find('/');
188
if (p != std::string::npos) {
189
host = host.substr(0, p);
192
if (host.size() >= 7 && host.substr(0, 7) == "http://") {
193
host = host.substr(7);
194
} else if (host.size() >= 8 && host.substr(0, 8) == "https://") {
195
host = host.substr(7);
197
auto p = host.find('/');
198
if (p != std::string::npos) {
199
host = host.substr(0, p);
202
if (host.find(':') == std::string::npos) {
206
std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); });
207
auto it = clients_.find(host);
209
if (it == clients_.end()) {
210
auto id = td::actor::create_actor<HttpRemote>("remote", host, actor_id(this));
211
it = clients_.emplace(host, std::move(id)).first;
214
td::actor::send_closure(it->second, &HttpRemote::receive_request, std::move(request), std::move(payload),
218
void close_client(std::string host) {
219
auto it = clients_.find(host);
220
CHECK(it != clients_.end());
227
td::actor::ActorOwn<ton::http::HttpServer> server_;
228
std::map<std::string, td::actor::ActorOwn<HttpRemote>> clients_;
231
void HttpRemote::alarm() {
233
if (fail_at_ && fail_at_.is_in_past()) {
234
LOG(INFO) << "closing outbound HTTP connection because of upper level request timeout";
235
td::actor::send_closure(proxy_, &HttpProxy::close_client, domain_);
239
alarm_timestamp().relax(fail_at_);
242
if (close_at_ && close_at_.is_in_past()) {
243
LOG(INFO) << "closing outbound HTTP connection because of idle timeout";
244
td::actor::send_closure(proxy_, &HttpProxy::close_client, domain_);
248
alarm_timestamp().relax(close_at_);
251
int main(int argc, char *argv[]) {
252
SET_VERBOSITY_LEVEL(verbosity_DEBUG);
254
td::set_default_failure_signal_handler().ensure();
256
td::actor::ActorOwn<HttpProxy> x;
257
td::unique_ptr<td::LogInterface> logger_;
259
td::log_interface = td::default_log_interface;
263
p.set_description("simple http proxy");
264
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
265
int v = VERBOSITY_NAME(FATAL) + (td::to_integer<int>(arg));
266
SET_VERBOSITY_LEVEL(v);
268
p.add_option('V', "version", "shows http-proxy build version", [&]() {
269
std::cout << "http-proxy build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
272
p.add_option('h', "help", "prints_help", [&]() {
274
td::StringBuilder sb(td::MutableSlice{b, 10000});
276
std::cout << sb.as_cslice().c_str();
279
p.add_checked_option('p', "port", "sets listening port", [&](td::Slice arg) -> td::Status {
280
TRY_RESULT(port, td::to_integer_safe<td::uint16>(arg));
281
td::actor::send_closure(x, &HttpProxy::set_port, port);
282
return td::Status::OK();
284
p.add_option('d', "daemonize", "set SIGHUP", [&]() {
285
td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
286
#if TD_DARWIN || TD_LINUX
292
#if TD_DARWIN || TD_LINUX
293
p.add_option('l', "logname", "log to file", [&](td::Slice fname) {
294
logger_ = td::FileLog::create(fname.str()).move_as_ok();
295
td::log_interface = logger_.get();
299
td::actor::Scheduler scheduler({7});
301
scheduler.run_in_context([&] { x = td::actor::create_actor<HttpProxy>("proxymain"); });
303
scheduler.run_in_context([&] { p.run(argc, argv).ensure(); });
304
scheduler.run_in_context([&] { td::actor::send_closure(x, &HttpProxy::run); });
305
while (scheduler.run(1)) {