blob: aa93df4869cd6ae25fab1890431fc3946c656e01 [file]
/*
Copyright (c) DataStax, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "http_server.hpp"
using datastax::String;
using datastax::internal::Memory;
using datastax::internal::OStringStream;
using datastax::internal::ScopedMutex;
using datastax::internal::core::Address;
using datastax::internal::core::EventLoop;
using datastax::internal::core::Task;
String response(int status, const String& body = "", const String& content_type = "") {
OStringStream ss;
ss << "HTTP/1.0 " << status << " " << http_status_str(static_cast<http_status>(status)) << "\r\n";
if (!body.empty()) {
ss << "Content-Type: ";
if (content_type.empty()) {
ss << "text/plain";
} else {
ss << content_type;
}
ss << "\r\nContent-Length: " << body.size() << "\r\n\r\n" << body;
} else {
ss << "\r\n";
}
return ss.str();
}
using namespace mockssandra;
using namespace mockssandra::http;
void Server::listen() {
server_connection_->listen(&event_loop_group_);
server_connection_->wait_listen();
}
void Server::close() {
if (server_connection_) {
server_connection_->close();
server_connection_->wait_close();
}
}
bool Server::use_ssl(const String& key, const String& cert, const String& ca_cert /*= ""*/,
bool require_client_cert /*= false*/) {
return server_connection_->use_ssl(key, cert, ca_cert, require_client_cert);
}
Server::ClientConnection::ClientConnection(internal::ServerConnection* server_connection,
Server* server)
: internal::ClientConnection(server_connection)
, path_(server->path())
, content_type_(server->content_type())
, response_body_(server->response_body())
, response_status_code_(server->response_status_code())
, enable_valid_response_(server->enable_valid_response())
, close_connnection_after_request_(server->close_connnection_after_request()) {
http_parser_init(&parser_, HTTP_REQUEST);
http_parser_settings_init(&parser_settings_);
parser_.data = this;
parser_settings_.on_url = on_url;
}
void Server::ClientConnection::on_read(const char* data, size_t len) {
request_ = String(data, len);
size_t parsed = http_parser_execute(&parser_, &parser_settings_, data, len);
if (parsed < static_cast<size_t>(len)) {
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);
fprintf(stderr, "%s: %s\n", http_errno_name(err), http_errno_description(err));
close();
}
}
int Server::ClientConnection::on_url(http_parser* parser, const char* buf, size_t len) {
ClientConnection* self = static_cast<ClientConnection*>(parser->data);
self->handle_url(buf, len);
return 0;
}
void Server::ClientConnection::handle_url(const char* buf, size_t len) {
String path(buf, len);
if (path.substr(0, path.find("?")) == path_) { // Compare without query parameters
if (enable_valid_response_) {
if (response_body_.empty()) {
write(response(response_status_code_, request_)); // Echo response
} else {
write(response(response_status_code_, response_body_, content_type_));
}
} else {
write("Invalid HTTP server response");
}
} else {
write(response(404));
}
// From the HTTP/1.0 protocol specification:
//
// > When an Entity-Body is included with a message, the length of that body may be determined in
// > one of two ways. If a Content-Length header field is present, its value in bytes represents
// > the length of the Entity-Body. Otherwise, the body length is determined by the closing of the
// > connection by the server.
if (close_connnection_after_request_) {
close();
}
}