OmniSciDB  a5dc49c757
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ThriftClient.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2022 HEAVY.AI, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Shared/ThriftClient.h"
18 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
19 #include "Shared/ThriftConfig.h"
20 #endif
21 
22 #include <iostream>
23 #include <sstream>
24 
25 #include <boost/algorithm/string.hpp>
26 #include <boost/core/ignore_unused.hpp>
27 #include <boost/filesystem.hpp>
28 
29 #include <thrift/protocol/TBinaryProtocol.h>
30 #include <thrift/transport/THttpClient.h>
31 #include <thrift/transport/TSocket.h>
33 
34 using namespace ::apache::thrift::transport;
35 using namespace ::apache::thrift::protocol;
37 
38 void check_standard_ca(std::string& ca_cert_file) {
39  if (ca_cert_file.empty()) {
40  static std::list<std::string> v_known_ca_paths({
41  "/etc/ssl/certs/ca-certificates.crt",
42  "/etc/pki/tls/certs/ca-bundle.crt",
43  "/usr/share/ssl/certs/ca-bundle.crt",
44  "/usr/local/share/certs/ca-root.crt",
45  "/etc/ssl/cert.pem",
46  "/etc/ssl/ca-bundle.pem",
47  });
48  for (const auto& known_ca_path : v_known_ca_paths) {
49  if (boost::filesystem::exists(known_ca_path)) {
50  ca_cert_file = known_ca_path;
51  break;
52  }
53  }
54  }
55 }
56 
57 class InsecureAccessManager : public AccessManager {
58  public:
59  Decision verify(const sockaddr_storage& sa) throw() override {
60  boost::ignore_unused(sa);
61  return ALLOW;
62  };
63  Decision verify(const std::string& host, const char* name, int size) throw() override {
64  boost::ignore_unused(host);
65  boost::ignore_unused(name);
66  boost::ignore_unused(size);
67  return ALLOW;
68  };
69  Decision verify(const sockaddr_storage& sa,
70  const char* data,
71  int size) throw() override {
72  boost::ignore_unused(sa);
73  boost::ignore_unused(data);
74  boost::ignore_unused(size);
75  return ALLOW;
76  };
77 };
78 
79 /*
80  * The Http client that comes with Thrift constructs a very simple set of HTTP
81  * headers, ignoring cookies. This class simply inherits from THttpClient to
82  * override the two methods - parseHeader (where it collects any cookies) and
83  * flush where it inserts the cookies into the http header.
84  *
85  * The methods that are over ridden here are virtual in the parent class, as is
86  * the parents class's destructor.
87  *
88  */
89 class ProxyTHttpClient : public THttpClient {
90  public:
91  // mimic and call the super constructors.
92  ProxyTHttpClient(std::shared_ptr<TTransport> transport,
93  std::string host,
94  std::string path)
95 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
96  : THttpClient(transport, host, path, shared::default_tconfig()){}
97 #else
98  : THttpClient(transport, host, path) {
99  }
100 #endif
101 
102  ProxyTHttpClient(std::string host, int port, std::string path)
103 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
104  : THttpClient(host, port, path, shared::default_tconfig()){}
105 #else
106  : THttpClient(host, port, path) {
107  }
108 #endif
109 
110  ~ProxyTHttpClient() override {
111  }
112  // thrift parseHeader d and call the super constructor.
113  void parseHeader(char* header) override {
114  // note boost::istarts_with is case insensitive
115  if (boost::istarts_with(header, "set-cookie:")) {
116  std::string tmp(header);
117  std::string cookie = tmp.substr(tmp.find(":") + 1, std::string::npos);
118  cookies_.push_back(cookie);
119  }
120  THttpClient::parseHeader(header);
121  }
122 
123  void flush() override {
124  /*
125  * Unfortunately the decision to write the header and the body in the same
126  * method precludes using the parent class's flush method here; in what is
127  * effectively a copy of 'flush' in THttpClient with the addition of
128  * cookies, a better error report for a header that is too large and
129  * 'Connection: keep-alive'.
130  */
131  uint8_t* buf;
132  uint32_t len;
133  writeBuffer_.getBuffer(&buf, &len);
134 
135  constexpr static const char* CRLF = "\r\n";
136 
137  std::ostringstream h;
138  h << "POST " << path_ << " HTTP/1.1" << CRLF << "Host: " << host_ << CRLF
139  << "Content-Type: application/x-thrift" << CRLF << "Content-Length: " << len << CRLF
140  << "Accept: application/x-thrift" << CRLF << "User-Agent: Thrift/"
141  << THRIFT_PACKAGE_VERSION << " (C++/THttpClient)" << CRLF
142  << "Connection: keep-alive" << CRLF;
143  if (!cookies_.empty()) {
144  std::string cookie = "Cookie:" + boost::algorithm::join(cookies_, ";");
145  h << cookie << CRLF;
146  }
147  h << CRLF;
148 
149  cookies_.clear();
150  std::string header = h.str();
151  if (header.size() > (std::numeric_limits<uint32_t>::max)()) {
152  throw TTransportException(
153  "Header too big [" + std::to_string(header.size()) +
154  "]. Max = " + std::to_string((std::numeric_limits<uint32_t>::max)()));
155  }
156  // Write the header, then the data, then flush
157  transport_->write((const uint8_t*)header.c_str(),
158  static_cast<uint32_t>(header.size()));
159  transport_->write(buf, len);
160  transport_->flush();
161 
162  // Reset the buffer and header variables
163  writeBuffer_.resetBuffer();
164  readHeaders_ = true;
165  }
166 
167  std::vector<std::string> cookies_;
168 };
170 ThriftClientConnection::ThriftClientConnection(const std::string& server_host,
171  const int port,
172  const ThriftConnectionType conn_type,
173  bool skip_host_verify,
174  std::shared_ptr<TSSLSocketFactory> factory)
175  : server_host_(server_host)
176  , port_(port)
177  , conn_type_(conn_type)
178  , skip_host_verify_(skip_host_verify)
179  , trust_cert_file_("") {
180  if (factory && (conn_type_ == ThriftConnectionType::BINARY_SSL ||
182  using_X509_store_ = true;
183  factory_ = factory;
184  factory_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
185  if (skip_host_verify_) {
186  factory_->access(
187  std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
188  }
189  }
190 }
191 
192 std::shared_ptr<TProtocol> ThriftClientConnection::get_protocol() {
193  std::shared_ptr<apache::thrift::transport::TTransport> mytransport;
197  port_,
201 
202  } else {
204  }
205 
206  try {
207  mytransport->open();
208  } catch (const apache::thrift::TException& e) {
209  throw apache::thrift::TException(std::string(e.what()) + ": host " + server_host_ +
210  ", port " + std::to_string(port_));
211  }
214  return std::shared_ptr<TProtocol>(new TJSONProtocol(mytransport));
215  } else {
216  return std::shared_ptr<TProtocol>(new TBinaryProtocol(mytransport));
217  }
218 }
219 
221  const std::string& server_host,
222  const int port,
223  const std::string& ca_cert_name,
224  bool with_timeout,
225  bool with_keepalive,
226  unsigned connect_timeout,
227  unsigned recv_timeout,
228  unsigned send_timeout) {
229  std::shared_ptr<TTransport> transport;
230 
231  if (!factory_ && !ca_cert_name.empty()) {
232  // need to build a factory once for ssl conection
233  factory_ =
234  std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory(SSLProtocol::SSLTLS));
235  factory_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
236  factory_->loadTrustedCertificates(ca_cert_name.c_str());
237  factory_->authenticate(false);
238  factory_->access(std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
239  }
240  if (!using_X509_store_ && ca_cert_name.empty()) {
241 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
242  const auto socket =
243  std::make_shared<TSocket>(server_host, port, shared::default_tconfig());
244 #else
245  const auto socket = std::make_shared<TSocket>(server_host, port);
246 #endif
247  if (with_timeout) {
248  socket->setKeepAlive(with_keepalive);
249  socket->setConnTimeout(connect_timeout);
250  socket->setRecvTimeout(recv_timeout);
251  socket->setSendTimeout(send_timeout);
252 #ifdef __APPLE__
253  socket->setLinger(false, 0);
254 #endif
255  }
256 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
257  transport = std::make_shared<TBufferedTransport>(socket, shared::default_tconfig());
258 #else
259  transport = std::make_shared<TBufferedTransport>(socket);
260 #endif
261  } else {
262  std::shared_ptr<TSocket> secure_socket = factory_->createSocket(server_host, port);
263  if (with_timeout) {
264  secure_socket->setKeepAlive(with_keepalive);
265  secure_socket->setConnTimeout(connect_timeout);
266  secure_socket->setRecvTimeout(recv_timeout);
267  secure_socket->setSendTimeout(send_timeout);
268 #ifdef __APPLE__
269  secure_socket->setLinger(false, 0);
270 #endif
271  }
272 #ifdef HAVE_THRIFT_MESSAGE_LIMIT
273  transport = std::shared_ptr<TTransport>(
274  new TBufferedTransport(secure_socket, shared::default_tconfig()));
275 #else
276  transport = std::shared_ptr<TTransport>(new TBufferedTransport(secure_socket));
277 #endif
278  }
279 
280  return transport;
281 }
282 
284  const std::string& server_host,
285  const int port,
286  const std::string& trust_cert_fileX,
287  bool use_https,
288  bool skip_verify) {
289  trust_cert_file_ = trust_cert_fileX;
291 
292  if (!factory_) {
293  factory_ =
294  std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory(SSLProtocol::SSLTLS));
295  }
296  std::shared_ptr<TTransport> transport;
297  std::shared_ptr<TTransport> socket;
298  if (use_https) {
299  if (skip_verify) {
300  factory_->authenticate(false);
301  factory_->access(
302  std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
303  }
304  if (!using_X509_store_) {
305  factory_->loadTrustedCertificates(trust_cert_file_.c_str());
306  }
307  socket = factory_->createSocket(server_host, port);
308  // transport = std::shared_ptr<TTransport>(new THttpClient(socket,
309  // server_host,
310  // "/"));
311  transport =
312  std::shared_ptr<TTransport>(new ProxyTHttpClient(socket, server_host, "/"));
313  } else {
314  transport = std::shared_ptr<TTransport>(new ProxyTHttpClient(server_host, port, "/"));
315  }
316  return transport;
317 }
ThriftConnectionType conn_type_
Definition: ThriftClient.h:77
unsigned connect_timeout
void check_standard_ca(std::string &ca_cert_file)
ThriftConnectionType
Definition: ThriftClient.h:29
std::shared_ptr< TTransport > open_http_client_transport(const std::string &server_host, const int port, const std::string &trust_cert_file_, bool use_https, bool skip_verify)
std::string join(T const &container, std::string const &delim)
virtual ~ThriftClientConnection()
unsigned send_timeout
std::string to_string(char const *&&v)
std::shared_ptr< apache::thrift::TConfiguration > default_tconfig()
Definition: ThriftConfig.h:26
Decision verify(const sockaddr_storage &sa) override
Decision verify(const std::string &host, const char *name, int size) override
std::shared_ptr< TTransport > open_buffered_client_transport(const std::string &server_host, const int port, const std::string &ca_cert_name, const bool with_timeout=false, const bool with_keepalive=true, const unsigned connect_timeout=0, const unsigned recv_timeount=0, const unsigned send_timeout=0)
ProxyTHttpClient(std::shared_ptr< TTransport > transport, std::string host, std::string path)
Decision verify(const sockaddr_storage &sa, const char *data, int size) override
std::string ca_cert_name_
Definition: ThriftClient.h:79
bool with_keepalive
std::string trust_cert_file_
Definition: ThriftClient.h:80
unsigned recv_timeout
std::string server_host_
Definition: ThriftClient.h:75
std::shared_ptr< TProtocol > get_protocol()
std::shared_ptr< TSSLSocketFactory > factory_
Definition: ThriftClient.h:82
list header
Definition: report.py:113
string name
Definition: setup.in.py:72
AccessManager::Decision Decision