Ton

Форк
0
/
http-proxy.cpp 
309 строк · 10.3 Кб
1
/*
2
    This file is part of TON Blockchain source code.
3

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.
8

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.
13

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/>.
16

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.
25

26
    Copyright 2019-2020 Telegram Systems LLP
27
*/
28
#include "http/http-server.h"
29
#include "http/http-client.h"
30

31
#include "td/utils/port/signals.h"
32
#include "td/utils/OptionParser.h"
33
#include "td/utils/FileLog.h"
34

35
#include <algorithm>
36
#include <list>
37
#include "git.h"
38

39
#if TD_DARWIN || TD_LINUX
40
#include <unistd.h>
41
#endif
42

43
class HttpProxy;
44

45
class HttpRemote : public td::actor::Actor {
46
 public:
47
  struct Query {
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;
52
  };
53
  HttpRemote(std::string domain, td::actor::ActorId<HttpProxy> proxy) : domain_(std::move(domain)), proxy_(proxy) {
54
  }
55
  void start_up() override {
56
    class Cb : public ton::http::HttpClient::Callback {
57
     public:
58
      Cb(td::actor::ActorId<HttpRemote> id) : id_(id) {
59
      }
60
      void on_ready() override {
61
        td::actor::send_closure(id_, &HttpRemote::set_ready, true);
62
      }
63
      void on_stop_ready() override {
64
        td::actor::send_closure(id_, &HttpRemote::set_ready, false);
65
      }
66

67
     private:
68
      td::actor::ActorId<HttpRemote> id_;
69
    };
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);
73
  }
74
  void set_ready(bool ready) {
75
    if (ready == ready_) {
76
      return;
77
    }
78
    ready_ = ready;
79
    if (!ready) {
80
      fail_at_ = td::Timestamp::in(10.0);
81
      alarm_timestamp().relax(fail_at_);
82
    } else {
83
      fail_at_ = td::Timestamp::never();
84
      while (list_.size() > 0) {
85
        auto q = std::move(list_.front());
86
        list_.pop_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);
90
      }
91
    }
92
  }
93
  void receive_request(
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>>>
96
          promise) {
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>>>
101
                   R) mutable {
102
          if (R.is_error()) {
103
            promise.set_error(R.move_as_error());
104
          } else {
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"});
110
            }
111
            promise.set_value(std::move(v));
112
          }
113
        });
114
    if (ready_) {
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);
118
    } else {
119
      list_.push_back(Query{std::move(request), std::move(payload), td::Timestamp::in(3.0), std::move(P)});
120
    }
121
  }
122

123
  void alarm() override;
124

125
 private:
126
  std::string domain_;
127
  bool ready_ = false;
128
  td::Timestamp fail_at_;
129
  td::Timestamp close_at_;
130
  td::actor::ActorOwn<ton::http::HttpClient> client_;
131

132
  std::list<Query> list_;
133

134
  td::actor::ActorId<HttpProxy> proxy_;
135
};
136

137
class HttpProxy : public td::actor::Actor {
138
 public:
139
  HttpProxy() {
140
  }
141

142
  void set_port(td::uint16 port) {
143
    if (port_ != 0) {
144
      LOG(ERROR) << "duplicate port";
145
      std::_Exit(2);
146
    }
147
    port_ = port;
148
  }
149

150
  void run() {
151
    if (port_ == 0) {
152
      LOG(ERROR) << "no port specified";
153
      std::_Exit(2);
154
    }
155

156
    class Cb : public ton::http::HttpServer::Callback {
157
     public:
158
      Cb(td::actor::ActorId<HttpProxy> proxy) : proxy_(proxy) {
159
      }
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>>>
163
              promise) override {
164
        td::actor::send_closure(proxy_, &HttpProxy::receive_request, std::move(request), std::move(payload),
165
                                std::move(promise));
166
      }
167

168
     private:
169
      td::actor::ActorId<HttpProxy> proxy_;
170
    };
171

172
    server_ = ton::http::HttpServer::create(port_, std::make_shared<Cb>(actor_id(this)));
173
  }
174

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>>>
178
          promise) {
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);
186
      }
187
      auto p = host.find('/');
188
      if (p != std::string::npos) {
189
        host = host.substr(0, p);
190
      }
191
    } else {
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);
196
      }
197
      auto p = host.find('/');
198
      if (p != std::string::npos) {
199
        host = host.substr(0, p);
200
      }
201
    }
202
    if (host.find(':') == std::string::npos) {
203
      host = host + ":80";
204
    }
205

206
    std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); });
207
    auto it = clients_.find(host);
208

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;
212
    }
213

214
    td::actor::send_closure(it->second, &HttpRemote::receive_request, std::move(request), std::move(payload),
215
                            std::move(promise));
216
  }
217

218
  void close_client(std::string host) {
219
    auto it = clients_.find(host);
220
    CHECK(it != clients_.end());
221
    clients_.erase(it);
222
  }
223

224
 private:
225
  td::uint16 port_;
226

227
  td::actor::ActorOwn<ton::http::HttpServer> server_;
228
  std::map<std::string, td::actor::ActorOwn<HttpRemote>> clients_;
229
};
230

231
void HttpRemote::alarm() {
232
  if (!ready_) {
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_);
236
      stop();
237
      return;
238
    } else {
239
      alarm_timestamp().relax(fail_at_);
240
    }
241
  }
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_);
245
    stop();
246
    return;
247
  }
248
  alarm_timestamp().relax(close_at_);
249
}
250

251
int main(int argc, char *argv[]) {
252
  SET_VERBOSITY_LEVEL(verbosity_DEBUG);
253

254
  td::set_default_failure_signal_handler().ensure();
255

256
  td::actor::ActorOwn<HttpProxy> x;
257
  td::unique_ptr<td::LogInterface> logger_;
258
  SCOPE_EXIT {
259
    td::log_interface = td::default_log_interface;
260
  };
261

262
  td::OptionParser p;
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);
267
  });
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";
270
    std::exit(0);
271
  });
272
  p.add_option('h', "help", "prints_help", [&]() {
273
    char b[10240];
274
    td::StringBuilder sb(td::MutableSlice{b, 10000});
275
    sb << p;
276
    std::cout << sb.as_cslice().c_str();
277
    std::exit(2);
278
  });
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();
283
  });
284
  p.add_option('d', "daemonize", "set SIGHUP", [&]() {
285
    td::set_signal_handler(td::SignalType::HangUp, [](int sig) {
286
#if TD_DARWIN || TD_LINUX
287
      close(0);
288
      setsid();
289
#endif
290
    }).ensure();
291
  });
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();
296
  });
297
#endif
298

299
  td::actor::Scheduler scheduler({7});
300

301
  scheduler.run_in_context([&] { x = td::actor::create_actor<HttpProxy>("proxymain"); });
302

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)) {
306
  }
307

308
  return 0;
309
}
310

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.