forked from vitalif/vitastor
Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | 0d1b6d0760 |
|
@ -56,6 +56,11 @@ if (ISAL_LIBRARIES)
|
|||
add_definitions(-DWITH_ISAL)
|
||||
endif (ISAL_LIBRARIES)
|
||||
|
||||
find_package(OpenSSL)
|
||||
if (OPENSSL_FOUND)
|
||||
add_definitions(-DWITH_OPENSSL)
|
||||
endif (OPENSSL_FOUND)
|
||||
|
||||
add_custom_target(build_tests)
|
||||
add_custom_target(test
|
||||
COMMAND
|
||||
|
|
|
@ -27,10 +27,19 @@ static void parse_http_headers(std::string & res, http_response_t *parsed);
|
|||
|
||||
struct http_co_t
|
||||
{
|
||||
#ifdef WITH_OPENSSL
|
||||
static SSL_CTX *ssl_ctx = NULL;
|
||||
SSL *ssl_cli = NULL;
|
||||
BIO *ssl_rbio = NULL;
|
||||
BIO *ssl_wbio = NULL;
|
||||
std::vector<uint8_t> encrypted_out;
|
||||
#endif
|
||||
|
||||
timerfd_manager_t *tfd;
|
||||
std::function<void(const http_response_t*)> response_callback;
|
||||
|
||||
int request_timeout = 0;
|
||||
bool ssl = false;
|
||||
std::string host;
|
||||
std::string request;
|
||||
std::string ws_outbox;
|
||||
|
@ -46,7 +55,7 @@ struct http_co_t
|
|||
int timeout_id = -1;
|
||||
int epoll_events = 0;
|
||||
int sent = 0;
|
||||
std::vector<char> rbuf;
|
||||
std::vector<uint8_t> rbuf;
|
||||
iovec read_iov, send_iov;
|
||||
msghdr read_msg = { 0 }, send_msg = { 0 };
|
||||
http_response_t parsed;
|
||||
|
@ -259,6 +268,12 @@ void http_response_t::parse_json_response(std::string & error, json11::Json & r)
|
|||
|
||||
http_co_t::~http_co_t()
|
||||
{
|
||||
#ifdef WITH_OPENSSL
|
||||
if (ssl_cli)
|
||||
{
|
||||
SSL_free(ssl_cli);
|
||||
}
|
||||
#endif
|
||||
close_connection();
|
||||
}
|
||||
|
||||
|
@ -275,6 +290,16 @@ void http_co_t::close_connection()
|
|||
close(peer_fd);
|
||||
peer_fd = -1;
|
||||
}
|
||||
#ifdef WITH_OPENSSL
|
||||
if (ssl_ctx)
|
||||
{
|
||||
// Frees context, client and bios at once
|
||||
SSL_free(ssl_ctx);
|
||||
ssl_rbio = NULL;
|
||||
ssl_wbio = NULL;
|
||||
ssl_cli = NULL;
|
||||
}
|
||||
#endif
|
||||
state = HTTP_CO_CLOSED;
|
||||
connected_host = "";
|
||||
response = "";
|
||||
|
@ -304,6 +329,27 @@ void http_co_t::start_connection()
|
|||
}
|
||||
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
epoll_events = 0;
|
||||
#ifdef WITH_OPENSSL
|
||||
// https://wiki.openssl.org/index.php/Hostname_validation
|
||||
if (ssl)
|
||||
{
|
||||
if (!ssl_ctx)
|
||||
ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
ssl_rbio = BIO_new(BIO_s_mem());
|
||||
ssl_wbio = BIO_new(BIO_s_mem());
|
||||
ssl_cli = SSL_new(ssl_ctx);
|
||||
if (!ssl_ctx || !ssl_cli || !ssl_rbio || !ssl_wbio)
|
||||
{
|
||||
parsed = { .error = std::string("openssl initialization failed: ")+ERR_get_error(NULL) };
|
||||
response_callback(&parsed);
|
||||
response_callback = NULL;
|
||||
stackout();
|
||||
return;
|
||||
}
|
||||
SSL_set_connect_state(ssl_cli);
|
||||
SSL_set_bio(ssl_cli, ssl_rbio, ssl_wbio);
|
||||
}
|
||||
#endif
|
||||
// Finally call connect
|
||||
int r = ::connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
|
||||
if (r < 0 && errno != EINPROGRESS)
|
||||
|
@ -432,11 +478,11 @@ void http_co_t::submit_read(bool check_timeout)
|
|||
stackin();
|
||||
int res;
|
||||
again:
|
||||
if (rbuf.size() != READ_BUFFER_SIZE)
|
||||
if (rbuf.capacity()-rbuf.size() < READ_BUFFER_SIZE)
|
||||
{
|
||||
rbuf.resize(READ_BUFFER_SIZE);
|
||||
rbuf.reserve(rbuf.size() + READ_BUFFER_SIZE);
|
||||
}
|
||||
read_iov = { .iov_base = rbuf.data(), .iov_len = READ_BUFFER_SIZE };
|
||||
read_iov = { .iov_base = rbuf.data()+rbuf.size(), .iov_len = READ_BUFFER_SIZE };
|
||||
read_msg.msg_iov = &read_iov;
|
||||
read_msg.msg_iovlen = 1;
|
||||
res = recvmsg(peer_fd, &read_msg, 0);
|
||||
|
@ -466,6 +512,22 @@ again:
|
|||
else if (res <= 0)
|
||||
{
|
||||
// < 0 means error, 0 means EOF
|
||||
on_read_error(res);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ssl)
|
||||
handle_ssl_read(rbuf);
|
||||
else
|
||||
response += std::string((char*)rbuf.data(), res);
|
||||
rbuf.resize(0);
|
||||
handle_read();
|
||||
}
|
||||
stackout();
|
||||
}
|
||||
|
||||
void http_co_t::on_read_error(int res)
|
||||
{
|
||||
epoll_events = epoll_events & ~EPOLLIN;
|
||||
if (state == HTTP_CO_HEADERS_RECEIVED)
|
||||
std::swap(parsed.body, response);
|
||||
|
@ -473,12 +535,151 @@ again:
|
|||
if (res < 0)
|
||||
parsed = { .error = std::string("recvmsg: ")+strerror(-res) };
|
||||
run_cb_and_clear();
|
||||
}
|
||||
|
||||
int http_co_t::do_ssl_handshake()
|
||||
{
|
||||
stackin();
|
||||
int r;
|
||||
while (1)
|
||||
{
|
||||
r = SSL_do_handshake(ssl_cli);
|
||||
if (r == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
r = ssl_encrypt();
|
||||
if (r >= 0)
|
||||
submit_send();
|
||||
else
|
||||
{
|
||||
r = -r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response += std::string(rbuf.data(), res);
|
||||
handle_read();
|
||||
if (r == SSL_ERROR_WANT_READ || r == SSL_ERROR_NONE)
|
||||
{
|
||||
// OK or wait until we have more incoming data
|
||||
r = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
stackout();
|
||||
return r;
|
||||
}
|
||||
|
||||
// Enqueue outbound encrypted TLS data
|
||||
int http_co_t::ssl_encrypt()
|
||||
{
|
||||
stackin();
|
||||
int queued = 0;
|
||||
while (true)
|
||||
{
|
||||
if (encrypted_out.size() >= encrypted_out.capacity()/2)
|
||||
encrypted_out.reserve(encrypted_out.size() < READ_BUFFER_SIZE ? encrypted_out.size() + READ_BUFFER_SIZE : 2*encrypted_out.size());
|
||||
int r = BIO_read(ssl_wbio, encrypted_out.data()+encrypted_out.size(), encrypted_out.capacity()-encrypted_out.size());
|
||||
if (r > 0)
|
||||
{
|
||||
queued += r;
|
||||
encrypted_out.resize(encrypted_out.size()+r);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!BIO_should_retry(ssl_wbio))
|
||||
queued = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stackout();
|
||||
return queued;
|
||||
}
|
||||
|
||||
void http_co_t::handle_ssl_write()
|
||||
{
|
||||
stackin();
|
||||
int r = 0;
|
||||
while (sent < request.size())
|
||||
{
|
||||
if (!SSL_is_init_finished(ssl_cli))
|
||||
{
|
||||
if (do_ssl_handshake() != 0)
|
||||
{
|
||||
on_read_error(-EIO);
|
||||
break;
|
||||
}
|
||||
if (!SSL_is_init_finished(ssl_cli))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = SSL_write(ssl_cli, request.data()+sent, request.size()-sent);
|
||||
if (n > 0)
|
||||
sent += n;
|
||||
else if (get_sslstatus(ssl_cli, n) == SSLSTATUS_FAIL)
|
||||
{
|
||||
on_read_error(-EIO);
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
r = ssl_encrypt();
|
||||
if (r >= 0)
|
||||
submit_send();
|
||||
else
|
||||
{
|
||||
on_read_error(-EIO);
|
||||
break;
|
||||
}
|
||||
}
|
||||
stackout();
|
||||
}
|
||||
|
||||
// Process incoming encrypted TLS data
|
||||
void http_co_t::handle_ssl_read()
|
||||
{
|
||||
stackin();
|
||||
int size = rbuf.size();
|
||||
int done = 0;
|
||||
while (done < size)
|
||||
{
|
||||
int n = BIO_write(ssl_rbio, rbuf.data()+done, size-done);
|
||||
if (n > 0)
|
||||
{
|
||||
done += n;
|
||||
}
|
||||
if (n <= 0)
|
||||
{
|
||||
on_read_error(-EIO);
|
||||
break;
|
||||
}
|
||||
if (!SSL_is_init_finished(ssl_cli))
|
||||
{
|
||||
if (do_ssl_handshake() != 0)
|
||||
{
|
||||
on_read_error(-EIO);
|
||||
break;
|
||||
}
|
||||
if (!SSL_is_init_finished(ssl_cli))
|
||||
break;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (response.capacity() - response.size() < READ_BUFFER_SIZE)
|
||||
response.reserve(2*response.size() < response.size() + READ_BUFFER_SIZE ? response.size() + READ_BUFFER_SIZE : 2*response.size());
|
||||
n = SSL_read(ssl_cli, response.data() + response.size(), READ_BUFFER_SIZE);
|
||||
if (n <= 0)
|
||||
{
|
||||
n = SSL_get_error(ssl_cli, n);
|
||||
if (n == SSL_ERROR_WANT_READ)
|
||||
break;
|
||||
}
|
||||
} while (n > 0);
|
||||
}
|
||||
if (done < size)
|
||||
memmove(rbuf.data(), rbuf.data()+done, size-done);
|
||||
rbuf.resize(size-done);
|
||||
stackout();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue