#include "AdapterTls.h"
namespace Socket
{
AdapterTls::AdapterTls(const Socket &sock, ::gnutls_priority_t priority_cache, ::gnutls_certificate_credentials_t x509_cred) noexcept
{
// https://leandromoreira.com.br/2015/10/12/how-to-optimize-nginx-configuration-for-http2-tls-ssl/
// https://www.gnutls.org/manual/html_node/False-Start.html
#ifdef GNUTLS_ENABLE_FALSE_START
constexpr int flags = GNUTLS_SERVER | GNUTLS_ENABLE_FALSE_START;
#else
constexpr int flags = GNUTLS_SERVER;
#endif
::gnutls_init(&this->session, flags);
::gnutls_priority_set(this->session, priority_cache);
::gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
::gnutls_certificate_server_set_request(this->session, GNUTLS_CERT_IGNORE);
::gnutls_transport_set_int2(this->session, sock.get_handle(), sock.get_handle() );
char h2[] = "h2";
char http11[] = "http/1.1";
const ::gnutls_datum_t protocols[] {
{ reinterpret_cast(h2), sizeof(h2) - 1 },
{ reinterpret_cast(http11), sizeof(http11) - 1 },
};
::gnutls_alpn_set_protocols(this->session, protocols, sizeof(protocols) / sizeof(::gnutls_datum_t), 0);
}
AdapterTls::AdapterTls(const ::gnutls_session_t session) noexcept : session(session)
{
}
bool AdapterTls::handshake() noexcept
{
int ret;
do
{
ret = ::gnutls_handshake(this->session);
}
while (ret < 0 && ::gnutls_error_is_fatal(ret) == 0);
if (ret < 0)
{
Socket sock(this->get_handle() );
sock.close();
::gnutls_deinit(this->session);
return false;
}
return true;
}
long AdapterTls::nonblock_send_all(const void *buf, const size_t length, const std::chrono::milliseconds &timeout) const noexcept
{
// size_t record_size = ::gnutls_record_get_max_size(this->session);
size_t record_size = length;
if (0 == record_size)
{
return -1;
}
Socket sock(this->get_handle() );
// ::gnutls_record_set_timeout(this->session, static_cast(timeout.count() ) );
size_t total = 0;
while (total < length)
{
if (record_size > length - total)
{
record_size = length - total;
}
// const long send_size = ::gnutls_record_send(this->session, reinterpret_cast(buf) + total, record_size);
long send_size = 0;
do
{
sock.nonblock_send_sync();
}
while (GNUTLS_E_AGAIN == (send_size = ::gnutls_record_send(this->session, reinterpret_cast(buf) + total, record_size) ) );
if (send_size < 0)
{
return send_size;
}
total += send_size;
}
return static_cast(total);
}
System::native_socket_type AdapterTls::get_handle() const noexcept
{
return static_cast(::gnutls_transport_get_int(this->session) );
}
::gnutls_session_t AdapterTls::get_tls_session() const noexcept
{
return this->session;
}
Adapter *AdapterTls::copy() const noexcept
{
return new AdapterTls(this->session);
}
long AdapterTls::nonblock_recv(void *buf, const size_t length, const std::chrono::milliseconds &timeout) const noexcept
{
// ::gnutls_record_set_timeout(this->session, static_cast(timeout.count() ) );
Socket sock(this->get_handle() );
sock.nonblock_recv_sync();
return ::gnutls_record_recv(this->session, buf, length);
}
long AdapterTls::nonblock_send(const void *buf, const size_t length, const std::chrono::milliseconds &timeout) const noexcept
{
return this->nonblock_send_all(buf, length, timeout);
}
void AdapterTls::close() noexcept
{
Socket sock(this->get_handle() );
// Wait for send all data to client
sock.nonblock_send_sync();
::gnutls_bye(this->session, GNUTLS_SHUT_RDWR);
sock.close();
::gnutls_deinit(this->session);
}
};