Merge branch 'v1.3.0' into refactor_object_wrapper

This commit is contained in:
lganzzzo 2021-10-14 01:18:50 +03:00
commit bc1806fa5b
118 changed files with 3398 additions and 1170 deletions

View File

@ -21,6 +21,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(OATPP_INSTALL "Create installation target for oat++" ON)
option(OATPP_BUILD_TESTS "Create test target for oat++" ON)
option(OATPP_LINK_ATOMIC "Link atomic library for other platform than MSVC|MINGW|APPLE|FreeBSD" ON)
option(OATPP_MSVC_LINK_STATIC_RUNTIME "MSVC: Link with static runtime (/MT and /MTd)." OFF)
###################################################################################################
## COMPILATION CONFIG #############################################################################
@ -113,6 +114,9 @@ message("oatpp version: '${OATPP_THIS_MODULE_VERSION}'")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
include(cmake/msvc-runtime.cmake)
configure_msvc_runtime()
add_subdirectory(src)
if(OATPP_BUILD_TESTS)

188
changelog/1.3.0.md Normal file
View File

@ -0,0 +1,188 @@
# Oat++ 1.3.0
Previous release - [1.2.5](1.2.5.md)
Feel free to ask questions - [Chat on Gitter!](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [The New oatpp::String](#the-new-oatppstring)
- [ConnectionPool::get() Timeout](#connectionpoolget-timeout)
- [JSON Serializer Escape Flags](#json-serializer-escape-flags)
- [ConnectionMonitor](#connectionmonitor)
- [Response::getBody()](#responsegetbody)
- [data::stream::FIFOStream](#datastreamfifostream)
- [data::stream::BufferedInputStream](#datastreambufferedinputstream)
## The New oatpp::String
Now it's much easier to use `oatpp::String` since `oatpp::String` is now wrapper over `std::string`
```cpp
{
std::string s1 = Hello;
oatpp::String s2 = s1;
}
{
oatpp::String s1 = "Hello";
std::string s2 = *s1; // *s1 returns a refernce to the internal std::string object
}
{
oatpp::String s1 = "Hello";
std::string s2 = s1; // s1 is used a l-value with a typecast operator
}
{
oatpp::String s1 = "Hello";
bool b = s1 == "Hello"; // compare s1 with const char*
assert(b);
}
{
oatpp::String s1 = "Hello";
std::stringg s2 = "Hello";
bool b = s1 == s2; // compare s1 with std::string
assert(b);
}
{
oatpp::String s1 = "Hello";
std::string s2 = "World";
oatpp::String s3 = s1 + " " + s2; // concat oatpp::String with const char* and std::string directly
OATPP_LOGD("TEST", "str='%s'", s3->c_str()); // prints 'Hello World'
}
```
## ConnectionPool::get() Timeout
[#408](https://github.com/oatpp/oatpp/issues/408)
```cpp
{
auto connectionProvider = oatpp::network::tcp::client::ConnectionProvider::createShared({"httpbin.org", 80});
auto pool = oatpp::network::ClientConnectionPool::createShared(connectionProvider,
1,
std::chrono::seconds(10),
std::chrono::seconds(5));
OATPP_LOGD("TEST", "start")
auto c1 = pool->get(); //<--- this one will succeed
OATPP_LOGD("TEST", "c1=%llu", c1.get())
auto c2 = pool->get(); //<--- this one will fail in 5 sec. Since Max-Resources is 1, Pool timeout is 5 sec. And c1 is not freed.
OATPP_LOGD("TEST", "c2=%llu", c2.get())
}
```
Output:
```
D |2021-08-04 01:32:56 1628029976986744| TEST:start
D |2021-08-04 01:32:57 1628029977126940| TEST:c1=140716915331208
D |2021-08-04 01:33:02 1628029982128324| TEST:c2=0
```
## JSON Serializer Escape Flags
[#381](https://github.com/oatpp/oatpp/issues/381)
Now you can control if solidus is escaped or not.
### Default Behavior
```cpp
oatpp::parser::json::mapping::ObjectMapper mapper;
// mapper.getSerializer()->getConfig()->escapeFlags = 0; // by default FLAG_ESCAPE_SOLIDUS is ON
auto res = mapper.writeToString(oatpp::String("https://oatpp.io/"));
OATPP_LOGD("TEST", "res='%s'", res->c_str());
```
Output:
```
res='"https:\/\/oatpp.io\/"' # by default, solidus is escaped
```
### Clear Escape Flags
```cpp
oatpp::parser::json::mapping::ObjectMapper mapper;
mapper.getSerializer()->getConfig()->escapeFlags = 0;
auto res = mapper.writeToString(oatpp::String("https://oatpp.io/"));
OATPP_LOGD("TEST", "res='%s'", res->c_str());
```
Output:
```
res='"https://oatpp.io/"' # solidus isn't escaped
```
## ConnectionMonitor
`oatpp::network::monitor::ConnectionMonitor` is a middleman who's able to monitor provided connections and close those ones that not satisfy selected rules.
```cpp
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] {
auto connectionProvider = oatpp::network::tcp::server::ConnectionProvider::createShared({"0.0.0.0", 8000, oatpp::network::Address::IP_4});
auto monitor = std::make_shared<oatpp::network::monitor::ConnectionMonitor>(connectionProvider);
/* close all connections that stay opened for more than 120 seconds */
monitor->addMetricsChecker(
std::make_shared<oatpp::network::monitor::ConnectionMaxAgeChecker>(
std::chrono::seconds(120)
)
);
/* close all connections that have had no successful reads and writes for longer than 5 seconds */
monitor->addMetricsChecker(
std::make_shared<oatpp::network::monitor::ConnectionInactivityChecker>(
std::chrono::seconds(5),
std::chrono::seconds(5),
)
);
return monitor;
}());
```
**Note:** `ConnectionMonitor` also works with `ClientConnectionProvider` as well.
## Response::getBody()
`oatpp::web::protocol::http::outgoing::Response` has a new method `getBody()` to retreive the body of the response. This is handy for response interceptors.
## data::stream::FIFOStream
The new `FIFOStream` stream is a buffered
[`InputStream`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#inputstream) with an
[`WriteCallback`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#writecallback).
Check the corresponding documentation on how to use these interfaces.
Instead of using a static buffer like `BufferInputStream` it is build upon `data::buffer::FIFOBuffer` and is able to
dynamically grow when data is written to it that would surpass its capacity.
It is especially useful if you need to buffer data from a stream upfront or have multiple data sources that should be
buffered in a single stream.
However, it is not synchronized, so be careful when using `FIFOStream` in a multithreaded manner.
You need to implement your own locking.
## data::stream::BufferedInputStream
`FIFOStream` also introduced a new interface
[`BufferedInputStream`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#bufferedinputstream) which unifies
the bufferd-stream-interface all existing buffered streams (`InputStreamBufferedProxy`, `BufferInputStream`,
`FIFOStream`) to allow for generalisation.

36
cmake/msvc-runtime.cmake Normal file
View File

@ -0,0 +1,36 @@
macro(configure_msvc_runtime)
if(MSVC)
# Set compiler options.
set(variables
CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(OATPP_MSVC_LINK_STATIC_RUNTIME)
message(STATUS "MSVC: using statically-linked runtime (/MT and /MTd).")
foreach(variable ${variables})
if(${variable} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${variable} "${${variable}}")
endif()
endforeach()
else()
message(STATUS "MSVC: using dynamically-linked runtime (/MD and /MDd).")
foreach(variable ${variables})
if(${variable} MATCHES "/MT")
string(REGEX REPLACE "/MT" "/MD" ${variable} "${${variable}}")
endif()
endforeach()
endif()
foreach(variable ${variables})
set(${variable} "${${variable}}" CACHE STRING "MSVC_${variable}" FORCE)
endforeach()
endif()
endmacro(configure_msvc_runtime)

View File

@ -0,0 +1,27 @@
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/macro/codegen.hpp"
typedef oatpp::parser::Caret ParsingCaret;
typedef oatpp::parser::json::mapping::Serializer Serializer;
typedef oatpp::parser::json::mapping::Deserializer Deserializer;
#include OATPP_CODEGEN_BEGIN(DTO)
class EmptyDto : public oatpp::DTO {
DTO_INIT(EmptyDto, DTO)
};
class Test1 : public oatpp::DTO {
DTO_INIT(Test1, DTO)
DTO_FIELD(String, strF);
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
oatpp::String input(reinterpret_cast<const char*>(data), size, true);
oatpp::parser::json::mapping::ObjectMapper mapper;
try {
mapper.readFromString<oatpp::Object<Test1>>(input);
} catch(...) {}
return 0;
}

View File

@ -62,8 +62,6 @@ add_library(oatpp
oatpp/core/base/memory/ObjectPool.cpp
oatpp/core/base/memory/ObjectPool.hpp
oatpp/core/collection/FastQueue.hpp
oatpp/core/collection/LinkedList.hpp
oatpp/core/collection/ListMap.hpp
oatpp/core/concurrency/SpinLock.cpp
oatpp/core/concurrency/SpinLock.hpp
oatpp/core/concurrency/Thread.cpp
@ -109,6 +107,8 @@ add_library(oatpp
oatpp/core/data/stream/BufferStream.hpp
oatpp/core/data/stream/ChunkedBuffer.cpp
oatpp/core/data/stream/ChunkedBuffer.hpp
oatpp/core/data/stream/FIFOStream.cpp
oatpp/core/data/stream/FIFOStream.hpp
oatpp/core/data/stream/FileStream.cpp
oatpp/core/data/stream/FileStream.hpp
oatpp/core/data/stream/Stream.cpp
@ -138,6 +138,14 @@ add_library(oatpp
oatpp/encoding/Hex.hpp
oatpp/encoding/Unicode.cpp
oatpp/encoding/Unicode.hpp
oatpp/network/monitor/ConnectionInactivityChecker.cpp
oatpp/network/monitor/ConnectionInactivityChecker.hpp
oatpp/network/monitor/ConnectionMaxAgeChecker.cpp
oatpp/network/monitor/ConnectionMaxAgeChecker.hpp
oatpp/network/monitor/ConnectionMonitor.cpp
oatpp/network/monitor/ConnectionMonitor.hpp
oatpp/network/monitor/MetricsChecker.hpp
oatpp/network/monitor/StatCollector.hpp
oatpp/network/tcp/client/ConnectionProvider.cpp
oatpp/network/tcp/client/ConnectionProvider.hpp
oatpp/network/tcp/server/ConnectionProvider.cpp
@ -277,6 +285,9 @@ set_target_properties(oatpp PROPERTIES
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
)
if (MSVC)
target_compile_options(oatpp PRIVATE /permissive-)
endif()
set(CMAKE_THREAD_PREFER_PTHREAD ON)
@ -321,6 +332,9 @@ set_target_properties(oatpp-test PROPERTIES
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
)
if (MSVC)
target_compile_options(oatpp-test PRIVATE /permissive-)
endif()
target_link_libraries(oatpp-test PUBLIC oatpp)

View File

@ -36,6 +36,8 @@ void UnitTest::run(v_int32 times) {
v_counter objectsCount = base::Environment::getObjectsCount();
v_counter objectsCreated = base::Environment::getObjectsCreated();
before();
v_int64 ticks = base::Environment::getMicroTickCount();
@ -44,6 +46,8 @@ void UnitTest::run(v_int32 times) {
}
v_int64 millis = base::Environment::getMicroTickCount() - ticks;
after();
v_counter leakingObjects = base::Environment::getObjectsCount() - objectsCount;
v_counter objectsCreatedPerTest = base::Environment::getObjectsCreated() - objectsCreated;

View File

@ -68,6 +68,14 @@ public:
* Override this method. It should contain test logic.
*/
virtual void onRun() = 0;
/**
* Optionally override this method. It should contain logic run before all test iterations.
*/
virtual void before(){};
/**
* Optionally override this method. It should contain logic run after all test iterations.
*/
virtual void after(){};
/**
* Run this test repeatedly for specified number of times.
@ -82,14 +90,20 @@ public:
};
#define OATPP_RUN_TEST_0(TEST) \
oatpp::test::UnitTest::runTest<TEST>(1)
#define OATPP_RUN_TEST_1(TEST, N) \
oatpp::test::UnitTest::runTest<TEST>(N)
/**
* Convenience macro to run test. <br>
* Usage Example:<br>
* `OATPP_RUN_TEST(oatpp::test::web::FullTest);`
* Running the test 10 times:
* `OATPP_RUN_TEST(oatpp::test::web::FullTest, 10);`
*/
#define OATPP_RUN_TEST(TEST) \
oatpp::test::UnitTest::runTest<TEST>(1)
#define OATPP_RUN_TEST(...) \
OATPP_MACRO_EXPAND(OATPP_MACRO_MACRO_BINARY_SELECTOR(OATPP_RUN_TEST_, (__VA_ARGS__)) (__VA_ARGS__))
}}
#endif /* oatpp_test_UnitTest_hpp */

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -65,6 +66,13 @@ Action Action::createWaitListAction(CoroutineWaitList* waitList) {
return result;
}
Action Action::createWaitListActionWithTimeout(CoroutineWaitList* waitList, const std::chrono::steady_clock::time_point& timeout) {
Action result(TYPE_WAIT_LIST_WITH_TIMEOUT);
result.m_data.waitListWithTimeout.waitList = waitList;
result.m_data.waitListWithTimeout.timeoutTimeSinceEpochMS = std::chrono::duration_cast<std::chrono::milliseconds>(timeout.time_since_epoch()).count();
return result;
}
Action::Action()
: m_type(TYPE_NONE)
{}
@ -168,23 +176,16 @@ CoroutineStarter::CoroutineStarter(CoroutineStarter&& other)
}
CoroutineStarter::~CoroutineStarter() {
if(m_first != nullptr) {
auto curr = m_first;
while(curr != nullptr) {
AbstractCoroutine* next = nullptr;
if(curr->m_parentReturnAction.m_type == Action::TYPE_COROUTINE) {
next = curr->m_parentReturnAction.m_data.coroutine;
}
delete curr;
curr = next;
}
}
freeCoroutines();
}
/*
* Move assignment operator.
*/
CoroutineStarter& CoroutineStarter::operator=(CoroutineStarter&& other) {
if (this == std::addressof(other)) return *this;
freeCoroutines();
m_first = other.m_first;
m_last = other.m_last;
other.m_first = nullptr;
@ -216,6 +217,21 @@ CoroutineStarter& CoroutineStarter::next(CoroutineStarter&& starter) {
return *this;
}
void CoroutineStarter::freeCoroutines()
{
if (m_first != nullptr) {
auto curr = m_first;
while (curr != nullptr) {
AbstractCoroutine* next = nullptr;
if (curr->m_parentReturnAction.m_type == Action::TYPE_COROUTINE) {
next = curr->m_parentReturnAction.m_data.coroutine;
}
delete curr;
curr = next;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CoroutineHandle

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -113,6 +114,11 @@ public:
*/
static constexpr const v_int32 TYPE_WAIT_LIST = 9;
/**
* Indicate that coroutine should be put on a wait-list provided with a timeout.
*/
static constexpr const v_int32 TYPE_WAIT_LIST_WITH_TIMEOUT = 10;
public:
/**
@ -174,6 +180,11 @@ private:
IOEventType ioEventType;
};
struct WaitListWithTimeout {
CoroutineWaitList* waitList;
v_int64 timeoutTimeSinceEpochMS;
};
private:
union Data {
FunctionPtr fptr;
@ -182,6 +193,7 @@ private:
IOData ioData;
v_int64 timePointMicroseconds;
CoroutineWaitList* waitList;
WaitListWithTimeout waitListWithTimeout;
};
private:
mutable v_int32 m_type;
@ -243,6 +255,14 @@ public:
*/
static Action createWaitListAction(CoroutineWaitList* waitList);
/**
* Create TYPE_WAIT_LIST_WITH_TIMEOUT Action.
* @param waitList - wait-list to put coroutine on.
* @param timeout - latest time point at which the coroutine should be continued.
* @return - Action.
*/
static Action createWaitListActionWithTimeout(CoroutineWaitList* waitList, const std::chrono::steady_clock::time_point& timeout);
/**
* Constructor. Create start-coroutine Action.
* @param coroutine - pointer to &l:AbstractCoroutine;.
@ -341,6 +361,11 @@ class CoroutineStarter {
private:
AbstractCoroutine* m_first;
AbstractCoroutine* m_last;
private:
void freeCoroutines();
public:
/**
@ -597,8 +622,8 @@ public:
* @return - &id:oatpp::async::CoroutineStarter;.
*/
template<typename ...ConstructorArgs>
static CoroutineStarter start(ConstructorArgs... args) {
return new T(args...);
static CoroutineStarter start(ConstructorArgs&&... args) {
return new T(std::forward<ConstructorArgs>(args)...);
}
/**
@ -686,6 +711,9 @@ public:
* Move assignment operator.
*/
StarterForResult& operator=(StarterForResult&& other) {
if (this == std::addressof(other)) return *this;
delete m_coroutine;
m_coroutine = other.m_coroutine;
other.m_coroutine = nullptr;
return *this;
@ -759,7 +787,7 @@ public:
* @param ptr - pointer of the function to call.
* @return - Action.
*/
virtual Action call(const AbstractCoroutine::FunctionPtr& ptr) override {
Action call(const AbstractCoroutine::FunctionPtr& ptr) override {
Function f = static_cast<Function>(ptr);
return (static_cast<T*>(this)->*f)();
}

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,18 +26,63 @@
#include "CoroutineWaitList.hpp"
#include "./Processor.hpp"
#include <algorithm>
#include <set>
namespace oatpp { namespace async {
CoroutineWaitList::CoroutineWaitList(CoroutineWaitList&& other) {
{
std::lock_guard<oatpp::concurrency::SpinLock> lock{other.m_lock};
m_list = std::move(other.m_list);
}
{
std::lock_guard<oatpp::concurrency::SpinLock> lock{other.m_timeoutsLock};
m_coroutinesWithTimeout = std::move(other.m_coroutinesWithTimeout);
m_timeoutCheckingProcessors = std::move(other.m_timeoutCheckingProcessors);
for (const std::pair<Processor*, v_int64>& entry : m_timeoutCheckingProcessors) {
Processor* processor = entry.first;
processor->removeCoroutineWaitListWithTimeouts(std::addressof(other));
processor->addCoroutineWaitListWithTimeouts(this);
}
}
}
CoroutineWaitList::~CoroutineWaitList() {
notifyAll();
}
void CoroutineWaitList::checkCoroutinesForTimeouts() {
std::lock_guard<oatpp::concurrency::SpinLock> listLock{m_lock};
std::lock_guard<oatpp::concurrency::SpinLock> lock{m_timeoutsLock};
const auto currentTimeSinceEpochMS = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
const auto newEndIt = std::remove_if(std::begin(m_coroutinesWithTimeout), std::end(m_coroutinesWithTimeout), [&](const std::pair<CoroutineHandle*, v_int64>& entry) {
return currentTimeSinceEpochMS > entry.second;
});
for (CoroutineHandle* curr = m_list.first, *prev = nullptr; !m_list.empty() && m_list.last->_ref != curr; curr = curr->_ref) {
const bool removeFromWaitList = std::any_of(newEndIt, std::end(m_coroutinesWithTimeout), [=](const std::pair<CoroutineHandle*, v_int64>& entry) {
return entry.first == curr;
});
if (!removeFromWaitList) {
prev = curr;
continue;
}
m_list.cutEntry(curr, prev);
if (--m_timeoutCheckingProcessors[curr->_PP] <= 0) {
curr->_PP->removeCoroutineWaitListWithTimeouts(this);
m_timeoutCheckingProcessors.erase(curr->_PP);
}
curr->_PP->pushOneTask(curr);
}
m_coroutinesWithTimeout.erase(newEndIt, std::end(m_coroutinesWithTimeout));
}
void CoroutineWaitList::setListener(Listener* listener) {
m_listener = listener;
}
@ -51,6 +97,17 @@ void CoroutineWaitList::pushFront(CoroutineHandle* coroutine) {
}
}
void CoroutineWaitList::pushFront(CoroutineHandle* coroutine, v_int64 timeoutTimeSinceEpochMS) {
{
std::lock_guard<oatpp::concurrency::SpinLock> lock{m_timeoutsLock};
m_coroutinesWithTimeout.emplace_back(coroutine, timeoutTimeSinceEpochMS);
if (++m_timeoutCheckingProcessors[coroutine->_PP] == 1) {
coroutine->_PP->addCoroutineWaitListWithTimeouts(this);
}
}
pushFront(coroutine);
}
void CoroutineWaitList::pushBack(CoroutineHandle* coroutine) {
{
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
@ -61,21 +118,68 @@ void CoroutineWaitList::pushBack(CoroutineHandle* coroutine) {
}
}
void CoroutineWaitList::pushBack(CoroutineHandle* coroutine, v_int64 timeoutTimeSinceEpochMS) {
{
std::lock_guard<oatpp::concurrency::SpinLock> lock{m_timeoutsLock};
m_coroutinesWithTimeout.emplace_back(coroutine, timeoutTimeSinceEpochMS);
if (++m_timeoutCheckingProcessors[coroutine->_PP] == 1) {
coroutine->_PP->addCoroutineWaitListWithTimeouts(this);
}
}
pushBack(coroutine);
}
void CoroutineWaitList::notifyFirst() {
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
std::lock_guard<oatpp::concurrency::SpinLock> lock{m_lock};
if(m_list.first) {
auto coroutine = m_list.popFront();
coroutine->_PP->pushOneTask(coroutine);
removeFirstCoroutine();
}
}
void CoroutineWaitList::notifyAll() {
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
while (!m_list.empty()) {
auto curr = m_list.popFront();
curr->_PP->pushOneTask(curr);
}
while (!m_list.empty()) {
removeFirstCoroutine();
}
}
void CoroutineWaitList::removeFirstCoroutine() {
auto coroutine = m_list.popFront();
{
std::lock_guard<oatpp::concurrency::SpinLock> lock{m_timeoutsLock};
if (--m_timeoutCheckingProcessors[coroutine->_PP] <= 0) {
coroutine->_PP->removeCoroutineWaitListWithTimeouts(this);
m_timeoutCheckingProcessors.erase(coroutine->_PP);
}
}
coroutine->_PP->pushOneTask(coroutine);
}
CoroutineWaitList& CoroutineWaitList::operator=(CoroutineWaitList&& other) {
if (this == std::addressof(other)) return *this;
notifyAll();
{
std::lock_guard<oatpp::concurrency::SpinLock> otherLock{other.m_lock};
std::lock_guard<oatpp::concurrency::SpinLock> myLock{m_lock};
m_list = std::move(other.m_list);
}
{
std::lock_guard<oatpp::concurrency::SpinLock> otherLock{other.m_timeoutsLock};
std::lock_guard<oatpp::concurrency::SpinLock> myLock{m_timeoutsLock};
m_coroutinesWithTimeout = std::move(other.m_coroutinesWithTimeout);
m_timeoutCheckingProcessors = std::move(other.m_timeoutCheckingProcessors);
for (const std::pair<Processor*, v_int64>& entry : m_timeoutCheckingProcessors) {
Processor* processor = entry.first;
processor->removeCoroutineWaitListWithTimeouts(std::addressof(other));
processor->addCoroutineWaitListWithTimeouts(this);
}
}
return *this;
}
}}

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,7 +30,10 @@
#include "oatpp/core/collection/FastQueue.hpp"
#include "oatpp/core/concurrency/SpinLock.hpp"
#include <map>
#include <mutex>
#include <thread>
#include <utility>
namespace oatpp { namespace async {
@ -59,6 +63,16 @@ private:
oatpp::collection::FastQueue<CoroutineHandle> m_list;
oatpp::concurrency::SpinLock m_lock;
Listener* m_listener = nullptr;
std::map<Processor*, v_int64> m_timeoutCheckingProcessors;
std::vector<std::pair<CoroutineHandle*, v_int64>> m_coroutinesWithTimeout;
oatpp::concurrency::SpinLock m_timeoutsLock;
private:
void checkCoroutinesForTimeouts();
void removeFirstCoroutine();
protected:
/*
* Put coroutine on wait-list.
@ -67,12 +81,28 @@ protected:
*/
void pushFront(CoroutineHandle* coroutine);
/*
* Put coroutine on wait-list with timeout.
* This method should be called by Coroutine Processor only.
* @param coroutine
* @param timeoutTimeSinceEpochMS
*/
void pushFront(CoroutineHandle* coroutine, v_int64 timeoutTimeSinceEpochMS);
/*
* Put coroutine on wait-list.
* This method should be called by Coroutine Processor only.
* @param coroutine
*/
void pushBack(CoroutineHandle* coroutine);
/*
* Put coroutine on wait-list with timeout.
* This method should be called by Coroutine Processor only.
* @param coroutine
* @param timeoutTimeSinceEpochMS
*/
void pushBack(CoroutineHandle* coroutine, v_int64 timeoutTimeSinceEpochMS);
public:
/**
@ -118,12 +148,7 @@ public:
*/
void notifyAll();
CoroutineWaitList& operator=(CoroutineWaitList&& other) {
notifyAll();
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_lock);
m_list = std::move(other.m_list);
return *this;
}
CoroutineWaitList& operator=(CoroutineWaitList&& other);
};

View File

@ -54,12 +54,12 @@ void Executor::SubmissionProcessor::run() {
void Executor::SubmissionProcessor::pushTasks(oatpp::collection::FastQueue<CoroutineHandle>& tasks) {
(void)tasks;
std::runtime_error("[oatpp::async::Executor::SubmissionProcessor::pushTasks]: Error. This method does nothing.");
throw std::runtime_error("[oatpp::async::Executor::SubmissionProcessor::pushTasks]: Error. This method does nothing.");
}
void Executor::SubmissionProcessor::pushOneTask(CoroutineHandle* task) {
(void)task;
std::runtime_error("[oatpp::async::Executor::SubmissionProcessor::pushOneTask]: Error. This method does nothing.");
throw std::runtime_error("[oatpp::async::Executor::SubmissionProcessor::pushOneTask]: Error. This method does nothing.");
}
void Executor::SubmissionProcessor::stop() {
@ -94,7 +94,7 @@ Executor::Executor(v_int32 processorWorkersCount, v_int32 ioWorkersCount, v_int3
m_allWorkers.insert(m_allWorkers.end(), m_processorWorkers.begin(), m_processorWorkers.end());
std::vector<std::shared_ptr<worker::Worker>> ioWorkers;
ioWorkers.reserve(ioWorkersCount);
switch(ioWorkerType) {
case IO_WORKER_TYPE_NAIVE: {
@ -119,6 +119,7 @@ Executor::Executor(v_int32 processorWorkersCount, v_int32 ioWorkersCount, v_int3
linkWorkers(ioWorkers);
std::vector<std::shared_ptr<worker::Worker>> timerWorkers;
timerWorkers.reserve(timerWorkersCount);
for(v_int32 i = 0; i < timerWorkersCount; i++) {
timerWorkers.push_back(std::make_shared<worker::TimerWorker>());
}
@ -233,10 +234,10 @@ void Executor::stop() {
}
v_int32 Executor::getTasksCount() {
v_int32 result = 0;
for(auto procWorker : m_processorWorkers) {
for(const auto& procWorker : m_processorWorkers) {
result += procWorker->getProcessor().getTasksCount();
}

View File

@ -31,8 +31,6 @@
#include "oatpp/core/concurrency/SpinLock.hpp"
#include "oatpp/core/concurrency/Thread.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include <tuple>
#include <mutex>
#include <condition_variable>

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +29,38 @@
namespace oatpp { namespace async {
void Processor::checkCoroutinesForTimeouts() {
while (m_running) {
{
std::unique_lock<std::recursive_mutex> lock{m_coroutineWaitListsWithTimeoutsMutex};
while (m_coroutineWaitListsWithTimeouts.empty()) {
m_coroutineWaitListsWithTimeoutsCV.wait(lock);
if (!m_running) return;
}
const auto coroutineWaitListsWithTimeouts = m_coroutineWaitListsWithTimeouts;
for (CoroutineWaitList* waitList : coroutineWaitListsWithTimeouts) {
waitList->checkCoroutinesForTimeouts();
}
}
std::this_thread::sleep_for(std::chrono::milliseconds{100});
}
}
void Processor::addCoroutineWaitListWithTimeouts(CoroutineWaitList* waitList) {
{
std::lock_guard<std::recursive_mutex> lock{m_coroutineWaitListsWithTimeoutsMutex};
m_coroutineWaitListsWithTimeouts.insert(waitList);
}
m_coroutineWaitListsWithTimeoutsCV.notify_one();
}
void Processor::removeCoroutineWaitListWithTimeouts(CoroutineWaitList* waitList) {
std::lock_guard<std::recursive_mutex> lock{m_coroutineWaitListsWithTimeoutsMutex};
m_coroutineWaitListsWithTimeouts.erase(waitList);
}
void Processor::addWorker(const std::shared_ptr<worker::Worker>& worker) {
switch(worker->getType()) {
@ -97,6 +130,11 @@ void Processor::addCoroutine(CoroutineHandle* coroutine) {
action.m_data.waitList->pushBack(coroutine);
break;
case Action::TYPE_WAIT_LIST_WITH_TIMEOUT:
coroutine->_SCH_A = Action::createActionByType(Action::TYPE_NONE);
action.m_data.waitListWithTimeout.waitList->pushBack(coroutine, action.m_data.waitListWithTimeout.timeoutTimeSinceEpochMS);
break;
default:
m_queue.pushBack(coroutine);
@ -180,7 +218,7 @@ bool Processor::iterate(v_int32 numIterations) {
auto CP = m_queue.first;
if (CP == nullptr) {
goto end_loop;
break;
}
if (CP->finished()) {
m_queue.popFrontNoData();
@ -209,6 +247,12 @@ bool Processor::iterate(v_int32 numIterations) {
action.m_data.waitList->pushBack(CP);
break;
case Action::TYPE_WAIT_LIST_WITH_TIMEOUT:
CP->_SCH_A = Action::createActionByType(Action::TYPE_NONE);
m_queue.popFront();
action.m_data.waitListWithTimeout.waitList->pushBack(CP, action.m_data.waitListWithTimeout.timeoutTimeSinceEpochMS);
break;
default:
m_queue.round();
}
@ -217,8 +261,6 @@ bool Processor::iterate(v_int32 numIterations) {
}
end_loop:
popTasks();
std::lock_guard<oatpp::concurrency::SpinLock> lock(m_taskLock);
@ -232,6 +274,9 @@ void Processor::stop() {
m_running = false;
}
m_taskCondition.notify_one();
m_coroutineWaitListsWithTimeoutsCV.notify_one();
m_coroutineWaitListTimeoutChecker.join();
}
v_int32 Processor::getTasksCount() {

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,12 +27,14 @@
#define oatpp_async_Processor_hpp
#include "./Coroutine.hpp"
#include "./CoroutineWaitList.hpp"
#include "oatpp/core/collection/FastQueue.hpp"
#include <mutex>
#include <list>
#include <vector>
#include <condition_variable>
#include <list>
#include <mutex>
#include <set>
#include <vector>
namespace oatpp { namespace async {
@ -41,11 +44,12 @@ namespace oatpp { namespace async {
* Do not use bare processor to run coroutines. Use &id:oatpp::async::Executor; instead;.
*/
class Processor {
friend class CoroutineWaitList;
private:
class TaskSubmission {
public:
virtual ~TaskSubmission() {};
virtual ~TaskSubmission() = default;
virtual CoroutineHandle* createCoroutine(Processor* processor) = 0;
};
@ -108,8 +112,19 @@ private:
private:
bool m_running = true;
std::atomic<v_int32> m_tasksCounter;
std::atomic_bool m_running{true};
std::atomic<v_int32> m_tasksCounter{0};
private:
std::recursive_mutex m_coroutineWaitListsWithTimeoutsMutex;
std::condition_variable_any m_coroutineWaitListsWithTimeoutsCV;
std::set<CoroutineWaitList*> m_coroutineWaitListsWithTimeouts;
std::thread m_coroutineWaitListTimeoutChecker{&Processor::checkCoroutinesForTimeouts, this};
void checkCoroutinesForTimeouts();
void addCoroutineWaitListWithTimeouts(CoroutineWaitList* waitList);
void removeCoroutineWaitListWithTimeouts(CoroutineWaitList* waitList);
private:
@ -123,10 +138,7 @@ private:
public:
Processor()
: m_running(true)
, m_tasksCounter(0)
{}
Processor() = default;
/**
* Add dedicated co-worker to processor.

View File

@ -39,7 +39,11 @@ namespace oatpp { namespace async { namespace worker {
void IOEventWorker::initEventQueue() {
#if !defined __ANDROID_API__ || __ANDROID_API__ >= 21
m_eventQueueHandle = ::epoll_create1(0);
#else
m_eventQueueHandle = ::epoll_create(0);
#endif
if(m_eventQueueHandle == -1) {
OATPP_LOGE("[oatpp::async::worker::IOEventWorker::initEventQueue()]", "Error. Call to ::epoll_create1() failed. errno=%d", errno);

View File

@ -26,7 +26,6 @@
#define oatpp_async_worker_IOWorker_hpp
#include "./Worker.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/concurrency/SpinLock.hpp"
#include <thread>

View File

@ -26,7 +26,6 @@
#define oatpp_async_worker_TimerWorker_hpp
#include "./Worker.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/concurrency/SpinLock.hpp"
#include <thread>

View File

@ -34,18 +34,11 @@
#if defined(WIN32) || defined(_WIN32)
#include <WinSock2.h>
#endif
#if (defined(WIN32) || defined(_WIN32)) && defined(_WIN64)
struct tm* localtime_r(time_t *_clock, struct tm *_result) {
_localtime64_s(_result, _clock);
return _result;
}
#elif (defined(WIN32) || defined(_WIN32)) && not defined(_WIN64)
struct tm* localtime_r(time_t *_clock, struct tm *_result) {
_localtime32_s(_result, _clock);
return _result;
}
struct tm* localtime_r(time_t *_clock, struct tm *_result) {
localtime_s(_result, _clock);
return _result;
}
#endif
namespace oatpp { namespace base {

View File

@ -37,7 +37,7 @@
#include <stdexcept>
#include <cstdlib>
#define OATPP_VERSION "1.2.5"
#define OATPP_VERSION "1.3.0"
typedef unsigned char v_char8;
typedef v_char8 *p_char8;

View File

@ -1,281 +0,0 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_collection_LinkedList_hpp
#define oatpp_collection_LinkedList_hpp
#include "oatpp/core/base/memory/ObjectPool.hpp"
#include "oatpp/core/base/Countable.hpp"
#include "oatpp/core/base/Environment.hpp"
namespace oatpp { namespace collection {
template<class T>
class LinkedList : public base::Countable {
public:
OBJECT_POOL(LinkedList_Pool, LinkedList, 32)
SHARED_OBJECT_POOL(Shared_LinkedList_Pool, LinkedList, 32)
public:
class LinkedListNode {
friend LinkedList;
friend oatpp::base::memory::MemoryPool;
public:
OBJECT_POOL_THREAD_LOCAL(LinkedList_Node_Pool, LinkedListNode, 32)
private:
T data;
LinkedListNode* next;
protected:
LinkedListNode(const T& nodeData, LinkedListNode* nextNode)
: data(nodeData)
, next(nextNode)
{}
~LinkedListNode(){
}
public:
const T& getData(){
return data;
}
LinkedListNode* getNext(){
return next;
}
};
private:
LinkedListNode* m_first;
LinkedListNode* m_last;
v_int32 m_count;
oatpp::base::memory::MemoryPool& m_itemMemoryPool;
LinkedListNode* createNode(const T& data, LinkedListNode* next){
return new (m_itemMemoryPool.obtain()) LinkedListNode(data, next);
}
void destroyNode(LinkedListNode* node){
node->~LinkedListNode();
oatpp::base::memory::MemoryPool::free(node);
}
public:
LinkedList()
: m_first(nullptr)
, m_last(nullptr)
, m_count(0)
, m_itemMemoryPool(LinkedListNode::LinkedList_Node_Pool::getPool())
{}
public:
static std::shared_ptr<LinkedList> createShared(){
return Shared_LinkedList_Pool::allocateShared();
}
static std::shared_ptr<LinkedList> copy(LinkedList<T>* other){
auto result = createShared();
auto curr = other->m_first;
while(curr != nullptr){
result->pushBack(curr->data);
curr = curr->next;
}
return result;
}
virtual ~LinkedList() {
clear();
}
void pushFront(const T& data){
if(m_first == nullptr){
LinkedListNode* newNode = createNode(data, nullptr);
m_first = newNode;
m_last = newNode;
}else{
LinkedListNode* newNode = createNode(data, m_first);
m_first = newNode;
}
m_count++;
}
void pushBack(const T& data){
LinkedListNode* newNode = createNode(data, nullptr);
if(m_last == nullptr){
m_first = newNode;
m_last = newNode;
}else{
m_last->next = newNode;
m_last = newNode;
}
m_count++;
}
void pushBackAll(const std::shared_ptr<LinkedList>& list){
auto curr = list->getFirstNode();
while(curr != nullptr) {
pushBack(curr->getData());
curr = curr->getNext();
}
}
void insertAfterNode(const T& data, LinkedListNode* currentNode){
LinkedListNode* node = createNode(data, currentNode->next);
currentNode->next = node;
if(currentNode == m_last){
m_last = node;
}
m_count++;
}
T popFront(){
if(m_first != nullptr){
LinkedListNode* node = m_first;
m_first = m_first->next;
if(m_first == nullptr){
m_last = nullptr;
}
m_count --;
T result = node->data;
destroyNode(node);
return result;
}
throw std::runtime_error("[oatpp::collection::LinkedList::popFront()]: index out of bounds");
}
const T& getFirst() const{
return m_first->data;
}
const T& getLast() const{
return m_last->data;
}
const T& get(v_int32 index) const{
LinkedListNode* node = getNode(index);
if(node != nullptr){
return node->data;
}
throw std::runtime_error("[oatpp::collection::LinkedList::get(index)]: index out of bounds");
}
LinkedListNode* getNode(v_int32 index) const {
if(index >= m_count){
return nullptr;
}
v_int32 i = 0;
LinkedListNode* curr = m_first;
while(curr != nullptr){
if(i == index){
return curr;
}
curr = curr->next;
i++;
}
return nullptr;
}
LinkedListNode* getFirstNode() const {
return m_first;
}
v_int32 count() const{
return m_count;
}
/**
* for each item call a function
*
* list->forEachNode([](auto item){
* // your code here
* });
*/
template<typename F>
void forEach(const F& lambda) const {
auto curr = m_first;
while(curr != nullptr) {
lambda(curr->data);
curr = curr->next;
}
}
/**
* for each node call a function
*
* list->forEachNode([](auto node){
* // your code here
* });
*/
template<typename F>
void forEachNode(const F& lambda) const {
auto curr = m_first;
while(curr != nullptr) {
lambda(curr);
curr = curr->next;
}
}
void clear(){
LinkedListNode* curr = m_first;
while(curr != nullptr){
LinkedListNode* next = curr->next;
destroyNode(curr);
curr = next;
}
m_first = nullptr;
m_last = nullptr;
m_count = 0;
}
};
}}
#endif /* oatpp_collection_LinkedList_hpp */

View File

@ -1,277 +0,0 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_collection_ListMap_hpp
#define oatpp_collection_ListMap_hpp
#include "oatpp/core/base/memory/ObjectPool.hpp"
#include "oatpp/core/base/Countable.hpp"
namespace oatpp { namespace collection {
template<class K, class V>
class ListMap : public oatpp::base::Countable {
public:
OBJECT_POOL(ListMap_Pool, ListMap, 32)
SHARED_OBJECT_POOL(Shared_ListMap_Pool, ListMap, 32)
public:
//--------------------------------------------------------------------------------------
// Entry
class Entry{
friend ListMap;
public:
OBJECT_POOL_THREAD_LOCAL(ListMap_Entry_Pool, Entry, 64)
private:
K key;
V value;
Entry* next;
protected:
Entry(const K& pKey, const V& pValue, Entry* pNext)
: key(pKey)
, value(pValue)
, next(pNext)
{}
~Entry(){
}
public:
const K& getKey() const{
return key;
}
const V& getValue() const{
return value;
}
Entry* getNext() const{
return next;
}
};
private:
Entry* m_first;
Entry* m_last;
v_int32 m_count;
oatpp::base::memory::MemoryPool& m_itemMemoryPool;
private:
Entry* createEntry(const K& pKey, const V& pValue, Entry* pNext){
return new (m_itemMemoryPool.obtain()) Entry(pKey, pValue, pNext);
}
void destroyEntry(Entry* entry){
entry->~Entry();
oatpp::base::memory::MemoryPool::free(entry);
}
private:
template<class Key>
Entry* getEntryByKey(const Key& key) const{
Entry* curr = m_first;
while(curr != nullptr){
if(key == curr->key){
return curr;
}
curr = curr->next;
}
return nullptr;
}
void addOneEntry(Entry* entry){
if(m_last == nullptr){
m_first = entry;
m_last = entry;
}else{
m_last->next = entry;
m_last = entry;
}
m_count++;
}
public:
ListMap()
: m_first(nullptr)
, m_last(nullptr)
, m_count(0)
, m_itemMemoryPool(Entry::ListMap_Entry_Pool::getPool())
{}
public:
static std::shared_ptr<ListMap> createShared(){
return Shared_ListMap_Pool::allocateShared();
}
~ListMap() override {
clear();
}
Entry* put(const K& key, const V& value){
Entry* entry = getEntryByKey(key);
if(entry != nullptr){
if(entry->value != value){
entry->value = value;
}
}else{
entry = createEntry(key, value, nullptr);
addOneEntry(entry);
}
return entry;
}
bool putIfNotExists(const K& key, const V& value){
Entry* entry = getEntryByKey(key);
if(entry == nullptr){
entry = createEntry(key, value, nullptr);
addOneEntry(entry);
return true;
}
return false;
}
const Entry* find(const K& key) const{
Entry* entry = getEntryByKey<K>(key);
if(entry != nullptr){
return entry;
}
return nullptr;
}
const V& get(const K& key, const V& defaultValue) const {
Entry* entry = getEntryByKey<K>(key);
if(entry != nullptr){
return entry->getValue();
}
return defaultValue;
}
/*
template <class Key>
const Entry* getByKeyTemplate(const Key& key) const{
Entry* entry = getEntryByKey(key);
if(entry != nullptr){
return entry;
}
return nullptr;
}
*/
V remove(const K& key){
if(m_first != nullptr){
if(m_first->key->equals(key)){
Entry* next = m_first->next;
V result = m_first->value;
destroyEntry(m_first);
m_first = next;
return result;
}
Entry* curr = m_first;
Entry* next = m_first->next;
while(next != nullptr){
if(next->key->equals(key)){
V result = next->value;
curr->next = next->next;
destroyEntry(next);
return result;
}
curr = next;
next = curr->next;
}
}
return V::empty();
}
Entry* getEntryByIndex(v_int32 index) const{
if(index >= m_count){
return nullptr;
}
v_int32 i = 0;
Entry* curr = m_first;
while(curr != nullptr){
if(i == index){
return curr;
}
curr = curr->next;
i++;
}
return nullptr;
}
Entry* getFirstEntry() const {
return m_first;
}
v_int32 count() const{
return m_count;
}
void clear(){
Entry* curr = m_first;
while(curr != nullptr){
Entry* next = curr->next;
destroyEntry(curr);
curr = next;
}
m_first = nullptr;
m_last = nullptr;
m_count = 0;
}
};
}}
#endif /* oatpp_collection_ListMap_hpp */

View File

@ -246,7 +246,7 @@ v_io_size FIFOBuffer::write(const void *data, v_buff_size count) {
}
v_io_size FIFOBuffer::readAndWriteToStream(data::stream::OutputStream* stream, v_buff_size count, async::Action& action) {
v_io_size FIFOBuffer::readAndWriteToStream(data::stream::WriteCallback* stream, v_buff_size count, async::Action& action) {
if(!m_canRead) {
return IOError::RETRY_READ;
@ -299,7 +299,7 @@ v_io_size FIFOBuffer::readAndWriteToStream(data::stream::OutputStream* stream, v
}
v_io_size FIFOBuffer::readFromStreamAndWrite(data::stream::InputStream* stream, v_buff_size count, async::Action& action) {
v_io_size FIFOBuffer::readFromStreamAndWrite(data::stream::ReadCallback* stream, v_buff_size count, async::Action& action) {
if(m_canRead && m_writePosition == m_readPosition) {
return IOError::RETRY_WRITE;

View File

@ -123,7 +123,7 @@ public:
* @param action
* @return [1..count], IOErrors.
*/
v_io_size readAndWriteToStream(data::stream::OutputStream* stream, v_buff_size count, async::Action& action);
v_io_size readAndWriteToStream(data::stream::WriteCallback* stream, v_buff_size count, async::Action& action);
/**
* call stream.read() and then write bytes read to buffer
@ -132,7 +132,7 @@ public:
* @param action
* @return
*/
v_io_size readFromStreamAndWrite(data::stream::InputStream* stream, v_buff_size count, async::Action& action);
v_io_size readFromStreamAndWrite(data::stream::ReadCallback* stream, v_buff_size count, async::Action& action);
/**
* flush all availableToRead bytes to stream

View File

@ -86,11 +86,8 @@ void InlineWriteData::setEof() {
// ProcessingPipeline
ProcessingPipeline::ProcessingPipeline(const std::vector<base::ObjectHandle<Processor>>& processors)
: m_processors(processors)
: m_processors(processors), m_intermediateData(processors.size() - 1)
{
for(v_int32 i = 0; i < (v_int32) m_processors.size() - 1; i ++) {
m_intermediateData.push_back(data::buffer::InlineReadData());
}
}
v_io_size ProcessingPipeline::suggestInputStreamReadSize() {

View File

@ -27,8 +27,6 @@
#include "./Type.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/base/memory/ObjectPool.hpp"
#include "oatpp/core/base/Countable.hpp"

View File

@ -87,6 +87,8 @@ namespace __class {
: notNull(pNotNull)
{}
virtual ~PolymorphicDispatcher() = default;
const bool notNull;
virtual type::Void createObject() const = 0;
@ -495,7 +497,7 @@ Void EnumInterpreterAsString<T, notnull>::fromInterpretation(const Void& interVa
try {
const auto &entry = EnumOW::getEntryByName(interValue.staticCast<String>());
return EnumOW(entry.value);
} catch (const std::runtime_error& e) { // TODO - add a specific error for this.
} catch (const std::runtime_error&) { // TODO - add a specific error for this.
error = EnumInterpreterError::ENTRY_NOT_FOUND;
}
return Void(nullptr, EnumOW::Class::getType());
@ -556,7 +558,7 @@ Void EnumInterpreterAsNumber<T, notnull>::fromInterpretation(const Void& interVa
interValue.staticCast<OW>()
);
return EnumOW(entry.value);
} catch (const std::runtime_error& e) { // TODO - add a specific error for this.
} catch (const std::runtime_error&) { // TODO - add a specific error for this.
error = EnumInterpreterError::ENTRY_NOT_FOUND;
}
return Void(nullptr, EnumOW::Class::getType());

View File

@ -51,6 +51,8 @@ namespace __class {
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;
/**

View File

@ -64,6 +64,7 @@ public:
*/
std::string description = "";
std::string pattern = "";
bool required = false;
};
private:
@ -176,6 +177,8 @@ namespace __class {
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;

View File

@ -52,6 +52,8 @@ namespace __class {
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;
/**

View File

@ -26,6 +26,9 @@
#include "oatpp/core/data/stream/BufferStream.hpp"
#include "oatpp/core/utils/ConversionUtils.hpp"
#include "oatpp/core/data/share/MemoryLabel.hpp"
#include <fstream>
namespace oatpp { namespace data { namespace mapping { namespace type {
@ -36,7 +39,46 @@ String::String(const std::shared_ptr<std::string>& ptr, const type::Type* const
throw std::runtime_error("Value type does not match");
}
}
String String::loadFromFile(const char* filename) {
std::ifstream file (filename, std::ios::in|std::ios::binary|std::ios::ate);
if (file.is_open()) {
auto result = data::mapping::type::String(file.tellg());
file.seekg(0, std::ios::beg);
file.read((char*) result->data(), result->size());
file.close();
return result;
}
return nullptr;
}
void String::saveToFile(const char* filename) const {
std::ofstream fs(filename, std::ios::out | std::ios::binary);
if(m_ptr != nullptr) {
fs.write(m_ptr->data(), m_ptr->size());
}
fs.close();
}
const std::string& String::operator*() const {
return this->m_ptr.operator*();
}
bool String::equalsCI_ASCII(const std::string& other) {
auto ciLabel = share::StringKeyLabelCI(m_ptr);
return ciLabel == other;
}
bool String::equalsCI_ASCII(const String& other) {
auto ciLabel = share::StringKeyLabelCI(m_ptr);
return ciLabel == other;
}
bool String::equalsCI_ASCII(const char* other) {
auto ciLabel = share::StringKeyLabelCI(m_ptr);
return ciLabel == other;
}
String operator + (const char* a, const String& b) {
data::stream::BufferOutputStream stream;
stream << a << b;

View File

@ -30,10 +30,14 @@
#include "oatpp/core/base/memory/ObjectPool.hpp"
#include "oatpp/core/base/Countable.hpp"
#include <algorithm>
#include <cctype>
#include <iterator>
namespace oatpp { namespace data { namespace mapping { namespace type {
namespace __class {
class String; // FWD
class Int8; // FWD
@ -52,7 +56,7 @@ namespace __class {
class Float64; // FWD
class Boolean; // FWD
}
/**
@ -62,13 +66,13 @@ class String : public type::ObjectWrapper<std::string, __class::String> {
public:
String(const std::shared_ptr<std::string>& ptr, const type::Type* const valueType);
public:
String() {}
explicit String(v_buff_size size)
: type::ObjectWrapper<std::string, __class::String>(std::make_shared<std::string>(size, 0))
{}
String(const char* data, v_buff_size size)
: type::ObjectWrapper<std::string, __class::String>(std::make_shared<std::string>(data, size))
{}
@ -102,24 +106,43 @@ public:
std::make_shared<std::string>(std::forward<std::string>(str))
)
{}
String(const std::shared_ptr<std::string>& ptr)
: type::ObjectWrapper<std::string, __class::String>(ptr)
{}
String(std::shared_ptr<std::string>&& ptr)
: type::ObjectWrapper<std::string, __class::String>(std::forward<std::shared_ptr<std::string>>(ptr))
{}
String(const String& other)
: type::ObjectWrapper<std::string, __class::String>(other)
{}
String(String&& other)
: type::ObjectWrapper<std::string, __class::String>(std::forward<String>(other))
{}
const std::string& operator*() const {
/**
* Load data from file and store in &id:oatpp::String;.
* @param filename - name of the file.
* @return - &id:oatpp::String;.
*/
static String loadFromFile(const char* filename);
/**
* Save content of the buffer to file.
* @param filename - name of the file.
*/
void saveToFile(const char* filename) const;
const std::string& operator*() const;
operator std::string() const {
if (this->m_ptr == nullptr) {
throw std::runtime_error("[oatpp::data::mapping::type::String::operator std::string() const]: "
"Error. Null pointer.");
}
return this->m_ptr.operator*();
}
@ -165,6 +188,27 @@ public:
return *this;
}
/**
* Case insensitive compare (ASCII only).
* @param other
* @return
*/
bool equalsCI_ASCII(const std::string& other);
/**
* Case insensitive compare (ASCII only).
* @param other
* @return
*/
bool equalsCI_ASCII(const String& other);
/**
* Case insensitive compare (ASCII only).
* @param other
* @return
*/
bool equalsCI_ASCII(const char* str);
template<typename T,
typename enabled = typename std::enable_if<std::is_same<T, std::nullptr_t>::value, void>::type
>
@ -219,9 +263,9 @@ public:
inline bool operator != (const String &other) const {
return !operator == (other);
}
};
String operator + (const char* a, const String& b);
String operator + (const String& a, const char* b);
String operator + (const String& a, const String& b);
@ -301,7 +345,7 @@ public:
inline operator TValueType() const {
return *this->m_ptr;
}
};
/**
@ -471,29 +515,29 @@ template<>
struct ObjectWrapperByUnderlyingType <bool> {
typedef Boolean ObjectWrapper;
};
namespace __class {
class String {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class Int8 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class UInt8 {
@ -510,12 +554,12 @@ namespace __class {
class Int16 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class UInt16 {
@ -528,16 +572,16 @@ namespace __class {
}
};
class Int32 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class UInt32 {
@ -550,16 +594,16 @@ namespace __class {
}
};
class Int64 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class UInt64 {
@ -576,53 +620,53 @@ namespace __class {
class Float32 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class Float64 {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
class Boolean {
public:
static const ClassId CLASS_ID;
static Type* getType(){
static Type type(CLASS_ID, nullptr);
return &type;
}
};
}
}}}}
namespace std {
template<>
struct hash<oatpp::data::mapping::type::String> {
typedef oatpp::data::mapping::type::String argument_type;
typedef v_uint64 result_type;
result_type operator()(argument_type const& s) const noexcept {
if(s.get() == nullptr) return 0;
return hash<std::string> {} (*s);
}
};
template<>

View File

@ -310,6 +310,9 @@ public:
*/
class AbstractInterpretation {
public:
virtual ~AbstractInterpretation() = default;
/**
* Convert the object to its interpretation.
* @param originalValue

View File

@ -51,6 +51,8 @@ namespace __class {
*/
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;

View File

@ -51,6 +51,8 @@ namespace __class {
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;
/**

View File

@ -51,6 +51,8 @@ namespace __class {
class PolymorphicDispatcher {
public:
virtual ~PolymorphicDispatcher() = default;
virtual type::Void createObject() const = 0;
/**

View File

@ -180,6 +180,46 @@ public:
}
/**
* Erases all occurrences of key and replaces them with a new entry
* @param key
* @param value
* @return - true if an entry was replaced, false if entry was only inserted.
*/
bool putOrReplace(const Key& key, const StringKeyLabel& value) {
std::lock_guard<concurrency::SpinLock> lock(m_lock);
bool needsErase = m_map.find(key) != m_map.end();
if (needsErase) {
m_map.erase(key);
}
m_map.insert({key, value});
m_fullyInitialized = false;
return needsErase;
}
/**
* Erases all occurrences of key and replaces them with a new entry. Not thread-safe.
* @param key
* @param value
* @return - `true` if an entry was replaced, `false` if entry was only inserted.
*/
bool putOrReplace_LockFree(const Key& key, const StringKeyLabel& value) {
bool needsErase = m_map.find(key) != m_map.end();
if (needsErase) {
m_map.erase(key);
}
m_map.insert({key, value});
m_fullyInitialized = false;
return needsErase;
}
/**
* Get value as &id:oatpp::String;.
* @param key

View File

@ -25,6 +25,8 @@
#ifndef oatpp_data_share_MemoryLabel_hpp
#define oatpp_data_share_MemoryLabel_hpp
#include <memory>
#include "oatpp/core/data/mapping/type/Primitive.hpp"
#include "oatpp/core/utils/String.hpp"
@ -64,9 +66,15 @@ public:
/**
* Constructor.
* @param str
* @param ptr
*/
MemoryLabel(const std::shared_ptr<std::string>& str) : MemoryLabel(str, str->data(), (v_buff_size) str->size()) {}
MemoryLabel(const std::shared_ptr<std::string>& ptr) :
MemoryLabel(
ptr,
ptr ? ptr->data() : nullptr,
ptr ? (v_buff_size) ptr->size() : 0
)
{}
/**
* Constructor.
@ -105,7 +113,7 @@ public:
*/
void captureToOwnMemory() const {
if(!m_memoryHandle || m_memoryHandle->data() != (const char*)m_data || m_memoryHandle->size() != m_size) {
m_memoryHandle.reset(new std::string((const char*) m_data, m_size));
m_memoryHandle = std::make_shared<std::string>((const char*) m_data, m_size);
m_data = (p_char8) m_memoryHandle->data();
}
}
@ -117,7 +125,8 @@ public:
* @return - `true` if equals.
*/
bool equals(const char* data) const {
return utils::String::compare(m_data, m_size, data, std::strlen(data)) == 0;
auto len = data != nullptr ? std::strlen(data) : 0;
return utils::String::compare(m_data, m_size, data, len) == 0;
}
/**
@ -170,7 +179,13 @@ public:
StringKeyLabel() : MemoryLabel() {};
StringKeyLabel(std::nullptr_t) : MemoryLabel() {}
/**
* Constructor.
* @param ptr
*/
StringKeyLabel(const std::shared_ptr<std::string>& ptr) : MemoryLabel(ptr) {}
StringKeyLabel(const std::shared_ptr<std::string>& memoryHandle, const char* data, v_buff_size size);
StringKeyLabel(const char* constText);
StringKeyLabel(const String& str);
@ -229,6 +244,8 @@ public:
StringKeyLabelCI(std::nullptr_t) : MemoryLabel() {}
StringKeyLabelCI(const std::shared_ptr<std::string>& ptr) : MemoryLabel(ptr) {}
StringKeyLabelCI(const std::shared_ptr<std::string>& memoryHandle, const char* data, v_buff_size size);
StringKeyLabelCI(const char* constText);
StringKeyLabelCI(const String& str);
@ -242,7 +259,8 @@ public:
}
inline bool operator==(const char* str) const {
return utils::String::compareCI(m_data, m_size, str, std::strlen(str)) == 0;
auto len = str != nullptr ? std::strlen(str) : 0;
return utils::String::compareCI_ASCII(m_data, m_size, str, len) == 0;
}
inline bool operator!=(const char* str) const {
@ -252,7 +270,7 @@ public:
inline bool operator==(const String& str) const {
if(m_data == nullptr) return str == nullptr;
if(str == nullptr) return false;
return utils::String::compareCI(m_data, m_size, str->data(), str->size()) == 0;
return utils::String::compareCI_ASCII(m_data, m_size, str->data(), str->size()) == 0;
}
inline bool operator!=(const String& str) const {
@ -260,7 +278,7 @@ public:
}
inline bool operator==(const StringKeyLabelCI &other) const {
return utils::String::compareCI(m_data, m_size, other.m_data, other.m_size) == 0;
return utils::String::compareCI_ASCII(m_data, m_size, other.m_data, other.m_size) == 0;
}
inline bool operator!=(const StringKeyLabelCI &other) const {
@ -268,11 +286,11 @@ public:
}
inline bool operator < (const StringKeyLabelCI &other) const {
return utils::String::compareCI(m_data, m_size, other.m_data, other.m_size) < 0;
return utils::String::compareCI_ASCII(m_data, m_size, other.m_data, other.m_size) < 0;
}
inline bool operator > (const StringKeyLabelCI &other) const {
return utils::String::compareCI(m_data, m_size, other.m_data, other.m_size) > 0;
return utils::String::compareCI_ASCII(m_data, m_size, other.m_data, other.m_size) > 0;
}
};
@ -289,7 +307,7 @@ namespace std {
result_type operator()(oatpp::data::share::StringKeyLabel const& s) const noexcept {
p_char8 data = (p_char8) s.getData();
auto data = (p_char8) s.getData();
result_type result = 0;
for(v_buff_size i = 0; i < s.getSize(); i++) {
v_char8 c = data[i];
@ -309,7 +327,7 @@ namespace std {
result_type operator()(oatpp::data::share::StringKeyLabelCI const& s) const noexcept {
p_char8 data = (p_char8) s.getData();
auto data = (p_char8) s.getData();
result_type result = 0;
for(v_buff_size i = 0; i < s.getSize(); i++) {
v_char8 c = data[i] | 32;

View File

@ -147,7 +147,7 @@ oatpp::async::CoroutineStarter BufferOutputStream::flushToStreamAsync(const std:
, m_stream(stream)
{}
Action act() {
Action act() override {
if(m_inlineData.currBufferPtr == nullptr) {
m_inlineData.currBufferPtr = m_this->m_data;
m_inlineData.bytesLeft = m_this->m_position;
@ -238,4 +238,27 @@ void BufferInputStream::setCurrentPosition(v_buff_size position) {
m_position = position;
}
v_io_size BufferInputStream::peek(void *data, v_buff_size count, async::Action &action) {
(void) action;
v_buff_size desiredAmount = count;
if(desiredAmount > m_size - m_position) {
desiredAmount = m_size - m_position;
}
std::memcpy(data, &m_data[m_position], desiredAmount);
return desiredAmount;
}
v_io_size BufferInputStream::availableToRead() const {
return m_size - m_position;
}
v_io_size BufferInputStream::commitReadOffset(v_buff_size count) {
if(count > m_size - m_position) {
count = m_size - m_position;
}
m_position += count;
return count;
}
}}}

View File

@ -146,7 +146,7 @@ public:
/**
* BufferInputStream
*/
class BufferInputStream : public InputStream {
class BufferInputStream : public BufferedInputStream {
public:
static data::stream::DefaultInitializedContext DEFAULT_CONTEXT;
private:
@ -245,6 +245,26 @@ public:
*/
void setCurrentPosition(v_buff_size position);
/**
* Peek up to count of bytes int he buffer
* @param data
* @param count
* @return [1..count], IOErrors.
*/
v_io_size peek(void *data, v_buff_size count, async::Action& action) override;
/**
* Amount of bytes currently available to read from buffer.
* @return &id:oatpp::v_io_size;.
*/
v_io_size availableToRead() const override;
/**
* Commit read offset
* @param count
* @return [1..count], IOErrors.
*/
v_io_size commitReadOffset(v_buff_size count) override;
};

View File

@ -303,15 +303,13 @@ oatpp::async::CoroutineStarter ChunkedBuffer::flushToStreamAsync(const std::shar
}
std::shared_ptr<ChunkedBuffer::Chunks> ChunkedBuffer::getChunks() {
auto chunks = Chunks::createShared();
auto chunks = std::make_shared<Chunks>();
auto curr = m_firstEntry;
v_int32 count = 0;
while (curr != nullptr) {
if(curr->next != nullptr){
chunks->pushBack(Chunk::createShared(curr->chunk, CHUNK_ENTRY_SIZE));
} else {
chunks->pushBack(Chunk::createShared(curr->chunk, m_size - CHUNK_ENTRY_SIZE * count));
}
chunks->push_back(Chunk::createShared(curr->chunk, curr->next
? CHUNK_ENTRY_SIZE
: m_size - CHUNK_ENTRY_SIZE * count));
++count;
curr = curr->next;
}

View File

@ -25,9 +25,10 @@
#ifndef oatpp_data_stream_ChunkedBuffer_hpp
#define oatpp_data_stream_ChunkedBuffer_hpp
#include <list>
#include "Stream.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/async/Coroutine.hpp"
namespace oatpp { namespace data{ namespace stream {
@ -99,7 +100,7 @@ public:
};
public:
typedef oatpp::collection::LinkedList<std::shared_ptr<Chunk>> Chunks;
typedef std::list<std::shared_ptr<Chunk>> Chunks;
private:
v_buff_size m_size;

View File

@ -0,0 +1,136 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Benedikt-Alexander Mokroß <github@bamkrs.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#include "FIFOStream.hpp"
#include "oatpp/core/utils/Binary.hpp"
namespace oatpp { namespace data { namespace stream {
data::stream::DefaultInitializedContext FIFOInputStream::DEFAULT_CONTEXT(data::stream::StreamType::STREAM_FINITE);
FIFOInputStream::FIFOInputStream(v_buff_size initialSize)
: m_memoryHandle(std::make_shared<std::string>(initialSize, (char)0))
, m_fifo(std::make_shared<data::buffer::FIFOBuffer>((void*)m_memoryHandle->data(), m_memoryHandle->size(), 0, 0, false))
, m_maxCapacity(-1) {
}
void FIFOInputStream::reset() {
m_fifo->setBufferPosition(0, 0, false);
}
v_io_size FIFOInputStream::read(void *data, v_buff_size count, async::Action& action) {
(void) action;
return m_fifo->read(data, count);
}
void FIFOInputStream::setInputStreamIOMode(IOMode ioMode) {
m_ioMode = ioMode;
}
IOMode FIFOInputStream::getInputStreamIOMode() {
return m_ioMode;
}
Context& FIFOInputStream::getInputStreamContext() {
return DEFAULT_CONTEXT;
}
std::shared_ptr<std::string> FIFOInputStream::getDataMemoryHandle() {
return m_memoryHandle;
}
v_io_size FIFOInputStream::write(const void *data, v_buff_size count, async::Action &action) {
(void) action;
reserveBytesUpfront(count);
return m_fifo->write(data, count);
}
void FIFOInputStream::reserveBytesUpfront(v_buff_size count) {
v_buff_size capacityNeeded = availableToRead() + count;
if(capacityNeeded > m_fifo->getBufferSize()) {
v_buff_size newCapacity = utils::Binary::nextP2(capacityNeeded);
if(newCapacity < 0 || (m_maxCapacity > 0 && newCapacity > m_maxCapacity)) {
newCapacity = m_maxCapacity;
}
if(newCapacity < capacityNeeded) {
throw std::runtime_error("[oatpp::data::stream::BufferOutputStream::reserveBytesUpfront()]: Error. Unable to allocate requested memory.");
}
// ToDo: In-Memory-Resize
auto newHandle = std::make_shared<std::string>(newCapacity, (char)0);
v_io_size oldSize = m_fifo->availableToRead();
m_fifo->read((void*)newHandle->data(), oldSize);
auto newFifo = std::make_shared<data::buffer::FIFOBuffer>((void*)newHandle->data(), newHandle->size(), 0, oldSize, oldSize > 0);
m_memoryHandle = newHandle;
m_fifo = newFifo;
}
}
v_io_size FIFOInputStream::readAndWriteToStream(data::stream::OutputStream *stream,
v_buff_size count,
async::Action &action) {
return m_fifo->readAndWriteToStream(stream, count, action);
}
v_io_size FIFOInputStream::readFromStreamAndWrite(data::stream::InputStream *stream,
v_buff_size count,
async::Action &action) {
reserveBytesUpfront(count);
return m_fifo->readFromStreamAndWrite(stream, count, action);
}
v_io_size FIFOInputStream::flushToStream(data::stream::OutputStream *stream) {
return m_fifo->flushToStream(stream);
}
async::CoroutineStarter FIFOInputStream::flushToStreamAsync(const std::shared_ptr<data::stream::OutputStream> &stream) {
return m_fifo->flushToStreamAsync(stream);
}
v_io_size FIFOInputStream::availableToWrite() {
return m_fifo->availableToWrite();
}
v_io_size FIFOInputStream::peek(void *data, v_buff_size count, async::Action &action) {
(void) action;
return m_fifo->peek(data, count);
}
v_io_size FIFOInputStream::availableToRead() const {
return m_fifo->availableToRead();
}
v_io_size FIFOInputStream::commitReadOffset(v_buff_size count) {
return m_fifo->commitReadOffset(count);
}
}}}

View File

@ -0,0 +1,175 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Benedikt-Alexander Mokroß <github@bamkrs.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_data_stream_FIFOStream_hpp
#define oatpp_data_stream_FIFOStream_hpp
#include "Stream.hpp"
#include "oatpp/core/data/buffer/FIFOBuffer.hpp"
namespace oatpp { namespace data { namespace stream {
/**
* FIFOInputStream
*/
class FIFOInputStream : public BufferedInputStream, public WriteCallback {
public:
static data::stream::DefaultInitializedContext DEFAULT_CONTEXT;
private:
std::shared_ptr<std::string> m_memoryHandle;
std::shared_ptr<data::buffer::FIFOBuffer> m_fifo;
v_buff_size m_maxCapacity;
IOMode m_ioMode;
public:
/**
* Constructor.
* @param data - buffer.
*/
FIFOInputStream(v_buff_size initialSize = 4096);
static std::shared_ptr<FIFOInputStream> createShared(v_buff_size initialSize = 4096) {
return std::make_shared<FIFOInputStream>(initialSize);
}
/**
* Discards all data in the buffer and resets it to an empty state
*/
void reset();
/**
* Read data from stream. <br>
* It is a legal case if return result < count. Caller should handle this!
* *Calls to this method are always NON-BLOCKING*
* @param data - buffer to read data to.
* @param count - size of the buffer.
* @param action - async specific action. If action is NOT &id:oatpp::async::Action::TYPE_NONE;, then
* caller MUST return this action on coroutine iteration.
* @return - actual number of bytes read. 0 - designates end of the buffer.
*/
v_io_size read(void *data, v_buff_size count, async::Action& action) override;
/**
* Set stream I/O mode.
* @throws
*/
void setInputStreamIOMode(IOMode ioMode) override;
/**
* Get stream I/O mode.
* @return
*/
IOMode getInputStreamIOMode() override;
/**
* Get stream context.
* @return
*/
Context& getInputStreamContext() override;
/**
* Get data memory handle.
* @return - data memory handle.
*/
std::shared_ptr<std::string> getDataMemoryHandle();
/**
* Write operation callback.
* @param data - pointer to data.
* @param count - size of the data in bytes.
* @param action - async specific action. If action is NOT &id:oatpp::async::Action::TYPE_NONE;, then
* caller MUST return this action on coroutine iteration.
* @return - actual number of bytes written. 0 - to indicate end-of-file.
*/
v_io_size write(const void *data, v_buff_size count, async::Action &action) override;
/**
* Peek up to count of bytes int he buffer
* @param data
* @param count
* @return [1..count], IOErrors.
*/
v_io_size peek(void *data, v_buff_size count, async::Action& action) override;
/**
* Amount of bytes currently available to read from buffer.
* @return &id:oatpp::v_io_size;.
*/
v_io_size availableToRead() const override;
/**
* Commit read offset
* @param count
* @return [1..count], IOErrors.
*/
v_io_size commitReadOffset(v_buff_size count) override;
/**
* Reserve bytes for future writes. Check &id:oatpp::data::stream::FIFOStream::availableToWrite for the capacity.
*/
void reserveBytesUpfront(v_buff_size count);
/**
* call read and then write bytes read to output stream
* @param stream
* @param count
* @param action
* @return [1..count], IOErrors.
*/
v_io_size readAndWriteToStream(data::stream::OutputStream* stream, v_buff_size count, async::Action& action);
/**
* call stream.read() and then write bytes read to buffer
* @param stream
* @param count
* @param action
* @return
*/
v_io_size readFromStreamAndWrite(data::stream::InputStream* stream, v_buff_size count, async::Action& action);
/**
* flush all availableToRead bytes to stream
* @param stream
* @return
*/
v_io_size flushToStream(data::stream::OutputStream* stream);
/**
* flush all availableToRead bytes to stream in asynchronous manner
* @param stream - &id:data::stream::OutputStream;.
* @return - &id:async::CoroutineStarter;.
*/
async::CoroutineStarter flushToStreamAsync(const std::shared_ptr<data::stream::OutputStream>& stream);
/**
* Amount of buffer space currently available for data writes.
* @return &id:oatpp::v_io_size;.
*/
v_io_size availableToWrite();
};
}}}
#endif // oatpp_data_stream_FIFOStream_hpp

View File

@ -119,7 +119,7 @@ async::CoroutineStarter WriteCallback::writeExactSizeDataAsync(const void* data,
, m_inlineData(data, size)
{}
Action act() {
Action act() override {
return m_this->writeExactSizeDataAsyncInline(m_inlineData, finish());
}
@ -727,7 +727,11 @@ async::CoroutineStarter transferAsync(const base::ObjectHandle<ReadCallback>& re
switch(res) {
case IOError::BROKEN_PIPE:
return error<AsyncTransferError>("[oatpp::data::stream::transferAsync]: Error. ReadCallback. BROKEN_PIPE.");
if(m_transferSize > 0) {
return error<AsyncTransferError>("[oatpp::data::stream::transferAsync]: Error. ReadCallback. BROKEN_PIPE.");
}
m_inData.set(nullptr, 0);
break;
case IOError::ZERO_VALUE:
m_inData.set(nullptr, 0);
@ -746,10 +750,13 @@ async::CoroutineStarter transferAsync(const base::ObjectHandle<ReadCallback>& re
return repeat();
default:
if(!action.isNone()) {
return action;
if(m_transferSize > 0) {
if (!action.isNone()) {
return action;
}
return error<AsyncTransferError>("[oatpp::data::stream::transferAsync]: Error. ReadCallback. Unknown IO error.");
}
return error<AsyncTransferError>("[oatpp::data::stream::transferAsync]: Error. ReadCallback. Unknown IO error.");
m_inData.set(nullptr, 0);
}

View File

@ -350,6 +350,38 @@ public:
};
/**
* Buffered Input Stream
*/
class BufferedInputStream : public InputStream {
public:
/**
* Default virtual destructor.
*/
virtual ~BufferedInputStream() = default;
/**
* Peek up to count of bytes int he buffer
* @param data
* @param count
* @return [1..count], IOErrors.
*/
virtual v_io_size peek(void *data, v_buff_size count, async::Action& action) = 0;
/**
* Amount of bytes currently available to read from buffer.
* @return &id:oatpp::v_io_size;.
*/
virtual v_io_size availableToRead() const = 0;
/**
* Commit read offset
* @param count
* @return [1..count], IOErrors.
*/
virtual v_io_size commitReadOffset(v_buff_size count) = 0;
};
/**
* I/O Stream.
*/

View File

@ -101,5 +101,9 @@ oatpp::data::stream::IOMode InputStreamBufferedProxy::getInputStreamIOMode() {
Context& InputStreamBufferedProxy::getInputStreamContext() {
return m_inputStream->getInputStreamContext();
}
v_io_size InputStreamBufferedProxy::availableToRead() const {
return m_buffer.availableToRead();
}
}}}

View File

@ -82,7 +82,7 @@ public:
};
class InputStreamBufferedProxy : public oatpp::base::Countable, public InputStream {
class InputStreamBufferedProxy : public oatpp::base::Countable, public BufferedInputStream {
public:
OBJECT_POOL(InputStreamBufferedProxy_Pool, InputStreamBufferedProxy, 32)
SHARED_OBJECT_POOL(Shared_InputStreamBufferedProxy_Pool, InputStreamBufferedProxy, 32)
@ -119,9 +119,11 @@ public:
v_io_size read(void *data, v_buff_size count, async::Action& action) override;
v_io_size peek(void *data, v_buff_size count, async::Action& action);
v_io_size peek(void *data, v_buff_size count, async::Action& action) override;
v_io_size commitReadOffset(v_buff_size count);
v_io_size commitReadOffset(v_buff_size count) override;
v_io_size availableToRead() const override;
/**
* Set InputStream I/O mode.

View File

@ -25,7 +25,6 @@
#ifndef oatpp_parser_Caret_hpp
#define oatpp_parser_Caret_hpp
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/Types.hpp"
namespace oatpp { namespace parser {

View File

@ -6,7 +6,8 @@
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>,
* Matthias Haselmaier <mhaselmaier@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -189,96 +190,87 @@ private:
private:
std::shared_ptr<Provider<TResource>> m_provider;
v_int64 m_counter;
v_int64 m_counter{0};
v_int64 m_maxResources;
v_int64 m_maxResourceTTL;
std::atomic<bool> m_running;
bool m_finished;
std::atomic<bool> m_running{true};
bool m_finished{false};
private:
std::list<PoolRecord> m_bench;
async::CoroutineWaitList m_waitList;
std::condition_variable m_condition;
std::mutex m_lock;
std::chrono::duration<v_int64, std::micro> m_timeout;
protected:
PoolTemplate(const std::shared_ptr<Provider<TResource>>& provider, v_int64 maxResources, v_int64 maxResourceTTL)
PoolTemplate(const std::shared_ptr<Provider<TResource>>& provider, v_int64 maxResources, v_int64 maxResourceTTL, const std::chrono::duration<v_int64, std::micro>& timeout)
: m_provider(provider)
, m_counter(0)
, m_maxResources(maxResources)
, m_maxResourceTTL(maxResourceTTL)
, m_running(true)
, m_finished(false)
, m_timeout(timeout)
{}
void startCleanupTask(const std::shared_ptr<PoolTemplate>& _this) {
static void startCleanupTask(const std::shared_ptr<PoolTemplate>& _this) {
std::thread poolCleanupTask(cleanupTask, _this);
poolCleanupTask.detach();
}
public:
static std::shared_ptr<PoolTemplate> createShared(const std::shared_ptr<Provider<TResource>>& provider,
v_int64 maxResources,
const std::chrono::duration<v_int64, std::micro>& maxResourceTTL)
{
/* "new" is called directly to keep constructor private */
auto ptr = std::shared_ptr<PoolTemplate>(new PoolTemplate(provider, maxResources, maxResourceTTL.count()));
ptr->startCleanupTask();
return ptr;
}
virtual ~PoolTemplate() {
stop();
}
std::shared_ptr<TResource> get(const std::shared_ptr<PoolTemplate>& _this) {
static std::shared_ptr<TResource> get(const std::shared_ptr<PoolTemplate>& _this) {
auto readyPredicate = [&_this]() { return !_this->m_running || !_this->m_bench.empty() || _this->m_counter < _this->m_maxResources; };
std::unique_lock<std::mutex> guard{_this->m_lock};
if (_this->m_timeout == std::chrono::microseconds::zero())
{
std::unique_lock<std::mutex> guard(m_lock);
while (m_running && m_bench.size() == 0 && m_counter >= m_maxResources ) {
m_condition.wait(guard);
while (!readyPredicate()) {
_this->m_condition.wait(guard);
}
} else if (!_this->m_condition.wait_for(guard, _this->m_timeout, std::move(readyPredicate))) {
return nullptr;
}
if(!m_running) {
return nullptr;
}
if (m_bench.size() > 0) {
auto record = m_bench.front();
m_bench.pop_front();
return std::make_shared<AcquisitionProxyImpl>(record.resource, _this);
} else {
++ m_counter;
}
}
try {
return std::make_shared<AcquisitionProxyImpl>(m_provider->get(), _this);
} catch (...) {
std::lock_guard<std::mutex> guard(m_lock);
-- m_counter;
if(!_this->m_running) {
return nullptr;
}
if (_this->m_bench.size() > 0) {
auto record = _this->m_bench.front();
_this->m_bench.pop_front();
return std::make_shared<AcquisitionProxyImpl>(record.resource, _this);
} else {
++ _this->m_counter;
}
guard.unlock();
try {
return std::make_shared<AcquisitionProxyImpl>(_this->m_provider->get(), _this);
} catch (...) {
guard.lock();
--_this->m_counter;
return nullptr;
}
}
async::CoroutineStarterForResult<const std::shared_ptr<TResource>&> getAsync(const std::shared_ptr<PoolTemplate>& _this) {
static async::CoroutineStarterForResult<const std::shared_ptr<TResource>&> getAsync(const std::shared_ptr<PoolTemplate>& _this) {
class GetCoroutine : public oatpp::async::CoroutineWithResult<GetCoroutine, const std::shared_ptr<TResource>&> {
private:
std::shared_ptr<PoolTemplate> m_pool;
std::shared_ptr<Provider<TResource>> m_provider;
std::chrono::steady_clock::time_point m_startTime{std::chrono::steady_clock::now()};
public:
GetCoroutine(const std::shared_ptr<PoolTemplate>& pool, const std::shared_ptr<Provider<TResource>>& provider)
GetCoroutine(const std::shared_ptr<PoolTemplate>& pool)
: m_pool(pool)
, m_provider(provider)
{}
bool timedout() const noexcept {
return m_pool->m_timeout != std::chrono::microseconds::zero() && m_pool->m_timeout < (std::chrono::steady_clock::now() - m_startTime);
}
async::Action act() override {
if (timedout()) return this->_return(nullptr);
{
/* Careful!!! Using non-async lock */
@ -286,7 +278,9 @@ public:
if (m_pool->m_running && m_pool->m_bench.size() == 0 && m_pool->m_counter >= m_pool->m_maxResources) {
guard.unlock();
return async::Action::createWaitListAction(&m_pool->m_waitList);
return m_pool->m_timeout == std::chrono::microseconds::zero()
? async::Action::createWaitListAction(&m_pool->m_waitList)
: async::Action::createWaitListActionWithTimeout(&m_pool->m_waitList, m_startTime + m_pool->m_timeout);
}
if(!m_pool->m_running) {
@ -305,7 +299,7 @@ public:
}
return m_provider->getAsync().callbackTo(&GetCoroutine::onGet);
return m_pool->m_provider->getAsync().callbackTo(&GetCoroutine::onGet);
}
@ -324,10 +318,26 @@ public:
};
return GetCoroutine::startForResult(_this, m_provider);
return GetCoroutine::startForResult(_this);
}
public:
static std::shared_ptr<PoolTemplate> createShared(const std::shared_ptr<Provider<TResource>>& provider,
v_int64 maxResources,
const std::chrono::duration<v_int64, std::micro>& maxResourceTTL)
{
/* "new" is called directly to keep constructor private */
auto ptr = std::shared_ptr<PoolTemplate>(new PoolTemplate(provider, maxResources, maxResourceTTL.count()));
startCleanupTask(ptr);
return ptr;
}
virtual ~PoolTemplate() {
stop();
}
void invalidate(const std::shared_ptr<TResource>& resource) {
auto proxy = std::static_pointer_cast<AcquisitionProxyImpl>(resource);
proxy->__pool__invalidate();
@ -385,14 +395,19 @@ private:
typedef PoolTemplate<TResource, AcquisitionProxyImpl> TPool;
protected:
/**
/*
* Protected Constructor.
* @param provider
* @param maxResources
* @param maxResourceTTL
* @param timeout
*/
Pool(const std::shared_ptr<TProvider>& provider, v_int64 maxResources, v_int64 maxResourceTTL)
: PoolTemplate<TResource, AcquisitionProxyImpl>(provider, maxResources, maxResourceTTL)
Pool(const std::shared_ptr<TProvider>& provider,
v_int64 maxResources,
v_int64 maxResourceTTL,
const std::chrono::duration<v_int64, std::micro>& timeout = std::chrono::microseconds::zero()
)
: PoolTemplate<TResource, AcquisitionProxyImpl>(provider, maxResources, maxResourceTTL, timeout)
{
TProvider::m_properties = provider->getProperties();
}
@ -404,14 +419,16 @@ public:
* @param provider - resource provider.
* @param maxResources - max resource count in the pool.
* @param maxResourceTTL - max time-to-live for unused resource in the pool.
* @param timeout - optional timeout on &l:Pool::get (); and &l:Pool::getAsync (); operations.
* @return - `std::shared_ptr` of `Pool`.
*/
static std::shared_ptr<Pool> createShared(const std::shared_ptr<TProvider>& provider,
v_int64 maxResources,
const std::chrono::duration<v_int64, std::micro>& maxResourceTTL)
const std::chrono::duration<v_int64, std::micro>& maxResourceTTL,
const std::chrono::duration<v_int64, std::micro>& timeout = std::chrono::microseconds::zero())
{
/* "new" is called directly to keep constructor private */
auto ptr = std::shared_ptr<Pool>(new Pool(provider, maxResources, maxResourceTTL.count()));
auto ptr = std::shared_ptr<Pool>(new Pool(provider, maxResources, maxResourceTTL.count(), timeout));
ptr->startCleanupTask(ptr);
return ptr;
}

View File

@ -35,7 +35,7 @@ namespace oatpp { namespace provider {
* @tparam T - resource class.
*/
template <class T>
class Provider {
class Provider : public oatpp::base::Countable {
protected:
void setProperty(const oatpp::String& key, const oatpp::String& value) {

View File

@ -23,29 +23,10 @@
***************************************************************************/
#include "String.hpp"
#include <fstream>
#include <cstring>
namespace oatpp { namespace utils {
data::mapping::type::String String::loadFromFile(const char* filename) {
std::ifstream file (filename, std::ios::in|std::ios::binary|std::ios::ate);
if (file.is_open()) {
auto result = data::mapping::type::String(file.tellg());
file.seekg(0, std::ios::beg);
file.read((char*) result->data(), result->size());
file.close();
return result;
}
return nullptr;
}
void String::saveToFile(const data::mapping::type::String& data, const char* filename) {
std::ofstream fs(filename, std::ios::out | std::ios::binary);
fs.write(data->data(), data->size());
fs.close();
}
v_buff_size String::compare(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2) {
if(data1 == data2) return 0;
@ -68,7 +49,7 @@ v_buff_size String::compare(const void* data1, v_buff_size size1, const void* da
}
v_buff_size String::compareCI(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2) {
v_buff_size String::compareCI_ASCII(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2) {
if(data1 == data2) return 0;
if(data1 == nullptr) return -1;
@ -101,14 +82,14 @@ v_buff_size String::compareCI(const void* data1, v_buff_size size1, const void*
}
void String::lowerCaseASCII(void* data, v_buff_size size) {
void String::lowerCase_ASCII(void* data, v_buff_size size) {
for(v_buff_size i = 0; i < size; i++) {
v_char8 a = ((p_char8) data)[i];
if(a >= 'A' && a <= 'Z') ((p_char8) data)[i] = a | 32;
}
}
void String::upperCaseASCII(void* data, v_buff_size size) {
void String::upperCase_ASCII(void* data, v_buff_size size) {
for(v_buff_size i = 0; i < size; i++) {
v_char8 a = ((p_char8) data)[i];
if(a >= 'a' && a <= 'z') ((p_char8) data)[i] = a & 223;

View File

@ -25,7 +25,6 @@
#ifndef oatpp_utils_String_hpp
#define oatpp_utils_String_hpp
#include "oatpp/core/data/mapping/type/Primitive.hpp"
#include "oatpp/core/base/Environment.hpp"
namespace oatpp { namespace utils {
@ -36,19 +35,6 @@ namespace oatpp { namespace utils {
class String {
public:
/**
* Load data from file and store in &id:oatpp::String;.
* @param filename - name of the file.
* @return - &id:oatpp::String;.
*/
static data::mapping::type::String loadFromFile(const char* filename);
/**
* Save content of the buffer to file.
* @param filename - name of the file.
*/
static void saveToFile(const data::mapping::type::String& data, const char* filename);
/**
* Compare data1, data2 using `std::memcmp`.
* *It's safe to pass nullptr for data1/data2*
@ -63,7 +49,7 @@ public:
static v_buff_size compare(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2);
/**
* Compare data1, data2 - case insensitive.
* Compare data1, data2 - case insensitive (ASCII only).
* *It's safe to pass nullptr for data1/data2*
* @param data1 - pointer to data1.
* @param size1 - size of data1.
@ -73,21 +59,21 @@ public:
* 0 if all count bytes of data1 and data2 are equal.<br>
* Positive value if the first differing byte in data1 is greater than the corresponding byte in data2.
*/
static v_buff_size compareCI(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2);
static v_buff_size compareCI_ASCII(const void* data1, v_buff_size size1, const void* data2, v_buff_size size2);
/**
* Change characters in data to lowercase (ASCII only).
* @param data - pointer to data.
* @param size - size of the data.
*/
static void lowerCaseASCII(void* data, v_buff_size size);
static void lowerCase_ASCII(void* data, v_buff_size size);
/**
* Change characters in data to uppercase (ASCII only).
* @param data - pointer to data.
* @param size - size of the data.
*/
static void upperCaseASCII(void* data, v_buff_size size);
static void upperCase_ASCII(void* data, v_buff_size size);
};

View File

@ -57,13 +57,13 @@ public:
/**
* No properties here. It is just a logical division
*/
class ServerConnectionProvider : public ConnectionProvider {
class ServerConnectionProvider : virtual public ConnectionProvider {
};
/**
* No properties here. It is just a logical division
*/
class ClientConnectionProvider : public ConnectionProvider {
class ClientConnectionProvider : virtual public ConnectionProvider {
};
}}

View File

@ -36,7 +36,7 @@ const v_int32 Server::STATUS_RUNNING = 2;
const v_int32 Server::STATUS_STOPPING = 3;
const v_int32 Server::STATUS_DONE = 4;
Server::Server(const std::shared_ptr<ServerConnectionProvider> &connectionProvider,
Server::Server(const std::shared_ptr<ConnectionProvider> &connectionProvider,
const std::shared_ptr<ConnectionHandler> &connectionHandler)
: m_status(STATUS_CREATED)
, m_connectionProvider(connectionProvider)
@ -50,11 +50,15 @@ void Server::conditionalMainLoop() {
while (getStatus() == STATUS_RUNNING) {
if (m_condition()) {
auto connection = m_connectionProvider->get();
std::shared_ptr<data::stream::IOStream> connection;
{
std::lock_guard<oatpp::concurrency::SpinLock> lg(m_spinlock);
connection = m_connectionProvider->get();
}
if (connection) {
if (getStatus() == STATUS_RUNNING) {
if (m_condition()) {
std::lock_guard<oatpp::concurrency::SpinLock> lg(m_spinlock);
m_connectionHandler->handleConnection(connection, params /* null params */);
} else {
setStatus(STATUS_STOPPING);
@ -75,10 +79,15 @@ void Server::mainLoop(Server *instance) {
std::shared_ptr<const std::unordered_map<oatpp::String, oatpp::String>> params;
while (instance->getStatus() == STATUS_RUNNING) {
auto connection = instance->m_connectionProvider->get();
std::shared_ptr<data::stream::IOStream> connection;
{
std::lock_guard<oatpp::concurrency::SpinLock> lg(instance->m_spinlock);
connection = instance->m_connectionProvider->get();
}
if (connection) {
if (instance->getStatus() == STATUS_RUNNING) {
std::lock_guard<oatpp::concurrency::SpinLock> lg(instance->m_spinlock);
instance->m_connectionHandler->handleConnection(connection, params /* null params */);
} else {
OATPP_LOGD("[oatpp::network::server::mainLoop()]", "Error. Server already stopped - closing connection...");
@ -86,7 +95,6 @@ void Server::mainLoop(Server *instance) {
}
}
instance->setStatus(STATUS_DONE);
}
@ -115,7 +123,7 @@ void Server::run(std::function<bool()> conditional) {
void Server::run(bool startAsNewThread) {
std::unique_lock<std::mutex> ul(m_mutex);
OATPP_LOGW("[oatpp::network::server::run(bool)]", "Using oatpp::network::server::run(bool) is deprecated and will be removed in the next release. Please implement your own threading.")
OATPP_LOGW("[oatpp::network::server::run(bool)]", "Using oatpp::network::server::run(bool) is deprecated and will be removed in the next release. Please implement your own threading (See https://github.com/oatpp/oatpp-threaded-starter).")
switch (getStatus()) {
case STATUS_STARTING:
throw std::runtime_error("[oatpp::network::server::run()] Error. Server already starting");
@ -163,6 +171,16 @@ v_int32 Server::getStatus() {
return m_status.load();
}
void Server::setConnectionProvider(const std::shared_ptr<ServerConnectionProvider> &connectionProvider) {
std::lock_guard<oatpp::concurrency::SpinLock> lg(m_spinlock);
m_connectionProvider = connectionProvider;
}
void Server::setConnectionHandler(const std::shared_ptr<ConnectionHandler> &connectionHandler) {
std::lock_guard<oatpp::concurrency::SpinLock> lg(m_spinlock);
m_connectionHandler = connectionHandler;
}
Server::~Server() {
stop();
}

View File

@ -58,8 +58,9 @@ private:
std::function<bool()> m_condition;
std::thread m_thread;
std::mutex m_mutex;
oatpp::concurrency::SpinLock m_spinlock;
std::shared_ptr<ServerConnectionProvider> m_connectionProvider;
std::shared_ptr<ConnectionProvider> m_connectionProvider;
std::shared_ptr<ConnectionHandler> m_connectionHandler;
bool m_threaded;
@ -71,7 +72,7 @@ public:
* @param connectionProvider - &id:oatpp::network::ConnectionProvider;.
* @param connectionHandler - &id:oatpp::network::ConnectionHandler;.
*/
Server(const std::shared_ptr<ServerConnectionProvider>& connectionProvider,
Server(const std::shared_ptr<ConnectionProvider>& connectionProvider,
const std::shared_ptr<ConnectionHandler>& connectionHandler);
virtual ~Server();
@ -127,6 +128,13 @@ public:
* to &id:oatpp::network::ConnectionHandler;.
* @param startAsNewThread - Start the server blocking (thread of callee) or non-blocking (own thread)
* @deprecated Deprecated since 1.3.0, will be removed in the next release.
* The new repository https://github.com/oatpp/oatpp-threaded-starter shows many configurations how to run Oat++ in its own thread.
* From simple No-Stop to Stop-Simple and ending in Oat++ completely isolated in its own thread-scope.
* We recommend the Stop-Simple for most applications! You can find it here: https://github.com/oatpp/oatpp-threaded-starter/blob/master/src/App_StopSimple.cpp
* The other examples are non trivial and highly specialized on specific environments or requirements.
* Please read the comments carefully and think about the consequences twice.
* If someone wants to use them please get back to us in an issue in the new repository and we can assist you with them.
* Again: These examples introduce special conditions and requirements for your code!
*/
void run(bool startAsNewThread);
@ -147,6 +155,18 @@ public:
* </ul>
*/
v_int32 getStatus();
/**
* Replaces the internal connection-provider
* @param connectionProvider - &id:oatpp::network::ConnectionProvider;.
*/
void setConnectionProvider(const std::shared_ptr<ServerConnectionProvider>& connectionProvider);
/**
* Replaces the internal connection-handler
* @param connectionHandler - &id:oatpp::network::ConnectionHandler;.
*/
void setConnectionHandler(const std::shared_ptr<ConnectionHandler>& connectionHandler);
};

View File

@ -35,7 +35,7 @@ oatpp::String Url::Parser::parseScheme(oatpp::parser::Caret& caret) {
if(size > 0) {
std::unique_ptr<v_char8[]> buff(new v_char8[size]);
std::memcpy(buff.get(), &caret.getData()[pos0], size);
utils::String::lowerCaseASCII(buff.get(), size);
utils::String::lowerCase_ASCII(buff.get(), size);
return oatpp::String((const char*)buff.get(), size);
}
return nullptr;

View File

@ -27,7 +27,6 @@
#include "oatpp/core/data/share/LazyStringMap.hpp"
#include "oatpp/core/parser/Caret.hpp"
#include "oatpp/core/collection/ListMap.hpp"
#include "oatpp/core/Types.hpp"
namespace oatpp { namespace network {

View File

@ -0,0 +1,49 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#include "ConnectionInactivityChecker.hpp"
namespace oatpp { namespace network { namespace monitor {
ConnectionInactivityChecker::ConnectionInactivityChecker(const std::chrono::duration<v_int64, std::micro>& lastReadTimeout,
const std::chrono::duration<v_int64, std::micro>& lastWriteTimeout)
: m_lastReadTimeout(lastReadTimeout)
, m_lastWriteTimeout(lastWriteTimeout)
{}
std::vector<oatpp::String> ConnectionInactivityChecker::getMetricsList() {
return {};
}
std::shared_ptr<StatCollector> ConnectionInactivityChecker::createStatCollector(const oatpp::String& metricName) {
throw std::runtime_error("[oatpp::network::monitor::ConnectionInactivityChecker::createStatCollector()]: "
"Error. ConnectionInactivityChecker doesn't use any stat collectors.");
}
bool ConnectionInactivityChecker::check(const ConnectionStats& stats, v_int64 currMicroTime) {
return currMicroTime - stats.timestampLastRead < m_lastReadTimeout.count() &&
currMicroTime - stats.timestampLastWrite < m_lastWriteTimeout.count();
}
}}}

View File

@ -0,0 +1,60 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_network_monitor_ConnectionInactivityChecker_hpp
#define oatpp_network_monitor_ConnectionInactivityChecker_hpp
#include "MetricsChecker.hpp"
namespace oatpp { namespace network { namespace monitor {
/**
* ConnectionInactivityChecker - checks if a connection is inactive (has no read/writes) and whether it should be closed.
* Extends - &id:oatpp::network::monitor::MetricsChecker;.
*/
class ConnectionInactivityChecker : public MetricsChecker {
private:
std::chrono::duration<v_int64, std::micro> m_lastReadTimeout;
std::chrono::duration<v_int64, std::micro> m_lastWriteTimeout;
public:
/**
* Constructor.
* @param lastReadTimeout - how long can live connection without reads.
* @param lastWriteTimeout - how long can live connection without writes.
*/
ConnectionInactivityChecker(const std::chrono::duration<v_int64, std::micro>& lastReadTimeout,
const std::chrono::duration<v_int64, std::micro>& lastWriteTimeout);
std::vector<oatpp::String> getMetricsList();
std::shared_ptr<StatCollector> createStatCollector(const oatpp::String& metricName);
bool check(const ConnectionStats& stats, v_int64 currMicroTime);
};
}}}
#endif //oatpp_network_monitor_ConnectionInactivityChecker_hpp

View File

@ -0,0 +1,46 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#include "ConnectionMaxAgeChecker.hpp"
namespace oatpp { namespace network { namespace monitor {
ConnectionMaxAgeChecker::ConnectionMaxAgeChecker(const std::chrono::duration<v_int64, std::micro>& maxAge)
: m_maxAge(maxAge)
{}
std::vector<oatpp::String> ConnectionMaxAgeChecker::getMetricsList() {
return {};
}
std::shared_ptr<StatCollector> ConnectionMaxAgeChecker::createStatCollector(const oatpp::String& metricName) {
throw std::runtime_error("[oatpp::network::monitor::ConnectionMaxAgeChecker::createStatCollector()]: "
"Error. ConnectionMaxAgeChecker doesn't use any stat collectors.");
}
bool ConnectionMaxAgeChecker::check(const ConnectionStats& stats, v_int64 currMicroTime) {
return currMicroTime - stats.timestampCreated < m_maxAge.count();
}
}}}

View File

@ -0,0 +1,57 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_network_monitor_ConnectionMaxAgeChecker_hpp
#define oatpp_network_monitor_ConnectionMaxAgeChecker_hpp
#include "MetricsChecker.hpp"
namespace oatpp { namespace network { namespace monitor {
/**
* ConnectionMaxAgeChecker - checks if connection is too old and should be closed.
* Extends - &id:oatpp::network::monitor::MetricsChecker;.
*/
class ConnectionMaxAgeChecker : public MetricsChecker {
private:
std::chrono::duration<v_int64, std::micro> m_maxAge;
public:
/**
* Constructor.
* @param maxAge - how long should connection live.
*/
ConnectionMaxAgeChecker(const std::chrono::duration<v_int64, std::micro>& maxAge);
std::vector<oatpp::String> getMetricsList();
std::shared_ptr<StatCollector> createStatCollector(const oatpp::String& metricName);
bool check(const ConnectionStats& stats, v_int64 currMicroTime);
};
}}}
#endif //oatpp_network_monitor_ConnectionMaxAgeChecker_hpp

View File

@ -0,0 +1,333 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#include "ConnectionMonitor.hpp"
#include <chrono>
#include <thread>
namespace oatpp { namespace network { namespace monitor {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ConnectionMonitor::ConnectionProxy
ConnectionMonitor::ConnectionProxy::ConnectionProxy(const std::shared_ptr<Monitor>& monitor,
const std::shared_ptr<ConnectionProvider>& connectionProvider,
const std::shared_ptr<data::stream::IOStream>& connection)
: m_monitor(monitor)
, m_connectionProvider(connectionProvider)
, m_connection(connection)
{
m_stats.timestampCreated = base::Environment::getMicroTickCount();
}
ConnectionMonitor::ConnectionProxy::~ConnectionProxy() {
std::lock_guard<std::mutex> lock(m_statsMutex);
m_monitor->freeConnectionStats(m_stats);
if(m_stats.metricsData.size() > 0) {
for(auto& pair : m_stats.metricsData) {
OATPP_LOGE("[oatpp::network::ConnectionMonitor::ConnectionProxy::~ConnectionProxy()]",
"Error. Memory leak. Metric data was not deleted: Metric name - '%s'", pair.first->c_str());
}
}
m_monitor->removeConnection((v_uint64) this);
}
v_io_size ConnectionMonitor::ConnectionProxy::read(void *buffer, v_buff_size count, async::Action& action) {
auto res = m_connection->read(buffer, count, action);
std::lock_guard<std::mutex> lock(m_statsMutex);
m_monitor->onConnectionRead(m_stats, res);
return res;
}
v_io_size ConnectionMonitor::ConnectionProxy::write(const void *data, v_buff_size count, async::Action& action) {
auto res = m_connection->write(data, count, action);
std::lock_guard<std::mutex> lock(m_statsMutex);
m_monitor->onConnectionWrite(m_stats, res);
return res;
}
void ConnectionMonitor::ConnectionProxy::setInputStreamIOMode(data::stream::IOMode ioMode) {
m_connection->setInputStreamIOMode(ioMode);
}
data::stream::IOMode ConnectionMonitor::ConnectionProxy::getInputStreamIOMode() {
return m_connection->getInputStreamIOMode();
}
data::stream::Context& ConnectionMonitor::ConnectionProxy::getInputStreamContext() {
return m_connection->getInputStreamContext();
}
void ConnectionMonitor::ConnectionProxy::setOutputStreamIOMode(data::stream::IOMode ioMode) {
m_connection->setOutputStreamIOMode(ioMode);
}
data::stream::IOMode ConnectionMonitor::ConnectionProxy::getOutputStreamIOMode() {
return m_connection->getOutputStreamIOMode();
}
data::stream::Context& ConnectionMonitor::ConnectionProxy::getOutputStreamContext() {
return m_connection->getOutputStreamContext();
}
void ConnectionMonitor::ConnectionProxy::invalidate() {
m_connectionProvider->invalidate(m_connection);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Monitor
void ConnectionMonitor::Monitor::monitorTask(std::shared_ptr<Monitor> monitor) {
while(monitor->m_running) {
{
std::lock_guard<std::mutex> lock(monitor->m_connectionsMutex);
auto currMicroTime = oatpp::base::Environment::getMicroTickCount();
for(auto& pair : monitor->m_connections) {
auto connection = pair.second.lock();
std::lock_guard<std::mutex> dataLock(connection->m_statsMutex);
std::lock_guard<std::mutex> analysersLock(monitor->m_checkMutex);
for(auto& a : monitor->m_metricsCheckers) {
bool res = a->check(connection->m_stats, currMicroTime);
if(!res) {
connection->invalidate();
break;
}
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
{
std::lock_guard<std::mutex>(monitor->m_runMutex);
monitor->m_stopped = true;
}
monitor->m_runCondition.notify_all();
}
void* ConnectionMonitor::Monitor::createOrGetMetricData(ConnectionStats& stats, const std::shared_ptr<StatCollector>& collector) {
void* data;
auto it = stats.metricsData.find(collector->metricName());
if(it == stats.metricsData.end()) {
data = collector->createMetricData();
stats.metricsData.insert({collector->metricName(), data});
} else {
data = it->second;
}
return data;
}
std::shared_ptr<ConnectionMonitor::Monitor> ConnectionMonitor::Monitor::createShared() {
auto monitor = std::make_shared<Monitor>();
std::thread t([monitor](){
ConnectionMonitor::Monitor::monitorTask(monitor);
});
t.detach();
return monitor;
}
void ConnectionMonitor::Monitor::addConnection(v_uint64 id, const std::weak_ptr<ConnectionProxy>& connection) {
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.insert({id, connection});
}
void ConnectionMonitor::Monitor::freeConnectionStats(ConnectionStats& stats) {
std::lock_guard<std::mutex> lock(m_checkMutex);
for(auto& metric : stats.metricsData) {
auto it = m_statCollectors.find(metric.first);
if(it != m_statCollectors.end()) {
it->second->deleteMetricData(metric.second);
} else {
OATPP_LOGE("[oatpp::network::ConnectionMonitor::Monitor::freeConnectionStats]",
"Error. Can't free Metric data. Unknown Metric: name - '%s'", it->first->c_str());
}
}
}
void ConnectionMonitor::Monitor::removeConnection(v_uint64 id) {
std::lock_guard<std::mutex> lock(m_connectionsMutex);
m_connections.erase(id);
}
void ConnectionMonitor::Monitor::addStatCollector(const std::shared_ptr<StatCollector>& collector) {
std::lock_guard<std::mutex> lock(m_checkMutex);
m_statCollectors.insert({collector->metricName(), collector});
}
void ConnectionMonitor::Monitor::removeStatCollector(const oatpp::String& metricName) {
std::lock_guard<std::mutex> lock(m_checkMutex);
m_statCollectors.erase(metricName);
}
void ConnectionMonitor::Monitor::addMetricsChecker(const std::shared_ptr<MetricsChecker>& checker) {
std::lock_guard<std::mutex> lock(m_checkMutex);
m_metricsCheckers.push_back(checker);
auto metrics = checker->getMetricsList();
for(auto& m : metrics) {
auto it = m_statCollectors.find(m);
if(it == m_statCollectors.end()) {
m_statCollectors.insert({m, checker->createStatCollector(m)});
}
}
}
void ConnectionMonitor::Monitor::onConnectionRead(ConnectionStats& stats, v_io_size readResult) {
v_int64 currTimestamp = base::Environment::getMicroTickCount();
if(readResult > 0) {
stats.totalRead += readResult;
stats.lastReadSize = readResult;
stats.timestampLastRead = currTimestamp;
}
{
std::lock_guard<std::mutex> lock(m_checkMutex);
for(auto& pair : m_statCollectors) {
pair.second->onRead(createOrGetMetricData(stats, pair.second), readResult, currTimestamp);
}
}
}
void ConnectionMonitor::Monitor::onConnectionWrite(ConnectionStats& stats, v_io_size writeResult) {
v_int64 currTimestamp = base::Environment::getMicroTickCount();
if(writeResult > 0) {
stats.totalWrite += writeResult;
stats.lastWriteSize = writeResult;
stats.timestampLastWrite = currTimestamp;
}
{
std::lock_guard<std::mutex> lock(m_checkMutex);
for(auto& pair : m_statCollectors) {
pair.second->onWrite(createOrGetMetricData(stats, pair.second), writeResult, currTimestamp);
}
}
}
void ConnectionMonitor::Monitor::stop() {
m_running = false;
std::unique_lock<std::mutex> runLock(m_runMutex);
while(!m_stopped) {
m_runCondition.wait(runLock);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ConnectionMonitor
ConnectionMonitor::ConnectionMonitor(const std::shared_ptr<ConnectionProvider>& connectionProvider)
: m_monitor(Monitor::createShared())
, m_connectionProvider(connectionProvider)
{
}
std::shared_ptr<data::stream::IOStream> ConnectionMonitor::get() {
auto connection = m_connectionProvider->get();
if(!connection) {
return nullptr;
}
auto proxy = std::make_shared<ConnectionProxy>(m_monitor, m_connectionProvider, connection);
m_monitor->addConnection((v_uint64) proxy.get(), proxy);
return proxy;
}
async::CoroutineStarterForResult<const std::shared_ptr<data::stream::IOStream>&>
ConnectionMonitor::getAsync() {
class GetConnectionCoroutine : public async::CoroutineWithResult<GetConnectionCoroutine, const std::shared_ptr<data::stream::IOStream>&> {
private:
std::shared_ptr<Monitor> m_monitor;
std::shared_ptr<ConnectionProvider> m_connectionProvider;
public:
GetConnectionCoroutine(const std::shared_ptr<Monitor>& monitor,
const std::shared_ptr<ConnectionProvider>& connectionProvider)
: m_monitor(monitor)
, m_connectionProvider(connectionProvider)
{}
Action act() override {
return m_connectionProvider->getAsync().callbackTo(&GetConnectionCoroutine::onConnection);
}
Action onConnection(const std::shared_ptr<data::stream::IOStream>& connection) {
if(!connection) {
return _return(nullptr);
}
auto proxy = std::make_shared<ConnectionProxy>(m_monitor, m_connectionProvider, connection);
m_monitor->addConnection((v_uint64) proxy.get(), proxy);
return _return(proxy);
}
};
return GetConnectionCoroutine::startForResult(m_monitor, m_connectionProvider);
}
void ConnectionMonitor::addStatCollector(const std::shared_ptr<StatCollector>& collector) {
m_monitor->addStatCollector(collector);
}
void ConnectionMonitor::addMetricsChecker(const std::shared_ptr<MetricsChecker>& checker) {
m_monitor->addMetricsChecker(checker);
}
void ConnectionMonitor::invalidate(const std::shared_ptr<data::stream::IOStream>& connection) {
auto proxy = std::static_pointer_cast<ConnectionProxy>(connection);
proxy->invalidate();
}
void ConnectionMonitor::stop() {
m_monitor->stop();
}
}}}

View File

@ -0,0 +1,151 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_network_monitor_ConnectionMonitor_hpp
#define oatpp_network_monitor_ConnectionMonitor_hpp
#include "MetricsChecker.hpp"
#include "oatpp/network/ConnectionProvider.hpp"
#include "oatpp/core/data/stream/Stream.hpp"
#include <unordered_map>
#include <condition_variable>
namespace oatpp { namespace network { namespace monitor {
/**
* ConnectionMonitor is a middleman who's able to manage provided connections
* and close those ones that are not satisfy selected rules.
*/
class ConnectionMonitor : public ClientConnectionProvider, public ServerConnectionProvider {
private:
class Monitor; // FWD
class ConnectionProxy : public data::stream::IOStream {
friend Monitor;
private:
std::shared_ptr<Monitor> m_monitor;
/* provider which created this connection */
std::shared_ptr<ConnectionProvider> m_connectionProvider;
std::shared_ptr<data::stream::IOStream> m_connection;
std::mutex m_statsMutex;
ConnectionStats m_stats;
public:
ConnectionProxy(const std::shared_ptr<Monitor>& monitor,
const std::shared_ptr<ConnectionProvider>& connectionProvider,
const std::shared_ptr<data::stream::IOStream>& connection);
~ConnectionProxy() override;
v_io_size read(void *buffer, v_buff_size count, async::Action& action) override;
v_io_size write(const void *data, v_buff_size count, async::Action& action) override;
void setInputStreamIOMode(data::stream::IOMode ioMode) override;
data::stream::IOMode getInputStreamIOMode() override;
data::stream::Context& getInputStreamContext() override;
void setOutputStreamIOMode(data::stream::IOMode ioMode) override;
data::stream::IOMode getOutputStreamIOMode() override;
data::stream::Context& getOutputStreamContext() override;
void invalidate();
};
private:
class Monitor : public oatpp::base::Countable {
private:
std::mutex m_runMutex;
std::condition_variable m_runCondition;
std::atomic<bool> m_running {true};
bool m_stopped {false};
std::mutex m_connectionsMutex;
std::unordered_map<v_uint64, std::weak_ptr<ConnectionProxy>> m_connections;
std::mutex m_checkMutex;
std::vector<std::shared_ptr<MetricsChecker>> m_metricsCheckers;
std::unordered_map<oatpp::String, std::shared_ptr<StatCollector>> m_statCollectors;
private:
static void monitorTask(std::shared_ptr<Monitor> monitor);
private:
static void* createOrGetMetricData(ConnectionStats& stats, const std::shared_ptr<StatCollector>& collector);
public:
static std::shared_ptr<Monitor> createShared();
void addConnection(v_uint64 id, const std::weak_ptr<ConnectionProxy>& connection);
void freeConnectionStats(ConnectionStats& stats);
void removeConnection(v_uint64 id);
void addStatCollector(const std::shared_ptr<StatCollector>& collector);
void removeStatCollector(const oatpp::String& metricName);
void addMetricsChecker(const std::shared_ptr<MetricsChecker>& checker);
void onConnectionRead(ConnectionStats& stats, v_io_size readResult);
void onConnectionWrite(ConnectionStats& stats, v_io_size writeResult);
void stop();
};
private:
std::shared_ptr<Monitor> m_monitor;
std::shared_ptr<ConnectionProvider> m_connectionProvider;
public:
/**
* Constructor.
* @param connectionProvider - underlying connection provider.
*/
ConnectionMonitor(const std::shared_ptr<ConnectionProvider>& connectionProvider);
std::shared_ptr<data::stream::IOStream> get() override;
async::CoroutineStarterForResult<const std::shared_ptr<data::stream::IOStream>&> getAsync() override;
void addStatCollector(const std::shared_ptr<StatCollector>& collector);
/**
* Add metrics checker.
* @param checker - &id:oatpp::network::monitor::MetricsChecker;.
*/
void addMetricsChecker(const std::shared_ptr<MetricsChecker>& checker);
void invalidate(const std::shared_ptr<data::stream::IOStream>& connection) override;
void stop() override;
};
}}}
#endif //oatpp_network_monitor_ConnectionMonitor_hpp

View File

@ -0,0 +1,71 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_network_monitor_MetricsChecker_hpp
#define oatpp_network_monitor_MetricsChecker_hpp
#include "StatCollector.hpp"
namespace oatpp { namespace network { namespace monitor {
/**
* MetricsChecker checks &id:oatpp::network::monitor::ConnectionStats; if those are satisfy the rule.
*/
class MetricsChecker : public oatpp::base::Countable {
public:
/**
* Default virtual destructor.
*/
virtual ~MetricsChecker() = default;
/**
* Get list of metrics names that are checked by this MetricsChecker.
* @return
*/
virtual std::vector<oatpp::String> getMetricsList() = 0;
/**
* Create &id:oatpp::network::monitor::StatCollector; for given `metricName`.
* This method will be called by &id:oatpp::network::monitor::ConnectionMonitor; only if there is
* no such `StatCollector` registered in the `ConnectionMonitor` yet.
* @param metricName - name of the metric.
* @return - &id:oatpp::network::monitor::StatCollector;.
*/
virtual std::shared_ptr<StatCollector> createStatCollector(const oatpp::String& metricName) = 0;
/**
* Called by &id:oatpp::network::monitor::ConnectionMonitor; on each
* time interval to check if connection satisfies the rule.
* @param stats - &id:oatpp::network::monitor::ConnectionStats;.
* @param currMicroTime - current time microseconds.
* @return - `true` if connection satisfies the rule. `false` if connection should be closed.
*/
virtual bool check(const ConnectionStats& stats, v_int64 currMicroTime) = 0;
};
}}}
#endif //oatpp_network_monitor_MetricsChecker_hpp

View File

@ -0,0 +1,131 @@
/***************************************************************************
*
* Project _____ __ ____ _ _
* ( _ ) /__\ (_ _)_| |_ _| |_
* )(_)( /(__)\ )( (_ _)(_ _)
* (_____)(__)(__)(__) |_| |_|
*
*
* Copyright 2018-present, Leonid Stryzhevskyi <lganzzzo@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
***************************************************************************/
#ifndef oatpp_network_monitor_StatCollector_hpp
#define oatpp_network_monitor_StatCollector_hpp
#include "oatpp/core/Types.hpp"
#include "oatpp/core/IODefinitions.hpp"
namespace oatpp { namespace network { namespace monitor {
/**
* ConnectionStats.
*/
struct ConnectionStats {
/**
* Timestamp created microseconds.
* When connection was created.
*/
v_int64 timestampCreated = 0;
/**
* Total bytes read from the connection.
* Logs all bytes when the `read` method is called.
*/
v_io_size totalRead = 0;
/**
* Total bytes written to the connection.
* Logs all bytes when the `write` method is called.
*/
v_io_size totalWrite = 0;
/**
* Timestamp microseconds when the last successful read was performed on the connection.
*/
v_int64 timestampLastRead = 0;
/**
* Timestamp microseconds when the last successful write was performed on the connection.
*/
v_int64 timestampLastWrite = 0;
/**
* Amount of bytes read during the last successful read.
*/
v_io_size lastReadSize = 0;
/**
* Amount of bytes written during the last successful write.
*/
v_io_size lastWriteSize = 0;
/**
* Data collected by stat-collectors - &l:StatCollector;
*/
std::unordered_map<oatpp::String, void*> metricsData;
};
/**
* StatCollector collects metrics data of the connection.
*/
class StatCollector : public oatpp::base::Countable {
public:
/**
* Default virtual destructor.
*/
virtual ~StatCollector() = default;
/**
* Unique metric name that is collected by this `StatCollector`.
* @return - metricName. &id:oatpp::String;.
*/
virtual oatpp::String metricName() = 0;
/**
* Metric data constructor.
* @return
*/
virtual void* createMetricData() = 0;
/**
* Metric data destructor.
* @param metricData
*/
virtual void deleteMetricData(void* metricData) = 0;
/**
* On connection read event.
* @param metricData - metric data of the given connection.- the one created in the `createMetricData` method.
* @param readResult - result of the connection read method.
* @param timestamp - timestamp microseconds when the connection `read` method was called.
*/
virtual void onRead(void* metricData, v_io_size readResult, v_int64 timestamp) = 0;
/**
* On connection write event.
* @param metricData - metric data of the given connection.- the one created in the `createMetricData` method.
* @param writeResult - result of the connection write method.
* @param timestamp - timestamp microseconds when the connection `write` method was called.
*/
virtual void onWrite(void* metricData, v_io_size writeResult, v_int64 timestamp) = 0;
};
}}}
#endif //oatpp_network_monitor_StatCollector_hpp

View File

@ -35,7 +35,7 @@ namespace oatpp { namespace network { namespace tcp { namespace client {
/**
* Simple provider of clinet TCP connections.
*/
class ConnectionProvider : public base::Countable, public ClientConnectionProvider {
class ConnectionProvider : public ClientConnectionProvider {
protected:
network::Address m_address;
public:

View File

@ -43,6 +43,31 @@
#endif
#endif
// Workaround for MinGW from: https://www.mail-archive.com/users@ipv6.org/msg02107.html
#if defined(__MINGW32__) && _WIN32_WINNT < 0x0600
const char * inet_ntop (int af, const void *src, char *dst, socklen_t cnt) {
if (af == AF_INET) {
struct sockaddr_in in;
memset (&in, 0, sizeof(in));
in.sin_family = AF_INET;
memcpy (&in.sin_addr, src, sizeof(struct in_addr));
getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
} else if (af == AF_INET6) {
struct sockaddr_in6 in;
memset (&in, 0, sizeof(in));
in.sin6_family = AF_INET6;
memcpy (&in.sin6_addr, src, sizeof(struct in_addr6));
getnameinfo ((struct sockaddr *)&in, sizeof (struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
return dst;
}
return NULL;
}
#endif
namespace oatpp { namespace network { namespace tcp { namespace server {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -97,8 +122,6 @@ void ConnectionProvider::stop() {
oatpp::v_io_handle ConnectionProvider::instantiateServer(){
int iResult;
SOCKET serverHandle = INVALID_SOCKET;
struct addrinfo *result = nullptr;
@ -118,7 +141,7 @@ oatpp::v_io_handle ConnectionProvider::instantiateServer(){
auto portStr = oatpp::utils::conversion::int32ToStr(m_address.port);
iResult = getaddrinfo(m_address.host->c_str(), portStr->c_str(), &hints, &result);
const int iResult = getaddrinfo(m_address.host->c_str(), portStr->c_str(), &hints, &result);
if (iResult != 0) {
OATPP_LOGE("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]", "Error. Call to getaddrinfo() failed with result=%d", iResult);
throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: Error. Call to getaddrinfo() failed.");
@ -159,6 +182,13 @@ oatpp::v_io_handle ConnectionProvider::instantiateServer(){
throw std::runtime_error("[oatpp::network::tcp::server::ConnectionProvider::instantiateServer()]: Error. Call to ioctlsocket failed.");
}
// Update port after binding (typicaly in case of port = 0)
struct ::sockaddr_in s_in;
::memset(&s_in, 0, sizeof(s_in));
::socklen_t s_in_len = sizeof(s_in);
::getsockname(serverHandle, (struct sockaddr *)&s_in, &s_in_len);
setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(ntohs(s_in.sin_port)));
return serverHandle;
}
@ -232,6 +262,13 @@ oatpp::v_io_handle ConnectionProvider::instantiateServer(){
fcntl(serverHandle, F_SETFL, O_NONBLOCK);
// Update port after binding (typicaly in case of port = 0)
struct ::sockaddr_in s_in;
::memset(&s_in, 0, sizeof(s_in));
::socklen_t s_in_len = sizeof(s_in);
::getsockname(serverHandle, (struct sockaddr *)&s_in, &s_in_len);
setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(ntohs(s_in.sin_port)));
return serverHandle;
}

View File

@ -36,7 +36,7 @@ namespace oatpp { namespace network { namespace tcp { namespace server {
/**
* Simple provider of TCP connections.
*/
class ConnectionProvider : public base::Countable, public ServerConnectionProvider {
class ConnectionProvider : public ServerConnectionProvider {
public:
/**

View File

@ -184,7 +184,7 @@ std::shared_ptr<Interface::ConnectionSubmission> Interface::connect() {
auto submission = std::make_shared<ConnectionSubmission>(true);
{
std::lock_guard<std::mutex> lock(m_mutex);
m_submissions.pushBack(submission);
m_submissions.push_back(submission);
}
m_condition.notify_one();
return submission;
@ -199,7 +199,7 @@ std::shared_ptr<Interface::ConnectionSubmission> Interface::connectNonBlocking()
std::unique_lock<std::mutex> lock(m_mutex, std::try_to_lock);
if (lock.owns_lock()) {
submission = std::make_shared<ConnectionSubmission>(true);
m_submissions.pushBack(submission);
m_submissions.push_back(submission);
}
}
if (submission) {
@ -212,19 +212,23 @@ std::shared_ptr<Interface::ConnectionSubmission> Interface::connectNonBlocking()
std::shared_ptr<Socket> Interface::accept(const bool& waitingHandle) {
std::unique_lock<std::mutex> lock(m_mutex);
while (waitingHandle && m_submissions.getFirstNode() == nullptr) {
while (waitingHandle && m_submissions.empty()) {
m_condition.wait(lock);
}
if(!waitingHandle) {
return nullptr;
}
return acceptSubmission(m_submissions.popFront());
const auto submission = m_submissions.front();
m_submissions.pop_front();
return acceptSubmission(submission);
}
std::shared_ptr<Socket> Interface::acceptNonBlocking() {
std::unique_lock<std::mutex> lock(m_mutex, std::try_to_lock);
if(lock.owns_lock() && m_submissions.getFirstNode() != nullptr) {
return acceptSubmission(m_submissions.popFront());
if(lock.owns_lock() && !m_submissions.empty()) {
const auto submission = m_submissions.front();
m_submissions.pop_front();
return acceptSubmission(submission);
}
return nullptr;
}
@ -233,14 +237,9 @@ void Interface::dropAllConnection() {
std::unique_lock<std::mutex> lock(m_mutex);
auto curr = m_submissions.getFirstNode();
while(curr != nullptr) {
auto submission = curr->getData();
for (const auto& submission : m_submissions) {
submission->invalidate();
curr = curr->getNext();
}
m_submissions.clear();
}

View File

@ -27,8 +27,7 @@
#include "./Socket.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include <list>
#include <unordered_map>
namespace oatpp { namespace network { namespace virtual_ {
@ -116,7 +115,7 @@ private:
std::mutex m_listenerMutex;
std::mutex m_mutex;
std::condition_variable m_condition;
oatpp::collection::LinkedList<std::shared_ptr<ConnectionSubmission>> m_submissions;
std::list<std::shared_ptr<ConnectionSubmission>> m_submissions;
private:
Interface(const oatpp::String& name);

View File

@ -39,7 +39,7 @@ std::shared_ptr<QueryResult> Executor::execute(const oatpp::String& query,
const std::shared_ptr<const data::mapping::TypeResolver>& typeResolver,
const std::shared_ptr<Connection>& connection)
{
const auto& qt = parseQueryTemplate(nullptr, query, {}, false);
const auto& qt = parseQueryTemplate(nullptr, query, ParamsTypeMap(), false);
return execute(qt, params, typeResolver, connection);
}

View File

@ -72,7 +72,7 @@ void SchemaMigration::migrate() {
break;
case SOURCE_FILE:
script = utils::String::loadFromFile(source.param->c_str());
script = oatpp::String::loadFromFile(source.param->c_str());
break;
default:

View File

@ -29,7 +29,7 @@
namespace oatpp { namespace parser { namespace json{
v_buff_size Utils::calcEscapedStringSize(const char* data, v_buff_size size, v_buff_size& safeSize) {
v_buff_size Utils::calcEscapedStringSize(const char* data, v_buff_size size, v_buff_size& safeSize, v_uint32 flags) {
v_buff_size result = 0;
v_buff_size i = 0;
safeSize = size;
@ -37,18 +37,39 @@ v_buff_size Utils::calcEscapedStringSize(const char* data, v_buff_size size, v_b
v_char8 a = data[i];
if(a < 32) {
i ++;
if(a == '\b' || a == '\f' || a == '\n' || a == '\r' || a == '\t'){
result += 2; // '\n'
} else {
result += 6; // '\uFFFF' - 6 chars
switch (a) {
case '\b':
case '\f':
case '\n':
case '\r':
case '\t': result += 2; break; // '\n'
default:
result += 6; // '\uFFFF' - 6 chars
break;
}
} else if(a < 128){
i ++;
if(a == '\"' || a == '\\' || a == '/'){
result += 2; // '\/'
} else {
result ++;
switch (a) {
case '\"':
case '\\': result += 2; break; // '\/'
case '/':
result ++;
if((flags & FLAG_ESCAPE_SOLIDUS) > 0) result ++;
break;
default:
result ++;
break;
}
} else {
v_buff_size charSize = oatpp::encoding::Unicode::getUtf8CharSequenceLength(a);
if(charSize != 0) {
@ -195,10 +216,10 @@ v_buff_size Utils::escapeUtf8Char(const char* sequence, p_char8 buffer){
return 11;
}
}
oatpp::String Utils::escapeString(const char* data, v_buff_size size) {
oatpp::String Utils::escapeString(const char* data, v_buff_size size, v_uint32 flags) {
v_buff_size safeSize;
v_buff_size escapedSize = calcEscapedStringSize(data, size, safeSize);
v_buff_size escapedSize = calcEscapedStringSize(data, size, safeSize, flags);
if(escapedSize == size) {
return String((const char*)data, size);
}
@ -211,43 +232,51 @@ oatpp::String Utils::escapeString(const char* data, v_buff_size size) {
while (i < safeSize) {
v_char8 a = data[i];
if (a < 32) {
if (a == '\b') {
resultData[pos] = '\\'; resultData[pos + 1] = 'b'; pos += 2;
}
else if (a == '\f') {
resultData[pos] = '\\'; resultData[pos + 1] = 'f'; pos += 2;
}
else if (a == '\n') {
resultData[pos] = '\\'; resultData[pos + 1] = 'n'; pos += 2;
}
else if (a == '\r') {
resultData[pos] = '\\'; resultData[pos + 1] = 'r'; pos += 2;
}
else if (a == '\t') {
resultData[pos] = '\\'; resultData[pos + 1] = 't'; pos += 2;
}
else {
resultData[pos] = '\\';
resultData[pos + 1] = 'u';
oatpp::encoding::Hex::writeUInt16(a, &resultData[pos + 2]);
pos += 6;
switch (a) {
case '\b': resultData[pos] = '\\'; resultData[pos + 1] = 'b'; pos += 2; break;
case '\f': resultData[pos] = '\\'; resultData[pos + 1] = 'f'; pos += 2; break;
case '\n': resultData[pos] = '\\'; resultData[pos + 1] = 'n'; pos += 2; break;
case '\r': resultData[pos] = '\\'; resultData[pos + 1] = 'r'; pos += 2; break;
case '\t': resultData[pos] = '\\'; resultData[pos + 1] = 't'; pos += 2; break;
default:
resultData[pos] = '\\';
resultData[pos + 1] = 'u';
oatpp::encoding::Hex::writeUInt16(a, &resultData[pos + 2]);
pos += 6;
break;
}
i++;
}
else if (a < 128) {
if (a == '\"') {
resultData[pos] = '\\'; resultData[pos + 1] = '"'; pos += 2;
}
else if (a == '\\') {
resultData[pos] = '\\'; resultData[pos + 1] = '\\'; pos += 2;
}
else if (a == '/') {
resultData[pos] = '\\'; resultData[pos + 1] = '/'; pos += 2;
}
else {
resultData[pos] = data[i];
pos++;
switch (a) {
case '\"': resultData[pos] = '\\'; resultData[pos + 1] = '"'; pos += 2; break;
case '\\': resultData[pos] = '\\'; resultData[pos + 1] = '\\'; pos += 2; break;
case '/':
if((flags & FLAG_ESCAPE_SOLIDUS) > 0) {
resultData[pos] = '\\';
resultData[pos + 1] = '/';
pos += 2;
} else {
resultData[pos] = data[i];
pos++;
}
break;
default:
resultData[pos] = data[i];
pos++;
break;
}
i++;
}
else {

View File

@ -37,6 +37,12 @@ namespace oatpp { namespace parser { namespace json {
* Used by &id:oatpp::parser::json::mapping::Serializer;, &id:oatpp::parser::json::mapping::Deserializer;.
*/
class Utils {
public:
static constexpr v_uint32 FLAG_ESCAPE_SOLIDUS = 1;
static constexpr v_uint32 FLAG_ESCAPE_ALL = FLAG_ESCAPE_SOLIDUS;
public:
/**
@ -60,7 +66,7 @@ public:
typedef oatpp::parser::Caret ParsingCaret;
private:
static v_buff_size escapeUtf8Char(const char* sequence, p_char8 buffer);
static v_buff_size calcEscapedStringSize(const char* data, v_buff_size size, v_buff_size& safeSize);
static v_buff_size calcEscapedStringSize(const char* data, v_buff_size size, v_buff_size& safeSize, v_uint32 flags);
static v_buff_size calcUnescapedStringSize(const char* data, v_buff_size size, v_int64& errorCode, v_buff_size& errorPosition);
static void unescapeStringToBuffer(const char* data, v_buff_size size, p_char8 resultData);
static const char* preparseString(ParsingCaret& caret, v_buff_size& size);
@ -71,9 +77,10 @@ public:
* *Note:* if(copyAsOwnData == false && escapedString == initialString) then result string will point to initial data.
* @param data - pointer to string to escape.
* @param size - data size.
* @param flags - escape flags.
* @return - &id:oatpp::String;.
*/
static String escapeString(const char* data, v_buff_size size);
static String escapeString(const char* data, v_buff_size size, v_uint32 flags = FLAG_ESCAPE_ALL);
/**
* Unescape string as for json standard.

View File

@ -75,8 +75,8 @@ void Serializer::setSerializerMethod(const data::mapping::type::ClassId& classId
}
}
void Serializer::serializeString(data::stream::ConsistentOutputStream* stream, const char* data, v_buff_size size) {
auto encodedValue = Utils::escapeString(data, size);
void Serializer::serializeString(data::stream::ConsistentOutputStream* stream, const char* data, v_buff_size size, v_uint32 escapeFlags) {
auto encodedValue = Utils::escapeString(data, size, escapeFlags);
stream->writeCharSimple('\"');
stream->writeSimple(encodedValue);
stream->writeCharSimple('\"');
@ -87,8 +87,6 @@ void Serializer::serializeString(Serializer* serializer,
const oatpp::Void& polymorph)
{
(void) serializer;
if(!polymorph) {
stream->writeSimple("null", 4);
return;
@ -96,7 +94,7 @@ void Serializer::serializeString(Serializer* serializer,
auto str = static_cast<std::string*>(polymorph.get());
serializeString(stream, str->data(), str->size());
serializeString(stream, str->data(), str->size(), serializer->m_config->escapeFlags);
}
@ -161,9 +159,9 @@ void Serializer::serializeObject(Serializer* serializer,
for (auto const& field : fields) {
auto value = field->get(object);
if(value || serializer->getConfig()->includeNullFields) {
if(value || serializer->m_config->includeNullFields) {
(first) ? first = false : stream->writeSimple(",", 1);
serializeString(stream, field->name, std::strlen(field->name));
serializeString(stream, field->name, std::strlen(field->name), serializer->m_config->escapeFlags);
stream->writeSimple(":", 1);
serializer->serialize(stream, value);
}

View File

@ -25,6 +25,7 @@
#ifndef oatpp_parser_json_mapping_Serializer_hpp
#define oatpp_parser_json_mapping_Serializer_hpp
#include "oatpp/parser/json/Utils.hpp"
#include "oatpp/parser/json/Beautifier.hpp"
#include "oatpp/core/Types.hpp"
#include <vector>
@ -96,6 +97,11 @@ public:
*/
std::vector<std::string> enabledInterpretations = {};
/**
* Escape flags.
*/
v_uint32 escapeFlags = json::Utils::FLAG_ESCAPE_ALL;
};
public:
typedef void (*SerializerMethod)(Serializer*,
@ -155,10 +161,10 @@ private:
for(auto& pair : *map) {
const auto& value = pair.second;
if(value || serializer->getConfig()->includeNullFields) {
if(value || serializer->m_config->includeNullFields) {
(first) ? first = false : stream->writeSimple(",", 1);
const auto& key = pair.first;
serializeString(stream, key->data(), key->size());
serializeString(stream, key->data(), key->size(), serializer->m_config->escapeFlags);
stream->writeSimple(":", 1);
serializer->serialize(stream, value);
}
@ -167,8 +173,12 @@ private:
stream->writeCharSimple('}');
}
static void serializeString(oatpp::data::stream::ConsistentOutputStream* stream,
const char* data,
v_buff_size size,
v_uint32 escapeFlags);
static void serializeString(oatpp::data::stream::ConsistentOutputStream* stream, const char* data, v_buff_size size);
static void serializeString(Serializer* serializer,
data::stream::ConsistentOutputStream* stream,
const oatpp::Void& polymorph);

View File

@ -37,6 +37,85 @@
namespace oatpp { namespace web { namespace client {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HttpRequestExecutor::ConnectionProxy
HttpRequestExecutor::ConnectionProxy::ConnectionProxy(const std::shared_ptr<ClientConnectionProvider>& connectionProvider,
const std::shared_ptr<data::stream::IOStream>& connection)
: m_connectionProvider(connectionProvider)
, m_connection(connection)
, m_valid(true)
, m_invalidateOnDestroy(false)
{}
HttpRequestExecutor::ConnectionProxy::~ConnectionProxy() {
if(m_invalidateOnDestroy) {
invalidate();
}
}
v_io_size HttpRequestExecutor::ConnectionProxy::read(void *buffer, v_buff_size count, async::Action& action) {
return m_connection->read(buffer, count, action);
}
v_io_size HttpRequestExecutor::ConnectionProxy::write(const void *data, v_buff_size count, async::Action& action) {
return m_connection->write(data,count, action);
}
void HttpRequestExecutor::ConnectionProxy::setInputStreamIOMode(data::stream::IOMode ioMode) {
m_connection->setInputStreamIOMode(ioMode);
}
data::stream::IOMode HttpRequestExecutor::ConnectionProxy::getInputStreamIOMode() {
return m_connection->getInputStreamIOMode();
}
data::stream::Context& HttpRequestExecutor::ConnectionProxy::getInputStreamContext() {
return m_connection->getInputStreamContext();
}
void HttpRequestExecutor::ConnectionProxy::setOutputStreamIOMode(data::stream::IOMode ioMode) {
return m_connection->setOutputStreamIOMode(ioMode);
}
data::stream::IOMode HttpRequestExecutor::ConnectionProxy::getOutputStreamIOMode() {
return m_connection->getOutputStreamIOMode();
}
data::stream::Context& HttpRequestExecutor::ConnectionProxy::getOutputStreamContext() {
return m_connection->getOutputStreamContext();
}
void HttpRequestExecutor::ConnectionProxy::invalidate() {
if(m_valid) {
m_connectionProvider->invalidate(m_connection);
m_valid = false;
}
}
void HttpRequestExecutor::ConnectionProxy::setInvalidateOnDestroy(bool invalidateOnDestroy) {
m_invalidateOnDestroy = invalidateOnDestroy;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HttpRequestExecutor::HttpConnectionHandle
HttpRequestExecutor::HttpConnectionHandle::HttpConnectionHandle(const std::shared_ptr<ConnectionProxy>& connectionProxy)
: m_connectionProxy(connectionProxy)
{}
std::shared_ptr<HttpRequestExecutor::ConnectionProxy> HttpRequestExecutor::HttpConnectionHandle::getConnection() {
return m_connectionProxy;
}
void HttpRequestExecutor::HttpConnectionHandle::invalidate() {
m_connectionProxy->invalidate();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HttpRequestExecutor
HttpRequestExecutor::HttpRequestExecutor(const std::shared_ptr<ClientConnectionProvider>& connectionProvider,
const std::shared_ptr<RetryPolicy>& retryPolicy,
const std::shared_ptr<const BodyDecoder>& bodyDecoder)
@ -59,7 +138,8 @@ std::shared_ptr<HttpRequestExecutor::ConnectionHandle> HttpRequestExecutor::getC
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_CONNECT,
"[oatpp::web::client::HttpRequestExecutor::getConnection()]: ConnectionProvider failed to provide Connection");
}
return std::make_shared<HttpConnectionHandle>(connection);
auto connectionProxy = std::make_shared<ConnectionProxy>(m_connectionProvider, connection);
return std::make_shared<HttpConnectionHandle>(connectionProxy);
}
oatpp::async::CoroutineStarterForResult<const std::shared_ptr<HttpRequestExecutor::ConnectionHandle>&>
@ -79,7 +159,8 @@ HttpRequestExecutor::getConnectionAsync() {
}
Action onConnectionReady(const std::shared_ptr<oatpp::data::stream::IOStream>& connection) {
return _return(std::make_shared<HttpConnectionHandle>(connection));
auto connectionProxy = std::make_shared<ConnectionProxy>(m_connectionProvider, connection);
return _return(std::make_shared<HttpConnectionHandle>(connectionProxy));
}
};
@ -91,8 +172,8 @@ HttpRequestExecutor::getConnectionAsync() {
void HttpRequestExecutor::invalidateConnection(const std::shared_ptr<ConnectionHandle>& connectionHandle) {
if(connectionHandle) {
auto connection = static_cast<HttpConnectionHandle*>(connectionHandle.get())->connection;
m_connectionProvider->invalidate(connection);
auto handle = static_cast<HttpConnectionHandle*>(connectionHandle.get());
handle->invalidate();
}
}
@ -104,9 +185,10 @@ HttpRequestExecutor::executeOnce(const String& method,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle) {
std::shared_ptr<oatpp::data::stream::IOStream> connection;
if(connectionHandle) {
connection = static_cast<HttpConnectionHandle*>(connectionHandle.get())->connection;
std::shared_ptr<ConnectionProxy> connection;
std::shared_ptr<HttpConnectionHandle> httpCH = std::static_pointer_cast<HttpConnectionHandle>(connectionHandle);
if(httpCH) {
connection = httpCH->getConnection();
}
if(!connection){
@ -132,23 +214,28 @@ HttpRequestExecutor::executeOnce(const String& method,
const auto& result = headerReader.readHeaders(connection, error);
if(error.status.code != 0) {
invalidateConnection(connectionHandle);
connection->invalidate();
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::executeOnce()]: Failed to parse response. Invalid response headers");
}
if(error.ioStatus < 0) {
invalidateConnection(connectionHandle);
connection->invalidate();
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::executeOnce()]: Failed to read response.");
}
auto connectionHeader = result.headers.getAsMemoryLabel<oatpp::data::share::StringKeyLabelCI>(Header::CONNECTION);
if (connectionHeader == "close") {
connection->setInvalidateOnDestroy(true);
}
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(connection,
buffer,
result.bufferPosStart,
result.bufferPosEnd,
result.bufferPosStart != result.bufferPosEnd);
return Response::createShared(result.startingLine.statusCode,
result.startingLine.description.toString(),
result.headers, bodyStream, m_bodyDecoder);
@ -174,12 +261,12 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
Headers m_headers;
std::shared_ptr<Body> m_body;
std::shared_ptr<const BodyDecoder> m_bodyDecoder;
std::shared_ptr<ConnectionHandle> m_connectionHandle;
std::shared_ptr<HttpConnectionHandle> m_connectionHandle;
oatpp::data::share::MemoryLabel m_buffer;
ResponseHeadersReader m_headersReader;
std::shared_ptr<oatpp::data::stream::OutputStreamBufferedProxy> m_upstream;
private:
std::shared_ptr<oatpp::data::stream::IOStream> m_connection;
std::shared_ptr<ConnectionProxy> m_connection;
public:
ExecutorCoroutine(HttpRequestExecutor* _this,
@ -188,7 +275,7 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<const BodyDecoder>& bodyDecoder,
const std::shared_ptr<ConnectionHandle>& connectionHandle)
const std::shared_ptr<HttpConnectionHandle>& connectionHandle)
: m_this(_this)
, m_method(method)
, m_path(path)
@ -203,7 +290,7 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
Action act() override {
if(m_connectionHandle) {
m_connection = static_cast<HttpConnectionHandle*>(m_connectionHandle.get())->connection;
m_connection = m_connectionHandle->getConnection();
}
if(!m_connection) {
@ -227,7 +314,12 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
}
Action onHeadersParsed(const ResponseHeadersReader::Result& result) {
auto connectionHeader = result.headers.getAsMemoryLabel<oatpp::data::share::StringKeyLabelCI>(Header::CONNECTION);
if (connectionHeader == "close") {
m_connection->setInvalidateOnDestroy(true);
}
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(m_connection,
m_buffer,
result.bufferPosStart,
@ -242,8 +334,8 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
Action handleError(oatpp::async::Error* error) override {
if(m_connectionHandle) {
m_this->invalidateConnection(m_connectionHandle);
if(m_connection) {
m_connection->invalidate();
}
return error;
@ -251,8 +343,9 @@ HttpRequestExecutor::executeOnceAsync(const String& method,
}
};
return ExecutorCoroutine::startForResult(this, method, path, headers, body, m_bodyDecoder, connectionHandle);
auto httpCH = std::static_pointer_cast<HttpConnectionHandle>(connectionHandle);
return ExecutorCoroutine::startForResult(this, method, path, headers, body, m_bodyDecoder, httpCH);
}

View File

@ -44,6 +44,38 @@ private:
protected:
std::shared_ptr<ClientConnectionProvider> m_connectionProvider;
std::shared_ptr<const BodyDecoder> m_bodyDecoder;
public:
class ConnectionProxy : public data::stream::IOStream {
private:
/* provider which created this connection */
std::shared_ptr<ClientConnectionProvider> m_connectionProvider;
std::shared_ptr<data::stream::IOStream> m_connection;
bool m_valid;
bool m_invalidateOnDestroy;
public:
ConnectionProxy(const std::shared_ptr<ClientConnectionProvider>& connectionProvider,
const std::shared_ptr<data::stream::IOStream>& connection);
~ConnectionProxy() override;
v_io_size read(void *buffer, v_buff_size count, async::Action& action) override;
v_io_size write(const void *data, v_buff_size count, async::Action& action) override;
void setInputStreamIOMode(data::stream::IOMode ioMode) override;
data::stream::IOMode getInputStreamIOMode() override;
data::stream::Context& getInputStreamContext() override;
void setOutputStreamIOMode(data::stream::IOMode ioMode) override;
data::stream::IOMode getOutputStreamIOMode() override;
data::stream::Context& getOutputStreamContext() override;
void invalidate();
void setInvalidateOnDestroy(bool invalidateOnDestroy);
};
public:
/**
@ -51,20 +83,16 @@ public:
* For more details see &id:oatpp::web::client::RequestExecutor::ConnectionHandle;.
*/
class HttpConnectionHandle : public ConnectionHandle {
private:
std::shared_ptr<ConnectionProxy> m_connectionProxy;
public:
/**
* Constructor.
* @param stream - &id:oatpp::data::stream::IOStream;.
*/
HttpConnectionHandle(const std::shared_ptr<oatpp::data::stream::IOStream>& stream)
: connection(stream)
{}
HttpConnectionHandle(const std::shared_ptr<ConnectionProxy>& connectionProxy);
std::shared_ptr<ConnectionProxy> getConnection();
void invalidate();
/**
* Connection.
*/
std::shared_ptr<oatpp::data::stream::IOStream> connection;
};
public:
@ -107,7 +135,7 @@ public:
* Invalidate connection.
* @param connectionHandle
*/
virtual void invalidateConnection(const std::shared_ptr<ConnectionHandle>& connectionHandle) override;
void invalidateConnection(const std::shared_ptr<ConnectionHandle>& connectionHandle) override;
/**
* Execute http request.

View File

@ -33,7 +33,7 @@ namespace oatpp { namespace web { namespace mime { namespace multipart {
Part::Part(const Headers &headers,
const std::shared_ptr<data::stream::InputStream> &inputStream,
const oatpp::String inMemoryData,
const oatpp::String& inMemoryData,
v_int64 knownSize)
: m_headers(headers)
, m_inputStream(inputStream)
@ -60,7 +60,7 @@ Part::Part(const Headers& headers) : Part(headers, nullptr, nullptr, -1) {}
Part::Part() : Part(Headers(), nullptr, nullptr, -1) {}
void Part::setDataInfo(const std::shared_ptr<data::stream::InputStream>& inputStream,
const oatpp::String inMemoryData,
const oatpp::String& inMemoryData,
v_int64 knownSize)
{
m_inputStream = inputStream;

View File

@ -61,7 +61,7 @@ public:
*/
Part(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& inputStream,
const oatpp::String inMemoryData,
const oatpp::String& inMemoryData,
v_int64 knownSize);
/**
@ -82,7 +82,7 @@ public:
* @param knownSize - known size of the data in the input stream. Pass `-1` value if size is unknown.
*/
void setDataInfo(const std::shared_ptr<data::stream::InputStream>& inputStream,
const oatpp::String inMemoryData,
const oatpp::String& inMemoryData,
v_int64 knownSize);
/**

View File

@ -31,7 +31,6 @@
#include "oatpp/core/parser/Caret.hpp"
#include "oatpp/core/data/share/LazyStringMap.hpp"
#include "oatpp/core/collection/ListMap.hpp"
#include "oatpp/core/Types.hpp"
#include <unordered_map>

View File

@ -101,6 +101,15 @@ bool Request::putHeaderIfNotExists(const oatpp::String& key, const oatpp::String
return m_headers.putIfNotExists(key, value);
}
bool Request::putOrReplaceHeader(const String &key, const String &value) {
return m_headers.putOrReplace(key, value);
}
bool Request::putOrReplaceHeader_Unsafe(const data::share::StringKeyLabelCI& key,
const data::share::StringKeyLabel &value) {
return m_headers.putOrReplace(key, value);
}
void Request::putHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers.put(key, value);
}

View File

@ -154,6 +154,22 @@ public:
*/
bool putHeaderIfNotExists(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::String;.
* @param value - &id:oatpp::String;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.
* @param value - &id:oatpp::data::share::StringKeyLabel;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value);
/**
* Add http header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.

View File

@ -66,6 +66,15 @@ bool Response::putHeaderIfNotExists(const oatpp::String& key, const oatpp::Strin
return m_headers.putIfNotExists(key, value);
}
bool Response::putOrReplaceHeader(const String &key, const String &value) {
return m_headers.putOrReplace(key, value);
}
bool Response::putOrReplaceHeader_Unsafe(const data::share::StringKeyLabelCI& key,
const data::share::StringKeyLabel &value) {
return m_headers.putOrReplace(key, value);
}
void Response::putHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers.put(key, value);
}

View File

@ -116,6 +116,22 @@ public:
*/
bool putHeaderIfNotExists(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::String;.
* @param value - &id:oatpp::String;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.
* @param value - &id:oatpp::data::share::StringKeyLabel;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value);
/**
* Add http header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.

View File

@ -28,7 +28,6 @@
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/core/data/stream/Stream.hpp"
#include "oatpp/core/collection/ListMap.hpp"
#include "oatpp/core/async/Coroutine.hpp"
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
@ -70,7 +69,7 @@ public:
* If body size is unknown then should return -1.
* @return - &id:oatpp::v_io_size;.
*/
virtual v_buff_size getKnownSize() = 0;
virtual v_int64 getKnownSize() = 0;
};

View File

@ -26,23 +26,24 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
BufferBody::BufferBody(const oatpp::String& buffer, const data::share::StringKeyLabel& contentType)
BufferBody::BufferBody(const oatpp::String &buffer, const data::share::StringKeyLabel &contentType)
: m_buffer(buffer)
, m_contentType(contentType)
, m_inlineData((void*) m_buffer->data(), m_buffer->size())
{}
std::shared_ptr<BufferBody> BufferBody::createShared(const oatpp::String& buffer, const data::share::StringKeyLabel& contentType) {
std::shared_ptr<BufferBody> BufferBody::createShared(const oatpp::String &buffer,
const data::share::StringKeyLabel &contentType) {
return Shared_Http_Outgoing_BufferBody_Pool::allocateShared(buffer, contentType);
}
v_io_size BufferBody::read(void *buffer, v_buff_size count, async::Action& action) {
v_io_size BufferBody::read(void *buffer, v_buff_size count, async::Action &action) {
(void) action;
v_buff_size desiredToRead = m_inlineData.bytesLeft;
if(desiredToRead > 0) {
if (desiredToRead > 0) {
if (desiredToRead > count) {
desiredToRead = count;
@ -59,9 +60,9 @@ v_io_size BufferBody::read(void *buffer, v_buff_size count, async::Action& actio
}
void BufferBody::declareHeaders(Headers& headers) {
if(m_contentType) {
headers.put(Header::CONTENT_TYPE, m_contentType);
void BufferBody::declareHeaders(Headers &headers) {
if (m_contentType) {
headers.putIfNotExists(Header::CONTENT_TYPE, m_contentType);
}
}
@ -69,7 +70,7 @@ p_char8 BufferBody::getKnownData() {
return (p_char8) m_buffer->data();
}
v_buff_size BufferBody::getKnownSize() {
v_int64 BufferBody::getKnownSize() {
return m_buffer->size();
}

View File

@ -81,7 +81,7 @@ public:
* Return known size of the body.
* @return - `v_buff_size`.
*/
v_buff_size getKnownSize() override;
v_int64 getKnownSize() override;
};

View File

@ -191,7 +191,7 @@ p_char8 MultipartBody::getKnownData() {
return nullptr;
}
v_buff_size MultipartBody::getKnownSize() {
v_int64 MultipartBody::getKnownSize() {
return -1;
}

View File

@ -166,7 +166,7 @@ public:
* Always returns `-1` - as body size is unknown.
* @return - `-1`. `v_buff_size`.
*/
v_buff_size getKnownSize() override;
v_int64 getKnownSize() override;
};

View File

@ -68,6 +68,15 @@ bool Request::putHeaderIfNotExists(const oatpp::String& key, const oatpp::String
return m_headers.putIfNotExists(key, value);
}
bool Request::putOrReplaceHeader(const String &key, const String &value) {
return m_headers.putOrReplace(key, value);
}
bool Request::putOrReplaceHeader_Unsafe(const data::share::StringKeyLabelCI& key,
const data::share::StringKeyLabel &value) {
return m_headers.putOrReplace(key, value);
}
void Request::putHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers.put(key, value);
}
@ -158,14 +167,14 @@ oatpp::async::CoroutineStarter Request::sendAsync(std::shared_ptr<Request> _this
std::shared_ptr<oatpp::data::stream::BufferOutputStream> m_headersWriteBuffer;
public:
SendAsyncCoroutine(const std::shared_ptr<Request>& request,
SendAsyncCoroutine(std::shared_ptr<Request> request,
const std::shared_ptr<data::stream::OutputStream>& stream)
: m_this(request)
: m_this(std::move(request))
, m_stream(stream)
, m_headersWriteBuffer(std::make_shared<oatpp::data::stream::BufferOutputStream>())
{}
Action act() {
Action act() override {
v_buff_size bodySize = -1;
@ -231,7 +240,7 @@ oatpp::async::CoroutineStarter Request::sendAsync(std::shared_ptr<Request> _this
};
return SendAsyncCoroutine::start(_this, stream);
return SendAsyncCoroutine::start(std::move(_this), stream);
}

View File

@ -109,6 +109,22 @@ public:
*/
bool putHeaderIfNotExists(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::String;.
* @param value - &id:oatpp::String;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.
* @param value - &id:oatpp::data::share::StringKeyLabel;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value);
/**
* Add http header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.

View File

@ -48,6 +48,10 @@ protocol::http::Headers& Response::getHeaders() {
return m_headers;
}
std::shared_ptr<Body> Response::getBody() const {
return m_body;
}
void Response::putHeader(const oatpp::String& key, const oatpp::String& value) {
m_headers.put(key, value);
}
@ -56,6 +60,15 @@ bool Response::putHeaderIfNotExists(const oatpp::String& key, const oatpp::Strin
return m_headers.putIfNotExists(key, value);
}
bool Response::putOrReplaceHeader(const String &key, const String &value) {
return m_headers.putOrReplace(key, value);
}
bool Response::putOrReplaceHeader_Unsafe(const data::share::StringKeyLabelCI& key,
const data::share::StringKeyLabel &value) {
return m_headers.putOrReplace(key, value);
}
void Response::putHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers.put(key, value);
}
@ -89,7 +102,7 @@ void Response::send(data::stream::OutputStream* stream,
http::encoding::EncoderProvider* contentEncoderProvider)
{
v_buff_size bodySize = -1;
v_int64 bodySize = -1;
if(m_body){
@ -132,14 +145,20 @@ void Response::send(data::stream::OutputStream* stream,
if (bodySize >= 0) {
if (bodySize + headersWriteBuffer->getCurrentPosition() < headersWriteBuffer->getCapacity()) {
headersWriteBuffer->writeSimple(m_body->getKnownData(), bodySize);
if(m_body->getKnownData() == nullptr) {
headersWriteBuffer->flushToStream(stream);
} else {
headersWriteBuffer->flushToStream(stream);
stream->writeExactSizeDataSimple(m_body->getKnownData(), bodySize);
/* Reuse headers buffer */
/* Transfer without chunked encoder */
data::stream::transfer(m_body, stream, 0, headersWriteBuffer->getData(), headersWriteBuffer->getCapacity());
} else {
if (bodySize + headersWriteBuffer->getCurrentPosition() < headersWriteBuffer->getCapacity()) {
headersWriteBuffer->writeSimple(m_body->getKnownData(), bodySize);
headersWriteBuffer->flushToStream(stream);
} else {
headersWriteBuffer->flushToStream(stream);
stream->writeExactSizeDataSimple(m_body->getKnownData(), bodySize);
}
}
} else {
headersWriteBuffer->flushToStream(stream);

View File

@ -89,6 +89,12 @@ public:
*/
Headers& getHeaders();
/**
* Get body
* @return - &id:oatpp::web::protocol::http::outgoing::Body;
*/
std::shared_ptr<Body> getBody() const;
/**
* Add http header.
* @param key - &id:oatpp::String;.
@ -104,6 +110,22 @@ public:
*/
bool putHeaderIfNotExists(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::String;.
* @param value - &id:oatpp::String;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader(const oatpp::String& key, const oatpp::String& value);
/**
* Replaces or adds header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.
* @param value - &id:oatpp::data::share::StringKeyLabel;.
* @return - `true` if header was replaces, `false` if header was added.
*/
bool putOrReplaceHeader_Unsafe(const oatpp::data::share::StringKeyLabelCI& key, const oatpp::data::share::StringKeyLabel& value);
/**
* Add http header.
* @param key - &id:oatpp::data::share::StringKeyLabelCI;.

View File

@ -44,7 +44,7 @@ p_char8 StreamingBody::getKnownData() {
}
v_buff_size StreamingBody::getKnownSize() {
v_int64 StreamingBody::getKnownSize() {
return -1;
}

View File

@ -69,7 +69,7 @@ public:
* Return known size of the body.
* @return - `-1`.
*/
v_buff_size getKnownSize() override;
v_int64 getKnownSize() override;
};

View File

@ -55,7 +55,7 @@ void CommunicationUtils::considerConnectionState(const std::shared_ptr<protocol:
/* Set HTTP/1.1 default Connection header value (Keep-Alive), if no Connection header present in response. */
/* Set keep-alive to value specified in response otherwise */
auto& protocol = request->getStartingLine().protocol;
if(protocol && oatpp::utils::String::compareCI(protocol.getData(), protocol.getSize(), "HTTP/1.1", 8) == 0) {
if(protocol && oatpp::utils::String::compareCI_ASCII(protocol.getData(), protocol.getSize(), "HTTP/1.1", 8) == 0) {
if(outState && outState != Header::Value::CONNECTION_KEEP_ALIVE) {
connectionState = ConnectionState::CLOSING;
}

View File

@ -28,12 +28,9 @@
namespace oatpp { namespace web { namespace server { namespace api {
void ApiController::addEndpointsToRouter(const std::shared_ptr<Router>& router){
auto node = m_endpoints->getFirstNode();
while (node != nullptr) {
auto endpoint = node->getData();
for (const auto& endpoint : *m_endpoints) {
router->route(endpoint->info()->method, endpoint->info()->path, endpoint->handler);
node = node->getNext();
}
};
}
std::shared_ptr<ApiController::Endpoints> ApiController::getEndpoints() {

View File

@ -36,9 +36,9 @@
#include "oatpp/web/protocol/http/outgoing/Request.hpp"
#include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
#include "oatpp/core/utils/ConversionUtils.hpp"
#include <list>
#include <unordered_map>
namespace oatpp { namespace web { namespace server { namespace api {
@ -104,7 +104,7 @@ public:
/**
* Convenience typedef for list of &id:oatpp::web::server::api::Endpoint;.
*/
typedef oatpp::collection::LinkedList<std::shared_ptr<Endpoint>> Endpoints;
typedef std::list<std::shared_ptr<Endpoint>> Endpoints;
/**
* Convenience typedef for &id:oatpp::web::server::HttpRequestHandler;.
@ -369,7 +369,7 @@ protected:
std::shared_ptr<RequestHandler> getEndpointHandler(const std::string& endpointName);
protected:
std::shared_ptr<Endpoints> m_endpoints;
std::shared_ptr<Endpoints> m_endpoints{std::make_shared<Endpoints>()};
std::shared_ptr<handler::ErrorHandler> m_errorHandler;
std::shared_ptr<handler::AuthorizationHandler> m_defaultAuthorizationHandler;
std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_defaultObjectMapper;
@ -378,9 +378,7 @@ protected:
const oatpp::String m_routerPrefix;
public:
ApiController(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& defaultObjectMapper, const oatpp::String &routerPrefix = nullptr)
: m_endpoints(Endpoints::createShared())
, m_errorHandler(nullptr)
, m_defaultObjectMapper(defaultObjectMapper)
: m_defaultObjectMapper(defaultObjectMapper)
, m_routerPrefix(routerPrefix)
{}
public:
@ -391,7 +389,7 @@ public:
const EndpointInfoBuilder& infoBuilder)
{
auto endpoint = Endpoint::createShared(handler, infoBuilder);
endpoints->pushBack(endpoint);
endpoints->push_back(endpoint);
return endpoint;
}

Some files were not shown because too many files have changed in this diff Show More