Skip to content

Commit d36334f

Browse files
author
Michael Fero
committed
CPP-799 - Use metadata service to determine contact points
- Vendoring http-parser from nodejs repo - Cleaning up mockssandra SSL code - Updating certificate generation to match SNI docker keys - Generate stronger keys; md5 and sha1 are deprecated in OpenSSL v1.1.0 - Implementing HttpClient; including tests - Uses HTTP/1.0 to avoid chunking
1 parent fb222ad commit d36334f

22 files changed

Lines changed: 9476 additions & 49 deletions

cpp-driver/cmake/modules/CppDriver.cmake

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,25 @@ macro(CassMiniZip)
482482
endif()
483483
endmacro()
484484

485+
#------------------------
486+
# CassHttpParser
487+
#
488+
# Set some HTTP_PARSER_* variables, set up some source_group's, and add the
489+
# HTTP_PARSER include dir to our list of include dirs.
490+
#
491+
# Input: CASS_SRC_DIR
492+
# Output: HTTP_PARSER_INCLUDE_DIR, HTTP_PARSER_HEADER_FILES,
493+
# HTTP_PARSER_SOURCE_FILES
494+
#------------------------
495+
macro(CassHttpParser)
496+
set(HTTP_PARSER_INCLUDE_DIR "${CASS_SRC_DIR}/third_party/http-parser")
497+
set(HTTP_PARSER_HEADER_FILES ${HTTP_PARSER_INCLUDE_DIR}/http_parser.h)
498+
set(HTTP_PARSER_SOURCE_FILES ${HTTP_PARSER_INCLUDE_DIR}/http_parser.c)
499+
source_group("Header Files\\http-parser" FILES ${HTTP_PARSER_HEADER_FILES})
500+
source_group("Source Files\\http-parser" FILES ${HTTP_PARSER_SOURCE_FILES})
501+
include_directories(${HTTP_PARSER_INCLUDE_DIR})
502+
endmacro()
503+
485504
#------------------------
486505
# CassSimulacron
487506
#
@@ -1014,6 +1033,10 @@ macro(CassFindSourceFiles)
10141033
set(CASS_INC_FILES ${CASS_INC_FILES} ${MINIZIP_HEADER_FILES})
10151034
set(CASS_SRC_FILES ${CASS_SRC_FILES} ${MINIZIP_SOURCE_FILES})
10161035

1036+
CassHttpParser()
1037+
set(CASS_INC_FILES ${CASS_INC_FILES} ${HTTP_PARSER_HEADER_FILES})
1038+
set(CASS_SRC_FILES ${CASS_SRC_FILES} ${HTTP_PARSER_SOURCE_FILES})
1039+
10171040
set(CASS_ALL_SOURCE_FILES ${CASS_SRC_FILES} ${CASS_API_HEADER_FILES} ${CASS_INC_FILES})
10181041
endmacro()
10191042

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
Copyright (c) DataStax, 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 "http_server.hpp"
18+
19+
using datastax::String;
20+
using datastax::internal::Memory;
21+
using datastax::internal::OStringStream;
22+
using datastax::internal::ScopedMutex;
23+
using datastax::internal::core::Address;
24+
using datastax::internal::core::EventLoop;
25+
using datastax::internal::core::Task;
26+
27+
String response(int status, const String& body = "", const String& content_type = "") {
28+
OStringStream ss;
29+
ss << "HTTP/1.0 " << status << " " << http_status_str(static_cast<http_status>(status)) << "\r\n";
30+
if (status == 200 && !body.empty()) {
31+
ss << "Content-Type: ";
32+
if (content_type.empty()) {
33+
ss << "text/plain";
34+
} else {
35+
ss << content_type;
36+
}
37+
ss << "\r\nContent-Length: " << body.size() << "\r\n\r\n" << body;
38+
} else {
39+
ss << "\r\n";
40+
}
41+
42+
return ss.str();
43+
}
44+
45+
using namespace mockssandra;
46+
using namespace mockssandra::http;
47+
48+
void Server::listen() {
49+
server_connection_->listen(&event_loop_group_);
50+
server_connection_->wait_listen();
51+
}
52+
53+
void Server::close() {
54+
if (server_connection_) {
55+
server_connection_->close();
56+
server_connection_->wait_close();
57+
}
58+
}
59+
60+
bool Server::use_ssl(const String& key, const String& cert, const String& password /*= ""*/,
61+
const String& client_cert /*= ""*/) {
62+
return server_connection_->use_ssl(key, cert, password, client_cert);
63+
}
64+
65+
Server::ClientConnection::ClientConnection(internal::ServerConnection* server_connection,
66+
const String& path, const String& content_type,
67+
const String& response_body, bool enable_valid_response)
68+
: internal::ClientConnection(server_connection)
69+
, path_(path)
70+
, content_type_(content_type)
71+
, response_body_(response_body)
72+
, enable_valid_response_(enable_valid_response) {
73+
http_parser_init(&parser_, HTTP_REQUEST);
74+
http_parser_settings_init(&parser_settings_);
75+
76+
parser_.data = this;
77+
parser_settings_.on_url = on_url;
78+
}
79+
80+
void Server::ClientConnection::on_read(const char* data, size_t len) {
81+
request_ = String(data, len);
82+
size_t parsed = http_parser_execute(&parser_, &parser_settings_, data, len);
83+
if (parsed < static_cast<size_t>(len)) {
84+
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
85+
fprintf(stderr, "%s: %s\n", http_errno_name(err), http_errno_description(err));
86+
close();
87+
}
88+
}
89+
90+
int Server::ClientConnection::on_url(http_parser* parser, const char* buf, size_t len) {
91+
ClientConnection* self = static_cast<ClientConnection*>(parser->data);
92+
self->handle_url(buf, len);
93+
return 0;
94+
}
95+
96+
void Server::ClientConnection::handle_url(const char* buf, size_t len) {
97+
String path(buf, len);
98+
if (path == path_) {
99+
if (enable_valid_response_) {
100+
if (response_body_.empty()) {
101+
write(response(200, request_)); // Echo response
102+
} else {
103+
write(response(200, response_body_, content_type_));
104+
}
105+
} else {
106+
write("Invalid HTTP server response");
107+
}
108+
} else {
109+
write(response(404));
110+
}
111+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
Copyright (c) DataStax, 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+
#ifndef HTTP_MOCK_SERVER_HPP
18+
#define HTTP_MOCK_SERVER_HPP
19+
20+
#define HTTP_MOCK_SERVER_IP "127.0.0.1"
21+
#define HTTP_MOCK_SERVER_PORT 30443
22+
23+
#include "http_parser.h"
24+
#include "mockssandra.hpp"
25+
#include "string.hpp"
26+
27+
namespace mockssandra { namespace http {
28+
29+
/**
30+
* Mockssandra HTTP server.
31+
*
32+
* If no response body is set then the default response will the be original request; e.g. echo HTTP
33+
* server.
34+
*/
35+
class Server {
36+
public:
37+
public:
38+
Server()
39+
: path_("/")
40+
, content_type_("text/plain")
41+
, enable_valid_response_(true)
42+
, event_loop_group_(1, "HTTP Server")
43+
, factory_(this)
44+
, server_connection_(new internal::ServerConnection(
45+
Address(HTTP_MOCK_SERVER_IP, HTTP_MOCK_SERVER_PORT), factory_)) {}
46+
47+
const String& path() const { return path_; }
48+
const String& content_type() const { return content_type_; }
49+
const String& response_body() const { return response_body_; }
50+
bool enable_valid_response() { return enable_valid_response_; }
51+
52+
void set_path(const String& path) { path_ = path; }
53+
void set_content_type(const String& content_type) { content_type_ = content_type; }
54+
void set_response_body(const String& response_body) { response_body_ = response_body; }
55+
void enable_valid_response(bool enable) { enable_valid_response_ = enable; }
56+
57+
bool use_ssl(const String& key, const String& cert, const String& password = "",
58+
const String& client_cert = "");
59+
60+
void listen();
61+
void close();
62+
63+
private:
64+
class ClientConnection : public internal::ClientConnection {
65+
public:
66+
ClientConnection(internal::ServerConnection* server_connection, const String& endpoint,
67+
const String& content_type, const String& response_body,
68+
bool enable_valid_response);
69+
70+
virtual void on_read(const char* data, size_t len);
71+
72+
private:
73+
static int on_url(http_parser* parser, const char* buf, size_t len);
74+
void handle_url(const char* buf, size_t len);
75+
76+
private:
77+
String path_;
78+
String content_type_;
79+
String response_body_;
80+
bool enable_valid_response_;
81+
String request_;
82+
http_parser parser_;
83+
http_parser_settings parser_settings_;
84+
};
85+
86+
class ClientConnectionFactory : public internal::ClientConnectionFactory {
87+
public:
88+
ClientConnectionFactory(Server* server)
89+
: server_(server) {}
90+
91+
virtual internal::ClientConnection*
92+
create(internal::ServerConnection* server_connection) const {
93+
return new ClientConnection(server_connection, server_->path(), server_->content_type(),
94+
server_->response_body(), server_->enable_valid_response());
95+
}
96+
97+
private:
98+
Server* const server_;
99+
};
100+
101+
private:
102+
String path_;
103+
String content_type_;
104+
String response_body_;
105+
bool enable_valid_response_;
106+
SimpleEventLoopGroup event_loop_group_;
107+
ClientConnectionFactory factory_;
108+
internal::ServerConnection::Ptr server_connection_;
109+
};
110+
111+
}} // namespace mockssandra::http
112+
113+
#endif
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Copyright (c) DataStax, 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+
#ifndef HTTP_SERVER_TEST_HPP
18+
#define HTTP_SERVER_TEST_HPP
19+
20+
#include "http_server.hpp"
21+
#include "loop_test.hpp"
22+
#include "socket_connector.hpp"
23+
24+
class HttpTest : public LoopTest {
25+
public:
26+
~HttpTest() { server_.close(); }
27+
28+
const datastax::String& ca_cert() const { return ca_cert_; }
29+
const datastax::String& cert() const { return cert_; }
30+
const datastax::String& key() const { return key_; }
31+
32+
void set_path(const datastax::String& path) { server_.set_path(path); }
33+
34+
void set_content_type(const datastax::String& content_type) {
35+
server_.set_content_type(content_type);
36+
}
37+
38+
void set_response_body(const datastax::String& response_body) {
39+
server_.set_response_body(response_body);
40+
}
41+
42+
void enable_valid_response(bool enable) { server_.enable_valid_response(enable); }
43+
44+
void start_http_server() { server_.listen(); }
45+
void stop_http_server() { server_.close(); }
46+
47+
datastax::internal::core::SocketSettings use_ssl(String cn = "127.0.0.1",
48+
bool is_server_using_ssl = true) {
49+
datastax::internal::core::SocketSettings settings;
50+
51+
#ifdef HAVE_OPENSSL
52+
datastax::String ca_key = mockssandra::Ssl::generate_key();
53+
ca_cert_ = mockssandra::Ssl::generate_cert(ca_key, cn);
54+
key_ = mockssandra::Ssl::generate_key();
55+
cert_ = mockssandra::Ssl::generate_cert(key_, cn, ca_cert_, ca_key);
56+
57+
datastax::internal::core::SslContext::Ptr ssl_context(
58+
datastax::internal::core::SslContextFactory::create());
59+
ssl_context->set_cert(cert().c_str(), cert().size());
60+
ssl_context->set_private_key(key().c_str(), key().size(), "",
61+
0); // No password expected for the private key
62+
ssl_context->add_trusted_cert(ca_cert().c_str(), ca_cert().size());
63+
64+
settings.ssl_context = ssl_context;
65+
66+
if (is_server_using_ssl) {
67+
server_.use_ssl(ca_key, ca_cert_, "", cert_);
68+
}
69+
#endif
70+
71+
return settings;
72+
}
73+
74+
void use_ssl(const String& key, const String& cert, const String& ca_key, const String& ca_cert) {
75+
#ifdef HAVE_OPENSSL
76+
key_ = key;
77+
cert_ = cert;
78+
ca_cert_ = ca_cert;
79+
80+
server_.use_ssl(ca_key, ca_cert_, "", cert_);
81+
#endif
82+
}
83+
84+
private:
85+
datastax::String ca_cert_;
86+
datastax::String cert_;
87+
datastax::String key_;
88+
mockssandra::http::Server server_;
89+
};
90+
91+
#endif

0 commit comments

Comments
 (0)