OmniSciDB  d2f719934e
 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 2018, OmniSci, 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 #include "Shared/ThriftClient.h"
17 #include <boost/algorithm/string.hpp>
18 #include <boost/core/ignore_unused.hpp>
19 #include <boost/filesystem.hpp>
20 #include <iostream>
21 #include <sstream>
22 using namespace ::apache::thrift::transport;
24 
25 void check_standard_ca(std::string& ca_cert_file) {
26  if (ca_cert_file.empty()) {
27  static std::list<std::string> v_known_ca_paths({
28  "/etc/ssl/certs/ca-certificates.crt",
29  "/etc/pki/tls/certs/ca-bundle.crt",
30  "/usr/share/ssl/certs/ca-bundle.crt",
31  "/usr/local/share/certs/ca-root.crt",
32  "/etc/ssl/cert.pem",
33  "/etc/ssl/ca-bundle.pem",
34  });
35  for (const auto& known_ca_path : v_known_ca_paths) {
36  if (boost::filesystem::exists(known_ca_path)) {
37  ca_cert_file = known_ca_path;
38  break;
39  }
40  }
41  }
42 }
43 
44 class InsecureAccessManager : public AccessManager {
45  public:
46  Decision verify(const sockaddr_storage& sa) throw() override {
47  boost::ignore_unused(sa);
48  return ALLOW;
49  };
50  Decision verify(const std::string& host, const char* name, int size) throw() override {
51  boost::ignore_unused(host);
52  boost::ignore_unused(name);
53  boost::ignore_unused(size);
54  return ALLOW;
55  };
56  Decision verify(const sockaddr_storage& sa,
57  const char* data,
58  int size) throw() override {
59  boost::ignore_unused(sa);
60  boost::ignore_unused(data);
61  boost::ignore_unused(size);
62  return ALLOW;
63  };
64 };
65 
66 /*
67  * The Http client that comes with Thrift constructs a very simple set of HTTP
68  * headers, ignoring cookies. This class simply inherits from THttpClient to
69  * override the two methods - parseHeader (where it collects any cookies) and
70  * flush where it inserts the cookies into the http header.
71  *
72  * The methods that are over ridden here are virtual in the parent class, as is
73  * the parents class's destructor.
74  *
75  */
76 class ProxyTHttpClient : public THttpClient {
77  public:
78  // mimic and call the super constructors.
79  ProxyTHttpClient(std::shared_ptr<TTransport> transport,
80  std::string host,
81  std::string path)
82  : THttpClient(transport, host, path) {}
83 
84  ProxyTHttpClient(std::string host, int port, std::string path)
85  : THttpClient(host, port, path) {}
86 
87  ~ProxyTHttpClient() override {}
88  // thrift parseHeader d and call the super constructor.
89  void parseHeader(char* header) override {
90  // note boost::istarts_with is case insensitive
91  if (boost::istarts_with(header, "set-cookie:")) {
92  std::string tmp(header);
93  std::string cookie = tmp.substr(tmp.find(":") + 1, std::string::npos);
94  cookies_.push_back(cookie);
95  }
96  THttpClient::parseHeader(header);
97  }
98 
99  void flush() override {
100  /*
101  * Unfortunately the decision to write the header and the body in the same
102  * method precludes using the parent class's flush method here; in what is
103  * effectively a copy of 'flush' in THttpClient with the addition of
104  * cookies, a better error report for a header that is too large and
105  * 'Connection: keep-alive'.
106  */
107  uint8_t* buf;
108  uint32_t len;
109  writeBuffer_.getBuffer(&buf, &len);
110 
111  std::ostringstream h;
112  h << "POST " << path_ << " HTTP/1.1" << THttpClient::CRLF << "Host: " << host_
113  << THttpClient::CRLF << "Content-Type: application/x-thrift" << THttpClient::CRLF
114  << "Content-Length: " << len << THttpClient::CRLF << "Accept: application/x-thrift"
115  << THttpClient::CRLF << "User-Agent: Thrift/" << THRIFT_PACKAGE_VERSION
116  << " (C++/THttpClient)" << THttpClient::CRLF << "Connection: keep-alive"
117  << THttpClient::CRLF;
118  if (!cookies_.empty()) {
119  std::string cookie = "Cookie:" + boost::algorithm::join(cookies_, ";");
120  h << cookie << THttpClient::CRLF;
121  }
122  h << THttpClient::CRLF;
123 
124  cookies_.clear();
125  std::string header = h.str();
126  if (header.size() > (std::numeric_limits<uint32_t>::max)()) {
127  throw TTransportException(
128  "Header too big [" + std::to_string(header.size()) +
129  "]. Max = " + std::to_string((std::numeric_limits<uint32_t>::max)()));
130  }
131  // Write the header, then the data, then flush
132  transport_->write((const uint8_t*)header.c_str(),
133  static_cast<uint32_t>(header.size()));
134  transport_->write(buf, len);
135  transport_->flush();
136 
137  // Reset the buffer and header variables
138  writeBuffer_.resetBuffer();
139  readHeaders_ = true;
140  }
141 
142  std::vector<std::string> cookies_;
143 };
145 ThriftClientConnection::ThriftClientConnection(const std::string& server_host,
146  const int port,
147  const ThriftConnectionType conn_type,
148  bool skip_host_verify,
149  std::shared_ptr<TSSLSocketFactory> factory)
150  : server_host_(server_host)
151  , port_(port)
152  , conn_type_(conn_type)
153  , skip_host_verify_(skip_host_verify)
154  , trust_cert_file_("") {
155  if (factory && (conn_type_ == ThriftConnectionType::BINARY_SSL ||
157  using_X509_store_ = true;
158  factory_ = factory;
159  factory_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
160  if (skip_host_verify_) {
161  factory_->access(
162  std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
163  }
164  }
165 }
166 
167 std::shared_ptr<TProtocol> ThriftClientConnection::get_protocol() {
168  std::shared_ptr<apache::thrift::transport::TTransport> mytransport;
172  port_,
176 
177  } else {
179  }
180 
181  try {
182  mytransport->open();
183  } catch (const apache::thrift::TException& e) {
184  throw apache::thrift::TException(std::string(e.what()) + ": host " + server_host_ +
185  ", port " + std::to_string(port_));
186  }
189  return std::shared_ptr<TProtocol>(new TJSONProtocol(mytransport));
190  } else {
191  return std::shared_ptr<TProtocol>(new TBinaryProtocol(mytransport));
192  }
193 }
194 
196  const std::string& server_host,
197  const int port,
198  const std::string& ca_cert_name,
199  bool with_timeout,
200  bool with_keepalive,
201  unsigned connect_timeout,
202  unsigned recv_timeout,
203  unsigned send_timeout) {
204  std::shared_ptr<TTransport> transport;
205 
206  if (!factory_ && !ca_cert_name.empty()) {
207  // need to build a factory once for ssl conection
208  factory_ =
209  std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory(SSLProtocol::SSLTLS));
210  factory_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
211  factory_->loadTrustedCertificates(ca_cert_name.c_str());
212  factory_->authenticate(false);
213  factory_->access(std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
214  }
215  if (!using_X509_store_ && ca_cert_name.empty()) {
216  const auto socket = std::make_shared<TSocket>(server_host, port);
217  if (with_timeout) {
218  socket->setKeepAlive(with_keepalive);
219  socket->setConnTimeout(connect_timeout);
220  socket->setRecvTimeout(recv_timeout);
221  socket->setSendTimeout(send_timeout);
222  }
223  transport = std::make_shared<TBufferedTransport>(socket);
224  } else {
225  std::shared_ptr<TSocket> secure_socket = factory_->createSocket(server_host, port);
226  if (with_timeout) {
227  secure_socket->setKeepAlive(with_keepalive);
228  secure_socket->setConnTimeout(connect_timeout);
229  secure_socket->setRecvTimeout(recv_timeout);
230  secure_socket->setSendTimeout(send_timeout);
231  }
232  transport = std::shared_ptr<TTransport>(new TBufferedTransport(secure_socket));
233  }
234 
235  return transport;
236 }
237 
239  const std::string& server_host,
240  const int port,
241  const std::string& trust_cert_fileX,
242  bool use_https,
243  bool skip_verify) {
244  trust_cert_file_ = trust_cert_fileX;
246 
247  if (!factory_) {
248  factory_ =
249  std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory(SSLProtocol::SSLTLS));
250  }
251  std::shared_ptr<TTransport> transport;
252  std::shared_ptr<TTransport> socket;
253  if (use_https) {
254  if (skip_verify) {
255  factory_->authenticate(false);
256  factory_->access(
257  std::shared_ptr<InsecureAccessManager>(new InsecureAccessManager()));
258  }
259  if (!using_X509_store_) {
260  factory_->loadTrustedCertificates(trust_cert_file_.c_str());
261  }
262  socket = factory_->createSocket(server_host, port);
263  // transport = std::shared_ptr<TTransport>(new THttpClient(socket,
264  // server_host,
265  // "/"));
266  transport =
267  std::shared_ptr<TTransport>(new ProxyTHttpClient(socket, server_host, "/"));
268  } else {
269  transport = std::shared_ptr<TTransport>(new ProxyTHttpClient(server_host, port, "/"));
270  }
271  return transport;
272 }
ThriftConnectionType conn_type_
Definition: ThriftClient.h:79
unsigned connect_timeout
void check_standard_ca(std::string &ca_cert_file)
ThriftConnectionType
Definition: ThriftClient.h:31
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)
string name
Definition: setup.in.py:72
std::string join(T const &container, std::string const &delim)
virtual ~ThriftClientConnection()
unsigned send_timeout
std::string to_string(char const *&&v)
std::vector< std::string > cookies_
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:81
bool with_keepalive
std::string trust_cert_file_
Definition: ThriftClient.h:82
unsigned recv_timeout
std::string server_host_
Definition: ThriftClient.h:77
ProxyTHttpClient(std::string host, int port, std::string path)
std::shared_ptr< TProtocol > get_protocol()
void flush() override
std::shared_ptr< TSSLSocketFactory > factory_
Definition: ThriftClient.h:84
void parseHeader(char *header) override
AccessManager::Decision Decision
~ProxyTHttpClient() override