Update yasio to latest

This commit is contained in:
halx99 2023-01-04 23:20:07 +08:00
parent 2547e8d3f8
commit 5bd36bfa7c
25 changed files with 882 additions and 592 deletions

View File

@ -40,7 +40,7 @@ THE SOFTWARE.
# include "audio/AudioDecoderEXT.h"
#endif
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
NS_AX_BEGIN

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
#include <unordered_map>
#include "mio/mio.hpp"
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
/**
* @addtogroup base

View File

@ -48,7 +48,7 @@
#include <map>
#include <mutex>
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
// minizip 1.2.0 is same with other platforms
#define unzGoToFirstFile64(A, B, C, D) unzGoToFirstFile2(A, B, C, D, NULL, 0, NULL, 0)

View File

@ -2,6 +2,7 @@
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
Copyright (c) 2022-2023 Bytedance Inc.
https://axmolengine.github.io/

View File

@ -32,7 +32,7 @@ THE SOFTWARE.
#include <WinVer.h>
#include <timeapi.h>
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
#include "ntcvt/ntcvt.hpp"
/**
@brief This function change the PVRFrame show/hide setting in register.

View File

@ -31,7 +31,7 @@ THE SOFTWARE.
#include "scripting/lua-bindings/manual/CCLuaStack.h"
#include "scripting/lua-bindings/manual/CCLuaEngine.h"
#include "platform/CCFileUtils.h"
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
USING_NS_AX;

View File

@ -46,7 +46,7 @@
#include "renderer/backend/VertexLayout.h"
#include "ui/GUIDefine.h"
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
#include <thread>
USING_NS_AX;

View File

@ -29,7 +29,7 @@ SOFTWARE.
#include "yasio/bindings/lyasio.hpp"
#include "yasio/detail/object_pool.hpp"
#include "yasio/detail/ref_ptr.hpp"
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
// A workaround to fix compile issue caused by `CCPlatformMacros.h` doesn't handle `__has_attribute` it properly
# if !__has_attribute(format)

View File

@ -36,6 +36,10 @@ SOFTWARE.
# include <sdkddkver.h>
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
# define snprintf sprintf_s
#endif
// Tests whether compiler has(fully) c++11 support and keywords workaround for msvc
#if !defined(_MSC_VER) || _MSC_VER >= 1900
# define YASIO__HAS_CXX11 1
@ -50,8 +54,7 @@ SOFTWARE.
#endif
// Tests whether compiler has c++14 support
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
(defined(_MSC_VER) && _MSC_VER >= 1900 && (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)))
#if (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_MSC_VER) && _MSC_VER >= 1900 && (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)))
# ifndef YASIO_HAS_CXX14
# define YASIO__HAS_CXX14 1
# endif // C++14 features macro
@ -62,9 +65,7 @@ SOFTWARE.
// Tests whether compiler has c++17 support
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
(defined(_MSC_VER) && _MSC_VER > 1900 && \
((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || \
(defined(_MSVC_LANG) && (_MSVC_LANG > 201402L))))
(defined(_MSC_VER) && _MSC_VER > 1900 && ((defined(_HAS_CXX17) && _HAS_CXX17 == 1) || (defined(_MSVC_LANG) && (_MSVC_LANG > 201402L))))
# ifndef YASIO_HAS_CXX17
# define YASIO__HAS_CXX17 1
# endif // C++17 features macro
@ -75,9 +76,7 @@ SOFTWARE.
// Tests whether compiler has c++20 support
#if (defined(__cplusplus) && __cplusplus > 201703L) || \
(defined(_MSC_VER) && _MSC_VER > 1900 && \
((defined(_HAS_CXX20) && _HAS_CXX20 == 1) || \
(defined(_MSVC_LANG) && (_MSVC_LANG > 201703L))))
(defined(_MSC_VER) && _MSC_VER > 1900 && ((defined(_HAS_CXX20) && _HAS_CXX20 == 1) || (defined(_MSVC_LANG) && (_MSVC_LANG > 201703L))))
# ifndef YASIO__HAS_CXX20
# define YASIO__HAS_CXX20 1
# endif // C++20 features macro
@ -103,8 +102,7 @@ SOFTWARE.
// Tests whether current OS is BSD-like system for process common BSD socket behaviors
#if !defined(_WIN32) && !defined(__linux__)
# include <sys/param.h>
# if defined(BSD) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
# if defined(BSD) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
# define YASIO__OS_BSD_LIKE 1
# else
# define YASIO__OS_BSD_LIKE 0
@ -128,8 +126,7 @@ SOFTWARE.
#endif
// 64bits Sense Macros
#if defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) || \
defined(__x86_64) || defined(__arm64__) || defined(__aarch64__)
#if defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64) || defined(__x86_64) || defined(__arm64__) || defined(__aarch64__)
# define YASIO__64BITS 1
#else
# define YASIO__64BITS 0
@ -149,8 +146,7 @@ SOFTWARE.
#endif
#include <system_error>
#define yasio__throw_error(ec, what) \
YASIO__THROW0(std::system_error(std::error_code{(int)ec, std::system_category()}, what))
#define yasio__throw_error(ec, what) YASIO__THROW0(std::system_error(std::error_code{(int)ec, std::system_category()}, what))
#define yasio__throw_error0(ec) yasio__throw_error(ec, "")
// Compatibility with non-clang compilers...

View File

@ -206,13 +206,6 @@ SOFTWARE.
// The default max listen count of tcp server.
#define YASIO_SOMAXCONN 19
// The max wait duration in microseconds when io_service nothing to do.
#define YASIO_MAX_WAIT_DURATION (5LL * 60LL * 1000LL * 1000LL)
// The min wait duration in microseconds when io_service have outstanding work to do.
// !!!Only affects Single Core CPU
#define YASIO_MIN_WAIT_DURATION 10LL
// The default ttl of multicast
#define YASIO_DEFAULT_MULTICAST_TTL (int)128
@ -238,4 +231,7 @@ SOFTWARE.
#define YASIO_SSL_PIN "yasio_ssl_client"
#define YASIO_SSL_PIN_LEN (sizeof(YASIO_SSL_PIN) - 1)
#define YASIO_SSL_PON "yasio_ssl_server"
#define YASIO_SSL_PON_LEN (sizeof(YASIO_SSL_PON) - 1)
#endif

207
thirdparty/yasio/detail/mbedtls.inl vendored Normal file
View File

@ -0,0 +1,207 @@
//////////////////////////////////////////////////////////////////////////////////////////
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// client application.
//////////////////////////////////////////////////////////////////////////////////////////
/*
The MIT License (MIT)
Copyright (c) 2012-2023 HALX99
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef YASIO__MBEDTLS_INL
#define YASIO__MBEDTLS_INL
#if YASIO_SSL_BACKEND == 2
YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
{
auto ctx = new ssl_ctx_st();
::mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
::mbedtls_entropy_init(&ctx->entropy);
::mbedtls_ssl_config_init(&ctx->conf);
::mbedtls_x509_crt_init(&ctx->cert);
::mbedtls_pk_init(&ctx->pkey);
do
{
int ret = 0;
// ssl role
if ((ret = ::mbedtls_ssl_config_defaults(&ctx->conf, opts.client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
YASIO_LOG("mbedtls_ssl_config_defaults fail with ret=%d", ret);
// rgn engine
cxx17::string_view pers = opts.client ? cxx17::string_view{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN} : cxx17::string_view{YASIO_SSL_PON, YASIO_SSL_PON_LEN};
ret = ::mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, ::mbedtls_entropy_func, &ctx->entropy, (const unsigned char*)pers.data(), pers.length());
if (ret != 0)
{
YASIO_LOG("mbedtls_ctr_drbg_seed fail with ret=%d", ret);
break;
}
::mbedtls_ssl_conf_rng(&ctx->conf, ::mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
// --- load cert
int authmode = MBEDTLS_SSL_VERIFY_OPTIONAL;
if (yasio__valid_str(opts.crtfile_)) // the cafile_ must be full path
{
if ((ret = ::mbedtls_x509_crt_parse_file(&ctx->cert, opts.crtfile_)) == 0)
authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
else
{
YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
break;
}
}
if (opts.client)
{
::mbedtls_ssl_conf_authmode(&ctx->conf, authmode);
::mbedtls_ssl_conf_ca_chain(&ctx->conf, &ctx->cert, nullptr);
}
else
{
// --- load server private key
if (yasio__valid_str(opts.keyfile_))
{
# if MBEDTLS_VERSION_MAJOR >= 3
if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr, mbedtls_ctr_drbg_random, &ctx->ctr_drbg) != 0)
# else
if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr) != 0)
# endif
{
YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
break;
}
}
::mbedtls_ssl_conf_ca_chain(&ctx->conf, ctx->cert.next, nullptr);
if ((ret = mbedtls_ssl_conf_own_cert(&ctx->conf, &ctx->cert, &ctx->pkey)) != 0)
{
YASIO_LOG("mbedtls_ssl_conf_own_cert with ret=-0x%x", (unsigned int)-ret);
break;
}
}
} while (false);
return ctx;
}
YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx)
{
if (!ctx)
return;
::mbedtls_pk_free(&ctx->pkey);
::mbedtls_x509_crt_free(&ctx->cert);
::mbedtls_ssl_config_free(&ctx->conf);
::mbedtls_entropy_free(&ctx->entropy);
::mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
delete ctx;
ctx = nullptr;
}
YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client)
{
auto ssl = new ssl_st();
::mbedtls_ssl_init(ssl);
::mbedtls_ssl_setup(ssl, &ctx->conf);
// ssl_set_fd
ssl->bio.fd = fd;
::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::mbedtls_net_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */);
::mbedtls_ssl_set_hostname(ssl, hostname);
return ssl;
}
YASIO__DECL void yssl_shutdown(ssl_st*& ssl)
{
::mbedtls_ssl_close_notify(ssl);
::mbedtls_ssl_free(ssl);
delete ssl;
ssl = nullptr;
}
YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err)
{
int ret = ::mbedtls_ssl_handshake_step(ssl);
switch (ret)
{
case 0:
if (ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER)
return 0;
err = EWOULDBLOCK;
ret = -1;
break;
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
err = EWOULDBLOCK;
break; // Nothing need to do
default:
err = yasio::errc::ssl_handshake_failed;
}
return ret;
}
const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen)
{
int n = snprintf(buf, buflen, "error:%d:", sslerr);
::mbedtls_strerror(sslerr, buf + n, buflen - n);
return buf;
}
YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err)
{
int n = ::mbedtls_ssl_write(ssl, static_cast<const uint8_t*>(data), len);
if (n > 0)
return n;
switch (n)
{
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
err = EWOULDBLOCK;
break;
default:
err = yasio::errc::ssl_write_failed;
}
return -1;
}
YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err)
{
int n = ::mbedtls_ssl_read(ssl, static_cast<uint8_t*>(data), len);
if (n > 0)
return n;
switch (n)
{
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: // n=0, the upper caller will regards as eof
n = 0;
case 0:
err = yasio::xxsocket::get_last_errno();
::mbedtls_ssl_close_notify(ssl);
break;
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
err = EWOULDBLOCK;
break;
default:
err = yasio::errc::ssl_read_failed;
}
return n;
}
#endif
#endif

View File

@ -1,5 +1,5 @@
//////////////////////////////////////////////////////////////////////////////////////////
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// client application.
//////////////////////////////////////////////////////////////////////////////////////////
/*
@ -49,33 +49,30 @@ namespace gc
#define YASIO_POOL_FL_BEGIN(chunk) reinterpret_cast<free_link_node*>(chunk->data)
#define YASIO_POOL_PREALLOCATE 1
template <typename _Ty> static size_t aligned_storage_size()
template <typename _Ty>
static size_t aligned_storage_size()
{
return sizeof(typename std::aligned_storage<sizeof(_Ty), std::alignment_of<_Ty>::value>::type);
}
namespace detail
{
class object_pool
{
typedef struct free_link_node
{
class object_pool {
typedef struct free_link_node {
free_link_node* next;
} * free_link;
}* free_link;
typedef struct chunk_link_node
{
typedef struct chunk_link_node {
chunk_link_node* next;
char data[0];
} * chunk_link;
}* chunk_link;
object_pool(const object_pool&) = delete;
object_pool(const object_pool&) = delete;
void operator=(const object_pool&) = delete;
public:
OBJECT_POOL_DECL object_pool(size_t element_size, size_t element_count)
: free_link_(nullptr), chunk_(nullptr), element_size_(element_size),
element_count_(element_count)
: free_link_(nullptr), chunk_(nullptr), element_size_(element_size), element_count_(element_count)
{
#if YASIO_POOL_PREALLOCATE
release(allocate_from_process_heap()); // preallocate 1 chunk
@ -93,7 +90,7 @@ public:
while ((p = *q) != nullptr)
{
*q = p->next;
free(p);
delete[] (uint8_t*)(p);
}
free_link_ = nullptr;
@ -147,8 +144,7 @@ private:
}
OBJECT_POOL_DECL void* allocate_from_process_heap(void)
{
chunk_link new_chunk =
(chunk_link)malloc(sizeof(chunk_link_node) + element_size_ * element_count_);
chunk_link new_chunk = (chunk_link) new uint8_t[sizeof(chunk_link_node) + element_size_ * element_count_];
#ifdef _DEBUG
::memset(new_chunk, 0x00, sizeof(chunk_link_node));
#endif
@ -171,8 +167,7 @@ private:
for (char* ptr = chunk->data; ptr < rbegin; ptr += element_size_)
{
reinterpret_cast<free_link_node*>(ptr)->next =
reinterpret_cast<free_link_node*>(ptr + element_size_);
reinterpret_cast<free_link_node*>(ptr)->next = reinterpret_cast<free_link_node*>(ptr + element_size_);
}
return reinterpret_cast<free_link_node*>(rbegin);
}
@ -185,14 +180,13 @@ private:
};
} // namespace detail
template <typename _Ty, typename _Mutex = std::mutex> class object_pool : public detail::object_pool
{
template <typename _Ty, typename _Mutex = std::mutex>
class object_pool : public detail::object_pool {
public:
object_pool(size_t _ElemCount = 512)
: detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount)
{}
object_pool(size_t _ElemCount = 512) : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) {}
template <typename... _Types> _Ty* construct(_Types&&... args)
template <typename... _Types>
_Ty* construct(_Types&&... args)
{
return new (allocate()) _Ty(std::forward<_Types>(args)...);
}
@ -218,17 +212,16 @@ public:
_Mutex mutex_;
};
template <typename _Ty> class object_pool<_Ty, void> : public detail::object_pool
{
object_pool(const object_pool&) = delete;
template <typename _Ty>
class object_pool<_Ty, void> : public detail::object_pool {
object_pool(const object_pool&) = delete;
void operator=(const object_pool&) = delete;
public:
object_pool(size_t _ElemCount = 512)
: detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount)
{}
object_pool(size_t _ElemCount = 512) : detail::object_pool(yasio::gc::aligned_storage_size<_Ty>(), _ElemCount) {}
template <typename... _Types> _Ty* construct(_Types&&... args)
template <typename... _Types>
_Ty* construct(_Types&&... args)
{
return new (allocate()) _Ty(std::forward<_Types>(args)...);
}
@ -244,39 +237,57 @@ public:
void deallocate(void* _Ptr) { release(_Ptr); }
};
#define DEFINE_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \
public: \
static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \
\
static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \
\
static void operator delete(void* p) { get_pool().deallocate(p); } \
\
static yasio::gc::object_pool<ELEMENT_TYPE, void>& get_pool() \
{ \
static yasio::gc::object_pool<ELEMENT_TYPE, void> s_pool(ELEMENT_COUNT); \
return s_pool; \
#define DEFINE_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \
public: \
static void* operator new(size_t /*size*/) \
{ \
return get_pool().allocate(); \
} \
\
static void* operator new(size_t /*size*/, std::nothrow_t) \
{ \
return get_pool().allocate(); \
} \
\
static void operator delete(void* p) \
{ \
get_pool().deallocate(p); \
} \
\
static yasio::gc::object_pool<ELEMENT_TYPE, void>& get_pool() \
{ \
static yasio::gc::object_pool<ELEMENT_TYPE, void> s_pool(ELEMENT_COUNT); \
return s_pool; \
}
// The thread safe edition
#define DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \
public: \
static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \
\
static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \
\
static void operator delete(void* p) { get_pool().deallocate(p); } \
\
static yasio::gc::object_pool<ELEMENT_TYPE, std::mutex>& get_pool() \
{ \
static yasio::gc::object_pool<ELEMENT_TYPE, std::mutex> s_pool(ELEMENT_COUNT); \
return s_pool; \
#define DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(ELEMENT_TYPE, ELEMENT_COUNT) \
public: \
static void* operator new(size_t /*size*/) \
{ \
return get_pool().allocate(); \
} \
\
static void* operator new(size_t /*size*/, std::nothrow_t) \
{ \
return get_pool().allocate(); \
} \
\
static void operator delete(void* p) \
{ \
get_pool().deallocate(p); \
} \
\
static yasio::gc::object_pool<ELEMENT_TYPE, std::mutex>& get_pool() \
{ \
static yasio::gc::object_pool<ELEMENT_TYPE, std::mutex> s_pool(ELEMENT_COUNT); \
return s_pool; \
}
//////////////////////// allocator /////////////////
// TEMPLATE CLASS object_pool_allocator, can't used by std::vector, DO NOT use at non-msvc compiler.
template <class _Ty, size_t _ElemCount = 512, class _Mutex = void> class object_pool_allocator
{ // generic allocator for objects of class _Ty
template <class _Ty, size_t _ElemCount = 512, class _Mutex = void>
class object_pool_allocator { // generic allocator for objects of class _Ty
public:
typedef _Ty value_type;
@ -292,8 +303,8 @@ public:
typedef long difference_type;
#endif
template <class _Other> struct rebind
{ // convert this type to _ALLOCATOR<_Other>
template <class _Other>
struct rebind { // convert this type to _ALLOCATOR<_Other>
typedef object_pool_allocator<_Other> other;
};
@ -315,7 +326,8 @@ public:
{ // construct by copying (do nothing)
}
template <class _Other> object_pool_allocator(const object_pool_allocator<_Other>&) throw()
template <class _Other>
object_pool_allocator(const object_pool_allocator<_Other>&) throw()
{ // construct from a related allocator (do nothing)
}
@ -357,17 +369,20 @@ public:
new ((void*)_Ptr) _Ty(std::forward<_Ty>(_Val));
}
template <class _Other> void construct(pointer _Ptr, _Other&& _Val)
template <class _Other>
void construct(pointer _Ptr, _Other&& _Val)
{ // construct object at _Ptr with value _Val
new ((void*)_Ptr) _Ty(std::forward<_Other>(_Val));
}
template <class _Objty, class... _Types> void construct(_Objty* _Ptr, _Types&&... _Args)
template <class _Objty, class... _Types>
void construct(_Objty* _Ptr, _Types&&... _Args)
{ // construct _Objty(_Types...) at _Ptr
::new ((void*)_Ptr) _Objty(std::forward<_Types>(_Args)...);
}
template <class _Uty> void destroy(_Uty* _Ptr)
template <class _Uty>
void destroy(_Uty* _Ptr)
{ // destroy object at _Ptr, do nothing
_Ptr->~_Uty();
}
@ -386,15 +401,13 @@ public:
};
template <class _Ty, class _Other>
inline bool operator==(const object_pool_allocator<_Ty>&,
const object_pool_allocator<_Other>&) throw()
inline bool operator==(const object_pool_allocator<_Ty>&, const object_pool_allocator<_Other>&) throw()
{ // test for allocator equality
return (true);
}
template <class _Ty, class _Other>
inline bool operator!=(const object_pool_allocator<_Ty>& _Left,
const object_pool_allocator<_Other>& _Right) throw()
inline bool operator!=(const object_pool_allocator<_Ty>& _Left, const object_pool_allocator<_Other>& _Right) throw()
{ // test for allocator inequality
return (!(_Left == _Right));
}

View File

@ -33,7 +33,7 @@ SOFTWARE.
#include <limits>
#include <stack>
#include <fstream>
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/string_view.hpp"
#include "yasio/detail/endian_portable.hpp"
#include "yasio/detail/utils.hpp"
#include "yasio/detail/byte_buffer.hpp"

187
thirdparty/yasio/detail/openssl.inl vendored Normal file
View File

@ -0,0 +1,187 @@
//////////////////////////////////////////////////////////////////////////////////////////
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
// client application.
//////////////////////////////////////////////////////////////////////////////////////////
/*
The MIT License (MIT)
Copyright (c) 2012-2023 HALX99
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef YASIO__OPENSSL_INL
#define YASIO__OPENSSL_INL
#if YASIO_SSL_BACKEND == 1 // OpenSSL
// The ssl error mask (1 << 31), a little hack, but works
# define YSSL_ERR_MASK 0x80000000
YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
{
auto ctx = ::SSL_CTX_new(opts.client ? ::SSLv23_client_method() : SSLv23_server_method());
auto mode = SSL_CTX_get_mode(ctx);
# if defined(SSL_MODE_RELEASE_BUFFERS)
mode |= SSL_MODE_RELEASE_BUFFERS;
# endif
::SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | mode);
if (opts.client)
{
if (yasio__valid_str(opts.crtfile_))
{
if (::SSL_CTX_load_verify_locations(ctx, opts.crtfile_, nullptr) == 1)
{
::SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ::SSL_CTX_get_verify_callback(ctx));
# if OPENSSL_VERSION_NUMBER >= 0x10101000L
::SSL_CTX_set_post_handshake_auth(ctx, 1);
# endif
# if defined(X509_V_FLAG_PARTIAL_CHAIN)
/* Have intermediate certificates in the trust store be treated as
trust-anchors, in the same way as self-signed root CA certificates
are. This allows users to verify servers using the intermediate cert
only, instead of needing the whole chain. */
X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), X509_V_FLAG_PARTIAL_CHAIN);
# endif
}
else
YASIO_LOG("[global] load ca certifaction file failed!");
}
else
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
}
else
{
if (yasio__valid_str(opts.crtfile_) && ::SSL_CTX_use_certificate_file(ctx, opts.crtfile_, SSL_FILETYPE_PEM) <= 0)
YASIO_LOG("[gobal] load server cert file failed!");
if (yasio__valid_str(opts.keyfile_) && ::SSL_CTX_use_PrivateKey_file(ctx, opts.keyfile_, SSL_FILETYPE_PEM) <= 0)
YASIO_LOG("[gobal] load server private key file failed!");
}
return ctx;
}
YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx)
{
::SSL_CTX_free((SSL_CTX*)ctx);
ctx = nullptr;
}
YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client)
{
auto ssl = ::SSL_new(ctx);
::SSL_set_fd(ssl, fd);
if (client)
{
::SSL_set_connect_state(ssl);
::SSL_set_tlsext_host_name(ssl, hostname);
}
else
::SSL_set_accept_state(ssl);
return ssl;
}
YASIO__DECL void yssl_shutdown(ssl_st*& ssl)
{
::SSL_shutdown(ssl);
::SSL_free(ssl);
ssl = nullptr;
}
YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err)
{
ERR_clear_error();
int ret = ::SSL_do_handshake(ssl);
if (ret == 1) // handshake succeed
return 0;
int sslerr = ::SSL_get_error(ssl, ret);
/*
When using a non-blocking socket, nothing is to be done, but select() can be used to check for
the required condition: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html
*/
if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE)
{
err = EWOULDBLOCK;
return -1;
}
/* ssl handshake fail */
err = yasio::errc::ssl_handshake_failed; // emit ssl handshake failed, continue handle close flow
return (sslerr != SSL_ERROR_SYSCALL) ? static_cast<int>(ERR_get_error() | YSSL_ERR_MASK) : yasio::xxsocket::get_last_errno();
}
YASIO__DECL const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen)
{
if (yasio__testbits(sslerr, YSSL_ERR_MASK))
::ERR_error_string_n((unsigned int)sslerr & ~YSSL_ERR_MASK, buf, buflen);
else
yasio::xxsocket::strerror_r(sslerr, buf, buflen);
return buf;
}
YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err)
{
ERR_clear_error();
int n = ::SSL_write(ssl, data, static_cast<int>(len));
if (n > 0)
return n;
int sslerr = ::SSL_get_error(ssl, n);
if (sslerr == SSL_ERROR_ZERO_RETURN)
{ // SSL_ERROR_SYSCALL
err = yasio::xxsocket::get_last_errno();
return 0;
}
switch (sslerr)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
err = EWOULDBLOCK;
break;
default:
err = yasio::errc::ssl_write_failed;
}
return -1;
}
YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err)
{
ERR_clear_error();
int n = ::SSL_read(ssl, data, static_cast<int>(len));
if (n > 0)
return n;
int sslerr = ::SSL_get_error(ssl, n);
switch (sslerr)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* The operation did not complete; the same TLS/SSL I/O function
should be called again later. This is basically an EWOULDBLOCK
equivalent. */
err = EWOULDBLOCK;
break;
default:
err = sslerr == SSL_ERROR_SYSCALL ? yasio::xxsocket::get_last_errno() : yasio::errc::ssl_read_failed;
if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) // peer closed connection in SSL handshake
return 0;
}
return -1;
}
#endif
#endif

View File

@ -26,7 +26,7 @@ public:
void reset() { this->fd_set_.clear(); }
int poll_io(timeval& waitd_tv) { return ::poll(this->fd_set_.data(), static_cast<int>(this->fd_set_.size()), waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000); }
int poll_io(int wait_ms) { return ::poll(this->fd_set_.data(), static_cast<int>(this->fd_set_.size()), wait_ms); }
int is_set(socket_native_type fd, int events) const
{

View File

@ -36,7 +36,11 @@ public:
return *this;
}
int poll_io(timeval& waitd_tv) { return ::select(this->max_descriptor_, &(fd_set_[read_op]), &(fd_set_[write_op]), nullptr, &waitd_tv); }
int poll_io(int waitd_ms)
{
timeval waitd_tv = {(decltype(timeval::tv_sec))(waitd_ms / 1000), (decltype(timeval::tv_usec))(waitd_ms % 1000)};
return ::select(this->max_descriptor_, &(fd_set_[read_op]), &(fd_set_[write_op]), nullptr, &waitd_tv);
}
int is_set(socket_native_type fd, int events) const
{

View File

@ -241,12 +241,13 @@ namespace inet
struct socket_event {
enum
{ // event mask
read = 1,
write = 2,
error = 4,
null = 0,
read = 1,
write = 2,
error = 4,
readwrite = read | write,
};
};
}
} // namespace inet
} // namespace yasio
#endif

View File

@ -29,6 +29,8 @@ SOFTWARE.
#ifndef YASIO__SSL_HPP
#define YASIO__SSL_HPP
#include "yasio/detail/config.hpp"
#if YASIO_SSL_BACKEND == 1 // OpenSSL
# include <openssl/bio.h>
# include <openssl/ssl.h>
@ -41,27 +43,56 @@ SOFTWARE.
# include "mbedtls/entropy.h"
# include "mbedtls/ctr_drbg.h"
# include "mbedtls/error.h"
# include "mbedtls/version.h"
struct ssl_ctx_st {
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
mbedtls_x509_crt cacert;
mbedtls_x509_crt cert;
mbedtls_pk_context pkey;
mbedtls_ssl_config conf;
};
struct ssl_st : public mbedtls_ssl_context {
mbedtls_net_context bio;
};
inline ssl_st* mbedtls_ssl_new(ssl_ctx_st* ctx)
{
auto ssl = new ssl_st();
::mbedtls_ssl_init(ssl);
::mbedtls_ssl_setup(ssl, &ctx->conf);
return ssl;
}
inline void mbedtls_ssl_set_fd(ssl_st* ssl, int fd)
{
ssl->bio.fd = fd;
::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::mbedtls_net_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */);
}
#endif
#if defined(YASIO_SSL_BACKEND)
struct yssl_options {
const char* crtfile_;
const char* keyfile_;
bool client;
};
YASIO__DECL ssl_ctx_st* yssl_ctx_new(const yssl_options& opts);
YASIO__DECL void yssl_ctx_free(ssl_ctx_st*& ctx);
YASIO__DECL ssl_st* yssl_new(ssl_ctx_st* ctx, int fd, const char* hostname, bool client);
YASIO__DECL void yssl_shutdown(ssl_st*&);
/**
* @returns
* 0: succeed
* other: use yssl_strerror(ret & YSSL_ERROR_MASK, ...) get error message
* - err can bb
- EWOULDBLOCK: status ok, repeat call next time
* - yasio::errc::ssl_handshake_failed: failed
*/
YASIO__DECL int yssl_do_handshake(ssl_st* ssl, int& err);
YASIO__DECL const char* yssl_strerror(ssl_st* ssl, int sslerr, char* buf, size_t buflen);
YASIO__DECL int yssl_write(ssl_st* ssl, const void* data, size_t len, int& err);
YASIO__DECL int yssl_read(ssl_st* ssl, void* data, size_t len, int& err);
#endif
///////////////////////////////////////////////////////////////////
// --- Implement common yasio ssl api with different ssl backends
#define yasio__valid_str(str) (str && *str)
#if YASIO_SSL_BACKEND == 1 // openssl
# include "yasio/detail/openssl.inl"
#elif YASIO_SSL_BACKEND == 2 // mbedtls
# include "yasio/detail/mbedtls.inl"
#else
# error "yasio - Unsupported ssl backend provided!"
#endif

View File

@ -1436,6 +1436,15 @@ inline std::basic_string<_CharT, _Traits, Allocator>& assign(std::basic_string<_
lhs.clear();
return lhs;
}
template <typename _CharT, typename _Traits, typename Allocator>
inline std::basic_string<_CharT, _Traits, Allocator>& append(std::basic_string<_CharT, _Traits, Allocator>& lhs, const basic_string_view<_CharT, _Traits>& rhs)
{
if (!rhs.empty())
lhs.append(rhs.data(), rhs.size());
else
lhs.clear();
return lhs;
}
template <typename _CharT, typename _Traits, typename Allocator = std::allocator<_CharT>>
inline std::basic_string<_CharT, _Traits, Allocator> svtos(const basic_string_view<_CharT, _Traits>& value)
{

View File

@ -303,6 +303,7 @@ void xxsocket::traverse_local_address(std::function<bool(const ip::endpoint&)> h
endpoint ep;
int iret = getaddrinfo(hostname, nullptr, &hint, &ailist);
(void)iret;
if (ailist != nullptr)
{
for (auto aip = ailist; aip != nullptr; aip = aip->ai_next)
@ -362,17 +363,17 @@ void xxsocket::traverse_local_address(std::function<bool(const ip::endpoint&)> h
xxsocket::xxsocket(void) : fd(invalid_socket) {}
xxsocket::xxsocket(socket_native_type h) : fd(h) {}
xxsocket::xxsocket(xxsocket&& right) : fd(invalid_socket) { swap(right); }
xxsocket::xxsocket(xxsocket&& right) YASIO__NOEXCEPT : fd(invalid_socket) { swap(right); }
xxsocket::xxsocket(int af, int type, int protocol) : fd(invalid_socket) { open(af, type, protocol); }
xxsocket::~xxsocket(void) { close(); }
xxsocket& xxsocket::operator=(socket_native_type handle)
xxsocket& xxsocket::operator=(socket_native_type handle) YASIO__NOEXCEPT
{
if (!this->is_open())
this->fd = handle;
return *this;
}
xxsocket& xxsocket::operator=(xxsocket&& right) { return swap(right); }
xxsocket& xxsocket::operator=(xxsocket&& right) YASIO__NOEXCEPT { return swap(right); }
xxsocket& xxsocket::swap(xxsocket& rhs)
{
@ -550,7 +551,7 @@ int xxsocket::connect_n(socket_native_type s, const endpoint& ep, const std::chr
if (ret == 0)
goto done; /* connect completed immediately */
if ((ret = xxsocket::select(s, &rset, &wset, nullptr, wtimeout)) <= 0)
if (xxsocket::select(s, &rset, &wset, nullptr, wtimeout) <= 0)
error = xxsocket::get_last_errno();
else if ((FD_ISSET(s, &rset) || FD_ISSET(s, &wset)))
{ /* Everythings are ok */
@ -930,17 +931,27 @@ const char* xxsocket::strerror(int error)
{
#if defined(_WIN32)
static char error_msg[256];
ZeroMemory(error_msg, sizeof(error_msg));
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK /* remove line-end charactors \r\n */, NULL,
error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // english language
error_msg, sizeof(error_msg), nullptr);
return error_msg;
return xxsocket::strerror_r(error, error_msg, sizeof(error_msg));
#else
return ::strerror(error);
#endif
}
const char* xxsocket::strerror_r(int error, char* buf, size_t buflen)
{
#if defined(_WIN32)
ZeroMemory(buf, buflen);
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK /* remove line-end charactors \r\n */, NULL,
error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // english language
buf, static_cast<DWORD>(buflen), nullptr);
return buf;
#else
(void)::strerror_r(error, buf, buflen);
return buf;
#endif
}
const char* xxsocket::gai_strerror(int error)
{
#if defined(_WIN32)
@ -960,7 +971,7 @@ struct ws2_32_gc {
ws2_32_gc(void)
{
WSADATA dat = {0};
WSAStartup(0x0202, &dat);
(void)WSAStartup(0x0202, &dat);
}
~ws2_32_gc(void) { WSACleanup(); }
};

View File

@ -635,13 +635,13 @@ public:
// Disable copy constructor
YASIO__DECL xxsocket(const xxsocket&) = delete;
// Construct with a exist socket, it will replace the source
YASIO__DECL xxsocket(xxsocket&&);
YASIO__DECL xxsocket(xxsocket&&) YASIO__NOEXCEPT;
YASIO__DECL xxsocket& operator=(socket_native_type handle);
YASIO__DECL xxsocket& operator=(socket_native_type handle) YASIO__NOEXCEPT;
// Disable copy assign operator
YASIO__DECL xxsocket& operator=(const xxsocket&) = delete;
// Construct with a exist socket, it will replace the source
YASIO__DECL xxsocket& operator=(xxsocket&&);
YASIO__DECL xxsocket& operator=(xxsocket&&) YASIO__NOEXCEPT;
// See also as function: open
YASIO__DECL xxsocket(int af, int type, int protocol);
@ -1022,6 +1022,7 @@ public:
YASIO__DECL static bool not_recv_error(int error);
YASIO__DECL static const char* strerror(int error);
YASIO__DECL static const char* strerror_r(int error, char* buf, size_t buflen);
YASIO__DECL static const char* gai_strerror(int error);
/// <summary>

View File

@ -117,15 +117,15 @@ enum
YCPF_NEEDS_QUERY = 1 << 21,
/// below is byte2 of private flags (25~32) are mutable, and will be cleared automatically when connect flow done, see clear_mutable_flags.
/* whether ssl client in handshaking */
YCPF_SSL_HANDSHAKING = 1 << 25,
};
namespace
{
// the minimal wait duration for select
static highp_time_t yasio__min_wait_duration = 0LL;
static highp_time_t yasio__min_wait_usec = 0LL;
// By default we will wait no longer than 5 minutes. This will ensure that
// any changes to the system clock are detected after no longer than this.
static const highp_time_t yasio__max_wait_usec = 5 * 60 * 1000 * 1000LL;
// the max transport alloc size
static const size_t yasio__max_tsize = (std::max)({sizeof(io_transport_tcp), sizeof(io_transport_udp), sizeof(io_transport_ssl), sizeof(io_transport_kcp)});
} // namespace
@ -139,11 +139,14 @@ struct yasio__global_state {
{
auto __get_cprint = [&]() -> const print_fn2_t& { return custom_print; };
// for single core CPU, we set minimal wait duration to 10us by default
yasio__min_wait_duration = std::thread::hardware_concurrency() > 1 ? 0LL : YASIO_MIN_WAIT_DURATION;
yasio__min_wait_usec = std::thread::hardware_concurrency() > 1 ? 0LL : 10LL;
#if defined(YASIO_SSL_BACKEND) && YASIO_SSL_BACKEND == 1
# if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
if (OPENSSL_init_ssl(0, nullptr) == 1)
{
yasio__setbits(this->init_flags_, INITF_SSL);
ERR_clear_error();
}
# endif
#endif
#if defined(YASIO_HAVE_CARES)
@ -190,27 +193,13 @@ void highp_timer::cancel(io_service& service)
}
/// io_send_op
int io_send_op::perform(io_transport* transport, const void* buf, int n) { return transport->write_cb_(buf, n, nullptr); }
int io_send_op::perform(io_transport* transport, const void* buf, int n, int& error) { return transport->write_cb_(buf, n, nullptr, error); }
/// io_sendto_op
int io_sendto_op::perform(io_transport* transport, const void* buf, int n) { return transport->write_cb_(buf, n, std::addressof(destination_)); }
#if defined(YASIO_SSL_BACKEND)
void ssl_auto_handle::destroy()
int io_sendto_op::perform(io_transport* transport, const void* buf, int n, int& error)
{
if (ssl_)
{
# if YASIO_SSL_BACKEND == 1
::SSL_shutdown(ssl_);
::SSL_free(ssl_);
# elif YASIO_SSL_BACKEND == 2
::mbedtls_ssl_free(ssl_);
delete ssl_;
# endif
ssl_ = nullptr;
}
return transport->write_cb_(buf, n, std::addressof(destination_), error);
}
#endif
/// io_channel
io_channel::io_channel(io_service& service, int index) : io_base(), service_(service)
@ -220,6 +209,15 @@ io_channel::io_channel(io_service& service, int index) : io_base(), service_(ser
index_ = index;
decode_len_ = [=](void* ptr, int len) { return this->__builtin_decode_len(ptr, len); };
}
#if defined(YASIO_SSL_BACKEND)
SSL_CTX* io_channel::get_ssl_context(bool client) const
{
if (client)
return service_.ssl_roles_[YSSL_CLIENT];
auto& ctx = service_.ssl_roles_[YSSL_SERVER];
return (ctx) ? ctx : service_.init_ssl_context(YSSL_SERVER);
}
#endif
const print_fn2_t& io_channel::__get_cprint() const { return get_service().options_.print_; }
std::string io_channel::format_destination() const
{
@ -251,7 +249,6 @@ void io_channel::join_multicast_group()
case AF_INET6:
socket_->set_optval(IPPROTO_IPV6, IP_MULTICAST_IF, multiif_.in6_.sin6_scope_id);
break;
default:;
}
int loopback = yasio__testbits(properties_, YCPF_MCAST_LOOPBACK) ? 1 : 0;
@ -340,14 +337,14 @@ io_transport::io_transport(io_channel* ctx, xxsocket_ptr&& s) : ctx_(ctx)
#endif
}
const print_fn2_t& io_transport::__get_cprint() const { return ctx_->get_service().options_.print_; }
int io_transport::write(dynamic_buffer_t&& buffer, completion_cb_t&& handler)
int io_transport::write(sbyte_buffer&& buffer, completion_cb_t&& handler)
{
int n = static_cast<int>(buffer.size());
send_queue_.emplace(cxx14::make_unique<io_send_op>(std::move(buffer), std::move(handler)));
get_service().interrupt();
return n;
}
int io_transport::do_read(int revent, int& error, highp_time_t&) { return revent ? this->call_read(buffer_ + offset_, sizeof(buffer_) - offset_, error) : 0; }
int io_transport::do_read(int revent, int& error, highp_time_t&) { return this->call_read(buffer_ + offset_, sizeof(buffer_) - offset_, revent, error); }
bool io_transport::do_write(highp_time_t& wait_duration)
{
bool ret = false;
@ -381,7 +378,7 @@ bool io_transport::do_write(highp_time_t& wait_duration)
}
}
else
wait_duration = yasio__min_wait_duration;
wait_duration = yasio__min_wait_usec;
}
if (no_wevent && pollout_registerred_)
{
@ -393,9 +390,9 @@ bool io_transport::do_write(highp_time_t& wait_duration)
return ret;
}
int io_transport::call_read(void* data, int size, int& error)
int io_transport::call_read(void* data, int size, int revent, int& error)
{
int n = read_cb_(data, size);
int n = read_cb_(data, size, revent, error);
if (n > 0)
{
ctx_->bytes_transferred_ += n;
@ -403,7 +400,6 @@ int io_transport::call_read(void* data, int size, int& error)
}
if (n < 0)
{
error = xxsocket::get_last_errno();
if (xxsocket::not_recv_error(error))
return (error = 0); // status ok, clear error
return n;
@ -417,7 +413,7 @@ int io_transport::call_read(void* data, int size, int& error)
}
int io_transport::call_write(io_send_op* op, int& error)
{
int n = op->perform(this, op->buffer_.data() + op->offset_, static_cast<int>(op->buffer_.size() - op->offset_));
int n = op->perform(this, op->buffer_.data() + op->offset_, static_cast<int>(op->buffer_.size() - op->offset_), error);
if (n > 0)
{
// #performance: change offset only, remain data will be send at next frame.
@ -427,7 +423,6 @@ int io_transport::call_write(io_send_op* op, int& error)
}
else if (n < 0)
{
error = xxsocket::get_last_errno();
if (xxsocket::not_send_error(error))
n = 0;
else if (yasio__testbits(ctx_->properties_, YCM_UDP))
@ -448,97 +443,81 @@ void io_transport::complete_op(io_send_op* op, int error)
}
void io_transport::set_primitives()
{
this->write_cb_ = [=](const void* data, int len, const ip::endpoint*) { return socket_->send(data, len); };
this->read_cb_ = [=](void* data, int len) { return socket_->recv(data, len, 0); };
this->write_cb_ = [=](const void* data, int len, const ip::endpoint*, int& error) {
int n = socket_->send(data, len);
if (n < 0)
error = xxsocket::get_last_errno();
return n;
};
this->read_cb_ = [=](void* data, int len, int revent, int& error) {
if (revent)
{
int n = socket_->recv(data, len);
if (n < 0)
error = xxsocket::get_last_errno();
return n;
}
error = EWOULDBLOCK;
return -1;
};
}
// -------------------- io_transport_tcp ---------------------
inline io_transport_tcp::io_transport_tcp(io_channel* ctx, xxsocket_ptr&& s) : io_transport(ctx, std::forward<xxsocket_ptr>(s)) {}
// ----------------------- io_transport_ssl ----------------
#if defined(YASIO_SSL_BACKEND)
io_transport_ssl::io_transport_ssl(io_channel* ctx, xxsocket_ptr&& s) : io_transport_tcp(ctx, std::forward<xxsocket_ptr>(s)), ssl_(std::move(ctx->ssl_)) {}
io_transport_ssl::io_transport_ssl(io_channel* ctx, xxsocket_ptr&& sock) : io_transport_tcp(ctx, std::forward<xxsocket_ptr>(sock))
{
this->state_ = io_base::state::CONNECTING; // for ssl, inital state shoud be connecing for ssl handshake
bool client = yasio__testbits(ctx->properties_, YCM_CLIENT);
this->ssl_ = yssl_new(ctx->get_ssl_context(client), static_cast<int>(this->socket_->native_handle()), ctx->remote_host_.c_str(), client);
}
int io_transport_ssl::do_ssl_handshake(int& error)
{
int ret = yssl_do_handshake(ssl_, error);
if (ret == 0) // handshake succeed
{ // because we invoke handshake in call_read, so we emit EWOULDBLOCK to mark ssl transport status `ok`
this->state_ = io_base::state::OPENED;
this->read_cb_ = [=](void* data, int len, int revent, int& error) {
if (revent)
return yssl_read(ssl_, data, len, error);
error = EWOULDBLOCK;
return -1;
};
this->write_cb_ = [=](const void* data, int len, const ip::endpoint*, int& error) { return yssl_write(ssl_, data, len, error); };
YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx_->index_, this->id_, this->local_endpoint().to_string().c_str(),
this->remote_endpoint().to_string().c_str());
get_service().fire_event(ctx_->index_, YEK_ON_OPEN, 0, this);
error = EWOULDBLOCK;
}
else
{
if (error == EWOULDBLOCK)
get_service().interrupt();
else
{ // handshake failed, print reason
char buf[256] = {0};
YASIO_KLOGE("[index: %d] do_ssl_handshake fail with %s", ctx_->index_, yssl_strerror(ssl_, ret, buf, sizeof(buf)));
if (yasio__testbits(ctx_->properties_, YCM_CLIENT))
{
YASIO_KLOGE("[index: %d] connect server %s failed, ec=%d, detail:%s", ctx_->index_, ctx_->format_destination().c_str(), error,
io_service::strerror(error));
get_service().fire_event(ctx_->index(), YEK_ON_OPEN, error, ctx_);
}
}
}
return -1;
}
void io_transport_ssl::do_ssl_shutdown()
{
if (ssl_)
yssl_shutdown(ssl_);
}
void io_transport_ssl::set_primitives()
{
this->read_cb_ = [=](void* data, int len) {
# if YASIO_SSL_BACKEND == 1
ERR_clear_error();
int n = ::SSL_read(ssl_, data, len);
if (n > 0)
return n;
int error = SSL_get_error(ssl_, n);
switch (error)
{
case SSL_ERROR_ZERO_RETURN: // n=0, the upper caller will regards as eof
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* The operation did not complete; the same TLS/SSL I/O function
should be called again later. This is basically an EWOULDBLOCK
equivalent. */
if (xxsocket::get_last_errno() != EWOULDBLOCK)
xxsocket::set_last_errno(EWOULDBLOCK);
break;
default:
xxsocket::set_last_errno(yasio::errc::ssl_read_failed);
}
return n;
# elif YASIO_SSL_BACKEND == 2
auto ssl = static_cast<SSL*>(ssl_);
int n = ::mbedtls_ssl_read(ssl, static_cast<uint8_t*>(data), len);
if (n > 0)
return n;
switch (n)
{
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: // n=0, the upper caller will regards as eof
n = 0;
case 0:
::mbedtls_ssl_close_notify(ssl);
break;
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
if (xxsocket::get_last_errno() != EWOULDBLOCK)
xxsocket::set_last_errno(EWOULDBLOCK);
break;
default:
xxsocket::set_last_errno(yasio::errc::ssl_read_failed);
}
return n;
# endif
};
this->write_cb_ = [=](const void* data, int len, const ip::endpoint*) {
# if YASIO_SSL_BACKEND == 1
ERR_clear_error();
int n = ::SSL_write(ssl_, data, len);
if (n > 0)
return n;
int error = SSL_get_error(ssl_, n);
switch (error)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
if (xxsocket::get_last_errno() != EWOULDBLOCK)
xxsocket::set_last_errno(EWOULDBLOCK);
break;
default:
xxsocket::set_last_errno(yasio::errc::ssl_write_failed);
}
# elif YASIO_SSL_BACKEND == 2
int n = ::mbedtls_ssl_write(static_cast<SSL*>(ssl_), static_cast<const uint8_t*>(data), len);
if (n > 0)
return n;
switch (n)
{
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
if (xxsocket::get_last_errno() != EWOULDBLOCK)
xxsocket::set_last_errno(EWOULDBLOCK);
break;
default:
xxsocket::set_last_errno(yasio::errc::ssl_write_failed);
}
# endif
return -1;
};
this->read_cb_ = [=](void* /*data*/, int /*len*/, int /*revent*/, int& error) { return do_ssl_handshake(error); };
}
#endif
// ----------------------- io_transport_udp ----------------
@ -593,11 +572,11 @@ void io_transport_udp::disconnect()
connected_ = false;
set_primitives();
}
int io_transport_udp::write(dynamic_buffer_t&& buffer, completion_cb_t&& handler)
int io_transport_udp::write(sbyte_buffer&& buffer, completion_cb_t&& handler)
{
return connected_ ? io_transport::write(std::move(buffer), std::move(handler)) : write_to(std::move(buffer), ensure_destination(), std::move(handler));
}
int io_transport_udp::write_to(dynamic_buffer_t&& buffer, const ip::endpoint& to, completion_cb_t&& handler)
int io_transport_udp::write_to(sbyte_buffer&& buffer, const ip::endpoint& to, completion_cb_t&& handler)
{
int n = static_cast<int>(buffer.size());
send_queue_.emplace(cxx14::make_unique<io_sendto_op>(std::move(buffer), std::move(handler), to));
@ -610,23 +589,30 @@ void io_transport_udp::set_primitives()
io_transport::set_primitives();
else
{
this->write_cb_ = [=](const void* data, int len, const ip::endpoint* destination) {
this->write_cb_ = [=](const void* data, int len, const ip::endpoint* destination, int& error) {
assert(destination);
int n = socket_->sendto(data, len, *destination);
if (n < 0)
{
auto error = xxsocket::get_last_errno();
error = xxsocket::get_last_errno();
if (!xxsocket::not_send_error(error))
YASIO_KLOGW("[index: %d] write udp socket failed, ec=%d, detail:%s", this->cindex(), error, io_service::strerror(error));
}
return n;
};
this->read_cb_ = [=](void* data, int len) {
ip::endpoint peer;
int n = socket_->recvfrom(data, len, peer);
if (n > 0)
this->peer_ = peer;
return n;
this->read_cb_ = [=](void* data, int len, int revent, int& error) {
if (revent)
{
ip::endpoint peer;
int n = socket_->recvfrom(data, len, peer);
if (n > 0)
this->peer_ = peer;
if (n < 0)
error = xxsocket::get_last_errno();
return n;
}
error = EWOULDBLOCK;
return -1;
};
}
}
@ -645,15 +631,18 @@ io_transport_kcp::io_transport_kcp(io_channel* ctx, xxsocket_ptr&& s) : io_trans
::ikcp_nodelay(this->kcp_, 1, 5000 /*kcp max interval is 5000(ms)*/, 2, 1);
::ikcp_setoutput(this->kcp_, [](const char* buf, int len, ::ikcpcb* /*kcp*/, void* user) {
auto t = (io_transport_kcp*)user;
if (yasio__min_wait_duration == 0)
return t->write_cb_(buf, len, std::addressof(t->ensure_destination()));
if (yasio__min_wait_usec == 0)
{
int ignored_ec = 0;
return t->write_cb_(buf, len, std::addressof(t->ensure_destination()), ignored_ec);
}
// Enqueue to transport queue
return t->io_transport_udp::write(dynamic_buffer_t{buf, buf + len}, nullptr);
return t->io_transport_udp::write(sbyte_buffer{buf, buf + len}, nullptr);
});
}
io_transport_kcp::~io_transport_kcp() { ::ikcp_release(this->kcp_); }
int io_transport_kcp::write(dynamic_buffer_t&& buffer, completion_cb_t&& /*handler*/)
int io_transport_kcp::write(sbyte_buffer&& buffer, completion_cb_t&& /*handler*/)
{
std::lock_guard<std::recursive_mutex> lck(send_mtx_);
int len = static_cast<int>(buffer.size());
@ -663,14 +652,14 @@ int io_transport_kcp::write(dynamic_buffer_t&& buffer, completion_cb_t&& /*handl
}
int io_transport_kcp::do_read(int revent, int& error, highp_time_t& wait_duration)
{
int n = revent ? this->call_read(&rawbuf_.front(), static_cast<int>(rawbuf_.size()), error) : 0;
int n = this->call_read(&rawbuf_.front(), static_cast<int>(rawbuf_.size()), revent, error);
if (n > 0)
this->handle_input(rawbuf_.data(), n, error, wait_duration);
if (!error)
{ // !important, should always try to call ikcp_recv when no error occured.
n = ::ikcp_recv(kcp_, buffer_ + offset_, sizeof(buffer_) - offset_);
if (n > 0) // If got data from kcp, don't wait
wait_duration = yasio__min_wait_duration;
wait_duration = yasio__min_wait_usec;
else if (n < 0)
n = 0; // EAGAIN/EWOULDBLOCK
}
@ -696,7 +685,7 @@ bool io_transport_kcp::do_write(highp_time_t& wait_duration)
::ikcp_update(kcp_, static_cast<IUINT32>(::yasio::clock()));
::ikcp_flush(kcp_);
this->check_timeout(wait_duration); // call ikcp_check
if (yasio__min_wait_duration == 0)
if (yasio__min_wait_usec == 0)
return true;
// Call super do_write to perform low layer socket.send
// benefit of transport queue:
@ -710,7 +699,7 @@ void io_transport_kcp::check_timeout(highp_time_t& wait_duration) const
auto expire_time = ::ikcp_check(kcp_, current);
highp_time_t duration = static_cast<highp_time_t>(expire_time - current) * std::milli::den;
if (duration < 0)
duration = yasio__min_wait_duration;
duration = yasio__min_wait_usec;
if (wait_duration > duration)
wait_duration = duration;
}
@ -806,6 +795,12 @@ void io_service::handle_stop()
}
void io_service::initialize(const io_hostent* channel_eps, int channel_count)
{
#if defined(YASIO_SSL_BACKEND)
ssl_roles_[YSSL_CLIENT] = ssl_roles_[YSSL_SERVER] = nullptr;
#endif
this->wait_duration_ = yasio__max_wait_usec;
// at least one channel
if (channel_count < 1)
channel_count = 1;
@ -884,12 +879,11 @@ void io_service::run()
yasio::set_thread_name("yasio");
#if defined(YASIO_SSL_BACKEND)
init_ssl_context();
init_ssl_context(YSSL_CLIENT); // by default, init ssl client context
#endif
#if defined(YASIO_HAVE_CARES)
recreate_ares_channel();
ares_socket_t ares_socks[ARES_GETSOCK_MAXNUM] = {0};
int ares_socks_count = 0;
#endif
// Call once at startup
@ -897,24 +891,30 @@ void io_service::run()
// The core event loop
fd_set_adapter fd_set; // The temp file descriptor set
this->wait_duration_ = YASIO_MAX_WAIT_DURATION;
do
{
auto wait_duration = get_timeout(this->wait_duration_); // Gets current wait duration
this->wait_duration_ = YASIO_MAX_WAIT_DURATION; // Reset next wait duration
if (wait_duration > 0)
{
fd_set = this->fd_set_;
timeval waitd_tv = {(decltype(timeval::tv_sec))(wait_duration / 1000000), (decltype(timeval::tv_usec))(wait_duration % 1000000)};
this->wait_duration_ = yasio__max_wait_usec; // Reset next wait duration
fd_set = this->fd_set_;
timeval waitd_tv = {(decltype(timeval::tv_sec))(wait_duration / 1000000), (decltype(timeval::tv_usec))(wait_duration % 1000000)};
#if defined(YASIO_HAVE_CARES)
if (ares_outstanding_work_)
{
ares_socks_count = register_ares_fds(ares_socks, fd_set);
::ares_timeout(this->ares_, &waitd_tv, &waitd_tv);
}
/**
* retrieves the set of file descriptors which the calling application should poll io,
* after poll_io, for ares invoke flow, refer to:
* https://c-ares.org/ares_fds.html
* https://c-ares.org/ares_timeout.html
* https://c-ares.org/ares_process_fd.html
*/
auto ares_nfds = do_ares_fds(ares_socks, fd_set, waitd_tv);
#endif
YASIO_KLOGV("[core] poll_io max_nfds=%d, waiting... %ld milliseconds", fd_set.max_descriptor(), waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000);
int retval = fd_set.poll_io(waitd_tv);
const int waitd_ms = static_cast<int>(waitd_tv.tv_sec * 1000 + waitd_tv.tv_usec / 1000);
if (waitd_ms > 0)
{
YASIO_KLOGV("[core] poll_io max_nfds=%d, waiting... %ld milliseconds", fd_set.max_descriptor(), waitd_ms);
int retval = fd_set.poll_io(waitd_ms);
YASIO_KLOGV("[core] poll_io waked up, retval=%d", retval);
if (retval < 0)
{
@ -936,8 +936,8 @@ void io_service::run()
}
#if defined(YASIO_HAVE_CARES)
// process possible async resolve requests.
process_ares_requests(ares_socks, ares_socks_count, fd_set);
// process events for name resolution.
do_ares_process_fds(ares_socks, ares_nfds, fd_set);
#endif
// process active transports
@ -954,7 +954,8 @@ void io_service::run()
destroy_ares_channel();
#endif
#if defined(YASIO_SSL_BACKEND)
cleanup_ssl_context();
cleanup_ssl_context(YSSL_CLIENT);
cleanup_ssl_context(YSSL_SERVER);
#endif
this->state_ = io_service::state::AT_EXITING;
@ -974,7 +975,8 @@ void io_service::process_transports(fd_set_adapter& fd_set)
++iter;
continue;
}
shutdown_internal(transport);
if (transport->error_ == 0)
transport->error_ = yasio::errc::shutdown_by_localhost;
}
handle_close(transport);
@ -1081,15 +1083,25 @@ bool io_service::open(size_t index, int kind)
io_channel* io_service::channel_at(size_t index) const { return (index < channels_.size()) ? channels_[index] : nullptr; }
void io_service::handle_close(transport_handle_t thandle)
{
auto ctx = thandle->ctx_;
auto ec = thandle->error_;
// @Because we can't retrive peer endpoint when connect reset by peer, so use id to trace.
YASIO_KLOGD("[index: %d] the connection #%u is lost, ec=%d, where=%d, detail:%s", ctx->index_, thandle->id_, ec, (int)thandle->error_stage_,
io_service::strerror(ec));
this->fire_event(thandle->cindex(), YEK_ON_CLOSE, ec, thandle);
auto ctx = thandle->ctx_;
auto error = thandle->error_;
const bool client = yasio__testbits(ctx->properties_, YCM_CLIENT);
if (thandle->state_ == io_base::state::OPENED)
{ // @Because we can't retrive peer endpoint when connect reset by peer, so use id to trace.
YASIO_KLOGD("[index: %d] the connection #%u is lost, ec=%d, where=%d, detail:%s", ctx->index_, thandle->id_, error, (int)thandle->error_stage_,
io_service::strerror(error));
this->fire_event(ctx->index(), YEK_ON_CLOSE, error, thandle);
}
#if defined(YASIO_SSL_BACKEND)
if (yasio__testbits(ctx->properties_, YCM_SSL))
static_cast<io_transport_ssl*>(thandle)->do_ssl_shutdown();
#endif
if (yasio__testbits(ctx->properties_, YCM_TCP) && error == yasio::errc::shutdown_by_localhost)
thandle->socket_->shutdown();
cleanup_io(thandle);
deallocate_transport(thandle);
if (yasio__testbits(ctx->properties_, YCM_CLIENT))
if (client)
{
yasio__clearbits(ctx->opmask_, YOPM_CLOSE);
cleanup_channel(ctx, false);
@ -1098,7 +1110,7 @@ void io_service::handle_close(transport_handle_t thandle)
void io_service::register_descriptor(const socket_native_type fd, int events) { this->fd_set_.set(fd, events); }
void io_service::deregister_descriptor(const socket_native_type fd, int events) { this->fd_set_.unset(fd, events); }
int io_service::write(transport_handle_t transport, dynamic_buffer_t buffer, completion_cb_t handler)
int io_service::write(transport_handle_t transport, sbyte_buffer buffer, completion_cb_t handler)
{
if (transport && transport->is_open())
return !buffer.empty() ? transport->write(std::move(buffer), std::move(handler)) : 0;
@ -1108,7 +1120,7 @@ int io_service::write(transport_handle_t transport, dynamic_buffer_t buffer, com
return -1;
}
}
int io_service::write_to(transport_handle_t transport, dynamic_buffer_t buffer, const ip::endpoint& to, completion_cb_t handler)
int io_service::write_to(transport_handle_t transport, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t handler)
{
if (transport && transport->is_open())
return !buffer.empty() ? transport->write_to(std::move(buffer), to, std::move(handler)) : 0;
@ -1189,10 +1201,9 @@ void io_service::do_connect_completion(io_channel* ctx, fd_set_adapter& fd_set)
assert(ctx->state_ == io_base::state::CONNECTING);
if (ctx->state_ == io_base::state::CONNECTING)
{
int error = -1;
#if !defined(YASIO_SSL_BACKEND)
if (fd_set.is_set(ctx->socket_->native_handle(), socket_event::readwrite))
{
int error = -1;
if (ctx->socket_->get_optval(SOL_SOCKET, SO_ERROR, error) >= 0 && error == 0)
{
// The nonblocking tcp handshake complete, remove write event avoid high-CPU occupation
@ -1203,188 +1214,21 @@ void io_service::do_connect_completion(io_channel* ctx, fd_set_adapter& fd_set)
handle_connect_failed(ctx, error);
ctx->timer_.cancel(*this);
}
#else
if (!yasio__testbits(ctx->properties_, YCPF_SSL_HANDSHAKING))
{
if (fd_set.is_set(ctx->socket_->native_handle(), socket_event::readwrite))
{
if (ctx->socket_->get_optval(SOL_SOCKET, SO_ERROR, error) >= 0 && error == 0)
{
// The nonblocking tcp handshake complete, remove write event avoid high-CPU occupation
deregister_descriptor(ctx->socket_->native_handle(), socket_event::write);
if (!yasio__testbits(ctx->properties_, YCM_SSL))
handle_connect_succeed(ctx, ctx->socket_);
else
do_ssl_handshake(ctx);
}
else
handle_connect_failed(ctx, error);
}
}
else
do_ssl_handshake(ctx);
if (ctx->state_ != io_base::state::CONNECTING)
ctx->timer_.cancel(*this);
#endif
}
}
#if defined(YASIO_SSL_BACKEND)
void io_service::init_ssl_context()
SSL_CTX* io_service::init_ssl_context(ssl_role role)
{
# if YASIO_SSL_BACKEND == 1
# if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
auto req_method = ::TLS_client_method();
# else
auto req_method = ::SSLv23_client_method();
# endif
ssl_ctx_ = ::SSL_CTX_new(req_method);
# if defined(SSL_MODE_RELEASE_BUFFERS)
::SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_RELEASE_BUFFERS);
# endif
::SSL_CTX_set_mode(ssl_ctx_, SSL_MODE_ENABLE_PARTIAL_WRITE);
if (!this->options_.cafile_.empty())
{
if (::SSL_CTX_load_verify_locations(ssl_ctx_, this->options_.cafile_.c_str(), nullptr) == 1)
{
::SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER, ::SSL_CTX_get_verify_callback(ssl_ctx_));
# if OPENSSL_VERSION_NUMBER >= 0x10101000L
::SSL_CTX_set_post_handshake_auth(ssl_ctx_, 1);
# endif
# if defined(X509_V_FLAG_PARTIAL_CHAIN)
/* Have intermediate certificates in the trust store be treated as
trust-anchors, in the same way as self-signed root CA certificates
are. This allows users to verify servers using the intermediate cert
only, instead of needing the whole chain. */
X509_STORE_set_flags(SSL_CTX_get_cert_store(ssl_ctx_), X509_V_FLAG_PARTIAL_CHAIN);
# endif
}
else
YASIO_KLOGE("[global] load ca certifaction file failed!");
}
else
SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_NONE, nullptr);
# elif YASIO_SSL_BACKEND == 2
ssl_ctx_ = new SSL_CTX();
::mbedtls_ssl_config_init(&ssl_ctx_->conf);
::mbedtls_x509_crt_init(&ssl_ctx_->cacert);
::mbedtls_ctr_drbg_init(&ssl_ctx_->ctr_drbg);
::mbedtls_entropy_init(&ssl_ctx_->entropy);
cxx17::string_view pers{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN};
int ret = ::mbedtls_ctr_drbg_seed(&ssl_ctx_->ctr_drbg, ::mbedtls_entropy_func, &ssl_ctx_->entropy, (const unsigned char*)pers.data(), pers.length());
if (ret != 0)
YASIO_KLOGE("mbedtls_ctr_drbg_seed fail with ret=%d", ret);
int authmode = MBEDTLS_SSL_VERIFY_OPTIONAL;
if (!this->options_.cafile_.empty()) // the cafile_ must be full path
{
if ((ret = ::mbedtls_x509_crt_parse_file(&ssl_ctx_->cacert, this->options_.cafile_.c_str())) == 0)
authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
else
YASIO_KLOGE("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
}
if ((ret = ::mbedtls_ssl_config_defaults(&ssl_ctx_->conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
YASIO_KLOGE("mbedtls_ssl_config_defaults fail with ret=%d", ret);
::mbedtls_ssl_conf_authmode(&ssl_ctx_->conf, authmode);
::mbedtls_ssl_conf_ca_chain(&ssl_ctx_->conf, &ssl_ctx_->cacert, nullptr);
::mbedtls_ssl_conf_rng(&ssl_ctx_->conf, ::mbedtls_ctr_drbg_random, &ssl_ctx_->ctr_drbg);
# endif
auto ctx = role == YSSL_CLIENT ? yssl_ctx_new(yssl_options{options_.cafile_.c_str(), nullptr, true})
: yssl_ctx_new(yssl_options{options_.crtfile_.c_str(), options_.keyfile_.c_str(), false});
ssl_roles_[role] = ctx;
return ctx;
}
void io_service::cleanup_ssl_context()
void io_service::cleanup_ssl_context(ssl_role role)
{
if (ssl_ctx_)
{
# if YASIO_SSL_BACKEND == 1
SSL_CTX_free((SSL_CTX*)ssl_ctx_);
# elif YASIO_SSL_BACKEND == 2
::mbedtls_x509_crt_free(&ssl_ctx_->cacert);
::mbedtls_ssl_config_free(&ssl_ctx_->conf);
::mbedtls_ctr_drbg_free(&ssl_ctx_->ctr_drbg);
::mbedtls_entropy_free(&ssl_ctx_->entropy);
delete ssl_ctx_;
# endif
ssl_ctx_ = nullptr;
}
}
void io_service::do_ssl_handshake(io_channel* ctx)
{
if (!ctx->ssl_)
{
# if YASIO_SSL_BACKEND == 1
auto ssl = ::SSL_new(ssl_ctx_);
::SSL_set_fd(ssl, static_cast<int>(ctx->socket_->native_handle()));
::SSL_set_connect_state(ssl);
::SSL_set_tlsext_host_name(ssl, ctx->remote_host_.c_str());
# elif YASIO_SSL_BACKEND == 2
auto ssl = ::mbedtls_ssl_new(ssl_ctx_);
::mbedtls_ssl_set_fd(ssl, static_cast<int>(ctx->socket_->native_handle()));
::mbedtls_ssl_set_hostname(ssl, ctx->remote_host_.c_str());
# endif
yasio__setbits(ctx->properties_, YCPF_SSL_HANDSHAKING); // start ssl handshake
ctx->ssl_.reset(ssl);
}
# if YASIO_SSL_BACKEND == 1
int ret = ::SSL_do_handshake(ctx->ssl_);
if (ret != 1)
{
int status = ::SSL_get_error(ctx->ssl_, ret);
/*
When using a non-blocking socket, nothing is to be done, but select() can be used to check for
the required condition: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html
*/
if (status == SSL_ERROR_WANT_READ || status == SSL_ERROR_WANT_WRITE)
return;
# if defined(SSL_ERROR_WANT_ASYNC)
if (status == SSL_ERROR_WANT_ASYNC)
return;
# endif
int error = static_cast<int>(ERR_get_error());
if (error)
{
char errstring[256] = {0};
ERR_error_string_n(error, errstring, sizeof(errstring));
YASIO_KLOGE("[index: %d] SSL_do_handshake fail with ret=%d,error=%X, detail:%s", ctx->index_, ret, error, errstring);
}
else
{
error = xxsocket::get_last_errno();
YASIO_KLOGE("[index: %d] SSL_do_handshake fail with ret=%d,status=%d, error=%d, detail:%s", ctx->index_, ret, status, error, xxsocket::strerror(error));
}
ctx->ssl_.destroy();
handle_connect_failed(ctx, yasio::errc::ssl_handshake_failed);
}
else
handle_connect_succeed(ctx, ctx->socket_);
# elif YASIO_SSL_BACKEND == 2
auto ssl = static_cast<SSL*>(ctx->ssl_);
int ret = ::mbedtls_ssl_handshake_step(ssl);
if (ret == 0)
{
if (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER)
interrupt();
else // mbedtls_ssl_get_verify_result return 0 when valid cacert provided
handle_connect_succeed(ctx, ctx->socket_);
}
else
{
char errstring[256] = {0};
switch (ret)
{
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
break; // Nothing need to do
default:
::mbedtls_strerror(ret, errstring, sizeof(errstring));
YASIO_KLOGE("[index: %d] mbedtls_ssl_handshake_step fail with ret=%d, detail:%s", ctx->index_, ret, errstring);
ctx->ssl_.destroy();
handle_connect_failed(ctx, yasio::errc::ssl_handshake_failed);
}
}
# endif
auto& ctx = ssl_roles_[role];
if (ctx)
yssl_ctx_free(ctx);
}
#endif
#if defined(YASIO_HAVE_CARES)
@ -1425,33 +1269,39 @@ void io_service::ares_getaddrinfo_cb(void* arg, int status, int /*timeouts*/, ar
}
current_service.interrupt();
}
int io_service::register_ares_fds(socket_native_type* ares_socks, fd_set_adapter& fd_set)
int io_service::do_ares_fds(socket_native_type* socks, fd_set_adapter& fd_set, timeval& waitd_tv)
{
int count = 0;
int bitmask = ::ares_getsock(this->ares_, ares_socks, ARES_GETSOCK_MAXNUM);
for (int i = 0; i < ARES_GETSOCK_MAXNUM; ++i)
int nfds = 0;
if (ares_outstanding_work_)
{
if (ARES_GETSOCK_READABLE(bitmask, i) || ARES_GETSOCK_WRITABLE(bitmask, i))
int bitmask = ::ares_getsock(this->ares_, socks, ARES_GETSOCK_MAXNUM);
for (int i = 0; i < ARES_GETSOCK_MAXNUM; ++i)
{
auto fd = ares_socks[i];
++count;
fd_set.set(fd, socket_event::readwrite);
int events = socket_event::null;
if (ARES_GETSOCK_READABLE(bitmask, i))
events |= socket_event::read;
if (ARES_GETSOCK_WRITABLE(bitmask, i))
events |= socket_event::write;
if (events)
{
++nfds;
fd_set.set(socks[i], events);
}
else
break;
}
else
break;
if (nfds)
::ares_timeout(this->ares_, &waitd_tv, &waitd_tv);
}
return count;
return nfds;
}
void io_service::process_ares_requests(socket_native_type* socks, int count, fd_set_adapter& fd_set)
void io_service::do_ares_process_fds(socket_native_type* socks, int nfds, fd_set_adapter& fd_set)
{
if (this->ares_outstanding_work_ > 0)
for (auto i = 0; i < nfds; ++i)
{
for (auto i = 0; i < count; ++i)
{
auto fd = socks[i];
::ares_process_fd(this->ares_, fd_set.is_set(fd, socket_event::read) ? fd : ARES_SOCKET_BAD,
fd_set.is_set(fd, socket_event::write) ? fd : ARES_SOCKET_BAD);
}
auto fd = socks[i];
::ares_process_fd(this->ares_, fd_set.is_set(fd, socket_event::read) ? fd : ARES_SOCKET_BAD, fd_set.is_set(fd, socket_event::write) ? fd : ARES_SOCKET_BAD);
}
}
void io_service::recreate_ares_channel()
@ -1668,7 +1518,7 @@ transport_handle_t io_service::do_dgram_accept(io_channel* ctx, const ip::endpoi
// We always establish 4 tuple with clients
transport->confgure_remote(peer);
if (user_route)
notify_connect_succeed(transport);
active_transport(transport);
else
handle_connect_succeed(transport);
return transport;
@ -1705,18 +1555,23 @@ void io_service::handle_connect_succeed(transport_handle_t transport)
connection->set_keepalive(options_.tcp_keepalive_.onoff, options_.tcp_keepalive_.idle, options_.tcp_keepalive_.interval, options_.tcp_keepalive_.probs);
}
notify_connect_succeed(transport);
active_transport(transport);
}
void io_service::notify_connect_succeed(transport_handle_t t)
void io_service::active_transport(transport_handle_t t)
{
auto ctx = t->ctx_;
auto& s = t->socket_;
this->transports_.push_back(t);
YASIO__UNUSED_PARAM(s);
YASIO_KLOGV("[index: %d] sndbuf=%d, rcvbuf=%d", ctx->index_, s->get_optval<int>(SOL_SOCKET, SO_SNDBUF), s->get_optval<int>(SOL_SOCKET, SO_RCVBUF));
YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx->index_, t->id_, t->local_endpoint().to_string().c_str(),
t->remote_endpoint().to_string().c_str());
this->fire_event(ctx->index_, YEK_ON_OPEN, 0, t);
if (!yasio__testbits(ctx->properties_, YCM_SSL))
{
YASIO__UNUSED_PARAM(s);
YASIO_KLOGV("[index: %d] sndbuf=%d, rcvbuf=%d", ctx->index_, s->get_optval<int>(SOL_SOCKET, SO_SNDBUF), s->get_optval<int>(SOL_SOCKET, SO_RCVBUF));
YASIO_KLOGD("[index: %d] the connection #%u <%s> --> <%s> is established.", ctx->index_, t->id_, t->local_endpoint().to_string().c_str(),
t->remote_endpoint().to_string().c_str());
this->fire_event(ctx->index_, YEK_ON_OPEN, 0, t);
}
else if (yasio__testbits(ctx->properties_, YCM_CLIENT))
this->interrupt();
}
transport_handle_t io_service::allocate_transport(io_channel* ctx, xxsocket_ptr&& s)
{
@ -1836,7 +1691,7 @@ void io_service::unpack(transport_handle_t transport, int bytes_expected, int by
if (offset > 0)
{ /* move remain data to head of buffer and hold 'offset'. */
::memmove(transport->buffer_, transport->buffer_ + bytes_expected, offset);
this->wait_duration_ = yasio__min_wait_duration;
this->wait_duration_ = yasio__min_wait_usec;
}
// move properly pdu to ready queue, the other thread who care about will retrieve it.
YASIO_KLOGV("[index: %d] received a properly packet from peer, packet size:%d", transport->cindex(), transport->expected_size_);
@ -1909,13 +1764,6 @@ bool io_service::open_internal(io_channel* ctx)
this->interrupt();
return true;
}
void io_service::shutdown_internal(transport_handle_t transport)
{
if (transport->error_ == 0)
transport->error_ = yasio::errc::shutdown_by_localhost;
if (yasio__testbits(transport->ctx_->properties_, YCM_TCP))
transport->socket_->shutdown();
}
bool io_service::close_internal(io_channel* ctx)
{
yasio__clearbits(ctx->opmask_, YOPM_OPEN);
@ -1973,9 +1821,6 @@ highp_time_t io_service::get_timeout(highp_time_t usec)
}
bool io_service::cleanup_channel(io_channel* ctx, bool clear_mask)
{
#if YASIO_SSL_BACKEND != 0
ctx->ssl_.destroy();
#endif
ctx->clear_mutable_flags();
bool bret = cleanup_io(ctx, clear_mask);
#if defined(YAISO_ENABLE_PASSIVE_EVENT)
@ -2238,9 +2083,21 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do
options_.dns_dirty_ = true;
break;
#endif
case YOPT_S_EVENT_CB:
options_.on_event_ = *va_arg(ap, event_cb_t*);
break;
case YOPT_S_DEFER_EVENT_CB:
options_.on_defer_event_ = *va_arg(ap, defer_event_cb_t*);
break;
case YOPT_S_FORWARD_EVENT:
options_.forward_event_ = !!va_arg(ap, int);
break;
#if defined(YASIO_SSL_BACKEND)
case YOPT_S_SSL_CERT:
options_.crtfile_ = va_arg(ap, const char*);
options_.keyfile_ = va_arg(ap, const char*);
break;
#endif
case YOPT_C_UNPACK_PARAMS: {
auto channel = channel_at(static_cast<size_t>(va_arg(ap, int)));
if (channel)
@ -2264,12 +2121,6 @@ void io_service::set_option_internal(int opt, va_list ap) // lgtm [cpp/poorly-do
channel->uparams_.no_bswap = va_arg(ap, int);
break;
}
case YOPT_S_EVENT_CB:
options_.on_event_ = *va_arg(ap, event_cb_t*);
break;
case YOPT_S_DEFER_EVENT_CB:
options_.on_defer_event_ = *va_arg(ap, defer_event_cb_t*);
break;
case YOPT_C_LFBFD_FN: {
auto channel = channel_at(static_cast<size_t>(va_arg(ap, int)));
if (channel)

View File

@ -49,12 +49,12 @@ SOFTWARE.
#include "yasio/detail/errc.hpp"
#include "yasio/detail/byte_buffer.hpp"
#include "yasio/detail/fd_set_adapter.hpp"
#include "yasio/cxx17/memory.hpp"
#include "yasio/cxx17/string_view.hpp"
#include "yasio/stl/memory.hpp"
#include "yasio/stl/string_view.hpp"
#include "yasio/xxsocket.hpp"
#if !defined(YASIO_HAVE_CARES)
# include "yasio/cxx17/shared_mutex.hpp"
# include "yasio/stl/shared_mutex.hpp"
#endif
#if defined(YASIO_HAVE_KCP)
@ -180,6 +180,12 @@ enum
// when forward event enabled, the option YOPT_S_DEFERRED_EVENT was ignored
YOPT_S_FORWARD_EVENT,
// Set ssl server cert and private key file
// params:
// crtfile: const char*
// keyfile: const char*
YOPT_S_SSL_CERT,
// Sets channel length field based frame decode function, native C++ ONLY
// params: index:int, func:decode_len_fn_t*
YOPT_C_UNPACK_FN = 101,
@ -291,7 +297,8 @@ enum
YCK_UDP_SERVER = YCM_UDP | YCM_SERVER,
YCK_KCP_CLIENT = YCM_KCP | YCM_CLIENT | YCM_UDP,
YCK_KCP_SERVER = YCM_KCP | YCM_SERVER | YCM_UDP,
YCK_SSL_CLIENT = YCM_SSL | YCM_CLIENT | YCM_TCP,
YCK_SSL_CLIENT = YCK_TCP_CLIENT | YCM_SSL,
YCK_SSL_SERVER = YCK_TCP_SERVER | YCM_SSL,
};
// channel flags
@ -368,14 +375,17 @@ typedef highp_timer_ptr deadline_timer_ptr;
typedef event_cb_t io_event_cb_t;
typedef completion_cb_t io_completion_cb_t;
typedef sbyte_buffer dynamic_buffer_t;
inline dynamic_buffer_t make_dynamic_buffer(const void* p, size_t n) { return dynamic_buffer_t{(const char*)p, (const char*)p + n, std::true_type{}}; }
// the ssl role
enum ssl_role
{
YSSL_CLIENT,
YSSL_SERVER,
};
struct io_hostent {
io_hostent() = default;
io_hostent(cxx17::string_view ip, u_short port) : host_(cxx17::svtos(ip)), port_(port) {}
io_hostent(io_hostent&& rhs) : host_(std::move(rhs.host_)), port_(rhs.port_) {}
io_hostent(io_hostent&& rhs) YASIO__NOEXCEPT : host_(std::move(rhs.host_)), port_(rhs.port_) {}
io_hostent(const io_hostent& rhs) : host_(rhs.host_), port_(rhs.port_) {}
void set_ip(cxx17::string_view ip) { cxx17::assign(host_, ip); }
const std::string& get_ip() const { return host_; }
@ -483,37 +493,6 @@ public:
unsigned int id() const { return id_; }
};
#if defined(YASIO_SSL_BACKEND)
class ssl_auto_handle {
public:
ssl_auto_handle() : ssl_(nullptr) {}
~ssl_auto_handle() { destroy(); }
ssl_auto_handle(ssl_auto_handle&& rhs) : ssl_(rhs.release()) {}
ssl_auto_handle& operator=(ssl_auto_handle&& rhs)
{
this->reset(rhs.release());
return *this;
}
SSL* release()
{
auto tmp = ssl_;
ssl_ = nullptr;
return tmp;
}
void reset(SSL* ssl)
{
if (ssl_)
destroy();
ssl_ = ssl;
}
operator SSL*() { return ssl_; }
YASIO__DECL void destroy();
protected:
SSL* ssl_ = nullptr;
};
#endif
class YASIO_API io_channel : public io_base {
friend class io_service;
friend class io_transport;
@ -524,6 +503,9 @@ class YASIO_API io_channel : public io_base {
public:
io_service& get_service() const { return service_; }
#if defined(YASIO_SSL_BACKEND)
YASIO__DECL SSL_CTX* get_ssl_context(bool client) const;
#endif
int index() const { return index_; }
u_short remote_port() const { return remote_port_; }
YASIO__DECL std::string format_destination() const;
@ -626,7 +608,7 @@ private:
ip::endpoint multiaddr_, multiif_;
// Current it's only for UDP
dynamic_buffer_t buffer_;
sbyte_buffer buffer_;
// The bytes transferred from socket low layer, currently, only works for client channel
long long bytes_transferred_ = 0;
@ -636,23 +618,19 @@ private:
#if defined(YASIO_HAVE_KCP)
int kcp_conv_ = 0;
#endif
#if defined(YASIO_SSL_BACKEND)
ssl_auto_handle ssl_;
#endif
};
// for tcp transport only
class YASIO_API io_send_op {
public:
io_send_op(dynamic_buffer_t&& buffer, completion_cb_t&& handler) : offset_(0), buffer_(std::move(buffer)), handler_(std::move(handler)) {}
io_send_op(sbyte_buffer&& buffer, completion_cb_t&& handler) : offset_(0), buffer_(std::move(buffer)), handler_(std::move(handler)) {}
virtual ~io_send_op() {}
size_t offset_; // read pos from sending buffer
dynamic_buffer_t buffer_; // sending data buffer
size_t offset_; // read pos from sending buffer
sbyte_buffer buffer_; // sending data buffer
completion_cb_t handler_;
YASIO__DECL virtual int perform(transport_handle_t transport, const void* buf, int n);
YASIO__DECL virtual int perform(transport_handle_t transport, const void* buf, int n, int& error);
#if !defined(YASIO_DISABLE_OBJECT_POOL)
DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_send_op, 512)
@ -662,11 +640,11 @@ public:
// for udp transport only
class YASIO_API io_sendto_op : public io_send_op {
public:
io_sendto_op(dynamic_buffer_t&& buffer, completion_cb_t&& handler, const ip::endpoint& destination)
io_sendto_op(sbyte_buffer&& buffer, completion_cb_t&& handler, const ip::endpoint& destination)
: io_send_op(std::move(buffer), std::move(handler)), destination_(destination)
{}
YASIO__DECL int perform(transport_handle_t transport, const void* buf, int n) override;
YASIO__DECL int perform(transport_handle_t transport, const void* buf, int n, int& error) override;
#if !defined(YASIO_DISABLE_OBJECT_POOL)
DEFINE_CONCURRENT_OBJECT_POOL_ALLOCATION(io_sendto_op, 512)
#endif
@ -698,7 +676,7 @@ public:
protected:
io_service& get_service() const { return ctx_->get_service(); }
bool is_open() const { return state_ == state::OPENED && socket_ && socket_->is_open(); }
dynamic_buffer_t fetch_packet()
sbyte_buffer fetch_packet()
{
expected_size_ = -1;
return std::move(expected_packet_);
@ -708,16 +686,16 @@ protected:
YASIO__DECL const print_fn2_t& __get_cprint() const;
// Call at user thread
YASIO__DECL virtual int write(dynamic_buffer_t&&, completion_cb_t&&);
YASIO__DECL virtual int write(sbyte_buffer&&, completion_cb_t&&);
// Call at user thread
virtual int write_to(dynamic_buffer_t&&, const ip::endpoint&, completion_cb_t&&)
virtual int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&)
{
YASIO_LOG("[warning] io_transport doesn't support 'write_to' operation!");
return 0;
}
YASIO__DECL int call_read(void* data, int size, int& error);
YASIO__DECL int call_read(void* data, int size, int revent, int& error);
YASIO__DECL int call_write(io_send_op*, int& error);
YASIO__DECL void complete_op(io_send_op*, int error);
@ -738,12 +716,12 @@ protected:
int offset_ = 0; // recv buffer offset
int expected_size_ = -1;
dynamic_buffer_t expected_packet_;
sbyte_buffer expected_packet_;
io_channel* ctx_;
std::function<int(const void*, int, const ip::endpoint*)> write_cb_;
std::function<int(void*, int)> read_cb_;
std::function<int(const void*, int, const ip::endpoint*, int&)> write_cb_;
std::function<int(void*, int, int, int&)> read_cb_;
privacy::concurrent_queue<send_op_ptr> send_queue_;
};
@ -760,10 +738,11 @@ public:
YASIO__DECL io_transport_ssl(io_channel* ctx, xxsocket_ptr&& s);
YASIO__DECL void set_primitives() override;
# if defined(YASIO_SSL_BACKEND)
YASIO__DECL void do_ssl_shutdown();
protected:
ssl_auto_handle ssl_;
# endif
YASIO__DECL int do_ssl_handshake(int& error); // always invoke at do_read
SSL* ssl_ = nullptr;
};
#else
class io_transport_ssl {};
@ -781,8 +760,8 @@ protected:
YASIO__DECL void connect();
YASIO__DECL void disconnect();
YASIO__DECL int write(dynamic_buffer_t&&, completion_cb_t&&) override;
YASIO__DECL int write_to(dynamic_buffer_t&&, const ip::endpoint&, completion_cb_t&&) override;
YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override;
YASIO__DECL int write_to(sbyte_buffer&&, const ip::endpoint&, completion_cb_t&&) override;
YASIO__DECL void set_primitives() override;
@ -807,7 +786,7 @@ public:
ikcpcb* internal_object() { return kcp_; }
protected:
YASIO__DECL int write(dynamic_buffer_t&&, completion_cb_t&&) override;
YASIO__DECL int write(sbyte_buffer&&, completion_cb_t&&) override;
YASIO__DECL int do_read(int revent, int& error, highp_time_t& wait_duration) override;
YASIO__DECL bool do_write(highp_time_t& wait_duration) override;
@ -816,7 +795,7 @@ protected:
YASIO__DECL void check_timeout(highp_time_t& wait_duration) const;
dynamic_buffer_t rawbuf_; // the low level raw buffer
sbyte_buffer rawbuf_; // the low level raw buffer
ikcpcb* kcp_;
std::recursive_mutex send_mtx_;
};
@ -824,7 +803,7 @@ protected:
class io_transport_kcp {};
#endif
using io_packet = dynamic_buffer_t;
using io_packet = sbyte_buffer;
#if !defined(YASIO_USE_SHARED_PACKET)
using packet_t = io_packet;
inline packet_t wrap_packet(io_packet& raw_packet) { return std::move(raw_packet); }
@ -959,6 +938,9 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields]
friend class io_transport_tcp;
friend class io_transport_udp;
friend class io_transport_kcp;
#if defined(YASIO_SSL_BACKEND)
friend class io_transport_ssl;
#endif
friend class io_channel;
public:
@ -1041,9 +1023,9 @@ public:
*/
int write(transport_handle_t thandle, const void* buf, size_t len, completion_cb_t completion_handler = nullptr)
{
return write(thandle, make_dynamic_buffer(buf, len), std::move(completion_handler));
return write(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, std::move(completion_handler));
}
YASIO__DECL int write(transport_handle_t thandle, dynamic_buffer_t buffer, completion_cb_t completion_handler = nullptr);
YASIO__DECL int write(transport_handle_t thandle, sbyte_buffer buffer, completion_cb_t completion_handler = nullptr);
/*
** Summary: Write data to unconnected UDP transport with specified address.
@ -1054,9 +1036,9 @@ public:
*/
int write_to(transport_handle_t thandle, const void* buf, size_t len, const ip::endpoint& to, completion_cb_t completion_handler = nullptr)
{
return write_to(thandle, make_dynamic_buffer(buf, len), to, std::move(completion_handler));
return write_to(thandle, sbyte_buffer{(const char*)buf, (const char*)buf + len, std::true_type{}}, to, std::move(completion_handler));
}
YASIO__DECL int write_to(transport_handle_t thandle, dynamic_buffer_t buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr);
YASIO__DECL int write_to(transport_handle_t thandle, sbyte_buffer buffer, const ip::endpoint& to, completion_cb_t completion_handler = nullptr);
// The highp_timer support, !important, the callback is called on the thread of io_service
YASIO__DECL highp_timer_ptr schedule(const std::chrono::microseconds& duration, timer_cb_t);
@ -1105,17 +1087,16 @@ private:
YASIO__DECL void do_connect_completion(io_channel*, fd_set_adapter& fd_set);
#if defined(YASIO_SSL_BACKEND)
YASIO__DECL void init_ssl_context();
YASIO__DECL void cleanup_ssl_context();
YASIO__DECL void do_ssl_handshake(io_channel*);
YASIO__DECL SSL_CTX* init_ssl_context(ssl_role role);
YASIO__DECL void cleanup_ssl_context(ssl_role role);
#endif
#if defined(YASIO_HAVE_CARES)
YASIO__DECL static void ares_getaddrinfo_cb(void* arg, int status, int timeouts, ares_addrinfo* answerlist);
YASIO__DECL void ares_work_started();
YASIO__DECL void ares_work_finished();
YASIO__DECL int register_ares_fds(socket_native_type* ares_socks, fd_set_adapter& fd_set);
YASIO__DECL void process_ares_requests(socket_native_type* socks, int count, fd_set_adapter& fd_set);
YASIO__DECL int do_ares_fds(socket_native_type* socks, fd_set_adapter& fd_set, timeval& waitd_tv);
YASIO__DECL void do_ares_process_fds(socket_native_type* socks, int count, fd_set_adapter& fd_set);
YASIO__DECL void recreate_ares_channel();
YASIO__DECL void config_ares_name_servers();
YASIO__DECL void destroy_ares_channel();
@ -1124,7 +1105,7 @@ private:
void handle_connect_succeed(io_channel* ctx, xxsocket_ptr s) { handle_connect_succeed(allocate_transport(ctx, std::move(s))); }
YASIO__DECL void handle_connect_succeed(transport_handle_t);
YASIO__DECL void handle_connect_failed(io_channel*, int ec);
YASIO__DECL void notify_connect_succeed(transport_handle_t);
YASIO__DECL void active_transport(transport_handle_t);
YASIO__DECL transport_handle_t allocate_transport(io_channel*, xxsocket_ptr&&);
YASIO__DECL void deallocate_transport(transport_handle_t);
@ -1166,9 +1147,6 @@ private:
YASIO__DECL void clear_transports(); // clear all transports
YASIO__DECL bool close_internal(io_channel*);
// shutdown a tcp-connection if possible
YASIO__DECL void shutdown_internal(transport_handle_t);
// supporting server
YASIO__DECL void do_accept(io_channel*);
YASIO__DECL void do_accept_completion(io_channel*, fd_set_adapter& fd_set);
@ -1248,8 +1226,12 @@ private:
print_fn2_t print_;
#if defined(YASIO_SSL_BACKEND)
// The full path cacert(.pem) file for ssl verifaction
// SSL client, the full path cacert(.pem) file for ssl verifaction
std::string cafile_;
// SSL server
std::string crtfile_;
std::string keyfile_;
#endif
#if defined(YASIO_HAVE_CARES)
@ -1262,7 +1244,7 @@ private:
// The stop flag to notify all transports needs close
uint8_t stop_flag_ = 0;
#if defined(YASIO_SSL_BACKEND)
SSL_CTX* ssl_ctx_ = nullptr;
SSL_CTX* ssl_roles_[2];
#endif
#if defined(YASIO_HAVE_CARES)
ares_channel ares_ = nullptr; // the ares handle for non blocking io dns resolve support