Skip to content

Commit 927b64a

Browse files
authored
Merge pull request #274 from riptano/CPP-799
CPP-799 - Use metadata service to determine contact points
2 parents 3d880bc + de129e5 commit 927b64a

23 files changed

Lines changed: 9739 additions & 51 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: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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 (!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+
Server* server)
67+
: internal::ClientConnection(server_connection)
68+
, path_(server->path())
69+
, content_type_(server->content_type())
70+
, response_body_(server->response_body())
71+
, response_status_code_(server->response_status_code())
72+
, enable_valid_response_(server->enable_valid_response())
73+
, close_connnection_after_request_(server->close_connnection_after_request()) {
74+
http_parser_init(&parser_, HTTP_REQUEST);
75+
http_parser_settings_init(&parser_settings_);
76+
77+
parser_.data = this;
78+
parser_settings_.on_url = on_url;
79+
}
80+
81+
void Server::ClientConnection::on_read(const char* data, size_t len) {
82+
request_ = String(data, len);
83+
size_t parsed = http_parser_execute(&parser_, &parser_settings_, data, len);
84+
if (parsed < static_cast<size_t>(len)) {
85+
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
86+
fprintf(stderr, "%s: %s\n", http_errno_name(err), http_errno_description(err));
87+
close();
88+
}
89+
}
90+
91+
int Server::ClientConnection::on_url(http_parser* parser, const char* buf, size_t len) {
92+
ClientConnection* self = static_cast<ClientConnection*>(parser->data);
93+
self->handle_url(buf, len);
94+
return 0;
95+
}
96+
97+
void Server::ClientConnection::handle_url(const char* buf, size_t len) {
98+
String path(buf, len);
99+
if (path.substr(0, path.find("?")) == path_) { // Compare without query parameters
100+
if (enable_valid_response_) {
101+
if (response_body_.empty()) {
102+
write(response(response_status_code_, request_)); // Echo response
103+
} else {
104+
write(response(response_status_code_, response_body_, content_type_));
105+
}
106+
} else {
107+
write("Invalid HTTP server response");
108+
}
109+
} else {
110+
write(response(404));
111+
}
112+
// From the HTTP/1.0 protocol specification:
113+
//
114+
// > When an Entity-Body is included with a message, the length of that body may be determined in
115+
// > one of two ways. If a Content-Length header field is present, its value in bytes represents
116+
// > the length of the Entity-Body. Otherwise, the body length is determined by the closing of the
117+
// > connection by the server.
118+
if (close_connnection_after_request_) {
119+
close();
120+
}
121+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
Server()
38+
: path_("/")
39+
, content_type_("text/plain")
40+
, response_status_code_(200)
41+
, enable_valid_response_(true)
42+
, close_connnection_after_request_(true)
43+
, event_loop_group_(1, "HTTP Server")
44+
, factory_(this)
45+
, server_connection_(new internal::ServerConnection(
46+
Address(HTTP_MOCK_SERVER_IP, HTTP_MOCK_SERVER_PORT), factory_)) {}
47+
48+
const String& path() const { return path_; }
49+
const String& content_type() const { return content_type_; }
50+
const String& response_body() const { return response_body_; }
51+
int response_status_code() const { return response_status_code_; }
52+
bool enable_valid_response() { return enable_valid_response_; }
53+
bool close_connnection_after_request() { return close_connnection_after_request_; }
54+
55+
void set_path(const String& path) { path_ = path; }
56+
void set_content_type(const String& content_type) { content_type_ = content_type; }
57+
void set_response_body(const String& response_body) { response_body_ = response_body; }
58+
void set_response_status_code(int status_code) { response_status_code_ = status_code; }
59+
void enable_valid_response(bool enable) { enable_valid_response_ = enable; }
60+
void set_close_connnection_after_request(bool enable) {
61+
close_connnection_after_request_ = enable;
62+
}
63+
64+
bool use_ssl(const String& key, const String& cert, const String& password = "",
65+
const String& client_cert = "");
66+
67+
void listen();
68+
void close();
69+
70+
private:
71+
class ClientConnection : public internal::ClientConnection {
72+
public:
73+
ClientConnection(internal::ServerConnection* server_connection, Server* server);
74+
75+
virtual void on_read(const char* data, size_t len);
76+
77+
private:
78+
static int on_url(http_parser* parser, const char* buf, size_t len);
79+
void handle_url(const char* buf, size_t len);
80+
81+
private:
82+
String path_;
83+
String content_type_;
84+
String response_body_;
85+
int response_status_code_;
86+
bool enable_valid_response_;
87+
bool close_connnection_after_request_;
88+
String request_;
89+
http_parser parser_;
90+
http_parser_settings parser_settings_;
91+
};
92+
93+
class ClientConnectionFactory : public internal::ClientConnectionFactory {
94+
public:
95+
ClientConnectionFactory(Server* server)
96+
: server_(server) {}
97+
98+
virtual internal::ClientConnection*
99+
create(internal::ServerConnection* server_connection) const {
100+
return new ClientConnection(server_connection, server_);
101+
}
102+
103+
private:
104+
Server* const server_;
105+
};
106+
107+
private:
108+
String path_;
109+
String content_type_;
110+
String response_body_;
111+
int response_status_code_;
112+
bool enable_valid_response_;
113+
bool close_connnection_after_request_;
114+
SimpleEventLoopGroup event_loop_group_;
115+
ClientConnectionFactory factory_;
116+
internal::ServerConnection::Ptr server_connection_;
117+
};
118+
119+
}} // namespace mockssandra::http
120+
121+
#endif
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 set_response_status_code(int status_code) { server_.set_response_status_code(status_code); }
43+
44+
void enable_valid_response(bool enable) { server_.enable_valid_response(enable); }
45+
46+
void set_close_connnection_after_request(bool enable) {
47+
server_.set_close_connnection_after_request(enable);
48+
}
49+
50+
void start_http_server() { server_.listen(); }
51+
void stop_http_server() { server_.close(); }
52+
53+
datastax::internal::core::SocketSettings use_ssl(String cn = "127.0.0.1",
54+
bool is_server_using_ssl = true) {
55+
datastax::internal::core::SocketSettings settings;
56+
57+
#ifdef HAVE_OPENSSL
58+
datastax::String ca_key = mockssandra::Ssl::generate_key();
59+
ca_cert_ = mockssandra::Ssl::generate_cert(ca_key, cn);
60+
key_ = mockssandra::Ssl::generate_key();
61+
cert_ = mockssandra::Ssl::generate_cert(key_, cn, ca_cert_, ca_key);
62+
63+
datastax::internal::core::SslContext::Ptr ssl_context(
64+
datastax::internal::core::SslContextFactory::create());
65+
ssl_context->set_cert(cert().c_str(), cert().size());
66+
ssl_context->set_private_key(key().c_str(), key().size(), "",
67+
0); // No password expected for the private key
68+
ssl_context->add_trusted_cert(ca_cert().c_str(), ca_cert().size());
69+
70+
settings.ssl_context = ssl_context;
71+
72+
if (is_server_using_ssl) {
73+
server_.use_ssl(ca_key, ca_cert_, "", cert_);
74+
}
75+
#endif
76+
77+
return settings;
78+
}
79+
80+
void use_ssl(const String& key, const String& cert, const String& ca_key, const String& ca_cert) {
81+
#ifdef HAVE_OPENSSL
82+
key_ = key;
83+
cert_ = cert;
84+
ca_cert_ = ca_cert;
85+
86+
server_.use_ssl(ca_key, ca_cert_, "", cert_);
87+
#endif
88+
}
89+
90+
private:
91+
datastax::String ca_cert_;
92+
datastax::String cert_;
93+
datastax::String key_;
94+
mockssandra::http::Server server_;
95+
};
96+
97+
#endif

0 commit comments

Comments
 (0)