From 80e95b31fefa54719314e6a09cd0ad9273c8dac1 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 14 Jun 2021 21:08:49 +0200 Subject: [PATCH] Added a new http_trantor mosule. Added trantor to it and made it compile. --- modules/http_trantor/SCsub | 18 + modules/http_trantor/detect.py | 32 + modules/http_trantor/trantor/HEAD | 1 + modules/http_trantor/trantor/License | 57 + modules/http_trantor/trantor/README.md | 49 + modules/http_trantor/trantor/exports.h | 42 + modules/http_trantor/trantor/net/Channel.cc | 110 + modules/http_trantor/trantor/net/Channel.h | 314 +++ modules/http_trantor/trantor/net/EventLoop.cc | 372 +++ modules/http_trantor/trantor/net/EventLoop.h | 321 +++ .../trantor/net/EventLoopThread.cc | 72 + .../trantor/net/EventLoopThread.h | 72 + .../trantor/net/EventLoopThreadPool.cc | 72 + .../trantor/net/EventLoopThreadPool.h | 92 + .../http_trantor/trantor/net/InetAddress.cc | 232 ++ .../http_trantor/trantor/net/InetAddress.h | 226 ++ modules/http_trantor/trantor/net/Resolver.h | 57 + modules/http_trantor/trantor/net/TcpClient.cc | 228 ++ modules/http_trantor/trantor/net/TcpClient.h | 247 ++ .../http_trantor/trantor/net/TcpConnection.h | 261 ++ modules/http_trantor/trantor/net/TcpServer.cc | 201 ++ modules/http_trantor/trantor/net/TcpServer.h | 250 ++ modules/http_trantor/trantor/net/callbacks.h | 43 + .../trantor/net/inner/Acceptor.cc | 97 + .../http_trantor/trantor/net/inner/Acceptor.h | 56 + .../trantor/net/inner/AresResolver.cc | 258 ++ .../trantor/net/inner/AresResolver.h | 148 + .../trantor/net/inner/Connector.cc | 261 ++ .../trantor/net/inner/Connector.h | 88 + .../trantor/net/inner/NormalResolver.cc | 95 + .../trantor/net/inner/NormalResolver.h | 62 + .../http_trantor/trantor/net/inner/Poller.cc | 32 + .../http_trantor/trantor/net/inner/Poller.h | 54 + .../http_trantor/trantor/net/inner/Socket.cc | 248 ++ .../http_trantor/trantor/net/inner/Socket.h | 157 ++ .../trantor/net/inner/TcpConnectionImpl.cc | 1747 ++++++++++++ .../trantor/net/inner/TcpConnectionImpl.h | 344 +++ .../http_trantor/trantor/net/inner/Timer.cc | 64 + .../http_trantor/trantor/net/inner/Timer.h | 68 + .../trantor/net/inner/TimerQueue.cc | 294 ++ .../trantor/net/inner/TimerQueue.h | 76 + .../trantor/net/inner/poller/EpollPoller.cc | 241 ++ .../trantor/net/inner/poller/EpollPoller.h | 64 + .../trantor/net/inner/poller/KQueue.cc | 249 ++ .../trantor/net/inner/poller/KQueue.h | 55 + .../trantor/tests/AsyncFileLoggerTest.cc | 45 + .../trantor/tests/AsyncFileLoggerTest1.cc | 33 + .../http_trantor/trantor/tests/CMakeLists.txt | 51 + .../trantor/tests/ConcurrentTaskQueueTest.cc | 37 + .../trantor/tests/DelayedSSLClientTest.cc | 64 + .../trantor/tests/DelayedSSLServerTest.cc | 46 + modules/http_trantor/trantor/tests/DnsTest.cc | 48 + .../http_trantor/trantor/tests/KickoffTest.cc | 46 + .../http_trantor/trantor/tests/LoggerTest.cc | 1139 ++++++++ .../trantor/tests/RunInLoopTest1.cc | 30 + .../trantor/tests/RunInLoopTest2.cc | 44 + .../trantor/tests/RunOnQuitTest.cc | 27 + .../trantor/tests/SSLClientTest.cc | 54 + .../trantor/tests/SSLServerTest.cc | 43 + .../trantor/tests/SendfileTest.cc | 91 + .../trantor/tests/SerialTaskQueueTest1.cc | 29 + .../trantor/tests/SerialTaskQueueTest2.cc | 42 + .../trantor/tests/TcpClientTest.cc | 53 + .../trantor/tests/TcpServerTest.cc | 41 + .../http_trantor/trantor/tests/TimerTest.cc | 33 + .../http_trantor/trantor/tests/TimerTest1.cc | 23 + .../trantor/tests/TimingWheelTest.cc | 48 + modules/http_trantor/trantor/tests/server.pem | 49 + .../trantor/third_party/wepoll/LICENSE | 28 + .../trantor/third_party/wepoll/README.md | 41 + .../trantor/third_party/wepoll/Wepoll.c | 2491 +++++++++++++++++ .../trantor/third_party/wepoll/Wepoll.h | 120 + .../trantor/unittests/CMakeLists.txt | 19 + .../trantor/unittests/DateUnittest.cc | 43 + .../trantor/unittests/InetAddressUnittest.cc | 22 + .../trantor/unittests/MsgBufferUnittest.cc | 78 + .../trantor/unittests/splitStringUnittest.cc | 159 ++ .../trantor/utils/AsyncFileLogger.cc | 253 ++ .../trantor/utils/AsyncFileLogger.h | 140 + .../trantor/utils/ConcurrentTaskQueue.cc | 95 + .../trantor/utils/ConcurrentTaskQueue.h | 89 + modules/http_trantor/trantor/utils/Date.cc | 359 +++ modules/http_trantor/trantor/utils/Date.h | 281 ++ modules/http_trantor/trantor/utils/Funcs.h | 53 + .../trantor/utils/LockFreeQueue.h | 114 + .../http_trantor/trantor/utils/LogStream.cc | 273 ++ .../http_trantor/trantor/utils/LogStream.h | 277 ++ modules/http_trantor/trantor/utils/Logger.cc | 212 ++ modules/http_trantor/trantor/utils/Logger.h | 383 +++ .../http_trantor/trantor/utils/MsgBuffer.cc | 232 ++ .../http_trantor/trantor/utils/MsgBuffer.h | 376 +++ .../http_trantor/trantor/utils/NonCopyable.h | 41 + .../http_trantor/trantor/utils/ObjectPool.h | 76 + .../trantor/utils/SerialTaskQueue.cc | 56 + .../trantor/utils/SerialTaskQueue.h | 100 + .../http_trantor/trantor/utils/TaskQueue.h | 58 + .../http_trantor/trantor/utils/TimingWheel.cc | 126 + .../http_trantor/trantor/utils/TimingWheel.h | 104 + .../trantor/utils/WindowsSupport.cc | 55 + .../trantor/utils/WindowsSupport.h | 26 + 100 files changed, 17023 insertions(+) create mode 100644 modules/http_trantor/SCsub create mode 100644 modules/http_trantor/detect.py create mode 100644 modules/http_trantor/trantor/HEAD create mode 100644 modules/http_trantor/trantor/License create mode 100755 modules/http_trantor/trantor/README.md create mode 100644 modules/http_trantor/trantor/exports.h create mode 100644 modules/http_trantor/trantor/net/Channel.cc create mode 100644 modules/http_trantor/trantor/net/Channel.h create mode 100644 modules/http_trantor/trantor/net/EventLoop.cc create mode 100644 modules/http_trantor/trantor/net/EventLoop.h create mode 100644 modules/http_trantor/trantor/net/EventLoopThread.cc create mode 100644 modules/http_trantor/trantor/net/EventLoopThread.h create mode 100644 modules/http_trantor/trantor/net/EventLoopThreadPool.cc create mode 100644 modules/http_trantor/trantor/net/EventLoopThreadPool.h create mode 100644 modules/http_trantor/trantor/net/InetAddress.cc create mode 100644 modules/http_trantor/trantor/net/InetAddress.h create mode 100644 modules/http_trantor/trantor/net/Resolver.h create mode 100644 modules/http_trantor/trantor/net/TcpClient.cc create mode 100644 modules/http_trantor/trantor/net/TcpClient.h create mode 100644 modules/http_trantor/trantor/net/TcpConnection.h create mode 100644 modules/http_trantor/trantor/net/TcpServer.cc create mode 100644 modules/http_trantor/trantor/net/TcpServer.h create mode 100644 modules/http_trantor/trantor/net/callbacks.h create mode 100644 modules/http_trantor/trantor/net/inner/Acceptor.cc create mode 100644 modules/http_trantor/trantor/net/inner/Acceptor.h create mode 100644 modules/http_trantor/trantor/net/inner/AresResolver.cc create mode 100644 modules/http_trantor/trantor/net/inner/AresResolver.h create mode 100644 modules/http_trantor/trantor/net/inner/Connector.cc create mode 100644 modules/http_trantor/trantor/net/inner/Connector.h create mode 100644 modules/http_trantor/trantor/net/inner/NormalResolver.cc create mode 100644 modules/http_trantor/trantor/net/inner/NormalResolver.h create mode 100644 modules/http_trantor/trantor/net/inner/Poller.cc create mode 100644 modules/http_trantor/trantor/net/inner/Poller.h create mode 100644 modules/http_trantor/trantor/net/inner/Socket.cc create mode 100644 modules/http_trantor/trantor/net/inner/Socket.h create mode 100644 modules/http_trantor/trantor/net/inner/TcpConnectionImpl.cc create mode 100644 modules/http_trantor/trantor/net/inner/TcpConnectionImpl.h create mode 100644 modules/http_trantor/trantor/net/inner/Timer.cc create mode 100644 modules/http_trantor/trantor/net/inner/Timer.h create mode 100644 modules/http_trantor/trantor/net/inner/TimerQueue.cc create mode 100644 modules/http_trantor/trantor/net/inner/TimerQueue.h create mode 100644 modules/http_trantor/trantor/net/inner/poller/EpollPoller.cc create mode 100644 modules/http_trantor/trantor/net/inner/poller/EpollPoller.h create mode 100644 modules/http_trantor/trantor/net/inner/poller/KQueue.cc create mode 100644 modules/http_trantor/trantor/net/inner/poller/KQueue.h create mode 100644 modules/http_trantor/trantor/tests/AsyncFileLoggerTest.cc create mode 100644 modules/http_trantor/trantor/tests/AsyncFileLoggerTest1.cc create mode 100644 modules/http_trantor/trantor/tests/CMakeLists.txt create mode 100644 modules/http_trantor/trantor/tests/ConcurrentTaskQueueTest.cc create mode 100644 modules/http_trantor/trantor/tests/DelayedSSLClientTest.cc create mode 100644 modules/http_trantor/trantor/tests/DelayedSSLServerTest.cc create mode 100644 modules/http_trantor/trantor/tests/DnsTest.cc create mode 100644 modules/http_trantor/trantor/tests/KickoffTest.cc create mode 100644 modules/http_trantor/trantor/tests/LoggerTest.cc create mode 100644 modules/http_trantor/trantor/tests/RunInLoopTest1.cc create mode 100644 modules/http_trantor/trantor/tests/RunInLoopTest2.cc create mode 100644 modules/http_trantor/trantor/tests/RunOnQuitTest.cc create mode 100644 modules/http_trantor/trantor/tests/SSLClientTest.cc create mode 100644 modules/http_trantor/trantor/tests/SSLServerTest.cc create mode 100644 modules/http_trantor/trantor/tests/SendfileTest.cc create mode 100755 modules/http_trantor/trantor/tests/SerialTaskQueueTest1.cc create mode 100644 modules/http_trantor/trantor/tests/SerialTaskQueueTest2.cc create mode 100644 modules/http_trantor/trantor/tests/TcpClientTest.cc create mode 100644 modules/http_trantor/trantor/tests/TcpServerTest.cc create mode 100644 modules/http_trantor/trantor/tests/TimerTest.cc create mode 100644 modules/http_trantor/trantor/tests/TimerTest1.cc create mode 100644 modules/http_trantor/trantor/tests/TimingWheelTest.cc create mode 100644 modules/http_trantor/trantor/tests/server.pem create mode 100644 modules/http_trantor/trantor/third_party/wepoll/LICENSE create mode 100644 modules/http_trantor/trantor/third_party/wepoll/README.md create mode 100644 modules/http_trantor/trantor/third_party/wepoll/Wepoll.c create mode 100644 modules/http_trantor/trantor/third_party/wepoll/Wepoll.h create mode 100644 modules/http_trantor/trantor/unittests/CMakeLists.txt create mode 100644 modules/http_trantor/trantor/unittests/DateUnittest.cc create mode 100644 modules/http_trantor/trantor/unittests/InetAddressUnittest.cc create mode 100644 modules/http_trantor/trantor/unittests/MsgBufferUnittest.cc create mode 100644 modules/http_trantor/trantor/unittests/splitStringUnittest.cc create mode 100644 modules/http_trantor/trantor/utils/AsyncFileLogger.cc create mode 100644 modules/http_trantor/trantor/utils/AsyncFileLogger.h create mode 100644 modules/http_trantor/trantor/utils/ConcurrentTaskQueue.cc create mode 100644 modules/http_trantor/trantor/utils/ConcurrentTaskQueue.h create mode 100644 modules/http_trantor/trantor/utils/Date.cc create mode 100644 modules/http_trantor/trantor/utils/Date.h create mode 100644 modules/http_trantor/trantor/utils/Funcs.h create mode 100644 modules/http_trantor/trantor/utils/LockFreeQueue.h create mode 100644 modules/http_trantor/trantor/utils/LogStream.cc create mode 100644 modules/http_trantor/trantor/utils/LogStream.h create mode 100644 modules/http_trantor/trantor/utils/Logger.cc create mode 100644 modules/http_trantor/trantor/utils/Logger.h create mode 100644 modules/http_trantor/trantor/utils/MsgBuffer.cc create mode 100644 modules/http_trantor/trantor/utils/MsgBuffer.h create mode 100644 modules/http_trantor/trantor/utils/NonCopyable.h create mode 100644 modules/http_trantor/trantor/utils/ObjectPool.h create mode 100644 modules/http_trantor/trantor/utils/SerialTaskQueue.cc create mode 100644 modules/http_trantor/trantor/utils/SerialTaskQueue.h create mode 100644 modules/http_trantor/trantor/utils/TaskQueue.h create mode 100644 modules/http_trantor/trantor/utils/TimingWheel.cc create mode 100644 modules/http_trantor/trantor/utils/TimingWheel.h create mode 100644 modules/http_trantor/trantor/utils/WindowsSupport.cc create mode 100644 modules/http_trantor/trantor/utils/WindowsSupport.h diff --git a/modules/http_trantor/SCsub b/modules/http_trantor/SCsub new file mode 100644 index 0000000..ed36a74 --- /dev/null +++ b/modules/http_trantor/SCsub @@ -0,0 +1,18 @@ +#!/usr/bin/env python + +Import("env_mod") +Import("env") + +env_mod.core_sources = [] + +env_mod.add_source_files(env_mod.core_sources, "*.cc") +env_mod.add_source_files(env_mod.core_sources, "trantor/net/*.cc") +env_mod.add_source_files(env_mod.core_sources, "trantor/net/inner/*.cc") +env_mod.add_source_files(env_mod.core_sources, "trantor/net/inner/poller/*.cpp") +env_mod.add_source_files(env_mod.core_sources, "utils/*.cc") +#windows only +#env_mod.add_source_files(env_mod.core_sources, "third_party/wepoll/*.c") + +# Build it all as a library +lib = env_mod.add_library("trantor", env_mod.core_sources) +env.Prepend(LIBS=[lib]) diff --git a/modules/http_trantor/detect.py b/modules/http_trantor/detect.py new file mode 100644 index 0000000..5c06e4b --- /dev/null +++ b/modules/http_trantor/detect.py @@ -0,0 +1,32 @@ +import os +import platform +import sys + + +def is_active(): + return True + + +def get_name(): + return "http_trantor" + + +def can_build(): + return True + + +def get_opts(): + return [] + +def get_flags(): + + return [] + + +def configure(env): + env.Prepend(CPPPATH=["#modules/http_trantor"]) + env.Prepend(CPPPATH=["#modules/http_trantor/trantor/net"]) + env.Prepend(CPPPATH=["#modules/http_trantor/trantor/net/inner"]) + env.Prepend(CPPPATH=["#modules/http_trantor/trantor/utils"]) + + diff --git a/modules/http_trantor/trantor/HEAD b/modules/http_trantor/trantor/HEAD new file mode 100644 index 0000000..b47dacd --- /dev/null +++ b/modules/http_trantor/trantor/HEAD @@ -0,0 +1 @@ +255976d89866d556efd24264f65765f3e02bc32b \ No newline at end of file diff --git a/modules/http_trantor/trantor/License b/modules/http_trantor/trantor/License new file mode 100644 index 0000000..277f5cd --- /dev/null +++ b/modules/http_trantor/trantor/License @@ -0,0 +1,57 @@ +// Trantor - A non-blocking I/O based TCP network library, using C++14/17, +// Copyright (c) 2016-2021, Tao An. All rights reserved. +// https://github.com/an-tao/trantor +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Tao An nor the names of other contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Muduo - A reactor-based C++ network library for Linux +// Copyright (c) 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Shuo Chen nor the names of other contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/modules/http_trantor/trantor/README.md b/modules/http_trantor/trantor/README.md new file mode 100755 index 0000000..a76aa4e --- /dev/null +++ b/modules/http_trantor/trantor/README.md @@ -0,0 +1,49 @@ +# TRANTOR + +[![Build Status](https://travis-ci.org/an-tao/trantor.svg?branch=master)](https://travis-ci.org/an-tao/trantor) +[![Build status](https://ci.appveyor.com/api/projects/status/yn8xunsubn37pi1u/branch/master?svg=true)](https://ci.appveyor.com/project/an-tao/trantor/branch/master) +[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/an-tao/trantor.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/an-tao/trantor/context:cpp) + + +## Overview +A non-blocking I/O cross-platform TCP network library, using C++14. +Drawing on the design of Muduo Library + +## suported platforms +- Linux +- MacOS +- UNIX(BSD) +- Windows + +## Feature highlights +- non-blocking I/O +- cross-platform +- Thread pool +- Lock free design +- Support SSL +- Server and Client + + +## Build +```shell +git clone https://github.com/an-tao/trantor.git +cd trantor +cmake -Bbuild -H. +cd build +make -j +``` + +## Licensing +Trantor - A non-blocking I/O based TCP network library, using C++14. + +Copyright (c) 2016-2021, Tao An. All rights reserved. + +https://github.com/an-tao/trantor + +For more information see [License](License) + +## Community +[Gitter](https://gitter.im/drogon-web/community) + +## Documentation +[DocsForge](https://trantor.docsforge.com/) diff --git a/modules/http_trantor/trantor/exports.h b/modules/http_trantor/trantor/exports.h new file mode 100644 index 0000000..9f5f910 --- /dev/null +++ b/modules/http_trantor/trantor/exports.h @@ -0,0 +1,42 @@ + +#ifndef TRANTOR_EXPORT_H +#define TRANTOR_EXPORT_H + +#ifdef TRANTOR_STATIC_DEFINE +# define TRANTOR_EXPORT +# define TRANTOR_NO_EXPORT +#else +# ifndef TRANTOR_EXPORT +# ifdef trantor_EXPORTS + /* We are building this library */ +# define TRANTOR_EXPORT +# else + /* We are using this library */ +# define TRANTOR_EXPORT +# endif +# endif + +# ifndef TRANTOR_NO_EXPORT +# define TRANTOR_NO_EXPORT +# endif +#endif + +#ifndef TRANTOR_DEPRECATED +# define TRANTOR_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef TRANTOR_DEPRECATED_EXPORT +# define TRANTOR_DEPRECATED_EXPORT TRANTOR_EXPORT TRANTOR_DEPRECATED +#endif + +#ifndef TRANTOR_DEPRECATED_NO_EXPORT +# define TRANTOR_DEPRECATED_NO_EXPORT TRANTOR_NO_EXPORT TRANTOR_DEPRECATED +#endif + +#if 0 /* DEFINE_NO_DEPRECATED */ +# ifndef TRANTOR_NO_DEPRECATED +# define TRANTOR_NO_DEPRECATED +# endif +#endif + +#endif /* TRANTOR_EXPORT_H */ diff --git a/modules/http_trantor/trantor/net/Channel.cc b/modules/http_trantor/trantor/net/Channel.cc new file mode 100644 index 0000000..eaceea1 --- /dev/null +++ b/modules/http_trantor/trantor/net/Channel.cc @@ -0,0 +1,110 @@ +/** + * + * Channel.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Channel.h" +#include +#ifdef _WIN32 +#include "Wepoll.h" +#define POLLIN EPOLLIN +#define POLLPRI EPOLLPRI +#define POLLOUT EPOLLOUT +#define POLLHUP EPOLLHUP +#define POLLNVAL 0 +#define POLLERR EPOLLERR +#else +#include +#endif +#include +namespace trantor +{ +const int Channel::kNoneEvent = 0; + +const int Channel::kReadEvent = POLLIN | POLLPRI; +const int Channel::kWriteEvent = POLLOUT; + +Channel::Channel(EventLoop *loop, int fd) + : loop_(loop), fd_(fd), events_(0), revents_(0), index_(-1), tied_(false) +{ +} + +void Channel::remove() +{ + assert(events_ == kNoneEvent); + addedToLoop_ = false; + loop_->removeChannel(this); +} + +void Channel::update() +{ + loop_->updateChannel(this); +} + +void Channel::handleEvent() +{ + // LOG_TRACE<<"revents_="< guard = tie_.lock(); + if (guard) + { + handleEventSafely(); + } + } + else + { + handleEventSafely(); + } +} +void Channel::handleEventSafely() +{ + if (eventCallback_) + { + eventCallback_(); + return; + } + if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) + { + // LOG_TRACE<<"handle close"; + if (closeCallback_) + closeCallback_(); + } + if (revents_ & (POLLNVAL | POLLERR)) + { + // LOG_TRACE<<"handle error"; + if (errorCallback_) + errorCallback_(); + } +#ifdef __linux__ + if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) +#else + if (revents_ & (POLLIN | POLLPRI)) +#endif + { + // LOG_TRACE<<"handle read"; + if (readCallback_) + readCallback_(); + } +#ifdef _WIN32 + if ((revents_ & POLLOUT) && !(revents_ & POLLHUP)) +#else + if (revents_ & POLLOUT) +#endif + { + // LOG_TRACE<<"handle write"; + if (writeCallback_) + writeCallback_(); + } +} + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/Channel.h b/modules/http_trantor/trantor/net/Channel.h new file mode 100644 index 0000000..7060ee5 --- /dev/null +++ b/modules/http_trantor/trantor/net/Channel.h @@ -0,0 +1,314 @@ +/** + * + * @file Channel.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +namespace trantor +{ +class EventLoop; +/** + * @brief This class is used to implement reactor pattern. A Channel object + * manages a socket fd. Users use a Channel object to receive write or read + * events on the socket it manages. + * + */ +class TRANTOR_EXPORT Channel : NonCopyable +{ + public: + using EventCallback = std::function; + /** + * @brief Construct a new Channel instance. + * + * @param loop The event loop in which the channel works. + * @param fd The socket fd. + */ + Channel(EventLoop *loop, int fd); + + /** + * @brief Set the read callback. + * + * @param cb The callback is called when read event occurs on the socket. + * @note One should call the enableReading() method to ensure that the + * callback would be called when some data is received on the socket. + */ + void setReadCallback(const EventCallback &cb) + { + readCallback_ = cb; + }; + void setReadCallback(EventCallback &&cb) + { + readCallback_ = std::move(cb); + } + + /** + * @brief Set the write callback. + * + * @param cb The callback is called when write event occurs on the socket. + * @note One should call the enableWriting() method to ensure that the + * callback would be called when the socket can be written. + */ + void setWriteCallback(const EventCallback &cb) + { + writeCallback_ = cb; + }; + void setWriteCallback(EventCallback &&cb) + { + writeCallback_ = std::move(cb); + } + + /** + * @brief Set the close callback. + * + * @param cb The callback is called when the socket is closed. + */ + void setCloseCallback(const EventCallback &cb) + { + closeCallback_ = cb; + } + void setCloseCallback(EventCallback &&cb) + { + closeCallback_ = std::move(cb); + } + + /** + * @brief Set the error callback. + * + * @param cb The callback is called when an error occurs on the socket. + */ + void setErrorCallback(const EventCallback &cb) + { + errorCallback_ = cb; + } + void setErrorCallback(EventCallback &&cb) + { + errorCallback_ = std::move(cb); + } + + /** + * @brief Set the event callback. + * + * @param cb The callback is called when any event occurs on the socket. + * @note If the event callback is set to the channel, any other callback + * wouldn't be called again. + */ + void setEventCallback(const EventCallback &cb) + { + eventCallback_ = cb; + } + void setEventCallback(EventCallback &&cb) + { + eventCallback_ = std::move(cb); + } + + /** + * @brief Return the fd of the socket. + * + * @return int + */ + int fd() const + { + return fd_; + } + + /** + * @brief Return the events enabled on the socket. + * + * @return int + */ + int events() const + { + return events_; + } + + /** + * @brief Return the events that occurred on the socket. + * + * @return int + */ + int revents() const + { + return revents_; + } + + /** + * @brief Check whether there is no event enabled on the socket. + * + * @return true + * @return false + */ + bool isNoneEvent() const + { + return events_ == kNoneEvent; + }; + + /** + * @brief Disable all events on the socket. + * + */ + void disableAll() + { + events_ = kNoneEvent; + update(); + } + + /** + * @brief Remove the socket from the poller in the event loop. + * + */ + void remove(); + + /** + * @brief Return the event loop. + * + * @return EventLoop* + */ + EventLoop *ownerLoop() + { + return loop_; + }; + + /** + * @brief Enable the read event on the socket. + * + */ + void enableReading() + { + events_ |= kReadEvent; + update(); + } + + /** + * @brief Disable the read event on the socket. + * + */ + void disableReading() + { + events_ &= ~kReadEvent; + update(); + } + + /** + * @brief Enable the write event on the socket. + * + */ + void enableWriting() + { + events_ |= kWriteEvent; + update(); + } + + /** + * @brief Disable the write event on the socket. + * + */ + void disableWriting() + { + events_ &= ~kWriteEvent; + update(); + } + + /** + * @brief Check whether the write event is enabled on the socket. + * + * @return true + * @return false + */ + bool isWriting() const + { + return events_ & kWriteEvent; + } + + /** + * @brief Check whether the read event is enabled on the socket. + * + * @return true + * @return false + */ + bool isReading() const + { + return events_ & kReadEvent; + } + + /** + * @brief Set and update the events enabled. + * + * @param events + */ + void updateEvents(int events) + { + events_ = events; + update(); + } + + /** + * @brief This method is used to ensure that the callback owner is valid + * when a callback is called. + * + * @param obj The callback owner. Usually, the owner is also the owner of + * the channel. + * @note The 'obj' is kept in a weak_ptr object, so this method does not + * cause a circular reference problem. + */ + void tie(const std::shared_ptr &obj) + { + tie_ = obj; + tied_ = true; + } + + static const int kNoneEvent; + static const int kReadEvent; + static const int kWriteEvent; + + private: + friend class EventLoop; + friend class EpollPoller; + friend class KQueue; + void update(); + void handleEvent(); + void handleEventSafely(); + int setRevents(int revt) + { + // LOG_TRACE<<"revents="< tie_; + bool tied_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/EventLoop.cc b/modules/http_trantor/trantor/net/EventLoop.cc new file mode 100644 index 0000000..8a60a03 --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoop.cc @@ -0,0 +1,372 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) + +// Taken from Muduo and modified +// Copyright 2016, Tao An. All rights reserved. +// https://github.com/an-tao/trantor +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#include +#include + +#include "Poller.h" +#include "TimerQueue.h" +#include "Channel.h" + +#include +#include +#ifdef _WIN32 +#include +using ssize_t = long long; +#else +#include +#endif +#include +#ifdef __linux__ +#include +#endif +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include + +namespace trantor +{ +#ifdef __linux__ +int createEventfd() +{ + int evtfd = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (evtfd < 0) + { + std::cout << "Failed in eventfd" << std::endl; + abort(); + } + + return evtfd; +} +const int kPollTimeMs = 10000; +#endif +thread_local EventLoop *t_loopInThisThread = nullptr; + +EventLoop::EventLoop() + : looping_(false), + threadId_(std::this_thread::get_id()), + quit_(false), + poller_(Poller::newPoller(this)), + currentActiveChannel_(nullptr), + eventHandling_(false), + timerQueue_(new TimerQueue(this)), +#ifdef __linux__ + wakeupFd_(createEventfd()), + wakeupChannelPtr_(new Channel(this, wakeupFd_)), +#endif + threadLocalLoopPtr_(&t_loopInThisThread) +{ + if (t_loopInThisThread) + { + LOG_FATAL << "There is already an EventLoop in this thread"; + exit(-1); + } + t_loopInThisThread = this; +#ifdef __linux__ + wakeupChannelPtr_->setReadCallback(std::bind(&EventLoop::wakeupRead, this)); + wakeupChannelPtr_->enableReading(); +#elif !defined _WIN32 + auto r = pipe(wakeupFd_); + (void)r; + assert(!r); + fcntl(wakeupFd_[0], F_SETFL, O_NONBLOCK | O_CLOEXEC); + fcntl(wakeupFd_[1], F_SETFL, O_NONBLOCK | O_CLOEXEC); + wakeupChannelPtr_ = + std::unique_ptr(new Channel(this, wakeupFd_[0])); + wakeupChannelPtr_->setReadCallback(std::bind(&EventLoop::wakeupRead, this)); + wakeupChannelPtr_->enableReading(); +#else + poller_->setEventCallback([](uint64_t event) { assert(event == 1); }); +#endif +} +#ifdef __linux__ +void EventLoop::resetTimerQueue() +{ + assertInLoopThread(); + assert(!looping_); + timerQueue_->reset(); +} +#endif +void EventLoop::resetAfterFork() +{ + poller_->resetAfterFork(); +} +EventLoop::~EventLoop() +{ + quit(); + assert(!looping_); + t_loopInThisThread = nullptr; +#ifdef __linux__ + close(wakeupFd_); +#elif defined _WIN32 +#else + close(wakeupFd_[0]); + close(wakeupFd_[1]); +#endif +} +EventLoop *EventLoop::getEventLoopOfCurrentThread() +{ + return t_loopInThisThread; +} +void EventLoop::updateChannel(Channel *channel) +{ + assert(channel->ownerLoop() == this); + assertInLoopThread(); + poller_->updateChannel(channel); +} +void EventLoop::removeChannel(Channel *channel) +{ + assert(channel->ownerLoop() == this); + assertInLoopThread(); + if (eventHandling_) + { + assert(currentActiveChannel_ == channel || + std::find(activeChannels_.begin(), + activeChannels_.end(), + channel) == activeChannels_.end()); + } + poller_->removeChannel(channel); +} +void EventLoop::quit() +{ + quit_ = true; + + Func f; + while (funcsOnQuit_.dequeue(f)) + { + f(); + } + + // There is a chance that loop() just executes while(!quit_) and exits, + // then EventLoop destructs, then we are accessing an invalid object. + // Can be fixed using mutex_ in both places. + if (!isInLoopThread()) + { + wakeup(); + } +} +void EventLoop::loop() +{ + assert(!looping_); + assertInLoopThread(); + looping_ = true; + quit_ = false; + + while (!quit_) + { + activeChannels_.clear(); +#ifdef __linux__ + poller_->poll(kPollTimeMs, &activeChannels_); +#else + poller_->poll(static_cast(timerQueue_->getTimeout()), + &activeChannels_); + timerQueue_->processTimers(); +#endif + // TODO sort channel by priority + // std::cout<<"after ->poll()"<handleEvent(); + } + currentActiveChannel_ = NULL; + eventHandling_ = false; + // std::cout << "looping" << endl; + doRunInLoopFuncs(); + } + looping_ = false; +} +void EventLoop::abortNotInLoopThread() +{ + LOG_FATAL << "It is forbidden to run loop on threads other than event-loop " + "thread"; + exit(1); +} +void EventLoop::runInLoop(const Func &cb) +{ + if (isInLoopThread()) + { + cb(); + } + else + { + queueInLoop(cb); + } +} +void EventLoop::runInLoop(Func &&cb) +{ + if (isInLoopThread()) + { + cb(); + } + else + { + queueInLoop(std::move(cb)); + } +} +void EventLoop::queueInLoop(const Func &cb) +{ + funcs_.enqueue(cb); + if (!isInLoopThread() || !looping_) + { + wakeup(); + } +} +void EventLoop::queueInLoop(Func &&cb) +{ + funcs_.enqueue(std::move(cb)); + if (!isInLoopThread() || !looping_) + { + wakeup(); + } +} + +TimerId EventLoop::runAt(const Date &time, const Func &cb) +{ + auto microSeconds = + time.microSecondsSinceEpoch() - Date::now().microSecondsSinceEpoch(); + std::chrono::steady_clock::time_point tp = + std::chrono::steady_clock::now() + + std::chrono::microseconds(microSeconds); + return timerQueue_->addTimer(cb, tp, std::chrono::microseconds(0)); +} +TimerId EventLoop::runAt(const Date &time, Func &&cb) +{ + auto microSeconds = + time.microSecondsSinceEpoch() - Date::now().microSecondsSinceEpoch(); + std::chrono::steady_clock::time_point tp = + std::chrono::steady_clock::now() + + std::chrono::microseconds(microSeconds); + return timerQueue_->addTimer(std::move(cb), + tp, + std::chrono::microseconds(0)); +} +TimerId EventLoop::runAfter(double delay, const Func &cb) +{ + return runAt(Date::date().after(delay), cb); +} +TimerId EventLoop::runAfter(double delay, Func &&cb) +{ + return runAt(Date::date().after(delay), std::move(cb)); +} +TimerId EventLoop::runEvery(double interval, const Func &cb) +{ + std::chrono::microseconds dur( + static_cast(interval * 1000000)); + auto tp = std::chrono::steady_clock::now() + dur; + return timerQueue_->addTimer(cb, tp, dur); +} +TimerId EventLoop::runEvery(double interval, Func &&cb) +{ + std::chrono::microseconds dur( + static_cast(interval * 1000000)); + auto tp = std::chrono::steady_clock::now() + dur; + return timerQueue_->addTimer(std::move(cb), tp, dur); +} +void EventLoop::invalidateTimer(TimerId id) +{ + if (isRunning() && timerQueue_) + timerQueue_->invalidateTimer(id); +} +void EventLoop::doRunInLoopFuncs() +{ + callingFuncs_ = true; + { + // the destructor for the Func may itself insert a new entry into the + // queue + while (!funcs_.empty()) + { + Func func; + while (funcs_.dequeue(func)) + { + func(); + } + } + } + callingFuncs_ = false; +} +void EventLoop::wakeup() +{ + // if (!looping_) + // return; + uint64_t tmp = 1; +#ifdef __linux__ + int ret = write(wakeupFd_, &tmp, sizeof(tmp)); + (void)ret; +#elif defined _WIN32 + poller_->postEvent(1); +#else + int ret = write(wakeupFd_[1], &tmp, sizeof(tmp)); + (void)ret; +#endif +} +void EventLoop::wakeupRead() +{ + ssize_t ret = 0; +#ifdef __linux__ + uint64_t tmp; + ret = read(wakeupFd_, &tmp, sizeof(tmp)); +#elif defined _WIN32 +#else + uint64_t tmp; + ret = read(wakeupFd_[0], &tmp, sizeof(tmp)); +#endif + if (ret < 0) + LOG_SYSERR << "wakeup read error"; +} + +void EventLoop::moveToCurrentThread() +{ + if (isRunning()) + { + LOG_FATAL << "EventLoop cannot be moved when running"; + exit(-1); + } + if (isInLoopThread()) + { + LOG_WARN << "This EventLoop is already in the current thread"; + return; + } + if (t_loopInThisThread) + { + LOG_FATAL << "There is already an EventLoop in this thread, you cannot " + "move another in"; + exit(-1); + } + *threadLocalLoopPtr_ = nullptr; + t_loopInThisThread = this; + threadLocalLoopPtr_ = &t_loopInThisThread; + threadId_ = std::this_thread::get_id(); +} + +void EventLoop::runOnQuit(Func &&cb) +{ + funcsOnQuit_.enqueue(std::move(cb)); +} + +void EventLoop::runOnQuit(const Func &cb) +{ + funcsOnQuit_.enqueue(cb); +} + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/EventLoop.h b/modules/http_trantor/trantor/net/EventLoop.h new file mode 100644 index 0000000..d28f6b8 --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoop.h @@ -0,0 +1,321 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) + +// Taken from Muduo and modified +// Copyright 2016, Tao An. All rights reserved. +// https://github.com/an-tao/trantor +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +class Poller; +class TimerQueue; +class Channel; +using ChannelList = std::vector; +using Func = std::function; +using TimerId = uint64_t; +enum +{ + InvalidTimerId = 0 +}; + +/** + * @brief As the name implies, this class represents an event loop that runs in + * a perticular thread. The event loop can handle network I/O events and timers + * in asynchronous mode. + * @note An event loop object always belongs to a separate thread, and there is + * one event loop object at most in a thread. We can call an event loop object + * the event loop of the thread it belongs to, or call that thread the thread of + * the event loop. + */ +class TRANTOR_EXPORT EventLoop : NonCopyable +{ + public: + EventLoop(); + ~EventLoop(); + + /** + * @brief Run the event loop. This method will be blocked until the event + * loop exits. + * + */ + void loop(); + + /** + * @brief Let the event loop quit. + * + */ + void quit(); + + /** + * @brief Assertion that the current thread is the thread to which the event + * loop belongs. If the assertion fails, the program aborts. + */ + void assertInLoopThread() + { + if (!isInLoopThread()) + { + abortNotInLoopThread(); + } + }; +#ifdef __linux__ + /** + * @brief Make the timer queue works after calling the fork() function. + * + */ + void resetTimerQueue(); +#endif + /** + * @brief Make the event loop works after calling the fork() function. + * + */ + void resetAfterFork(); + + /** + * @brief Return true if the current thread is the thread to which the event + * loop belongs. + * + * @return true + * @return false + */ + bool isInLoopThread() const + { + return threadId_ == std::this_thread::get_id(); + }; + + /** + * @brief Get the event loop of the current thread. Return nullptr if there + * is no event loop in the current thread. + * + * @return EventLoop* + */ + static EventLoop *getEventLoopOfCurrentThread(); + + /** + * @brief Run the function f in the thread of the event loop. + * + * @param f + * @note If the current thread is the thread of the event loop, the function + * f is executed directly before the method exiting. + */ + void runInLoop(const Func &f); + void runInLoop(Func &&f); + + /** + * @brief Run the function f in the thread of the event loop. + * + * @param f + * @note The difference between this method and the runInLoop() method is + * that the function f is executed after the method exiting no matter if the + * current thread is the thread of the event loop. + */ + void queueInLoop(const Func &f); + void queueInLoop(Func &&f); + + /** + * @brief Run a function at a time point. + * + * @param time The time to run the function. + * @param cb The function to run. + * @return TimerId The ID of the timer. + */ + TimerId runAt(const Date &time, const Func &cb); + TimerId runAt(const Date &time, Func &&cb); + + /** + * @brief Run a function after a period of time. + * + * @param delay Represent the period of time in seconds. + * @param cb The function to run. + * @return TimerId The ID of the timer. + */ + TimerId runAfter(double delay, const Func &cb); + TimerId runAfter(double delay, Func &&cb); + + /** + * @brief Run a function after a period of time. + * @note Users could use chrono literals to represent a time duration + * For example: + * @code + runAfter(5s, task); + runAfter(10min, task); + @endcode + */ + TimerId runAfter(const std::chrono::duration &delay, const Func &cb) + { + return runAfter(delay.count(), cb); + } + TimerId runAfter(const std::chrono::duration &delay, Func &&cb) + { + return runAfter(delay.count(), std::move(cb)); + } + + /** + * @brief Repeatedly run a function every period of time. + * + * @param interval The duration in seconds. + * @param cb The function to run. + * @return TimerId The ID of the timer. + */ + TimerId runEvery(double interval, const Func &cb); + TimerId runEvery(double interval, Func &&cb); + + /** + * @brief Repeatedly run a function every period of time. + * Users could use chrono literals to represent a time duration + * For example: + * @code + runEvery(5s, task); + runEvery(10min, task); + runEvery(0.1h, task); + @endcode + */ + TimerId runEvery(const std::chrono::duration &interval, + const Func &cb) + { + return runEvery(interval.count(), cb); + } + TimerId runEvery(const std::chrono::duration &interval, Func &&cb) + { + return runEvery(interval.count(), std::move(cb)); + } + + /** + * @brief Invalidate the timer identified by the given ID. + * + * @param id The ID of the timer. + */ + void invalidateTimer(TimerId id); + + /** + * @brief Move the EventLoop to the current thread, this method must be + * called before the loop is running. + * + */ + void moveToCurrentThread(); + + /** + * @brief Update channel status. This method is usually used internally. + * + * @param chl + */ + void updateChannel(Channel *chl); + + /** + * @brief Remove a channel from the event loop. This method is usually used + * internally. + * + * @param chl + */ + void removeChannel(Channel *chl); + + /** + * @brief Return the index of the event loop. + * + * @return size_t + */ + size_t index() + { + return index_; + } + + /** + * @brief Set the index of the event loop. + * + * @param index + */ + void setIndex(size_t index) + { + index_ = index; + } + + /** + * @brief Return true if the event loop is running. + * + * @return true + * @return false + */ + bool isRunning() + { + return looping_ && (!quit_); + } + + /** + * @brief Check if the event loop is calling a function. + * + * @return true + * @return false + */ + bool isCallingFunctions() + { + return callingFuncs_; + } + + /** + * @brief Run functions when the event loop quits + * + * @param cb the function to run + * @note the function runs on the thread that quits the EventLoop + */ + void runOnQuit(Func &&cb); + void runOnQuit(const Func &cb); + + private: + void abortNotInLoopThread(); + void wakeup(); + void wakeupRead(); + bool looping_; + std::thread::id threadId_; + bool quit_; + std::unique_ptr poller_; + + ChannelList activeChannels_; + Channel *currentActiveChannel_; + + bool eventHandling_; + MpscQueue funcs_; + std::unique_ptr timerQueue_; + MpscQueue funcsOnQuit_; + bool callingFuncs_{false}; +#ifdef __linux__ + int wakeupFd_; + std::unique_ptr wakeupChannelPtr_; +#elif defined _WIN32 +#else + int wakeupFd_[2]; + std::unique_ptr wakeupChannelPtr_; +#endif + + void doRunInLoopFuncs(); +#ifdef _WIN32 + size_t index_{size_t(-1)}; +#else + size_t index_{std::numeric_limits::max()}; +#endif + EventLoop **threadLocalLoopPtr_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/EventLoopThread.cc b/modules/http_trantor/trantor/net/EventLoopThread.cc new file mode 100644 index 0000000..39ee02e --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoopThread.cc @@ -0,0 +1,72 @@ +/** + * + * @file EventLoopThread.cc + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include +#ifdef __linux__ +#include +#endif + +using namespace trantor; +EventLoopThread::EventLoopThread(const std::string &threadName) + : loop_(nullptr), + loopThreadName_(threadName), + thread_([this]() { loopFuncs(); }) +{ + auto f = promiseForLoopPointer_.get_future(); + loop_ = f.get(); +} +EventLoopThread::~EventLoopThread() +{ + run(); + if (loop_) + { + loop_->quit(); + } + if (thread_.joinable()) + { + thread_.join(); + } +} +// void EventLoopThread::stop() { +// if(loop_) +// loop_->quit(); +//} +void EventLoopThread::wait() +{ + thread_.join(); +} +void EventLoopThread::loopFuncs() +{ +#ifdef __linux__ + ::prctl(PR_SET_NAME, loopThreadName_.c_str()); +#endif + EventLoop loop; + loop.queueInLoop([this]() { promiseForLoop_.set_value(1); }); + promiseForLoopPointer_.set_value(&loop); + auto f = promiseForRun_.get_future(); + (void)f.get(); + loop.loop(); + // LOG_DEBUG << "loop out"; + loop_ = NULL; +} +void EventLoopThread::run() +{ + std::call_once(once_, [this]() { + auto f = promiseForLoop_.get_future(); + promiseForRun_.set_value(1); + // Make sure the event loop loops before returning. + (void)f.get(); + }); +} diff --git a/modules/http_trantor/trantor/net/EventLoopThread.h b/modules/http_trantor/trantor/net/EventLoopThread.h new file mode 100644 index 0000000..4d9caa1 --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoopThread.h @@ -0,0 +1,72 @@ +/** + * + * @file EventLoopThread.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class represents an event loop thread. + * + */ +class TRANTOR_EXPORT EventLoopThread : NonCopyable +{ + public: + explicit EventLoopThread(const std::string &threadName = "EventLoopThread"); + ~EventLoopThread(); + + /** + * @brief Wait for the event loop to exit. + * @note This method blocks the current thread until the event loop exits. + */ + void wait(); + + /** + * @brief Get the pointer of the event loop of the thread. + * + * @return EventLoop* + */ + EventLoop *getLoop() const + { + return loop_; + } + + /** + * @brief Run the event loop of the thread. This method doesn't block the + * current thread. + * + */ + void run(); + + private: + EventLoop *loop_; + std::string loopThreadName_; + void loopFuncs(); + std::promise promiseForLoopPointer_; + std::promise promiseForRun_; + std::promise promiseForLoop_; + std::once_flag once_; + std::thread thread_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/EventLoopThreadPool.cc b/modules/http_trantor/trantor/net/EventLoopThreadPool.cc new file mode 100644 index 0000000..c96927b --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoopThreadPool.cc @@ -0,0 +1,72 @@ +/** + * + * EventLoopThreadPool.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +using namespace trantor; +EventLoopThreadPool::EventLoopThreadPool(size_t threadNum, + const std::string &name) + : loopIndex_(0) +{ + for (size_t i = 0; i < threadNum; ++i) + { + loopThreadVector_.emplace_back(std::make_shared(name)); + } +} +void EventLoopThreadPool::start() +{ + for (unsigned int i = 0; i < loopThreadVector_.size(); ++i) + { + loopThreadVector_[i]->run(); + } +} +// void EventLoopThreadPool::stop(){ +// for(unsigned int i=0;iwait(); + } +} +EventLoop *EventLoopThreadPool::getNextLoop() +{ + if (loopThreadVector_.size() > 0) + { + EventLoop *loop = loopThreadVector_[loopIndex_]->getLoop(); + ++loopIndex_; + if (loopIndex_ >= loopThreadVector_.size()) + loopIndex_ = 0; + return loop; + } + return nullptr; +} +EventLoop *EventLoopThreadPool::getLoop(size_t id) +{ + if (id < loopThreadVector_.size()) + return loopThreadVector_[id]->getLoop(); + return nullptr; +} +std::vector EventLoopThreadPool::getLoops() const +{ + std::vector ret; + for (auto &loopThread : loopThreadVector_) + { + ret.push_back(loopThread->getLoop()); + } + return ret; +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/net/EventLoopThreadPool.h b/modules/http_trantor/trantor/net/EventLoopThreadPool.h new file mode 100644 index 0000000..6332169 --- /dev/null +++ b/modules/http_trantor/trantor/net/EventLoopThreadPool.h @@ -0,0 +1,92 @@ +/** + * + * @file EventLoopThreadPool.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class represents a pool of EventLoopThread objects + * + */ +class TRANTOR_EXPORT EventLoopThreadPool : NonCopyable +{ + public: + EventLoopThreadPool() = delete; + + /** + * @brief Construct a new event loop thread pool instance. + * + * @param threadNum The number of threads + * @param name The name of the EventLoopThreadPool object. + */ + EventLoopThreadPool(size_t threadNum, + const std::string &name = "EventLoopThreadPool"); + + /** + * @brief Run all event loops in the pool. + * @note This function doesn't block the current thread. + */ + void start(); + + /** + * @brief Wait for all event loops in the pool to quit. + * + * @note This function blocks the current thread. + */ + void wait(); + + /** + * @brief Return the number of the event loop. + * + * @return size_t + */ + size_t size() + { + return loopThreadVector_.size(); + } + + /** + * @brief Get the next event loop in the pool. + * + * @return EventLoop* + */ + EventLoop *getNextLoop(); + + /** + * @brief Get the event loop in the `id` position in the pool. + * + * @param id The id of the first event loop is zero. If the id >= the number + * of event loops, nullptr is returned. + * @return EventLoop* + */ + EventLoop *getLoop(size_t id); + + /** + * @brief Get all event loops in the pool. + * + * @return std::vector + */ + std::vector getLoops() const; + + private: + std::vector> loopThreadVector_; + size_t loopIndex_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/InetAddress.cc b/modules/http_trantor/trantor/net/InetAddress.cc new file mode 100644 index 0000000..18b952a --- /dev/null +++ b/modules/http_trantor/trantor/net/InetAddress.cc @@ -0,0 +1,232 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) + +#include + +#include +//#include + +#ifdef _WIN32 +struct in6_addr_uint +{ + union + { + u_char Byte[16]; + u_short Word[8]; + uint32_t __s6_addr32[4]; + } uext; +}; +#else +#include // memset +#include +#include +#endif + +// INADDR_ANY use (type)value casting. +static const in_addr_t kInaddrAny = INADDR_ANY; +static const in_addr_t kInaddrLoopback = INADDR_LOOPBACK; + +// /* Structure describing an Internet socket address. */ +// struct sockaddr_in { +// sa_family_t sin_family; /* address family: AF_INET */ +// uint16_t sin_port; /* port in network byte order */ +// struct in_addr sin_addr; /* internet address */ +// }; + +// /* Internet address. */ +// typedef uint32_t in_addr_t; +// struct in_addr { +// in_addr_t s_addr; /* address in network byte order */ +// }; + +// struct sockaddr_in6 { +// sa_family_t sin6_family; /* address family: AF_INET6 */ +// uint16_t sin6_port; /* port in network byte order */ +// uint32_t sin6_flowinfo; /* IPv6 flow information */ +// struct in6_addr sin6_addr; /* IPv6 address */ +// uint32_t sin6_scope_id; /* IPv6 scope-id */ +// }; + +using namespace trantor; + +/* +#ifdef __linux__ +#if !(__GNUC_PREREQ(4, 6)) +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif +#endif +*/ + +InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6) + : isIpV6_(ipv6) +{ + if (ipv6) + { + memset(&addr6_, 0, sizeof(addr6_)); + addr6_.sin6_family = AF_INET6; + in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any; + addr6_.sin6_addr = ip; + addr6_.sin6_port = htons(port); + } + else + { + memset(&addr_, 0, sizeof(addr_)); + addr_.sin_family = AF_INET; + in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny; + addr_.sin_addr.s_addr = htonl(ip); + addr_.sin_port = htons(port); + } + isUnspecified_ = false; +} + +InetAddress::InetAddress(const std::string &ip, uint16_t port, bool ipv6) + : isIpV6_(ipv6) +{ + if (ipv6) + { + memset(&addr6_, 0, sizeof(addr6_)); + addr6_.sin6_family = AF_INET6; + addr6_.sin6_port = htons(port); + if (::inet_pton(AF_INET6, ip.c_str(), &addr6_.sin6_addr) <= 0) + { + return; + } + } + else + { + memset(&addr_, 0, sizeof(addr_)); + addr_.sin_family = AF_INET; + addr_.sin_port = htons(port); + if (::inet_pton(AF_INET, ip.c_str(), &addr_.sin_addr) <= 0) + { + return; + } + } + isUnspecified_ = false; +} + +std::string InetAddress::toIpPort() const +{ + char buf[64] = ""; + uint16_t port = ntohs(addr_.sin_port); + snprintf(buf, sizeof(buf), ":%u", port); + return toIp() + std::string(buf); +} +bool InetAddress::isIntranetIp() const +{ + if (addr_.sin_family == AF_INET) + { + uint32_t ip_addr = ntohl(addr_.sin_addr.s_addr); + if ((ip_addr >= 0x0A000000 && ip_addr <= 0x0AFFFFFF) || + (ip_addr >= 0xAC100000 && ip_addr <= 0xAC1FFFFF) || + (ip_addr >= 0xC0A80000 && ip_addr <= 0xC0A8FFFF) || + ip_addr == 0x7f000001) + + { + return true; + } + } + else + { + auto addrP = ip6NetEndian(); + // Loopback ip + if (*addrP == 0 && *(addrP + 1) == 0 && *(addrP + 2) == 0 && + ntohl(*(addrP + 3)) == 1) + return true; + // Privated ip is prefixed by FEC0::/10 or FE80::/10, need testing + auto i32 = (ntohl(*addrP) & 0xffc00000); + if (i32 == 0xfec00000 || i32 == 0xfe800000) + return true; + if (*addrP == 0 && *(addrP + 1) == 0 && ntohl(*(addrP + 2)) == 0xffff) + { + // the IPv6 version of an IPv4 IP address + uint32_t ip_addr = ntohl(*(addrP + 3)); + if ((ip_addr >= 0x0A000000 && ip_addr <= 0x0AFFFFFF) || + (ip_addr >= 0xAC100000 && ip_addr <= 0xAC1FFFFF) || + (ip_addr >= 0xC0A80000 && ip_addr <= 0xC0A8FFFF) || + ip_addr == 0x7f000001) + + { + return true; + } + } + } + return false; +} + +bool InetAddress::isLoopbackIp() const +{ + if (!isIpV6()) + { + uint32_t ip_addr = ntohl(addr_.sin_addr.s_addr); + if (ip_addr == 0x7f000001) + { + return true; + } + } + else + { + auto addrP = ip6NetEndian(); + if (*addrP == 0 && *(addrP + 1) == 0 && *(addrP + 2) == 0 && + ntohl(*(addrP + 3)) == 1) + return true; + // the IPv6 version of an IPv4 loopback address + if (*addrP == 0 && *(addrP + 1) == 0 && ntohl(*(addrP + 2)) == 0xffff && + ntohl(*(addrP + 3)) == 0x7f000001) + return true; + } + return false; +} + +std::string InetAddress::toIp() const +{ + char buf[64]; + if (addr_.sin_family == AF_INET) + { +#if defined _MSC_VER && _MSC_VER >= 1900 + ::inet_ntop(AF_INET, (PVOID)&addr_.sin_addr, buf, sizeof(buf)); +#else + ::inet_ntop(AF_INET, &addr_.sin_addr, buf, sizeof(buf)); +#endif + } + else if (addr_.sin_family == AF_INET6) + { +#if defined _MSC_VER && _MSC_VER >= 1900 + ::inet_ntop(AF_INET6, (PVOID)&addr6_.sin6_addr, buf, sizeof(buf)); +#else + ::inet_ntop(AF_INET6, &addr6_.sin6_addr, buf, sizeof(buf)); +#endif + } + + return buf; +} + +uint32_t InetAddress::ipNetEndian() const +{ + // assert(family() == AF_INET); + return addr_.sin_addr.s_addr; +} + +const uint32_t *InetAddress::ip6NetEndian() const +{ +// assert(family() == AF_INET6); +#ifdef __linux__ + return addr6_.sin6_addr.s6_addr32; +#elif defined _WIN32 + // TODO is this OK ? + const struct in6_addr_uint *addr_temp = + reinterpret_cast(&addr6_.sin6_addr); + return (*addr_temp).uext.__s6_addr32; +#else + return addr6_.sin6_addr.__u6_addr.__u6_addr32; +#endif +} +uint16_t InetAddress::toPort() const +{ + return ntohs(portNetEndian()); +} diff --git a/modules/http_trantor/trantor/net/InetAddress.h b/modules/http_trantor/trantor/net/InetAddress.h new file mode 100644 index 0000000..1750bb5 --- /dev/null +++ b/modules/http_trantor/trantor/net/InetAddress.h @@ -0,0 +1,226 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// +// This is a public header file, it must only include public header files. + +// Taken from Muduo and modified +// Copyright 2016, Tao An. All rights reserved. +// https://github.com/an-tao/trantor +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#ifndef MUDUO_NET_INETADDRESS_H +#define MUDUO_NET_INETADDRESS_H + +#include +#include + +#ifdef _WIN32 +#include +using sa_family_t = unsigned short; +using in_addr_t = uint32_t; +using uint16_t = unsigned short; +#else +#include +#include +#include +#endif +#include +#include +#include +namespace trantor +{ +/** + * @brief Wrapper of sockaddr_in. This is an POD interface class. + * + */ +class TRANTOR_EXPORT InetAddress +{ + public: + /** + * @brief Constructs an endpoint with given port number. Mostly used in + * TcpServer listening. + * + * @param port + * @param loopbackOnly + * @param ipv6 + */ + InetAddress(uint16_t port = 0, + bool loopbackOnly = false, + bool ipv6 = false); + + /** + * @brief Constructs an endpoint with given ip and port. + * + * @param ip A IPv4 or IPv6 address. + * @param port + * @param ipv6 + */ + InetAddress(const std::string &ip, uint16_t port, bool ipv6 = false); + + /** + * @brief Constructs an endpoint with given struct `sockaddr_in`. Mostly + * used when accepting new connections + * + * @param addr + */ + explicit InetAddress(const struct sockaddr_in &addr) + : addr_(addr), isUnspecified_(false) + { + } + + /** + * @brief Constructs an IPv6 endpoint with given struct `sockaddr_in6`. + * Mostly used when accepting new connections + * + * @param addr + */ + explicit InetAddress(const struct sockaddr_in6 &addr) + : addr6_(addr), isIpV6_(true), isUnspecified_(false) + { + } + + /** + * @brief Return the sin_family of the endpoint. + * + * @return sa_family_t + */ + sa_family_t family() const + { + return addr_.sin_family; + } + + /** + * @brief Return the IP string of the endpoint. + * + * @return std::string + */ + std::string toIp() const; + + /** + * @brief Return the IP and port string of the endpoint. + * + * @return std::string + */ + std::string toIpPort() const; + + /** + * @brief Return the port number of the endpoint. + * + * @return uint16_t + */ + uint16_t toPort() const; + + /** + * @brief Check if the endpoint is IPv4 or IPv6. + * + * @return true + * @return false + */ + bool isIpV6() const + { + return isIpV6_; + } + + /** + * @brief Return true if the endpoint is an intranet endpoint. + * + * @return true + * @return false + */ + bool isIntranetIp() const; + + /** + * @brief Return true if the endpoint is a loopback endpoint. + * + * @return true + * @return false + */ + bool isLoopbackIp() const; + + /** + * @brief Get the pointer to the sockaddr struct. + * + * @return const struct sockaddr* + */ + const struct sockaddr *getSockAddr() const + { + return static_cast((void *)(&addr6_)); + } + + /** + * @brief Set the sockaddr_in6 struct in the endpoint. + * + * @param addr6 + */ + void setSockAddrInet6(const struct sockaddr_in6 &addr6) + { + addr6_ = addr6; + isIpV6_ = (addr6_.sin6_family == AF_INET6); + isUnspecified_ = false; + } + + /** + * @brief Return the integer value of the IP(v4) in net endian byte order. + * + * @return uint32_t + */ + uint32_t ipNetEndian() const; + + /** + * @brief Return the pointer to the integer value of the IP(v6) in net + * endian byte order. + * + * @return const uint32_t* + */ + const uint32_t *ip6NetEndian() const; + + /** + * @brief Return the port number in net endian byte order. + * + * @return uint16_t + */ + uint16_t portNetEndian() const + { + return addr_.sin_port; + } + + /** + * @brief Set the port number in net endian byte order. + * + * @param port + */ + void setPortNetEndian(uint16_t port) + { + addr_.sin_port = port; + } + + /** + * @brief Return true if the address is not initalized. + */ + inline bool isUnspecified() const + { + return isUnspecified_; + } + + private: + union + { + struct sockaddr_in addr_; + struct sockaddr_in6 addr6_; + }; + bool isIpV6_{false}; + bool isUnspecified_{true}; +}; + +} // namespace trantor + +#endif // MUDUO_NET_INETADDRESS_H diff --git a/modules/http_trantor/trantor/net/Resolver.h b/modules/http_trantor/trantor/net/Resolver.h new file mode 100644 index 0000000..478887b --- /dev/null +++ b/modules/http_trantor/trantor/net/Resolver.h @@ -0,0 +1,57 @@ +// Copyright 2016, Tao An. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#pragma once +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class represents an asynchronous DNS resolver. + * @note Although the c-ares library is not essential, it is recommended to + * install it for higher performance + */ +class TRANTOR_EXPORT Resolver +{ + public: + using Callback = std::function; + + /** + * @brief Create a new DNS resolver. + * + * @param loop The event loop in which the DNS resolver runs. + * @param timeout The timeout in seconds for DNS. + * @return std::shared_ptr + */ + static std::shared_ptr newResolver(EventLoop* loop = nullptr, + size_t timeout = 60); + + /** + * @brief Resolve an address asynchronously. + * + * @param hostname + * @param callback + */ + virtual void resolve(const std::string& hostname, + const Callback& callback) = 0; + + virtual ~Resolver() + { + } + + /** + * @brief Check whether the c-ares library is used. + * + * @return true + * @return false + */ + static bool isCAresUsed(); +}; +} // namespace trantor \ No newline at end of file diff --git a/modules/http_trantor/trantor/net/TcpClient.cc b/modules/http_trantor/trantor/net/TcpClient.cc new file mode 100644 index 0000000..98048a0 --- /dev/null +++ b/modules/http_trantor/trantor/net/TcpClient.cc @@ -0,0 +1,228 @@ +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// + +// Taken from muduo and modified by an tao + +#include + +#include +#include "Connector.h" +#include "inner/TcpConnectionImpl.h" +#include + +#include +#include + +#include "Socket.h" + +#include // snprintf + +using namespace trantor; +using namespace std::placeholders; + +namespace trantor +{ +// void removeConnector(const ConnectorPtr &) +// { +// // connector-> +// } +#ifndef _WIN32 +TcpClient::IgnoreSigPipe TcpClient::initObj; +#endif + +static void defaultConnectionCallback(const TcpConnectionPtr &conn) +{ + LOG_TRACE << conn->localAddr().toIpPort() << " -> " + << conn->peerAddr().toIpPort() << " is " + << (conn->connected() ? "UP" : "DOWN"); + // do not call conn->forceClose(), because some users want to register + // message callback only. +} + +static void defaultMessageCallback(const TcpConnectionPtr &, MsgBuffer *buf) +{ + buf->retrieveAll(); +} + +} // namespace trantor + +TcpClient::TcpClient(EventLoop *loop, + const InetAddress &serverAddr, + const std::string &nameArg) + : loop_(loop), + connector_(new Connector(loop, serverAddr, false)), + name_(nameArg), + connectionCallback_(defaultConnectionCallback), + messageCallback_(defaultMessageCallback), + retry_(false), + connect_(true) +{ + connector_->setNewConnectionCallback( + std::bind(&TcpClient::newConnection, this, _1)); + connector_->setErrorCallback([this]() { + if (connectionErrorCallback_) + { + connectionErrorCallback_(); + } + }); + LOG_TRACE << "TcpClient::TcpClient[" << name_ << "] - connector "; +} + +TcpClient::~TcpClient() +{ + LOG_TRACE << "TcpClient::~TcpClient[" << name_ << "] - connector "; + TcpConnectionImplPtr conn; + { + std::lock_guard lock(mutex_); + conn = std::dynamic_pointer_cast(connection_); + } + if (conn) + { + assert(loop_ == conn->getLoop()); + // TODO: not 100% safe, if we are in different thread + auto loop = loop_; + loop_->runInLoop([conn, loop]() { + conn->setCloseCallback([loop](const TcpConnectionPtr &connPtr) { + loop->queueInLoop([connPtr]() { + static_cast(connPtr.get()) + ->connectDestroyed(); + }); + }); + }); + conn->forceClose(); + } + else + { + /// TODO need test in this condition + connector_->stop(); + } +} + +void TcpClient::connect() +{ + // TODO: check state + LOG_TRACE << "TcpClient::connect[" << name_ << "] - connecting to " + << connector_->serverAddress().toIpPort(); + connect_ = true; + connector_->start(); +} + +void TcpClient::disconnect() +{ + connect_ = false; + + { + std::lock_guard lock(mutex_); + if (connection_) + { + connection_->shutdown(); + } + } +} + +void TcpClient::stop() +{ + connect_ = false; + connector_->stop(); +} + +void TcpClient::newConnection(int sockfd) +{ + loop_->assertInLoopThread(); + InetAddress peerAddr(Socket::getPeerAddr(sockfd)); + InetAddress localAddr(Socket::getLocalAddr(sockfd)); + // TODO poll with zero timeout to double confirm the new connection + // TODO use make_shared if necessary + std::shared_ptr conn; + if (sslCtxPtr_) + { +#ifdef USE_OPENSSL + conn = std::make_shared(loop_, + sockfd, + localAddr, + peerAddr, + sslCtxPtr_, + false, + validateCert_, + SSLHostName_); +#else + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#endif + } + else + { + conn = std::make_shared(loop_, + sockfd, + localAddr, + peerAddr); + } + conn->setConnectionCallback(connectionCallback_); + conn->setRecvMsgCallback(messageCallback_); + conn->setWriteCompleteCallback(writeCompleteCallback_); + conn->setCloseCallback(std::bind(&TcpClient::removeConnection, this, _1)); + { + std::lock_guard lock(mutex_); + connection_ = conn; + } + conn->setSSLErrorCallback([this](SSLError err) { + if (sslErrorCallback_) + { + sslErrorCallback_(err); + } + }); + conn->connectEstablished(); +} + +void TcpClient::removeConnection(const TcpConnectionPtr &conn) +{ + loop_->assertInLoopThread(); + assert(loop_ == conn->getLoop()); + + { + std::lock_guard lock(mutex_); + assert(connection_ == conn); + connection_.reset(); + } + + loop_->queueInLoop( + std::bind(&TcpConnectionImpl::connectDestroyed, + std::dynamic_pointer_cast(conn))); + if (retry_ && connect_) + { + LOG_TRACE << "TcpClient::connect[" << name_ << "] - Reconnecting to " + << connector_->serverAddress().toIpPort(); + connector_->restart(); + } +} + +void TcpClient::enableSSL( + bool useOldTLS, + bool validateCert, + std::string hostname, + const std::vector> &sslConfCmds) +{ +#ifdef USE_OPENSSL + /* Create a new OpenSSL context */ + sslCtxPtr_ = newSSLContext(useOldTLS, validateCert, sslConfCmds); + validateCert_ = validateCert; + if (!hostname.empty()) + { + std::transform(hostname.begin(), + hostname.end(), + hostname.begin(), + tolower); + SSLHostName_ = std::move(hostname); + } + +#else + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#endif +} diff --git a/modules/http_trantor/trantor/net/TcpClient.h b/modules/http_trantor/trantor/net/TcpClient.h new file mode 100644 index 0000000..045ad06 --- /dev/null +++ b/modules/http_trantor/trantor/net/TcpClient.h @@ -0,0 +1,247 @@ +// taken from muduo +// Copyright 2010, Shuo Chen. All rights reserved. +// http://code.google.com/p/muduo/ +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Shuo Chen (chenshuo at chenshuo dot com) +// +// This is a public header file, it must only include public header files. + +// Copyright 2016, Tao An. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +namespace trantor +{ +class Connector; +using ConnectorPtr = std::shared_ptr; +class SSLContext; +/** + * @brief This class represents a TCP client. + * + */ +class TRANTOR_EXPORT TcpClient : NonCopyable +{ + public: + /** + * @brief Construct a new TCP client instance. + * + * @param loop The event loop in which the client runs. + * @param serverAddr The address of the server. + * @param nameArg The name of the client. + */ + TcpClient(EventLoop *loop, + const InetAddress &serverAddr, + const std::string &nameArg); + ~TcpClient(); + + /** + * @brief Connect to the server. + * + */ + void connect(); + + /** + * @brief Disconnect from the server. + * + */ + void disconnect(); + + /** + * @brief Stop connecting to the server. + * + */ + void stop(); + + /** + * @brief Get the TCP connection to the server. + * + * @return TcpConnectionPtr + */ + TcpConnectionPtr connection() const + { + std::lock_guard lock(mutex_); + return connection_; + } + + /** + * @brief Get the event loop. + * + * @return EventLoop* + */ + EventLoop *getLoop() const + { + return loop_; + } + + /** + * @brief Check whether the client re-connect to the server. + * + * @return true + * @return false + */ + bool retry() const + { + return retry_; + } + + /** + * @brief Enable retrying. + * + */ + void enableRetry() + { + retry_ = true; + } + + /** + * @brief Get the name of the client. + * + * @return const std::string& + */ + const std::string &name() const + { + return name_; + } + + /** + * @brief Set the connection callback. + * + * @param cb The callback is called when the connection to the server is + * established or closed. + */ + void setConnectionCallback(const ConnectionCallback &cb) + { + connectionCallback_ = cb; + } + void setConnectionCallback(ConnectionCallback &&cb) + { + connectionCallback_ = std::move(cb); + } + + /** + * @brief Set the connection error callback. + * + * @param cb The callback is called when an error occurs during connecting + * to the server. + */ + void setConnectionErrorCallback(const ConnectionErrorCallback &cb) + { + connectionErrorCallback_ = cb; + } + + /** + * @brief Set the message callback. + * + * @param cb The callback is called when some data is received from the + * server. + */ + void setMessageCallback(const RecvMessageCallback &cb) + { + messageCallback_ = cb; + } + void setMessageCallback(RecvMessageCallback &&cb) + { + messageCallback_ = std::move(cb); + } + /// Set write complete callback. + /// Not thread safe. + + /** + * @brief Set the write complete callback. + * + * @param cb The callback is called when data to send is written to the + * socket. + */ + void setWriteCompleteCallback(const WriteCompleteCallback &cb) + { + writeCompleteCallback_ = cb; + } + void setWriteCompleteCallback(WriteCompleteCallback &&cb) + { + writeCompleteCallback_ = std::move(cb); + } + + /** + * @brief Set the callback for errors of SSL + * @param cb The callback is called when an SSL error occurs. + */ + void setSSLErrorCallback(const SSLErrorCallback &cb) + { + sslErrorCallback_ = cb; + } + void setSSLErrorCallback(SSLErrorCallback &&cb) + { + sslErrorCallback_ = std::move(cb); + } + + /** + * @brief Enable SSL encryption. + * @param useOldTLS If true, the TLS 1.0 and 1.1 are supported by the + * client. + * @param validateCert If true, we try to validate if the peer's SSL cert + * is valid. + * @param hostname The server hostname for SNI. If it is empty, the SNI is + * not used. + * @param sslConfCmds The commands used to call the SSL_CONF_cmd function in + * OpenSSL. + * @note It's well known that TLS 1.0 and 1.1 are not considered secure in + * 2020. And it's a good practice to only use TLS 1.2 and above. + */ + void enableSSL(bool useOldTLS = false, + bool validateCert = true, + std::string hostname = "", + const std::vector> + &sslConfCmds = {}); + + private: + /// Not thread safe, but in loop + void newConnection(int sockfd); + /// Not thread safe, but in loop + void removeConnection(const TcpConnectionPtr &conn); + + EventLoop *loop_; + ConnectorPtr connector_; // avoid revealing Connector + const std::string name_; + ConnectionCallback connectionCallback_; + ConnectionErrorCallback connectionErrorCallback_; + RecvMessageCallback messageCallback_; + WriteCompleteCallback writeCompleteCallback_; + SSLErrorCallback sslErrorCallback_; + std::atomic_bool retry_; // atomic + std::atomic_bool connect_; // atomic + // always in loop thread + mutable std::mutex mutex_; + TcpConnectionPtr connection_; // @GuardedBy mutex_ + std::shared_ptr sslCtxPtr_; + bool validateCert_{false}; + std::string SSLHostName_; +#ifndef _WIN32 + class IgnoreSigPipe + { + public: + IgnoreSigPipe() + { + ::signal(SIGPIPE, SIG_IGN); + } + }; + + static IgnoreSigPipe initObj; +#endif +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/TcpConnection.h b/modules/http_trantor/trantor/net/TcpConnection.h new file mode 100644 index 0000000..90f2446 --- /dev/null +++ b/modules/http_trantor/trantor/net/TcpConnection.h @@ -0,0 +1,261 @@ +/** + * + * @file TcpConnection.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +class SSLContext; +TRANTOR_EXPORT std::shared_ptr newSSLServerContext( + const std::string &certPath, + const std::string &keyPath, + bool useOldTLS = false, + const std::vector> &sslConfCmds = {}); +/** + * @brief This class represents a TCP connection. + * + */ +class TRANTOR_EXPORT TcpConnection +{ + public: + TcpConnection() = default; + virtual ~TcpConnection(){}; + + /** + * @brief Send some data to the peer. + * + * @param msg + * @param len + */ + virtual void send(const char *msg, size_t len) = 0; + virtual void send(const void *msg, size_t len) = 0; + virtual void send(const std::string &msg) = 0; + virtual void send(std::string &&msg) = 0; + virtual void send(const MsgBuffer &buffer) = 0; + virtual void send(MsgBuffer &&buffer) = 0; + virtual void send(const std::shared_ptr &msgPtr) = 0; + virtual void send(const std::shared_ptr &msgPtr) = 0; + + /** + * @brief Send a file to the peer. + * + * @param fileName + * @param offset + * @param length + */ + virtual void sendFile(const char *fileName, + size_t offset = 0, + size_t length = 0) = 0; + + /** + * @brief Get the local address of the connection. + * + * @return const InetAddress& + */ + virtual const InetAddress &localAddr() const = 0; + + /** + * @brief Get the remote address of the connection. + * + * @return const InetAddress& + */ + virtual const InetAddress &peerAddr() const = 0; + + /** + * @brief Return true if the connection is established. + * + * @return true + * @return false + */ + virtual bool connected() const = 0; + + /** + * @brief Return false if the connection is established. + * + * @return true + * @return false + */ + virtual bool disconnected() const = 0; + + /** + * @brief Get the buffer in which the received data stored. + * + * @return MsgBuffer* + */ + virtual MsgBuffer *getRecvBuffer() = 0; + + /** + * @brief Set the high water mark callback + * + * @param cb The callback is called when the data in sending buffer is + * larger than the water mark. + * @param markLen The water mark in bytes. + */ + virtual void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, + size_t markLen) = 0; + + /** + * @brief Set the TCP_NODELAY option to the socket. + * + * @param on + */ + virtual void setTcpNoDelay(bool on) = 0; + + /** + * @brief Shutdown the connection. + * @note This method only closes the writing direction. + */ + virtual void shutdown() = 0; + + /** + * @brief Close the connection forcefully. + * + */ + virtual void forceClose() = 0; + + /** + * @brief Get the event loop in which the connection I/O is handled. + * + * @return EventLoop* + */ + virtual EventLoop *getLoop() = 0; + + /** + * @brief Set the custom data on the connection. + * + * @param context + */ + void setContext(const std::shared_ptr &context) + { + contextPtr_ = context; + } + void setContext(std::shared_ptr &&context) + { + contextPtr_ = std::move(context); + } + + /** + * @brief Get the custom data from the connection. + * + * @tparam T + * @return std::shared_ptr + */ + template + std::shared_ptr getContext() const + { + return std::static_pointer_cast(contextPtr_); + } + + /** + * @brief Return true if the custom data is set by user. + * + * @return true + * @return false + */ + bool hasContext() const + { + return (bool)contextPtr_; + } + + /** + * @brief Clear the custom data. + * + */ + void clearContext() + { + contextPtr_.reset(); + } + + /** + * @brief Call this method to avoid being kicked off by TcpServer, refer to + * the kickoffIdleConnections method in the TcpServer class. + * + */ + virtual void keepAlive() = 0; + + /** + * @brief Return true if the keepAlive() method is called. + * + * @return true + * @return false + */ + virtual bool isKeepAlive() = 0; + + /** + * @brief Return the number of bytes sent + * + * @return size_t + */ + virtual size_t bytesSent() const = 0; + + /** + * @brief Return the number of bytes received. + * + * @return size_t + */ + virtual size_t bytesReceived() const = 0; + + /** + * @brief Check whether the connection is SSL encrypted. + * + * @return true + * @return false + */ + virtual bool isSSLConnection() const = 0; + + /** + * @brief Start the SSL encryption on the connection (as a client). + * + * @param callback The callback is called when the SSL connection is + * established. + * @param hostname The server hostname for SNI. If it is empty, the SNI is + * not used. + * @param sslConfCmds The commands used to call the SSL_CONF_cmd function in + * OpenSSL. + */ + virtual void startClientEncryption( + std::function callback, + bool useOldTLS = false, + bool validateCert = true, + std::string hostname = "", + const std::vector> &sslConfCmds = + {}) = 0; + + /** + * @brief Start the SSL encryption on the connection (as a server). + * + * @param ctx The SSL context. + * @param callback The callback is called when the SSL connection is + * established. + */ + virtual void startServerEncryption(const std::shared_ptr &ctx, + std::function callback) = 0; + + protected: + bool validateCert_ = false; + + private: + std::shared_ptr contextPtr_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/TcpServer.cc b/modules/http_trantor/trantor/net/TcpServer.cc new file mode 100644 index 0000000..d67488b --- /dev/null +++ b/modules/http_trantor/trantor/net/TcpServer.cc @@ -0,0 +1,201 @@ +/** + * + * @file TcpServer.cc + * @author An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/trantor + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * Trantor + * + */ + +#include "Acceptor.h" +#include "inner/TcpConnectionImpl.h" +#include +#include +#include +#include +using namespace trantor; +using namespace std::placeholders; + +TcpServer::TcpServer(EventLoop *loop, + const InetAddress &address, + const std::string &name, + bool reUseAddr, + bool reUsePort) + : loop_(loop), + acceptorPtr_(new Acceptor(loop, address, reUseAddr, reUsePort)), + serverName_(name), + recvMessageCallback_([](const TcpConnectionPtr &, MsgBuffer *buffer) { + LOG_ERROR << "unhandled recv message [" << buffer->readableBytes() + << " bytes]"; + buffer->retrieveAll(); + }) +{ + acceptorPtr_->setNewConnectionCallback( + std::bind(&TcpServer::newConnection, this, _1, _2)); +} + +TcpServer::~TcpServer() +{ + // loop_->assertInLoopThread(); + LOG_TRACE << "TcpServer::~TcpServer [" << serverName_ << "] destructing"; +} + +void TcpServer::newConnection(int sockfd, const InetAddress &peer) +{ + LOG_TRACE << "new connection:fd=" << sockfd + << " address=" << peer.toIpPort(); + // test code for blocking or nonblocking + // std::vector str(1024*1024*100); + // for(int i=0;iassertInLoopThread(); + EventLoop *ioLoop = NULL; + if (loopPoolPtr_ && loopPoolPtr_->size() > 0) + { + ioLoop = loopPoolPtr_->getNextLoop(); + } + if (ioLoop == NULL) + ioLoop = loop_; + std::shared_ptr newPtr; + if (sslCtxPtr_) + { +#ifdef USE_OPENSSL + newPtr = std::make_shared( + ioLoop, + sockfd, + InetAddress(Socket::getLocalAddr(sockfd)), + peer, + sslCtxPtr_); +#else + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#endif + } + else + { + newPtr = std::make_shared( + ioLoop, sockfd, InetAddress(Socket::getLocalAddr(sockfd)), peer); + } + + if (idleTimeout_ > 0) + { + assert(timingWheelMap_[ioLoop]); + newPtr->enableKickingOff(idleTimeout_, timingWheelMap_[ioLoop]); + } + newPtr->setRecvMsgCallback(recvMessageCallback_); + + newPtr->setConnectionCallback( + [this](const TcpConnectionPtr &connectionPtr) { + if (connectionCallback_) + connectionCallback_(connectionPtr); + }); + newPtr->setWriteCompleteCallback( + [this](const TcpConnectionPtr &connectionPtr) { + if (writeCompleteCallback_) + writeCompleteCallback_(connectionPtr); + }); + newPtr->setCloseCallback(std::bind(&TcpServer::connectionClosed, this, _1)); + connSet_.insert(newPtr); + newPtr->connectEstablished(); +} + +void TcpServer::start() +{ + loop_->runInLoop([this]() { + assert(!started_); + started_ = true; + if (idleTimeout_ > 0) + { + timingWheelMap_[loop_] = + std::make_shared(loop_, + idleTimeout_, + 1.0F, + idleTimeout_ < 500 + ? idleTimeout_ + 1 + : 100); + if (loopPoolPtr_) + { + auto loopNum = loopPoolPtr_->size(); + while (loopNum > 0) + { + // LOG_TRACE << "new Wheel loopNum=" << loopNum; + auto poolLoop = loopPoolPtr_->getNextLoop(); + timingWheelMap_[poolLoop] = + std::make_shared(poolLoop, + idleTimeout_, + 1.0F, + idleTimeout_ < 500 + ? idleTimeout_ + 1 + : 100); + --loopNum; + } + } + } + LOG_TRACE << "map size=" << timingWheelMap_.size(); + acceptorPtr_->listen(); + }); +} +void TcpServer::stop() +{ + loop_->runInLoop([this]() { acceptorPtr_.reset(); }); + for (auto connection : connSet_) + { + connection->forceClose(); + } + loopPoolPtr_.reset(); + for (auto &iter : timingWheelMap_) + { + std::promise pro; + auto f = pro.get_future(); + iter.second->getLoop()->runInLoop([&iter, &pro]() mutable { + iter.second.reset(); + pro.set_value(1); + }); + f.get(); + } +} +void TcpServer::connectionClosed(const TcpConnectionPtr &connectionPtr) +{ + LOG_TRACE << "connectionClosed"; + // loop_->assertInLoopThread(); + loop_->runInLoop([this, connectionPtr]() { + size_t n = connSet_.erase(connectionPtr); + (void)n; + assert(n == 1); + }); + + static_cast(connectionPtr.get())->connectDestroyed(); +} + +const std::string TcpServer::ipPort() const +{ + return acceptorPtr_->addr().toIpPort(); +} + +const trantor::InetAddress &TcpServer::address() const +{ + return acceptorPtr_->addr(); +} + +void TcpServer::enableSSL( + const std::string &certPath, + const std::string &keyPath, + bool useOldTLS, + const std::vector> &sslConfCmds) +{ +#ifdef USE_OPENSSL + /* Create a new OpenSSL context */ + sslCtxPtr_ = newSSLServerContext(certPath, keyPath, useOldTLS, sslConfCmds); +#else + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#endif +} diff --git a/modules/http_trantor/trantor/net/TcpServer.h b/modules/http_trantor/trantor/net/TcpServer.h new file mode 100644 index 0000000..194fbb2 --- /dev/null +++ b/modules/http_trantor/trantor/net/TcpServer.h @@ -0,0 +1,250 @@ +/** + * + * @file TcpServer.h + * @author An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/trantor + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * Trantor + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace trantor +{ +class Acceptor; +class SSLContext; +/** + * @brief This class represents a TCP server. + * + */ +class TRANTOR_EXPORT TcpServer : NonCopyable +{ + public: + /** + * @brief Construct a new TCP server instance. + * + * @param loop The event loop in which the acceptor of the server is + * handled. + * @param address The address of the server. + * @param name The name of the server. + * @param reUseAddr The SO_REUSEADDR option. + * @param reUsePort The SO_REUSEPORT option. + */ + TcpServer(EventLoop *loop, + const InetAddress &address, + const std::string &name, + bool reUseAddr = true, + bool reUsePort = true); + ~TcpServer(); + + /** + * @brief Start the server. + * + */ + void start(); + + /** + * @brief Stop the server. + * + */ + void stop(); + + /** + * @brief Set the number of event loops in which the I/O of connections to + * the server is handled. + * + * @param num + */ + void setIoLoopNum(size_t num) + { + assert(!started_); + loopPoolPtr_ = std::make_shared(num); + loopPoolPtr_->start(); + } + + /** + * @brief Set the event loops pool in which the I/O of connections to + * the server is handled. + * + * @param pool + */ + void setIoLoopThreadPool(const std::shared_ptr &pool) + { + assert(pool->size() > 0); + assert(!started_); + loopPoolPtr_ = pool; + loopPoolPtr_->start(); + } + + /** + * @brief Set the message callback. + * + * @param cb The callback is called when some data is received on a + * connection to the server. + */ + void setRecvMessageCallback(const RecvMessageCallback &cb) + { + recvMessageCallback_ = cb; + } + void setRecvMessageCallback(RecvMessageCallback &&cb) + { + recvMessageCallback_ = std::move(cb); + } + + /** + * @brief Set the connection callback. + * + * @param cb The callback is called when a connection is established or + * closed. + */ + void setConnectionCallback(const ConnectionCallback &cb) + { + connectionCallback_ = cb; + } + void setConnectionCallback(ConnectionCallback &&cb) + { + connectionCallback_ = std::move(cb); + } + + /** + * @brief Set the write complete callback. + * + * @param cb The callback is called when data to send is written to the + * socket of a connection. + */ + void setWriteCompleteCallback(const WriteCompleteCallback &cb) + { + writeCompleteCallback_ = cb; + } + void setWriteCompleteCallback(WriteCompleteCallback &&cb) + { + writeCompleteCallback_ = std::move(cb); + } + + /** + * @brief Get the name of the server. + * + * @return const std::string& + */ + const std::string &name() const + { + return serverName_; + } + + /** + * @brief Get the IP and port string of the server. + * + * @return const std::string + */ + const std::string ipPort() const; + + /** + * @brief Get the address of the server. + * + * @return const trantor::InetAddress& + */ + const trantor::InetAddress &address() const; + + /** + * @brief Get the event loop of the server. + * + * @return EventLoop* + */ + EventLoop *getLoop() const + { + return loop_; + } + + /** + * @brief Get the I/O event loops of the server. + * + * @return std::vector + */ + std::vector getIoLoops() const + { + return loopPoolPtr_->getLoops(); + } + + /** + * @brief An idle connection is a connection that has no read or write, kick + * off it after timeout seconds. + * + * @param timeout + */ + void kickoffIdleConnections(size_t timeout) + { + loop_->runInLoop([this, timeout]() { + assert(!started_); + idleTimeout_ = timeout; + }); + } + + /** + * @brief Enable SSL encryption. + * + * @param certPath The path of the certificate file. + * @param keyPath The path of the private key file. + * @param useOldTLS If true, the TLS 1.0 and 1.1 are supported by the + * server. + * @param sslConfCmds The commands used to call the SSL_CONF_cmd function in + * OpenSSL. + * @note It's well known that TLS 1.0 and 1.1 are not considered secure in + * 2020. And it's a good practice to only use TLS 1.2 and above. + */ + void enableSSL(const std::string &certPath, + const std::string &keyPath, + bool useOldTLS = false, + const std::vector> + &sslConfCmds = {}); + + private: + EventLoop *loop_; + std::unique_ptr acceptorPtr_; + void newConnection(int fd, const InetAddress &peer); + std::string serverName_; + std::set connSet_; + + RecvMessageCallback recvMessageCallback_; + ConnectionCallback connectionCallback_; + WriteCompleteCallback writeCompleteCallback_; + + size_t idleTimeout_{0}; + std::map> timingWheelMap_; + void connectionClosed(const TcpConnectionPtr &connectionPtr); + std::shared_ptr loopPoolPtr_; +#ifndef _WIN32 + class IgnoreSigPipe + { + public: + IgnoreSigPipe() + { + ::signal(SIGPIPE, SIG_IGN); + LOG_TRACE << "Ignore SIGPIPE"; + } + }; + + IgnoreSigPipe initObj; +#endif + bool started_{false}; + + // OpenSSL SSL context Object; + std::shared_ptr sslCtxPtr_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/callbacks.h b/modules/http_trantor/trantor/net/callbacks.h new file mode 100644 index 0000000..f737870 --- /dev/null +++ b/modules/http_trantor/trantor/net/callbacks.h @@ -0,0 +1,43 @@ +/** + * + * callbacks.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +namespace trantor +{ +enum class SSLError +{ + kSSLHandshakeError, + kSSLInvalidCertificate +}; +using TimerCallback = std::function; + +// the data has been read to (buf, len) +class TcpConnection; +class MsgBuffer; +using TcpConnectionPtr = std::shared_ptr; +// tcp server and connection callback +using RecvMessageCallback = + std::function; +using ConnectionErrorCallback = std::function; +using ConnectionCallback = std::function; +using CloseCallback = std::function; +using WriteCompleteCallback = std::function; +using HighWaterMarkCallback = + std::function; +using SSLErrorCallback = std::function; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/Acceptor.cc b/modules/http_trantor/trantor/net/inner/Acceptor.cc new file mode 100644 index 0000000..6186b94 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Acceptor.cc @@ -0,0 +1,97 @@ +/** + * + * Acceptor.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Acceptor.h" +using namespace trantor; + +#ifndef O_CLOEXEC +#define O_CLOEXEC O_NOINHERIT +#endif + +Acceptor::Acceptor(EventLoop *loop, + const InetAddress &addr, + bool reUseAddr, + bool reUsePort) + + : +#ifndef _WIN32 + idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)), +#endif + sock_( + Socket::createNonblockingSocketOrDie(addr.getSockAddr()->sa_family)), + addr_(addr), + loop_(loop), + acceptChannel_(loop, sock_.fd()) +{ + sock_.setReuseAddr(reUseAddr); + sock_.setReusePort(reUsePort); + sock_.bindAddress(addr_); + acceptChannel_.setReadCallback(std::bind(&Acceptor::readCallback, this)); + if (addr_.toPort() == 0) + { + addr_ = InetAddress{Socket::getLocalAddr(sock_.fd())}; + } +} +Acceptor::~Acceptor() +{ + acceptChannel_.disableAll(); + acceptChannel_.remove(); +#ifndef _WIN32 + ::close(idleFd_); +#endif +} +void Acceptor::listen() +{ + loop_->assertInLoopThread(); + sock_.listen(); + acceptChannel_.enableReading(); +} + +void Acceptor::readCallback() +{ + InetAddress peer; + int newsock = sock_.accept(&peer); + if (newsock >= 0) + { + if (newConnectionCallback_) + { + newConnectionCallback_(newsock, peer); + } + else + { +#ifndef _WIN32 + ::close(newsock); +#else + closesocket(newsock); +#endif + } + } + else + { + LOG_SYSERR << "Accpetor::readCallback"; +// Read the section named "The special problem of +// accept()ing when you can't" in libev's doc. +// By Marc Lehmann, author of libev. +/// errno is thread safe +#ifndef _WIN32 + if (errno == EMFILE) + { + ::close(idleFd_); + idleFd_ = sock_.accept(&peer); + ::close(idleFd_); + idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); + } +#endif + } +} diff --git a/modules/http_trantor/trantor/net/inner/Acceptor.h b/modules/http_trantor/trantor/net/inner/Acceptor.h new file mode 100644 index 0000000..62ec421 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Acceptor.h @@ -0,0 +1,56 @@ +/** + * + * Acceptor.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include "Socket.h" +#include +#include "Channel.h" +#include + +namespace trantor +{ +using NewConnectionCallback = std::function; +class Acceptor : NonCopyable +{ + public: + Acceptor(EventLoop *loop, + const InetAddress &addr, + bool reUseAddr = true, + bool reUsePort = true); + ~Acceptor(); + const InetAddress &addr() const + { + return addr_; + } + void setNewConnectionCallback(const NewConnectionCallback &cb) + { + newConnectionCallback_ = cb; + }; + void listen(); + + protected: +#ifndef _WIN32 + int idleFd_; +#endif + Socket sock_; + InetAddress addr_; + EventLoop *loop_; + NewConnectionCallback newConnectionCallback_; + Channel acceptChannel_; + void readCallback(); +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/AresResolver.cc b/modules/http_trantor/trantor/net/inner/AresResolver.cc new file mode 100644 index 0000000..3b77b44 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/AresResolver.cc @@ -0,0 +1,258 @@ +// Copyright 2016, Tao An. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#include "AresResolver.h" +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include // inet_ntop +#include +#endif +#include +#include +#include + +using namespace trantor; +using namespace std::placeholders; + +namespace +{ +double getSeconds(struct timeval* tv) +{ + if (tv) + return double(tv->tv_sec) + double(tv->tv_usec) / 1000000.0; + else + return -1.0; +} + +const char* getSocketType(int type) +{ + if (type == SOCK_DGRAM) + return "UDP"; + else if (type == SOCK_STREAM) + return "TCP"; + else + return "Unknown"; +} + +} // namespace + +bool Resolver::isCAresUsed() +{ + return true; +} + +AresResolver::LibraryInitializer::LibraryInitializer() +{ + ares_library_init(ARES_LIB_INIT_ALL); +} +AresResolver::LibraryInitializer::~LibraryInitializer() +{ + ares_library_cleanup(); +} + +AresResolver::LibraryInitializer AresResolver::libraryInitializer_; + +std::shared_ptr Resolver::newResolver(trantor::EventLoop* loop, + size_t timeout) +{ + return std::make_shared(loop, timeout); +} + +AresResolver::AresResolver(EventLoop* loop, size_t timeout) + : loop_(loop), timeout_(timeout) +{ + if (!loop) + { + loop_ = getLoop(); + } +} +void AresResolver::init() +{ + if (!ctx_) + { + struct ares_options options; + int optmask = ARES_OPT_FLAGS; + options.flags = ARES_FLAG_NOCHECKRESP; + options.flags |= ARES_FLAG_STAYOPEN; + options.flags |= ARES_FLAG_IGNTC; // UDP only + optmask |= ARES_OPT_SOCK_STATE_CB; + options.sock_state_cb = &AresResolver::ares_sock_statecallback_; + options.sock_state_cb_data = this; + optmask |= ARES_OPT_TIMEOUT; + options.timeout = 2; + // optmask |= ARES_OPT_LOOKUPS; + // options.lookups = lookups; + + int status = ares_init_options(&ctx_, &options, optmask); + if (status != ARES_SUCCESS) + { + assert(0); + } + ares_set_socket_callback(ctx_, + &AresResolver::ares_sock_createcallback_, + this); + } +} +AresResolver::~AresResolver() +{ + if (ctx_) + ares_destroy(ctx_); +} + +void AresResolver::resolveInLoop(const std::string& hostname, + const Callback& cb) +{ + loop_->assertInLoopThread(); +#ifdef _WIN32 + if (hostname == "localhost") + { + const static trantor::InetAddress localhost_{"127.0.0.1", 0}; + cb(localhost_); + return; + } +#endif + init(); + QueryData* queryData = new QueryData(this, cb, hostname); + ares_gethostbyname(ctx_, + hostname.c_str(), + AF_INET, + &AresResolver::ares_hostcallback_, + queryData); + struct timeval tv; + struct timeval* tvp = ares_timeout(ctx_, NULL, &tv); + double timeout = getSeconds(tvp); + // LOG_DEBUG << "timeout " << timeout << " active " << timerActive_; + if (!timerActive_ && timeout >= 0.0) + { + loop_->runAfter(timeout, + std::bind(&AresResolver::onTimer, shared_from_this())); + timerActive_ = true; + } + return; +} + +void AresResolver::onRead(int sockfd) +{ + ares_process_fd(ctx_, sockfd, ARES_SOCKET_BAD); +} + +void AresResolver::onTimer() +{ + assert(timerActive_ == true); + ares_process_fd(ctx_, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + struct timeval tv; + struct timeval* tvp = ares_timeout(ctx_, NULL, &tv); + double timeout = getSeconds(tvp); + + if (timeout < 0) + { + timerActive_ = false; + } + else + { + loop_->runAfter(timeout, + std::bind(&AresResolver::onTimer, shared_from_this())); + } +} + +void AresResolver::onQueryResult(int status, + struct hostent* result, + const std::string& hostname, + const Callback& callback) +{ + LOG_TRACE << "onQueryResult " << status; + struct sockaddr_in addr; + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = 0; + if (result) + { + addr.sin_addr = *reinterpret_cast(result->h_addr); + } + InetAddress inet(addr); + { + std::lock_guard lock(globalMutex()); + auto& addrItem = globalCache()[hostname]; + addrItem.first = addr.sin_addr; + addrItem.second = trantor::Date::date(); + } + callback(inet); +} + +void AresResolver::onSockCreate(int sockfd, int type) +{ + (void)type; + loop_->assertInLoopThread(); + assert(channels_.find(sockfd) == channels_.end()); + Channel* channel = new Channel(loop_, sockfd); + channel->setReadCallback(std::bind(&AresResolver::onRead, this, sockfd)); + channel->enableReading(); + channels_[sockfd].reset(channel); +} + +void AresResolver::onSockStateChange(int sockfd, bool read, bool write) +{ + (void)write; + loop_->assertInLoopThread(); + ChannelList::iterator it = channels_.find(sockfd); + assert(it != channels_.end()); + if (read) + { + // update + // if (write) { } else { } + } + else + { + // remove + it->second->disableAll(); + it->second->remove(); + channels_.erase(it); + } +} + +void AresResolver::ares_hostcallback_(void* data, + int status, + int timeouts, + struct hostent* hostent) +{ + (void)timeouts; + QueryData* query = static_cast(data); + + query->owner_->onQueryResult(status, + hostent, + query->hostname_, + query->callback_); + delete query; +} + +#ifdef _WIN32 +int AresResolver::ares_sock_createcallback_(SOCKET sockfd, int type, void* data) +#else +int AresResolver::ares_sock_createcallback_(int sockfd, int type, void* data) +#endif +{ + LOG_TRACE << "sockfd=" << sockfd << " type=" << getSocketType(type); + static_cast(data)->onSockCreate(sockfd, type); + return 0; +} + +void AresResolver::ares_sock_statecallback_(void* data, +#ifdef _WIN32 + SOCKET sockfd, +#else + int sockfd, +#endif + int read, + int write) +{ + LOG_TRACE << "sockfd=" << sockfd << " read=" << read << " write=" << write; + static_cast(data)->onSockStateChange(sockfd, read, write); +} diff --git a/modules/http_trantor/trantor/net/inner/AresResolver.h b/modules/http_trantor/trantor/net/inner/AresResolver.h new file mode 100644 index 0000000..6f4adac --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/AresResolver.h @@ -0,0 +1,148 @@ +// Copyright 2016, Tao An. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#pragma once +#include +#include +#include +#include +#include +#include + +extern "C" +{ + struct hostent; + struct ares_channeldata; + using ares_channel = struct ares_channeldata*; +} +namespace trantor +{ +class AresResolver : public Resolver, + public NonCopyable, + public std::enable_shared_from_this +{ + public: + AresResolver(trantor::EventLoop* loop, size_t timeout); + ~AresResolver(); + + virtual void resolve(const std::string& hostname, + const Callback& cb) override + { + bool cached = false; + InetAddress inet; + { + std::lock_guard lock(globalMutex()); + auto iter = globalCache().find(hostname); + if (iter != globalCache().end()) + { + auto& cachedAddr = iter->second; + if (timeout_ == 0 || + cachedAddr.second.after(timeout_) > trantor::Date::date()) + { + struct sockaddr_in addr; + memset(&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr = cachedAddr.first; + inet = InetAddress(addr); + cached = true; + } + } + } + if (cached) + { + cb(inet); + return; + } + if (loop_->isInLoopThread()) + { + resolveInLoop(hostname, cb); + } + else + { + loop_->queueInLoop([thisPtr = shared_from_this(), hostname, cb]() { + thisPtr->resolveInLoop(hostname, cb); + }); + } + } + + private: + struct QueryData + { + AresResolver* owner_; + Callback callback_; + std::string hostname_; + QueryData(AresResolver* o, + const Callback& cb, + const std::string& hostname) + : owner_(o), callback_(cb), hostname_(hostname) + { + } + }; + void resolveInLoop(const std::string& hostname, const Callback& cb); + void init(); + trantor::EventLoop* loop_; + ares_channel ctx_{nullptr}; + bool timerActive_{false}; + using ChannelList = std::map>; + ChannelList channels_; + static std::unordered_map>& + globalCache() + { + static std::unordered_map> + dnsCache; + return dnsCache; + } + static std::mutex& globalMutex() + { + static std::mutex mutex_; + return mutex_; + } + static EventLoop* getLoop() + { + static EventLoopThread loopThread; + loopThread.run(); + return loopThread.getLoop(); + } + const size_t timeout_{60}; + + void onRead(int sockfd); + void onTimer(); + void onQueryResult(int status, + struct hostent* result, + const std::string& hostname, + const Callback& callback); + void onSockCreate(int sockfd, int type); + void onSockStateChange(int sockfd, bool read, bool write); + + static void ares_hostcallback_(void* data, + int status, + int timeouts, + struct hostent* hostent); +#ifdef _WIN32 + static int ares_sock_createcallback_(SOCKET sockfd, int type, void* data); +#else + static int ares_sock_createcallback_(int sockfd, int type, void* data); +#endif + static void ares_sock_statecallback_(void* data, +#ifdef _WIN32 + SOCKET sockfd, +#else + int sockfd, +#endif + int read, + int write); + struct LibraryInitializer + { + LibraryInitializer(); + ~LibraryInitializer(); + }; + static LibraryInitializer libraryInitializer_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/Connector.cc b/modules/http_trantor/trantor/net/inner/Connector.cc new file mode 100644 index 0000000..1b70397 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Connector.cc @@ -0,0 +1,261 @@ +/** + * + * @file Connector.cc + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Connector.h" +#include "Channel.h" +#include "Socket.h" + +using namespace trantor; + +Connector::Connector(EventLoop *loop, const InetAddress &addr, bool retry) + : loop_(loop), serverAddr_(addr), retry_(retry) +{ +} +Connector::Connector(EventLoop *loop, InetAddress &&addr, bool retry) + : loop_(loop), serverAddr_(std::move(addr)), retry_(retry) +{ +} + +void Connector::start() +{ + connect_ = true; + loop_->runInLoop([this]() { startInLoop(); }); +} +void Connector::restart() +{ +} +void Connector::stop() +{ +} + +void Connector::startInLoop() +{ + loop_->assertInLoopThread(); + assert(status_ == Status::Disconnected); + if (connect_) + { + connect(); + } + else + { + LOG_DEBUG << "do not connect"; + } +} +void Connector::connect() +{ + int sockfd = Socket::createNonblockingSocketOrDie(serverAddr_.family()); + errno = 0; + int ret = Socket::connect(sockfd, serverAddr_); + int savedErrno = (ret == 0) ? 0 : errno; + switch (savedErrno) + { + case 0: + case EINPROGRESS: + case EINTR: + case EISCONN: + LOG_TRACE << "connecting"; + connecting(sockfd); + break; + + case EAGAIN: + case EADDRINUSE: + case EADDRNOTAVAIL: + case ECONNREFUSED: + case ENETUNREACH: + if (retry_) + { + retry(sockfd); + } + break; + + case EACCES: + case EPERM: + case EAFNOSUPPORT: + case EALREADY: + case EBADF: + case EFAULT: + case ENOTSOCK: + LOG_SYSERR << "connect error in Connector::startInLoop " + << savedErrno; +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + if (errorCallback_) + errorCallback_(); + break; + + default: + LOG_SYSERR << "Unexpected error in Connector::startInLoop " + << savedErrno; +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + if (errorCallback_) + errorCallback_(); + break; + } +} + +void Connector::connecting(int sockfd) +{ + status_ = Status::Connecting; + assert(!channelPtr_); + channelPtr_.reset(new Channel(loop_, sockfd)); + channelPtr_->setWriteCallback( + std::bind(&Connector::handleWrite, shared_from_this())); + channelPtr_->setErrorCallback( + std::bind(&Connector::handleError, shared_from_this())); + channelPtr_->setCloseCallback( + std::bind(&Connector::handleError, shared_from_this())); + LOG_TRACE << "connecting:" << sockfd; + channelPtr_->enableWriting(); +} + +int Connector::removeAndResetChannel() +{ + channelPtr_->disableAll(); + channelPtr_->remove(); + int sockfd = channelPtr_->fd(); + // Can't reset channel_ here, because we are inside Channel::handleEvent + loop_->queueInLoop([this]() { channelPtr_.reset(); }); + return sockfd; +} + +void Connector::handleWrite() +{ + if (status_ == Status::Connecting) + { + int sockfd = removeAndResetChannel(); + int err = Socket::getSocketError(sockfd); + if (err) + { + LOG_WARN << "Connector::handleWrite - SO_ERROR = " << err << " " + << strerror_tl(err); + if (retry_) + { + retry(sockfd); + } + else + { +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + } + if (errorCallback_) + { + errorCallback_(); + } + } + else if (Socket::isSelfConnect(sockfd)) + { + LOG_WARN << "Connector::handleWrite - Self connect"; + if (retry_) + { + retry(sockfd); + } + else + { +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + } + if (errorCallback_) + { + errorCallback_(); + } + } + else + { + status_ = Status::Connected; + if (connect_) + { + newConnectionCallback_(sockfd); + } + else + { +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + } + } + } + else + { + // what happened? + assert(status_ == Status::Disconnected); + } +} + +void Connector::handleError() +{ + if (status_ == Status::Connecting) + { + status_ = Status::Disconnected; + int sockfd = removeAndResetChannel(); + int err = Socket::getSocketError(sockfd); + LOG_TRACE << "SO_ERROR = " << err << " " << strerror_tl(err); + if (retry_) + { + retry(sockfd); + } + else + { +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + } + if (errorCallback_) + { + errorCallback_(); + } + } +} + +void Connector::retry(int sockfd) +{ + assert(retry_); +#ifndef _WIN32 + ::close(sockfd); +#else + closesocket(sockfd); +#endif + status_ = Status::Disconnected; + if (connect_) + { + LOG_INFO << "Connector::retry - Retry connecting to " + << serverAddr_.toIpPort() << " in " << retryInterval_ + << " milliseconds. "; + loop_->runAfter(retryInterval_ / 1000.0, + std::bind(&Connector::startInLoop, shared_from_this())); + retryInterval_ = retryInterval_ * 2; + if (retryInterval_ > maxRetryInterval_) + retryInterval_ = maxRetryInterval_; + } + else + { + LOG_DEBUG << "do not connect"; + } +} diff --git a/modules/http_trantor/trantor/net/inner/Connector.h b/modules/http_trantor/trantor/net/inner/Connector.h new file mode 100644 index 0000000..2c943ba --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Connector.h @@ -0,0 +1,88 @@ +/** + * + * Connector.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace trantor +{ +class Connector : public NonCopyable, + public std::enable_shared_from_this +{ + public: + using NewConnectionCallback = std::function; + using ConnectionErrorCallback = std::function; + Connector(EventLoop *loop, const InetAddress &addr, bool retry = true); + Connector(EventLoop *loop, InetAddress &&addr, bool retry = true); + void setNewConnectionCallback(const NewConnectionCallback &cb) + { + newConnectionCallback_ = cb; + } + void setNewConnectionCallback(NewConnectionCallback &&cb) + { + newConnectionCallback_ = std::move(cb); + } + void setErrorCallback(const ConnectionErrorCallback &cb) + { + errorCallback_ = cb; + } + void setErrorCallback(ConnectionErrorCallback &&cb) + { + errorCallback_ = std::move(cb); + } + const InetAddress &serverAddress() const + { + return serverAddr_; + } + void start(); + void restart(); + void stop(); + + private: + NewConnectionCallback newConnectionCallback_; + ConnectionErrorCallback errorCallback_; + enum class Status + { + Disconnected, + Connecting, + Connected + }; + static constexpr int kMaxRetryDelayMs = 30 * 1000; + static constexpr int kInitRetryDelayMs = 500; + std::shared_ptr channelPtr_; + EventLoop *loop_; + InetAddress serverAddr_; + + std::atomic_bool connect_{false}; + std::atomic status_{Status::Disconnected}; + + int retryInterval_{kInitRetryDelayMs}; + int maxRetryInterval_{kMaxRetryDelayMs}; + + bool retry_; + + void startInLoop(); + void connect(); + void connecting(int sockfd); + int removeAndResetChannel(); + void handleWrite(); + void handleError(); + void retry(int sockfd); +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/NormalResolver.cc b/modules/http_trantor/trantor/net/inner/NormalResolver.cc new file mode 100644 index 0000000..87c2d7c --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/NormalResolver.cc @@ -0,0 +1,95 @@ +#include "NormalResolver.h" +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include // memset +#endif + +using namespace trantor; + +std::shared_ptr Resolver::newResolver(trantor::EventLoop *, + size_t timeout) +{ + return std::make_shared(timeout); +} +bool Resolver::isCAresUsed() +{ + return false; +} +void NormalResolver::resolve(const std::string &hostname, + const Callback &callback) +{ + { + std::lock_guard guard(globalMutex()); + auto iter = globalCache().find(hostname); + if (iter != globalCache().end()) + { + auto &cachedAddr = iter->second; + if (timeout_ == 0 || cachedAddr.second.after(static_cast( + timeout_)) > trantor::Date::date()) + { + callback(cachedAddr.first); + return; + } + } + } + + concurrentTaskQueue().runTaskInQueue( + [thisPtr = shared_from_this(), callback, hostname]() { + { + std::lock_guard guard(thisPtr->globalMutex()); + auto iter = thisPtr->globalCache().find(hostname); + if (iter != thisPtr->globalCache().end()) + { + auto &cachedAddr = iter->second; + if (thisPtr->timeout_ == 0 || + cachedAddr.second.after(static_cast( + thisPtr->timeout_)) > trantor::Date::date()) + { + callback(cachedAddr.first); + return; + } + } + } + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + auto error = getaddrinfo(hostname.data(), nullptr, &hints, &res); + if (error == -1 || res == nullptr) + { + LOG_SYSERR << "InetAddress::resolve"; + callback(InetAddress{}); + return; + } + InetAddress inet; + if (res->ai_family == AF_INET) + { + struct sockaddr_in addr; + memset(&addr, 0, sizeof addr); + addr = *reinterpret_cast(res->ai_addr); + inet = InetAddress(addr); + } + else if (res->ai_family == AF_INET6) + { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof addr); + addr = *reinterpret_cast(res->ai_addr); + inet = InetAddress(addr); + } + callback(inet); + { + std::lock_guard guard(thisPtr->globalMutex()); + auto &addrItem = thisPtr->globalCache()[hostname]; + addrItem.first = inet; + addrItem.second = trantor::Date::date(); + } + return; + }); +} diff --git a/modules/http_trantor/trantor/net/inner/NormalResolver.h b/modules/http_trantor/trantor/net/inner/NormalResolver.h new file mode 100644 index 0000000..bc19e0f --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/NormalResolver.h @@ -0,0 +1,62 @@ +// Copyright 2016, Tao An. All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the License file. + +// Author: Tao An + +#pragma once +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +constexpr size_t kResolveBufferLength{16 * 1024}; +class NormalResolver : public Resolver, + public NonCopyable, + public std::enable_shared_from_this +{ + public: + virtual void resolve(const std::string& hostname, + const Callback& callback) override; + explicit NormalResolver(size_t timeout) + : timeout_(timeout), resolveBuffer_(kResolveBufferLength) + { + } + virtual ~NormalResolver() + { + } + + private: + static std::unordered_map>& + globalCache() + { + static std::unordered_map< + std::string, + std::pair> + dnsCache_; + return dnsCache_; + } + static std::mutex& globalMutex() + { + static std::mutex mutex_; + return mutex_; + } + static trantor::ConcurrentTaskQueue& concurrentTaskQueue() + { + static trantor::ConcurrentTaskQueue queue( + std::thread::hardware_concurrency() < 8 + ? 8 + : std::thread::hardware_concurrency(), + "Dns Queue"); + return queue; + } + const size_t timeout_; + std::vector resolveBuffer_; +}; +} // namespace trantor \ No newline at end of file diff --git a/modules/http_trantor/trantor/net/inner/Poller.cc b/modules/http_trantor/trantor/net/inner/Poller.cc new file mode 100644 index 0000000..379236a --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Poller.cc @@ -0,0 +1,32 @@ +/** + * + * Poller.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Poller.h" +#ifdef __linux__ +#include "poller/EpollPoller.h" +#elif defined _WIN32 +#include "Wepoll.h" +#include "poller/EpollPoller.h" +#else +#include "poller/KQueue.h" +#endif +using namespace trantor; +Poller *Poller::newPoller(EventLoop *loop) +{ +#if defined __linux__ || defined _WIN32 + return new EpollPoller(loop); +#else + return new KQueue(loop); +#endif +} diff --git a/modules/http_trantor/trantor/net/inner/Poller.h b/modules/http_trantor/trantor/net/inner/Poller.h new file mode 100644 index 0000000..9261824 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Poller.h @@ -0,0 +1,54 @@ +/** + * + * Poller.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#include "NonCopyable.h" +#include "EventLoop.h" + +#include +#include + +namespace trantor +{ +class Channel; +#ifdef _WIN32 +using EventCallback = std::function; +#endif +class Poller : NonCopyable +{ + public: + explicit Poller(EventLoop *loop) : ownerLoop_(loop){}; + virtual ~Poller() + { + } + void assertInLoopThread() + { + ownerLoop_->assertInLoopThread(); + } + virtual void poll(int timeoutMs, ChannelList *activeChannels) = 0; + virtual void updateChannel(Channel *channel) = 0; + virtual void removeChannel(Channel *channel) = 0; +#ifdef _WIN32 + virtual void postEvent(uint64_t event) = 0; + virtual void setEventCallback(const EventCallback &cb) = 0; +#endif + virtual void resetAfterFork() + { + } + static Poller *newPoller(EventLoop *loop); + + private: + EventLoop *ownerLoop_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/Socket.cc b/modules/http_trantor/trantor/net/inner/Socket.cc new file mode 100644 index 0000000..c9eb58d --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Socket.cc @@ -0,0 +1,248 @@ +/** + * + * Socket.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include "Socket.h" +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +using namespace trantor; + +bool Socket::isSelfConnect(int sockfd) +{ + struct sockaddr_in6 localaddr = getLocalAddr(sockfd); + struct sockaddr_in6 peeraddr = getPeerAddr(sockfd); + if (localaddr.sin6_family == AF_INET) + { + const struct sockaddr_in *laddr4 = + reinterpret_cast(&localaddr); + const struct sockaddr_in *raddr4 = + reinterpret_cast(&peeraddr); + return laddr4->sin_port == raddr4->sin_port && + laddr4->sin_addr.s_addr == raddr4->sin_addr.s_addr; + } + else if (localaddr.sin6_family == AF_INET6) + { + return localaddr.sin6_port == peeraddr.sin6_port && + memcmp(&localaddr.sin6_addr, + &peeraddr.sin6_addr, + sizeof localaddr.sin6_addr) == 0; + } + else + { + return false; + } +} + +void Socket::bindAddress(const InetAddress &localaddr) +{ + assert(sockFd_ > 0); + int ret; + if (localaddr.isIpV6()) + ret = ::bind(sockFd_, localaddr.getSockAddr(), sizeof(sockaddr_in6)); + else + ret = ::bind(sockFd_, localaddr.getSockAddr(), sizeof(sockaddr_in)); + + if (ret == 0) + return; + else + { + LOG_SYSERR << ", Bind address failed at " << localaddr.toIpPort(); + exit(1); + } +} +void Socket::listen() +{ + assert(sockFd_ > 0); + int ret = ::listen(sockFd_, SOMAXCONN); + if (ret < 0) + { + LOG_SYSERR << "listen failed"; + exit(1); + } +} +int Socket::accept(InetAddress *peeraddr) +{ + struct sockaddr_in6 addr6; + memset(&addr6, 0, sizeof(addr6)); + socklen_t size = sizeof(addr6); +#ifdef __linux__ + int connfd = ::accept4(sockFd_, + (struct sockaddr *)&addr6, + &size, + SOCK_NONBLOCK | SOCK_CLOEXEC); +#else + int connfd = + static_cast(::accept(sockFd_, (struct sockaddr *)&addr6, &size)); + setNonBlockAndCloseOnExec(connfd); +#endif + if (connfd >= 0) + { + peeraddr->setSockAddrInet6(addr6); + } + return connfd; +} +void Socket::closeWrite() +{ +#ifndef _WIN32 + if (::shutdown(sockFd_, SHUT_WR) < 0) +#else + if (::shutdown(sockFd_, SD_SEND) < 0) +#endif + { + LOG_SYSERR << "sockets::shutdownWrite"; + } +} +int Socket::read(char *buffer, uint64_t len) +{ +#ifndef _WIN32 + return ::read(sockFd_, buffer, len); +#else + return recv(sockFd_, buffer, static_cast(len), 0); +#endif +} + +struct sockaddr_in6 Socket::getLocalAddr(int sockfd) +{ + struct sockaddr_in6 localaddr; + memset(&localaddr, 0, sizeof(localaddr)); + socklen_t addrlen = static_cast(sizeof localaddr); + if (::getsockname(sockfd, + static_cast((void *)(&localaddr)), + &addrlen) < 0) + { + LOG_SYSERR << "sockets::getLocalAddr"; + } + return localaddr; +} + +struct sockaddr_in6 Socket::getPeerAddr(int sockfd) +{ + struct sockaddr_in6 peeraddr; + memset(&peeraddr, 0, sizeof(peeraddr)); + socklen_t addrlen = static_cast(sizeof peeraddr); + if (::getpeername(sockfd, + static_cast((void *)(&peeraddr)), + &addrlen) < 0) + { + LOG_SYSERR << "sockets::getPeerAddr"; + } + return peeraddr; +} + +void Socket::setTcpNoDelay(bool on) +{ +#ifdef _WIN32 + char optval = on ? 1 : 0; +#else + int optval = on ? 1 : 0; +#endif + ::setsockopt(sockFd_, + IPPROTO_TCP, + TCP_NODELAY, + &optval, + static_cast(sizeof optval)); + // TODO CHECK +} + +void Socket::setReuseAddr(bool on) +{ +#ifdef _WIN32 + char optval = on ? 1 : 0; +#else + int optval = on ? 1 : 0; +#endif + ::setsockopt(sockFd_, + SOL_SOCKET, + SO_REUSEADDR, + &optval, + static_cast(sizeof optval)); + // TODO CHECK +} + +void Socket::setReusePort(bool on) +{ +#ifdef SO_REUSEPORT +#ifdef _WIN32 + char optval = on ? 1 : 0; +#else + int optval = on ? 1 : 0; +#endif + int ret = ::setsockopt(sockFd_, + SOL_SOCKET, + SO_REUSEPORT, + &optval, + static_cast(sizeof optval)); + if (ret < 0 && on) + { + LOG_SYSERR << "SO_REUSEPORT failed."; + } +#else + if (on) + { + LOG_ERROR << "SO_REUSEPORT is not supported."; + } +#endif +} + +void Socket::setKeepAlive(bool on) +{ +#ifdef _WIN32 + char optval = on ? 1 : 0; +#else + int optval = on ? 1 : 0; +#endif + ::setsockopt(sockFd_, + SOL_SOCKET, + SO_KEEPALIVE, + &optval, + static_cast(sizeof optval)); + // TODO CHECK +} + +int Socket::getSocketError() +{ +#ifdef _WIN32 + char optval; +#else + int optval; +#endif + socklen_t optlen = static_cast(sizeof optval); + + if (::getsockopt(sockFd_, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) + { + return errno; + } + else + { + return optval; + } +} + +Socket::~Socket() +{ + LOG_TRACE << "Socket deconstructed:" << sockFd_; + if (sockFd_ >= 0) +#ifndef _WIN32 + close(sockFd_); +#else + closesocket(sockFd_); +#endif +} diff --git a/modules/http_trantor/trantor/net/inner/Socket.h b/modules/http_trantor/trantor/net/inner/Socket.h new file mode 100644 index 0000000..8cce9dd --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Socket.h @@ -0,0 +1,157 @@ +/** + * + * Socket.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +namespace trantor +{ +class Socket : NonCopyable +{ + public: + static int createNonblockingSocketOrDie(int family) + { +#ifdef __linux__ + int sock = ::socket(family, + SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, + IPPROTO_TCP); +#else + int sock = static_cast(::socket(family, SOCK_STREAM, IPPROTO_TCP)); + setNonBlockAndCloseOnExec(sock); +#endif + if (sock < 0) + { + LOG_SYSERR << "sockets::createNonblockingOrDie"; + exit(1); + } + LOG_TRACE << "sock=" << sock; + return sock; + } + + static int getSocketError(int sockfd) + { + int optval; + socklen_t optlen = static_cast(sizeof optval); +#ifdef _WIN32 + if (::getsockopt( + sockfd, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen) < 0) +#else + if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) +#endif + { + return errno; + } + else + { + return optval; + } + } + + static int connect(int sockfd, const InetAddress &addr) + { + if (addr.isIpV6()) + return ::connect(sockfd, + addr.getSockAddr(), + static_cast( + sizeof(struct sockaddr_in6))); + else + return ::connect(sockfd, + addr.getSockAddr(), + static_cast( + sizeof(struct sockaddr_in))); + } + + static bool isSelfConnect(int sockfd); + + explicit Socket(int sockfd) : sockFd_(sockfd) + { + } + ~Socket(); + /// abort if address in use + void bindAddress(const InetAddress &localaddr); + /// abort if address in use + void listen(); + int accept(InetAddress *peeraddr); + void closeWrite(); + int read(char *buffer, uint64_t len); + int fd() + { + return sockFd_; + } + static struct sockaddr_in6 getLocalAddr(int sockfd); + static struct sockaddr_in6 getPeerAddr(int sockfd); + + /// + /// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). + /// + void setTcpNoDelay(bool on); + + /// + /// Enable/disable SO_REUSEADDR + /// + void setReuseAddr(bool on); + + /// + /// Enable/disable SO_REUSEPORT + /// + void setReusePort(bool on); + + /// + /// Enable/disable SO_KEEPALIVE + /// + void setKeepAlive(bool on); + int getSocketError(); + + protected: + int sockFd_; + + public: + // taken from muduo + static void setNonBlockAndCloseOnExec(int sockfd) + { +#ifdef _WIN32 + // TODO how to set FD_CLOEXEC on windows? is it necessary? + u_long arg = 1; + auto ret = ioctlsocket(sockfd, (long)FIONBIO, &arg); + if (ret) + { + LOG_ERROR << "ioctlsocket error"; + } +#else + // non-block + int flags = ::fcntl(sockfd, F_GETFL, 0); + flags |= O_NONBLOCK; + int ret = ::fcntl(sockfd, F_SETFL, flags); + // TODO check + + // close-on-exec + flags = ::fcntl(sockfd, F_GETFD, 0); + flags |= FD_CLOEXEC; + ret = ::fcntl(sockfd, F_SETFD, flags); + // TODO check + + (void)ret; +#endif + } +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.cc b/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.cc new file mode 100644 index 0000000..57ff1de --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.cc @@ -0,0 +1,1747 @@ +/** + * + * @file TcpConnectionImpl.cc + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "TcpConnectionImpl.h" +#include "Socket.h" +#include "Channel.h" +#ifdef __linux__ +#include +#endif +#include +#ifndef _WIN32 +#include +#else +#include +#include +#include +#endif +#include +#include +#ifdef USE_OPENSSL +#include +#include +#include +#endif +#ifdef _WIN32 +#define stat _stati64 +#endif +#include + +using namespace trantor; + +#ifdef USE_OPENSSL +namespace trantor +{ +namespace internal +{ +#ifdef _WIN32 +// Code yanked from stackoverflow +// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store +inline bool loadWindowsSystemCert(X509_STORE *store) +{ + auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT"); + + if (!hStore) + { + return false; + } + + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) != + nullptr) + { + auto encoded_cert = + static_cast(pContext->pbCertEncoded); + + auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if (x509) + { + X509_STORE_add_cert(store, x509); + X509_free(x509); + } + } + + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + return true; +} +#endif + +inline std::string certNameToRegex(const std::string &certName) +{ + std::string result; + result.reserve(certName.size() + 11); + + bool isStar = false; + bool isLeadingStar = true; + for (char ch : certName) + { + if (isStar == false) + { + if (ch == '*') + isStar = true; + else if (ch == '.') + { + result += "\\."; + isLeadingStar = false; + } + else + { + result.push_back(ch); + isLeadingStar = false; + } + } + else + { + if (ch == '.' && isLeadingStar) + result += "([^.]*\\.|)?"; + else + result += std::string("[^.]*") + ch; + isStar = false; + } + } + assert(isStar == false); + return result; +} + +inline bool verifyName(const std::string &certName, const std::string &hostname) +{ + std::regex re(certNameToRegex(certName)); + return std::regex_match(hostname, re); +} + +inline bool verifyCommonName(X509 *cert, const std::string &hostname) +{ + X509_NAME *subjectName = X509_get_subject_name(cert); + + if (subjectName != nullptr) + { + std::array name; + auto length = X509_NAME_get_text_by_NID(subjectName, + NID_commonName, + name.data(), + (int)name.size()); + if (length == -1) + return false; + + return verifyName(std::string(name.begin(), name.begin() + length), + hostname); + } + + return false; +} + +inline bool verifyAltName(X509 *cert, const std::string &hostname) +{ + bool good = false; + auto altNames = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + + if (altNames) + { + int numNames = sk_GENERAL_NAME_num(altNames); + + for (int i = 0; i < numNames && !good; i++) + { + auto val = sk_GENERAL_NAME_value(altNames, i); + if (val->type != GEN_DNS) + { + LOG_WARN << "Name using IP addresses are not supported. Open " + "an issue if you need that feature"; + continue; + } +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); +#else + auto name = (const char *)ASN1_STRING_data(val->d.ia5); +#endif + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); + good = verifyName(std::string(name, name + name_len), hostname); + } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)altNames); + return good; +} + +} // namespace internal + +void initOpenSSL() +{ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) + // Initialize OpenSSL once; + static std::once_flag once; + std::call_once(once, []() { + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + }); +#endif +} + +class SSLContext +{ + public: + explicit SSLContext( + bool useOldTLS, + bool enableValidtion, + const std::vector> &sslConfCmds) + { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + ctxPtr_ = SSL_CTX_new(TLS_method()); + SSL_CONF_CTX *cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctxPtr_); + for (const auto &cmd : sslConfCmds) + { + SSL_CONF_cmd(cctx, cmd.first.data(), cmd.second.data()); + } + SSL_CONF_CTX_finish(cctx); + if (!useOldTLS) + { + SSL_CTX_set_min_proto_version(ctxPtr_, TLS1_2_VERSION); + } + else + { + LOG_WARN << "TLS 1.0/1.1 are enabled. They are considered " + "obsolete, insecure standards and should only be " + "used for legacy purpose."; + } +#else + ctxPtr_ = SSL_CTX_new(SSLv23_method()); + SSL_CONF_CTX *cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctxPtr_); + for (const auto &cmd : sslConfCmds) + { + SSL_CONF_cmd(cctx, cmd.first.data(), cmd.second.data()); + } + SSL_CONF_CTX_finish(cctx); + if (!useOldTLS) + { + SSL_CTX_set_options(ctxPtr_, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); + } + else + { + LOG_WARN << "TLS 1.0/1.1 are enabled. They are considered " + "obsolete, insecure standards and should only be " + "used for legacy purpose."; + } +#endif +#ifdef _WIN32 + if (enableValidtion) + internal::loadWindowsSystemCert(SSL_CTX_get_cert_store(ctxPtr_)); +#else + if (enableValidtion) + SSL_CTX_set_default_verify_paths(ctxPtr_); +#endif + } + ~SSLContext() + { + if (ctxPtr_) + { + SSL_CTX_free(ctxPtr_); + } + } + + SSL_CTX *get() + { + return ctxPtr_; + } + + private: + SSL_CTX *ctxPtr_; +}; +class SSLConn +{ + public: + explicit SSLConn(SSL_CTX *ctx) + { + SSL_ = SSL_new(ctx); + } + ~SSLConn() + { + if (SSL_) + { + SSL_free(SSL_); + } + } + SSL *get() + { + return SSL_; + } + + private: + SSL *SSL_; +}; + +std::shared_ptr newSSLContext( + bool useOldTLS, + bool validateCert, + const std::vector> &sslConfCmds) +{ // init OpenSSL + initOpenSSL(); + return std::make_shared(useOldTLS, validateCert, sslConfCmds); +} +std::shared_ptr newSSLServerContext( + const std::string &certPath, + const std::string &keyPath, + bool useOldTLS, + const std::vector> &sslConfCmds) +{ + auto ctx = newSSLContext(useOldTLS, false, sslConfCmds); + auto r = SSL_CTX_use_certificate_chain_file(ctx->get(), certPath.c_str()); + if (!r) + { +#ifndef _MSC_VER + LOG_FATAL << strerror(errno); +#else + LOG_FATAL << strerror_tl(errno); +#endif + abort(); + } + r = SSL_CTX_use_PrivateKey_file(ctx->get(), + keyPath.c_str(), + SSL_FILETYPE_PEM); + if (!r) + { +#ifndef _MSC_VER + LOG_FATAL << strerror(errno); +#else + LOG_FATAL << strerror_tl(errno); +#endif + abort(); + } + r = SSL_CTX_check_private_key(ctx->get()); + if (!r) + { +#ifndef _MSC_VER + LOG_FATAL << strerror(errno); +#else + LOG_FATAL << strerror_tl(errno); +#endif + abort(); + } + return ctx; +} +} // namespace trantor +#else +namespace trantor +{ +std::shared_ptr newSSLServerContext( + const std::string &certPath, + const std::string &keyPath, + bool useOldTLS, + const std::vector> &sslConfCmds) +{ + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +} +} // namespace trantor +#endif + +TcpConnectionImpl::TcpConnectionImpl(EventLoop *loop, + int socketfd, + const InetAddress &localAddr, + const InetAddress &peerAddr) + : loop_(loop), + ioChannelPtr_(new Channel(loop, socketfd)), + socketPtr_(new Socket(socketfd)), + localAddr_(localAddr), + peerAddr_(peerAddr) +{ + LOG_TRACE << "new connection:" << peerAddr.toIpPort() << "->" + << localAddr.toIpPort(); + ioChannelPtr_->setReadCallback( + std::bind(&TcpConnectionImpl::readCallback, this)); + ioChannelPtr_->setWriteCallback( + std::bind(&TcpConnectionImpl::writeCallback, this)); + ioChannelPtr_->setCloseCallback( + std::bind(&TcpConnectionImpl::handleClose, this)); + ioChannelPtr_->setErrorCallback( + std::bind(&TcpConnectionImpl::handleError, this)); + socketPtr_->setKeepAlive(true); + name_ = localAddr.toIpPort() + "--" + peerAddr.toIpPort(); +} +TcpConnectionImpl::~TcpConnectionImpl() +{ +} +#ifdef USE_OPENSSL +void TcpConnectionImpl::startClientEncryptionInLoop( + std::function &&callback, + bool useOldTLS, + bool validateCert, + const std::string &hostname, + const std::vector> &sslConfCmds) +{ + validateCert_ = validateCert; + loop_->assertInLoopThread(); + if (isEncrypted_) + { + LOG_WARN << "This connection is already encrypted"; + return; + } + sslEncryptionPtr_ = std::make_unique(); + sslEncryptionPtr_->upgradeCallback_ = std::move(callback); + sslEncryptionPtr_->sslCtxPtr_ = + newSSLContext(useOldTLS, validateCert_, sslConfCmds); + sslEncryptionPtr_->sslPtr_ = + std::make_unique(sslEncryptionPtr_->sslCtxPtr_->get()); + if (validateCert) + { + SSL_set_verify(sslEncryptionPtr_->sslPtr_->get(), + SSL_VERIFY_NONE, + nullptr); + validateCert_ = validateCert; + } + if (!hostname.empty()) + { + SSL_set_tlsext_host_name(sslEncryptionPtr_->sslPtr_->get(), + hostname.data()); + sslEncryptionPtr_->hostname_ = hostname; + } + isEncrypted_ = true; + sslEncryptionPtr_->isUpgrade_ = true; + auto r = SSL_set_fd(sslEncryptionPtr_->sslPtr_->get(), socketPtr_->fd()); + (void)r; + assert(r); + sslEncryptionPtr_->sendBufferPtr_ = + std::make_unique>(); + LOG_TRACE << "connectEstablished"; + ioChannelPtr_->enableWriting(); + SSL_set_connect_state(sslEncryptionPtr_->sslPtr_->get()); +} +void TcpConnectionImpl::startServerEncryptionInLoop( + const std::shared_ptr &ctx, + std::function &&callback) +{ + loop_->assertInLoopThread(); + if (isEncrypted_) + { + LOG_WARN << "This connection is already encrypted"; + return; + } + sslEncryptionPtr_ = std::make_unique(); + sslEncryptionPtr_->upgradeCallback_ = std::move(callback); + sslEncryptionPtr_->sslCtxPtr_ = ctx; + sslEncryptionPtr_->isServer_ = true; + sslEncryptionPtr_->sslPtr_ = + std::make_unique(sslEncryptionPtr_->sslCtxPtr_->get()); + isEncrypted_ = true; + sslEncryptionPtr_->isUpgrade_ = true; + if (sslEncryptionPtr_->isServer_ == false) + SSL_set_verify(sslEncryptionPtr_->sslPtr_->get(), + SSL_VERIFY_NONE, + nullptr); + auto r = SSL_set_fd(sslEncryptionPtr_->sslPtr_->get(), socketPtr_->fd()); + (void)r; + assert(r); + sslEncryptionPtr_->sendBufferPtr_ = + std::make_unique>(); + LOG_TRACE << "upgrade to ssl"; + SSL_set_accept_state(sslEncryptionPtr_->sslPtr_->get()); +} +#endif +void TcpConnectionImpl::startServerEncryption( + const std::shared_ptr &ctx, + std::function callback) +{ +#ifndef USE_OPENSSL + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#else + if (loop_->isInLoopThread()) + { + startServerEncryptionInLoop(ctx, std::move(callback)); + } + else + { + loop_->queueInLoop([thisPtr = shared_from_this(), + ctx, + callback = std::move(callback)]() mutable { + thisPtr->startServerEncryptionInLoop(ctx, std::move(callback)); + }); + } + +#endif +} +void TcpConnectionImpl::startClientEncryption( + std::function callback, + bool useOldTLS, + bool validateCert, + std::string hostname, + const std::vector> &sslConfCmds) +{ +#ifndef USE_OPENSSL + LOG_FATAL << "OpenSSL is not found in your system!"; + abort(); +#else + if (!hostname.empty()) + { + std::transform(hostname.begin(), + hostname.end(), + hostname.begin(), + tolower); + assert(sslEncryptionPtr_ != nullptr); + sslEncryptionPtr_->hostname_ = hostname; + } + if (loop_->isInLoopThread()) + { + startClientEncryptionInLoop(std::move(callback), + useOldTLS, + validateCert, + hostname, + sslConfCmds); + } + else + { + loop_->queueInLoop([thisPtr = shared_from_this(), + callback = std::move(callback), + useOldTLS, + hostname = std::move(hostname), + validateCert, + &sslConfCmds]() mutable { + thisPtr->startClientEncryptionInLoop(std::move(callback), + useOldTLS, + validateCert, + hostname, + sslConfCmds); + }); + } +#endif +} +void TcpConnectionImpl::readCallback() +{ +// LOG_TRACE<<"read Callback"; +#ifdef USE_OPENSSL + if (!isEncrypted_) + { +#endif + loop_->assertInLoopThread(); + int ret = 0; + + ssize_t n = readBuffer_.readFd(socketPtr_->fd(), &ret); + // LOG_TRACE<<"read "< 0) + { + bytesReceived_ += n; + if (recvMsgCallback_) + { + recvMsgCallback_(shared_from_this(), &readBuffer_); + } + } +#ifdef USE_OPENSSL + } + else + { + LOG_TRACE << "read Callback"; + loop_->assertInLoopThread(); + if (sslEncryptionPtr_->statusOfSSL_ == SSLStatus::Handshaking) + { + doHandshaking(); + return; + } + else if (sslEncryptionPtr_->statusOfSSL_ == SSLStatus::Connected) + { + int rd; + bool newDataFlag = false; + size_t readLength; + do + { + readBuffer_.ensureWritableBytes(1024); + readLength = readBuffer_.writableBytes(); + rd = SSL_read(sslEncryptionPtr_->sslPtr_->get(), + readBuffer_.beginWrite(), + static_cast(readLength)); + LOG_TRACE << "ssl read:" << rd << " bytes"; + if (rd <= 0) + { + int sslerr = + SSL_get_error(sslEncryptionPtr_->sslPtr_->get(), rd); + if (sslerr == SSL_ERROR_WANT_READ) + { + break; + } + else + { + LOG_TRACE << "ssl read err:" << sslerr; + sslEncryptionPtr_->statusOfSSL_ = + SSLStatus::DisConnected; + handleClose(); + return; + } + } + readBuffer_.hasWritten(rd); + newDataFlag = true; + } while ((size_t)rd == readLength); + if (newDataFlag) + { + // Run callback function + recvMsgCallback_(shared_from_this(), &readBuffer_); + } + } + } +#endif +} +void TcpConnectionImpl::extendLife() +{ + if (idleTimeout_ > 0) + { + auto now = Date::date(); + if (now < lastTimingWheelUpdateTime_.after(1.0)) + return; + lastTimingWheelUpdateTime_ = now; + auto entry = kickoffEntry_.lock(); + if (entry) + { + auto timingWheelPtr = timingWheelWeakPtr_.lock(); + if (timingWheelPtr) + timingWheelPtr->insertEntry(idleTimeout_, entry); + } + } +} +void TcpConnectionImpl::writeCallback() +{ +#ifdef USE_OPENSSL + if (!isEncrypted_ || + (sslEncryptionPtr_ && + sslEncryptionPtr_->statusOfSSL_ == SSLStatus::Connected)) + { +#endif + loop_->assertInLoopThread(); + extendLife(); + if (ioChannelPtr_->isWriting()) + { + assert(!writeBufferList_.empty()); + auto writeBuffer_ = writeBufferList_.front(); +#ifndef _WIN32 + if (writeBuffer_->sendFd_ < 0) +#else + if (writeBuffer_->sendFp_ == nullptr) +#endif + { + if (writeBuffer_->msgBuffer_->readableBytes() <= 0) + { + writeBufferList_.pop_front(); + if (writeBufferList_.empty()) + { + ioChannelPtr_->disableWriting(); + // + if (writeCompleteCallback_) + writeCompleteCallback_(shared_from_this()); + if (status_ == ConnStatus::Disconnecting) + { + socketPtr_->closeWrite(); + } + } + else + { + auto fileNode = writeBufferList_.front(); +#ifndef _WIN32 + assert(fileNode->sendFd_ >= 0); +#else + assert(fileNode->sendFp_); +#endif + sendFileInLoop(fileNode); + } + } + else + { + auto n = + writeInLoop(writeBuffer_->msgBuffer_->peek(), + writeBuffer_->msgBuffer_->readableBytes()); + if (n >= 0) + { + writeBuffer_->msgBuffer_->retrieve(n); + } + else + { +#ifdef _WIN32 + if (errno != 0 && errno != EWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + // TODO: any others? + if (errno == EPIPE || errno == ECONNRESET) + { + LOG_DEBUG << "EPIPE or ECONNRESET, erron=" + << errno; + return; + } + LOG_SYSERR << "Unexpected error(" << errno << ")"; + return; + } + } + } + } + else + { + // file + if (writeBuffer_->fileBytesToSend_ <= 0) + { + writeBufferList_.pop_front(); + if (writeBufferList_.empty()) + { + ioChannelPtr_->disableWriting(); + if (writeCompleteCallback_) + writeCompleteCallback_(shared_from_this()); + if (status_ == ConnStatus::Disconnecting) + { + socketPtr_->closeWrite(); + } + } + else + { +#ifndef _WIN32 + if (writeBufferList_.front()->sendFd_ < 0) +#else + if (writeBufferList_.front()->sendFp_ == nullptr) +#endif + { + // There is data to be sent in the buffer. + auto n = writeInLoop( + writeBufferList_.front()->msgBuffer_->peek(), + writeBufferList_.front() + ->msgBuffer_->readableBytes()); + if (n >= 0) + { + writeBufferList_.front()->msgBuffer_->retrieve( + n); + } + else + { +#ifdef _WIN32 + if (errno != 0 && errno != EWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + // TODO: any others? + if (errno == EPIPE || errno == ECONNRESET) + { + LOG_DEBUG + << "EPIPE or ECONNRESET, erron=" + << errno; + return; + } + LOG_SYSERR << "Unexpected error(" << errno + << ")"; + return; + } + } + } + else + { + // more file + sendFileInLoop(writeBufferList_.front()); + } + } + } + else + { + sendFileInLoop(writeBuffer_); + } + } + } + else + { + LOG_SYSERR << "no writing but call write callback"; + } +#ifdef USE_OPENSSL + } + else + { + LOG_TRACE << "write Callback"; + loop_->assertInLoopThread(); + if (sslEncryptionPtr_->statusOfSSL_ == SSLStatus::Handshaking) + { + doHandshaking(); + return; + } + } +#endif +} +void TcpConnectionImpl::connectEstablished() +{ +// loop_->assertInLoopThread(); +#ifdef USE_OPENSSL + if (!isEncrypted_) + { +#endif + auto thisPtr = shared_from_this(); + loop_->runInLoop([thisPtr]() { + LOG_TRACE << "connectEstablished"; + assert(thisPtr->status_ == ConnStatus::Connecting); + thisPtr->ioChannelPtr_->tie(thisPtr); + thisPtr->ioChannelPtr_->enableReading(); + thisPtr->status_ = ConnStatus::Connected; + if (thisPtr->connectionCallback_) + thisPtr->connectionCallback_(thisPtr); + }); +#ifdef USE_OPENSSL + } + else + { + loop_->runInLoop([thisPtr = shared_from_this()]() { + LOG_TRACE << "connectEstablished"; + assert(thisPtr->status_ == ConnStatus::Connecting); + thisPtr->ioChannelPtr_->tie(thisPtr); + thisPtr->ioChannelPtr_->enableReading(); + thisPtr->status_ = ConnStatus::Connected; + if (thisPtr->sslEncryptionPtr_->isServer_) + { + SSL_set_accept_state( + thisPtr->sslEncryptionPtr_->sslPtr_->get()); + } + else + { + thisPtr->ioChannelPtr_->enableWriting(); + SSL_set_connect_state( + thisPtr->sslEncryptionPtr_->sslPtr_->get()); + } + }); + } +#endif +} +void TcpConnectionImpl::handleClose() +{ + LOG_TRACE << "connection closed, fd=" << socketPtr_->fd(); + loop_->assertInLoopThread(); + status_ = ConnStatus::Disconnected; + ioChannelPtr_->disableAll(); + // ioChannelPtr_->remove(); + auto guardThis = shared_from_this(); + if (connectionCallback_) + connectionCallback_(guardThis); + if (closeCallback_) + { + LOG_TRACE << "to call close callback"; + closeCallback_(guardThis); + } +} +void TcpConnectionImpl::handleError() +{ + int err = socketPtr_->getSocketError(); + if (err == 0) + return; + if (err == EPIPE || err == ECONNRESET || err == 104) + { + LOG_DEBUG << "[" << name_ << "] - SO_ERROR = " << err << " " + << strerror_tl(err); + } + else + { + LOG_ERROR << "[" << name_ << "] - SO_ERROR = " << err << " " + << strerror_tl(err); + } +} +void TcpConnectionImpl::setTcpNoDelay(bool on) +{ + socketPtr_->setTcpNoDelay(on); +} +void TcpConnectionImpl::connectDestroyed() +{ + loop_->assertInLoopThread(); + if (status_ == ConnStatus::Connected) + { + status_ = ConnStatus::Disconnected; + ioChannelPtr_->disableAll(); + + connectionCallback_(shared_from_this()); + } + ioChannelPtr_->remove(); +} +void TcpConnectionImpl::shutdown() +{ + auto thisPtr = shared_from_this(); + loop_->runInLoop([thisPtr]() { + if (thisPtr->status_ == ConnStatus::Connected) + { + thisPtr->status_ = ConnStatus::Disconnecting; + if (!thisPtr->ioChannelPtr_->isWriting()) + { + thisPtr->socketPtr_->closeWrite(); + } + } + }); +} + +void TcpConnectionImpl::forceClose() +{ + auto thisPtr = shared_from_this(); + loop_->runInLoop([thisPtr]() { + if (thisPtr->status_ == ConnStatus::Connected || + thisPtr->status_ == ConnStatus::Disconnecting) + { + thisPtr->status_ = ConnStatus::Disconnecting; + thisPtr->handleClose(); + } + }); +} +#ifndef _WIN32 +void TcpConnectionImpl::sendInLoop(const void *buffer, size_t length) +#else +void TcpConnectionImpl::sendInLoop(const char *buffer, size_t length) +#endif +{ + loop_->assertInLoopThread(); + if (status_ != ConnStatus::Connected) + { + LOG_WARN << "Connection is not connected,give up sending"; + return; + } + extendLife(); + size_t remainLen = length; + ssize_t sendLen = 0; + if (!ioChannelPtr_->isWriting() && writeBufferList_.empty()) + { + // send directly + sendLen = writeInLoop(buffer, length); + if (sendLen < 0) + { + // error +#ifdef _WIN32 + if (errno != 0 && errno != EWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + if (errno == EPIPE || errno == ECONNRESET) // TODO: any others? + { + LOG_DEBUG << "EPIPE or ECONNRESET, erron=" << errno; + return; + } + LOG_SYSERR << "Unexpected error(" << errno << ")"; + return; + } + sendLen = 0; + } + remainLen -= sendLen; + } + if (remainLen > 0 && status_ == ConnStatus::Connected) + { + if (writeBufferList_.empty()) + { + BufferNodePtr node(new BufferNode); + node->msgBuffer_ = std::make_shared(); + writeBufferList_.push_back(std::move(node)); + } +#ifndef _WIN32 + else if (writeBufferList_.back()->sendFd_ >= 0) +#else + else if (writeBufferList_.back()->sendFp_) +#endif + { + BufferNodePtr node(new BufferNode); + node->msgBuffer_ = std::make_shared(); + writeBufferList_.push_back(std::move(node)); + } + writeBufferList_.back()->msgBuffer_->append( + static_cast(buffer) + sendLen, remainLen); + if (!ioChannelPtr_->isWriting()) + ioChannelPtr_->enableWriting(); + if (highWaterMarkCallback_ && + writeBufferList_.back()->msgBuffer_->readableBytes() > + highWaterMarkLen_) + { + highWaterMarkCallback_( + shared_from_this(), + writeBufferList_.back()->msgBuffer_->readableBytes()); + } + } +} +// The order of data sending should be same as the order of calls of send() +void TcpConnectionImpl::send(const std::shared_ptr &msgPtr) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(msgPtr->data(), msgPtr->length()); + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, msgPtr]() { + thisPtr->sendInLoop(msgPtr->data(), msgPtr->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, msgPtr]() { + thisPtr->sendInLoop(msgPtr->data(), msgPtr->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +// The order of data sending should be same as the order of calls of send() +void TcpConnectionImpl::send(const std::shared_ptr &msgPtr) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, msgPtr]() { + thisPtr->sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, msgPtr]() { + thisPtr->sendInLoop(msgPtr->peek(), msgPtr->readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +void TcpConnectionImpl::send(const char *msg, size_t len) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(msg, len); + } + else + { + ++sendNum_; + auto buffer = std::make_shared(msg, len); + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto buffer = std::make_shared(msg, len); + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +void TcpConnectionImpl::send(const void *msg, size_t len) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { +#ifndef _WIN32 + sendInLoop(msg, len); +#else + sendInLoop(static_cast(msg), len); +#endif + } + else + { + ++sendNum_; + auto buffer = + std::make_shared(static_cast(msg), + len); + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto buffer = + std::make_shared(static_cast(msg), len); + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer->data(), buffer->length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +void TcpConnectionImpl::send(const std::string &msg) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(msg.data(), msg.length()); + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, msg]() { + thisPtr->sendInLoop(msg.data(), msg.length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, msg]() { + thisPtr->sendInLoop(msg.data(), msg.length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +void TcpConnectionImpl::send(std::string &&msg) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(msg.data(), msg.length()); + } + else + { + auto thisPtr = shared_from_this(); + ++sendNum_; + loop_->queueInLoop([thisPtr, msg = std::move(msg)]() { + thisPtr->sendInLoop(msg.data(), msg.length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, msg = std::move(msg)]() { + thisPtr->sendInLoop(msg.data(), msg.length()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} + +void TcpConnectionImpl::send(const MsgBuffer &buffer) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(buffer.peek(), buffer.readableBytes()); + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, buffer]() { + thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} + +void TcpConnectionImpl::send(MsgBuffer &&buffer) +{ + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + sendInLoop(buffer.peek(), buffer.readableBytes()); + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, buffer = std::move(buffer)]() { + thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, buffer = std::move(buffer)]() { + thisPtr->sendInLoop(buffer.peek(), buffer.readableBytes()); + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + }); + } +} +void TcpConnectionImpl::sendFile(const char *fileName, + size_t offset, + size_t length) +{ + assert(fileName); +#ifndef _WIN32 + int fd = open(fileName, O_RDONLY); + + if (fd < 0) + { + LOG_SYSERR << fileName << " open error"; + return; + } + + if (length == 0) + { + struct stat filestat; + if (stat(fileName, &filestat) < 0) + { + LOG_SYSERR << fileName << " stat error"; + close(fd); + return; + } + length = filestat.st_size; + } + + sendFile(fd, offset, length); +#else +#ifndef _MSC_VER + auto fp = fopen(fileName, "rb"); +#else + FILE *fp; + if (fopen_s(&fp, fileName, "rb") != 0) + { + fp = nullptr; + } +#endif + if (fp == nullptr) + { + LOG_SYSERR << fileName << " open error"; + return; + } + + if (length == 0) + { + struct stat filestat; + if (stat(fileName, &filestat) < 0) + { + LOG_SYSERR << fileName << " stat error"; + fclose(fp); + return; + } + length = filestat.st_size; + } + + sendFile(fp, offset, length); +#endif +} + +#ifndef _WIN32 +void TcpConnectionImpl::sendFile(int sfd, size_t offset, size_t length) +#else +void TcpConnectionImpl::sendFile(FILE *fp, size_t offset, size_t length) +#endif +{ + assert(length > 0); +#ifndef _WIN32 + assert(sfd >= 0); + BufferNodePtr node(new BufferNode); + node->sendFd_ = sfd; +#else + assert(fp); + BufferNodePtr node(new BufferNode); + node->sendFp_ = fp; +#endif + node->offset_ = static_cast(offset); + node->fileBytesToSend_ = length; + if (loop_->isInLoopThread()) + { + std::lock_guard guard(sendNumMutex_); + if (sendNum_ == 0) + { + writeBufferList_.push_back(node); + if (writeBufferList_.size() == 1) + { + sendFileInLoop(writeBufferList_.front()); + return; + } + } + else + { + ++sendNum_; + auto thisPtr = shared_from_this(); + loop_->queueInLoop([thisPtr, node]() { + thisPtr->writeBufferList_.push_back(node); + { + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + } + + if (thisPtr->writeBufferList_.size() == 1) + { + thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); + } + }); + } + } + else + { + auto thisPtr = shared_from_this(); + std::lock_guard guard(sendNumMutex_); + ++sendNum_; + loop_->queueInLoop([thisPtr, node]() { + LOG_TRACE << "Push sendfile to list"; + thisPtr->writeBufferList_.push_back(node); + + { + std::lock_guard guard1(thisPtr->sendNumMutex_); + --thisPtr->sendNum_; + } + + if (thisPtr->writeBufferList_.size() == 1) + { + thisPtr->sendFileInLoop(thisPtr->writeBufferList_.front()); + } + }); + } +} + +void TcpConnectionImpl::sendFileInLoop(const BufferNodePtr &filePtr) +{ + loop_->assertInLoopThread(); +#ifndef _WIN32 + assert(filePtr->sendFd_ >= 0); +#else + assert(filePtr->sendFp_); +#endif +#ifdef __linux__ + if (!isEncrypted_) + { + auto bytesSent = sendfile(socketPtr_->fd(), + filePtr->sendFd_, + &filePtr->offset_, + filePtr->fileBytesToSend_); + if (bytesSent < 0) + { + if (errno != EAGAIN) + { + LOG_SYSERR << "TcpConnectionImpl::sendFileInLoop"; + if (ioChannelPtr_->isWriting()) + ioChannelPtr_->disableWriting(); + } + return; + } + if (bytesSent < filePtr->fileBytesToSend_) + { + if (bytesSent == 0) + { + LOG_SYSERR << "TcpConnectionImpl::sendFileInLoop"; + return; + } + } + LOG_TRACE << "sendfile() " << bytesSent << " bytes sent"; + filePtr->fileBytesToSend_ -= bytesSent; + if (!ioChannelPtr_->isWriting()) + { + ioChannelPtr_->enableWriting(); + } + return; + } +#endif +#ifndef _WIN32 + lseek(filePtr->sendFd_, filePtr->offset_, SEEK_SET); + if (!fileBufferPtr_) + { + fileBufferPtr_ = std::make_unique>(16 * 1024); + } + while (filePtr->fileBytesToSend_ > 0) + { + auto n = read(filePtr->sendFd_, + &(*fileBufferPtr_)[0], + fileBufferPtr_->size()); +#else + _fseeki64(filePtr->sendFp_, filePtr->offset_, SEEK_SET); + if (!fileBufferPtr_) + { + fileBufferPtr_ = std::make_unique>(16 * 1024); + } + while (filePtr->fileBytesToSend_ > 0) + { + auto n = fread(&(*fileBufferPtr_)[0], + 1, + fileBufferPtr_->size(), + filePtr->sendFp_); +#endif + if (n > 0) + { + auto nSend = writeInLoop(&(*fileBufferPtr_)[0], n); + if (nSend >= 0) + { + filePtr->fileBytesToSend_ -= nSend; + filePtr->offset_ += static_cast(nSend); + if (static_cast(nSend) < static_cast(n)) + { + if (!ioChannelPtr_->isWriting()) + { + ioChannelPtr_->enableWriting(); + } + return; + } + else if (nSend == n) + continue; + } + if (nSend < 0) + { +#ifdef _WIN32 + if (errno != 0 && errno != EWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + // TODO: any others? + if (errno == EPIPE || errno == ECONNRESET) + { + LOG_DEBUG << "EPIPE or ECONNRESET, erron=" << errno; + return; + } + LOG_SYSERR << "Unexpected error(" << errno << ")"; + return; + } + break; + } + } + if (n < 0) + { + LOG_SYSERR << "read error"; + if (ioChannelPtr_->isWriting()) + ioChannelPtr_->disableWriting(); + return; + } + if (n == 0) + { + LOG_SYSERR << "read"; + return; + } + } + if (!ioChannelPtr_->isWriting()) + { + ioChannelPtr_->enableWriting(); + } +} +#ifndef _WIN32 +ssize_t TcpConnectionImpl::writeInLoop(const void *buffer, size_t length) +#else +ssize_t TcpConnectionImpl::writeInLoop(const char *buffer, size_t length) +#endif +{ +#ifdef USE_OPENSSL + if (!isEncrypted_) + { +#endif + bytesSent_ += length; +#ifndef _WIN32 + return write(socketPtr_->fd(), buffer, length); +#else + errno = 0; + return ::send(socketPtr_->fd(), buffer, static_cast(length), 0); +#endif +#ifdef USE_OPENSSL + } + else + { + LOG_TRACE << "send in loop"; + loop_->assertInLoopThread(); + if (status_ != ConnStatus::Connected && + status_ != ConnStatus::Disconnecting) + { + LOG_WARN << "Connection is not connected,give up sending"; + return -1; + } + if (sslEncryptionPtr_->statusOfSSL_ != SSLStatus::Connected) + { + LOG_WARN << "SSL is not connected,give up sending"; + return -1; + } + // send directly + size_t sendTotalLen = 0; + while (sendTotalLen < length) + { + auto len = length - sendTotalLen; + if (len > sslEncryptionPtr_->sendBufferPtr_->size()) + { + len = sslEncryptionPtr_->sendBufferPtr_->size(); + } + memcpy(sslEncryptionPtr_->sendBufferPtr_->data(), + static_cast(buffer) + sendTotalLen, + len); + ERR_clear_error(); + auto sendLen = SSL_write(sslEncryptionPtr_->sslPtr_->get(), + sslEncryptionPtr_->sendBufferPtr_->data(), + static_cast(len)); + if (sendLen <= 0) + { + int sslerr = + SSL_get_error(sslEncryptionPtr_->sslPtr_->get(), sendLen); + if (sslerr != SSL_ERROR_WANT_WRITE && + sslerr != SSL_ERROR_WANT_READ) + { + // LOG_ERROR << "ssl write error:" << sslerr; + forceClose(); + return -1; + } + return sendTotalLen; + } + sendTotalLen += sendLen; + } + return sendTotalLen; + } +#endif +} + +#ifdef USE_OPENSSL + +TcpConnectionImpl::TcpConnectionImpl(EventLoop *loop, + int socketfd, + const InetAddress &localAddr, + const InetAddress &peerAddr, + const std::shared_ptr &ctxPtr, + bool isServer, + bool validateCert, + const std::string &hostname) + : isEncrypted_(true), + loop_(loop), + ioChannelPtr_(new Channel(loop, socketfd)), + socketPtr_(new Socket(socketfd)), + localAddr_(localAddr), + peerAddr_(peerAddr) +{ + LOG_TRACE << "new connection:" << peerAddr.toIpPort() << "->" + << localAddr.toIpPort(); + ioChannelPtr_->setReadCallback( + std::bind(&TcpConnectionImpl::readCallback, this)); + ioChannelPtr_->setWriteCallback( + std::bind(&TcpConnectionImpl::writeCallback, this)); + ioChannelPtr_->setCloseCallback( + std::bind(&TcpConnectionImpl::handleClose, this)); + ioChannelPtr_->setErrorCallback( + std::bind(&TcpConnectionImpl::handleError, this)); + socketPtr_->setKeepAlive(true); + name_ = localAddr.toIpPort() + "--" + peerAddr.toIpPort(); + sslEncryptionPtr_ = std::make_unique(); + sslEncryptionPtr_->sslPtr_ = std::make_unique(ctxPtr->get()); + sslEncryptionPtr_->isServer_ = isServer; + validateCert_ = validateCert; + if (isServer == false) + SSL_set_verify(sslEncryptionPtr_->sslPtr_->get(), + SSL_VERIFY_NONE, + nullptr); + if (!isServer && !hostname.empty()) + { + SSL_set_tlsext_host_name(sslEncryptionPtr_->sslPtr_->get(), + hostname.data()); + sslEncryptionPtr_->hostname_ = hostname; + } + assert(sslEncryptionPtr_->sslPtr_); + auto r = SSL_set_fd(sslEncryptionPtr_->sslPtr_->get(), socketfd); + (void)r; + assert(r); + isEncrypted_ = true; + sslEncryptionPtr_->sendBufferPtr_ = + std::make_unique>(); +} + +bool TcpConnectionImpl::validatePeerCertificate() +{ + LOG_TRACE << "Validating peer cerificate"; + assert(sslEncryptionPtr_ != nullptr); + assert(sslEncryptionPtr_->sslPtr_ != nullptr); + SSL *ssl = sslEncryptionPtr_->sslPtr_->get(); + + auto result = SSL_get_verify_result(ssl); + if (result != X509_V_OK) + { + LOG_DEBUG << "cert error code: " << result; + LOG_ERROR << "Server certificate is not valid"; + return false; + } + + X509 *cert = SSL_get_peer_certificate(ssl); + if (cert == nullptr) + { + LOG_ERROR << "Unable to obtain peer certificate"; + return false; + } + + bool domainIsValid = + internal::verifyCommonName(cert, sslEncryptionPtr_->hostname_) || + internal::verifyAltName(cert, sslEncryptionPtr_->hostname_); + X509_free(cert); + + if (domainIsValid) + { + return true; + } + else + { + LOG_ERROR << "Domain validation failed"; + return false; + } +} + +void TcpConnectionImpl::doHandshaking() +{ + assert(sslEncryptionPtr_->statusOfSSL_ == SSLStatus::Handshaking); + + int r = SSL_do_handshake(sslEncryptionPtr_->sslPtr_->get()); + LOG_TRACE << "hand shaking: " << r; + if (r == 1) + { + // Clients don't commonly have certificates. Let's not validate + // that + if (validateCert_ && sslEncryptionPtr_->isServer_ == false) + { + if (validatePeerCertificate() == false) + { + LOG_ERROR << "SSL certificate validation failed."; + ioChannelPtr_->disableReading(); + sslEncryptionPtr_->statusOfSSL_ = SSLStatus::DisConnected; + if (sslErrorCallback_) + { + sslErrorCallback_(SSLError::kSSLInvalidCertificate); + } + forceClose(); + return; + } + } + sslEncryptionPtr_->statusOfSSL_ = SSLStatus::Connected; + if (sslEncryptionPtr_->isUpgrade_) + { + sslEncryptionPtr_->upgradeCallback_(); + } + else + { + connectionCallback_(shared_from_this()); + } + return; + } + int err = SSL_get_error(sslEncryptionPtr_->sslPtr_->get(), r); + LOG_TRACE << "hand shaking: " << err; + if (err == SSL_ERROR_WANT_WRITE) + { // SSL want writable; + if (!ioChannelPtr_->isWriting()) + ioChannelPtr_->enableWriting(); + // ioChannelPtr_->disableReading(); + } + else if (err == SSL_ERROR_WANT_READ) + { // SSL want readable; + if (!ioChannelPtr_->isReading()) + ioChannelPtr_->enableReading(); + if (ioChannelPtr_->isWriting()) + ioChannelPtr_->disableWriting(); + } + else + { + // ERR_print_errors(err); + LOG_TRACE << "SSL handshake err: " << err; + ioChannelPtr_->disableReading(); + sslEncryptionPtr_->statusOfSSL_ = SSLStatus::DisConnected; + if (sslErrorCallback_) + { + sslErrorCallback_(SSLError::kSSLHandshakeError); + } + forceClose(); + } +} + +#endif diff --git a/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.h b/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.h new file mode 100644 index 0000000..cad3dfa --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/TcpConnectionImpl.h @@ -0,0 +1,344 @@ +/** + * + * @file TcpConnectionImpl.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include + +namespace trantor +{ +#ifdef USE_OPENSSL +enum class SSLStatus +{ + Handshaking, + Connecting, + Connected, + DisConnecting, + DisConnected +}; +class SSLContext; +class SSLConn; + +std::shared_ptr newSSLContext( + bool useOldTLS, + bool validateCert, + const std::vector> &sslConfCmds); +std::shared_ptr newSSLServerContext( + const std::string &certPath, + const std::string &keyPath, + bool useOldTLS, + const std::vector> &sslConfCmds); +// void initServerSSLContext(const std::shared_ptr &ctx, +// const std::string &certPath, +// const std::string &keyPath); +#endif +class Channel; +class Socket; +class TcpServer; +void removeConnection(EventLoop *loop, const TcpConnectionPtr &conn); +class TcpConnectionImpl : public TcpConnection, + public NonCopyable, + public std::enable_shared_from_this +{ + friend class TcpServer; + friend class TcpClient; + friend void trantor::removeConnection(EventLoop *loop, + const TcpConnectionPtr &conn); + + public: + class KickoffEntry + { + public: + explicit KickoffEntry(const std::weak_ptr &conn) + : conn_(conn) + { + } + void reset() + { + conn_.reset(); + } + ~KickoffEntry() + { + auto conn = conn_.lock(); + if (conn) + { + conn->forceClose(); + } + } + + private: + std::weak_ptr conn_; + }; + + TcpConnectionImpl(EventLoop *loop, + int socketfd, + const InetAddress &localAddr, + const InetAddress &peerAddr); +#ifdef USE_OPENSSL + TcpConnectionImpl(EventLoop *loop, + int socketfd, + const InetAddress &localAddr, + const InetAddress &peerAddr, + const std::shared_ptr &ctxPtr, + bool isServer = true, + bool validateCert = true, + const std::string &hostname = ""); +#endif + virtual ~TcpConnectionImpl(); + virtual void send(const char *msg, size_t len) override; + virtual void send(const void *msg, size_t len) override; + virtual void send(const std::string &msg) override; + virtual void send(std::string &&msg) override; + virtual void send(const MsgBuffer &buffer) override; + virtual void send(MsgBuffer &&buffer) override; + virtual void send(const std::shared_ptr &msgPtr) override; + virtual void send(const std::shared_ptr &msgPtr) override; + virtual void sendFile(const char *fileName, + size_t offset = 0, + size_t length = 0) override; + + virtual const InetAddress &localAddr() const override + { + return localAddr_; + } + virtual const InetAddress &peerAddr() const override + { + return peerAddr_; + } + + virtual bool connected() const override + { + return status_ == ConnStatus::Connected; + } + virtual bool disconnected() const override + { + return status_ == ConnStatus::Disconnected; + } + + // virtual MsgBuffer* getSendBuffer() override{ return &writeBuffer_;} + virtual MsgBuffer *getRecvBuffer() override + { + return &readBuffer_; + } + // set callbacks + virtual void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, + size_t markLen) override + { + highWaterMarkCallback_ = cb; + highWaterMarkLen_ = markLen; + } + + virtual void keepAlive() override + { + idleTimeout_ = 0; + auto entry = kickoffEntry_.lock(); + if (entry) + { + entry->reset(); + } + } + virtual bool isKeepAlive() override + { + return idleTimeout_ == 0; + } + virtual void setTcpNoDelay(bool on) override; + virtual void shutdown() override; + virtual void forceClose() override; + virtual EventLoop *getLoop() override + { + return loop_; + } + + virtual size_t bytesSent() const override + { + return bytesSent_; + } + virtual size_t bytesReceived() const override + { + return bytesReceived_; + } + virtual void startClientEncryption( + std::function callback, + bool useOldTLS = false, + bool validateCert = true, + std::string hostname = "", + const std::vector> &sslConfCmds = + {}) override; + virtual void startServerEncryption(const std::shared_ptr &ctx, + std::function callback) override; + virtual bool isSSLConnection() const override + { + return isEncrypted_; + } + + private: + /// Internal use only. + + std::weak_ptr kickoffEntry_; + std::weak_ptr timingWheelWeakPtr_; + size_t idleTimeout_{0}; + Date lastTimingWheelUpdateTime_; + + void enableKickingOff(size_t timeout, + const std::shared_ptr &timingWheel) + { + assert(timingWheel); + assert(timingWheel->getLoop() == loop_); + assert(timeout > 0); + auto entry = std::make_shared(shared_from_this()); + kickoffEntry_ = entry; + timingWheelWeakPtr_ = timingWheel; + idleTimeout_ = timeout; + timingWheel->insertEntry(timeout, entry); + } + void extendLife(); +#ifndef _WIN32 + void sendFile(int sfd, size_t offset = 0, size_t length = 0); +#else + void sendFile(FILE *fp, size_t offset = 0, size_t length = 0); +#endif + void setRecvMsgCallback(const RecvMessageCallback &cb) + { + recvMsgCallback_ = cb; + } + void setConnectionCallback(const ConnectionCallback &cb) + { + connectionCallback_ = cb; + } + void setWriteCompleteCallback(const WriteCompleteCallback &cb) + { + writeCompleteCallback_ = cb; + } + void setCloseCallback(const CloseCallback &cb) + { + closeCallback_ = cb; + } + void setSSLErrorCallback(const SSLErrorCallback &cb) + { + sslErrorCallback_ = cb; + } + + void connectDestroyed(); + virtual void connectEstablished(); + + protected: + struct BufferNode + { +#ifndef _WIN32 + int sendFd_{-1}; + off_t offset_; +#else + FILE *sendFp_{nullptr}; + long long offset_; +#endif + ssize_t fileBytesToSend_; + std::shared_ptr msgBuffer_; + ~BufferNode() + { +#ifndef _WIN32 + if (sendFd_ >= 0) + close(sendFd_); +#else + if (sendFp_) + fclose(sendFp_); +#endif + } + }; + using BufferNodePtr = std::shared_ptr; + enum class ConnStatus + { + Disconnected, + Connecting, + Connected, + Disconnecting + }; + bool isEncrypted_{false}; + EventLoop *loop_; + std::unique_ptr ioChannelPtr_; + std::unique_ptr socketPtr_; + MsgBuffer readBuffer_; + std::list writeBufferList_; + void readCallback(); + void writeCallback(); + InetAddress localAddr_, peerAddr_; + ConnStatus status_{ConnStatus::Connecting}; + // callbacks + RecvMessageCallback recvMsgCallback_; + ConnectionCallback connectionCallback_; + CloseCallback closeCallback_; + WriteCompleteCallback writeCompleteCallback_; + HighWaterMarkCallback highWaterMarkCallback_; + SSLErrorCallback sslErrorCallback_; + void handleClose(); + void handleError(); + // virtual void sendInLoop(const std::string &msg); + + void sendFileInLoop(const BufferNodePtr &file); +#ifndef _WIN32 + void sendInLoop(const void *buffer, size_t length); + ssize_t writeInLoop(const void *buffer, size_t length); +#else + void sendInLoop(const char *buffer, size_t length); + ssize_t writeInLoop(const char *buffer, size_t length); +#endif + size_t highWaterMarkLen_; + std::string name_; + + uint64_t sendNum_{0}; + std::mutex sendNumMutex_; + + size_t bytesSent_{0}; + size_t bytesReceived_{0}; + + std::unique_ptr> fileBufferPtr_; + +#ifdef USE_OPENSSL + private: + void doHandshaking(); + bool validatePeerCertificate(); + struct SSLEncryption + { + SSLStatus statusOfSSL_ = SSLStatus::Handshaking; + // OpenSSL + std::shared_ptr sslCtxPtr_; + std::unique_ptr sslPtr_; + std::unique_ptr> sendBufferPtr_; + bool isServer_{false}; + bool isUpgrade_{false}; + std::function upgradeCallback_; + std::string hostname_; + }; + std::unique_ptr sslEncryptionPtr_; + void startClientEncryptionInLoop( + std::function &&callback, + bool useOldTLS, + bool validateCert, + const std::string &hostname, + const std::vector> &sslConfCmds); + void startServerEncryptionInLoop(const std::shared_ptr &ctx, + std::function &&callback); +#endif +}; + +using TcpConnectionImplPtr = std::shared_ptr; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/Timer.cc b/modules/http_trantor/trantor/net/inner/Timer.cc new file mode 100644 index 0000000..4d10eeb --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Timer.cc @@ -0,0 +1,64 @@ +/** + * + * Timer.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Timer.h" +#include +#include + +namespace trantor +{ +std::atomic Timer::timersCreated_ = ATOMIC_VAR_INIT(InvalidTimerId); +Timer::Timer(const TimerCallback &cb, + const TimePoint &when, + const TimeInterval &interval) + : callback_(cb), + when_(when), + interval_(interval), + repeat_(interval.count() > 0), + id_(++timersCreated_) +{ +} +Timer::Timer(TimerCallback &&cb, + const TimePoint &when, + const TimeInterval &interval) + : callback_(std::move(cb)), + when_(when), + interval_(interval), + repeat_(interval.count() > 0), + id_(++timersCreated_) +{ + // LOG_TRACE<<"Timer move contrustor"; +} +void Timer::run() const +{ + callback_(); +} +void Timer::restart(const TimePoint &now) +{ + if (repeat_) + { + when_ = now + interval_; + } + else + when_ = std::chrono::steady_clock::now(); +} +bool Timer::operator<(const Timer &t) const +{ + return when_ < t.when_; +} +bool Timer::operator>(const Timer &t) const +{ + return when_ > t.when_; +} +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/Timer.h b/modules/http_trantor/trantor/net/inner/Timer.h new file mode 100644 index 0000000..17c6d33 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/Timer.h @@ -0,0 +1,68 @@ +/** + * + * Timer.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +using TimerId = uint64_t; +using TimePoint = std::chrono::steady_clock::time_point; +using TimeInterval = std::chrono::microseconds; +class Timer : public NonCopyable +{ + public: + Timer(const TimerCallback &cb, + const TimePoint &when, + const TimeInterval &interval); + Timer(TimerCallback &&cb, + const TimePoint &when, + const TimeInterval &interval); + ~Timer() + { + // std::cout<<"Timer unconstract!"<(const Timer &t) const; + const TimePoint &when() const + { + return when_; + } + bool isRepeat() + { + return repeat_; + } + TimerId id() + { + return id_; + } + + private: + TimerCallback callback_; + TimePoint when_; + const TimeInterval interval_; + const bool repeat_; + const TimerId id_; + static std::atomic timersCreated_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/TimerQueue.cc b/modules/http_trantor/trantor/net/inner/TimerQueue.cc new file mode 100644 index 0000000..91293a2 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/TimerQueue.cc @@ -0,0 +1,294 @@ +/** + * + * @file TimerQueue.cc + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include + +#include "TimerQueue.h" +#include "Channel.h" +#ifdef __linux__ +#include +#endif +#include +#include +#ifndef _WIN32 +#include +#endif + +using namespace trantor; +#ifdef __linux__ +static int createTimerfd() +{ + int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + if (timerfd < 0) + { + std::cerr << "create timerfd failed!" << std::endl; + } + return timerfd; +} + +static struct timespec howMuchTimeFromNow(const TimePoint &when) +{ + auto microSeconds = std::chrono::duration_cast( + when - std::chrono::steady_clock::now()) + .count(); + if (microSeconds < 100) + { + microSeconds = 100; + } + struct timespec ts; + ts.tv_sec = static_cast(microSeconds / 1000000); + ts.tv_nsec = static_cast((microSeconds % 1000000) * 1000); + return ts; +} +static void resetTimerfd(int timerfd, const TimePoint &expiration) +{ + // wake up loop by timerfd_settime() + struct itimerspec newValue; + struct itimerspec oldValue; + memset(&newValue, 0, sizeof(newValue)); + memset(&oldValue, 0, sizeof(oldValue)); + newValue.it_value = howMuchTimeFromNow(expiration); + int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue); + if (ret) + { + // LOG_SYSERR << "timerfd_settime()"; + } +} +static void readTimerfd(int timerfd, const TimePoint &) +{ + uint64_t howmany; + ssize_t n = ::read(timerfd, &howmany, sizeof howmany); + if (n != sizeof howmany) + { + LOG_ERROR << "TimerQueue::handleRead() reads " << n + << " bytes instead of 8"; + } +} + +void TimerQueue::handleRead() +{ + loop_->assertInLoopThread(); + const auto now = std::chrono::steady_clock::now(); + readTimerfd(timerfd_, now); + + std::vector expired = getExpired(now); + + callingExpiredTimers_ = true; + // cancelingTimers_.clear(); + // safe to callback outside critical section + for (auto const &timerPtr : expired) + { + if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end()) + { + timerPtr->run(); + } + } + callingExpiredTimers_ = false; + + reset(expired, now); +} +#else +static int64_t howMuchTimeFromNow(const TimePoint &when) +{ + auto microSeconds = std::chrono::duration_cast( + when - std::chrono::steady_clock::now()) + .count(); + if (microSeconds < 1000) + { + microSeconds = 1000; + } + return microSeconds / 1000; +} +void TimerQueue::processTimers() +{ + loop_->assertInLoopThread(); + const auto now = std::chrono::steady_clock::now(); + + std::vector expired = getExpired(now); + + callingExpiredTimers_ = true; + // cancelingTimers_.clear(); + // safe to callback outside critical section + for (auto const &timerPtr : expired) + { + if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end()) + { + timerPtr->run(); + } + } + callingExpiredTimers_ = false; + + reset(expired, now); +} +#endif +/////////////////////////////////////// +TimerQueue::TimerQueue(EventLoop *loop) + : loop_(loop), +#ifdef __linux__ + timerfd_(createTimerfd()), + timerfdChannelPtr_(new Channel(loop, timerfd_)), +#endif + timers_(), + callingExpiredTimers_(false) +{ +#ifdef __linux__ + timerfdChannelPtr_->setReadCallback( + std::bind(&TimerQueue::handleRead, this)); + // we are always reading the timerfd, we disarm it with timerfd_settime. + timerfdChannelPtr_->enableReading(); +#endif +} +#ifdef __linux__ +void TimerQueue::reset() +{ + loop_->runInLoop([this]() { + timerfdChannelPtr_->disableAll(); + timerfdChannelPtr_->remove(); + close(timerfd_); + timerfd_ = createTimerfd(); + timerfdChannelPtr_ = std::make_shared(loop_, timerfd_); + timerfdChannelPtr_->setReadCallback( + std::bind(&TimerQueue::handleRead, this)); + // we are always reading the timerfd, we disarm it with timerfd_settime. + timerfdChannelPtr_->enableReading(); + if (!timers_.empty()) + { + const auto nextExpire = timers_.top()->when(); + resetTimerfd(timerfd_, nextExpire); + } + }); +} +#endif +TimerQueue::~TimerQueue() +{ +#ifdef __linux__ + auto chlPtr = timerfdChannelPtr_; + auto fd = timerfd_; + loop_->runInLoop([chlPtr, fd]() { + chlPtr->disableAll(); + chlPtr->remove(); + ::close(fd); + }); +#endif +} + +TimerId TimerQueue::addTimer(const TimerCallback &cb, + const TimePoint &when, + const TimeInterval &interval) +{ + std::shared_ptr timerPtr = + std::make_shared(cb, when, interval); + + loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); }); + return timerPtr->id(); +} +TimerId TimerQueue::addTimer(TimerCallback &&cb, + const TimePoint &when, + const TimeInterval &interval) +{ + std::shared_ptr timerPtr = + std::make_shared(std::move(cb), when, interval); + + loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); }); + return timerPtr->id(); +} +void TimerQueue::addTimerInLoop(const TimerPtr &timer) +{ + loop_->assertInLoopThread(); + timerIdSet_.insert(timer->id()); + if (insert(timer)) + { +// the earliest timer changed +#ifdef __linux__ + resetTimerfd(timerfd_, timer->when()); +#endif + } +} + +void TimerQueue::invalidateTimer(TimerId id) +{ + loop_->runInLoop([this, id]() { timerIdSet_.erase(id); }); +} + +bool TimerQueue::insert(const TimerPtr &timerPtr) +{ + loop_->assertInLoopThread(); + bool earliestChanged = false; + if (timers_.size() == 0 || *timerPtr < *timers_.top()) + { + earliestChanged = true; + } + timers_.push(timerPtr); + // std::cout<<"after push new + // timer:"<when().microSecondsSinceEpoch()/1000000<assertInLoopThread(); + if (timers_.empty()) + { + return 10000; + } + else + { + return howMuchTimeFromNow(timers_.top()->when()); + } +} +#endif + +std::vector TimerQueue::getExpired(const TimePoint &now) +{ + std::vector expired; + while (!timers_.empty()) + { + if (timers_.top()->when() < now) + { + expired.push_back(timers_.top()); + timers_.pop(); + } + else + break; + } + return expired; +} +void TimerQueue::reset(const std::vector &expired, + const TimePoint &now) +{ + loop_->assertInLoopThread(); + for (auto const &timerPtr : expired) + { + auto iter = timerIdSet_.find(timerPtr->id()); + if (iter != timerIdSet_.end()) + { + if (timerPtr->isRepeat()) + { + timerPtr->restart(now); + insert(timerPtr); + } + else + { + timerIdSet_.erase(iter); + } + } + } +#ifdef __linux__ + if (!timers_.empty()) + { + const auto nextExpire = timers_.top()->when(); + resetTimerfd(timerfd_, nextExpire); + } +#endif +} diff --git a/modules/http_trantor/trantor/net/inner/TimerQueue.h b/modules/http_trantor/trantor/net/inner/TimerQueue.h new file mode 100644 index 0000000..6903fb4 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/TimerQueue.h @@ -0,0 +1,76 @@ +/** + * + * TimerQueue.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include "Timer.h" +#include +#include +#include +#include +namespace trantor +{ +// class Timer; +class EventLoop; +class Channel; +using TimerPtr = std::shared_ptr; +struct TimerPtrComparer +{ + bool operator()(const TimerPtr &x, const TimerPtr &y) const + { + return *x > *y; + } +}; + +class TimerQueue : NonCopyable +{ + public: + explicit TimerQueue(EventLoop *loop); + ~TimerQueue(); + TimerId addTimer(const TimerCallback &cb, + const TimePoint &when, + const TimeInterval &interval); + TimerId addTimer(TimerCallback &&cb, + const TimePoint &when, + const TimeInterval &interval); + void addTimerInLoop(const TimerPtr &timer); + void invalidateTimer(TimerId id); +#ifdef __linux__ + void reset(); +#else + int64_t getTimeout() const; + void processTimers(); +#endif + protected: + EventLoop *loop_; +#ifdef __linux__ + int timerfd_; + std::shared_ptr timerfdChannelPtr_; + void handleRead(); +#endif + std::priority_queue, TimerPtrComparer> + timers_; + + bool callingExpiredTimers_; + bool insert(const TimerPtr &timePtr); + std::vector getExpired(); + void reset(const std::vector &expired, const TimePoint &now); + std::vector getExpired(const TimePoint &now); + + private: + std::unordered_set timerIdSet_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/poller/EpollPoller.cc b/modules/http_trantor/trantor/net/inner/poller/EpollPoller.cc new file mode 100644 index 0000000..ac26875 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/poller/EpollPoller.cc @@ -0,0 +1,241 @@ +/** + * + * EpollPoller.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include "Channel.h" +#include "EpollPoller.h" +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#elif defined _WIN32 +#include "Wepoll.h" +#include +#include +#include +#include +#define EPOLL_CLOEXEC _O_NOINHERIT +#endif +namespace trantor +{ +#if defined __linux__ || defined _WIN32 + +#if defined __linux__ +static_assert(EPOLLIN == POLLIN, "EPOLLIN != POLLIN"); +static_assert(EPOLLPRI == POLLPRI, "EPOLLPRI != POLLPRI"); +static_assert(EPOLLOUT == POLLOUT, "EPOLLOUT != POLLOUT"); +static_assert(EPOLLRDHUP == POLLRDHUP, "EPOLLRDHUP != POLLRDHUP"); +static_assert(EPOLLERR == POLLERR, "EPOLLERR != POLLERR"); +static_assert(EPOLLHUP == POLLHUP, "EPOLLHUP != POLLHUP"); +#endif + +namespace +{ +const int kNew = -1; +const int kAdded = 1; +const int kDeleted = 2; +} // namespace + +EpollPoller::EpollPoller(EventLoop *loop) + : Poller(loop), +#ifdef _WIN32 + // wepoll does not suppor flags + epollfd_(::epoll_create1(0)), +#else + epollfd_(::epoll_create1(EPOLL_CLOEXEC)), +#endif + events_(kInitEventListSize) +{ +} +EpollPoller::~EpollPoller() +{ +#ifdef _WIN32 + epoll_close(epollfd_); +#else + close(epollfd_); +#endif +} +#ifdef _WIN32 +void EpollPoller::postEvent(uint64_t event) +{ + epoll_post_signal(epollfd_, event); +} +#endif +void EpollPoller::poll(int timeoutMs, ChannelList *activeChannels) +{ + int numEvents = ::epoll_wait(epollfd_, + &*events_.begin(), + static_cast(events_.size()), + timeoutMs); + int savedErrno = errno; + // Timestamp now(Timestamp::now()); + if (numEvents > 0) + { + // LOG_TRACE << numEvents << " events happended"; + fillActiveChannels(numEvents, activeChannels); + if (static_cast(numEvents) == events_.size()) + { + events_.resize(events_.size() * 2); + } + } + else if (numEvents == 0) + { + // std::cout << "nothing happended" << std::endl; + } + else + { + // error happens, log uncommon ones + if (savedErrno != EINTR) + { + errno = savedErrno; + LOG_SYSERR << "EPollEpollPoller::poll()"; + } + } + return; +} +void EpollPoller::fillActiveChannels(int numEvents, + ChannelList *activeChannels) const +{ + assert(static_cast(numEvents) <= events_.size()); + for (int i = 0; i < numEvents; ++i) + { +#ifdef _WIN32 + if (events_[i].events == EPOLLEVENT) + { + eventCallback_(events_[i].data.u64); + continue; + } +#endif + Channel *channel = static_cast(events_[i].data.ptr); +#ifndef NDEBUG + int fd = channel->fd(); + ChannelMap::const_iterator it = channels_.find(fd); + assert(it != channels_.end()); + assert(it->second == channel); +#endif + channel->setRevents(events_[i].events); + activeChannels->push_back(channel); + } + // LOG_TRACE<<"active Channels num:"<size(); +} +void EpollPoller::updateChannel(Channel *channel) +{ + assertInLoopThread(); + const int index = channel->index(); + // LOG_TRACE << "fd = " << channel->fd() + // << " events = " << channel->events() << " index = " << index; + if (index == kNew || index == kDeleted) + { +// a new one, add with EPOLL_CTL_ADD +#ifndef NDEBUG + int fd = channel->fd(); + if (index == kNew) + { + assert(channels_.find(fd) == channels_.end()); + channels_[fd] = channel; + } + else + { // index == kDeleted + assert(channels_.find(fd) != channels_.end()); + assert(channels_[fd] == channel); + } +#endif + channel->setIndex(kAdded); + update(EPOLL_CTL_ADD, channel); + } + else + { +// update existing one with EPOLL_CTL_MOD/DEL +#ifndef NDEBUG + int fd = channel->fd(); + (void)fd; + assert(channels_.find(fd) != channels_.end()); + assert(channels_[fd] == channel); +#endif + assert(index == kAdded); + if (channel->isNoneEvent()) + { + update(EPOLL_CTL_DEL, channel); + channel->setIndex(kDeleted); + } + else + { + update(EPOLL_CTL_MOD, channel); + } + } +} +void EpollPoller::removeChannel(Channel *channel) +{ + EpollPoller::assertInLoopThread(); +#ifndef NDEBUG + int fd = channel->fd(); + assert(channels_.find(fd) != channels_.end()); + assert(channels_[fd] == channel); + size_t n = channels_.erase(fd); + (void)n; + assert(n == 1); +#endif + assert(channel->isNoneEvent()); + int index = channel->index(); + assert(index == kAdded || index == kDeleted); + if (index == kAdded) + { + update(EPOLL_CTL_DEL, channel); + } + channel->setIndex(kNew); +} +void EpollPoller::update(int operation, Channel *channel) +{ + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.events = channel->events(); + event.data.ptr = channel; + int fd = channel->fd(); + if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) + { + if (operation == EPOLL_CTL_DEL) + { + // LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << + // " fd =" << fd; + } + else + { + // LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) + // << " fd =" << fd; + } + } +} +#else +EpollPoller::EpollPoller(EventLoop *loop) : Poller(loop) +{ + assert(false); +} +EpollPoller::~EpollPoller() +{ +} +void EpollPoller::poll(int, ChannelList *) +{ +} +void EpollPoller::updateChannel(Channel *) +{ +} +void EpollPoller::removeChannel(Channel *) +{ +} + +#endif +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/poller/EpollPoller.h b/modules/http_trantor/trantor/net/inner/poller/EpollPoller.h new file mode 100644 index 0000000..be04a4a --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/poller/EpollPoller.h @@ -0,0 +1,64 @@ +/** + * + * EpollPoller.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include "../Poller.h" +#include +#include + +#if defined __linux__ || defined _WIN32 +#include +#include +using EventList = std::vector; +#endif +namespace trantor +{ +class Channel; + +class EpollPoller : public Poller +{ + public: + explicit EpollPoller(EventLoop *loop); + virtual ~EpollPoller(); + virtual void poll(int timeoutMs, ChannelList *activeChannels) override; + virtual void updateChannel(Channel *channel) override; + virtual void removeChannel(Channel *channel) override; +#ifdef _WIN32 + virtual void postEvent(uint64_t event) override; + virtual void setEventCallback(const EventCallback &cb) override + { + eventCallback_ = cb; + } +#endif + + private: +#if defined __linux__ || defined _WIN32 + static const int kInitEventListSize = 16; +#ifdef _WIN32 + void *epollfd_; + EventCallback eventCallback_{[](uint64_t event) {}}; +#else + int epollfd_; +#endif + EventList events_; + void update(int operation, Channel *channel); +#ifndef NDEBUG + using ChannelMap = std::map; + ChannelMap channels_; +#endif + void fillActiveChannels(int numEvents, ChannelList *activeChannels) const; +#endif +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/poller/KQueue.cc b/modules/http_trantor/trantor/net/inner/poller/KQueue.cc new file mode 100644 index 0000000..ee917d2 --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/poller/KQueue.cc @@ -0,0 +1,249 @@ +#include "KQueue.h" +#include "Channel.h" +#ifdef USE_KQUEUE +#include +#include +#include +#include +#include +#include +#endif +namespace trantor +{ +#ifdef USE_KQUEUE +namespace +{ +const int kNew = -1; +const int kAdded = 1; +const int kDeleted = 2; +} // namespace + +KQueue::KQueue(EventLoop *loop) + : Poller(loop), kqfd_(kqueue()), events_(kInitEventListSize) +{ + assert(kqfd_ >= 0); +} + +KQueue::~KQueue() +{ + close(kqfd_); +} + +void KQueue::resetAfterFork() +{ + close(kqfd_); + kqfd_ = kqueue(); + for (auto &ch : channels_) + { + ch.second.first = 0; + if (ch.second.second->isReading() || ch.second.second->isWriting()) + { + update(ch.second.second); + } + } +} + +void KQueue::poll(int timeoutMs, ChannelList *activeChannels) +{ + struct timespec timeout; + timeout.tv_sec = timeoutMs / 1000; + timeout.tv_nsec = (timeoutMs % 1000) * 1000000; + + int numEvents = kevent(kqfd_, + NULL, + 0, + events_.data(), + static_cast(events_.size()), + &timeout); + int savedErrno = errno; + // Timestamp now(Timestamp::now()); + if (numEvents > 0) + { + // LOG_TRACE << numEvents << " events happended"; + fillActiveChannels(numEvents, activeChannels); + if (static_cast(numEvents) == events_.size()) + { + events_.resize(events_.size() * 2); + } + } + else if (numEvents == 0) + { + // std::cout << "nothing happended" << std::endl; + } + else + { + // error happens, log uncommon ones + if (savedErrno != EINTR) + { + errno = savedErrno; + LOG_SYSERR << "KQueue::poll()"; + } + } + return; +} + +void KQueue::fillActiveChannels(int numEvents, + ChannelList *activeChannels) const +{ + assert(static_cast(numEvents) <= events_.size()); + for (int i = 0; i < numEvents; ++i) + { + Channel *channel = static_cast(events_[i].udata); + assert(channels_.find(channel->fd()) != channels_.end()); + int events = events_[i].filter; + if (events == EVFILT_READ) + { + channel->setRevents(POLLIN); + } + else if (events == EVFILT_WRITE) + { + channel->setRevents(POLLOUT); + } + else + { + LOG_ERROR << "events=" << events; + continue; + } + activeChannels->push_back(channel); + } +} + +void KQueue::updateChannel(Channel *channel) +{ + assertInLoopThread(); + const int index = channel->index(); + // LOG_TRACE << "fd = " << channel->fd() + // << " events = " << channel->events() << " index = " << index; + if (index == kNew || index == kDeleted) + { + if (index == kNew) + { + assert(channels_.find(channel->fd()) == channels_.end()); + } + else + { // index == kDeleted + assert(channels_.find(channel->fd()) != channels_.end()); + assert(channels_[channel->fd()].second == channel); + } + update(channel); + channel->setIndex(kAdded); + } + else + { + // update existing one + int fd = channel->fd(); + (void)fd; + assert(channels_.find(fd) != channels_.end()); + assert(index == kAdded); + if (channel->isNoneEvent()) + { + update(channel); + channel->setIndex(kDeleted); + } + else + { + update(channel); + } + } +} +void KQueue::removeChannel(Channel *channel) +{ + assertInLoopThread(); + int fd = channel->fd(); + assert(channels_.find(fd) != channels_.end()); + assert(channel->isNoneEvent()); + int index = channel->index(); + assert(index == kAdded || index == kDeleted); + + if (index == kAdded) + { + update(channel); + } + + size_t n = channels_.erase(fd); + (void)n; + assert(n == 1); + channel->setIndex(kNew); +} + +void KQueue::update(Channel *channel) +{ + struct kevent ev[2]; + int n = 0; + auto events = channel->events(); + int oldEvents = 0; + if (channels_.find(channel->fd()) != channels_.end()) + { + oldEvents = channels_[channel->fd()].first; + } + + auto fd = channel->fd(); + channels_[fd] = {events, channel}; + + if ((events & Channel::kReadEvent) && (!(oldEvents & Channel::kReadEvent))) + { + EV_SET(&ev[n++], + fd, + EVFILT_READ, + EV_ADD | EV_ENABLE, + 0, + 0, + (void *)(intptr_t)channel); + } + else if ((!(events & Channel::kReadEvent)) && + (oldEvents & Channel::kReadEvent)) + { + EV_SET(&ev[n++], + fd, + EVFILT_READ, + EV_DELETE, + 0, + 0, + (void *)(intptr_t)channel); + } + if ((events & Channel::kWriteEvent) && + (!(oldEvents & Channel::kWriteEvent))) + { + EV_SET(&ev[n++], + fd, + EVFILT_WRITE, + EV_ADD | EV_ENABLE, + 0, + 0, + (void *)(intptr_t)channel); + } + else if ((!(events & Channel::kWriteEvent)) && + (oldEvents & Channel::kWriteEvent)) + { + EV_SET(&ev[n++], + fd, + EVFILT_WRITE, + EV_DELETE, + 0, + 0, + (void *)(intptr_t)channel); + } + kevent(kqfd_, ev, n, NULL, 0, NULL); +} +#else +KQueue::KQueue(EventLoop *loop) : Poller(loop) +{ + assert(false); +} +KQueue::~KQueue() +{ +} +void KQueue::poll(int, ChannelList *) +{ +} +void KQueue::updateChannel(Channel *) +{ +} +void KQueue::removeChannel(Channel *) +{ +} +void KQueue::resetAfterFork() +{ +} +#endif +} // namespace trantor diff --git a/modules/http_trantor/trantor/net/inner/poller/KQueue.h b/modules/http_trantor/trantor/net/inner/poller/KQueue.h new file mode 100644 index 0000000..290082d --- /dev/null +++ b/modules/http_trantor/trantor/net/inner/poller/KQueue.h @@ -0,0 +1,55 @@ +/** + * + * KQueue.h + * An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * https://github.com/an-tao/trantor + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * Trantor + * + */ + +#pragma once +#include "../Poller.h" +#include +#include + +#if (defined(__unix__) && !defined(__linux__)) || \ + (defined(__APPLE__) && defined(__MACH__)) +#define USE_KQUEUE +#include +#include +#include +using EventList = std::vector; +#endif +namespace trantor +{ +class Channel; + +class KQueue : public Poller +{ + public: + explicit KQueue(EventLoop *loop); + virtual ~KQueue(); + virtual void poll(int timeoutMs, ChannelList *activeChannels) override; + virtual void updateChannel(Channel *channel) override; + virtual void removeChannel(Channel *channel) override; + virtual void resetAfterFork() override; + + private: +#ifdef USE_KQUEUE + static const int kInitEventListSize = 16; + int kqfd_; + EventList events_; + using ChannelMap = std::unordered_map>; + ChannelMap channels_; + + void fillActiveChannels(int numEvents, ChannelList *activeChannels) const; + void update(Channel *channel); +#endif +}; + +} // namespace trantor \ No newline at end of file diff --git a/modules/http_trantor/trantor/tests/AsyncFileLoggerTest.cc b/modules/http_trantor/trantor/tests/AsyncFileLoggerTest.cc new file mode 100644 index 0000000..66c7219 --- /dev/null +++ b/modules/http_trantor/trantor/tests/AsyncFileLoggerTest.cc @@ -0,0 +1,45 @@ +#include +#include +#include +#include +int main() +{ + trantor::AsyncFileLogger asyncFileLogger; + asyncFileLogger.setFileName("async_test"); + asyncFileLogger.startLogging(); + trantor::Logger::setOutputFunction( + [&](const char *msg, const uint64_t len) { + asyncFileLogger.output(msg, len); + }, + [&]() { asyncFileLogger.flush(); }); + asyncFileLogger.setFileSizeLimit(100000000); + // LOG_DEBUG<<"debug log!"<<1; + // LOG_TRACE<<"trace log!"<<2; + // LOG_INFO<<"info log!"<<3; + // LOG_WARN<<"warning log!"<<4; + // if(1) + // LOG_ERROR<<"error log!"<<5; + // std::thread thread_([](){ + // LOG_FATAL<<"fatal log!"<<6; + // }); + // + // FILE *fp=fopen("/notexistfile","rb"); + // if(fp==NULL) + // { + // LOG_SYSERR<<"syserr log!"<<7; + // } + + int i = 0; + while (i < 1000000) + { + ++i; + if (i % 100 == 0) + { + LOG_ERROR << "this is the " << i << "th log"; + continue; + } + LOG_INFO << "this is the " << i << "th log"; + ++i; + LOG_DEBUG << "this is the " << i << "th log"; + } +} diff --git a/modules/http_trantor/trantor/tests/AsyncFileLoggerTest1.cc b/modules/http_trantor/trantor/tests/AsyncFileLoggerTest1.cc new file mode 100644 index 0000000..63c6916 --- /dev/null +++ b/modules/http_trantor/trantor/tests/AsyncFileLoggerTest1.cc @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; +int main() +{ + trantor::AsyncFileLogger asyncFileLogger; + asyncFileLogger.setFileName("async_test"); + asyncFileLogger.startLogging(); + trantor::Logger::setOutputFunction( + [&](const char *msg, const uint64_t len) { + asyncFileLogger.output(msg, len); + }, + [&]() { asyncFileLogger.flush(); }); + asyncFileLogger.setFileSizeLimit(100000000); + int i = 0; + while (i < 1000000) + { + ++i; + if (i % 100 == 0) + { + LOG_ERROR << "this is the " << i << "th log"; + continue; + } + LOG_INFO << "this is the " << i << "th log"; + ++i; + LOG_DEBUG << "this is the " << i << "th log"; + std::this_thread::sleep_for(1s); + } +} diff --git a/modules/http_trantor/trantor/tests/CMakeLists.txt b/modules/http_trantor/trantor/tests/CMakeLists.txt new file mode 100644 index 0000000..180f556 --- /dev/null +++ b/modules/http_trantor/trantor/tests/CMakeLists.txt @@ -0,0 +1,51 @@ +add_executable(ssl_server_test SSLServerTest.cc) +add_executable(ssl_client_test SSLClientTest.cc) +add_executable(serial_task_queue_test1 SerialTaskQueueTest1.cc) +add_executable(serial_task_queue_test2 SerialTaskQueueTest2.cc) +add_executable(timer_test TimerTest.cc) +add_executable(timer_test1 TimerTest1.cc) +add_executable(run_in_loop_test1 RunInLoopTest1.cc) +add_executable(run_in_loop_test2 RunInLoopTest2.cc) +add_executable(logger_test LoggerTest.cc) +add_executable(async_file_logger_test AsyncFileLoggerTest.cc) +add_executable(tcp_server_test TcpServerTest.cc) +add_executable(concurrent_task_queue_test ConcurrentTaskQueueTest.cc) +add_executable(tcp_client_test TcpClientTest.cc) +add_executable(async_file_logger_test1 AsyncFileLoggerTest1.cc) +add_executable(sendfile_test SendfileTest.cc) +add_executable(timing_wheel_test TimingWheelTest.cc) +add_executable(kickoff_test KickoffTest.cc) +add_executable(dns_test DnsTest.cc) +add_executable(delayed_ssl_server_test DelayedSSLServerTest.cc) +add_executable(delayed_ssl_client_test DelayedSSLClientTest.cc) +add_executable(run_on_quit_test RunOnQuitTest.cc) +set(targets_list + ssl_server_test + ssl_client_test + serial_task_queue_test1 + serial_task_queue_test2 + timer_test + timer_test1 + run_in_loop_test1 + run_in_loop_test2 + logger_test + async_file_logger_test + tcp_server_test + concurrent_task_queue_test + tcp_client_test + async_file_logger_test1 + sendfile_test + timing_wheel_test + kickoff_test + dns_test + delayed_ssl_server_test + delayed_ssl_client_test + run_on_quit_test) + +set_property(TARGET ${targets_list} PROPERTY CXX_STANDARD 14) +set_property(TARGET ${targets_list} PROPERTY CXX_STANDARD_REQUIRED ON) +set_property(TARGET ${targets_list} PROPERTY CXX_EXTENSIONS OFF) + +foreach(T ${targets_list}) + target_link_libraries(${T} PRIVATE trantor) +endforeach() diff --git a/modules/http_trantor/trantor/tests/ConcurrentTaskQueueTest.cc b/modules/http_trantor/trantor/tests/ConcurrentTaskQueueTest.cc new file mode 100644 index 0000000..cd62795 --- /dev/null +++ b/modules/http_trantor/trantor/tests/ConcurrentTaskQueueTest.cc @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +int main() +{ + trantor::ConcurrentTaskQueue queue(5, "concurrT"); + std::atomic_int sum; + sum = 0; + for (int i = 0; i < 4; ++i) + { + queue.runTaskInQueue([&sum]() { + LOG_DEBUG << "add sum"; + for (int i = 0; i < 10000; ++i) + { + ++sum; + } + }); + } + + queue.runTaskInQueue([&sum]() { + for (int i = 0; i < 20; ++i) + { + LOG_DEBUG << "sum=" << sum; + std::this_thread::sleep_for(100us); + } + }); + + getc(stdin); + LOG_DEBUG << "sum=" << sum; +} diff --git a/modules/http_trantor/trantor/tests/DelayedSSLClientTest.cc b/modules/http_trantor/trantor/tests/DelayedSSLClientTest.cc new file mode 100644 index 0000000..72c5530 --- /dev/null +++ b/modules/http_trantor/trantor/tests/DelayedSSLClientTest.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + trantor::Logger::setLogLevel(trantor::Logger::kTrace); + LOG_DEBUG << "TcpClient class test!"; + EventLoop loop; +#if USE_IPV6 + InetAddress serverAddr("::1", 8888, true); +#else + InetAddress serverAddr("127.0.0.1", 8888); +#endif + std::shared_ptr client[10]; + std::atomic_int connCount; + connCount = 10; + for (int i = 0; i < 10; ++i) + { + client[i] = std::make_shared(&loop, + serverAddr, + "tcpclienttest"); + client[i]->setConnectionCallback( + [i, &loop, &connCount](const TcpConnectionPtr &conn) { + if (conn->connected()) + { + } + else + { + LOG_DEBUG << i << " disconnected"; + --connCount; + if (connCount == 0) + loop.quit(); + } + }); + client[i]->setMessageCallback( + [](const TcpConnectionPtr &conn, MsgBuffer *buf) { + auto msg = std::string(buf->peek(), buf->readableBytes()); + + LOG_INFO << msg; + if (msg == "hello") + { + buf->retrieveAll(); + conn->startClientEncryption( + [conn]() { + LOG_INFO << "SSL established"; + conn->send("Hello"); + }, + false, + false); + } + if (conn->isSSLConnection()) + { + buf->retrieveAll(); + } + }); + client[i]->connect(); + } + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/DelayedSSLServerTest.cc b/modules/http_trantor/trantor/tests/DelayedSSLServerTest.cc new file mode 100644 index 0000000..466b7ad --- /dev/null +++ b/modules/http_trantor/trantor/tests/DelayedSSLServerTest.cc @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + LOG_DEBUG << "test start"; + Logger::setLogLevel(Logger::kTrace); + EventLoopThread loopThread; + loopThread.run(); +#if USE_IPV6 + InetAddress addr(8888, true, true); +#else + InetAddress addr(8888); +#endif + TcpServer server(loopThread.getLoop(), addr, "test"); + auto ctx = newSSLServerContext("server.pem", "server.pem", {}); + LOG_INFO << "start"; + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + LOG_DEBUG << std::string{buffer->peek(), buffer->readableBytes()}; + connectionPtr->send(*buffer); + buffer->retrieveAll(); + connectionPtr->shutdown(); + }); + server.setConnectionCallback([ctx](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + LOG_DEBUG << "New connection"; + connPtr->send("hello"); + connPtr->startServerEncryption(ctx, [] { + LOG_INFO << "SSL established"; + }); + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loopThread.wait(); +} diff --git a/modules/http_trantor/trantor/tests/DnsTest.cc b/modules/http_trantor/trantor/tests/DnsTest.cc new file mode 100644 index 0000000..1a71f53 --- /dev/null +++ b/modules/http_trantor/trantor/tests/DnsTest.cc @@ -0,0 +1,48 @@ +#include +#include +void dns(const std::shared_ptr &resolver) +{ + auto now = trantor::Date::now(); + resolver->resolve("www.baidu.com", [now](const trantor::InetAddress &addr) { + auto interval = trantor::Date::now().microSecondsSinceEpoch() - + now.microSecondsSinceEpoch(); + std::cout << "baidu:" << addr.toIp() << " " << interval / 1000 << "ms" + << std::endl; + }); + resolver->resolve("www.google.com", + [now](const trantor::InetAddress &addr) { + auto interval = + trantor::Date::now().microSecondsSinceEpoch() - + now.microSecondsSinceEpoch(); + std::cout << "google:" << addr.toIp() << " " + << interval / 1000 << "ms" << std::endl; + }); + resolver->resolve("www.sina.com", [now](const trantor::InetAddress &addr) { + auto interval = trantor::Date::now().microSecondsSinceEpoch() - + now.microSecondsSinceEpoch(); + std::cout << "sina:" << addr.toIp() << " " << interval / 1000 << "ms" + << std::endl; + }); + resolver->resolve("www.xjfisfjaskfeiakdjfg.com", + [now](const trantor::InetAddress &addr) { + auto interval = + trantor::Date::now().microSecondsSinceEpoch() - + now.microSecondsSinceEpoch(); + std::cout << "bad address:" << addr.toIp() << " " + << interval / 1000 << "ms" << std::endl; + }); + resolver->resolve("localhost", [now](const trantor::InetAddress &addr) { + auto interval = trantor::Date::now().microSecondsSinceEpoch() - + now.microSecondsSinceEpoch(); + std::cout << "localhost:" << addr.toIp() << " " << interval / 1000 + << "ms" << std::endl; + }); +} +int main() +{ + trantor::EventLoop loop; + auto resolver = trantor::Resolver::newResolver(&loop); + dns(resolver); + loop.runAfter(1.0, [resolver]() { dns(resolver); }); + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/KickoffTest.cc b/modules/http_trantor/trantor/tests/KickoffTest.cc new file mode 100644 index 0000000..1103822 --- /dev/null +++ b/modules/http_trantor/trantor/tests/KickoffTest.cc @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + LOG_DEBUG << "test start"; + Logger::setLogLevel(Logger::kTrace); + EventLoop loop; +#if USE_IPV6 + InetAddress addr(8888, true, true); +#else + InetAddress addr(8888); +#endif + TcpServer server(&loop, addr, "test"); + server.kickoffIdleConnections(10); + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + // LOG_DEBUG<<"recv callback!"; + std::cout << std::string(buffer->peek(), buffer->readableBytes()); + connectionPtr->send(buffer->peek(), buffer->readableBytes()); + buffer->retrieveAll(); + }); + int n = 0; + server.setConnectionCallback([&n](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + ++n; + if (n % 2 == 0) + { + connPtr->keepAlive(); + } + LOG_DEBUG << "New connection"; + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/LoggerTest.cc b/modules/http_trantor/trantor/tests/LoggerTest.cc new file mode 100644 index 0000000..408fcb7 --- /dev/null +++ b/modules/http_trantor/trantor/tests/LoggerTest.cc @@ -0,0 +1,1139 @@ +#include +#include +#include +int main() +{ + int i; + LOG_DEBUG << (float)3.14; + LOG_DEBUG << (const char)'8'; + LOG_DEBUG << &i; + LOG_DEBUG << (long double)3.1415; + LOG_DEBUG << trantor::Fmt("%.3g", 3.1415926); + LOG_DEBUG << "debug log!" << 1; + LOG_TRACE << "trace log!" << 2; + LOG_INFO << "info log!" << 3; + LOG_WARN << "warning log!" << 4; + if (1) + LOG_ERROR << "error log!" << 5; + std::thread thread_([]() { LOG_FATAL << "fatal log!" << 6; }); + + FILE *fp = fopen("/notexistfile", "rb"); + if (fp == NULL) + { + LOG_SYSERR << "syserr log!" << 7; + } + LOG_DEBUG << "long message test:"; + LOG_DEBUG + << "Applications\n" + "Developer\n" + "Library\n" + "Network\n" + "System\n" + "Users\n" + "Volumes\n" + "bin\n" + "cores\n" + "dev\n" + "etc\n" + "home\n" + "installer.failurerequests\n" + "net\n" + "opt\n" + "private\n" + "sbin\n" + "tmp\n" + "usr\n" + "var\n" + "vm\n" + "\n" + "/Applications:\n" + "Adobe\n" + "Adobe Creative Cloud\n" + "Adobe Photoshop CC\n" + "AirPlayer Pro.app\n" + "AliWangwang.app\n" + "Android Studio.app\n" + "App Store.app\n" + "Autodesk\n" + "Automator.app\n" + "Axure RP Pro 7.0.app\n" + "BaiduNetdisk_mac.app\n" + "CLion.app\n" + "Calculator.app\n" + "Calendar.app\n" + "Chess.app\n" + "CleanApp.app\n" + "Cocos\n" + "Contacts.app\n" + "DVD Player.app\n" + "Dashboard.app\n" + "Dictionary.app\n" + "Docs for Xcode.app\n" + "FaceTime.app\n" + "FinalShell\n" + "Firefox.app\n" + "Font Book.app\n" + "GitHub.app\n" + "Google Chrome.app\n" + "Image Capture.app\n" + "Lantern.app\n" + "Launchpad.app\n" + "License.rtf\n" + "MacPorts\n" + "Mail.app\n" + "Maps.app\n" + "Messages.app\n" + "Microsoft Excel.app\n" + "Microsoft Office 2011\n" + "Microsoft OneNote.app\n" + "Microsoft Outlook.app\n" + "Microsoft PowerPoint.app\n" + "Microsoft Word.app\n" + "Mindjet MindManager.app\n" + "Mission Control.app\n" + "Mockplus.app\n" + "MyEclipse 2015\n" + "Notes.app\n" + "Numbers.app\n" + "OmniGraffle.app\n" + "Pages.app\n" + "Photo Booth.app\n" + "Photos.app\n" + "Preview.app\n" + "QJVPN.app\n" + "QQ.app\n" + "QQMusic.app\n" + "QuickTime Player.app\n" + "RAR Extractor Lite.app\n" + "Reminders.app\n" + "Remote Desktop Connection.app\n" + "Renee Undeleter.app\n" + "Sabaki.app\n" + "Safari.app\n" + "ShadowsocksX.app\n" + "Siri.app\n" + "SogouCharacterViewer.app\n" + "SogouInputPad.app\n" + "Stickies.app\n" + "SupremePlayer Lite.app\n" + "System Preferences.app\n" + "TeX\n" + "Telegram.app\n" + "Telnet Lite.app\n" + "Termius.app\n" + "Tesumego - How to Make a Professional Go Player.app\n" + "TextEdit.app\n" + "Thunder.app\n" + "Time Machine.app\n" + "Tunnelblick.app\n" + "Utilities\n" + "VPN Shield.appdownload\n" + "WeChat.app\n" + "WinOnX2.app\n" + "Wireshark.app\n" + "Xcode.app\n" + "Yose.app\n" + "YoudaoNote.localized\n" + "finalshelldata\n" + "iBooks.app\n" + "iHex.app\n" + "iPhoto.app\n" + "iTools.app\n" + "iTunes.app\n" + "pgAdmin 4.app\n" + "vSSH Lite.app\n" + "wechatwebdevtools.app\n" + "\n" + "/Applications/Adobe:\n" + "Flash Player\n" + "\n" + "/Applications/Adobe/Flash Player:\n" + "AddIns\n" + "\n" + "/Applications/Adobe/Flash Player/AddIns:\n" + "airappinstaller\n" + "\n" + "/Applications/Adobe/Flash Player/AddIns/airappinstaller:\n" + "airappinstaller\n" + "digest.s\n" + "\n" + "/Applications/Adobe Creative Cloud:\n" + "Adobe Creative Cloud\n" + "Icon\n" + "Uninstall Adobe Creative Cloud\n" + "\n" + "/Applications/Adobe Photoshop CC:\n" + "Adobe Photoshop CC.app\n" + "Configuration\n" + "Icon\n" + "Legal\n" + "LegalNotices.pdf\n" + "Locales\n" + "Plug-ins\n" + "Presets\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app:\n" + "Contents\n" + "Linguistics\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop CC.app/Contents:\n" + "Application Data\n" + "Frameworks\n" + "Info.plist\n" + "MacOS\n" + "PkgInfo\n" + "Required\n" + "Resources\n" + "_CodeSignature\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data:\n" + "Custom File Info Panels\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info Panels:\n" + "4.0\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info Panels/4.0:\n" + "bin\n" + "custom\n" + "panels\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/bin:\n" + "FileInfoFoundation.swf\n" + "FileInfoUI.swf\n" + "framework.swf\n" + "loc\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/bin/loc:\n" + "FileInfo_ar_AE.dat\n" + "FileInfo_bg_BG.dat\n" + "FileInfo_cs_CZ.dat\n" + "FileInfo_da_DK.dat\n" + "FileInfo_de_DE.dat\n" + "FileInfo_el_GR.dat\n" + "FileInfo_en_US.dat\n" + "FileInfo_es_ES.dat\n" + "FileInfo_et_EE.dat\n" + "FileInfo_fi_FI.dat\n" + "FileInfo_fr_FR.dat\n" + "FileInfo_he_IL.dat\n" + "FileInfo_hr_HR.dat\n" + "FileInfo_hu_HU.dat\n" + "FileInfo_it_IT.dat\n" + "FileInfo_ja_JP.dat\n" + "FileInfo_ko_KR.dat\n" + "FileInfo_lt_LT.dat\n" + "FileInfo_lv_LV.dat\n" + "FileInfo_nb_NO.dat\n" + "FileInfo_nl_NL.dat\n" + "FileInfo_pl_PL.dat\n" + "FileInfo_pt_BR.dat\n" + "FileInfo_ro_RO.dat\n" + "FileInfo_ru_RU.dat\n" + "FileInfo_sk_SK.dat\n" + "FileInfo_sl_SI.dat\n" + "FileInfo_sv_SE.dat\n" + "FileInfo_tr_TR.dat\n" + "FileInfo_uk_UA.dat\n" + "FileInfo_zh_CN.dat\n" + "FileInfo_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/custom:\n" + "DICOM.xml\n" + "Mobile.xml\n" + "loc\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/custom/loc:\n" + "DICOM_ar_AE.dat\n" + "DICOM_bg_BG.dat\n" + "DICOM_cs_CZ.dat\n" + "DICOM_da_DK.dat\n" + "DICOM_de_DE.dat\n" + "DICOM_el_GR.dat\n" + "DICOM_en_US.dat\n" + "DICOM_es_ES.dat\n" + "DICOM_et_EE.dat\n" + "DICOM_fi_FI.dat\n" + "DICOM_fr_FR.dat\n" + "DICOM_he_IL.dat\n" + "DICOM_hr_HR.dat\n" + "DICOM_hu_HU.dat\n" + "DICOM_it_IT.dat\n" + "DICOM_ja_JP.dat\n" + "DICOM_ko_KR.dat\n" + "DICOM_lt_LT.dat\n" + "DICOM_lv_LV.dat\n" + "DICOM_nb_NO.dat\n" + "DICOM_nl_NL.dat\n" + "DICOM_pl_PL.dat\n" + "DICOM_pt_BR.dat\n" + "DICOM_ro_RO.dat\n" + "DICOM_ru_RU.dat\n" + "DICOM_sk_SK.dat\n" + "DICOM_sl_SI.dat\n" + "DICOM_sv_SE.dat\n" + "DICOM_tr_TR.dat\n" + "DICOM_uk_UA.dat\n" + "DICOM_zh_CN.dat\n" + "DICOM_zh_TW.dat\n" + "Mobile_ar_AE.dat\n" + "Mobile_bg_BG.dat\n" + "Mobile_cs_CZ.dat\n" + "Mobile_da_DK.dat\n" + "Mobile_de_DE.dat\n" + "Mobile_el_GR.dat\n" + "Mobile_en_US.dat\n" + "Mobile_es_ES.dat\n" + "Mobile_et_EE.dat\n" + "Mobile_fi_FI.dat\n" + "Mobile_fr_FR.dat\n" + "Mobile_he_IL.dat\n" + "Mobile_hr_HR.dat\n" + "Mobile_hu_HU.dat\n" + "Mobile_it_IT.dat\n" + "Mobile_ja_JP.dat\n" + "Mobile_ko_KR.dat\n" + "Mobile_lt_LT.dat\n" + "Mobile_lv_LV.dat\n" + "Mobile_nb_NO.dat\n" + "Mobile_nl_NL.dat\n" + "Mobile_pl_PL.dat\n" + "Mobile_pt_BR.dat\n" + "Mobile_ro_RO.dat\n" + "Mobile_ru_RU.dat\n" + "Mobile_sk_SK.dat\n" + "Mobile_sl_SI.dat\n" + "Mobile_sv_SE.dat\n" + "Mobile_tr_TR.dat\n" + "Mobile_uk_UA.dat\n" + "Mobile_zh_CN.dat\n" + "Mobile_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels:\n" + "IPTC\n" + "IPTCExt\n" + "advanced\n" + "audioData\n" + "camera\n" + "categories\n" + "description\n" + "dicom\n" + "gpsData\n" + "history\n" + "mobile\n" + "origin\n" + "rawpacket\n" + "videoData\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTC:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTC/bin:\n" + "iptc.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTC/loc:\n" + "IPTC_ar_AE.dat\n" + "IPTC_bg_BG.dat\n" + "IPTC_cs_CZ.dat\n" + "IPTC_da_DK.dat\n" + "IPTC_de_DE.dat\n" + "IPTC_el_GR.dat\n" + "IPTC_en_US.dat\n" + "IPTC_es_ES.dat\n" + "IPTC_et_EE.dat\n" + "IPTC_fi_FI.dat\n" + "IPTC_fr_FR.dat\n" + "IPTC_he_IL.dat\n" + "IPTC_hr_HR.dat\n" + "IPTC_hu_HU.dat\n" + "IPTC_it_IT.dat\n" + "IPTC_ja_JP.dat\n" + "IPTC_ko_KR.dat\n" + "IPTC_lt_LT.dat\n" + "IPTC_lv_LV.dat\n" + "IPTC_nb_NO.dat\n" + "IPTC_nl_NL.dat\n" + "IPTC_pl_PL.dat\n" + "IPTC_pt_BR.dat\n" + "IPTC_ro_RO.dat\n" + "IPTC_ru_RU.dat\n" + "IPTC_sk_SK.dat\n" + "IPTC_sl_SI.dat\n" + "IPTC_sv_SE.dat\n" + "IPTC_tr_TR.dat\n" + "IPTC_uk_UA.dat\n" + "IPTC_zh_CN.dat\n" + "IPTC_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTCExt:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTCExt/bin:\n" + "iptcExt.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/IPTCExt/loc:\n" + "IPTCExt_bg_BG.dat\n" + "IPTCExt_cs_CZ.dat\n" + "IPTCExt_da_DK.dat\n" + "IPTCExt_de_DE.dat\n" + "IPTCExt_en_US.dat\n" + "IPTCExt_es_ES.dat\n" + "IPTCExt_et_EE.dat\n" + "IPTCExt_fi_FI.dat\n" + "IPTCExt_fr_FR.dat\n" + "IPTCExt_hr_HR.dat\n" + "IPTCExt_hu_HU.dat\n" + "IPTCExt_it_IT.dat\n" + "IPTCExt_ja_JP.dat\n" + "IPTCExt_ko_KR.dat\n" + "IPTCExt_lt_LT.dat\n" + "IPTCExt_lv_LV.dat\n" + "IPTCExt_nb_NO.dat\n" + "IPTCExt_nl_NL.dat\n" + "IPTCExt_pl_PL.dat\n" + "IPTCExt_pt_BR.dat\n" + "IPTCExt_ro_RO.dat\n" + "IPTCExt_ru_RU.dat\n" + "IPTCExt_sk_SK.dat\n" + "IPTCExt_sl_SI.dat\n" + "IPTCExt_sv_SE.dat\n" + "IPTCExt_tr_TR.dat\n" + "IPTCExt_uk_UA.dat\n" + "IPTCExt_zh_CN.dat\n" + "IPTCExt_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/advanced:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/advanced/bin:\n" + "advanced.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/advanced/loc:\n" + "Advanced_ar_AE.dat\n" + "Advanced_bg_BG.dat\n" + "Advanced_cs_CZ.dat\n" + "Advanced_da_DK.dat\n" + "Advanced_de_DE.dat\n" + "Advanced_el_GR.dat\n" + "Advanced_en_US.dat\n" + "Advanced_es_ES.dat\n" + "Advanced_et_EE.dat\n" + "Advanced_fi_FI.dat\n" + "Advanced_fr_FR.dat\n" + "Advanced_he_IL.dat\n" + "Advanced_hr_HR.dat\n" + "Advanced_hu_HU.dat\n" + "Advanced_it_IT.dat\n" + "Advanced_ja_JP.dat\n" + "Advanced_ko_KR.dat\n" + "Advanced_lt_LT.dat\n" + "Advanced_lv_LV.dat\n" + "Advanced_nb_NO.dat\n" + "Advanced_nl_NL.dat\n" + "Advanced_pl_PL.dat\n" + "Advanced_pt_BR.dat\n" + "Advanced_ro_RO.dat\n" + "Advanced_ru_RU.dat\n" + "Advanced_sk_SK.dat\n" + "Advanced_sl_SI.dat\n" + "Advanced_sv_SE.dat\n" + "Advanced_tr_TR.dat\n" + "Advanced_uk_UA.dat\n" + "Advanced_zh_CN.dat\n" + "Advanced_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/audioData:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/audioData/bin:\n" + "audioData.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/audioData/loc:\n" + "AudioData_ar_AE.dat\n" + "AudioData_bg_BG.dat\n" + "AudioData_cs_CZ.dat\n" + "AudioData_da_DK.dat\n" + "AudioData_de_DE.dat\n" + "AudioData_el_GR.dat\n" + "AudioData_en_US.dat\n" + "AudioData_es_ES.dat\n" + "AudioData_et_EE.dat\n" + "AudioData_fi_FI.dat\n" + "AudioData_fr_FR.dat\n" + "AudioData_he_IL.dat\n" + "AudioData_hr_HR.dat\n" + "AudioData_hu_HU.dat\n" + "AudioData_it_IT.dat\n" + "AudioData_ja_JP.dat\n" + "AudioData_ko_KR.dat\n" + "AudioData_lt_LT.dat\n" + "AudioData_lv_LV.dat\n" + "AudioData_nb_NO.dat\n" + "AudioData_nl_NL.dat\n" + "AudioData_pl_PL.dat\n" + "AudioData_pt_BR.dat\n" + "AudioData_ro_RO.dat\n" + "AudioData_ru_RU.dat\n" + "AudioData_sk_SK.dat\n" + "AudioData_sl_SI.dat\n" + "AudioData_sv_SE.dat\n" + "AudioData_tr_TR.dat\n" + "AudioData_uk_UA.dat\n" + "AudioData_zh_CN.dat\n" + "AudioData_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/camera:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/camera/bin:\n" + "camera.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/camera/loc:\n" + "Camera_ar_AE.dat\n" + "Camera_bg_BG.dat\n" + "Camera_cs_CZ.dat\n" + "Camera_da_DK.dat\n" + "Camera_de_DE.dat\n" + "Camera_el_GR.dat\n" + "Camera_en_US.dat\n" + "Camera_es_ES.dat\n" + "Camera_et_EE.dat\n" + "Camera_fi_FI.dat\n" + "Camera_fr_FR.dat\n" + "Camera_he_IL.dat\n" + "Camera_hr_HR.dat\n" + "Camera_hu_HU.dat\n" + "Camera_it_IT.dat\n" + "Camera_ja_JP.dat\n" + "Camera_ko_KR.dat\n" + "Camera_lt_LT.dat\n" + "Camera_lv_LV.dat\n" + "Camera_nb_NO.dat\n" + "Camera_nl_NL.dat\n" + "Camera_pl_PL.dat\n" + "Camera_pt_BR.dat\n" + "Camera_ro_RO.dat\n" + "Camera_ru_RU.dat\n" + "Camera_sk_SK.dat\n" + "Camera_sl_SI.dat\n" + "Camera_sv_SE.dat\n" + "Camera_tr_TR.dat\n" + "Camera_uk_UA.dat\n" + "Camera_zh_CN.dat\n" + "Camera_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/categories:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/categories/bin:\n" + "categories.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/categories/loc:\n" + "Categories_ar_AE.dat\n" + "Categories_bg_BG.dat\n" + "Categories_cs_CZ.dat\n" + "Categories_da_DK.dat\n" + "Categories_de_DE.dat\n" + "Categories_el_GR.dat\n" + "Categories_en_US.dat\n" + "Categories_es_ES.dat\n" + "Categories_et_EE.dat\n" + "Categories_fi_FI.dat\n" + "Categories_fr_FR.dat\n" + "Categories_he_IL.dat\n" + "Categories_hr_HR.dat\n" + "Categories_hu_HU.dat\n" + "Categories_it_IT.dat\n" + "Categories_ja_JP.dat\n" + "Categories_ko_KR.dat\n" + "Categories_lt_LT.dat\n" + "Categories_lv_LV.dat\n" + "Categories_nb_NO.dat\n" + "Categories_nl_NL.dat\n" + "Categories_pl_PL.dat\n" + "Categories_pt_BR.dat\n" + "Categories_ro_RO.dat\n" + "Categories_ru_RU.dat\n" + "Categories_sk_SK.dat\n" + "Categories_sl_SI.dat\n" + "Categories_sv_SE.dat\n" + "Categories_tr_TR.dat\n" + "Categories_uk_UA.dat\n" + "Categories_zh_CN.dat\n" + "Categories_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/description:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/description/bin:\n" + "description.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/description/loc:\n" + "description_ar_AE.dat\n" + "description_bg_BG.dat\n" + "description_cs_CZ.dat\n" + "description_da_DK.dat\n" + "description_de_DE.dat\n" + "description_el_GR.dat\n" + "description_en_US.dat\n" + "description_es_ES.dat\n" + "description_et_EE.dat\n" + "description_fi_FI.dat\n" + "description_fr_FR.dat\n" + "description_he_IL.dat\n" + "description_hr_HR.dat\n" + "description_hu_HU.dat\n" + "description_it_IT.dat\n" + "description_ja_JP.dat\n" + "description_ko_KR.dat\n" + "description_lt_LT.dat\n" + "description_lv_LV.dat\n" + "description_nb_NO.dat\n" + "description_nl_NL.dat\n" + "description_pl_PL.dat\n" + "description_pt_BR.dat\n" + "description_ro_RO.dat\n" + "description_ru_RU.dat\n" + "description_sk_SK.dat\n" + "description_sl_SI.dat\n" + "description_sv_SE.dat\n" + "description_tr_TR.dat\n" + "description_uk_UA.dat\n" + "description_zh_CN.dat\n" + "description_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/dicom:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/dicom/bin:\n" + "dicom.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/dicom/loc:\n" + "DICOM_ar_AE.dat\n" + "DICOM_bg_BG.dat\n" + "DICOM_cs_CZ.dat\n" + "DICOM_da_DK.dat\n" + "DICOM_de_DE.dat\n" + "DICOM_el_GR.dat\n" + "DICOM_en_US.dat\n" + "DICOM_es_ES.dat\n" + "DICOM_et_EE.dat\n" + "DICOM_fi_FI.dat\n" + "DICOM_fr_FR.dat\n" + "DICOM_he_IL.dat\n" + "DICOM_hr_HR.dat\n" + "DICOM_hu_HU.dat\n" + "DICOM_it_IT.dat\n" + "DICOM_ja_JP.dat\n" + "DICOM_ko_KR.dat\n" + "DICOM_lt_LT.dat\n" + "DICOM_lv_LV.dat\n" + "DICOM_nb_NO.dat\n" + "DICOM_nl_NL.dat\n" + "DICOM_pl_PL.dat\n" + "DICOM_pt_BR.dat\n" + "DICOM_ro_RO.dat\n" + "DICOM_ru_RU.dat\n" + "DICOM_sk_SK.dat\n" + "DICOM_sl_SI.dat\n" + "DICOM_sv_SE.dat\n" + "DICOM_tr_TR.dat\n" + "DICOM_uk_UA.dat\n" + "DICOM_zh_CN.dat\n" + "DICOM_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/gpsData:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/gpsData/bin:\n" + "gpsData.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/gpsData/loc:\n" + "GPSData_ar_AE.dat\n" + "GPSData_bg_BG.dat\n" + "GPSData_cs_CZ.dat\n" + "GPSData_da_DK.dat\n" + "GPSData_de_DE.dat\n" + "GPSData_el_GR.dat\n" + "GPSData_en_US.dat\n" + "GPSData_es_ES.dat\n" + "GPSData_et_EE.dat\n" + "GPSData_fi_FI.dat\n" + "GPSData_fr_FR.dat\n" + "GPSData_he_IL.dat\n" + "GPSData_hr_HR.dat\n" + "GPSData_hu_HU.dat\n" + "GPSData_it_IT.dat\n" + "GPSData_ja_JP.dat\n" + "GPSData_ko_KR.dat\n" + "GPSData_lt_LT.dat\n" + "GPSData_lv_LV.dat\n" + "GPSData_nb_NO.dat\n" + "GPSData_nl_NL.dat\n" + "GPSData_pl_PL.dat\n" + "GPSData_pt_BR.dat\n" + "GPSData_ro_RO.dat\n" + "GPSData_ru_RU.dat\n" + "GPSData_sk_SK.dat\n" + "GPSData_sl_SI.dat\n" + "GPSData_sv_SE.dat\n" + "GPSData_tr_TR.dat\n" + "GPSData_uk_UA.dat\n" + "GPSData_zh_CN.dat\n" + "GPSData_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/history:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/history/bin:\n" + "history.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/history/loc:\n" + "History_ar_AE.dat\n" + "History_bg_BG.dat\n" + "History_cs_CZ.dat\n" + "History_da_DK.dat\n" + "History_de_DE.dat\n" + "History_el_GR.dat\n" + "History_en_US.dat\n" + "History_es_ES.dat\n" + "History_et_EE.dat\n" + "History_fi_FI.dat\n" + "History_fr_FR.dat\n" + "History_he_IL.dat\n" + "History_hr_HR.dat\n" + "History_hu_HU.dat\n" + "History_it_IT.dat\n" + "History_ja_JP.dat\n" + "History_ko_KR.dat\n" + "History_lt_LT.dat\n" + "History_lv_LV.dat\n" + "History_nb_NO.dat\n" + "History_nl_NL.dat\n" + "History_pl_PL.dat\n" + "History_pt_BR.dat\n" + "History_ro_RO.dat\n" + "History_ru_RU.dat\n" + "History_sk_SK.dat\n" + "History_sl_SI.dat\n" + "History_sv_SE.dat\n" + "History_tr_TR.dat\n" + "History_uk_UA.dat\n" + "History_zh_CN.dat\n" + "History_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/mobile:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/mobile/bin:\n" + "mobile.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/mobile/loc:\n" + "Mobile_ar_AE.dat\n" + "Mobile_bg_BG.dat\n" + "Mobile_cs_CZ.dat\n" + "Mobile_da_DK.dat\n" + "Mobile_de_DE.dat\n" + "Mobile_el_GR.dat\n" + "Mobile_en_US.dat\n" + "Mobile_es_ES.dat\n" + "Mobile_et_EE.dat\n" + "Mobile_fi_FI.dat\n" + "Mobile_fr_FR.dat\n" + "Mobile_he_IL.dat\n" + "Mobile_hr_HR.dat\n" + "Mobile_hu_HU.dat\n" + "Mobile_it_IT.dat\n" + "Mobile_ja_JP.dat\n" + "Mobile_ko_KR.dat\n" + "Mobile_lt_LT.dat\n" + "Mobile_lv_LV.dat\n" + "Mobile_nb_NO.dat\n" + "Mobile_nl_NL.dat\n" + "Mobile_pl_PL.dat\n" + "Mobile_pt_BR.dat\n" + "Mobile_ro_RO.dat\n" + "Mobile_ru_RU.dat\n" + "Mobile_sk_SK.dat\n" + "Mobile_sl_SI.dat\n" + "Mobile_sv_SE.dat\n" + "Mobile_tr_TR.dat\n" + "Mobile_uk_UA.dat\n" + "Mobile_zh_CN.dat\n" + "Mobile_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/origin:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/origin/bin:\n" + "origin.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/origin/loc:\n" + "origin_ar_AE.dat\n" + "origin_bg_BG.dat\n" + "origin_cs_CZ.dat\n" + "origin_da_DK.dat\n" + "origin_de_DE.dat\n" + "origin_el_GR.dat\n" + "origin_en_US.dat\n" + "origin_es_ES.dat\n" + "origin_et_EE.dat\n" + "origin_fi_FI.dat\n" + "origin_fr_FR.dat\n" + "origin_he_IL.dat\n" + "origin_hr_HR.dat\n" + "origin_hu_HU.dat\n" + "origin_it_IT.dat\n" + "origin_ja_JP.dat\n" + "origin_ko_KR.dat\n" + "origin_lt_LT.dat\n" + "origin_lv_LV.dat\n" + "origin_nb_NO.dat\n" + "origin_nl_NL.dat\n" + "origin_pl_PL.dat\n" + "origin_pt_BR.dat\n" + "origin_ro_RO.dat\n" + "origin_ru_RU.dat\n" + "origin_sk_SK.dat\n" + "origin_sl_SI.dat\n" + "origin_sv_SE.dat\n" + "origin_tr_TR.dat\n" + "origin_uk_UA.dat\n" + "origin_zh_CN.dat\n" + "origin_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/rawpacket:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/rawpacket/bin:\n" + "rawpacket.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/rawpacket/loc:\n" + "Rawpacket_ar_AE.dat\n" + "Rawpacket_bg_BG.dat\n" + "Rawpacket_cs_CZ.dat\n" + "Rawpacket_da_DK.dat\n" + "Rawpacket_de_DE.dat\n" + "Rawpacket_el_GR.dat\n" + "Rawpacket_en_US.dat\n" + "Rawpacket_es_ES.dat\n" + "Rawpacket_et_EE.dat\n" + "Rawpacket_fi_FI.dat\n" + "Rawpacket_fr_FR.dat\n" + "Rawpacket_he_IL.dat\n" + "Rawpacket_hr_HR.dat\n" + "Rawpacket_hu_HU.dat\n" + "Rawpacket_it_IT.dat\n" + "Rawpacket_ja_JP.dat\n" + "Rawpacket_ko_KR.dat\n" + "Rawpacket_lt_LT.dat\n" + "Rawpacket_lv_LV.dat\n" + "Rawpacket_nb_NO.dat\n" + "Rawpacket_nl_NL.dat\n" + "Rawpacket_pl_PL.dat\n" + "Rawpacket_pt_BR.dat\n" + "Rawpacket_ro_RO.dat\n" + "Rawpacket_ru_RU.dat\n" + "Rawpacket_sk_SK.dat\n" + "Rawpacket_sl_SI.dat\n" + "Rawpacket_sv_SE.dat\n" + "Rawpacket_tr_TR.dat\n" + "Rawpacket_uk_UA.dat\n" + "Rawpacket_zh_CN.dat\n" + "Rawpacket_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/videoData:\n" + "bin\n" + "loc\n" + "manifest.xml\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/videoData/bin:\n" + "videoData.swf\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Application Data/Custom File Info " + "Panels/4.0/panels/videoData/loc:\n" + "VideoData_ar_AE.dat\n" + "VideoData_bg_BG.dat\n" + "VideoData_cs_CZ.dat\n" + "VideoData_da_DK.dat\n" + "VideoData_de_DE.dat\n" + "VideoData_el_GR.dat\n" + "VideoData_en_US.dat\n" + "VideoData_es_ES.dat\n" + "VideoData_et_EE.dat\n" + "VideoData_fi_FI.dat\n" + "VideoData_fr_FR.dat\n" + "VideoData_he_IL.dat\n" + "VideoData_hr_HR.dat\n" + "VideoData_hu_HU.dat\n" + "VideoData_it_IT.dat\n" + "VideoData_ja_JP.dat\n" + "VideoData_ko_KR.dat\n" + "VideoData_lt_LT.dat\n" + "VideoData_lv_LV.dat\n" + "VideoData_nb_NO.dat\n" + "VideoData_nl_NL.dat\n" + "VideoData_pl_PL.dat\n" + "VideoData_pt_BR.dat\n" + "VideoData_ro_RO.dat\n" + "VideoData_ru_RU.dat\n" + "VideoData_sk_SK.dat\n" + "VideoData_sl_SI.dat\n" + "VideoData_sv_SE.dat\n" + "VideoData_tr_TR.dat\n" + "VideoData_uk_UA.dat\n" + "VideoData_zh_CN.dat\n" + "VideoData_zh_TW.dat\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks:\n" + "AdbeScriptUIFlex.framework\n" + "AdobeACE.framework\n" + "AdobeAGM.framework\n" + "AdobeAXE8SharedExpat.framework\n" + "AdobeAXEDOMCore.framework\n" + "AdobeBIB.framework\n" + "AdobeBIBUtils.framework\n" + "AdobeCoolType.framework\n" + "AdobeCrashReporter.framework\n" + "AdobeExtendScript.framework\n" + "AdobeLinguistic.framework\n" + "AdobeMPS.framework\n" + "AdobeOwl.framework\n" + "AdobePDFSettings.framework\n" + "AdobePIP.framework\n" + "AdobeScCore.framework\n" + "AdobeUpdater.framework\n" + "AdobeXMP.framework\n" + "AdobeXMPFiles.framework\n" + "AdobeXMPScript.framework\n" + "AlignmentLib.framework\n" + "CIT\n" + "CIT.framework\n" + "CITThreading.framework\n" + "Cg.framework\n" + "FileInfo.framework\n" + "ICUConverter.framework\n" + "ICUData.framework\n" + "IMSLib.dylib\n" + "LogSession.framework\n" + "PlugPlugOwl.framework\n" + "WRServices.framework\n" + "adbeape.framework\n" + "adobe_caps.framework\n" + "adobejp2k.framework\n" + "adobepdfl.framework\n" + "ahclient.framework\n" + "aif_core.framework\n" + "aif_ocl.framework\n" + "aif_ogl.framework\n" + "amtlib.framework\n" + "boost_date_time.framework\n" + "boost_signals.framework\n" + "boost_system.framework\n" + "boost_threads.framework\n" + "dvaaudiodevice.framework\n" + "dvacore.framework\n" + "dvamarshal.framework\n" + "dvamediatypes.framework\n" + "dvaplayer.framework\n" + "dvatransport.framework\n" + "dvaunittesting.framework\n" + "dynamiclink.framework\n" + "filter_graph.framework\n" + "libtbb.dylib\n" + "libtbbmalloc.dylib\n" + "mediacoreif.framework\n" + "patchmatch.framework\n" + "updaternotifications.framework\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework:\n" + "AdbeScriptUIFlex\n" + "Resources\n" + "Versions\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework/Versions:\n" + "A\n" + "Current\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework/Versions/A:\n" + "AdbeScriptUIFlex\n" + "CodeResources\n" + "_CodeSignature\n" + "resources\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework/Versions/A/" + "_CodeSignature:\n" + "CodeResources\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework/Versions/A/" + "resources:\n" + "Info.plist\n" + "english.lproj\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdbeScriptUIFlex.framework/Versions/A/" + "resources/english.lproj:\n" + "InfoPlist.strings\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdobeACE.framework:\n" + "AdobeACE\n" + "Versions\n" + "resources\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdobeACE.framework/Versions:\n" + "A\n" + "current\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdobeACE.framework/Versions/A:\n" + "AdobeACE\n" + "CodeResources\n" + "_CodeSignature\n" + "resources\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdobeACE.framework/Versions/A/" + "_CodeSignature:\n" + "CodeResources\n" + "\n" + "/Applications/Adobe Photoshop CC/Adobe Photoshop " + "CC.app/Contents/Frameworks/AdobeACE.framework/Versions/A/" + "resources:\n" + "Info.plist\n" + "english.lproj\n" + "" + << 123 << 123.123 << "haha" << '\n' + << std::string("12356"); + LOG_RAW << "Testing finished\n"; + LOG_RAW_TO(5) << "Testing finished\n"; + thread_.join(); +} diff --git a/modules/http_trantor/trantor/tests/RunInLoopTest1.cc b/modules/http_trantor/trantor/tests/RunInLoopTest1.cc new file mode 100644 index 0000000..d5c6951 --- /dev/null +++ b/modules/http_trantor/trantor/tests/RunInLoopTest1.cc @@ -0,0 +1,30 @@ +// +// Created by antao on 1/14/17. +// + +#include +#ifndef _WIN32 +#include +#endif + +#include +#include +#include +using namespace std::chrono_literals; +int main() +{ + trantor::EventLoop loop; + std::thread thread([&loop]() { + std::this_thread::sleep_for(3s); + loop.runInLoop([&loop]() { + std::cout << "runInLoop called in other thread" << std::endl; + loop.queueInLoop( + []() { std::cout << "queueInLoop in runInLoop" << std::endl; }); + }); + }); + loop.runInLoop([]() { std::cout << "runInLoop 1" << std::endl; }); + loop.runInLoop([]() { std::cout << "runInLoop 2" << std::endl; }); + loop.queueInLoop([]() { std::cout << "queueInLoop 1" << std::endl; }); + loop.runAfter(1.5, []() { std::cout << "run after 1.5" << std::endl; }); + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/RunInLoopTest2.cc b/modules/http_trantor/trantor/tests/RunInLoopTest2.cc new file mode 100644 index 0000000..ce969a3 --- /dev/null +++ b/modules/http_trantor/trantor/tests/RunInLoopTest2.cc @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +int main() +{ + std::atomic counter; + counter = 0; + std::promise pro; + auto ft = pro.get_future(); + trantor::EventLoopThread loopThread; + + auto loop = loopThread.getLoop(); + loop->runInLoop([&counter, &pro, loop]() { + for (int i = 0; i < 10000; ++i) + { + loop->queueInLoop([&counter, &pro]() { + ++counter; + if (counter.load() == 110000) + pro.set_value(1); + }); + } + }); + for (int i = 0; i < 10; ++i) + { + std::thread([&counter, loop, &pro]() { + for (int i = 0; i < 10000; ++i) + { + loop->runInLoop([&counter, &pro]() { + ++counter; + if (counter.load() == 110000) + pro.set_value(1); + }); + } + }).detach(); + } + loopThread.run(); + ft.get(); + std::cout << "counter=" << counter.load() << std::endl; +} diff --git a/modules/http_trantor/trantor/tests/RunOnQuitTest.cc b/modules/http_trantor/trantor/tests/RunOnQuitTest.cc new file mode 100644 index 0000000..37f9572 --- /dev/null +++ b/modules/http_trantor/trantor/tests/RunOnQuitTest.cc @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +int main() +{ + std::atomic flag(false); + { + trantor::EventLoopThread thr; + thr.getLoop()->runOnQuit([&]() { flag = true; }); + thr.run(); + thr.getLoop()->quit(); + } + + if (flag == false) + { + std::cerr << "Test failed\n"; + } + else + { + std::cout << "Success\n"; + } +} diff --git a/modules/http_trantor/trantor/tests/SSLClientTest.cc b/modules/http_trantor/trantor/tests/SSLClientTest.cc new file mode 100644 index 0000000..f38a0f3 --- /dev/null +++ b/modules/http_trantor/trantor/tests/SSLClientTest.cc @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + trantor::Logger::setLogLevel(trantor::Logger::kTrace); + LOG_DEBUG << "TcpClient class test!"; + EventLoop loop; +#if USE_IPV6 + InetAddress serverAddr("::1", 8888, true); +#else + InetAddress serverAddr("127.0.0.1", 8888); +#endif + std::shared_ptr client[10]; + std::atomic_int connCount; + connCount = 1; + for (int i = 0; i < connCount; ++i) + { + client[i] = std::make_shared(&loop, + serverAddr, + "tcpclienttest"); + client[i]->enableSSL(false, false); + client[i]->setConnectionCallback( + [i, &loop, &connCount](const TcpConnectionPtr &conn) { + if (conn->connected()) + { + LOG_DEBUG << i << " connected!"; + char tmp[20]; + sprintf(tmp, "%d client!!", i); + conn->send(tmp); + } + else + { + LOG_DEBUG << i << " disconnected"; + --connCount; + if (connCount == 0) + loop.quit(); + } + }); + client[i]->setMessageCallback( + [](const TcpConnectionPtr &conn, MsgBuffer *buf) { + LOG_DEBUG << std::string(buf->peek(), buf->readableBytes()); + buf->retrieveAll(); + conn->shutdown(); + }); + client[i]->connect(); + } + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/SSLServerTest.cc b/modules/http_trantor/trantor/tests/SSLServerTest.cc new file mode 100644 index 0000000..3cf5213 --- /dev/null +++ b/modules/http_trantor/trantor/tests/SSLServerTest.cc @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + LOG_DEBUG << "test start"; + Logger::setLogLevel(Logger::kTrace); + EventLoopThread loopThread; + loopThread.run(); +#if USE_IPV6 + InetAddress addr(8888, true, true); +#else + InetAddress addr(8888); +#endif + TcpServer server(loopThread.getLoop(), addr, "test"); + server.enableSSL("server.pem", "server.pem"); + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + // LOG_DEBUG<<"recv callback!"; + std::cout << std::string(buffer->peek(), buffer->readableBytes()); + connectionPtr->send(buffer->peek(), buffer->readableBytes()); + buffer->retrieveAll(); + connectionPtr->forceClose(); + }); + server.setConnectionCallback([](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + LOG_DEBUG << "New connection"; + connPtr->send("Hello world\r\n"); + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loopThread.wait(); +} diff --git a/modules/http_trantor/trantor/tests/SendfileTest.cc b/modules/http_trantor/trantor/tests/SendfileTest.cc new file mode 100644 index 0000000..c0abb5b --- /dev/null +++ b/modules/http_trantor/trantor/tests/SendfileTest.cc @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +using namespace trantor; +#define USE_IPV6 0 +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + std::cout << "usage:" << argv[0] << " filename" << std::endl; + return 1; + } + std::cout << "filename:" << argv[1] << std::endl; + struct stat filestat; + if (stat(argv[1], &filestat) < 0) + { + perror(""); + exit(1); + } + std::cout << "file len=" << filestat.st_size << std::endl; + + auto fp = fopen(argv[1], "rb"); + + if (fp == nullptr) + { + perror(""); + exit(1); + } + fclose(fp); + + LOG_DEBUG << "test start"; + + Logger::setLogLevel(Logger::kTrace); + EventLoopThread loopThread; + loopThread.run(); + +#if USE_IPV6 + InetAddress addr(1207, true, true); +#else + InetAddress addr(1207); +#endif + TcpServer server(loopThread.getLoop(), addr, "test"); + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + // LOG_DEBUG<<"recv callback!"; + }); + int counter = 0; + server.setConnectionCallback( + [argv, &counter](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + LOG_DEBUG << "New connection"; + std::thread t([connPtr, argv, &counter]() { + for (int i = 0; i < 5; ++i) + { + connPtr->sendFile(argv[1]); + char str[64]; + ++counter; + sprintf(str, "\n%d files sent!\n", counter); + connPtr->send(str, strlen(str)); + } + }); + t.detach(); + + for (int i = 0; i < 3; ++i) + { + connPtr->sendFile(argv[1]); + char str[64]; + ++counter; + sprintf(str, "\n%d files sent!\n", counter); + connPtr->send(str, strlen(str)); + } + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loopThread.wait(); +} diff --git a/modules/http_trantor/trantor/tests/SerialTaskQueueTest1.cc b/modules/http_trantor/trantor/tests/SerialTaskQueueTest1.cc new file mode 100755 index 0000000..b989d32 --- /dev/null +++ b/modules/http_trantor/trantor/tests/SerialTaskQueueTest1.cc @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; +int main() +{ + trantor::Logger::setLogLevel(trantor::Logger::kTrace); + trantor::SerialTaskQueue queue1("test queue1"); + trantor::SerialTaskQueue queue2(""); + queue1.runTaskInQueue([&]() { + for (int i = 0; i < 5; ++i) + { + std::this_thread::sleep_for(1s); + printf("task(%s) i=%d\n", queue1.getName().c_str(), i); + } + }); + queue2.runTaskInQueue([&]() { + for (int i = 0; i < 5; ++i) + { + std::this_thread::sleep_for(1s); + printf("task(%s) i=%d\n", queue2.getName().c_str(), i); + } + }); + queue1.waitAllTasksFinished(); + queue2.waitAllTasksFinished(); +} diff --git a/modules/http_trantor/trantor/tests/SerialTaskQueueTest2.cc b/modules/http_trantor/trantor/tests/SerialTaskQueueTest2.cc new file mode 100644 index 0000000..59068fe --- /dev/null +++ b/modules/http_trantor/trantor/tests/SerialTaskQueueTest2.cc @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +int main() +{ + std::atomic counter; + counter = 0; + std::promise pro; + auto ft = pro.get_future(); + trantor::SerialTaskQueue queue(""); + queue.runTaskInQueue([&counter, &pro, &queue]() { + for (int i = 0; i < 10000; ++i) + { + queue.runTaskInQueue([&counter, &pro]() { + ++counter; + if (counter.load() == 110000) + pro.set_value(1); + }); + } + }); + for (int i = 0; i < 10; ++i) + { + std::thread([&counter, &queue, &pro]() { + for (int i = 0; i < 10000; ++i) + { + queue.runTaskInQueue([&counter, &pro]() { + ++counter; + if (counter.load() == 110000) + pro.set_value(1); + }); + } + }).detach(); + } + + ft.get(); + std::cout << "counter=" << counter.load() << std::endl; +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/tests/TcpClientTest.cc b/modules/http_trantor/trantor/tests/TcpClientTest.cc new file mode 100644 index 0000000..68d6f89 --- /dev/null +++ b/modules/http_trantor/trantor/tests/TcpClientTest.cc @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + trantor::Logger::setLogLevel(trantor::Logger::kTrace); + LOG_DEBUG << "TcpClient class test!"; + EventLoop loop; +#if USE_IPV6 + InetAddress serverAddr("::1", 8888, true); +#else + InetAddress serverAddr("127.0.0.1", 8888); +#endif + std::shared_ptr client[10]; + std::atomic_int connCount; + connCount = 10; + for (int i = 0; i < 10; ++i) + { + client[i] = std::make_shared(&loop, + serverAddr, + "tcpclienttest"); + client[i]->setConnectionCallback( + [i, &loop, &connCount](const TcpConnectionPtr &conn) { + if (conn->connected()) + { + LOG_DEBUG << i << " connected!"; + char tmp[20]; + sprintf(tmp, "%d client!!", i); + conn->send(tmp); + } + else + { + LOG_DEBUG << i << " disconnected"; + --connCount; + if (connCount == 0) + loop.quit(); + } + }); + client[i]->setMessageCallback( + [](const TcpConnectionPtr &conn, MsgBuffer *buf) { + LOG_DEBUG << std::string(buf->peek(), buf->readableBytes()); + buf->retrieveAll(); + conn->shutdown(); + }); + client[i]->connect(); + } + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/TcpServerTest.cc b/modules/http_trantor/trantor/tests/TcpServerTest.cc new file mode 100644 index 0000000..dbd8b7c --- /dev/null +++ b/modules/http_trantor/trantor/tests/TcpServerTest.cc @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +using namespace trantor; +#define USE_IPV6 0 +int main() +{ + LOG_DEBUG << "test start"; + Logger::setLogLevel(Logger::kTrace); + EventLoopThread loopThread; + loopThread.run(); +#if USE_IPV6 + InetAddress addr(8888, true, true); +#else + InetAddress addr(8888); +#endif + TcpServer server(loopThread.getLoop(), addr, "test"); + server.setRecvMessageCallback( + [](const TcpConnectionPtr &connectionPtr, MsgBuffer *buffer) { + // LOG_DEBUG<<"recv callback!"; + std::cout << std::string(buffer->peek(), buffer->readableBytes()); + connectionPtr->send(buffer->peek(), buffer->readableBytes()); + buffer->retrieveAll(); + // connectionPtr->forceClose(); + }); + server.setConnectionCallback([](const TcpConnectionPtr &connPtr) { + if (connPtr->connected()) + { + LOG_DEBUG << "New connection"; + } + else if (connPtr->disconnected()) + { + LOG_DEBUG << "connection disconnected"; + } + }); + server.setIoLoopNum(3); + server.start(); + loopThread.wait(); +} diff --git a/modules/http_trantor/trantor/tests/TimerTest.cc b/modules/http_trantor/trantor/tests/TimerTest.cc new file mode 100644 index 0000000..606c45e --- /dev/null +++ b/modules/http_trantor/trantor/tests/TimerTest.cc @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +using namespace std::literals; + +int main() +{ + trantor::Logger::setLogLevel(trantor::Logger::kTrace); + trantor::EventLoop loop; + auto id1 = loop.runAfter(1s, []() { + LOG_ERROR << "This info shouldn't be displayed!"; + }); + loop.invalidateTimer(id1); + auto id2 = loop.runEvery(0.3s, []() { + LOG_ERROR << "This timer will be invalidated after 3 second;"; + }); + std::thread thread([id2, &loop]() { + std::this_thread::sleep_for(3s); + loop.invalidateTimer(id2); + }); + thread.detach(); + loop.runEvery(3, []() { LOG_DEBUG << " runEvery 3s"; }); + loop.runAt(trantor::Date::date().after(10), + []() { LOG_DEBUG << "runAt 10s later"; }); + loop.runAfter(5, []() { std::cout << "runAt 5s later" << std::endl; }); + loop.runEvery(1, []() { std::cout << "runEvery 1s" << std::endl; }); + loop.runAfter(4, []() { std::cout << "runAfter 4s" << std::endl; }); + loop.runAfter(10min, + []() { std::cout << "*********** run after 10 min\n"; }); + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/TimerTest1.cc b/modules/http_trantor/trantor/tests/TimerTest1.cc new file mode 100644 index 0000000..09884c5 --- /dev/null +++ b/modules/http_trantor/trantor/tests/TimerTest1.cc @@ -0,0 +1,23 @@ +#include +#include +#include +int main() +{ + trantor::EventLoop loop; + LOG_FATAL << trantor::Date::date().roundDay().microSecondsSinceEpoch(); + trantor::Date begin = trantor::Date::date().roundSecond().after(2); + auto id = loop.runAt(begin, [begin, &loop]() { + LOG_DEBUG << "test begin:"; + srand((unsigned int)time(NULL)); + for (int i = 0; i < 10000; ++i) + { + int aa = rand() % 10000; + double s = (double)aa / 1000.0 + 1; + loop.runAt(begin.after(s), + [s]() { LOG_ERROR << "run After:" << s; }); + } + LOG_DEBUG << "timer created!"; + }); + std::cout << "id=" << id << std::endl; + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/TimingWheelTest.cc b/modules/http_trantor/trantor/tests/TimingWheelTest.cc new file mode 100644 index 0000000..c9ae1b5 --- /dev/null +++ b/modules/http_trantor/trantor/tests/TimingWheelTest.cc @@ -0,0 +1,48 @@ +#include +#include +#include +class MyClass +{ + public: + MyClass(); + MyClass(MyClass &&) = default; + MyClass(const MyClass &) = default; + MyClass &operator=(MyClass &&) = default; + MyClass &operator=(const MyClass &) = default; + ~MyClass(); + + private: +}; + +MyClass::MyClass() +{ +} + +MyClass::~MyClass() +{ + LOG_DEBUG << "MyClass destructed!"; +} + +int main() +{ + LOG_DEBUG << "start"; + trantor::EventLoop loop; + std::weak_ptr weakEntry; + trantor::TimingWheel wheel(&loop, 75, 0.1, 100); + { + auto entry = std::shared_ptr(new MyClass); + + wheel.insertEntry(75, entry); + weakEntry = entry; + } + // loop.runAfter(5.0, [&]() { + // wheel.insertEntry(10, weakEntry.lock()); + // }); + // loop.runAfter(0.5,[&](){ + // wheel.insertEntry(28,weakEntry.lock()); + // }); + // loop.runEvery(1.0,[](){ + // LOG_DEBUG<<"tick"; + // }); + loop.loop(); +} diff --git a/modules/http_trantor/trantor/tests/server.pem b/modules/http_trantor/trantor/tests/server.pem new file mode 100644 index 0000000..507f700 --- /dev/null +++ b/modules/http_trantor/trantor/tests/server.pem @@ -0,0 +1,49 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/vLeZ4B80b22y +JSF9WoAEGo5D0GLlzSxL7rRZSd81h3YMKpsgFT4WkZxs9sqoRW/OTaqMGuEzGt6a +54RMwycQwJL+GGt/stZ+7AYHoLE6cQzP8iFcwy4mLFsX+gQjoE0UHr0RykKRl3Vw +qZST74AhAMlFvEDCfoMxTy9dy5ZjbLTFrH1kvo5CT9MEbyBLpxYrYYFjBAMIZdvV +82Mz3JftDLbx7t+96PaGLpDMLk9NgrV1kbUpzck/aCbZ5DgV7enTU33tVIcfMU5e +fZii044OkOvFNXibi6tdPORP8TBnrmEZjmDnFAgEijzpIcJ4VVPTrlCuxLUKcSDY +wYLro7gbAgMBAAECggEAesztcnoewkCTq0MovdZWo0o2z6wJi1DrC/7oNz+e2/PU +YVpwXA3+5AmCfC9cAIXoY+NOVclpbofJBsE89MUQoiQUgPU29GSgCE42VnBO0jVR +lWVohLblOcGy3hpcyEyE0VwWj+xQ0lqE9xFFfbIpB/ou7qDxgR/x+oTSu2oG+cmr +xv+Dj+k46QW0X2A3XHnlZkk0fQGb+Tf3fIJ4BBwF/UFQTDyZPZrsZfbpoEk1BnIf +VPcfM1/ca6h1t/oEnpD66H2TjxYzZC58mqZSZIZ+Pk+wocSsF5UcQLedcM137LDH +OR+fdscinr1xgMhV30RmAUP5tug9Km3oq8wf4Xl1IQKBgQDmDtYbwN7V1eA6Dbeq +HCk07t/llK+j4mvyuU+pVKfNHmnUUd+NPpDwREFrfoR6ilrZMJ4Obl7z53rja+9s +JqECBThhRzHxnUQgsu3DQeY7e2KxBBGGvhQBjRGtVRID3aFTTyMKvaOkTc7kL+x1 +5DbtoVAA1kbYnjgZJyZosx+bmQKBgQDVW6lXxoLzC7npXdXOTMJifExcCIzXskc9 +5b+rJTXgkqaPQSQ7nNmRReUY9r9UBsZKLduuYJZ3TLMCaU91/wBwc1ygkQvRz+8A +2QGMovlVOqwPAOEc1lMCXzQki9PcqmVX/e0oFmTtUgyF4RKEwL/q8A9fWy587rkQ +57MDNJ3h0wKBgQDNJgPFweq0EsGd4yeZuP0B59Wee0VYxgru6lLgM85iujEzFUNd +R6KlrqgLvElUoNW8gX8gbUmdBBlwfYqGDbhb/d212W/u/geHhSdCjBxLhI6QPYmH +dy6N54cQ4yBqdBNtH8+mv08SsBPDJf0db8GPi960sF+CwSxTObclfD2+WQKBgBgi +HxyLmsJNIEFSWN3V9uLW9ngui2fWhZJty2lbcyWs0ORBVQzdKArzof9Z4bhqb8Fy +QHgP+tURuunZ6aAKMQ2HLwIGhhS8dWdeJHu474UBdvbXfZ8aaxdIl4hOvK8oIwB5 ++3peVho1/q6iD8suVkcH0mVR1gdRpWNRIgGJ0RX7AoGBAMzp0u3X3SEMWTlqFR7D +BJqmpiMvJJJSqex1i4PJoy+uJpqfl3b1jUMnFIS4GiscVCIEJkPpZaz3eSTt7aVQ ++7v0b/Cusv1qYX2c5v9j9x7nmUlfdwT5rY2g6RXI/xlhPD9elb1dxlzmhiO4VQtr +AdiFmu0poyPpEGwjpcLEZq6K +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDajCCAlICCQCXmD7IkkDArzANBgkqhkiG9w0BAQsFADB3MQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEPMA0GA1UECgwG +YW4tdGFvMQ8wDQYDVQQLDAZhbi10YW8xIjAgBgkqhkiG9w0BCQEWE2FudGFvMjAw +MkBnbWFpbC5jb20wHhcNMTgwNzIxMDEyMjEyWhcNMTkwNzIxMDEyMjEyWjB3MQsw +CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEP +MA0GA1UECgwGYW4tdGFvMQ8wDQYDVQQLDAZhbi10YW8xIjAgBgkqhkiG9w0BCQEW +E2FudGFvMjAwMkBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC/vLeZ4B80b22yJSF9WoAEGo5D0GLlzSxL7rRZSd81h3YMKpsgFT4WkZxs +9sqoRW/OTaqMGuEzGt6a54RMwycQwJL+GGt/stZ+7AYHoLE6cQzP8iFcwy4mLFsX ++gQjoE0UHr0RykKRl3VwqZST74AhAMlFvEDCfoMxTy9dy5ZjbLTFrH1kvo5CT9ME +byBLpxYrYYFjBAMIZdvV82Mz3JftDLbx7t+96PaGLpDMLk9NgrV1kbUpzck/aCbZ +5DgV7enTU33tVIcfMU5efZii044OkOvFNXibi6tdPORP8TBnrmEZjmDnFAgEijzp +IcJ4VVPTrlCuxLUKcSDYwYLro7gbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAClA +Z6JHessY1mo0ObCp8Sh83gIiGu9M77xz6Gy2vUpUGE9N1hJwYH3uZPLbpnIuxZU0 +uDXd9w5vCJmtqo/4X6sedqKA6Upi8DROawtCUJaGrHrb2vJONyL7A3OWpHyYgOAa +z+sl4jbrGGv4azSg9ef1mQZTDcEcdsYiKj9U4zoyDXteyt6NQK3x5BtziB0mJfeM +gjrltVcc59s8Z2jf3PI3qmWtMuJm7KGHlan2DUz12AZxY3sJrFyew3Vi1V2uvIZB +YwgcXbVa3r6PQU3RwFwkazxAe80ZgiQlVsEiBl8mre4zznt5XHF6mzAqvpHyXPCI +LfEO3waLkgB94mFha5A= +-----END CERTIFICATE----- diff --git a/modules/http_trantor/trantor/third_party/wepoll/LICENSE b/modules/http_trantor/trantor/third_party/wepoll/LICENSE new file mode 100644 index 0000000..8753814 --- /dev/null +++ b/modules/http_trantor/trantor/third_party/wepoll/LICENSE @@ -0,0 +1,28 @@ +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll + +Copyright 2012-2020, Bert Belder +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/modules/http_trantor/trantor/third_party/wepoll/README.md b/modules/http_trantor/trantor/third_party/wepoll/README.md new file mode 100644 index 0000000..fc9ce85 --- /dev/null +++ b/modules/http_trantor/trantor/third_party/wepoll/README.md @@ -0,0 +1,41 @@ +## wepoll + +This library is based on [wepoll v1.5.8](https://github.com/piscisaureus/wepoll/commit/0598a791bf9cbbf480793d778930fc635b044980). + +An eventfd-like mechanism is added to it. After making the changes, we can wake up `trantor::EventLoop` from the epoll_wait() function. + +## Modifications + +```shell +diff wepoll.h Wepoll.h +53a54 +> EPOLLEVENT = (int)(1U << 14), +67a69 +> #define EPOLLEVENT (1U << 14) +111a114 +> WEPOLL_EXPORT void epoll_post_signal(HANDLE ephnd, uint64_t event); +``` + +```shell +diff wepoll.c Wepoll.c +50a51 +> EPOLLEVENT = (int)(1U << 14), +64a66 +> #define EPOLLEVENT (1U << 14) +1262a1265,1271 +> if (iocp_events[i].lpCompletionKey) +> { +> struct epoll_event* ev = &epoll_events[epoll_event_count++]; +> ev->data.u64 = (uint64_t)iocp_events[i].lpCompletionKey; +> ev->events = EPOLLEVENT; +> continue; +> } +2441a2451,2457 +> void epoll_post_signal(HANDLE port_handle, uint64_t event) +> { +> ULONG_PTR ev; +> ev = (ULONG_PTR)event; +> PostQueuedCompletionStatus(port_handle, 1, ev, NULL); +> } +> +``` diff --git a/modules/http_trantor/trantor/third_party/wepoll/Wepoll.c b/modules/http_trantor/trantor/third_party/wepoll/Wepoll.c new file mode 100644 index 0000000..5b8ee51 --- /dev/null +++ b/modules/http_trantor/trantor/third_party/wepoll/Wepoll.c @@ -0,0 +1,2491 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2020, Bert Belder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WEPOLL_EXPORT +#define WEPOLL_EXPORT +#endif + +#include + +enum EPOLL_EVENTS +{ + EPOLLIN = (int)(1U << 0), + EPOLLPRI = (int)(1U << 1), + EPOLLOUT = (int)(1U << 2), + EPOLLERR = (int)(1U << 3), + EPOLLHUP = (int)(1U << 4), + EPOLLRDNORM = (int)(1U << 6), + EPOLLRDBAND = (int)(1U << 7), + EPOLLWRNORM = (int)(1U << 8), + EPOLLWRBAND = (int)(1U << 9), + EPOLLMSG = (int)(1U << 10), /* Never reported. */ + EPOLLRDHUP = (int)(1U << 13), + EPOLLEVENT = (int)(1U << 14), + EPOLLONESHOT = (int)(1U << 31) +}; + +#define EPOLLIN (1U << 0) +#define EPOLLPRI (1U << 1) +#define EPOLLOUT (1U << 2) +#define EPOLLERR (1U << 3) +#define EPOLLHUP (1U << 4) +#define EPOLLRDNORM (1U << 6) +#define EPOLLRDBAND (1U << 7) +#define EPOLLWRNORM (1U << 8) +#define EPOLLWRBAND (1U << 9) +#define EPOLLMSG (1U << 10) +#define EPOLLRDHUP (1U << 13) +#define EPOLLEVENT (1U << 14) +#define EPOLLONESHOT (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_MOD 2 +#define EPOLL_CTL_DEL 3 + +typedef void* HANDLE; +typedef uintptr_t SOCKET; + +typedef union epoll_data +{ + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; + +struct epoll_event +{ + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WEPOLL_EXPORT HANDLE epoll_create(int size); + WEPOLL_EXPORT HANDLE epoll_create1(int flags); + + WEPOLL_EXPORT int epoll_close(HANDLE ephnd); + + WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); + + WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include + +#include + +#define WEPOLL_INTERNAL static +#define WEPOLL_INTERNAL_EXTERN static + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#elif defined(_MSC_VER) +#pragma warning(push, 1) +#endif + +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN + +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 + +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +WEPOLL_INTERNAL int nt_global_init(void); + +typedef LONG NTSTATUS; +typedef NTSTATUS* PNTSTATUS; + +#ifndef NT_SUCCESS +#define NT_SUCCESS(status) (((NTSTATUS)(status)) >= 0) +#endif + +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif + +#ifndef STATUS_PENDING +#define STATUS_PENDING ((NTSTATUS)0x00000103L) +#endif + +#ifndef STATUS_CANCELLED +#define STATUS_CANCELLED ((NTSTATUS)0xC0000120L) +#endif + +#ifndef STATUS_NOT_FOUND +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) +#endif + +typedef struct _IO_STATUS_BLOCK +{ + NTSTATUS Status; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG Reserved); + +typedef struct _UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +#define RTL_CONSTANT_STRING(s) \ + { \ + sizeof(s) - sizeof((s)[0]), sizeof(s), s \ + } + +typedef struct _OBJECT_ATTRIBUTES +{ + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +#define RTL_CONSTANT_OBJECT_ATTRIBUTES(ObjectName, Attributes) \ + { \ + sizeof(OBJECT_ATTRIBUTES), NULL, ObjectName, Attributes, NULL, NULL \ + } + +#ifndef FILE_OPEN +#define FILE_OPEN 0x00000001UL +#endif + +#define KEYEDEVENT_WAIT 0x00000001UL +#define KEYEDEVENT_WAKE 0x00000002UL +#define KEYEDEVENT_ALL_ACCESS \ + (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) + +#define NT_NTDLL_IMPORT_LIST(X) \ + X(NTSTATUS, \ + NTAPI, \ + NtCancelIoFileEx, \ + (HANDLE FileHandle, \ + PIO_STATUS_BLOCK IoRequestToCancel, \ + PIO_STATUS_BLOCK IoStatusBlock)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateFile, \ + (PHANDLE FileHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + PLARGE_INTEGER AllocationSize, \ + ULONG FileAttributes, \ + ULONG ShareAccess, \ + ULONG CreateDisposition, \ + ULONG CreateOptions, \ + PVOID EaBuffer, \ + ULONG EaLength)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateKeyedEvent, \ + (PHANDLE KeyedEventHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + ULONG Flags)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtDeviceIoControlFile, \ + (HANDLE FileHandle, \ + HANDLE Event, \ + PIO_APC_ROUTINE ApcRoutine, \ + PVOID ApcContext, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + ULONG IoControlCode, \ + PVOID InputBuffer, \ + ULONG InputBufferLength, \ + PVOID OutputBuffer, \ + ULONG OutputBufferLength)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtReleaseKeyedEvent, \ + (HANDLE KeyedEventHandle, \ + PVOID KeyValue, \ + BOOLEAN Alertable, \ + PLARGE_INTEGER Timeout)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtWaitForKeyedEvent, \ + (HANDLE KeyedEventHandle, \ + PVOID KeyValue, \ + BOOLEAN Alertable, \ + PLARGE_INTEGER Timeout)) \ + \ + X(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL_EXTERN return_type(attributes* name) parameters; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +#define AFD_POLL_RECEIVE 0x0001 +#define AFD_POLL_RECEIVE_EXPEDITED 0x0002 +#define AFD_POLL_SEND 0x0004 +#define AFD_POLL_DISCONNECT 0x0008 +#define AFD_POLL_ABORT 0x0010 +#define AFD_POLL_LOCAL_CLOSE 0x0020 +#define AFD_POLL_ACCEPT 0x0080 +#define AFD_POLL_CONNECT_FAIL 0x0100 + +typedef struct _AFD_POLL_HANDLE_INFO +{ + HANDLE Handle; + ULONG Events; + NTSTATUS Status; +} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; + +typedef struct _AFD_POLL_INFO +{ + LARGE_INTEGER Timeout; + ULONG NumberOfHandles; + ULONG Exclusive; + AFD_POLL_HANDLE_INFO Handles[1]; +} AFD_POLL_INFO, *PAFD_POLL_INFO; + +WEPOLL_INTERNAL int afd_create_device_handle(HANDLE iocp_handle, + HANDLE* afd_device_handle_out); + +WEPOLL_INTERNAL int afd_poll(HANDLE afd_device_handle, + AFD_POLL_INFO* poll_info, + IO_STATUS_BLOCK* io_status_block); +WEPOLL_INTERNAL int afd_cancel_poll(HANDLE afd_device_handle, + IO_STATUS_BLOCK* io_status_block); + +#define return_map_error(value) \ + do \ + { \ + err_map_win_error(); \ + return (value); \ + } while (0) + +#define return_set_error(value, error) \ + do \ + { \ + err_set_win_error(error); \ + return (value); \ + } while (0) + +WEPOLL_INTERNAL void err_map_win_error(void); +WEPOLL_INTERNAL void err_set_win_error(DWORD error); +WEPOLL_INTERNAL int err_check_handle(HANDLE handle); + +#define IOCTL_AFD_POLL 0x00012024 + +static UNICODE_STRING afd__device_name = + RTL_CONSTANT_STRING(L"\\Device\\Afd\\Wepoll"); + +static OBJECT_ATTRIBUTES afd__device_attributes = + RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__device_name, 0); + +int afd_create_device_handle(HANDLE iocp_handle, HANDLE* afd_device_handle_out) +{ + HANDLE afd_device_handle; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + /* By opening \Device\Afd without specifying any extended attributes, we'll + * get a handle that lets us talk to the AFD driver, but that doesn't have + * an associated endpoint (so it's not a socket). */ + status = NtCreateFile(&afd_device_handle, + SYNCHRONIZE, + &afd__device_attributes, + &iosb, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + NULL, + 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + + if (CreateIoCompletionPort(afd_device_handle, iocp_handle, 0, 0) == NULL) + goto error; + + if (!SetFileCompletionNotificationModes(afd_device_handle, + FILE_SKIP_SET_EVENT_ON_HANDLE)) + goto error; + + *afd_device_handle_out = afd_device_handle; + return 0; + +error: + CloseHandle(afd_device_handle); + return_map_error(-1); +} + +int afd_poll(HANDLE afd_device_handle, + AFD_POLL_INFO* poll_info, + IO_STATUS_BLOCK* io_status_block) +{ + NTSTATUS status; + + /* Blocking operation is not supported. */ + assert(io_status_block != NULL); + + io_status_block->Status = STATUS_PENDING; + status = NtDeviceIoControlFile(afd_device_handle, + NULL, + NULL, + io_status_block, + io_status_block, + IOCTL_AFD_POLL, + poll_info, + sizeof *poll_info, + poll_info, + sizeof *poll_info); + + if (status == STATUS_SUCCESS) + return 0; + else if (status == STATUS_PENDING) + return_set_error(-1, ERROR_IO_PENDING); + else + return_set_error(-1, RtlNtStatusToDosError(status)); +} + +int afd_cancel_poll(HANDLE afd_device_handle, IO_STATUS_BLOCK* io_status_block) +{ + NTSTATUS cancel_status; + IO_STATUS_BLOCK cancel_iosb; + + /* If the poll operation has already completed or has been cancelled + * earlier, there's nothing left for us to do. */ + if (io_status_block->Status != STATUS_PENDING) + return 0; + + cancel_status = + NtCancelIoFileEx(afd_device_handle, io_status_block, &cancel_iosb); + + /* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed + * just before calling NtCancelIoFileEx(). This is not an error. */ + if (cancel_status == STATUS_SUCCESS || cancel_status == STATUS_NOT_FOUND) + return 0; + else + return_set_error(-1, RtlNtStatusToDosError(cancel_status)); +} + +WEPOLL_INTERNAL int epoll_global_init(void); + +WEPOLL_INTERNAL int init(void); + +typedef struct port_state port_state_t; +typedef struct queue queue_t; +typedef struct sock_state sock_state_t; +typedef struct ts_tree_node ts_tree_node_t; + +WEPOLL_INTERNAL port_state_t* port_new(HANDLE* iocp_handle_out); +WEPOLL_INTERNAL int port_close(port_state_t* port_state); +WEPOLL_INTERNAL int port_delete(port_state_t* port_state); + +WEPOLL_INTERNAL int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout); + +WEPOLL_INTERNAL int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev); + +WEPOLL_INTERNAL int port_register_socket(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket); +WEPOLL_INTERNAL void port_unregister_socket(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* port_find_socket(port_state_t* port_state, + SOCKET socket); + +WEPOLL_INTERNAL void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL void port_add_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL HANDLE port_get_iocp_handle(port_state_t* port_state); +WEPOLL_INTERNAL queue_t* port_get_poll_group_queue(port_state_t* port_state); + +WEPOLL_INTERNAL port_state_t* port_state_from_handle_tree_node( + ts_tree_node_t* tree_node); +WEPOLL_INTERNAL ts_tree_node_t* port_state_to_handle_tree_node( + port_state_t* port_state); + +/* The reflock is a special kind of lock that normally prevents a chunk of + * memory from being freed, but does allow the chunk of memory to eventually be + * released in a coordinated fashion. + * + * Under normal operation, threads increase and decrease the reference count, + * which are wait-free operations. + * + * Exactly once during the reflock's lifecycle, a thread holding a reference to + * the lock may "destroy" the lock; this operation blocks until all other + * threads holding a reference to the lock have dereferenced it. After + * "destroy" returns, the calling thread may assume that no other threads have + * a reference to the lock. + * + * Attemmpting to lock or destroy a lock after reflock_unref_and_destroy() has + * been called is invalid and results in undefined behavior. Therefore the user + * should use another lock to guarantee that this can't happen. + */ + +typedef struct reflock +{ + volatile long state; /* 32-bit Interlocked APIs operate on `long` values. */ +} reflock_t; + +WEPOLL_INTERNAL int reflock_global_init(void); + +WEPOLL_INTERNAL void reflock_init(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_ref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref_and_destroy(reflock_t* reflock); + +#include + +/* N.b.: the tree functions do not set errno or LastError when they fail. Each + * of the API functions has at most one failure mode. It is up to the caller to + * set an appropriate error code when necessary. */ + +typedef struct tree tree_t; +typedef struct tree_node tree_node_t; + +typedef struct tree +{ + tree_node_t* root; +} tree_t; + +typedef struct tree_node +{ + tree_node_t* left; + tree_node_t* right; + tree_node_t* parent; + uintptr_t key; + bool red; +} tree_node_t; + +WEPOLL_INTERNAL void tree_init(tree_t* tree); +WEPOLL_INTERNAL void tree_node_init(tree_node_t* node); + +WEPOLL_INTERNAL int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key); +WEPOLL_INTERNAL void tree_del(tree_t* tree, tree_node_t* node); + +WEPOLL_INTERNAL tree_node_t* tree_find(const tree_t* tree, uintptr_t key); +WEPOLL_INTERNAL tree_node_t* tree_root(const tree_t* tree); + +typedef struct ts_tree +{ + tree_t tree; + SRWLOCK lock; +} ts_tree_t; + +typedef struct ts_tree_node +{ + tree_node_t tree_node; + reflock_t reflock; +} ts_tree_node_t; + +WEPOLL_INTERNAL void ts_tree_init(ts_tree_t* rtl); +WEPOLL_INTERNAL void ts_tree_node_init(ts_tree_node_t* node); + +WEPOLL_INTERNAL int ts_tree_add(ts_tree_t* ts_tree, + ts_tree_node_t* node, + uintptr_t key); + +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, + uintptr_t key); +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, + uintptr_t key); + +WEPOLL_INTERNAL void ts_tree_node_unref(ts_tree_node_t* node); +WEPOLL_INTERNAL void ts_tree_node_unref_and_destroy(ts_tree_node_t* node); + +static ts_tree_t epoll__handle_tree; + +int epoll_global_init(void) +{ + ts_tree_init(&epoll__handle_tree); + return 0; +} + +static HANDLE epoll__create(void) +{ + port_state_t* port_state; + HANDLE ephnd; + ts_tree_node_t* tree_node; + + if (init() < 0) + return NULL; + + port_state = port_new(&ephnd); + if (port_state == NULL) + return NULL; + + tree_node = port_state_to_handle_tree_node(port_state); + if (ts_tree_add(&epoll__handle_tree, tree_node, (uintptr_t)ephnd) < 0) + { + /* This should never happen. */ + port_delete(port_state); + return_set_error(NULL, ERROR_ALREADY_EXISTS); + } + + return ephnd; +} + +HANDLE epoll_create(int size) +{ + if (size <= 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +HANDLE epoll_create1(int flags) +{ + if (flags != 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +int epoll_close(HANDLE ephnd) +{ + ts_tree_node_t* tree_node; + port_state_t* port_state; + + if (init() < 0) + return -1; + + tree_node = ts_tree_del_and_ref(&epoll__handle_tree, (uintptr_t)ephnd); + if (tree_node == NULL) + { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = port_state_from_handle_tree_node(tree_node); + port_close(port_state); + + ts_tree_node_unref_and_destroy(tree_node); + + return port_delete(port_state); + +err: + err_check_handle(ephnd); + return -1; +} + +int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, struct epoll_event* ev) +{ + ts_tree_node_t* tree_node; + port_state_t* port_state; + int r; + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t)ephnd); + if (tree_node == NULL) + { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = port_state_from_handle_tree_node(tree_node); + r = port_ctl(port_state, op, sock, ev); + + ts_tree_node_unref(tree_node); + + if (r < 0) + goto err; + + return 0; + +err: + /* On Linux, in the case of epoll_ctl(), EBADF takes priority over other + * errors. Wepoll mimics this behavior. */ + err_check_handle(ephnd); + err_check_handle((HANDLE)sock); + return -1; +} + +int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout) +{ + ts_tree_node_t* tree_node; + port_state_t* port_state; + int num_events; + + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t)ephnd); + if (tree_node == NULL) + { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = port_state_from_handle_tree_node(tree_node); + num_events = port_wait(port_state, events, maxevents, timeout); + + ts_tree_node_unref(tree_node); + + if (num_events < 0) + goto err; + + return num_events; + +err: + err_check_handle(ephnd); + return -1; +} + +#include + +#define ERR__ERRNO_MAPPINGS(X) \ + X(ERROR_ACCESS_DENIED, EACCES) \ + X(ERROR_ALREADY_EXISTS, EEXIST) \ + X(ERROR_BAD_COMMAND, EACCES) \ + X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ + X(ERROR_BAD_LENGTH, EACCES) \ + X(ERROR_BAD_NETPATH, ENOENT) \ + X(ERROR_BAD_NET_NAME, ENOENT) \ + X(ERROR_BAD_NET_RESP, ENETDOWN) \ + X(ERROR_BAD_PATHNAME, ENOENT) \ + X(ERROR_BROKEN_PIPE, EPIPE) \ + X(ERROR_CANNOT_MAKE, EACCES) \ + X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ + X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ + X(ERROR_CONNECTION_ACTIVE, EISCONN) \ + X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ + X(ERROR_CRC, EACCES) \ + X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ + X(ERROR_DISK_FULL, ENOSPC) \ + X(ERROR_DUP_NAME, EADDRINUSE) \ + X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ + X(ERROR_FILE_NOT_FOUND, ENOENT) \ + X(ERROR_GEN_FAILURE, EACCES) \ + X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ + X(ERROR_HOST_DOWN, EHOSTUNREACH) \ + X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ + X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ + X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ + X(ERROR_INVALID_FUNCTION, EINVAL) \ + X(ERROR_INVALID_HANDLE, EBADF) \ + X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ + X(ERROR_INVALID_PARAMETER, EINVAL) \ + X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ + X(ERROR_IO_PENDING, EINPROGRESS) \ + X(ERROR_LOCK_VIOLATION, EACCES) \ + X(ERROR_MORE_DATA, EMSGSIZE) \ + X(ERROR_NETNAME_DELETED, ECONNABORTED) \ + X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ + X(ERROR_NETWORK_BUSY, ENETDOWN) \ + X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ + X(ERROR_NOACCESS, EFAULT) \ + X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ + X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ + X(ERROR_NOT_FOUND, ENOENT) \ + X(ERROR_NOT_LOCKED, EACCES) \ + X(ERROR_NOT_READY, EACCES) \ + X(ERROR_NOT_SAME_DEVICE, EXDEV) \ + X(ERROR_NOT_SUPPORTED, ENOTSUP) \ + X(ERROR_NO_MORE_FILES, ENOENT) \ + X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_OPERATION_ABORTED, EINTR) \ + X(ERROR_OUT_OF_PAPER, EACCES) \ + X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ + X(ERROR_PATH_NOT_FOUND, ENOENT) \ + X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ + X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ + X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ + X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ + X(ERROR_REQUEST_ABORTED, EINTR) \ + X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ + X(ERROR_SECTOR_NOT_FOUND, EACCES) \ + X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ + X(ERROR_SHARING_VIOLATION, EACCES) \ + X(ERROR_TOO_MANY_NAMES, ENOMEM) \ + X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ + X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ + X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ + X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ + X(ERROR_WRITE_PROTECT, EACCES) \ + X(ERROR_WRONG_DISK, EACCES) \ + X(WSAEACCES, EACCES) \ + X(WSAEADDRINUSE, EADDRINUSE) \ + X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ + X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ + X(WSAECONNABORTED, ECONNABORTED) \ + X(WSAECONNREFUSED, ECONNREFUSED) \ + X(WSAECONNRESET, ECONNRESET) \ + X(WSAEDISCON, EPIPE) \ + X(WSAEFAULT, EFAULT) \ + X(WSAEHOSTDOWN, EHOSTUNREACH) \ + X(WSAEHOSTUNREACH, EHOSTUNREACH) \ + X(WSAEINPROGRESS, EBUSY) \ + X(WSAEINTR, EINTR) \ + X(WSAEINVAL, EINVAL) \ + X(WSAEISCONN, EISCONN) \ + X(WSAEMSGSIZE, EMSGSIZE) \ + X(WSAENETDOWN, ENETDOWN) \ + X(WSAENETRESET, EHOSTUNREACH) \ + X(WSAENETUNREACH, ENETUNREACH) \ + X(WSAENOBUFS, ENOMEM) \ + X(WSAENOTCONN, ENOTCONN) \ + X(WSAENOTSOCK, ENOTSOCK) \ + X(WSAEOPNOTSUPP, EOPNOTSUPP) \ + X(WSAEPROCLIM, ENOMEM) \ + X(WSAESHUTDOWN, EPIPE) \ + X(WSAETIMEDOUT, ETIMEDOUT) \ + X(WSAEWOULDBLOCK, EWOULDBLOCK) \ + X(WSANOTINITIALISED, ENETDOWN) \ + X(WSASYSNOTREADY, ENETDOWN) \ + X(WSAVERNOTSUPPORTED, ENOSYS) + +static errno_t err__map_win_error_to_errno(DWORD error) +{ + switch (error) + { +#define X(error_sym, errno_sym) \ + case error_sym: \ + return errno_sym; + ERR__ERRNO_MAPPINGS(X) +#undef X + } + return EINVAL; +} + +void err_map_win_error(void) +{ + errno = err__map_win_error_to_errno(GetLastError()); +} + +void err_set_win_error(DWORD error) +{ + SetLastError(error); + errno = err__map_win_error_to_errno(error); +} + +int err_check_handle(HANDLE handle) +{ + DWORD flags; + + /* GetHandleInformation() succeeds when passed INVALID_HANDLE_VALUE, so + * check for this condition explicitly. */ + if (handle == INVALID_HANDLE_VALUE) + return_set_error(-1, ERROR_INVALID_HANDLE); + + if (!GetHandleInformation(handle, &flags)) + return_map_error(-1); + + return 0; +} + +#include + +#define array_count(a) (sizeof(a) / (sizeof((a)[0]))) + +#define container_of(ptr, type, member) \ + ((type*)((uintptr_t)(ptr)-offsetof(type, member))) + +#define unused_var(v) ((void)(v)) + +/* Polyfill `inline` for older versions of msvc (up to Visual Studio 2013) */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define inline __inline +#endif + +WEPOLL_INTERNAL int ws_global_init(void); +WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); + +static bool init__done = false; +static INIT_ONCE init__once = INIT_ONCE_STATIC_INIT; + +static BOOL CALLBACK init__once_callback(INIT_ONCE* once, + void* parameter, + void** context) +{ + unused_var(once); + unused_var(parameter); + unused_var(context); + + /* N.b. that initialization order matters here. */ + if (ws_global_init() < 0 || nt_global_init() < 0 || + reflock_global_init() < 0 || epoll_global_init() < 0) + return FALSE; + + init__done = true; + return TRUE; +} + +int init(void) +{ + if (!init__done && + !InitOnceExecuteOnce(&init__once, init__once_callback, NULL, NULL)) + /* `InitOnceExecuteOnce()` itself is infallible, and it doesn't set any + * error code when the once-callback returns FALSE. We return -1 here to + * indicate that global initialization failed; the failing init function + * is resposible for setting `errno` and calling `SetLastError()`. */ + return -1; + + return 0; +} + +/* Set up a workaround for the following problem: + * FARPROC addr = GetProcAddress(...); + * MY_FUNC func = (MY_FUNC) addr; <-- GCC 8 warning/error. + * MY_FUNC func = (MY_FUNC) (void*) addr; <-- MSVC warning/error. + * To compile cleanly with either compiler, do casts with this "bridge" type: + * MY_FUNC func = (MY_FUNC) (nt__fn_ptr_cast_t) addr; */ +#ifdef __GNUC__ +typedef void* nt__fn_ptr_cast_t; +#else +typedef FARPROC nt__fn_ptr_cast_t; +#endif + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL return_type(attributes* name) parameters = NULL; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +int nt_global_init(void) +{ + HMODULE ntdll; + FARPROC fn_ptr; + + ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll == NULL) + return -1; + +#define X(return_type, attributes, name, parameters) \ + fn_ptr = GetProcAddress(ntdll, #name); \ + if (fn_ptr == NULL) \ + return -1; \ + name = (return_type(attributes*) parameters)(nt__fn_ptr_cast_t)fn_ptr; + NT_NTDLL_IMPORT_LIST(X) +#undef X + + return 0; +} + +#include + +typedef struct poll_group poll_group_t; + +typedef struct queue_node queue_node_t; + +WEPOLL_INTERNAL poll_group_t* poll_group_acquire(port_state_t* port); +WEPOLL_INTERNAL void poll_group_release(poll_group_t* poll_group); + +WEPOLL_INTERNAL void poll_group_delete(poll_group_t* poll_group); + +WEPOLL_INTERNAL poll_group_t* poll_group_from_queue_node( + queue_node_t* queue_node); +WEPOLL_INTERNAL HANDLE +poll_group_get_afd_device_handle(poll_group_t* poll_group); + +typedef struct queue_node +{ + queue_node_t* prev; + queue_node_t* next; +} queue_node_t; + +typedef struct queue +{ + queue_node_t head; +} queue_t; + +WEPOLL_INTERNAL void queue_init(queue_t* queue); +WEPOLL_INTERNAL void queue_node_init(queue_node_t* node); + +WEPOLL_INTERNAL queue_node_t* queue_first(const queue_t* queue); +WEPOLL_INTERNAL queue_node_t* queue_last(const queue_t* queue); + +WEPOLL_INTERNAL void queue_prepend(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_append(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_to_start(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_to_end(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_remove(queue_node_t* node); + +WEPOLL_INTERNAL bool queue_is_empty(const queue_t* queue); +WEPOLL_INTERNAL bool queue_is_enqueued(const queue_node_t* node); + +#define POLL_GROUP__MAX_GROUP_SIZE 32 + +typedef struct poll_group +{ + port_state_t* port_state; + queue_node_t queue_node; + HANDLE afd_device_handle; + size_t group_size; +} poll_group_t; + +static poll_group_t* poll_group__new(port_state_t* port_state) +{ + HANDLE iocp_handle = port_get_iocp_handle(port_state); + queue_t* poll_group_queue = port_get_poll_group_queue(port_state); + + poll_group_t* poll_group = malloc(sizeof *poll_group); + if (poll_group == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + memset(poll_group, 0, sizeof *poll_group); + + queue_node_init(&poll_group->queue_node); + poll_group->port_state = port_state; + + if (afd_create_device_handle(iocp_handle, &poll_group->afd_device_handle) < + 0) + { + free(poll_group); + return NULL; + } + + queue_append(poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_delete(poll_group_t* poll_group) +{ + assert(poll_group->group_size == 0); + CloseHandle(poll_group->afd_device_handle); + queue_remove(&poll_group->queue_node); + free(poll_group); +} + +poll_group_t* poll_group_from_queue_node(queue_node_t* queue_node) +{ + return container_of(queue_node, poll_group_t, queue_node); +} + +HANDLE poll_group_get_afd_device_handle(poll_group_t* poll_group) +{ + return poll_group->afd_device_handle; +} + +poll_group_t* poll_group_acquire(port_state_t* port_state) +{ + queue_t* poll_group_queue = port_get_poll_group_queue(port_state); + poll_group_t* poll_group = !queue_is_empty(poll_group_queue) + ? container_of(queue_last(poll_group_queue), + poll_group_t, + queue_node) + : NULL; + + if (poll_group == NULL || + poll_group->group_size >= POLL_GROUP__MAX_GROUP_SIZE) + poll_group = poll_group__new(port_state); + if (poll_group == NULL) + return NULL; + + if (++poll_group->group_size == POLL_GROUP__MAX_GROUP_SIZE) + queue_move_to_start(poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_release(poll_group_t* poll_group) +{ + port_state_t* port_state = poll_group->port_state; + queue_t* poll_group_queue = port_get_poll_group_queue(port_state); + + poll_group->group_size--; + assert(poll_group->group_size < POLL_GROUP__MAX_GROUP_SIZE); + + queue_move_to_end(poll_group_queue, &poll_group->queue_node); + + /* Poll groups are currently only freed when the epoll port is closed. */ +} + +WEPOLL_INTERNAL sock_state_t* sock_new(port_state_t* port_state, SOCKET socket); +WEPOLL_INTERNAL void sock_delete(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void sock_force_delete(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev); + +WEPOLL_INTERNAL int sock_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state, + IO_STATUS_BLOCK* io_status_block, + struct epoll_event* ev); + +WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node( + queue_node_t* queue_node); +WEPOLL_INTERNAL queue_node_t* sock_state_to_queue_node( + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* sock_state_from_tree_node(tree_node_t* tree_node); +WEPOLL_INTERNAL tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state); + +#define PORT__MAX_ON_STACK_COMPLETIONS 256 + +typedef struct port_state +{ + HANDLE iocp_handle; + tree_t sock_tree; + queue_t sock_update_queue; + queue_t sock_deleted_queue; + queue_t poll_group_queue; + ts_tree_node_t handle_tree_node; + CRITICAL_SECTION lock; + size_t active_poll_count; +} port_state_t; + +static inline port_state_t* port__alloc(void) +{ + port_state_t* port_state = malloc(sizeof *port_state); + if (port_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + return port_state; +} + +static inline void port__free(port_state_t* port) +{ + assert(port != NULL); + free(port); +} + +static inline HANDLE port__create_iocp(void) +{ + HANDLE iocp_handle = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (iocp_handle == NULL) + return_map_error(NULL); + + return iocp_handle; +} + +port_state_t* port_new(HANDLE* iocp_handle_out) +{ + port_state_t* port_state; + HANDLE iocp_handle; + + port_state = port__alloc(); + if (port_state == NULL) + goto err1; + + iocp_handle = port__create_iocp(); + if (iocp_handle == NULL) + goto err2; + + memset(port_state, 0, sizeof *port_state); + + port_state->iocp_handle = iocp_handle; + tree_init(&port_state->sock_tree); + queue_init(&port_state->sock_update_queue); + queue_init(&port_state->sock_deleted_queue); + queue_init(&port_state->poll_group_queue); + ts_tree_node_init(&port_state->handle_tree_node); + InitializeCriticalSection(&port_state->lock); + + *iocp_handle_out = iocp_handle; + return port_state; + +err2: + port__free(port_state); +err1: + return NULL; +} + +static inline int port__close_iocp(port_state_t* port_state) +{ + HANDLE iocp_handle = port_state->iocp_handle; + port_state->iocp_handle = NULL; + + if (!CloseHandle(iocp_handle)) + return_map_error(-1); + + return 0; +} + +int port_close(port_state_t* port_state) +{ + int result; + + EnterCriticalSection(&port_state->lock); + result = port__close_iocp(port_state); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_delete(port_state_t* port_state) +{ + tree_node_t* tree_node; + queue_node_t* queue_node; + + /* At this point the IOCP port should have been closed. */ + assert(port_state->iocp_handle == NULL); + + while ((tree_node = tree_root(&port_state->sock_tree)) != NULL) + { + sock_state_t* sock_state = sock_state_from_tree_node(tree_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->sock_deleted_queue)) != NULL) + { + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->poll_group_queue)) != NULL) + { + poll_group_t* poll_group = poll_group_from_queue_node(queue_node); + poll_group_delete(poll_group); + } + + assert(queue_is_empty(&port_state->sock_update_queue)); + + DeleteCriticalSection(&port_state->lock); + + port__free(port_state); + + return 0; +} + +static int port__update_events(port_state_t* port_state) +{ + queue_t* sock_update_queue = &port_state->sock_update_queue; + + /* Walk the queue, submitting new poll requests for every socket that needs + * it. */ + while (!queue_is_empty(sock_update_queue)) + { + queue_node_t* queue_node = queue_first(sock_update_queue); + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + + if (sock_update(port_state, sock_state) < 0) + return -1; + + /* sock_update() removes the socket from the update queue. */ + } + + return 0; +} + +static inline void port__update_events_if_polling(port_state_t* port_state) +{ + if (port_state->active_poll_count > 0) + port__update_events(port_state); +} + +static inline int port__feed_events(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD iocp_event_count) +{ + int epoll_event_count = 0; + DWORD i; + + for (i = 0; i < iocp_event_count; i++) + { + if (iocp_events[i].lpCompletionKey) + { + struct epoll_event* ev = &epoll_events[epoll_event_count++]; + ev->data.u64 = (uint64_t)iocp_events[i].lpCompletionKey; + ev->events = EPOLLEVENT; + continue; + } + IO_STATUS_BLOCK* io_status_block = + (IO_STATUS_BLOCK*)iocp_events[i].lpOverlapped; + struct epoll_event* ev = &epoll_events[epoll_event_count]; + + epoll_event_count += sock_feed_event(port_state, io_status_block, ev); + } + + return epoll_event_count; +} + +static inline int port__poll(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD maxevents, + DWORD timeout) +{ + DWORD completion_count; + + if (port__update_events(port_state) < 0) + return -1; + + port_state->active_poll_count++; + + LeaveCriticalSection(&port_state->lock); + + BOOL r = GetQueuedCompletionStatusEx(port_state->iocp_handle, + iocp_events, + maxevents, + &completion_count, + timeout, + FALSE); + + EnterCriticalSection(&port_state->lock); + + port_state->active_poll_count--; + + if (!r) + return_map_error(-1); + + return port__feed_events(port_state, + epoll_events, + iocp_events, + completion_count); +} + +int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout) +{ + OVERLAPPED_ENTRY stack_iocp_events[PORT__MAX_ON_STACK_COMPLETIONS]; + OVERLAPPED_ENTRY* iocp_events; + uint64_t due = 0; + DWORD gqcs_timeout; + int result; + + /* Check whether `maxevents` is in range. */ + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + /* Decide whether the IOCP completion list can live on the stack, or + * allocate memory for it on the heap. */ + if ((size_t)maxevents <= array_count(stack_iocp_events)) + { + iocp_events = stack_iocp_events; + } + else if ((iocp_events = malloc((size_t)maxevents * sizeof *iocp_events)) == + NULL) + { + iocp_events = stack_iocp_events; + maxevents = array_count(stack_iocp_events); + } + + /* Compute the timeout for GetQueuedCompletionStatus, and the wait end + * time, if the user specified a timeout other than zero or infinite. */ + if (timeout > 0) + { + due = GetTickCount64() + (uint64_t)timeout; + gqcs_timeout = (DWORD)timeout; + } + else if (timeout == 0) + { + gqcs_timeout = 0; + } + else + { + gqcs_timeout = INFINITE; + } + + EnterCriticalSection(&port_state->lock); + + /* Dequeue completion packets until either at least one interesting event + * has been discovered, or the timeout is reached. */ + for (;;) + { + uint64_t now; + + result = port__poll( + port_state, events, iocp_events, (DWORD)maxevents, gqcs_timeout); + if (result < 0 || result > 0) + break; /* Result, error, or time-out. */ + + if (timeout < 0) + continue; /* When timeout is negative, never time out. */ + + /* Update time. */ + now = GetTickCount64(); + + /* Do not allow the due time to be in the past. */ + if (now >= due) + { + SetLastError(WAIT_TIMEOUT); + break; + } + + /* Recompute time-out argument for GetQueuedCompletionStatus. */ + gqcs_timeout = (DWORD)(due - now); + } + + port__update_events_if_polling(port_state); + + LeaveCriticalSection(&port_state->lock); + + if (iocp_events != stack_iocp_events) + free(iocp_events); + + if (result >= 0) + return result; + else if (GetLastError() == WAIT_TIMEOUT) + return 0; + else + return -1; +} + +static inline int port__ctl_add(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) +{ + sock_state_t* sock_state = sock_new(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) + { + sock_delete(port_state, sock_state); + return -1; + } + + port__update_events_if_polling(port_state); + + return 0; +} + +static inline int port__ctl_mod(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) +{ + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) + return -1; + + port__update_events_if_polling(port_state); + + return 0; +} + +static inline int port__ctl_del(port_state_t* port_state, SOCKET sock) +{ + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + sock_delete(port_state, sock_state); + + return 0; +} + +static inline int port__ctl_op(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) +{ + switch (op) + { + case EPOLL_CTL_ADD: + return port__ctl_add(port_state, sock, ev); + case EPOLL_CTL_MOD: + return port__ctl_mod(port_state, sock, ev); + case EPOLL_CTL_DEL: + return port__ctl_del(port_state, sock); + default: + return_set_error(-1, ERROR_INVALID_PARAMETER); + } +} + +int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) +{ + int result; + + EnterCriticalSection(&port_state->lock); + result = port__ctl_op(port_state, op, sock, ev); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_register_socket(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket) +{ + if (tree_add(&port_state->sock_tree, + sock_state_to_tree_node(sock_state), + socket) < 0) + return_set_error(-1, ERROR_ALREADY_EXISTS); + return 0; +} + +void port_unregister_socket(port_state_t* port_state, sock_state_t* sock_state) +{ + tree_del(&port_state->sock_tree, sock_state_to_tree_node(sock_state)); +} + +sock_state_t* port_find_socket(port_state_t* port_state, SOCKET socket) +{ + tree_node_t* tree_node = tree_find(&port_state->sock_tree, socket); + if (tree_node == NULL) + return_set_error(NULL, ERROR_NOT_FOUND); + return sock_state_from_tree_node(tree_node); +} + +void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state) +{ + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_update_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state) +{ + unused_var(port_state); + if (!queue_is_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +void port_add_deleted_socket(port_state_t* port_state, sock_state_t* sock_state) +{ + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_deleted_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state) +{ + unused_var(port_state); + if (!queue_is_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +HANDLE port_get_iocp_handle(port_state_t* port_state) +{ + assert(port_state->iocp_handle != NULL); + return port_state->iocp_handle; +} + +queue_t* port_get_poll_group_queue(port_state_t* port_state) +{ + return &port_state->poll_group_queue; +} + +port_state_t* port_state_from_handle_tree_node(ts_tree_node_t* tree_node) +{ + return container_of(tree_node, port_state_t, handle_tree_node); +} + +ts_tree_node_t* port_state_to_handle_tree_node(port_state_t* port_state) +{ + return &port_state->handle_tree_node; +} + +void queue_init(queue_t* queue) +{ + queue_node_init(&queue->head); +} + +void queue_node_init(queue_node_t* node) +{ + node->prev = node; + node->next = node; +} + +static inline void queue__detach_node(queue_node_t* node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +queue_node_t* queue_first(const queue_t* queue) +{ + return !queue_is_empty(queue) ? queue->head.next : NULL; +} + +queue_node_t* queue_last(const queue_t* queue) +{ + return !queue_is_empty(queue) ? queue->head.prev : NULL; +} + +void queue_prepend(queue_t* queue, queue_node_t* node) +{ + node->next = queue->head.next; + node->prev = &queue->head; + node->next->prev = node; + queue->head.next = node; +} + +void queue_append(queue_t* queue, queue_node_t* node) +{ + node->next = &queue->head; + node->prev = queue->head.prev; + node->prev->next = node; + queue->head.prev = node; +} + +void queue_move_to_start(queue_t* queue, queue_node_t* node) +{ + queue__detach_node(node); + queue_prepend(queue, node); +} + +void queue_move_to_end(queue_t* queue, queue_node_t* node) +{ + queue__detach_node(node); + queue_append(queue, node); +} + +void queue_remove(queue_node_t* node) +{ + queue__detach_node(node); + queue_node_init(node); +} + +bool queue_is_empty(const queue_t* queue) +{ + return !queue_is_enqueued(&queue->head); +} + +bool queue_is_enqueued(const queue_node_t* node) +{ + return node->prev != node; +} + +#define REFLOCK__REF ((long)0x00000001UL) +#define REFLOCK__REF_MASK ((long)0x0fffffffUL) +#define REFLOCK__DESTROY ((long)0x10000000UL) +#define REFLOCK__DESTROY_MASK ((long)0xf0000000UL) +#define REFLOCK__POISON ((long)0x300dead0UL) + +static HANDLE reflock__keyed_event = NULL; + +int reflock_global_init(void) +{ + NTSTATUS status = NtCreateKeyedEvent(&reflock__keyed_event, + KEYEDEVENT_ALL_ACCESS, + NULL, + 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + return 0; +} + +void reflock_init(reflock_t* reflock) +{ + reflock->state = 0; +} + +static void reflock__signal_event(void* address) +{ + NTSTATUS status = + NtReleaseKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +static void reflock__await_event(void* address) +{ + NTSTATUS status = + NtWaitForKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +void reflock_ref(reflock_t* reflock) +{ + long state = InterlockedAdd(&reflock->state, REFLOCK__REF); + + /* Verify that the counter didn't overflow and the lock isn't destroyed. */ + assert((state & REFLOCK__DESTROY_MASK) == 0); + unused_var(state); +} + +void reflock_unref(reflock_t* reflock) +{ + long state = InterlockedAdd(&reflock->state, -REFLOCK__REF); + + /* Verify that the lock was referenced and not already destroyed. */ + assert((state & REFLOCK__DESTROY_MASK & ~REFLOCK__DESTROY) == 0); + + if (state == REFLOCK__DESTROY) + reflock__signal_event(reflock); +} + +void reflock_unref_and_destroy(reflock_t* reflock) +{ + long state = + InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); + long ref_count = state & REFLOCK__REF_MASK; + + /* Verify that the lock was referenced and not already destroyed. */ + assert((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY); + + if (ref_count != 0) + reflock__await_event(reflock); + + state = InterlockedExchange(&reflock->state, REFLOCK__POISON); + assert(state == REFLOCK__DESTROY); +} + +#define SOCK__KNOWN_EPOLL_EVENTS \ + (EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \ + EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP) + +typedef enum sock__poll_status +{ + SOCK__POLL_IDLE = 0, + SOCK__POLL_PENDING, + SOCK__POLL_CANCELLED +} sock__poll_status_t; + +typedef struct sock_state +{ + IO_STATUS_BLOCK io_status_block; + AFD_POLL_INFO poll_info; + queue_node_t queue_node; + tree_node_t tree_node; + poll_group_t* poll_group; + SOCKET base_socket; + epoll_data_t user_data; + uint32_t user_events; + uint32_t pending_events; + sock__poll_status_t poll_status; + bool delete_pending; +} sock_state_t; + +static inline sock_state_t* sock__alloc(void) +{ + sock_state_t* sock_state = malloc(sizeof *sock_state); + if (sock_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + return sock_state; +} + +static inline void sock__free(sock_state_t* sock_state) +{ + assert(sock_state != NULL); + free(sock_state); +} + +static inline int sock__cancel_poll(sock_state_t* sock_state) +{ + assert(sock_state->poll_status == SOCK__POLL_PENDING); + + if (afd_cancel_poll(poll_group_get_afd_device_handle( + sock_state->poll_group), + &sock_state->io_status_block) < 0) + return -1; + + sock_state->poll_status = SOCK__POLL_CANCELLED; + sock_state->pending_events = 0; + return 0; +} + +sock_state_t* sock_new(port_state_t* port_state, SOCKET socket) +{ + SOCKET base_socket; + poll_group_t* poll_group; + sock_state_t* sock_state; + + if (socket == 0 || socket == INVALID_SOCKET) + return_set_error(NULL, ERROR_INVALID_HANDLE); + + base_socket = ws_get_base_socket(socket); + if (base_socket == INVALID_SOCKET) + return NULL; + + poll_group = poll_group_acquire(port_state); + if (poll_group == NULL) + return NULL; + + sock_state = sock__alloc(); + if (sock_state == NULL) + goto err1; + + memset(sock_state, 0, sizeof *sock_state); + + sock_state->base_socket = base_socket; + sock_state->poll_group = poll_group; + + tree_node_init(&sock_state->tree_node); + queue_node_init(&sock_state->queue_node); + + if (port_register_socket(port_state, sock_state, socket) < 0) + goto err2; + + return sock_state; + +err2: + sock__free(sock_state); +err1: + poll_group_release(poll_group); + + return NULL; +} + +static int sock__delete(port_state_t* port_state, + sock_state_t* sock_state, + bool force) +{ + if (!sock_state->delete_pending) + { + if (sock_state->poll_status == SOCK__POLL_PENDING) + sock__cancel_poll(sock_state); + + port_cancel_socket_update(port_state, sock_state); + port_unregister_socket(port_state, sock_state); + + sock_state->delete_pending = true; + } + + /* If the poll request still needs to complete, the sock_state object can't + * be free()d yet. `sock_feed_event()` or `port_close()` will take care + * of this later. */ + if (force || sock_state->poll_status == SOCK__POLL_IDLE) + { + /* Free the sock_state now. */ + port_remove_deleted_socket(port_state, sock_state); + poll_group_release(sock_state->poll_group); + sock__free(sock_state); + } + else + { + /* Free the socket later. */ + port_add_deleted_socket(port_state, sock_state); + } + + return 0; +} + +void sock_delete(port_state_t* port_state, sock_state_t* sock_state) +{ + sock__delete(port_state, sock_state, false); +} + +void sock_force_delete(port_state_t* port_state, sock_state_t* sock_state) +{ + sock__delete(port_state, sock_state, true); +} + +int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev) +{ + /* EPOLLERR and EPOLLHUP are always reported, even when not requested by the + * caller. However they are disabled after a event has been reported for a + * socket for which the EPOLLONESHOT flag was set. */ + uint32_t events = ev->events | EPOLLERR | EPOLLHUP; + + sock_state->user_events = events; + sock_state->user_data = ev->data; + + if ((events & SOCK__KNOWN_EPOLL_EVENTS & ~sock_state->pending_events) != 0) + port_request_socket_update(port_state, sock_state); + + return 0; +} + +static inline DWORD sock__epoll_events_to_afd_events(uint32_t epoll_events) +{ + /* Always monitor for AFD_POLL_LOCAL_CLOSE, which is triggered when the + * socket is closed with closesocket() or CloseHandle(). */ + DWORD afd_events = AFD_POLL_LOCAL_CLOSE; + + if (epoll_events & (EPOLLIN | EPOLLRDNORM)) + afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT; + if (epoll_events & (EPOLLPRI | EPOLLRDBAND)) + afd_events |= AFD_POLL_RECEIVE_EXPEDITED; + if (epoll_events & (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND)) + afd_events |= AFD_POLL_SEND; + if (epoll_events & (EPOLLIN | EPOLLRDNORM | EPOLLRDHUP)) + afd_events |= AFD_POLL_DISCONNECT; + if (epoll_events & EPOLLHUP) + afd_events |= AFD_POLL_ABORT; + if (epoll_events & EPOLLERR) + afd_events |= AFD_POLL_CONNECT_FAIL; + + return afd_events; +} + +static inline uint32_t sock__afd_events_to_epoll_events(DWORD afd_events) +{ + uint32_t epoll_events = 0; + + if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT)) + epoll_events |= EPOLLIN | EPOLLRDNORM; + if (afd_events & AFD_POLL_RECEIVE_EXPEDITED) + epoll_events |= EPOLLPRI | EPOLLRDBAND; + if (afd_events & AFD_POLL_SEND) + epoll_events |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + if (afd_events & AFD_POLL_DISCONNECT) + epoll_events |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + if (afd_events & AFD_POLL_ABORT) + epoll_events |= EPOLLHUP; + if (afd_events & AFD_POLL_CONNECT_FAIL) + /* Linux reports all these events after connect() has failed. */ + epoll_events |= EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLRDNORM | + EPOLLWRNORM | EPOLLRDHUP; + + return epoll_events; +} + +int sock_update(port_state_t* port_state, sock_state_t* sock_state) +{ + assert(!sock_state->delete_pending); + + if ((sock_state->poll_status == SOCK__POLL_PENDING) && + (sock_state->user_events & SOCK__KNOWN_EPOLL_EVENTS & + ~sock_state->pending_events) == 0) + { + /* All the events the user is interested in are already being monitored + * by the pending poll operation. It might spuriously complete because + * of an event that we're no longer interested in; when that happens + * we'll submit a new poll operation with the updated event mask. */ + } + else if (sock_state->poll_status == SOCK__POLL_PENDING) + { + /* A poll operation is already pending, but it's not monitoring for all + * the events that the user is interested in. Therefore, cancel the + * pending poll operation; when we receive it's completion package, a + * new poll operation will be submitted with the correct event mask. */ + if (sock__cancel_poll(sock_state) < 0) + return -1; + } + else if (sock_state->poll_status == SOCK__POLL_CANCELLED) + { + /* The poll operation has already been cancelled, we're still waiting + * for it to return. For now, there's nothing that needs to be done. */ + } + else if (sock_state->poll_status == SOCK__POLL_IDLE) + { + /* No poll operation is pending; start one. */ + sock_state->poll_info.Exclusive = FALSE; + sock_state->poll_info.NumberOfHandles = 1; + sock_state->poll_info.Timeout.QuadPart = INT64_MAX; + sock_state->poll_info.Handles[0].Handle = + (HANDLE)sock_state->base_socket; + sock_state->poll_info.Handles[0].Status = 0; + sock_state->poll_info.Handles[0].Events = + sock__epoll_events_to_afd_events(sock_state->user_events); + + if (afd_poll(poll_group_get_afd_device_handle(sock_state->poll_group), + &sock_state->poll_info, + &sock_state->io_status_block) < 0) + { + switch (GetLastError()) + { + case ERROR_IO_PENDING: + /* Overlapped poll operation in progress; this is expected. + */ + break; + case ERROR_INVALID_HANDLE: + /* Socket closed; it'll be dropped from the epoll set. */ + return sock__delete(port_state, sock_state, false); + default: + /* Other errors are propagated to the caller. */ + return_map_error(-1); + } + } + + /* The poll request was successfully submitted. */ + sock_state->poll_status = SOCK__POLL_PENDING; + sock_state->pending_events = sock_state->user_events; + } + else + { + /* Unreachable. */ + assert(false); + } + + port_cancel_socket_update(port_state, sock_state); + return 0; +} + +int sock_feed_event(port_state_t* port_state, + IO_STATUS_BLOCK* io_status_block, + struct epoll_event* ev) +{ + sock_state_t* sock_state = + container_of(io_status_block, sock_state_t, io_status_block); + AFD_POLL_INFO* poll_info = &sock_state->poll_info; + uint32_t epoll_events = 0; + + sock_state->poll_status = SOCK__POLL_IDLE; + sock_state->pending_events = 0; + + if (sock_state->delete_pending) + { + /* Socket has been deleted earlier and can now be freed. */ + return sock__delete(port_state, sock_state, false); + } + else if (io_status_block->Status == STATUS_CANCELLED) + { + /* The poll request was cancelled by CancelIoEx. */ + } + else if (!NT_SUCCESS(io_status_block->Status)) + { + /* The overlapped request itself failed in an unexpected way. */ + epoll_events = EPOLLERR; + } + else if (poll_info->NumberOfHandles < 1) + { + /* This poll operation succeeded but didn't report any socket events. */ + } + else if (poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) + { + /* The poll operation reported that the socket was closed. */ + return sock__delete(port_state, sock_state, false); + } + else + { + /* Events related to our socket were reported. */ + epoll_events = + sock__afd_events_to_epoll_events(poll_info->Handles[0].Events); + } + + /* Requeue the socket so a new poll request will be submitted. */ + port_request_socket_update(port_state, sock_state); + + /* Filter out events that the user didn't ask for. */ + epoll_events &= sock_state->user_events; + + /* Return if there are no epoll events to report. */ + if (epoll_events == 0) + return 0; + + /* If the the socket has the EPOLLONESHOT flag set, unmonitor all events, + * even EPOLLERR and EPOLLHUP. But always keep looking for closed sockets. + */ + if (sock_state->user_events & EPOLLONESHOT) + sock_state->user_events = 0; + + ev->data = sock_state->user_data; + ev->events = epoll_events; + return 1; +} + +sock_state_t* sock_state_from_queue_node(queue_node_t* queue_node) +{ + return container_of(queue_node, sock_state_t, queue_node); +} + +queue_node_t* sock_state_to_queue_node(sock_state_t* sock_state) +{ + return &sock_state->queue_node; +} + +sock_state_t* sock_state_from_tree_node(tree_node_t* tree_node) +{ + return container_of(tree_node, sock_state_t, tree_node); +} + +tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state) +{ + return &sock_state->tree_node; +} + +void ts_tree_init(ts_tree_t* ts_tree) +{ + tree_init(&ts_tree->tree); + InitializeSRWLock(&ts_tree->lock); +} + +void ts_tree_node_init(ts_tree_node_t* node) +{ + tree_node_init(&node->tree_node); + reflock_init(&node->reflock); +} + +int ts_tree_add(ts_tree_t* ts_tree, ts_tree_node_t* node, uintptr_t key) +{ + int r; + + AcquireSRWLockExclusive(&ts_tree->lock); + r = tree_add(&ts_tree->tree, &node->tree_node, key); + ReleaseSRWLockExclusive(&ts_tree->lock); + + return r; +} + +static inline ts_tree_node_t* ts_tree__find_node(ts_tree_t* ts_tree, + uintptr_t key) +{ + tree_node_t* tree_node = tree_find(&ts_tree->tree, key); + if (tree_node == NULL) + return NULL; + + return container_of(tree_node, ts_tree_node_t, tree_node); +} + +ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, uintptr_t key) +{ + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockExclusive(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) + { + tree_del(&ts_tree->tree, &ts_tree_node->tree_node); + reflock_ref(&ts_tree_node->reflock); + } + + ReleaseSRWLockExclusive(&ts_tree->lock); + + return ts_tree_node; +} + +ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, uintptr_t key) +{ + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockShared(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) + reflock_ref(&ts_tree_node->reflock); + + ReleaseSRWLockShared(&ts_tree->lock); + + return ts_tree_node; +} + +void ts_tree_node_unref(ts_tree_node_t* node) +{ + reflock_unref(&node->reflock); +} + +void ts_tree_node_unref_and_destroy(ts_tree_node_t* node) +{ + reflock_unref_and_destroy(&node->reflock); +} + +void tree_init(tree_t* tree) +{ + memset(tree, 0, sizeof *tree); +} + +void tree_node_init(tree_node_t* node) +{ + memset(node, 0, sizeof *node); +} + +#define TREE__ROTATE(cis, trans) \ + tree_node_t* p = node; \ + tree_node_t* q = node->trans; \ + tree_node_t* parent = p->parent; \ + \ + if (parent) \ + { \ + if (parent->left == p) \ + parent->left = q; \ + else \ + parent->right = q; \ + } \ + else \ + { \ + tree->root = q; \ + } \ + \ + q->parent = parent; \ + p->parent = q; \ + p->trans = q->cis; \ + if (p->trans) \ + p->trans->parent = p; \ + q->cis = p; + +static inline void tree__rotate_left(tree_t* tree, tree_node_t* node) +{ + TREE__ROTATE(left, right) +} + +static inline void tree__rotate_right(tree_t* tree, tree_node_t* node) +{ + TREE__ROTATE(right, left) +} + +#define TREE__INSERT_OR_DESCEND(side) \ + if (parent->side) \ + { \ + parent = parent->side; \ + } \ + else \ + { \ + parent->side = node; \ + break; \ + } + +#define TREE__REBALANCE_AFTER_INSERT(cis, trans) \ + tree_node_t* grandparent = parent->parent; \ + tree_node_t* uncle = grandparent->trans; \ + \ + if (uncle && uncle->red) \ + { \ + parent->red = uncle->red = false; \ + grandparent->red = true; \ + node = grandparent; \ + } \ + else \ + { \ + if (node == parent->trans) \ + { \ + tree__rotate_##cis(tree, parent); \ + node = parent; \ + parent = node->parent; \ + } \ + parent->red = false; \ + grandparent->red = true; \ + tree__rotate_##trans(tree, grandparent); \ + } + +int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) +{ + tree_node_t* parent; + + parent = tree->root; + if (parent) + { + for (;;) + { + if (key < parent->key) + { + TREE__INSERT_OR_DESCEND(left) + } + else if (key > parent->key) + { + TREE__INSERT_OR_DESCEND(right) + } + else + { + return -1; + } + } + } + else + { + tree->root = node; + } + + node->key = key; + node->left = node->right = NULL; + node->parent = parent; + node->red = true; + + for (; parent && parent->red; parent = node->parent) + { + if (parent == parent->parent->left) + { + TREE__REBALANCE_AFTER_INSERT(left, right) + } + else + { + TREE__REBALANCE_AFTER_INSERT(right, left) + } + } + tree->root->red = false; + + return 0; +} + +#define TREE__REBALANCE_AFTER_REMOVE(cis, trans) \ + tree_node_t* sibling = parent->trans; \ + \ + if (sibling->red) \ + { \ + sibling->red = false; \ + parent->red = true; \ + tree__rotate_##cis(tree, parent); \ + sibling = parent->trans; \ + } \ + if ((sibling->left && sibling->left->red) || \ + (sibling->right && sibling->right->red)) \ + { \ + if (!sibling->trans || !sibling->trans->red) \ + { \ + sibling->cis->red = false; \ + sibling->red = true; \ + tree__rotate_##trans(tree, sibling); \ + sibling = parent->trans; \ + } \ + sibling->red = parent->red; \ + parent->red = sibling->trans->red = false; \ + tree__rotate_##cis(tree, parent); \ + node = tree->root; \ + break; \ + } \ + sibling->red = true; + +void tree_del(tree_t* tree, tree_node_t* node) +{ + tree_node_t* parent = node->parent; + tree_node_t* left = node->left; + tree_node_t* right = node->right; + tree_node_t* next; + bool red; + + if (!left) + { + next = right; + } + else if (!right) + { + next = left; + } + else + { + next = right; + while (next->left) + next = next->left; + } + + if (parent) + { + if (parent->left == node) + parent->left = next; + else + parent->right = next; + } + else + { + tree->root = next; + } + + if (left && right) + { + red = next->red; + next->red = node->red; + next->left = left; + left->parent = next; + if (next != right) + { + parent = next->parent; + next->parent = node->parent; + node = next->right; + parent->left = node; + next->right = right; + right->parent = next; + } + else + { + next->parent = parent; + parent = next; + node = next->right; + } + } + else + { + red = node->red; + node = next; + } + + if (node) + node->parent = parent; + if (red) + return; + if (node && node->red) + { + node->red = false; + return; + } + + do + { + if (node == tree->root) + break; + if (node == parent->left) + { + TREE__REBALANCE_AFTER_REMOVE(left, right) + } + else + { + TREE__REBALANCE_AFTER_REMOVE(right, left) + } + node = parent; + parent = parent->parent; + } while (!node->red); + + if (node) + node->red = false; +} + +tree_node_t* tree_find(const tree_t* tree, uintptr_t key) +{ + tree_node_t* node = tree->root; + while (node) + { + if (key < node->key) + node = node->left; + else if (key > node->key) + node = node->right; + else + return node; + } + return NULL; +} + +tree_node_t* tree_root(const tree_t* tree) +{ + return tree->root; +} + +#ifndef SIO_BSP_HANDLE_POLL +#define SIO_BSP_HANDLE_POLL 0x4800001D +#endif + +#ifndef SIO_BASE_HANDLE +#define SIO_BASE_HANDLE 0x48000022 +#endif + +int ws_global_init(void) +{ + int r; + WSADATA wsa_data; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (r != 0) + return_set_error(-1, (DWORD)r); + + return 0; +} + +static inline SOCKET ws__ioctl_get_bsp_socket(SOCKET socket, DWORD ioctl) +{ + SOCKET bsp_socket; + DWORD bytes; + + if (WSAIoctl(socket, + ioctl, + NULL, + 0, + &bsp_socket, + sizeof bsp_socket, + &bytes, + NULL, + NULL) != SOCKET_ERROR) + return bsp_socket; + else + return INVALID_SOCKET; +} + +void epoll_post_signal(HANDLE port_handle, uint64_t event) +{ + ULONG_PTR ev; + ev = (ULONG_PTR)event; + PostQueuedCompletionStatus(port_handle, 1, ev, NULL); +} + +SOCKET ws_get_base_socket(SOCKET socket) +{ + SOCKET base_socket; + DWORD error; + + for (;;) + { + base_socket = ws__ioctl_get_bsp_socket(socket, SIO_BASE_HANDLE); + if (base_socket != INVALID_SOCKET) + return base_socket; + + error = GetLastError(); + if (error == WSAENOTSOCK) + return_set_error(INVALID_SOCKET, error); + + /* Even though Microsoft documentation clearly states that LSPs should + * never intercept the `SIO_BASE_HANDLE` ioctl [1], Komodia based LSPs + * do so anyway, breaking it, with the apparent intention of preventing + * LSP bypass [2]. Fortunately they don't handle `SIO_BSP_HANDLE_POLL`, + * which will at least let us obtain the socket associated with the next + * winsock protocol chain entry. If this succeeds, loop around and call + * `SIO_BASE_HANDLE` again with the returned BSP socket, to make sure + * that we unwrap all layers and retrieve the actual base socket. [1] + * https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls + * [2] + * https://www.komodia.com/newwiki/index.php?title=Komodia%27s_Redirector_bug_fixes#Version_2.2.2.6 + */ + base_socket = ws__ioctl_get_bsp_socket(socket, SIO_BSP_HANDLE_POLL); + if (base_socket != INVALID_SOCKET && base_socket != socket) + socket = base_socket; + else + return_set_error(INVALID_SOCKET, error); + } +} diff --git a/modules/http_trantor/trantor/third_party/wepoll/Wepoll.h b/modules/http_trantor/trantor/third_party/wepoll/Wepoll.h new file mode 100644 index 0000000..8ff6de3 --- /dev/null +++ b/modules/http_trantor/trantor/third_party/wepoll/Wepoll.h @@ -0,0 +1,120 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2020, Bert Belder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WEPOLL_H_ +#define WEPOLL_H_ + +#ifndef WEPOLL_EXPORT +#define WEPOLL_EXPORT +#endif + +#include + +enum EPOLL_EVENTS +{ + EPOLLIN = (int)(1U << 0), + EPOLLPRI = (int)(1U << 1), + EPOLLOUT = (int)(1U << 2), + EPOLLERR = (int)(1U << 3), + EPOLLHUP = (int)(1U << 4), + EPOLLRDNORM = (int)(1U << 6), + EPOLLRDBAND = (int)(1U << 7), + EPOLLWRNORM = (int)(1U << 8), + EPOLLWRBAND = (int)(1U << 9), + EPOLLMSG = (int)(1U << 10), /* Never reported. */ + EPOLLRDHUP = (int)(1U << 13), + EPOLLEVENT = (int)(1U << 14), + EPOLLONESHOT = (int)(1U << 31) +}; + +#define EPOLLIN (1U << 0) +#define EPOLLPRI (1U << 1) +#define EPOLLOUT (1U << 2) +#define EPOLLERR (1U << 3) +#define EPOLLHUP (1U << 4) +#define EPOLLRDNORM (1U << 6) +#define EPOLLRDBAND (1U << 7) +#define EPOLLWRNORM (1U << 8) +#define EPOLLWRBAND (1U << 9) +#define EPOLLMSG (1U << 10) +#define EPOLLRDHUP (1U << 13) +#define EPOLLEVENT (1U << 14) +#define EPOLLONESHOT (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_MOD 2 +#define EPOLL_CTL_DEL 3 + +typedef void* HANDLE; +typedef uintptr_t SOCKET; + +typedef union epoll_data +{ + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; + +struct epoll_event +{ + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WEPOLL_EXPORT HANDLE epoll_create(int size); + WEPOLL_EXPORT HANDLE epoll_create1(int flags); + + WEPOLL_EXPORT int epoll_close(HANDLE ephnd); + + WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); + + WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); + WEPOLL_EXPORT void epoll_post_signal(HANDLE ephnd, uint64_t event); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* WEPOLL_H_ */ diff --git a/modules/http_trantor/trantor/unittests/CMakeLists.txt b/modules/http_trantor/trantor/unittests/CMakeLists.txt new file mode 100644 index 0000000..4c7f492 --- /dev/null +++ b/modules/http_trantor/trantor/unittests/CMakeLists.txt @@ -0,0 +1,19 @@ +find_package(GTest REQUIRED) +add_executable(msgbuffer_unittest MsgBufferUnittest.cc) +add_executable(inetaddress_unittest InetAddressUnittest.cc) +add_executable(date_unittest DateUnittest.cc) +add_executable(split_string_unittest splitStringUnittest.cc) +set(UNITTEST_TARGETS + msgbuffer_unittest + inetaddress_unittest + date_unittest + split_string_unittest) +set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD 14) +set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_STANDARD_REQUIRED ON) +set_property(TARGET ${UNITTEST_TARGETS} PROPERTY CXX_EXTENSIONS OFF) + +include(GoogleTest) +foreach(T ${UNITTEST_TARGETS}) + target_link_libraries(${T} PRIVATE trantor GTest::GTest) + gtest_discover_tests(${T}) +endforeach() diff --git a/modules/http_trantor/trantor/unittests/DateUnittest.cc b/modules/http_trantor/trantor/unittests/DateUnittest.cc new file mode 100644 index 0000000..255ce28 --- /dev/null +++ b/modules/http_trantor/trantor/unittests/DateUnittest.cc @@ -0,0 +1,43 @@ +#include +#include +#include +#include +using namespace trantor; +TEST(Date, constructorTest) +{ + EXPECT_STREQ("1985-01-01 00:00:00", + trantor::Date(1985, 1, 1) + .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S") + .c_str()); + EXPECT_STREQ("2004-02-29 00:00:00.000000", + trantor::Date(2004, 2, 29) + .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .c_str()); + EXPECT_STRNE("2001-02-29 00:00:00.000000", + trantor::Date(2001, 2, 29) + .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .c_str()); + EXPECT_STREQ("2018-01-01 00:00:00.000000", + trantor::Date(2018, 1, 1, 12, 12, 12, 2321) + .roundDay() + .toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true) + .c_str()); +} +TEST(Date, DatabaseStringTest) +{ + auto now = trantor::Date::now(); + EXPECT_EQ(now, trantor::Date::fromDbStringLocal(now.toDbStringLocal())); + std::string dbString = "2018-01-01 00:00:00.123"; + auto dbDate = trantor::Date::fromDbStringLocal(dbString); + auto ms = (dbDate.microSecondsSinceEpoch() % 1000000) / 1000; + EXPECT_EQ(ms, 123); + dbString = "2018-01-01 00:00:00"; + dbDate = trantor::Date::fromDbStringLocal(dbString); + ms = (dbDate.microSecondsSinceEpoch() % 1000000) / 1000; + EXPECT_EQ(ms, 0); +} +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/unittests/InetAddressUnittest.cc b/modules/http_trantor/trantor/unittests/InetAddressUnittest.cc new file mode 100644 index 0000000..487ee37 --- /dev/null +++ b/modules/http_trantor/trantor/unittests/InetAddressUnittest.cc @@ -0,0 +1,22 @@ +#include +#include +#include +#include +using namespace trantor; +TEST(InetAddress, innerIpTest) +{ + EXPECT_EQ(true, InetAddress("192.168.0.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("192.168.12.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("10.168.0.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("10.0.0.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("172.31.10.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("127.0.0.1", 0).isIntranetIp()); + EXPECT_EQ(true, InetAddress("example.com", 0).isUnspecified()); + EXPECT_EQ(false, InetAddress("127.0.0.2", 0).isUnspecified()); + EXPECT_EQ(false, InetAddress("0.0.0.0", 0).isUnspecified()); +} +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/unittests/MsgBufferUnittest.cc b/modules/http_trantor/trantor/unittests/MsgBufferUnittest.cc new file mode 100644 index 0000000..9addd2c --- /dev/null +++ b/modules/http_trantor/trantor/unittests/MsgBufferUnittest.cc @@ -0,0 +1,78 @@ +#include +#include +#include +#include +using namespace trantor; +TEST(MsgBufferTest, readableTest) +{ + MsgBuffer buffer; + + EXPECT_EQ(0, buffer.readableBytes()); + buffer.append(std::string(128, 'a')); + EXPECT_EQ(128, buffer.readableBytes()); + buffer.retrieve(100); + EXPECT_EQ(28, buffer.readableBytes()); + EXPECT_EQ('a', buffer.peekInt8()); + buffer.retrieveAll(); + EXPECT_EQ(0, buffer.readableBytes()); +} +TEST(MsgBufferTest, writableTest) +{ + MsgBuffer buffer(100); + + EXPECT_EQ(100, buffer.writableBytes()); + buffer.append("abcde"); + EXPECT_EQ(95, buffer.writableBytes()); + buffer.append(std::string(100, 'x')); + EXPECT_EQ(111, buffer.writableBytes()); + buffer.retrieve(100); + EXPECT_EQ(111, buffer.writableBytes()); + buffer.append(std::string(112, 'c')); + EXPECT_EQ(99, buffer.writableBytes()); + buffer.retrieveAll(); + EXPECT_EQ(216, buffer.writableBytes()); +} + +TEST(MsgBufferTest, addInFrontTest) +{ + MsgBuffer buffer(100); + + EXPECT_EQ(100, buffer.writableBytes()); + buffer.addInFrontInt8('a'); + EXPECT_EQ(100, buffer.writableBytes()); + buffer.addInFrontInt64(123); + EXPECT_EQ(92, buffer.writableBytes()); + buffer.addInFrontInt64(100); + EXPECT_EQ(84, buffer.writableBytes()); + buffer.addInFrontInt8(1); + EXPECT_EQ(84, buffer.writableBytes()); +} + +TEST(MsgBuffer, MoveContrustor) +{ + MsgBuffer buf1(100); + const char *bufptr1 = buf1.peek(); + MsgBuffer buffnew1 = std::move(buf1); + EXPECT_EQ(bufptr1, buffnew1.peek()); + + MsgBuffer buf2(100); + const char *bufptr2 = buf2.peek(); + MsgBuffer buffnew2(std::move(buf2)); + EXPECT_EQ(bufptr2, buffnew2.peek()); +} + +TEST(Msgbuffer, MoveAssignmentOperator) +{ + MsgBuffer buf(100); + const char *bufptr = buf.peek(); + size_t writable = buf.writableBytes(); + MsgBuffer buffnew(1000); + buffnew = std::move(buf); + EXPECT_EQ(bufptr, buffnew.peek()); + EXPECT_EQ(writable, buffnew.writableBytes()); +} +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/unittests/splitStringUnittest.cc b/modules/http_trantor/trantor/unittests/splitStringUnittest.cc new file mode 100644 index 0000000..efa64af --- /dev/null +++ b/modules/http_trantor/trantor/unittests/splitStringUnittest.cc @@ -0,0 +1,159 @@ +#include +#include +#include +#include +using namespace trantor; +TEST(splitString, ACCEPT_EMPTY_STRING1) +{ + std::string originString = "1,2,3"; + auto out = splitString(originString, ",", true); + EXPECT_EQ(out.size(), 3); +} +TEST(splitString, ACCEPT_EMPTY_STRING2) +{ + std::string originString = ",1,2,3"; + auto out = splitString(originString, ",", true); + EXPECT_EQ(out.size(), 4); + EXPECT_TRUE(out[0].empty()); +} +TEST(splitString, ACCEPT_EMPTY_STRING3) +{ + std::string originString = ",1,2,3,"; + auto out = splitString(originString, ",", true); + EXPECT_EQ(out.size(), 5); + EXPECT_TRUE(out[0].empty()); +} +TEST(splitString, ACCEPT_EMPTY_STRING4) +{ + std::string originString = ",1,2,3,"; + auto out = splitString(originString, ":", true); + EXPECT_EQ(out.size(), 1); + EXPECT_STREQ(out[0].data(), ",1,2,3,"); +} +TEST(splitString, ACCEPT_EMPTY_STRING5) +{ + std::string originString = "trantor::splitString"; + auto out = splitString(originString, "::", true); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), "splitString"); +} +TEST(splitString, ACCEPT_EMPTY_STRING6) +{ + std::string originString = "trantor::::splitString"; + auto out = splitString(originString, "::", true); + EXPECT_EQ(out.size(), 3); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), ""); + EXPECT_STREQ(out[2].data(), "splitString"); +} +TEST(splitString, ACCEPT_EMPTY_STRING7) +{ + std::string originString = "trantor:::splitString"; + auto out = splitString(originString, "::", true); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), ":splitString"); +} +TEST(splitString, ACCEPT_EMPTY_STRING8) +{ + std::string originString = "trantor:::splitString"; + auto out = splitString(originString, "trantor:::splitString", true); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), ""); + EXPECT_STREQ(out[1].data(), ""); +} +TEST(splitString, ACCEPT_EMPTY_STRING9) +{ + std::string originString = ""; + auto out = splitString(originString, ",", true); + EXPECT_EQ(out.size(), 1); + EXPECT_STREQ(out[0].data(), ""); +} +TEST(splitString, ACCEPT_EMPTY_STRING10) +{ + std::string originString = "trantor"; + auto out = splitString(originString, "", true); + EXPECT_EQ(out.size(), 0); +} +TEST(splitString, ACCEPT_EMPTY_STRING11) +{ + std::string originString = ""; + auto out = splitString(originString, "", true); + EXPECT_EQ(out.size(), 0); +} + +TEST(splitString, NO_ACCEPT_EMPTY_STRING1) +{ + std::string originString = ",1,2,3"; + auto out = splitString(originString, ","); + EXPECT_EQ(out.size(), 3); + EXPECT_STREQ(out[0].data(), "1"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING2) +{ + std::string originString = ",1,2,3,"; + auto out = splitString(originString, ","); + EXPECT_EQ(out.size(), 3); + EXPECT_STREQ(out[0].data(), "1"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING3) +{ + std::string originString = ",1,2,3,"; + auto out = splitString(originString, ":"); + EXPECT_EQ(out.size(), 1); + EXPECT_STREQ(out[0].data(), ",1,2,3,"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING4) +{ + std::string originString = "trantor::splitString"; + auto out = splitString(originString, "::"); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), "splitString"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING5) +{ + std::string originString = "trantor::::splitString"; + auto out = splitString(originString, "::"); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), "splitString"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING6) +{ + std::string originString = "trantor:::splitString"; + auto out = splitString(originString, "::"); + EXPECT_EQ(out.size(), 2); + EXPECT_STREQ(out[0].data(), "trantor"); + EXPECT_STREQ(out[1].data(), ":splitString"); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING7) +{ + std::string originString = "trantor:::splitString"; + auto out = splitString(originString, "trantor:::splitString"); + EXPECT_EQ(out.size(), 0); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING8) +{ + std::string originString = ""; + auto out = splitString(originString, ","); + EXPECT_EQ(out.size(), 0); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING9) +{ + std::string originString = "trantor"; + auto out = splitString(originString, ""); + EXPECT_EQ(out.size(), 0); +} +TEST(splitString, NO_ACCEPT_EMPTY_STRING10) +{ + std::string originString = ""; + auto out = splitString(originString, ""); + EXPECT_EQ(out.size(), 0); +} +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/utils/AsyncFileLogger.cc b/modules/http_trantor/trantor/utils/AsyncFileLogger.cc new file mode 100644 index 0000000..e754d48 --- /dev/null +++ b/modules/http_trantor/trantor/utils/AsyncFileLogger.cc @@ -0,0 +1,253 @@ +/** + * + * AsyncFileLogger.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#ifndef _WIN32 +#include +#ifdef __linux__ +#include +#endif +#endif +#include +#include +#include +#include + +namespace trantor +{ +static constexpr std::chrono::seconds kLogFlushTimeout{1}; +static constexpr size_t kMemBufferSize{4 * 1024 * 1024}; +extern const char *strerror_tl(int savedErrno); +} // namespace trantor + +using namespace trantor; + +AsyncFileLogger::AsyncFileLogger() + : logBufferPtr_(new std::string), nextBufferPtr_(new std::string) +{ + logBufferPtr_->reserve(kMemBufferSize); + nextBufferPtr_->reserve(kMemBufferSize); +} + +AsyncFileLogger::~AsyncFileLogger() +{ + // std::cout << "~AsyncFileLogger" << std::endl; + stopFlag_ = true; + if (threadPtr_) + { + cond_.notify_all(); + threadPtr_->join(); + } + // std::cout << "thread exit" << std::endl; + { + std::lock_guard guard_(mutex_); + if (logBufferPtr_->length() > 0) + { + writeBuffers_.push(logBufferPtr_); + } + while (!writeBuffers_.empty()) + { + StringPtr tmpPtr = (StringPtr &&) writeBuffers_.front(); + writeBuffers_.pop(); + writeLogToFile(tmpPtr); + } + } +} + +void AsyncFileLogger::output(const char *msg, const uint64_t len) +{ + std::lock_guard guard_(mutex_); + if (len > kMemBufferSize) + return; + if (!logBufferPtr_) + { + logBufferPtr_ = std::make_shared(); + logBufferPtr_->reserve(kMemBufferSize); + } + if (logBufferPtr_->capacity() - logBufferPtr_->length() < len) + { + swapBuffer(); + cond_.notify_one(); + } + if (writeBuffers_.size() > 25) // 100M bytes logs in buffer + { + ++lostCounter_; + return; + } + + if (lostCounter_ > 0) + { + char logErr[128]; + auto strlen = + snprintf(logErr, + sizeof(logErr), + "%llu log information is lost\n", + static_cast(lostCounter_)); + lostCounter_ = 0; + logBufferPtr_->append(logErr, strlen); + } + logBufferPtr_->append(msg, len); +} + +void AsyncFileLogger::flush() +{ + std::lock_guard guard_(mutex_); + if (logBufferPtr_->length() > 0) + { + // std::cout<<"flush log buffer + // len:"<length()<( + new LoggerFile(filePath_, fileBaseName_, fileExtName_)); + } + loggerFilePtr_->writeLog(buf); + if (loggerFilePtr_->getLength() > sizeLimit_) + { + loggerFilePtr_.reset(); + } +} + +void AsyncFileLogger::logThreadFunc() +{ +#ifdef __linux__ + prctl(PR_SET_NAME, "AsyncFileLogger"); +#endif + while (!stopFlag_) + { + { + std::unique_lock lock(mutex_); + while (writeBuffers_.size() == 0 && !stopFlag_) + { + if (cond_.wait_for(lock, kLogFlushTimeout) == + std::cv_status::timeout) + { + if (logBufferPtr_->length() > 0) + { + swapBuffer(); + } + break; + } + } + tmpBuffers_.swap(writeBuffers_); + } + + while (!tmpBuffers_.empty()) + { + StringPtr tmpPtr = (StringPtr &&) tmpBuffers_.front(); + tmpBuffers_.pop(); + writeLogToFile(tmpPtr); + tmpPtr->clear(); + { + std::unique_lock lock(mutex_); + nextBufferPtr_ = tmpPtr; + } + } + if (loggerFilePtr_) + loggerFilePtr_->flush(); + } +} + +void AsyncFileLogger::startLogging() +{ + threadPtr_ = std::unique_ptr( + new std::thread(std::bind(&AsyncFileLogger::logThreadFunc, this))); +} + +AsyncFileLogger::LoggerFile::LoggerFile(const std::string &filePath, + const std::string &fileBaseName, + const std::string &fileExtName) + : creationDate_(Date::date()), + filePath_(filePath), + fileBaseName_(fileBaseName), + fileExtName_(fileExtName) +{ + fileFullName_ = filePath + fileBaseName + fileExtName; +#ifndef _MSC_VER + fp_ = fopen(fileFullName_.c_str(), "a"); +#else + fp_ = _fsopen(fileFullName_.c_str(), "a+", _SH_DENYWR); +#endif + if (fp_ == nullptr) + { + std::cout << strerror_tl(errno) << std::endl; + } +} + +uint64_t AsyncFileLogger::LoggerFile::fileSeq_{0}; +void AsyncFileLogger::LoggerFile::writeLog(const StringPtr buf) +{ + if (fp_) + { + // std::cout<<"write "<length()<<" bytes to file"<c_str(), 1, buf->length(), fp_); + } +} + +void AsyncFileLogger::LoggerFile::flush() +{ + if (fp_) + { + fflush(fp_); + } +} + +uint64_t AsyncFileLogger::LoggerFile::getLength() +{ + if (fp_) + return ftell(fp_); + return 0; +} + +AsyncFileLogger::LoggerFile::~LoggerFile() +{ + if (fp_) + { + fclose(fp_); + char seq[12]; + snprintf(seq, + sizeof(seq), + ".%06llu", + static_cast(fileSeq_ % 1000000)); + ++fileSeq_; + std::string newName = + filePath_ + fileBaseName_ + "." + + creationDate_.toCustomedFormattedString("%y%m%d-%H%M%S") + + std::string(seq) + fileExtName_; + rename(fileFullName_.c_str(), newName.c_str()); + } +} + +void AsyncFileLogger::swapBuffer() +{ + writeBuffers_.push(logBufferPtr_); + if (nextBufferPtr_) + { + logBufferPtr_ = nextBufferPtr_; + nextBufferPtr_.reset(); + logBufferPtr_->clear(); + } + else + { + logBufferPtr_ = std::make_shared(); + logBufferPtr_->reserve(kMemBufferSize); + } +} diff --git a/modules/http_trantor/trantor/utils/AsyncFileLogger.h b/modules/http_trantor/trantor/utils/AsyncFileLogger.h new file mode 100644 index 0000000..0f54c67 --- /dev/null +++ b/modules/http_trantor/trantor/utils/AsyncFileLogger.h @@ -0,0 +1,140 @@ +/** + * + * @file AsyncFileLogger.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +using StringPtr = std::shared_ptr; +using StringPtrQueue = std::queue; + +/** + * @brief This class implements utility functions for writing logs to files + * asynchronously. + * + */ +class TRANTOR_EXPORT AsyncFileLogger : NonCopyable +{ + public: + /** + * @brief Write the message to the log file. + * + * @param msg + * @param len + */ + void output(const char *msg, const uint64_t len); + + /** + * @brief Flush data from memory buffer to the log file. + * + */ + void flush(); + + /** + * @brief Start writing log files. + * + */ + void startLogging(); + + /** + * @brief Set the size limit of log files. When the log file size reaches + * the limit, the log file is switched. + * + * @param limit + */ + void setFileSizeLimit(uint64_t limit) + { + sizeLimit_ = limit; + } + + /** + * @brief Set the log file name. + * + * @param baseName The base name of the log file. + * @param extName The extended name of the log file. + * @param path The location where the log file is stored. + */ + void setFileName(const std::string &baseName, + const std::string &extName = ".log", + const std::string &path = "./") + { + fileBaseName_ = baseName; + extName[0] == '.' ? fileExtName_ = extName + : fileExtName_ = std::string(".") + extName; + filePath_ = path; + if (filePath_.length() == 0) + filePath_ = "./"; + if (filePath_[filePath_.length() - 1] != '/') + filePath_ = filePath_ + "/"; + } + ~AsyncFileLogger(); + AsyncFileLogger(); + + protected: + std::mutex mutex_; + std::condition_variable cond_; + StringPtr logBufferPtr_; + StringPtr nextBufferPtr_; + StringPtrQueue writeBuffers_; + StringPtrQueue tmpBuffers_; + void writeLogToFile(const StringPtr buf); + std::unique_ptr threadPtr_; + bool stopFlag_{false}; + void logThreadFunc(); + std::string filePath_{"./"}; + std::string fileBaseName_{"trantor"}; + std::string fileExtName_{".log"}; + uint64_t sizeLimit_{20 * 1024 * 1024}; + class LoggerFile : NonCopyable + { + public: + LoggerFile(const std::string &filePath, + const std::string &fileBaseName, + const std::string &fileExtName); + ~LoggerFile(); + void writeLog(const StringPtr buf); + uint64_t getLength(); + explicit operator bool() const + { + return fp_ != nullptr; + } + void flush(); + + protected: + FILE *fp_{nullptr}; + Date creationDate_; + std::string fileFullName_; + std::string filePath_; + std::string fileBaseName_; + std::string fileExtName_; + static uint64_t fileSeq_; + }; + std::unique_ptr loggerFilePtr_; + + uint64_t lostCounter_{0}; + void swapBuffer(); +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.cc b/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.cc new file mode 100644 index 0000000..8054755 --- /dev/null +++ b/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.cc @@ -0,0 +1,95 @@ +/** + * + * ConcurrentTaskQueue.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include +#include +#ifdef __linux__ +#include +#endif +using namespace trantor; +ConcurrentTaskQueue::ConcurrentTaskQueue(size_t threadNum, + const std::string &name) + : queueCount_(threadNum), queueName_(name), stop_(false) +{ + assert(threadNum > 0); + for (unsigned int i = 0; i < queueCount_; ++i) + { + threads_.push_back( + std::thread(std::bind(&ConcurrentTaskQueue::queueFunc, this, i))); + } +} +void ConcurrentTaskQueue::runTaskInQueue(const std::function &task) +{ + LOG_TRACE << "copy task into queue"; + std::lock_guard lock(taskMutex_); + taskQueue_.push(task); + taskCond_.notify_one(); +} +void ConcurrentTaskQueue::runTaskInQueue(std::function &&task) +{ + LOG_TRACE << "move task into queue"; + std::lock_guard lock(taskMutex_); + taskQueue_.push(std::move(task)); + taskCond_.notify_one(); +} +void ConcurrentTaskQueue::queueFunc(int queueNum) +{ + char tmpName[32]; + snprintf(tmpName, sizeof(tmpName), "%s%d", queueName_.c_str(), queueNum); +#ifdef __linux__ + ::prctl(PR_SET_NAME, tmpName); +#endif + while (!stop_) + { + std::function r; + { + std::unique_lock lock(taskMutex_); + while (!stop_ && taskQueue_.size() == 0) + { + taskCond_.wait(lock); + } + if (taskQueue_.size() > 0) + { + LOG_TRACE << "got a new task!"; + r = std::move(taskQueue_.front()); + taskQueue_.pop(); + } + else + continue; + } + r(); + } +} + +size_t ConcurrentTaskQueue::getTaskCount() +{ + std::lock_guard guard(taskMutex_); + return taskQueue_.size(); +} + +void ConcurrentTaskQueue::stop() +{ + if (!stop_) + { + stop_ = true; + taskCond_.notify_all(); + for (auto &t : threads_) + t.join(); + } +} +ConcurrentTaskQueue::~ConcurrentTaskQueue() +{ + stop(); +} diff --git a/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.h b/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.h new file mode 100644 index 0000000..bcde18c --- /dev/null +++ b/modules/http_trantor/trantor/utils/ConcurrentTaskQueue.h @@ -0,0 +1,89 @@ +/** + * + * @file ConcurrentTaskQueue.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class implements a task queue running in parallel. Basically this + * can be called a threads pool. + * + */ +class TRANTOR_EXPORT ConcurrentTaskQueue : public TaskQueue +{ + public: + /** + * @brief Construct a new concurrent task queue instance. + * + * @param threadNum The number of threads in the queue. + * @param name The name of the queue. + */ + ConcurrentTaskQueue(size_t threadNum, const std::string &name); + + /** + * @brief Run a task in the queue. + * + * @param task + */ + virtual void runTaskInQueue(const std::function &task); + virtual void runTaskInQueue(std::function &&task); + + /** + * @brief Get the name of the queue. + * + * @return std::string + */ + virtual std::string getName() const + { + return queueName_; + }; + + /** + * @brief Get the number of tasks to be executed in the queue. + * + * @return size_t + */ + size_t getTaskCount(); + + /** + * @brief Stop all threads in the queue. + * + */ + void stop(); + + ~ConcurrentTaskQueue(); + + private: + size_t queueCount_; + std::string queueName_; + + std::queue> taskQueue_; + std::vector threads_; + + std::mutex taskMutex_; + std::condition_variable taskCond_; + std::atomic_bool stop_; + void queueFunc(int queueNum); +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/Date.cc b/modules/http_trantor/trantor/utils/Date.cc new file mode 100644 index 0000000..14bc83e --- /dev/null +++ b/modules/http_trantor/trantor/utils/Date.cc @@ -0,0 +1,359 @@ +/** + * + * Date.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include "Date.h" +#include "Funcs.h" +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +namespace trantor +{ +#ifdef _WIN32 +int gettimeofday(timeval *tp, void *tzp) +{ + time_t clock; + struct tm tm; + SYSTEMTIME wtm; + + GetLocalTime(&wtm); + tm.tm_year = wtm.wYear - 1900; + tm.tm_mon = wtm.wMonth - 1; + tm.tm_mday = wtm.wDay; + tm.tm_hour = wtm.wHour; + tm.tm_min = wtm.wMinute; + tm.tm_sec = wtm.wSecond; + tm.tm_isdst = -1; + clock = mktime(&tm); + tp->tv_sec = static_cast(clock); + tp->tv_usec = wtm.wMilliseconds * 1000; + + return (0); +} +#endif +const Date Date::date() +{ +#ifndef _WIN32 + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t seconds = tv.tv_sec; + return Date(seconds * MICRO_SECONDS_PRE_SEC + tv.tv_usec); +#else + timeval tv; + gettimeofday(&tv, NULL); + int64_t seconds = tv.tv_sec; + return Date(seconds * MICRO_SECONDS_PRE_SEC + tv.tv_usec); +#endif +} +const Date Date::after(double second) const +{ + return Date(static_cast(microSecondsSinceEpoch_ + + second * MICRO_SECONDS_PRE_SEC)); +} +const Date Date::roundSecond() const +{ + return Date(microSecondsSinceEpoch_ - + (microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC)); +} +const Date Date::roundDay() const +{ + struct tm t; + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); +#ifndef _WIN32 + localtime_r(&seconds, &t); +#else + localtime_s(&t, &seconds); +#endif + t.tm_hour = 0; + t.tm_min = 0; + t.tm_sec = 0; + return Date(mktime(&t) * MICRO_SECONDS_PRE_SEC); +} +struct tm Date::tmStruct() const +{ + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + gmtime_r(&seconds, &tm_time); +#else + gmtime_s(&tm_time, &seconds); +#endif + return tm_time; +} +std::string Date::toFormattedString(bool showMicroseconds) const +{ + // std::cout<<"toFormattedString"<(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + gmtime_r(&seconds, &tm_time); +#else + gmtime_s(&tm_time, &seconds); +#endif + + if (showMicroseconds) + { + int microseconds = + static_cast(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC); + snprintf(buf, + sizeof(buf), + "%4d%02d%02d %02d:%02d:%02d.%06d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec, + microseconds); + } + else + { + snprintf(buf, + sizeof(buf), + "%4d%02d%02d %02d:%02d:%02d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec); + } + return buf; +} +std::string Date::toCustomedFormattedString(const std::string &fmtStr, + bool showMicroseconds) const +{ + char buf[256] = {0}; + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + gmtime_r(&seconds, &tm_time); +#else + gmtime_s(&tm_time, &seconds); +#endif + strftime(buf, sizeof(buf), fmtStr.c_str(), &tm_time); + if (!showMicroseconds) + return std::string(buf); + char decimals[12] = {0}; + int microseconds = + static_cast(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC); + snprintf(decimals, sizeof(decimals), ".%06d", microseconds); + return std::string(buf) + decimals; +} +void Date::toCustomedFormattedString(const std::string &fmtStr, + char *str, + size_t len) const +{ + // not safe + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + gmtime_r(&seconds, &tm_time); +#else + gmtime_s(&tm_time, &seconds); +#endif + strftime(str, len, fmtStr.c_str(), &tm_time); +} +std::string Date::toFormattedStringLocal(bool showMicroseconds) const +{ + // std::cout<<"toFormattedString"<(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + localtime_r(&seconds, &tm_time); +#else + localtime_s(&tm_time, &seconds); +#endif + + if (showMicroseconds) + { + int microseconds = + static_cast(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC); + snprintf(buf, + sizeof(buf), + "%4d%02d%02d %02d:%02d:%02d.%06d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec, + microseconds); + } + else + { + snprintf(buf, + sizeof(buf), + "%4d%02d%02d %02d:%02d:%02d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec); + } + return buf; +} +std::string Date::toDbStringLocal() const +{ + char buf[128] = {0}; + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + localtime_r(&seconds, &tm_time); +#else + localtime_s(&tm_time, &seconds); +#endif + bool showMicroseconds = + (microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC != 0); + if (showMicroseconds) + { + int microseconds = + static_cast(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC); + snprintf(buf, + sizeof(buf), + "%4d-%02d-%02d %02d:%02d:%02d.%06d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec, + microseconds); + } + else + { + if (*this == roundDay()) + { + snprintf(buf, + sizeof(buf), + "%4d-%02d-%02d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday); + } + else + { + snprintf(buf, + sizeof(buf), + "%4d-%02d-%02d %02d:%02d:%02d", + tm_time.tm_year + 1900, + tm_time.tm_mon + 1, + tm_time.tm_mday, + tm_time.tm_hour, + tm_time.tm_min, + tm_time.tm_sec); + } + } + return buf; +} +Date Date::fromDbStringLocal(const std::string &datetime) +{ + unsigned int year = {0}, month = {0}, day = {0}, hour = {0}, minute = {0}, + second = {0}, microSecond = {0}; + std::vector &&v = splitString(datetime, " "); + if (2 == v.size()) + { + // date + std::vector date = splitString(v[0], "-"); + if (3 == date.size()) + { + year = std::stol(date[0]); + month = std::stol(date[1]); + day = std::stol(date[2]); + std::vector time = splitString(v[1], ":"); + if (2 < time.size()) + { + hour = std::stol(time[0]); + minute = std::stol(time[1]); + auto seconds = splitString(time[2], "."); + second = std::stol(seconds[0]); + if (1 < seconds.size()) + { + if (seconds[1].length() > 6) + { + seconds[1].resize(6); + } + else if (seconds[1].length() < 6) + { + seconds[1].append(6 - seconds[1].length(), '0'); + } + microSecond = std::stol(seconds[1]); + } + } + } + } + return std::move( + trantor::Date(year, month, day, hour, minute, second, microSecond)); +} +std::string Date::toCustomedFormattedStringLocal(const std::string &fmtStr, + bool showMicroseconds) const +{ + char buf[256] = {0}; + time_t seconds = + static_cast(microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC); + struct tm tm_time; +#ifndef _WIN32 + localtime_r(&seconds, &tm_time); +#else + localtime_s(&tm_time, &seconds); +#endif + strftime(buf, sizeof(buf), fmtStr.c_str(), &tm_time); + if (!showMicroseconds) + return std::string(buf); + char decimals[12] = {0}; + int microseconds = + static_cast(microSecondsSinceEpoch_ % MICRO_SECONDS_PRE_SEC); + snprintf(decimals, sizeof(decimals), ".%06d", microseconds); + return std::string(buf) + decimals; +} +Date::Date(unsigned int year, + unsigned int month, + unsigned int day, + unsigned int hour, + unsigned int minute, + unsigned int second, + unsigned int microSecond) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + time_t epoch; + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + epoch = mktime(&tm); + microSecondsSinceEpoch_ = epoch * MICRO_SECONDS_PRE_SEC + microSecond; +} + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/Date.h b/modules/http_trantor/trantor/utils/Date.h new file mode 100644 index 0000000..8543a2d --- /dev/null +++ b/modules/http_trantor/trantor/utils/Date.h @@ -0,0 +1,281 @@ +/** + * + * @file Date.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include + +#define MICRO_SECONDS_PRE_SEC 1000000 + +namespace trantor +{ +/** + * @brief This class represents a time point. + * + */ +class TRANTOR_EXPORT Date +{ + public: + Date() : microSecondsSinceEpoch_(0){}; + + /** + * @brief Construct a new Date instance. + * + * @param microSec The microseconds from 1970-01-01 00:00:00. + */ + explicit Date(int64_t microSec) : microSecondsSinceEpoch_(microSec){}; + + /** + * @brief Construct a new Date instance. + * + * @param year + * @param month + * @param day + * @param hour + * @param minute + * @param second + * @param microSecond + */ + Date(unsigned int year, + unsigned int month, + unsigned int day, + unsigned int hour = 0, + unsigned int minute = 0, + unsigned int second = 0, + unsigned int microSecond = 0); + + /** + * @brief Create a Date object that represents the current time. + * + * @return const Date + */ + static const Date date(); + + /** + * @brief Same as the date() method. + * + * @return const Date + */ + static const Date now() + { + return Date::date(); + } + + /** + * @brief Return a new Date instance that represents the time after some + * seconds from *this. + * + * @param second + * @return const Date + */ + const Date after(double second) const; + + /** + * @brief Return a new Date instance that equals to *this, but with zero + * microseconds. + * + * @return const Date + */ + const Date roundSecond() const; + + /// Create a Date object equal to * this, but numbers of hours, minutes, + /// seconds and microseconds are zero. + + /** + * @brief Return a new Date instance that equals to * this, but with zero + * hours, minutes, seconds and microseconds. + * + * @return const Date + */ + const Date roundDay() const; + + ~Date(){}; + + /** + * @brief Return true if the time point is equal to another. + * + */ + bool operator==(const Date &date) const + { + return microSecondsSinceEpoch_ == date.microSecondsSinceEpoch_; + } + + /** + * @brief Return true if the time point is not equal to another. + * + */ + bool operator!=(const Date &date) const + { + return microSecondsSinceEpoch_ != date.microSecondsSinceEpoch_; + } + + /** + * @brief Return true if the time point is earlier than another. + * + */ + bool operator<(const Date &date) const + { + return microSecondsSinceEpoch_ < date.microSecondsSinceEpoch_; + } + + /** + * @brief Return true if the time point is later than another. + * + */ + bool operator>(const Date &date) const + { + return microSecondsSinceEpoch_ > date.microSecondsSinceEpoch_; + } + + /** + * @brief Return true if the time point is not earlier than another. + * + */ + bool operator>=(const Date &date) const + { + return microSecondsSinceEpoch_ >= date.microSecondsSinceEpoch_; + } + + /** + * @brief Return true if the time point is not later than another. + * + */ + bool operator<=(const Date &date) const + { + return microSecondsSinceEpoch_ <= date.microSecondsSinceEpoch_; + } + + /** + * @brief Get the number of milliseconds since 1970-01-01 00:00. + * + * @return int64_t + */ + int64_t microSecondsSinceEpoch() const + { + return microSecondsSinceEpoch_; + } + + /** + * @brief Get the number of seconds since 1970-01-01 00:00. + * + * @return int64_t + */ + int64_t secondsSinceEpoch() const + { + return microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC; + } + + /** + * @brief Get the tm struct for the time point. + * + * @return struct tm + */ + struct tm tmStruct() const; + + /** + * @brief Generate a UTC time string + * @example: + * 20180101 10:10:25 //If the @param showMicroseconds is false + * 20180101 10:10:25:102414 //If the @param showMicroseconds is true + */ + std::string toFormattedString(bool showMicroseconds) const; + + /** + * @brief Generate a UTC time string formated by the @param fmtStr + * The @param fmtStr is the format string for the function strftime() + * @example: + * 2018-01-01 10:10:25 //If the @param fmtStr is "%Y-%m-%d + * %H:%M:%S" and the @param showMicroseconds is false 2018-01-01 + * 10:10:25:102414 //If the @param fmtStr is "%Y-%m-%d %H:%M:%S" and the + * @param showMicroseconds is true + */ + std::string toCustomedFormattedString(const std::string &fmtStr, + bool showMicroseconds = false) const; + + /** + * @brief Generate a local time zone string, the format of the string is + * same as the mothed toFormattedString + * + * @param showMicroseconds + * @return std::string + */ + std::string toFormattedStringLocal(bool showMicroseconds) const; + + /** + * @brief Generate a local time zone string formated by the @param fmtStr + * + * @param fmtStr + * @param showMicroseconds + * @return std::string + */ + std::string toCustomedFormattedStringLocal( + const std::string &fmtStr, + bool showMicroseconds = false) const; + + /** + * @brief Generate a local time zone string for database. + * @example: + * 2018-01-01 //If hours, minutes, seconds and + * microseconds are zero 2018-01-01 10:10:25 //If the microsecond + * is zero 2018-01-01 10:10:25:102414 //If the microsecond is not zero + */ + std::string toDbStringLocal() const; + + /** + * @brief From DB string to trantor local time zone. + * + * Inverse of toDbStringLocal() + */ + static Date fromDbStringLocal(const std::string &datetime); + + /** + * @brief Generate a UTC time string. + * + * @param fmtStr The format string. + * @param str The string buffer for the generated time string. + * @param len The length of the string buffer. + */ + void toCustomedFormattedString(const std::string &fmtStr, + char *str, + size_t len) const; // UTC + + /** + * @brief Return true if the time point is in a same second as another. + * + * @param date + * @return true + * @return false + */ + bool isSameSecond(const Date &date) const + { + return microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC == + date.microSecondsSinceEpoch_ / MICRO_SECONDS_PRE_SEC; + } + + /** + * @brief Swap the time point with another. + * + * @param that + */ + void swap(Date &that) + { + std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_); + } + + private: + int64_t microSecondsSinceEpoch_{0}; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/Funcs.h b/modules/http_trantor/trantor/utils/Funcs.h new file mode 100644 index 0000000..7a13273 --- /dev/null +++ b/modules/http_trantor/trantor/utils/Funcs.h @@ -0,0 +1,53 @@ +/** + * + * Funcs.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#include +#include +namespace trantor +{ +inline uint64_t hton64(uint64_t n) +{ + static const int one = 1; + static const char sig = *(char *)&one; + if (sig == 0) + return n; // for big endian machine just return the input + char *ptr = reinterpret_cast(&n); + std::reverse(ptr, ptr + sizeof(uint64_t)); + return n; +} +inline uint64_t ntoh64(uint64_t n) +{ + return hton64(n); +} +inline std::vector splitString(const std::string &s, + const std::string &delimiter, + bool acceptEmptyString = false) +{ + if (delimiter.empty()) + return std::vector{}; + std::vector v; + size_t last = 0; + size_t next = 0; + while ((next = s.find(delimiter, last)) != std::string::npos) + { + if (next > last || acceptEmptyString) + v.push_back(s.substr(last, next - last)); + last = next + delimiter.length(); + } + if (s.length() > last || acceptEmptyString) + v.push_back(s.substr(last)); + return v; +} +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/LockFreeQueue.h b/modules/http_trantor/trantor/utils/LockFreeQueue.h new file mode 100644 index 0000000..f608a90 --- /dev/null +++ b/modules/http_trantor/trantor/utils/LockFreeQueue.h @@ -0,0 +1,114 @@ +/** + * + * @file LockFreeQueue.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#include +#include +#include +#include +#include +namespace trantor +{ +/** + * @brief This class template represents a lock-free multiple producers single + * consumer queue + * + * @tparam T The type of the items in the queue. + */ +template +class MpscQueue : public NonCopyable +{ + public: + MpscQueue() + : head_(new BufferNode), tail_(head_.load(std::memory_order_relaxed)) + { + } + ~MpscQueue() + { + T output; + while (this->dequeue(output)) + { + } + BufferNode *front = head_.load(std::memory_order_relaxed); + delete front; + } + + /** + * @brief Put a item into the queue. + * + * @param input + * @note This method can be called in multiple threads. + */ + void enqueue(T &&input) + { + BufferNode *node{new BufferNode(std::move(input))}; + BufferNode *prevhead{head_.exchange(node, std::memory_order_acq_rel)}; + prevhead->next_.store(node, std::memory_order_release); + } + void enqueue(const T &input) + { + BufferNode *node{new BufferNode(input)}; + BufferNode *prevhead{head_.exchange(node, std::memory_order_acq_rel)}; + prevhead->next_.store(node, std::memory_order_release); + } + + /** + * @brief Get a item from the queue. + * + * @param output + * @return false if the queue is empty. + * @note This method must be called in a single thread. + */ + bool dequeue(T &output) + { + BufferNode *tail = tail_.load(std::memory_order_relaxed); + BufferNode *next = tail->next_.load(std::memory_order_acquire); + + if (next == nullptr) + { + return false; + } + output = std::move(*(next->dataPtr_)); + delete next->dataPtr_; + tail_.store(next, std::memory_order_release); + delete tail; + return true; + } + + bool empty() + { + BufferNode *tail = tail_.load(std::memory_order_relaxed); + BufferNode *next = tail->next_.load(std::memory_order_acquire); + return next == nullptr; + } + + private: + struct BufferNode + { + BufferNode() = default; + BufferNode(const T &data) : dataPtr_(new T(data)) + { + } + BufferNode(T &&data) : dataPtr_(new T(std::move(data))) + { + } + T *dataPtr_; + std::atomic next_{nullptr}; + }; + + std::atomic head_; + std::atomic tail_; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/LogStream.cc b/modules/http_trantor/trantor/utils/LogStream.cc new file mode 100644 index 0000000..bbb6bd5 --- /dev/null +++ b/modules/http_trantor/trantor/utils/LogStream.cc @@ -0,0 +1,273 @@ +/** + * + * LogStream.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +// taken from muduo lib + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace trantor; +using namespace trantor::detail; + +namespace trantor +{ +namespace detail +{ +const char digits[] = "9876543210123456789"; +const char *zero = digits + 9; + +const char digitsHex[] = "0123456789ABCDEF"; + +// Efficient Integer to String Conversions, by Matthew Wilson. +template +size_t convert(char buf[], T value) +{ + T i = value; + char *p = buf; + + do + { + int lsd = static_cast(i % 10); + i /= 10; + *p++ = zero[lsd]; + } while (i != 0); + + if (value < 0) + { + *p++ = '-'; + } + *p = '\0'; + std::reverse(buf, p); + + return p - buf; +} + +size_t convertHex(char buf[], uintptr_t value) +{ + uintptr_t i = value; + char *p = buf; + + do + { + int lsd = static_cast(i % 16); + i /= 16; + *p++ = digitsHex[lsd]; + } while (i != 0); + + *p = '\0'; + std::reverse(buf, p); + + return p - buf; +} + +template class FixedBuffer; +template class FixedBuffer; + +} // namespace detail +} // namespace trantor + +template +const char *FixedBuffer::debugString() +{ + *cur_ = '\0'; + return data_; +} + +template +void FixedBuffer::cookieStart() +{ +} + +template +void FixedBuffer::cookieEnd() +{ +} + +template +void LogStream::formatInteger(T v) +{ + constexpr static int kMaxNumericSize = std::numeric_limits::digits10 + 4; + if (exBuffer_.empty()) + { + if (buffer_.avail() >= kMaxNumericSize) + { + size_t len = convert(buffer_.current(), v); + buffer_.add(len); + return; + } + else + { + exBuffer_.append(buffer_.data(), buffer_.length()); + } + } + auto oldLen = exBuffer_.length(); + exBuffer_.resize(oldLen + kMaxNumericSize); + size_t len = convert(&exBuffer_[oldLen], v); + exBuffer_.resize(oldLen + len); +} + +LogStream &LogStream::operator<<(short v) +{ + *this << static_cast(v); + return *this; +} + +LogStream &LogStream::operator<<(unsigned short v) +{ + *this << static_cast(v); + return *this; +} + +LogStream &LogStream::operator<<(int v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(unsigned int v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(long v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(unsigned long v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(const long long &v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(const unsigned long long &v) +{ + formatInteger(v); + return *this; +} + +LogStream &LogStream::operator<<(const void *p) +{ + uintptr_t v = reinterpret_cast(p); + constexpr static int kMaxNumericSize = + std::numeric_limits::digits / 4 + 4; + if (exBuffer_.empty()) + { + if (buffer_.avail() >= kMaxNumericSize) + { + char *buf = buffer_.current(); + buf[0] = '0'; + buf[1] = 'x'; + size_t len = convertHex(buf + 2, v); + buffer_.add(len + 2); + return *this; + } + else + { + exBuffer_.append(buffer_.data(), buffer_.length()); + } + } + auto oldLen = exBuffer_.length(); + exBuffer_.resize(oldLen + kMaxNumericSize); + char *buf = &exBuffer_[oldLen]; + buf[0] = '0'; + buf[1] = 'x'; + size_t len = convertHex(buf + 2, v); + exBuffer_.resize(oldLen + len + 2); + return *this; +} + +// TODO: replace this with Grisu3 by Florian Loitsch. +LogStream &LogStream::operator<<(const double &v) +{ + constexpr static int kMaxNumericSize = 32; + if (exBuffer_.empty()) + { + if (buffer_.avail() >= kMaxNumericSize) + { + int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12g", v); + buffer_.add(len); + return *this; + } + else + { + exBuffer_.append(buffer_.data(), buffer_.length()); + } + } + auto oldLen = exBuffer_.length(); + exBuffer_.resize(oldLen + kMaxNumericSize); + int len = snprintf(&(exBuffer_[oldLen]), kMaxNumericSize, "%.12g", v); + exBuffer_.resize(oldLen + len); + return *this; +} + +LogStream &LogStream::operator<<(const long double &v) +{ + constexpr static int kMaxNumericSize = 48; + if (exBuffer_.empty()) + { + if (buffer_.avail() >= kMaxNumericSize) + { + int len = snprintf(buffer_.current(), kMaxNumericSize, "%.12Lg", v); + buffer_.add(len); + return *this; + } + else + { + exBuffer_.append(buffer_.data(), buffer_.length()); + } + } + auto oldLen = exBuffer_.length(); + exBuffer_.resize(oldLen + kMaxNumericSize); + int len = snprintf(&(exBuffer_[oldLen]), kMaxNumericSize, "%.12Lg", v); + exBuffer_.resize(oldLen + len); + return *this; +} + +template +Fmt::Fmt(const char *fmt, T val) +{ + length_ = snprintf(buf_, sizeof buf_, fmt, val); + assert(static_cast(length_) < sizeof buf_); +} + +// Explicit instantiations + +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, char); + +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, short); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned short); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, int); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned int); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, long); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned long); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, long long); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, unsigned long long); + +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, float); +template TRANTOR_EXPORT Fmt::Fmt(const char *fmt, double); diff --git a/modules/http_trantor/trantor/utils/LogStream.h b/modules/http_trantor/trantor/utils/LogStream.h new file mode 100644 index 0000000..d668434 --- /dev/null +++ b/modules/http_trantor/trantor/utils/LogStream.h @@ -0,0 +1,277 @@ +/** + * + * @file LogStream.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +// Taken from muduo lib and modified. Classes in this file are used internally. +#include +#include + +#include +#include // memcpy +#include + +namespace trantor +{ +namespace detail +{ +static constexpr size_t kSmallBuffer{4000}; +static constexpr size_t kLargeBuffer{4000 * 1000}; + +template +class TRANTOR_EXPORT FixedBuffer : NonCopyable +{ + public: + FixedBuffer() : cur_(data_) + { + setCookie(cookieStart); + } + + ~FixedBuffer() + { + setCookie(cookieEnd); + } + + bool append(const char * /*restrict*/ buf, size_t len) + { + if ((size_t)(avail()) > len) + { + memcpy(cur_, buf, len); + cur_ += len; + return true; + } + return false; + } + + const char *data() const + { + return data_; + } + int length() const + { + return static_cast(cur_ - data_); + } + + // write to data_ directly + char *current() + { + return cur_; + } + int avail() const + { + return static_cast(end() - cur_); + } + void add(size_t len) + { + cur_ += len; + } + + void reset() + { + cur_ = data_; + } + void zeroBuffer() + { + memset(data_, 0, sizeof(data_)); + } + + // for used by GDB + const char *debugString(); + void setCookie(void (*cookie)()) + { + cookie_ = cookie; + } + // for used by unit test + std::string toString() const + { + return std::string(data_, length()); + } + // StringPiece toStringPiece() const { return StringPiece(data_, length()); + // } + + private: + const char *end() const + { + return data_ + sizeof data_; + } + // Must be outline function for cookies. + static void cookieStart(); + static void cookieEnd(); + + void (*cookie_)(); + char data_[SIZE]; + char *cur_; +}; + +} // namespace detail + +class TRANTOR_EXPORT LogStream : NonCopyable +{ + using self = LogStream; + + public: + using Buffer = detail::FixedBuffer; + + self &operator<<(bool v) + { + append(v ? "1" : "0", 1); + return *this; + } + + self &operator<<(short); + self &operator<<(unsigned short); + self &operator<<(int); + self &operator<<(unsigned int); + self &operator<<(long); + self &operator<<(unsigned long); + self &operator<<(const long long &); + self &operator<<(const unsigned long long &); + + self &operator<<(const void *); + + self &operator<<(float &v) + { + *this << static_cast(v); + return *this; + } + self &operator<<(const double &); + self &operator<<(const long double &v); + + self &operator<<(char v) + { + append(&v, 1); + return *this; + } + + // self& operator<<(signed char); + // self& operator<<(unsigned char); + template + self &operator<<(const char (&buf)[N]) + { + assert(strnlen(buf, N) == N - 1); + append(buf, N - 1); + return *this; + } + + self &operator<<(char *str) + { + if (str) + { + append(str, strlen(str)); + } + else + { + append("(null)", 6); + } + return *this; + } + + self &operator<<(const char *str) + { + if (str) + { + append(str, strlen(str)); + } + else + { + append("(null)", 6); + } + return *this; + } + + self &operator<<(const unsigned char *str) + { + return operator<<(reinterpret_cast(str)); + } + + self &operator<<(const std::string &v) + { + append(v.c_str(), v.size()); + return *this; + } + + void append(const char *data, size_t len) + { + if (exBuffer_.empty()) + { + if (!buffer_.append(data, len)) + { + exBuffer_.append(buffer_.data(), buffer_.length()); + exBuffer_.append(data, len); + } + } + else + { + exBuffer_.append(data, len); + } + } + // const Buffer& buffer() const { return buffer_; } + const char *bufferData() const + { + if (!exBuffer_.empty()) + { + return exBuffer_.data(); + } + return buffer_.data(); + } + + size_t bufferLength() const + { + if (!exBuffer_.empty()) + { + return exBuffer_.length(); + } + return buffer_.length(); + } + void resetBuffer() + { + buffer_.reset(); + exBuffer_.clear(); + } + + private: + template + void formatInteger(T); + + Buffer buffer_; + std::string exBuffer_; +}; + +class TRANTOR_EXPORT Fmt // : boost::noncopyable +{ + public: + template + Fmt(const char *fmt, T val); + + const char *data() const + { + return buf_; + } + int length() const + { + return length_; + } + + private: + char buf_[48]; + int length_; +}; + +inline LogStream &operator<<(LogStream &s, const Fmt &fmt) +{ + s.append(fmt.data(), fmt.length()); + return s; +} + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/Logger.cc b/modules/http_trantor/trantor/utils/Logger.cc new file mode 100644 index 0000000..b407ef3 --- /dev/null +++ b/modules/http_trantor/trantor/utils/Logger.cc @@ -0,0 +1,212 @@ +/** + * + * Logger.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include +#include +#ifndef _WIN32 +#include +#include +#else +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + +namespace trantor +{ +// helper class for known string length at compile time +class T +{ + public: + T(const char *str, unsigned len) : str_(str), len_(len) + { + assert(strlen(str) == len_); + } + + const char *str_; + const unsigned len_; +}; + +const char *strerror_tl(int savedErrno) +{ +#ifndef _MSC_VER + return strerror(savedErrno); +#else + static thread_local char errMsg[64]; + (void)strerror_s(errMsg, savedErrno); + return errMsg; +#endif +} + +inline LogStream &operator<<(LogStream &s, T v) +{ + s.append(v.str_, v.len_); + return s; +} + +inline LogStream &operator<<(LogStream &s, const Logger::SourceFile &v) +{ + s.append(v.data_, v.size_); + return s; +} +} // namespace trantor +using namespace trantor; + +static thread_local uint64_t lastSecond_{0}; +static thread_local char lastTimeString_[32] = {0}; +#ifdef __linux__ +static thread_local pid_t threadId_{0}; +#else +static thread_local uint64_t threadId_{0}; +#endif +// static thread_local LogStream logStream_; + +void Logger::formatTime() +{ + uint64_t now = static_cast(date_.secondsSinceEpoch()); + uint64_t microSec = + static_cast(date_.microSecondsSinceEpoch() - + date_.roundSecond().microSecondsSinceEpoch()); + if (now != lastSecond_) + { + lastSecond_ = now; +#ifndef _MSC_VER + strncpy(lastTimeString_, + date_.toFormattedString(false).c_str(), + sizeof(lastTimeString_) - 1); +#else + strncpy_s( + lastTimeString_, + date_.toFormattedString(false).c_str(), + sizeof(lastTimeString_) - 1); +#endif + } + logStream_ << T(lastTimeString_, 17); + char tmp[32]; + snprintf(tmp, + sizeof(tmp), + ".%06llu UTC ", + static_cast(microSec)); + logStream_ << T(tmp, 12); +#ifdef __linux__ + if (threadId_ == 0) + threadId_ = static_cast(::syscall(SYS_gettid)); +#elif defined __FreeBSD__ + if (threadId_ == 0) + { + threadId_ = pthread_getthreadid_np(); + } +#elif defined __OpenBSD__ + if (threadId_ == 0) + { + threadId_ = getthrid(); + } +#elif defined _WIN32 + if (threadId_ == 0) + { + std::stringstream ss; + ss << std::this_thread::get_id(); + threadId_ = std::stoull(ss.str()); + } +#else + if (threadId_ == 0) + { + pthread_threadid_np(NULL, &threadId_); + } +#endif + logStream_ << threadId_; +} +static const char *logLevelStr[Logger::LogLevel::kNumberOfLogLevels] = { + " TRACE ", + " DEBUG ", + " INFO ", + " WARN ", + " ERROR ", + " FATAL ", +}; +Logger::Logger(SourceFile file, int line) + : sourceFile_(file), fileLine_(line), level_(kInfo) +{ + formatTime(); + logStream_ << T(logLevelStr[level_], 7); +} +Logger::Logger(SourceFile file, int line, LogLevel level) + : sourceFile_(file), fileLine_(line), level_(level) +{ + formatTime(); + logStream_ << T(logLevelStr[level_], 7); +} +Logger::Logger(SourceFile file, int line, LogLevel level, const char *func) + : sourceFile_(file), fileLine_(line), level_(level) +{ + formatTime(); + logStream_ << T(logLevelStr[level_], 7) << "[" << func << "] "; +} +Logger::Logger(SourceFile file, int line, bool) + : sourceFile_(file), fileLine_(line), level_(kFatal) +{ + formatTime(); + logStream_ << T(logLevelStr[level_], 7); + if (errno != 0) + { + logStream_ << strerror_tl(errno) << " (errno=" << errno << ") "; + } +} +RawLogger::~RawLogger() +{ + if (index_ < 0) + { + auto &oFunc = Logger::outputFunc_(); + if (!oFunc) + return; + oFunc(logStream_.bufferData(), logStream_.bufferLength()); + } + else + { + auto &oFunc = Logger::outputFunc_(index_); + if (!oFunc) + return; + oFunc(logStream_.bufferData(), logStream_.bufferLength()); + } +} +Logger::~Logger() +{ + logStream_ << T(" - ", 3) << sourceFile_ << ':' << fileLine_ << '\n'; + if (index_ < 0) + { + auto &oFunc = Logger::outputFunc_(); + if (!oFunc) + return; + oFunc(logStream_.bufferData(), logStream_.bufferLength()); + if (level_ >= kError) + Logger::flushFunc_()(); + } + else + { + auto &oFunc = Logger::outputFunc_(index_); + if (!oFunc) + return; + oFunc(logStream_.bufferData(), logStream_.bufferLength()); + if (level_ >= kError) + Logger::flushFunc_(index_)(); + } + + // logStream_.resetBuffer(); +} +LogStream &Logger::stream() +{ + return logStream_; +} diff --git a/modules/http_trantor/trantor/utils/Logger.h b/modules/http_trantor/trantor/utils/Logger.h new file mode 100644 index 0000000..ddb6100 --- /dev/null +++ b/modules/http_trantor/trantor/utils/Logger.h @@ -0,0 +1,383 @@ +/** + * + * @file Logger.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class implements log functions. + * + */ +class TRANTOR_EXPORT Logger : public NonCopyable +{ + public: + enum LogLevel + { + kTrace = 0, + kDebug, + kInfo, + kWarn, + kError, + kFatal, + kNumberOfLogLevels + }; + + /** + * @brief Calculate of basename of source files in compile time. + * + */ + class SourceFile + { + public: + template + inline SourceFile(const char (&arr)[N]) : data_(arr), size_(N - 1) + { + // std::cout<(data_ - arr); + } + } + + explicit SourceFile(const char *filename) : data_(filename) + { + const char *slash = strrchr(filename, '/'); + if (slash) + { + data_ = slash + 1; + } + size_ = static_cast(strlen(data_)); + } + + const char *data_; + int size_; + }; + Logger(SourceFile file, int line); + Logger(SourceFile file, int line, LogLevel level); + Logger(SourceFile file, int line, bool isSysErr); + Logger(SourceFile file, int line, LogLevel level, const char *func); + ~Logger(); + Logger &setIndex(int index) + { + index_ = index; + return *this; + } + LogStream &stream(); + + /** + * @brief Set the output function. + * + * @param outputFunc The function to output a log message. + * @param flushFunc The function to flush. + * @note Logs are output to the standard output by default. + */ + static void setOutputFunction( + std::function outputFunc, + std::function flushFunc, + int index = -1) + { + if (index < 0) + { + outputFunc_() = outputFunc; + flushFunc_() = flushFunc; + } + else + { + outputFunc_(index) = outputFunc; + flushFunc_(index) = flushFunc; + } + } + + /** + * @brief Set the log level. Logs below the level are not printed. + * + * @param level + */ + static void setLogLevel(LogLevel level) + { + logLevel_() = level; + } + + /** + * @brief Get the current log level. + * + * @return LogLevel + */ + static LogLevel logLevel() + { + return logLevel_(); + } + + protected: + static void defaultOutputFunction(const char *msg, const uint64_t len) + { + fwrite(msg, 1, len, stdout); + } + static void defaultFlushFunction() + { + fflush(stdout); + } + void formatTime(); + static LogLevel &logLevel_() + { +#ifdef RELEASE + static LogLevel logLevel = LogLevel::kInfo; +#else + static LogLevel logLevel = LogLevel::kDebug; +#endif + return logLevel; + } + static std::function + &outputFunc_() + { + static std::function + outputFunc = Logger::defaultOutputFunction; + return outputFunc; + } + static std::function &flushFunc_() + { + static std::function flushFunc = Logger::defaultFlushFunction; + return flushFunc; + } + static std::function + &outputFunc_(size_t index) + { + static std::vector< + std::function> + outputFuncs; + if (index < outputFuncs.size()) + { + return outputFuncs[index]; + } + while (index >= outputFuncs.size()) + { + outputFuncs.emplace_back(outputFunc_()); + } + return outputFuncs[index]; + } + static std::function &flushFunc_(size_t index) + { + static std::vector> flushFuncs; + if (index < flushFuncs.size()) + { + return flushFuncs[index]; + } + while (index >= flushFuncs.size()) + { + flushFuncs.emplace_back(flushFunc_()); + } + return flushFuncs[index]; + } + friend class RawLogger; + LogStream logStream_; + Date date_{Date::now()}; + SourceFile sourceFile_; + int fileLine_; + LogLevel level_; + int index_{-1}; +}; +class TRANTOR_EXPORT RawLogger : public NonCopyable +{ + public: + ~RawLogger(); + RawLogger &setIndex(int index) + { + index_ = index; + return *this; + } + LogStream &stream() + { + return logStream_; + } + + private: + LogStream logStream_; + int index_{-1}; +}; +#ifdef NDEBUG +#define LOG_TRACE \ + if (0) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#else +#define LOG_TRACE \ + if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define LOG_TRACE_TO(index) \ + if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .setIndex(index) \ + .stream() + +#endif + +#define LOG_DEBUG \ + if (trantor::Logger::logLevel() <= trantor::Logger::kDebug) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kDebug, __func__) \ + .stream() +#define LOG_DEBUG_TO(index) \ + if (trantor::Logger::logLevel() <= trantor::Logger::kDebug) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kDebug, __func__) \ + .setIndex(index) \ + .stream() +#define LOG_INFO \ + if (trantor::Logger::logLevel() <= trantor::Logger::kInfo) \ + trantor::Logger(__FILE__, __LINE__).stream() +#define LOG_INFO_TO(index) \ + if (trantor::Logger::logLevel() <= trantor::Logger::kInfo) \ + trantor::Logger(__FILE__, __LINE__).setIndex(index).stream() +#define LOG_WARN \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kWarn).stream() +#define LOG_WARN_TO(index) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kWarn) \ + .setIndex(index) \ + .stream() +#define LOG_ERROR \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kError).stream() +#define LOG_ERROR_TO(index) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kError) \ + .setIndex(index) \ + .stream() +#define LOG_FATAL \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kFatal).stream() +#define LOG_FATAL_TO(index) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kFatal) \ + .setIndex(index) \ + .stream() +#define LOG_SYSERR trantor::Logger(__FILE__, __LINE__, true).stream() +#define LOG_SYSERR_TO(index) \ + trantor::Logger(__FILE__, __LINE__, true).setIndex(index).stream() + +#define LOG_RAW trantor::RawLogger().stream() +#define LOG_RAW_TO(index) trantor::RawLogger().setIndex(index).stream() + +#define LOG_TRACE_IF(cond) \ + if ((trantor::Logger::logLevel() <= trantor::Logger::kTrace) && (cond)) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define LOG_DEBUG_IF(cond) \ + if ((Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) && (cond)) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \ + .stream() +#define LOG_INFO_IF(cond) \ + if ((Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) && (cond)) \ + Tensor::Logger(__FILE__, __LINE__).stream() +#define LOG_WARN_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream() +#define LOG_ERROR_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream() +#define LOG_FATAL_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream() + +#ifdef NDEBUG +#define DLOG_TRACE \ + if (0) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define DLOG_DEBUG \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \ + .stream() +#define DLOG_INFO \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__).stream() +#define DLOG_WARN \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream() +#define DLOG_ERROR \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream() +#define DLOG_FATAL \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream() + +#define DLOG_TRACE_IF(cond) \ + if (0) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define DLOG_DEBUG_IF(cond) \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \ + .stream() +#define DLOG_INFO_IF(cond) \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__).stream() +#define DLOG_WARN_IF(cond) \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream() +#define DLOG_ERROR_IF(cond) \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream() +#define DLOG_FATAL_IF(cond) \ + if (0) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream() +#else +#define DLOG_TRACE \ + if (trantor::Logger::logLevel() <= trantor::Logger::kTrace) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define DLOG_DEBUG \ + if (Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \ + .stream() +#define DLOG_INFO \ + if (Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) \ + Tensor::Logger(__FILE__, __LINE__).stream() +#define DLOG_WARN \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream() +#define DLOG_ERROR \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream() +#define DLOG_FATAL \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream() + +#define DLOG_TRACE_IF(cond) \ + if ((trantor::Logger::logLevel() <= trantor::Logger::kTrace) && (cond)) \ + trantor::Logger(__FILE__, __LINE__, trantor::Logger::kTrace, __func__) \ + .stream() +#define DLOG_DEBUG_IF(cond) \ + if ((Tensor::Logger::logLevel() <= Tensor::Logger::kDebug) && (cond)) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kDebug, __func__) \ + .stream() +#define DLOG_INFO_IF(cond) \ + if ((Tensor::Logger::logLevel() <= Tensor::Logger::kInfo) && (cond)) \ + Tensor::Logger(__FILE__, __LINE__).stream() +#define DLOG_WARN_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kWarn).stream() +#define DLOG_ERROR_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kError).stream() +#define DLOG_FATAL_IF(cond) \ + if (cond) \ + Tensor::Logger(__FILE__, __LINE__, Tensor::Logger::kFatal).stream() +#endif + +const char *strerror_tl(int savedErrno); +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/MsgBuffer.cc b/modules/http_trantor/trantor/utils/MsgBuffer.cc new file mode 100644 index 0000000..038f7ea --- /dev/null +++ b/modules/http_trantor/trantor/utils/MsgBuffer.cc @@ -0,0 +1,232 @@ +/** + * + * MsgBuffer.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include +#include +#ifndef _WIN32 +#include +#include +#else +#include +#include +#endif +#include +#include + +using namespace trantor; +namespace trantor +{ +static constexpr size_t kBufferOffset{8}; +} + +MsgBuffer::MsgBuffer(size_t len) + : head_(kBufferOffset), initCap_(len), buffer_(len + head_), tail_(head_) +{ +} + +void MsgBuffer::ensureWritableBytes(size_t len) +{ + if (writableBytes() >= len) + return; + if (head_ + writableBytes() >= + (len + kBufferOffset)) // move readable bytes + { + std::copy(begin() + head_, begin() + tail_, begin() + kBufferOffset); + tail_ = kBufferOffset + (tail_ - head_); + head_ = kBufferOffset; + return; + } + // create new buffer + size_t newLen; + if ((buffer_.size() * 2) > (kBufferOffset + readableBytes() + len)) + newLen = buffer_.size() * 2; + else + newLen = kBufferOffset + readableBytes() + len; + MsgBuffer newbuffer(newLen); + newbuffer.append(*this); + swap(newbuffer); +} +void MsgBuffer::swap(MsgBuffer &buf) noexcept +{ + buffer_.swap(buf.buffer_); + std::swap(head_, buf.head_); + std::swap(tail_, buf.tail_); + std::swap(initCap_, buf.initCap_); +} +void MsgBuffer::append(const MsgBuffer &buf) +{ + ensureWritableBytes(buf.readableBytes()); + memcpy(&buffer_[tail_], buf.peek(), buf.readableBytes()); + tail_ += buf.readableBytes(); +} +void MsgBuffer::append(const char *buf, size_t len) +{ + ensureWritableBytes(len); + memcpy(&buffer_[tail_], buf, len); + tail_ += len; +} +void MsgBuffer::appendInt16(const uint16_t s) +{ + uint16_t ss = htons(s); + append(static_cast((void *)&ss), 2); +} +void MsgBuffer::appendInt32(const uint32_t i) +{ + uint32_t ii = htonl(i); + append(static_cast((void *)&ii), 4); +} +void MsgBuffer::appendInt64(const uint64_t l) +{ + uint64_t ll = hton64(l); + append(static_cast((void *)&ll), 8); +} + +void MsgBuffer::addInFrontInt16(const uint16_t s) +{ + uint16_t ss = htons(s); + addInFront(static_cast((void *)&ss), 2); +} +void MsgBuffer::addInFrontInt32(const uint32_t i) +{ + uint32_t ii = htonl(i); + addInFront(static_cast((void *)&ii), 4); +} +void MsgBuffer::addInFrontInt64(const uint64_t l) +{ + uint64_t ll = hton64(l); + addInFront(static_cast((void *)&ll), 8); +} + +uint16_t MsgBuffer::peekInt16() const +{ + assert(readableBytes() >= 2); + uint16_t rs = *(static_cast((void *)peek())); + return ntohs(rs); +} +uint32_t MsgBuffer::peekInt32() const +{ + assert(readableBytes() >= 4); + uint32_t rl = *(static_cast((void *)peek())); + return ntohl(rl); +} +uint64_t MsgBuffer::peekInt64() const +{ + assert(readableBytes() >= 8); + uint64_t rll = *(static_cast((void *)peek())); + return ntoh64(rll); +} + +void MsgBuffer::retrieve(size_t len) +{ + if (len >= readableBytes()) + { + retrieveAll(); + return; + } + head_ += len; +} +void MsgBuffer::retrieveAll() +{ + if (buffer_.size() > (initCap_ * 2)) + { + buffer_.resize(initCap_); + } + tail_ = head_ = kBufferOffset; +} +ssize_t MsgBuffer::readFd(int fd, int *retErrno) +{ + char extBuffer[8192]; + struct iovec vec[2]; + size_t writable = writableBytes(); + vec[0].iov_base = begin() + tail_; + vec[0].iov_len = static_cast(writable); + vec[1].iov_base = extBuffer; + vec[1].iov_len = sizeof(extBuffer); + const int iovcnt = (writable < sizeof extBuffer) ? 2 : 1; + ssize_t n = ::readv(fd, vec, iovcnt); + if (n < 0) + { + *retErrno = errno; + } + else if (static_cast(n) <= writable) + { + tail_ += n; + } + else + { + tail_ = buffer_.size(); + append(extBuffer, n - writable); + } + return n; +} + +std::string MsgBuffer::read(size_t len) +{ + if (len > readableBytes()) + len = readableBytes(); + std::string ret(peek(), len); + retrieve(len); + return ret; +} +uint8_t MsgBuffer::readInt8() +{ + uint8_t ret = peekInt8(); + retrieve(1); + return ret; +} +uint16_t MsgBuffer::readInt16() +{ + uint16_t ret = peekInt16(); + retrieve(2); + return ret; +} +uint32_t MsgBuffer::readInt32() +{ + uint32_t ret = peekInt32(); + retrieve(4); + return ret; +} +uint64_t MsgBuffer::readInt64() +{ + uint64_t ret = peekInt64(); + retrieve(8); + return ret; +} + +void MsgBuffer::addInFront(const char *buf, size_t len) +{ + if (head_ >= len) + { + memcpy(begin() + head_ - len, buf, len); + head_ -= len; + return; + } + if (len <= writableBytes()) + { + std::copy(begin() + head_, begin() + tail_, begin() + head_ + len); + memcpy(begin() + head_, buf, len); + tail_ += len; + return; + } + size_t newLen; + if (len + readableBytes() < initCap_) + newLen = initCap_; + else + newLen = len + readableBytes(); + MsgBuffer newBuf(newLen); + newBuf.append(buf, len); + newBuf.append(*this); + swap(newBuf); +} diff --git a/modules/http_trantor/trantor/utils/MsgBuffer.h b/modules/http_trantor/trantor/utils/MsgBuffer.h new file mode 100644 index 0000000..33c8633 --- /dev/null +++ b/modules/http_trantor/trantor/utils/MsgBuffer.h @@ -0,0 +1,376 @@ +/** + * + * @file MsgBuffer.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +using ssize_t = long long; +#endif + +namespace trantor +{ +static constexpr size_t kBufferDefaultLength{2048}; +static constexpr char CRLF[]{"\r\n"}; + +/** + * @brief This class represents a memory buffer used for sending and receiving + * data. + * + */ +class TRANTOR_EXPORT MsgBuffer +{ + public: + /** + * @brief Construct a new message buffer instance. + * + * @param len The initial size of the buffer. + */ + MsgBuffer(size_t len = kBufferDefaultLength); + + /** + * @brief Get the beginning of the buffer. + * + * @return const char* + */ + const char *peek() const + { + return begin() + head_; + } + + /** + * @brief Get the end of the buffer where new data can be written. + * + * @return const char* + */ + const char *beginWrite() const + { + return begin() + tail_; + } + char *beginWrite() + { + return begin() + tail_; + } + + /** + * @brief Get a byte value from the buffer. + * + * @return uint8_t + */ + uint8_t peekInt8() const + { + assert(readableBytes() >= 1); + return *(static_cast((void *)peek())); + } + + /** + * @brief Get a unsigned short value from the buffer. + * + * @return uint16_t + */ + uint16_t peekInt16() const; + + /** + * @brief Get a unsigned int value from the buffer. + * + * @return uint32_t + */ + uint32_t peekInt32() const; + + /** + * @brief Get a unsigned int64 value from the buffer. + * + * @return uint64_t + */ + uint64_t peekInt64() const; + + /** + * @brief Get and remove some bytes from the buffer. + * + * @param len + * @return std::string + */ + std::string read(size_t len); + + /** + * @brief Get the remove a byte value from the buffer. + * + * @return uint8_t + */ + uint8_t readInt8(); + + /** + * @brief Get and remove a unsigned short value from the buffer. + * + * @return uint16_t + */ + uint16_t readInt16(); + + /** + * @brief Get and remove a unsigned int value from the buffer. + * + * @return uint32_t + */ + uint32_t readInt32(); + + /** + * @brief Get and remove a unsigned int64 value from the buffer. + * + * @return uint64_t + */ + uint64_t readInt64(); + + /** + * @brief swap the buffer with another. + * + * @param buf + */ + void swap(MsgBuffer &buf) noexcept; + + /** + * @brief Return the size of the data in the buffer. + * + * @return size_t + */ + size_t readableBytes() const + { + return tail_ - head_; + } + + /** + * @brief Return the size of the empty part in the buffer + * + * @return size_t + */ + size_t writableBytes() const + { + return buffer_.size() - tail_; + } + + /** + * @brief Append new data to the buffer. + * + */ + void append(const MsgBuffer &buf); + template + void append(const char (&buf)[N]) + { + assert(strnlen(buf, N) == N - 1); + append(buf, N - 1); + } + void append(const char *buf, size_t len); + void append(const std::string &buf) + { + append(buf.c_str(), buf.length()); + } + + /** + * @brief Append a byte value to the end of the buffer. + * + * @param b + */ + void appendInt8(const uint8_t b) + { + append(static_cast((void *)&b), 1); + } + + /** + * @brief Append a unsigned short value to the end of the buffer. + * + * @param s + */ + void appendInt16(const uint16_t s); + + /** + * @brief Append a unsigned int value to the end of the buffer. + * + * @param i + */ + void appendInt32(const uint32_t i); + + /** + * @brief Appaend a unsigned int64 value to the end of the buffer. + * + * @param l + */ + void appendInt64(const uint64_t l); + + /** + * @brief Put new data to the beginning of the buffer. + * + * @param buf + * @param len + */ + void addInFront(const char *buf, size_t len); + + /** + * @brief Put a byte value to the beginning of the buffer. + * + * @param b + */ + void addInFrontInt8(const uint8_t b) + { + addInFront(static_cast((void *)&b), 1); + } + + /** + * @brief Put a unsigned short value to the beginning of the buffer. + * + * @param s + */ + void addInFrontInt16(const uint16_t s); + + /** + * @brief Put a unsigned int value to the beginning of the buffer. + * + * @param i + */ + void addInFrontInt32(const uint32_t i); + + /** + * @brief Put a unsigned int64 value to the beginning of the buffer. + * + * @param l + */ + void addInFrontInt64(const uint64_t l); + + /** + * @brief Remove all data in the buffer. + * + */ + void retrieveAll(); + + /** + * @brief Remove some bytes in the buffer. + * + * @param len + */ + void retrieve(size_t len); + + /** + * @brief Read data from a file descriptor and put it into the buffer.˝ + * + * @param fd The file descriptor. It is usually a socket. + * @param retErrno The error code when reading. + * @return ssize_t The number of bytes read from the file descriptor. -1 is + * returned when an error occurs. + */ + ssize_t readFd(int fd, int *retErrno); + + /** + * @brief Remove the data before a certain position from the buffer. + * + * @param end The position. + */ + void retrieveUntil(const char *end) + { + assert(peek() <= end); + assert(end <= beginWrite()); + retrieve(end - peek()); + } + + /** + * @brief Find the position of the buffer where the CRLF is found. + * + * @return const char* + */ + const char *findCRLF() const + { + const char *crlf = std::search(peek(), beginWrite(), CRLF, CRLF + 2); + return crlf == beginWrite() ? NULL : crlf; + } + + /** + * @brief Make sure the buffer has enough spaces to write data. + * + * @param len + */ + void ensureWritableBytes(size_t len); + + /** + * @brief Move the write pointer forward when the new data has been written + * to the buffer. + * + * @param len + */ + void hasWritten(size_t len) + { + assert(len <= writableBytes()); + tail_ += len; + } + + /** + * @brief Move the write pointer backward to remove data in the end of the + * buffer. + * + * @param offset + */ + void unwrite(size_t offset) + { + assert(readableBytes() >= offset); + tail_ -= offset; + } + + /** + * @brief Access a byte in the buffer. + * + * @param offset + * @return const char& + */ + const char &operator[](size_t offset) const + { + assert(readableBytes() >= offset); + return peek()[offset]; + } + char &operator[](size_t offset) + { + assert(readableBytes() >= offset); + return begin()[head_ + offset]; + } + + private: + size_t head_; + size_t initCap_; + std::vector buffer_; + size_t tail_; + const char *begin() const + { + return &buffer_[0]; + } + char *begin() + { + return &buffer_[0]; + } +}; + +inline void swap(MsgBuffer &one, MsgBuffer &two) noexcept +{ + one.swap(two); +} +} // namespace trantor + +namespace std +{ +template <> +inline void swap(trantor::MsgBuffer &one, trantor::MsgBuffer &two) noexcept +{ + one.swap(two); +} +} // namespace std diff --git a/modules/http_trantor/trantor/utils/NonCopyable.h b/modules/http_trantor/trantor/utils/NonCopyable.h new file mode 100644 index 0000000..5c0af62 --- /dev/null +++ b/modules/http_trantor/trantor/utils/NonCopyable.h @@ -0,0 +1,41 @@ +/** + * + * @file NonCopyable.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include + +namespace trantor +{ +/** + * @brief This class represents a non-copyable object. + * + */ +class TRANTOR_EXPORT NonCopyable +{ + protected: + NonCopyable() + { + } + ~NonCopyable() + { + } + NonCopyable(const NonCopyable &) = delete; + NonCopyable &operator=(const NonCopyable &) = delete; + // some uncopyable classes maybe support move constructor.... + NonCopyable(NonCopyable &&) noexcept(true) = default; + NonCopyable &operator=(NonCopyable &&) noexcept(true) = default; +}; + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/ObjectPool.h b/modules/http_trantor/trantor/utils/ObjectPool.h new file mode 100644 index 0000000..145a355 --- /dev/null +++ b/modules/http_trantor/trantor/utils/ObjectPool.h @@ -0,0 +1,76 @@ +/** + * + * @file ObjectPool.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace trantor +{ +/** + * @brief This class template represents a object pool. + * + * @tparam T + */ +template +class ObjectPool : public NonCopyable, + public std::enable_shared_from_this> +{ + public: + std::shared_ptr getObject() + { + static_assert(!std::is_pointer::value, + "The parameter type of the ObjectPool template can't be " + "pointer type"); + T *p{nullptr}; + { + std::lock_guard lock(mtx_); + if (!objs_.empty()) + { + p = objs_.back(); + objs_.pop_back(); + } + } + + if (p == nullptr) + { + p = new T; + } + + assert(p); + std::weak_ptr> weakPtr = this->shared_from_this(); + auto obj = std::shared_ptr(p, [weakPtr](T *ptr) { + auto self = weakPtr.lock(); + if (self) + { + std::lock_guard lock(self->mtx_); + self->objs_.push_back(ptr); + } + else + { + delete ptr; + } + }); + return obj; + } + + private: + std::vector objs_; + std::mutex mtx_; +}; +} // namespace trantor \ No newline at end of file diff --git a/modules/http_trantor/trantor/utils/SerialTaskQueue.cc b/modules/http_trantor/trantor/utils/SerialTaskQueue.cc new file mode 100644 index 0000000..4b989cc --- /dev/null +++ b/modules/http_trantor/trantor/utils/SerialTaskQueue.cc @@ -0,0 +1,56 @@ +/** + * + * SerialTaskQueue.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include +#ifdef __linux__ +#include +#endif +namespace trantor +{ +SerialTaskQueue::SerialTaskQueue(const std::string &name) + : queueName_(name.empty() ? "SerailTaskQueue" : name), + loopThread_(queueName_) +{ + loopThread_.run(); +} +void SerialTaskQueue::stop() +{ + stop_ = true; + loopThread_.getLoop()->quit(); + loopThread_.wait(); +} +SerialTaskQueue::~SerialTaskQueue() +{ + if (!stop_) + stop(); + LOG_TRACE << "destruct SerialTaskQueue('" << queueName_ << "')"; +} +void SerialTaskQueue::runTaskInQueue(const std::function &task) +{ + loopThread_.getLoop()->runInLoop(task); +} +void SerialTaskQueue::runTaskInQueue(std::function &&task) +{ + loopThread_.getLoop()->runInLoop(std::move(task)); +} + +void SerialTaskQueue::waitAllTasksFinished() +{ + syncTaskInQueue([]() { + + }); +} + +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/SerialTaskQueue.h b/modules/http_trantor/trantor/utils/SerialTaskQueue.h new file mode 100644 index 0000000..04742bb --- /dev/null +++ b/modules/http_trantor/trantor/utils/SerialTaskQueue.h @@ -0,0 +1,100 @@ +/** + * + * SerialTaskQueue.h + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include "TaskQueue.h" +#include +#include +#include +#include +#include +#include +namespace trantor +{ +/** + * @brief This class represents a task queue in which all tasks are executed one + * by one. + * + */ +class TRANTOR_EXPORT SerialTaskQueue : public TaskQueue +{ + public: + /** + * @brief Run a task in the queue. + * + * @param task + */ + virtual void runTaskInQueue(const std::function &task); + virtual void runTaskInQueue(std::function &&task); + + /** + * @brief Get the name of the queue. + * + * @return std::string + */ + virtual std::string getName() const + { + return queueName_; + }; + + /** + * @brief Wait until all tasks in the queue are finished. + * + */ + void waitAllTasksFinished(); + + SerialTaskQueue() = delete; + + /** + * @brief Construct a new serail task queue instance. + * + * @param name + */ + explicit SerialTaskQueue(const std::string &name); + + virtual ~SerialTaskQueue(); + + /** + * @brief Check whether a task is running in the queue. + * + * @return true + * @return false + */ + bool isRuningTask() + { + return loopThread_.getLoop() + ? loopThread_.getLoop()->isCallingFunctions() + : false; + } + + /** + * @brief Get the number of tasks in the queue. + * + * @return size_t + */ + size_t getTaskCount(); + + /** + * @brief Stop the queue. + * + */ + void stop(); + + protected: + std::string queueName_; + EventLoopThread loopThread_; + bool stop_{false}; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/TaskQueue.h b/modules/http_trantor/trantor/utils/TaskQueue.h new file mode 100644 index 0000000..fb656d5 --- /dev/null +++ b/modules/http_trantor/trantor/utils/TaskQueue.h @@ -0,0 +1,58 @@ +/** + * + * @file TaskQueue.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include "NonCopyable.h" +#include +#include +#include +namespace trantor +{ +/** + * @brief This class is a pure virtual class that can be implemented as a + * SerialTaskQueue or a ConcurrentTaskQueue. + * + */ +class TaskQueue : public NonCopyable +{ + public: + virtual void runTaskInQueue(const std::function &task) = 0; + virtual void runTaskInQueue(std::function &&task) = 0; + virtual std::string getName() const + { + return ""; + }; + + /** + * @brief Run a task in the queue sychronously. This means that the task is + * executed before the method returns. + * + * @param task + */ + void syncTaskInQueue(const std::function &task) + { + std::promise prom; + std::future fut = prom.get_future(); + runTaskInQueue([&]() { + task(); + prom.set_value(1); + }); + fut.get(); + }; + virtual ~TaskQueue() + { + } +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/TimingWheel.cc b/modules/http_trantor/trantor/utils/TimingWheel.cc new file mode 100644 index 0000000..3ee3ec7 --- /dev/null +++ b/modules/http_trantor/trantor/utils/TimingWheel.cc @@ -0,0 +1,126 @@ +/** + * + * TimingWheel.cc + * An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include + +using namespace trantor; + +TimingWheel::TimingWheel(trantor::EventLoop *loop, + size_t maxTimeout, + float ticksInterval, + size_t bucketsNumPerWheel) + : loop_(loop), + ticksInterval_(ticksInterval), + bucketsNumPerWheel_(bucketsNumPerWheel) +{ + assert(maxTimeout > 1); + assert(ticksInterval > 0); + assert(bucketsNumPerWheel_ > 1); + size_t maxTickNum = static_cast(maxTimeout / ticksInterval); + auto ticksNum = bucketsNumPerWheel; + wheelsNum_ = 1; + while (maxTickNum > ticksNum) + { + ++wheelsNum_; + ticksNum *= bucketsNumPerWheel_; + } + wheels_.resize(wheelsNum_); + for (size_t i = 0; i < wheelsNum_; ++i) + { + wheels_[i].resize(bucketsNumPerWheel_); + } + timerId_ = loop_->runEvery(ticksInterval_, [this]() { + ++ticksCounter_; + size_t t = ticksCounter_; + size_t pow = 1; + for (size_t i = 0; i < wheelsNum_; ++i) + { + if ((t % pow) == 0) + { + EntryBucket tmp; + { + // use tmp val to make this critical area as short as + // possible. + wheels_[i].front().swap(tmp); + wheels_[i].pop_front(); + wheels_[i].push_back(EntryBucket()); + } + } + pow = pow * bucketsNumPerWheel_; + } + }); +} + +TimingWheel::~TimingWheel() +{ + loop_->assertInLoopThread(); + loop_->invalidateTimer(timerId_); + for (auto iter = wheels_.rbegin(); iter != wheels_.rend(); ++iter) + { + iter->clear(); + } + LOG_TRACE << "TimingWheel destruct!"; +} + +void TimingWheel::insertEntry(size_t delay, EntryPtr entryPtr) +{ + if (delay <= 0) + return; + if (!entryPtr) + return; + if (loop_->isInLoopThread()) + { + insertEntryInloop(delay, entryPtr); + } + else + { + loop_->runInLoop( + [this, delay, entryPtr]() { insertEntryInloop(delay, entryPtr); }); + } +} + +void TimingWheel::insertEntryInloop(size_t delay, EntryPtr entryPtr) +{ + loop_->assertInLoopThread(); + + delay = static_cast(delay / ticksInterval_ + 1); + size_t t = ticksCounter_; + for (size_t i = 0; i < wheelsNum_; ++i) + { + if (delay <= bucketsNumPerWheel_) + { + wheels_[i][delay - 1].insert(entryPtr); + break; + } + if (i < (wheelsNum_ - 1)) + { + entryPtr = std::make_shared( + [this, delay, i, t, entryPtr]() { + if (delay > 0) + { + wheels_[i][(delay + (t % bucketsNumPerWheel_) - 1) % + bucketsNumPerWheel_] + .insert(entryPtr); + } + }); + } + else + { + // delay is too long to put entry at valid position in wheels; + wheels_[i][bucketsNumPerWheel_ - 1].insert(entryPtr); + } + delay = (delay + (t % bucketsNumPerWheel_) - 1) / bucketsNumPerWheel_; + t = t / bucketsNumPerWheel_; + } +} \ No newline at end of file diff --git a/modules/http_trantor/trantor/utils/TimingWheel.h b/modules/http_trantor/trantor/utils/TimingWheel.h new file mode 100644 index 0000000..55f7fe0 --- /dev/null +++ b/modules/http_trantor/trantor/utils/TimingWheel.h @@ -0,0 +1,104 @@ +/** + * + * @file TimingWheel.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMING_BUCKET_NUM_PER_WHEEL 100 +#define TIMING_TICK_INTERVAL 1.0 + +namespace trantor +{ +using EntryPtr = std::shared_ptr; + +using EntryBucket = std::unordered_set; +using BucketQueue = std::deque; + +/** + * @brief This class implements a timer strategy with high performance and low + * accuracy. This is usually used internally. + * + */ +class TRANTOR_EXPORT TimingWheel +{ + public: + class CallbackEntry + { + public: + CallbackEntry(std::function cb) : cb_(std::move(cb)) + { + } + ~CallbackEntry() + { + cb_(); + } + + private: + std::function cb_; + }; + + /** + * @brief Construct a new timing wheel instance. + * + * @param loop The event loop in which the timing wheel runs. + * @param maxTimeout The maximum timeout of the timing wheel. + * @param ticksInterval The internal timer tick interval. It affects the + * accuracy of the timing wheel. + * @param bucketsNumPerWheel The number of buckets per wheel. + * @note The max delay of the timing wheel is about + * ticksInterval*(bucketsNumPerWheel^wheelsNum) seconds. + * @example Four wheels with 200 buckets per wheel means the timing wheel + * can work with a timeout up to 200^4 seconds, about 50 years; + */ + TimingWheel(trantor::EventLoop *loop, + size_t maxTimeout, + float ticksInterval = TIMING_TICK_INTERVAL, + size_t bucketsNumPerWheel = TIMING_BUCKET_NUM_PER_WHEEL); + + void insertEntry(size_t delay, EntryPtr entryPtr); + + void insertEntryInloop(size_t delay, EntryPtr entryPtr); + + EventLoop *getLoop() + { + return loop_; + } + + ~TimingWheel(); + + private: + std::vector wheels_; + + std::atomic ticksCounter_{0}; + + trantor::TimerId timerId_; + trantor::EventLoop *loop_; + + float ticksInterval_; + size_t wheelsNum_; + size_t bucketsNumPerWheel_; +}; +} // namespace trantor diff --git a/modules/http_trantor/trantor/utils/WindowsSupport.cc b/modules/http_trantor/trantor/utils/WindowsSupport.cc new file mode 100644 index 0000000..78d6810 --- /dev/null +++ b/modules/http_trantor/trantor/utils/WindowsSupport.cc @@ -0,0 +1,55 @@ +/** + * + * WindowsSupport.cc + * An Tao + * + * Implementation of Windows support functions. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#include +#include + +// from polipo +int win32_read_socket(int fd, void *buf, int n) +{ + int rc = recv(fd, reinterpret_cast(buf), n, 0); + if (rc == SOCKET_ERROR) + { + _set_errno(WSAGetLastError()); + } + return rc; +} + +int readv(int fd, const struct iovec *vector, int count) +{ + int ret = 0; /* Return value */ + int i; + for (i = 0; i < count; i++) + { + int n = vector[i].iov_len; + int rc = win32_read_socket(fd, vector[i].iov_base, n); + if (rc == n) + { + ret += rc; + } + else + { + if (rc < 0) + { + ret = (ret == 0 ? rc : ret); + } + else + { + ret += rc; + } + break; + } + } + return ret; +} diff --git a/modules/http_trantor/trantor/utils/WindowsSupport.h b/modules/http_trantor/trantor/utils/WindowsSupport.h new file mode 100644 index 0000000..9081df8 --- /dev/null +++ b/modules/http_trantor/trantor/utils/WindowsSupport.h @@ -0,0 +1,26 @@ +/** + * + * @file WindowsSupport.h + * @author An Tao + * + * Public header file in trantor lib. + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a BSD-style license + * that can be found in the License file. + * + * + */ + +#pragma once + +#include +#include + +struct iovec +{ + void *iov_base; /* Starting address */ + int iov_len; /* Number of bytes */ +}; + +TRANTOR_EXPORT int readv(int fd, const struct iovec *vector, int count);