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