|
|
|
@ -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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|