Merge pull request #27 from oatpp/virtual_connection

Virtual connection
This commit is contained in:
Leonid Stryzhevskyi 2018-12-16 14:36:56 +02:00 committed by GitHub
commit 49e63fbf15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 3349 additions and 653 deletions

View File

@ -18,25 +18,27 @@ add_library(oatpp
codegen/codegen_undef_ApiClient_.hpp
codegen/codegen_undef_ApiController_.hpp
codegen/codegen_undef_DTO_.hpp
core/Types.cpp
core/Types.hpp
core/async/Coroutine.cpp
core/async/Coroutine.hpp
core/async/Processor.cpp
core/async/Processor.hpp
core/base/CommandLineArguments.cpp
core/base/CommandLineArguments.hpp
core/base/Config.hpp
core/base/Controllable.cpp
core/base/Controllable.hpp
core/base/Environment.cpp
core/base/Environment.hpp
core/base/StrBuffer.cpp
core/base/StrBuffer.hpp
core/base/memory/Allocator.cpp
core/base/memory/Allocator.hpp
core/base/memory/MemoryPool.cpp
core/base/memory/MemoryPool.hpp
core/base/memory/ObjectPool.cpp
core/base/memory/ObjectPool.hpp
core/base/StrBuffer.cpp
core/base/StrBuffer.hpp
core/base/CommandLineArguments.cpp
core/base/CommandLineArguments.hpp
core/collection/FastQueue.cpp
core/collection/FastQueue.hpp
core/collection/LinkedList.cpp
@ -65,6 +67,8 @@ add_library(oatpp
core/data/mapping/type/Primitive.hpp
core/data/mapping/type/Type.cpp
core/data/mapping/type/Type.hpp
core/data/share/MemoryLabel.cpp
core/data/share/MemoryLabel.hpp
core/data/stream/ChunkedBuffer.cpp
core/data/stream/ChunkedBuffer.hpp
core/data/stream/Delegate.cpp
@ -80,8 +84,6 @@ add_library(oatpp
core/os/io/Library.hpp
core/parser/ParsingCaret.cpp
core/parser/ParsingCaret.hpp
core/Types.cpp
core/Types.hpp
core/utils/ConversionUtils.cpp
core/utils/ConversionUtils.hpp
encoding/Base64.cpp
@ -90,48 +92,60 @@ add_library(oatpp
encoding/Hex.hpp
encoding/Unicode.cpp
encoding/Unicode.hpp
network/client/SimpleTCPConnectionProvider.cpp
network/client/SimpleTCPConnectionProvider.hpp
network/Connection.cpp
network/Connection.hpp
network/ConnectionProvider.cpp
network/ConnectionProvider.hpp
network/Url.cpp
network/Url.hpp
network/client/SimpleTCPConnectionProvider.cpp
network/client/SimpleTCPConnectionProvider.hpp
network/server/ConnectionHandler.cpp
network/server/ConnectionHandler.hpp
network/server/Server.cpp
network/server/Server.hpp
network/server/SimpleTCPConnectionProvider.cpp
network/server/SimpleTCPConnectionProvider.hpp
network/Url.cpp
network/Url.hpp
network/virtual_/Interface.cpp
network/virtual_/Interface.hpp
network/virtual_/Pipe.cpp
network/virtual_/Pipe.hpp
network/virtual_/Socket.cpp
network/virtual_/Socket.hpp
network/virtual_/client/ConnectionProvider.cpp
network/virtual_/client/ConnectionProvider.hpp
network/virtual_/server/ConnectionProvider.cpp
network/virtual_/server/ConnectionProvider.hpp
parser/json/Utils.cpp
parser/json/Utils.hpp
parser/json/mapping/Deserializer.cpp
parser/json/mapping/Deserializer.hpp
parser/json/mapping/ObjectMapper.cpp
parser/json/mapping/ObjectMapper.hpp
parser/json/mapping/Serializer.cpp
parser/json/mapping/Serializer.hpp
parser/json/Utils.cpp
parser/json/Utils.hpp
web/client/ApiClient.cpp
web/client/ApiClient.hpp
web/client/HttpRequestExecutor.cpp
web/client/HttpRequestExecutor.hpp
web/client/RequestExecutor.cpp
web/client/RequestExecutor.hpp
web/protocol/CommunicationError.cpp
web/protocol/CommunicationError.hpp
web/protocol/http/Http.cpp
web/protocol/http/Http.hpp
web/protocol/http/incoming/BodyDecoder.cpp
web/protocol/http/incoming/BodyDecoder.hpp
web/protocol/http/incoming/SimpleBodyDecoder.cpp
web/protocol/http/incoming/SimpleBodyDecoder.hpp
web/protocol/http/incoming/Request.cpp
web/protocol/http/incoming/Request.hpp
web/protocol/http/incoming/RequestHeadersReader.cpp
web/protocol/http/incoming/RequestHeadersReader.hpp
web/protocol/http/incoming/Response.cpp
web/protocol/http/incoming/Response.hpp
web/protocol/http/incoming/ResponseHeadersReader.cpp
web/protocol/http/incoming/ResponseHeadersReader.hpp
web/protocol/http/incoming/SimpleBodyDecoder.cpp
web/protocol/http/incoming/SimpleBodyDecoder.hpp
web/protocol/http/outgoing/Body.cpp
web/protocol/http/outgoing/Body.hpp
web/protocol/http/outgoing/BufferBody.cpp
@ -148,24 +162,22 @@ add_library(oatpp
web/protocol/http/outgoing/Response.hpp
web/protocol/http/outgoing/ResponseFactory.cpp
web/protocol/http/outgoing/ResponseFactory.hpp
web/server/api/ApiController.cpp
web/server/api/ApiController.hpp
web/server/api/Endpoint.cpp
web/server/api/Endpoint.hpp
web/server/AsyncHttpConnectionHandler.cpp
web/server/AsyncHttpConnectionHandler.hpp
web/server/handler/ErrorHandler.cpp
web/server/handler/ErrorHandler.hpp
web/server/handler/Interceptor.cpp
web/server/handler/Interceptor.hpp
web/server/HttpConnectionHandler.cpp
web/server/HttpConnectionHandler.hpp
web/server/HttpError.cpp
web/server/HttpError.hpp
web/server/HttpProcessor.cpp
web/server/HttpProcessor.hpp
web/server/HttpRouter.cpp
web/server/HttpRouter.hpp
web/server/api/ApiController.cpp
web/server/api/ApiController.hpp
web/server/api/Endpoint.cpp
web/server/api/Endpoint.hpp
web/server/handler/ErrorHandler.cpp
web/server/handler/ErrorHandler.hpp
web/server/handler/Interceptor.cpp
web/server/handler/Interceptor.hpp
web/url/mapping/Pattern.cpp
web/url/mapping/Pattern.hpp
web/url/mapping/Router.cpp
@ -241,28 +253,42 @@ if(OATPP_BUILD_TESTS)
test/Checker.hpp
test/UnitTest.cpp
test/UnitTest.hpp
test/core/base/CommandLineArgumentsTest.cpp
test/core/base/CommandLineArgumentsTest.hpp
test/core/base/RegRuleTest.cpp
test/core/base/RegRuleTest.hpp
test/core/base/collection/LinkedListTest.cpp
test/core/base/collection/LinkedListTest.hpp
test/core/base/memory/MemoryPoolTest.cpp
test/core/base/memory/MemoryPoolTest.hpp
test/core/base/memory/PerfTest.cpp
test/core/base/memory/PerfTest.hpp
test/core/base/RegRuleTest.cpp
test/core/base/RegRuleTest.hpp
test/core/base/CommandLineArgumentsTest.cpp
test/core/base/CommandLineArgumentsTest.hpp
test/core/data/mapping/type/TypeTest.cpp
test/core/data/mapping/type/TypeTest.hpp
test/encoding/UnicodeTest.cpp
test/encoding/UnicodeTest.hpp
test/core/data/share/MemoryLabelTest.cpp
test/core/data/share/MemoryLabelTest.hpp
test/encoding/Base64Test.cpp
test/encoding/Base64Test.hpp
test/parser/json/mapping/DeserializerTest.cpp
test/parser/json/mapping/DeserializerTest.hpp
test/encoding/UnicodeTest.cpp
test/encoding/UnicodeTest.hpp
test/network/virtual_/InterfaceTest.cpp
test/network/virtual_/InterfaceTest.hpp
test/network/virtual_/PipeTest.cpp
test/network/virtual_/PipeTest.hpp
test/parser/json/mapping/DTOMapperPerfTest.cpp
test/parser/json/mapping/DTOMapperPerfTest.hpp
test/parser/json/mapping/DTOMapperTest.cpp
test/parser/json/mapping/DTOMapperTest.hpp
test/parser/json/mapping/DeserializerTest.cpp
test/parser/json/mapping/DeserializerTest.hpp
test/web/FullAsyncTest.cpp
test/web/FullAsyncTest.hpp
test/web/FullTest.cpp
test/web/FullTest.hpp
test/web/app/Client.hpp
test/web/app/Controller.hpp
test/web/app/ControllerAsync.hpp
test/web/app/DTOs.hpp
)
target_link_libraries(oatppAllTests PRIVATE oatpp)
set_target_properties(oatppAllTests PROPERTIES

View File

@ -36,7 +36,7 @@
#include <stdlib.h>
#define OATPP_VERSION "0.18.9"
#define OATPP_VERSION "0.18.12"
#define OATPP_ASSERT(EXP) \
if(!(EXP)) { \

View File

@ -196,9 +196,8 @@ public:
T result = node->data;
destroyNode(node);
return result;
}else{
return T::empty();
}
throw std::runtime_error("[oatpp::collection::LinkedList::popFront()]: index out of bounds");
}
const T& getFirst() const{

View File

@ -27,6 +27,7 @@
namespace oatpp { namespace data{ namespace buffer {
os::io::Library::v_size FIFOBuffer::availableToRead() {
oatpp::concurrency::SpinLock lock(m_atom);
if(!m_canRead) {
return 0;
}
@ -37,6 +38,7 @@ os::io::Library::v_size FIFOBuffer::availableToRead() {
}
os::io::Library::v_size FIFOBuffer::availableToWrite() {
oatpp::concurrency::SpinLock lock(m_atom);
if(m_canRead && m_writePosition == m_readPosition) {
return 0;
}
@ -48,6 +50,8 @@ os::io::Library::v_size FIFOBuffer::availableToWrite() {
os::io::Library::v_size FIFOBuffer::read(void *data, os::io::Library::v_size count) {
oatpp::concurrency::SpinLock lock(m_atom);
if(!m_canRead) {
return 0;
}
@ -97,6 +101,8 @@ os::io::Library::v_size FIFOBuffer::read(void *data, os::io::Library::v_size cou
os::io::Library::v_size FIFOBuffer::write(const void *data, os::io::Library::v_size count) {
oatpp::concurrency::SpinLock lock(m_atom);
if(m_canRead && m_writePosition == m_readPosition) {
return 0;
}

View File

@ -26,6 +26,7 @@
#define oatpp_data_buffer_FIFOBuffer_hpp
#include "./IOBuffer.hpp"
#include "oatpp/core/concurrency/SpinLock.hpp"
#include "oatpp/core/os/io/Library.hpp"
namespace oatpp { namespace data{ namespace buffer {
@ -39,11 +40,13 @@ private:
os::io::Library::v_size m_readPosition;
os::io::Library::v_size m_writePosition;
IOBuffer m_buffer;
oatpp::concurrency::SpinLock::Atom m_atom;
public:
FIFOBuffer()
: m_canRead(false)
, m_readPosition(0)
, m_writePosition(0)
, m_atom(false)
{}
public:

View File

@ -0,0 +1,73 @@
/***************************************************************************
*
* 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 "MemoryLabel.hpp"
#include <cstring>
namespace oatpp { namespace data { namespace share {
MemoryLabel::MemoryLabel(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size)
: m_memoryHandle(memHandle)
, m_data(data)
, m_size(size)
{}
StringKeyLabel::StringKeyLabel(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size)
: oatpp::data::share::MemoryLabel(memHandle, data, size)
{}
StringKeyLabel::StringKeyLabel(const char* constText)
: oatpp::data::share::MemoryLabel(nullptr, (p_char8)constText, (v_int32)std::strlen(constText))
{}
StringKeyLabel::StringKeyLabel(const oatpp::String& str)
: oatpp::data::share::MemoryLabel(str.getPtr(), str->getData(), str->getSize())
{}
StringKeyLabelCI::StringKeyLabelCI(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size)
: oatpp::data::share::MemoryLabel(memHandle, data, size)
{}
StringKeyLabelCI::StringKeyLabelCI(const char* constText)
: oatpp::data::share::MemoryLabel(nullptr, (p_char8)constText, (v_int32)std::strlen(constText))
{}
StringKeyLabelCI::StringKeyLabelCI(const oatpp::String& str)
: oatpp::data::share::MemoryLabel(str.getPtr(), str->getData(), str->getSize())
{}
StringKeyLabelCI_FAST::StringKeyLabelCI_FAST(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size)
: oatpp::data::share::MemoryLabel(memHandle, data, size)
{}
StringKeyLabelCI_FAST::StringKeyLabelCI_FAST(const char* constText)
: oatpp::data::share::MemoryLabel(nullptr, (p_char8)constText, (v_int32)std::strlen(constText))
{}
StringKeyLabelCI_FAST::StringKeyLabelCI_FAST(const oatpp::String& str)
: oatpp::data::share::MemoryLabel(str.getPtr(), str->getData(), str->getSize())
{}
}}}

View File

@ -0,0 +1,217 @@
/***************************************************************************
*
* 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_data_share_MemoryLabel_hpp
#define oatpp_data_share_MemoryLabel_hpp
#include "oatpp/core/base/StrBuffer.hpp"
#include "oatpp/core/Types.hpp"
namespace oatpp { namespace data { namespace share {
/**
* MemoryLabel represent a part of the whole memory buffer refered by handle
*/
class MemoryLabel {
protected:
std::shared_ptr<base::StrBuffer> m_memoryHandle;
p_char8 m_data;
v_int32 m_size;
public:
MemoryLabel()
: m_memoryHandle(nullptr)
, m_data(nullptr)
, m_size(0)
{}
MemoryLabel(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size);
p_char8 getData() const {
return m_data;
}
v_int32 getSize() const {
return m_size;
}
bool equals(const char* data) const {
v_int32 size = (v_int32) std::strlen(data);
return m_size == size && base::StrBuffer::equals(m_data, data, m_size);
}
bool equals(const void* data, v_int32 size) const {
return m_size == size && base::StrBuffer::equals(m_data, data, m_size);
}
oatpp::String toString() const {
return oatpp::String((const char*) m_data, m_size, true);
}
std::string toStdString() const {
return std::string((const char*) m_data, m_size);
}
};
class StringKeyLabel : public MemoryLabel {
public:
StringKeyLabel() : MemoryLabel() {};
StringKeyLabel(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size);
StringKeyLabel(const char* constText);
StringKeyLabel(const oatpp::String& str);
bool operator==(const StringKeyLabel &other) const {
return m_size == other.m_size && base::StrBuffer::equals(m_data, other.m_data, m_size);
}
bool operator!=(const StringKeyLabel &other) const {
return !(m_size == other.m_size && base::StrBuffer::equals(m_data, other.m_data, m_size));
}
};
class StringKeyLabelCI : public MemoryLabel {
public:
StringKeyLabelCI() : MemoryLabel() {};
StringKeyLabelCI(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size);
StringKeyLabelCI(const char* constText);
StringKeyLabelCI(const oatpp::String& str);
bool operator==(const StringKeyLabelCI &other) const {
return m_size == other.m_size && base::StrBuffer::equalsCI(m_data, other.m_data, m_size);
}
bool operator!=(const StringKeyLabelCI &other) const {
return !(m_size == other.m_size && base::StrBuffer::equalsCI(m_data, other.m_data, m_size));
}
};
class StringKeyLabelCI_FAST : public MemoryLabel {
public:
StringKeyLabelCI_FAST(const std::shared_ptr<base::StrBuffer>& memHandle, p_char8 data, v_int32 size);
StringKeyLabelCI_FAST(const char* constText);
StringKeyLabelCI_FAST(const oatpp::String& str);
bool operator==(const StringKeyLabelCI_FAST &other) const {
return m_size == other.m_size && base::StrBuffer::equalsCI_FAST(m_data, other.m_data, m_size);
}
bool operator!=(const StringKeyLabelCI_FAST &other) const {
return !(m_size == other.m_size && base::StrBuffer::equalsCI_FAST(m_data, other.m_data, m_size));
}
};
}}}
namespace std {
template<>
struct hash<oatpp::data::share::StringKeyLabel> {
typedef oatpp::data::share::StringKeyLabel argument_type;
typedef v_word32 result_type;
result_type operator()(oatpp::data::share::StringKeyLabel const& s) const noexcept {
p_char8 data = s.getData();
v_int32 size4 = s.getSize() >> 2;
result_type result = 0;
for(v_int32 i = 0; i < size4; i++) {
result ^= *((p_word32) data);
data += 4;
}
for(v_int32 i = 0; i < s.getSize() - (size4 << 2); i++ ) {
((p_char8) &result)[i] ^= data[i];
}
return result;
}
};
template<>
struct hash<oatpp::data::share::StringKeyLabelCI> {
typedef oatpp::data::share::StringKeyLabelCI argument_type;
typedef v_word32 result_type;
result_type operator()(oatpp::data::share::StringKeyLabelCI const& s) const noexcept {
p_char8 data = s.getData();
v_int32 size4 = s.getSize() >> 2;
result_type result = 0;
for(v_int32 i = 0; i < size4; i++) {
result ^= (*((p_word32) data) | 538976288); // 538976288 = 32 | (32 << 8) | (32 << 16) | (32 << 24);
data += 4;
}
for(v_int32 i = 0; i < s.getSize() - (size4 << 2); i++ ) {
((p_char8) &result)[i] ^= (data[i] | 32);
}
return result;
}
};
template<>
struct hash<oatpp::data::share::StringKeyLabelCI_FAST> {
typedef oatpp::data::share::StringKeyLabelCI_FAST argument_type;
typedef v_word32 result_type;
result_type operator()(oatpp::data::share::StringKeyLabelCI_FAST const& s) const noexcept {
p_char8 data = s.getData();
v_int32 size4 = s.getSize() >> 2;
result_type result = 0;
for(v_int32 i = 0; i < size4; i++) {
result ^= (*((p_word32) data) | 538976288); // 538976288 = 32 | (32 << 8) | (32 << 16) | (32 << 24);
data += 4;
}
for(v_int32 i = 0; i < s.getSize() - (size4 << 2); i++ ) {
((p_char8) &result)[i] ^= (data[i] | 32);
}
return result;
}
};
}
#endif /* oatpp_data_share_MemoryLabel_hpp */

View File

@ -90,7 +90,7 @@ os::io::Library::v_size OutputStreamBufferedProxy::write(const void *data, os::i
os::io::Library::v_size OutputStreamBufferedProxy::flush() {
auto amount = m_posEnd - m_pos;
if(amount > 0){
os::io::Library::v_size result = m_outputStream->write(&m_buffer[m_pos], amount);
os::io::Library::v_size result = stream::writeExactSizeData(m_outputStream.get(), &m_buffer[m_pos], amount);
if(result == amount){
m_pos = 0;
m_posEnd = 0;

View File

@ -25,5 +25,24 @@
#include "./ConnectionProvider.hpp"
namespace oatpp { namespace network {
const char* const ConnectionProvider::PROPERTY_HOST = "host";
const char* const ConnectionProvider::PROPERTY_PORT = "port";
void ConnectionProvider::setProperty(const oatpp::String& key, const oatpp::String& value) {
m_properties[key] = value;
}
const std::unordered_map<oatpp::data::share::StringKeyLabelCI, oatpp::data::share::StringKeyLabel>& ConnectionProvider::getProperties() {
return m_properties;
}
oatpp::data::share::StringKeyLabel ConnectionProvider::getProperty(const oatpp::String& key) {
auto it = m_properties.find(key);
if(it == m_properties.end()) {
return nullptr;
}
return it->second;
}
}}

View File

@ -25,57 +25,63 @@
#ifndef oatpp_netword_ConnectionsProvider_hpp
#define oatpp_netword_ConnectionsProvider_hpp
#include "oatpp/core/data/share/MemoryLabel.hpp"
#include "oatpp/core/data/stream/Stream.hpp"
#include "oatpp/core/async/Coroutine.hpp"
#include <unordered_map>
namespace oatpp { namespace network {
/**
* Abstract ConnectionProvider.
* It may be anything that returns oatpp::data::stream::IOStream
* User of ConnectionProvider should care about IOStream only.
* All other properties are optional
*/
class ConnectionProvider {
public:
static const char* const PROPERTY_HOST;
static const char* const PROPERTY_PORT;
public:
typedef oatpp::data::stream::IOStream IOStream;
typedef oatpp::async::Action Action;
typedef oatpp::async::Action (oatpp::async::AbstractCoroutine::*AsyncCallback)(const std::shared_ptr<IOStream>&);
private:
std::unordered_map<oatpp::data::share::StringKeyLabelCI, oatpp::data::share::StringKeyLabel> m_properties;
protected:
/**
* Set optional property
*/
void setProperty(const oatpp::String& key, const oatpp::String& value);
public:
virtual ~ConnectionProvider() {}
virtual std::shared_ptr<IOStream> getConnection() = 0;
virtual Action getConnectionAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback) = 0;
/**
* Some optional properties that user might want to know.
* All properties are optional and user should not rely on this
*/
const std::unordered_map<oatpp::data::share::StringKeyLabelCI, oatpp::data::share::StringKeyLabel>& getProperties();
/**
* Get optional property
*/
oatpp::data::share::StringKeyLabel getProperty(const oatpp::String& key);
};
/**
* No properties here. It is just a logical division
*/
class ServerConnectionProvider : public ConnectionProvider {
protected:
v_word16 m_port;
public:
ServerConnectionProvider(v_word16 port)
: m_port(port)
{}
v_word16 getPort(){
return m_port;
}
};
/**
* No properties here. It is just a logical division
*/
class ClientConnectionProvider : public ConnectionProvider {
protected:
oatpp::String m_host;
v_word16 m_port;
public:
ClientConnectionProvider(const oatpp::String& host, v_word16 port)
: m_host(host)
, m_port(port)
{}
oatpp::String getHost() {
return m_host;
}
v_word16 getPort(){
return m_port;
}
};
}}

View File

@ -26,7 +26,7 @@
#include "oatpp/network/Connection.hpp"
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
#include "oatpp/core/utils/ConversionUtils.hpp"
#include "oatpp/test/Checker.hpp"
#include <fcntl.h>
@ -35,6 +35,14 @@
#include <sys/socket.h>
namespace oatpp { namespace network { namespace client {
SimpleTCPConnectionProvider::SimpleTCPConnectionProvider(const oatpp::String& host, v_word16 port)
: m_host(host)
, m_port(port)
{
setProperty(PROPERTY_HOST, m_host);
setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(port));
}
std::shared_ptr<oatpp::data::stream::IOStream> SimpleTCPConnectionProvider::getConnection(){

View File

@ -33,20 +33,28 @@
namespace oatpp { namespace network { namespace client {
class SimpleTCPConnectionProvider : public base::Controllable, public ClientConnectionProvider {
protected:
oatpp::String m_host;
v_word16 m_port;
public:
SimpleTCPConnectionProvider(const oatpp::String& host, v_int32 port)
: ClientConnectionProvider(host, port)
{}
SimpleTCPConnectionProvider(const oatpp::String& host, v_word16 port);
public:
static std::shared_ptr<SimpleTCPConnectionProvider>
createShared(const oatpp::String& host, v_int32 port){
static std::shared_ptr<SimpleTCPConnectionProvider> createShared(const oatpp::String& host, v_word16 port){
return std::make_shared<SimpleTCPConnectionProvider>(host, port);
}
std::shared_ptr<IOStream> getConnection() override;
Action getConnectionAsync(oatpp::async::AbstractCoroutine* parentCoroutine, AsyncCallback callback) override;
oatpp::String getHost() {
return m_host;
}
v_word16 getPort(){
return m_port;
}
};
}}}

View File

@ -35,9 +35,17 @@
#include <sys/socket.h>
#include <netinet/tcp.h>
namespace oatpp { namespace network { namespace server {
SimpleTCPConnectionProvider::SimpleTCPConnectionProvider(v_word16 port, bool nonBlocking)
: m_port(port)
, m_nonBlocking(nonBlocking)
{
m_serverHandle = instantiateServer();
setProperty(PROPERTY_HOST, "localhost");
setProperty(PROPERTY_PORT, oatpp::utils::conversion::int32ToStr(port));
}
oatpp::os::io::Library::v_handle SimpleTCPConnectionProvider::instantiateServer(){
oatpp::os::io::Library::v_handle serverHandle;

View File

@ -35,17 +35,13 @@ namespace oatpp { namespace network { namespace server {
class SimpleTCPConnectionProvider : public base::Controllable, public ServerConnectionProvider {
private:
oatpp::os::io::Library::v_handle m_serverHandle;
v_word16 m_port;
bool m_nonBlocking;
oatpp::os::io::Library::v_handle m_serverHandle;
private:
oatpp::os::io::Library::v_handle instantiateServer();
public:
SimpleTCPConnectionProvider(v_word16 port, bool nonBlocking = false)
: ServerConnectionProvider(port)
, m_nonBlocking(nonBlocking)
{
m_serverHandle = instantiateServer();
}
SimpleTCPConnectionProvider(v_word16 port, bool nonBlocking = false);
public:
static std::shared_ptr<SimpleTCPConnectionProvider> createShared(v_word16 port, bool nonBlocking = false){
@ -65,8 +61,14 @@ public:
* For Asynchronous IO in oatpp it is considered to be a good practice
* to accept connections in a seperate thread with the blocking accept()
* and then process connections in Asynchronous manner with non-blocking read/write
*
* It may be implemented later
*/
throw std::runtime_error("oatpp::network::server::SimpleTCPConnectionProvider::getConnectionAsync not implemented.");
throw std::runtime_error("[oatpp::network::server::SimpleTCPConnectionProvider::getConnectionAsync()] not implemented.");
}
v_word16 getPort(){
return m_port;
}
};

View File

@ -0,0 +1,108 @@
/***************************************************************************
*
* 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 "Interface.hpp"
namespace oatpp { namespace network { namespace virtual_ {
void Interface::ConnectionSubmission::setSocket(const std::shared_ptr<Socket>& socket) {
{
std::unique_lock<std::mutex> lock(m_mutex);
m_socket = socket;
}
m_condition.notify_one();
}
std::shared_ptr<Socket> Interface::ConnectionSubmission::getSocket() {
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_socket && m_pending) {
m_condition.wait(lock);
}
return m_socket;
}
std::shared_ptr<Socket> Interface::ConnectionSubmission::getSocketNonBlocking() {
return m_socket;
}
bool Interface::ConnectionSubmission::isPending() {
return m_pending;
}
std::shared_ptr<Socket> Interface::acceptSubmission(const std::shared_ptr<ConnectionSubmission>& submission) {
auto pipeIn = Pipe::createShared();
auto pipeOut = Pipe::createShared();
auto serverSocket = Socket::createShared(pipeIn, pipeOut);
auto clientSocket = Socket::createShared(pipeOut, pipeIn);
submission->setSocket(clientSocket);
return serverSocket;
}
std::shared_ptr<Interface::ConnectionSubmission> Interface::connect() {
auto submission = std::make_shared<ConnectionSubmission>();
{
std::lock_guard<std::mutex> lock(m_mutex);
m_submissions.pushBack(submission);
}
m_condition.notify_one();
return submission;
}
std::shared_ptr<Interface::ConnectionSubmission> Interface::connectNonBlocking() {
std::shared_ptr<ConnectionSubmission> submission;
{
std::unique_lock<std::mutex> lock(m_mutex, std::try_to_lock);
if(lock.owns_lock()) {
submission = std::make_shared<ConnectionSubmission>();
m_submissions.pushBack(submission);
}
}
if(submission) {
m_condition.notify_one();
}
return submission;
}
std::shared_ptr<Socket> Interface::accept() {
std::unique_lock<std::mutex> lock(m_mutex);
while (m_submissions.getFirstNode() == nullptr) {
m_condition.wait(lock);
}
return acceptSubmission(m_submissions.popFront());
}
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());
}
return nullptr;
}
}}}

View File

@ -0,0 +1,85 @@
/***************************************************************************
*
* 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_virtual__Interface_hpp
#define oatpp_network_virtual__Interface_hpp
#include "./Socket.hpp"
#include "oatpp/core/collection/LinkedList.hpp"
namespace oatpp { namespace network { namespace virtual_ {
class Interface : public oatpp::base::Controllable {
public:
class ConnectionSubmission {
private:
std::shared_ptr<Socket> m_socket;
std::mutex m_mutex;
std::condition_variable m_condition;
bool m_pending;
public:
ConnectionSubmission() : m_pending(true) {}
void setSocket(const std::shared_ptr<Socket>& socket);
std::shared_ptr<Socket> getSocket();
std::shared_ptr<Socket> getSocketNonBlocking();
bool isPending();
};
private:
std::shared_ptr<Socket> acceptSubmission(const std::shared_ptr<ConnectionSubmission>& submission);
private:
oatpp::String m_name;
std::mutex m_mutex;
std::condition_variable m_condition;
oatpp::collection::LinkedList<std::shared_ptr<ConnectionSubmission>> m_submissions;
public:
Interface(const oatpp::String& name)
: m_name(name)
{}
public:
static std::shared_ptr<Interface> createShared(const oatpp::String& name) {
return std::make_shared<Interface>(name);
}
std::shared_ptr<ConnectionSubmission> connect();
std::shared_ptr<ConnectionSubmission> connectNonBlocking();
std::shared_ptr<Socket> accept();
std::shared_ptr<Socket> acceptNonBlocking();
oatpp::String getName() {
return m_name;
}
};
}}}
#endif /* oatpp_network_virtual__Interface_hpp */

View File

@ -26,86 +26,82 @@
namespace oatpp { namespace network { namespace virtual_ {
void Pipe::Reader::setMaxAvailableToRead(os::io::Library::v_size maxAvailableToRead) {
m_maxAvailableToRead = maxAvailableToRead;
}
os::io::Library::v_size Pipe::Reader::read(void *data, os::io::Library::v_size count) {
Pipe& pipe = *m_pipe;
if(!pipe.m_alive) {
return oatpp::data::stream::Errors::ERROR_IO_PIPE;
if(m_maxAvailableToRead > -1 && count > m_maxAvailableToRead) {
count = m_maxAvailableToRead;
}
Pipe& pipe = *m_pipe;
oatpp::os::io::Library::v_size result;
if(m_nonBlocking) {
oatpp::concurrency::SpinLock spinLock(pipe.m_atom);
if(pipe.m_buffer.availableToRead() > 0) {
auto result = pipe.m_buffer.read(data, count);
pipe.m_writeCondition.notify_one();
return result;
result = pipe.m_buffer.read(data, count);
} else if(pipe.m_open) {
result = oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY;
} else {
return oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY;
result = oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
} else {
std::unique_lock<std::mutex> lock(pipe.m_mutex);
while (pipe.m_buffer.availableToRead() == 0 && pipe.m_open) {
pipe.m_conditionWrite.notify_one();
pipe.m_conditionRead.wait(lock);
}
if (pipe.m_buffer.availableToRead() > 0) {
result = pipe.m_buffer.read(data, count);
} else {
result = oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
}
std::unique_lock<std::mutex> lock(pipe.m_readMutex);
pipe.m_readCondition.wait(lock, [&pipe] {return (pipe.m_buffer.availableToRead() > 0 || !pipe.m_alive);});
pipe.m_conditionWrite.notify_one();
oatpp::concurrency::SpinLock spinLock(pipe.m_atom);
if(!pipe.m_alive) {
lock.unlock();
pipe.m_writeCondition.notify_all();
pipe.m_readCondition.notify_all();
return oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
if(pipe.m_buffer.availableToRead() == 0) {
return oatpp::data::stream::Errors::ERROR_IO_RETRY;
}
auto result = pipe.m_buffer.read(data, count);
lock.unlock();
pipe.m_writeCondition.notify_one();
return result;
}
void Pipe::Writer::setMaxAvailableToWrite(os::io::Library::v_size maxAvailableToWrite) {
m_maxAvailableToWrtie = maxAvailableToWrite;
}
os::io::Library::v_size Pipe::Writer::write(const void *data, os::io::Library::v_size count) {
Pipe& pipe = *m_pipe;
if(!pipe.m_alive) {
return oatpp::data::stream::Errors::ERROR_IO_PIPE;
if(m_maxAvailableToWrtie > -1 && count > m_maxAvailableToWrtie) {
count = m_maxAvailableToWrtie;
}
Pipe& pipe = *m_pipe;
oatpp::os::io::Library::v_size result;
if(m_nonBlocking) {
oatpp::concurrency::SpinLock spinLock(pipe.m_atom);
if(pipe.m_buffer.availableToWrite() > 0) {
auto result = pipe.m_buffer.write(data, count);
pipe.m_readCondition.notify_one();
return result;
result = pipe.m_buffer.write(data, count);
} else if(pipe.m_open) {
result = oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY;
} else {
return oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY;
result = oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
} else {
std::unique_lock<std::mutex> lock(pipe.m_mutex);
while (pipe.m_buffer.availableToWrite() == 0 && pipe.m_open) {
pipe.m_conditionRead.notify_one();
pipe.m_conditionWrite.wait(lock);
}
if (pipe.m_open && pipe.m_buffer.availableToWrite() > 0) {
result = pipe.m_buffer.write(data, count);
} else {
result = oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
}
std::unique_lock<std::mutex> lock(pipe.m_writeMutex);
pipe.m_writeCondition.wait(lock, [&pipe] {return (pipe.m_buffer.availableToWrite() > 0 || !pipe.m_alive);});
pipe.m_conditionRead.notify_one();
oatpp::concurrency::SpinLock spinLock(pipe.m_atom);
if(!pipe.m_alive) {
lock.unlock();
pipe.m_writeCondition.notify_all();
pipe.m_readCondition.notify_all();
return oatpp::data::stream::Errors::ERROR_IO_PIPE;
}
if(pipe.m_buffer.availableToWrite() == 0) {
return oatpp::data::stream::Errors::ERROR_IO_RETRY;
}
auto result = pipe.m_buffer.write(data, count);
lock.unlock();
pipe.m_readCondition.notify_one();
return result;
}

View File

@ -36,84 +36,105 @@
namespace oatpp { namespace network { namespace virtual_ {
class Pipe : public oatpp::base::Controllable {
public:
OBJECT_POOL(Pipe_Pool, Pipe, 32)
SHARED_OBJECT_POOL(Shared_Pipe_Pool, Pipe, 32)
public:
class Reader : public oatpp::base::Controllable, public oatpp::data::stream::InputStream {
public:
OBJECT_POOL(Pipe_Reader_Pool, Pipe, 32)
SHARED_OBJECT_POOL(Shared_Pipe_Reader_Pool, Reader, 32)
class Reader : public oatpp::data::stream::InputStream {
private:
std::shared_ptr<Pipe> m_pipe;
Pipe* m_pipe;
bool m_nonBlocking;
public:
Reader(const std::shared_ptr<Pipe>& pipe, bool nonBlocking = false)
: m_pipe(pipe)
, m_nonBlocking(nonBlocking)
{}
/**
* this one used for testing purposes only
*/
os::io::Library::v_size m_maxAvailableToRead;
public:
static std::shared_ptr<Reader> createShared(const std::shared_ptr<Pipe>& pipe, bool nonBlocking = false){
return Shared_Pipe_Reader_Pool::allocateShared(pipe, nonBlocking);
}
Reader(Pipe* pipe, bool nonBlocking = false)
: m_pipe(pipe)
, m_nonBlocking(nonBlocking)
, m_maxAvailableToRead(-1)
{}
void setNonBlocking(bool nonBlocking) {
m_nonBlocking = nonBlocking;
}
/**
* this one used for testing purposes only
* set to -1 in order to ignore this value
*/
void setMaxAvailableToRead(os::io::Library::v_size maxAvailableToRead);
os::io::Library::v_size read(void *data, os::io::Library::v_size count) override;
};
class Writer : public oatpp::base::Controllable, public oatpp::data::stream::OutputStream {
public:
OBJECT_POOL(Pipe_Writer_Pool, Pipe, 32)
SHARED_OBJECT_POOL(Shared_Pipe_Writer_Pool, Writer, 32)
class Writer : public oatpp::data::stream::OutputStream {
private:
std::shared_ptr<Pipe> m_pipe;
Pipe* m_pipe;
bool m_nonBlocking;
public:
Writer(const std::shared_ptr<Pipe>& pipe, bool nonBlocking = false)
: m_pipe(pipe)
, m_nonBlocking(nonBlocking)
{}
/**
* this one used for testing purposes only
*/
os::io::Library::v_size m_maxAvailableToWrtie;
public:
static std::shared_ptr<Writer> createShared(const std::shared_ptr<Pipe>& pipe, bool nonBlocking = false){
return Shared_Pipe_Writer_Pool::allocateShared(pipe, nonBlocking);
}
Writer(Pipe* pipe, bool nonBlocking = false)
: m_pipe(pipe)
, m_nonBlocking(nonBlocking)
, m_maxAvailableToWrtie(-1)
{}
void setNonBlocking(bool nonBlocking) {
m_nonBlocking = nonBlocking;
}
/**
* this one used for testing purposes only
* set to -1 in order to ignore this value
*/
void setMaxAvailableToWrite(os::io::Library::v_size maxAvailableToWrite);
os::io::Library::v_size write(const void *data, os::io::Library::v_size count) override;
};
private:
bool m_open;
Writer m_writer;
Reader m_reader;
oatpp::data::buffer::FIFOBuffer m_buffer;
bool m_alive;
oatpp::concurrency::SpinLock::Atom m_atom;
std::mutex m_readMutex;
std::condition_variable m_readCondition;
std::mutex m_writeMutex;
std::condition_variable m_writeCondition;
std::mutex m_mutex;
std::condition_variable m_conditionRead;
std::condition_variable m_conditionWrite;
public:
Pipe()
: m_alive(true)
, m_atom(false)
: m_open(true)
, m_writer(this)
, m_reader(this)
{}
std::shared_ptr<Reader> getReader(bool nonBlocking = false) {
return Reader::createShared(getSharedPtr<Pipe>(), nonBlocking);
static std::shared_ptr<Pipe> createShared(){
return std::make_shared<Pipe>();
}
std::shared_ptr<Writer> getWriter(bool nonBlocking = false) {
return Writer::createShared(getSharedPtr<Pipe>(), nonBlocking);
Writer* getWriter() {
return &m_writer;
}
Reader* getReader() {
return &m_reader;
}
void close() {
{
std::lock_guard<std::mutex> lock(m_mutex);
m_open = false;
}
m_conditionRead.notify_one();
m_conditionWrite.notify_one();
}
};

View File

@ -23,3 +23,32 @@
***************************************************************************/
#include "Socket.hpp"
namespace oatpp { namespace network { namespace virtual_ {
void Socket::setMaxAvailableToReadWrtie(os::io::Library::v_size maxToRead, os::io::Library::v_size maxToWrite) {
m_pipeIn->getReader()->setMaxAvailableToRead(maxToRead);
m_pipeOut->getWriter()->setMaxAvailableToWrite(maxToWrite);
}
os::io::Library::v_size Socket::read(void *data, os::io::Library::v_size count) {
return m_pipeIn->getReader()->read(data, count);
}
os::io::Library::v_size Socket::write(const void *data, os::io::Library::v_size count) {
return m_pipeOut->getWriter()->write(data, count);
}
void Socket::setNonBlocking(bool nonBlocking) {
m_pipeIn->getReader()->setNonBlocking(nonBlocking);
m_pipeOut->getWriter()->setNonBlocking(nonBlocking);
}
void Socket::close() {
m_pipeIn->close();
m_pipeOut->close();
m_pipeIn.reset();
m_pipeOut.reset();
}
}}}

View File

@ -25,41 +25,41 @@
#ifndef oatpp_network_virtual__Socket_hpp
#define oatpp_network_virtual__Socket_hpp
#include "Pipe.hpp"
#include "./Pipe.hpp"
namespace oatpp { namespace network { namespace virtual_ {
class Socket : public oatpp::base::Controllable, public oatpp::data::stream::IOStream {
public:
OBJECT_POOL(Socket_Pool, Socket, 32)
SHARED_OBJECT_POOL(Shared_Socket_Pool, Socket, 32)
private:
std::shared_ptr<Pipe::Reader> m_pipeReader;
std::shared_ptr<Pipe::Writer> m_pipeWriter;
std::shared_ptr<Pipe> m_pipeIn;
std::shared_ptr<Pipe> m_pipeOut;
public:
Socket(const std::shared_ptr<Pipe>& pipeIn, const std::shared_ptr<Pipe>& pipeOut)
: m_pipeIn(pipeIn)
, m_pipeOut(pipeOut)
{}
public:
os::io::Library::v_size read(void *data, os::io::Library::v_size count) override {
if(m_pipeReader) {
return m_pipeReader->read(data, count);
}
return -1;
static std::shared_ptr<Socket> createShared(const std::shared_ptr<Pipe>& pipeIn, const std::shared_ptr<Pipe>& pipeOut) {
return std::make_shared<Socket>(pipeIn, pipeOut);
}
os::io::Library::v_size write(const void *data, os::io::Library::v_size count) override {
if(m_pipeWriter) {
return m_pipeWriter->write(data, count);
}
return -1;
~Socket() {
close();
}
bool isConnected() {
return m_pipeReader && m_pipeWriter;
}
/**
* this one used for testing purposes only
* set to -1 in order to ignore this value
*/
void setMaxAvailableToReadWrtie(os::io::Library::v_size maxToRead, os::io::Library::v_size maxToWrite);
void close() {
m_pipeReader.reset();
m_pipeWriter.reset();
}
os::io::Library::v_size read(void *data, os::io::Library::v_size count) override;
os::io::Library::v_size write(const void *data, os::io::Library::v_size count) override;
void setNonBlocking(bool nonBlocking);
void close();
};

View File

@ -0,0 +1,79 @@
/***************************************************************************
*
* 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 "ConnectionProvider.hpp"
namespace oatpp { namespace network { namespace virtual_ { namespace client {
std::shared_ptr<ConnectionProvider::IOStream> ConnectionProvider::getConnection() {
auto submission = m_interface->connect();
auto socket = submission->getSocket();
socket->setNonBlocking(false);
socket->setMaxAvailableToReadWrtie(m_maxAvailableToRead, m_maxAvailableToWrite);
return socket;
}
oatpp::async::Action ConnectionProvider::getConnectionAsync(oatpp::async::AbstractCoroutine* parentCoroutine, AsyncCallback callback) {
class ConnectCoroutine : public oatpp::async::CoroutineWithResult<ConnectCoroutine, std::shared_ptr<oatpp::data::stream::IOStream>> {
private:
std::shared_ptr<virtual_::Interface> m_interface;
os::io::Library::v_size m_maxAvailableToRead;
os::io::Library::v_size m_maxAvailableToWrite;
std::shared_ptr<virtual_::Interface::ConnectionSubmission> m_submission;
public:
ConnectCoroutine(const std::shared_ptr<virtual_::Interface>& interface,
os::io::Library::v_size maxAvailableToRead,
os::io::Library::v_size maxAvailableToWrite)
: m_interface(interface)
, m_maxAvailableToRead(maxAvailableToRead)
, m_maxAvailableToWrite(maxAvailableToWrite)
{}
Action act() override {
m_submission = m_interface->connectNonBlocking();
if(m_submission){
return yieldTo(&ConnectCoroutine::obtainSocket);
}
return waitRetry();
}
Action obtainSocket() {
auto socket = m_submission->getSocketNonBlocking();
if(socket) {
socket->setNonBlocking(true);
socket->setMaxAvailableToReadWrtie(m_maxAvailableToRead, m_maxAvailableToWrite);
return _return(socket);
}
return waitRetry();
}
};
return parentCoroutine->startCoroutineForResult<ConnectCoroutine>(callback, m_interface, m_maxAvailableToRead, m_maxAvailableToWrite);
}
}}}}

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_virtual__client_ConnectionProvider_hpp
#define oatpp_network_virtual__client_ConnectionProvider_hpp
#include "oatpp/network/virtual_/Interface.hpp"
#include "oatpp/network/ConnectionProvider.hpp"
namespace oatpp { namespace network { namespace virtual_ { namespace client {
class ConnectionProvider : public oatpp::network::ClientConnectionProvider {
private:
std::shared_ptr<virtual_::Interface> m_interface;
os::io::Library::v_size m_maxAvailableToRead;
os::io::Library::v_size m_maxAvailableToWrite;
public:
ConnectionProvider(const std::shared_ptr<virtual_::Interface>& interface)
: m_interface(interface)
, m_maxAvailableToRead(-1)
, m_maxAvailableToWrite(-1)
{
setProperty(PROPERTY_HOST, m_interface->getName());
setProperty(PROPERTY_PORT, "0");
}
static std::shared_ptr<ConnectionProvider> createShared(const std::shared_ptr<virtual_::Interface>& interface) {
return std::make_shared<ConnectionProvider>(interface);
}
/**
* this one used for testing purposes only
* set to -1 in order to ignore this value
*/
void setSocketMaxAvailableToReadWrtie(os::io::Library::v_size maxToRead, os::io::Library::v_size maxToWrite) {
m_maxAvailableToRead = maxToRead;
m_maxAvailableToWrite = maxToWrite;
}
std::shared_ptr<IOStream> getConnection() override;
Action getConnectionAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback) override;
};
}}}}
#endif /* oatpp_network_virtual__client_ConnectionProvider_hpp */

View File

@ -0,0 +1,36 @@
/***************************************************************************
*
* 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 "ConnectionProvider.hpp"
namespace oatpp { namespace network { namespace virtual_ { namespace server {
std::shared_ptr<ConnectionProvider::IOStream> ConnectionProvider::getConnection() {
auto socket = m_interface->accept();
socket->setNonBlocking(false);
socket->setMaxAvailableToReadWrtie(m_maxAvailableToRead, m_maxAvailableToWrite);
return socket;
}
}}}}

View File

@ -0,0 +1,81 @@
/***************************************************************************
*
* 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_virtual__server_ConnectionProvider_hpp
#define oatpp_network_virtual__server_ConnectionProvider_hpp
#include "oatpp/network/virtual_/Interface.hpp"
#include "oatpp/network/ConnectionProvider.hpp"
namespace oatpp { namespace network { namespace virtual_ { namespace server {
class ConnectionProvider : public oatpp::network::ServerConnectionProvider {
private:
std::shared_ptr<virtual_::Interface> m_interface;
os::io::Library::v_size m_maxAvailableToRead;
os::io::Library::v_size m_maxAvailableToWrite;
public:
ConnectionProvider(const std::shared_ptr<virtual_::Interface>& interface)
: m_interface(interface)
, m_maxAvailableToRead(-1)
, m_maxAvailableToWrite(-1)
{
setProperty(PROPERTY_HOST, m_interface->getName());
setProperty(PROPERTY_PORT, "0");
}
static std::shared_ptr<ConnectionProvider> createShared(const std::shared_ptr<virtual_::Interface>& interface) {
return std::make_shared<ConnectionProvider>(interface);
}
/**
* this one used for testing purposes only
* set to -1 in order to ignore this value
*/
void setSocketMaxAvailableToReadWrtie(os::io::Library::v_size maxToRead, os::io::Library::v_size maxToWrite) {
m_maxAvailableToRead = maxToRead;
m_maxAvailableToWrite = maxToWrite;
}
std::shared_ptr<IOStream> getConnection() override;
Action getConnectionAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback) override {
/**
* No need to implement this.
* For Asynchronous IO in oatpp it is considered to be a good practice
* to accept connections in a seperate thread with the blocking accept()
* and then process connections in Asynchronous manner with non-blocking read/write
*
* It may be implemented later
*/
throw std::runtime_error("[oatpp::network::virtual_::server::ConnectionProvider::getConnectionAsync()] not implemented.");
}
};
}}}}
#endif /* oatpp_network_virtual__server_ConnectionProvider_hpp */

View File

@ -1,4 +1,12 @@
#include "oatpp/test/web/FullTest.hpp"
#include "oatpp/test/web/FullAsyncTest.hpp"
#include "oatpp/test/network/virtual_/PipeTest.hpp"
#include "oatpp/test/network/virtual_/InterfaceTest.hpp"
#include "oatpp/test/core/data/share/MemoryLabelTest.hpp"
#include "oatpp/test/parser/json/mapping/DeserializerTest.hpp"
#include "oatpp/test/parser/json/mapping/DTOMapperPerfTest.hpp"
#include "oatpp/test/parser/json/mapping/DTOMapperTest.hpp"
@ -49,6 +57,11 @@ void runTests() {
OATPP_RUN_TEST(oatpp::test::parser::json::mapping::DTOMapperTest);
OATPP_RUN_TEST(oatpp::test::encoding::Base64Test);
OATPP_RUN_TEST(oatpp::test::encoding::UnicodeTest);
OATPP_RUN_TEST(oatpp::test::core::data::share::MemoryLabelTest);
OATPP_RUN_TEST(oatpp::test::network::virtual_::PipeTest);
OATPP_RUN_TEST(oatpp::test::network::virtual_::InterfaceTest);
OATPP_RUN_TEST(oatpp::test::web::FullTest);
OATPP_RUN_TEST(oatpp::test::web::FullAsyncTest);
}
}

View File

@ -0,0 +1,169 @@
/***************************************************************************
*
* 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 "MemoryLabelTest.hpp"
#include "oatpp/core/data/share/MemoryLabel.hpp"
#include <unordered_map>
#include "oatpp/test/Checker.hpp"
#include "oatpp/web/protocol/http/Http.hpp"
namespace oatpp { namespace test { namespace core { namespace data { namespace share {
bool MemoryLabelTest::onRun() {
oatpp::String sharedData = "big text goes here";
oatpp::String key1 = "key1";
oatpp::String key2 = "key2";
oatpp::String key3 = "key3";
oatpp::String key4 = "key4";
std::unordered_map<oatpp::data::share::StringKeyLabel, oatpp::data::share::MemoryLabel> stringMap;
std::unordered_map<oatpp::data::share::StringKeyLabelCI, oatpp::data::share::MemoryLabel> stringMapCI;
std::unordered_map<oatpp::data::share::StringKeyLabelCI_FAST, oatpp::data::share::MemoryLabel> stringMapCI_FAST;
// Case-Sensitive
stringMap[key1] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[0], 3);
stringMap[key2] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[4], 4);
stringMap[key3] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[9], 4);
stringMap[key4] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[14], 4);
OATPP_ASSERT(oatpp::base::StrBuffer::equals("big", stringMap["key1"].getData(), 3));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("text", stringMap["key2"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("goes", stringMap["key3"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("here", stringMap["key4"].getData(), 4));
OATPP_ASSERT(stringMap.find("Key1") == stringMap.end());
OATPP_ASSERT(stringMap.find("Key2") == stringMap.end());
OATPP_ASSERT(stringMap.find("Key3") == stringMap.end());
OATPP_ASSERT(stringMap.find("Key4") == stringMap.end());
// CI
stringMapCI[key1] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[0], 3);
stringMapCI[key2] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[4], 4);
stringMapCI[key3] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[9], 4);
stringMapCI[key4] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[14], 4);
OATPP_ASSERT(oatpp::base::StrBuffer::equals("big", stringMapCI["key1"].getData(), 3));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("text", stringMapCI["key2"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("goes", stringMapCI["key3"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("here", stringMapCI["key4"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("big", stringMapCI["KEY1"].getData(), 3));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("text", stringMapCI["KEY2"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("goes", stringMapCI["KEY3"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("here", stringMapCI["KEY4"].getData(), 4));
// CI_FAST
stringMapCI_FAST[key1] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[0], 3);
stringMapCI_FAST[key2] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[4], 4);
stringMapCI_FAST[key3] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[9], 4);
stringMapCI_FAST[key4] = oatpp::data::share::MemoryLabel(sharedData.getPtr(), &sharedData->getData()[14], 4);
OATPP_ASSERT(oatpp::base::StrBuffer::equals("big", stringMapCI_FAST["key1"].getData(), 3));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("text", stringMapCI_FAST["key2"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("goes", stringMapCI_FAST["key3"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("here", stringMapCI_FAST["key4"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("big", stringMapCI_FAST["KEY1"].getData(), 3));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("text", stringMapCI_FAST["KEY2"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("goes", stringMapCI_FAST["KEY3"].getData(), 4));
OATPP_ASSERT(oatpp::base::StrBuffer::equals("here", stringMapCI_FAST["KEY4"].getData(), 4));
{
v_int32 iterationsCount = 100;
oatpp::String headersText =
"header0: value0\r\n"
"header1: value1\r\n"
"header2: value2\r\n"
"header3: value3\r\n"
"header4: value4\r\n"
"header5: value5\r\n"
"header6: value6\r\n"
"header7: value7\r\n"
"header8: value8\r\n"
"header9: value9\r\n"
"\r\n";
{
oatpp::test::PerformanceChecker timer("timer");
for(v_int32 i = 0; i < iterationsCount; i ++) {
oatpp::parser::ParsingCaret caret(headersText);
oatpp::web::protocol::http::Status status;
oatpp::web::protocol::http::Protocol::Headers headers;
oatpp::web::protocol::http::Protocol::parseHeaders(headers, headersText.getPtr(), caret, status);
OATPP_ASSERT(status.code == 0);
OATPP_ASSERT(headers.size() == 10);
OATPP_ASSERT(headers["header0"].equals("value0", 6));
OATPP_ASSERT(headers["header1"].equals("value1", 6));
OATPP_ASSERT(headers["header2"].equals("value2", 6));
OATPP_ASSERT(headers["header3"].equals("value3", 6));
OATPP_ASSERT(headers["header4"].equals("value4", 6));
OATPP_ASSERT(headers["header5"].equals("value5", 6));
OATPP_ASSERT(headers["header6"].equals("value6", 6));
OATPP_ASSERT(headers["header7"].equals("value7", 6));
OATPP_ASSERT(headers["header8"].equals("value8", 6));
OATPP_ASSERT(headers["header9"].equals("value9", 6));
/*
OATPP_ASSERT(headers["header0"].equals("value0"));
OATPP_ASSERT(headers["header1"].equals("value1"));
OATPP_ASSERT(headers["header2"].equals("value2"));
OATPP_ASSERT(headers["header3"].equals("value3"));
OATPP_ASSERT(headers["header4"].equals("value4"));
OATPP_ASSERT(headers["header5"].equals("value5"));
OATPP_ASSERT(headers["header6"].equals("value6"));
OATPP_ASSERT(headers["header7"].equals("value7"));
OATPP_ASSERT(headers["header8"].equals("value8"));
OATPP_ASSERT(headers["header9"].equals("value9"));
*/
}
}
}
return true;
}
}}}}}

View File

@ -0,0 +1,42 @@
/***************************************************************************
*
* 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_test_core_data_share_MemoryLabelTest_hpp
#define oatpp_test_core_data_share_MemoryLabelTest_hpp
#include "oatpp/test/UnitTest.hpp"
namespace oatpp { namespace test { namespace core { namespace data { namespace share {
class MemoryLabelTest : public UnitTest{
public:
MemoryLabelTest():UnitTest("TEST[core::data::share::MemoryLabelTest]"){}
bool onRun() override;
};
}}}}}
#endif /* oatpp_test_core_data_share_MemoryLabelTest_hpp */

View File

@ -0,0 +1,161 @@
/***************************************************************************
*
* 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 "InterfaceTest.hpp"
#include "oatpp/network/virtual_/Interface.hpp"
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
#include "oatpp/core/concurrency/Thread.hpp"
namespace oatpp { namespace test { namespace network { namespace virtual_ {
namespace {
typedef oatpp::network::virtual_::Interface Interface;
typedef oatpp::network::virtual_::Socket Socket;
typedef oatpp::collection::LinkedList<std::shared_ptr<oatpp::concurrency::Thread>> ThreadList;
class ClientTask : public oatpp::concurrency::Runnable {
private:
std::shared_ptr<Interface> m_interface;
oatpp::String m_dataSample;
public:
ClientTask(const std::shared_ptr<Interface>& interface,
const oatpp::String& dataSample)
: m_interface(interface)
, m_dataSample(dataSample)
{}
void run() override {
auto submission = m_interface->connect();
auto socket = submission->getSocket();
auto res = oatpp::data::stream::writeExactSizeData(socket.get(), m_dataSample->getData(), m_dataSample->getSize());
OATPP_ASSERT(res == m_dataSample->getSize());
v_char8 buffer[100];
auto stream = oatpp::data::stream::ChunkedBuffer::createShared();
res = oatpp::data::stream::transfer(socket, stream, 2, buffer, 100);
OATPP_ASSERT(res == 2);
OATPP_ASSERT(stream->getSize() == res);
OATPP_ASSERT(stream->toString() == "OK");
//OATPP_LOGD("client", "finished - OK");
}
};
class ServerTask : public oatpp::concurrency::Runnable {
private:
std::shared_ptr<Socket> m_socket;
oatpp::String m_dataSample;
public:
ServerTask(const std::shared_ptr<Socket>& socket,
const oatpp::String& dataSample)
: m_socket(socket)
, m_dataSample(dataSample)
{}
void run() override {
v_char8 buffer[100];
auto stream = oatpp::data::stream::ChunkedBuffer::createShared();
auto res = oatpp::data::stream::transfer(m_socket, stream, m_dataSample->getSize(), buffer, 100);
OATPP_ASSERT(res == m_dataSample->getSize());
OATPP_ASSERT(stream->getSize() == res);
OATPP_ASSERT(stream->toString() == m_dataSample);
res = oatpp::data::stream::writeExactSizeData(m_socket.get(), "OK", 2);
OATPP_ASSERT(res == 2);
}
};
class Server : public oatpp::concurrency::Runnable {
private:
std::shared_ptr<Interface> m_interface;
oatpp::String m_dataSample;
v_int32 m_numTasks;
public:
Server(const std::shared_ptr<Interface>& interface,
const oatpp::String& dataSample,
v_int32 numTasks)
: m_interface(interface)
, m_dataSample(dataSample)
, m_numTasks(numTasks)
{}
void run() override {
ThreadList threadList;
for(v_int32 i = 0; i < m_numTasks; i++) {
auto socket = m_interface->accept();
auto task = oatpp::concurrency::Thread::createShared(std::make_shared<ServerTask>(socket, m_dataSample));
threadList.pushBack(task);
}
auto curr = threadList.getFirstNode();
while (curr != nullptr) {
curr->getData()->join();
curr = curr->getNext();
}
}
};
}
bool InterfaceTest::onRun() {
oatpp::String dataSample = "1234567890-=][poiuytrewqasdfghjkl;'/.,mnbvcxzzxcvbnm,./';lkjhgfdsaqwertyuiop][=-0987654321";
auto interface = Interface::createShared("virtualhost");
v_int32 numTasks = 100;
ThreadList threadList;
auto server = oatpp::concurrency::Thread::createShared(std::make_shared<Server>(interface, dataSample, numTasks));
for(v_int32 i = 0; i < numTasks; i++) {
auto clientTask = oatpp::concurrency::Thread::createShared(std::make_shared<ClientTask>(interface, dataSample));
threadList.pushBack(clientTask);
}
auto curr = threadList.getFirstNode();
while (curr != nullptr) {
curr->getData()->join();
curr = curr->getNext();
}
server->join();
return true;
}
}}}}

View File

@ -0,0 +1,40 @@
/***************************************************************************
*
* 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_test_network_virtual__InterfaceTest_hpp
#define oatpp_test_network_virtual__InterfaceTest_hpp
#include "oatpp/test/UnitTest.hpp"
namespace oatpp { namespace test { namespace network { namespace virtual_ {
class InterfaceTest : public UnitTest {
public:
InterfaceTest():UnitTest("TEST[network::virtual_::InterfaceTest]"){}
bool onRun() override;
};
}}}}
#endif /* oatpp_test_network_virtual__InterfaceTest_hpp */

View File

@ -0,0 +1,150 @@
/***************************************************************************
*
* 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 "PipeTest.hpp"
#include "oatpp/network/virtual_/Pipe.hpp"
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
#include "oatpp/core/concurrency/Thread.hpp"
#include "oatpp/test/Checker.hpp"
#include <iostream>
namespace oatpp { namespace test { namespace network { namespace virtual_ {
namespace {
typedef oatpp::network::virtual_::Pipe Pipe;
const char* DATA_CHUNK = "<0123456789/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ>";
const os::io::Library::v_size CHUNK_SIZE = std::strlen(DATA_CHUNK);
class WriterTask : public oatpp::concurrency::Runnable {
private:
std::shared_ptr<Pipe> m_pipe;
v_int32 m_chunksToTransfer;
os::io::Library::v_size m_position = 0;
os::io::Library::v_size m_transferedBytes = 0;
public:
WriterTask(const std::shared_ptr<Pipe>& pipe, v_int32 chunksToTransfer)
: m_pipe(pipe)
, m_chunksToTransfer(chunksToTransfer)
{}
void run() override {
while (m_transferedBytes < CHUNK_SIZE * m_chunksToTransfer) {
auto res = m_pipe->getWriter()->write(&DATA_CHUNK[m_position], CHUNK_SIZE - m_position);
if(res > 0) {
m_transferedBytes += res;
m_position += res;
if(m_position == CHUNK_SIZE) {
m_position = 0;
}
}
}
OATPP_LOGD("WriterTask", "sent %d bytes", m_transferedBytes);
}
};
class ReaderTask : public oatpp::concurrency::Runnable {
private:
std::shared_ptr<oatpp::data::stream::ChunkedBuffer> m_buffer;
std::shared_ptr<Pipe> m_pipe;
v_int32 m_chunksToTransfer;
public:
ReaderTask(const std::shared_ptr<oatpp::data::stream::ChunkedBuffer> &buffer,
const std::shared_ptr<Pipe>& pipe,
v_int32 chunksToTransfer)
: m_buffer(buffer)
, m_pipe(pipe)
, m_chunksToTransfer(chunksToTransfer)
{}
void run() override {
v_char8 readBuffer[256];
while (m_buffer->getSize() < CHUNK_SIZE * m_chunksToTransfer) {
auto res = m_pipe->getReader()->read(readBuffer, 256);
if(res > 0) {
m_buffer->write(readBuffer, res);
}
}
OATPP_LOGD("ReaderTask", "sent %d bytes", m_buffer->getSize());
}
};
void runTransfer(const std::shared_ptr<Pipe>& pipe, v_int32 chunksToTransfer, bool writeNonBlock, bool readerNonBlock) {
OATPP_LOGD("transfer", "writer-nb: %d, reader-nb: %d", writeNonBlock, readerNonBlock);
auto buffer = oatpp::data::stream::ChunkedBuffer::createShared();
{
oatpp::test::PerformanceChecker timer("timer");
auto writerThread = oatpp::concurrency::Thread::createShared(std::make_shared<WriterTask>(pipe, chunksToTransfer));
auto readerThread = oatpp::concurrency::Thread::createShared(std::make_shared<ReaderTask>(buffer, pipe, chunksToTransfer));
writerThread->join();
readerThread->join();
}
OATPP_ASSERT(buffer->getSize() == chunksToTransfer * CHUNK_SIZE);
auto ruleBuffer = oatpp::data::stream::ChunkedBuffer::createShared();
for(v_int32 i = 0; i < chunksToTransfer; i ++) {
ruleBuffer->write(DATA_CHUNK, CHUNK_SIZE);
}
auto str1 = buffer->toString();
auto str2 = buffer->toString();
OATPP_ASSERT(str1 == str2);
}
}
bool PipeTest::onRun() {
auto pipe = Pipe::createShared();
v_int32 chunkCount = oatpp::data::buffer::IOBuffer::BUFFER_SIZE * 10 / CHUNK_SIZE;
runTransfer(pipe, chunkCount, false, false);
runTransfer(pipe, chunkCount, true, false);
runTransfer(pipe, chunkCount, false, true);
runTransfer(pipe, chunkCount, true, true);
return true;
}
}}}}

View File

@ -0,0 +1,40 @@
/***************************************************************************
*
* 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_test_network_virtual__PipeTest_hpp
#define oatpp_test_network_virtual__PipeTest_hpp
#include "oatpp/test/UnitTest.hpp"
namespace oatpp { namespace test { namespace network { namespace virtual_ {
class PipeTest : public UnitTest {
public:
PipeTest():UnitTest("TEST[network::virtual_::PipeTest]"){}
bool onRun() override;
};
}}}}
#endif /* oatpp_test_network_virtual__PipeTest_hpp */

125
test/web/FullAsyncTest.cpp Normal file
View File

@ -0,0 +1,125 @@
/***************************************************************************
*
* 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 "FullAsyncTest.hpp"
#include "oatpp/test/web/app/Client.hpp"
#include "oatpp/test/web/app/ControllerAsync.hpp"
#include "oatpp/web/client/HttpRequestExecutor.hpp"
#include "oatpp/web/server/AsyncHttpConnectionHandler.hpp"
#include "oatpp/web/server/HttpRouter.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/network/virtual_/client/ConnectionProvider.hpp"
#include "oatpp/network/virtual_/server/ConnectionProvider.hpp"
#include "oatpp/network/virtual_/Interface.hpp"
namespace oatpp { namespace test { namespace web {
bool FullAsyncTest::onRun() {
auto interface = oatpp::network::virtual_::Interface::createShared("virtualhost");
auto serverConnectionProvider = oatpp::network::virtual_::server::ConnectionProvider::createShared(interface);
auto clientConnectionProvider = oatpp::network::virtual_::client::ConnectionProvider::createShared(interface);
serverConnectionProvider->setSocketMaxAvailableToReadWrtie(1, 1);
clientConnectionProvider->setSocketMaxAvailableToReadWrtie(1, 1);
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
auto router = oatpp::web::server::HttpRouter::createShared();
auto connectionHandler = oatpp::web::server::AsyncHttpConnectionHandler::createShared(router);
auto controller = app::ControllerAsync::createShared(objectMapper);
controller->addEndpointsToRouter(router);
auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider);
auto client = app::Client::createShared(requestExecutor, objectMapper);
auto server = oatpp::network::server::Server::createShared(serverConnectionProvider, connectionHandler);
std::thread clientThread([client, server, connectionHandler, objectMapper]{
for(v_int32 i = 0; i < 10; i ++) {
{ /* test simple GET */
auto response = client->getRoot();
auto value = response->readBodyToString();
OATPP_ASSERT(value == "Hello World Async!!!");
}
{ /* test GET with path parameter */
auto response = client->getWithParams("my_test_param-Async");
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_param-Async");
}
{ /* test GET with header parameter */
auto response = client->getWithHeaders("my_test_header-Async");
//auto str = response->readBodyToString();
//OATPP_LOGE("AAA", "code=%d, str='%s'", response->statusCode, str->c_str());
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_header-Async");
}
{ /* test POST with body */
auto response = client->postBody("my_test_body-Async");
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_body-Async");
}
}
try {
connectionHandler->stop();
server->stop();
client->getRoot(); // wake blocking server accept
} catch(std::runtime_error e) {
// DO NOTHING
}
});
std::thread serverThread([server]{
server->run();
});
clientThread.join();
serverThread.join();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
return true;
}
}}}

View File

@ -22,40 +22,21 @@
*
***************************************************************************/
#ifndef oatpp_web_server_HttpError_hpp
#define oatpp_web_server_HttpError_hpp
#ifndef oatpp_test_web_FullAsyncTest_hpp
#define oatpp_test_web_FullAsyncTest_hpp
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/core/Types.hpp"
#include "oatpp/test/UnitTest.hpp"
namespace oatpp { namespace web { namespace server {
namespace oatpp { namespace test { namespace web {
class HttpError : public std::runtime_error {
private:
oatpp::web::protocol::http::Status m_status;
oatpp::String m_message;
class FullAsyncTest : public UnitTest {
public:
HttpError(const oatpp::web::protocol::http::Status& status,
const oatpp::String& message)
:std::runtime_error(status.description)
, m_status(status)
, m_message(message)
{}
oatpp::web::protocol::http::Status getStatus() {
return m_status;
}
oatpp::String& getMessage(){
return m_message;
}
FullAsyncTest():UnitTest("TEST[web::FullAsyncTest]"){}
bool onRun() override;
};
#define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \
if(!(COND)) { throw oatpp::web::server::HttpError(STATUS, MESSAGE); }
}}}
#endif /* oatpp_web_server_HttpError_hpp */
#endif /* oatpp_test_web_FullAsyncTest_hpp */

121
test/web/FullTest.cpp Normal file
View File

@ -0,0 +1,121 @@
/***************************************************************************
*
* 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 "FullTest.hpp"
#include "oatpp/test/web/app/Client.hpp"
#include "oatpp/test/web/app/ControllerAsync.hpp"
#include "oatpp/test/web/app/Controller.hpp"
#include "oatpp/web/client/HttpRequestExecutor.hpp"
#include "oatpp/web/server/HttpConnectionHandler.hpp"
#include "oatpp/web/server/HttpRouter.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/network/virtual_/client/ConnectionProvider.hpp"
#include "oatpp/network/virtual_/server/ConnectionProvider.hpp"
#include "oatpp/network/virtual_/Interface.hpp"
namespace oatpp { namespace test { namespace web {
bool FullTest::onRun() {
auto interface = oatpp::network::virtual_::Interface::createShared("virtualhost");
auto serverConnectionProvider = oatpp::network::virtual_::server::ConnectionProvider::createShared(interface);
auto clientConnectionProvider = oatpp::network::virtual_::client::ConnectionProvider::createShared(interface);
serverConnectionProvider->setSocketMaxAvailableToReadWrtie(1, 1);
clientConnectionProvider->setSocketMaxAvailableToReadWrtie(1, 1);
auto objectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared();
auto router = oatpp::web::server::HttpRouter::createShared();
auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
auto controller = app::Controller::createShared(objectMapper);
controller->addEndpointsToRouter(router);
auto requestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(clientConnectionProvider);
auto client = app::Client::createShared(requestExecutor, objectMapper);
auto server = oatpp::network::server::Server::createShared(serverConnectionProvider, connectionHandler);
std::thread clientThread([client, server, objectMapper]{
for(v_int32 i = 0; i < 10; i ++) {
{ /* test simple GET */
auto response = client->getRoot();
auto value = response->readBodyToString();
OATPP_ASSERT(value == "Hello World!!!");
}
{ /* test GET with path parameter */
auto response = client->getWithParams("my_test_param");
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_param");
}
{ /* test GET with header parameter */
auto response = client->getWithHeaders("my_test_header");
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_header");
}
{ /* test POST with body */
auto response = client->postBody("my_test_body");
auto dto = response->readBodyToDto<app::TestDto>(objectMapper);
OATPP_ASSERT(dto);
OATPP_ASSERT(dto->testValue == "my_test_body");
}
}
try {
server->stop();
client->getRoot(); // wake blocking server accept
} catch(std::runtime_error e) {
// DO NOTHING
}
});
std::thread serverThread([server]{
server->run();
});
clientThread.join();
serverThread.join();
return true;
}
}}}

View File

@ -22,4 +22,21 @@
*
***************************************************************************/
#include "HttpError.hpp"
#ifndef oatpp_test_web_FullTest_hpp
#define oatpp_test_web_FullTest_hpp
#include "oatpp/test/UnitTest.hpp"
namespace oatpp { namespace test { namespace web {
class FullTest : public UnitTest {
public:
FullTest():UnitTest("TEST[web::FullTest]"){}
bool onRun() override;
};
}}}
#endif /* oatpp_test_web_FullTest_hpp */

48
test/web/app/Client.hpp Normal file
View File

@ -0,0 +1,48 @@
/***************************************************************************
*
* 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_test_web_app_Client_hpp
#define oatpp_test_web_app_Client_hpp
#include "oatpp/web/client/ApiClient.hpp"
#include "oatpp/core/macro/codegen.hpp"
namespace oatpp { namespace test { namespace web { namespace app {
class Client : public oatpp::web::client::ApiClient {
#include OATPP_CODEGEN_BEGIN(ApiClient)
API_CLIENT_INIT(Client)
API_CALL("GET", "/", getRoot)
API_CALL("GET", "params/{param}", getWithParams, PATH(String, param))
API_CALL("GET", "headers", getWithHeaders, HEADER(String, param, "X-TEST-HEADER"))
API_CALL("POST", "body", postBody, BODY_STRING(String, body))
#include OATPP_CODEGEN_END(ApiClient)
};
}}}}
#endif /* oatpp_test_web_app_Client_hpp */

View File

@ -0,0 +1,86 @@
/***************************************************************************
*
* 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_test_web_app_Controller_hpp
#define oatpp_test_web_app_Controller_hpp
#include "./DTOs.hpp"
#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/macro/component.hpp"
namespace oatpp { namespace test { namespace web { namespace app {
class Controller : public oatpp::web::server::api::ApiController {
private:
static constexpr const char* TAG = "test::web::app::Controller";
public:
Controller(const std::shared_ptr<ObjectMapper>& objectMapper)
: oatpp::web::server::api::ApiController(objectMapper)
{}
public:
static std::shared_ptr<Controller> createShared(const std::shared_ptr<ObjectMapper>& objectMapper){
return std::make_shared<Controller>(objectMapper);
}
#include OATPP_CODEGEN_BEGIN(ApiController)
ENDPOINT("GET", "/", root) {
OATPP_LOGD(TAG, "GET '/'");
return createResponse(Status::CODE_200, "Hello World!!!");
}
ENDPOINT("GET", "params/{param}", getWithParams,
PATH(String, param)) {
OATPP_LOGD(TAG, "GET params/%s", param->c_str());
auto dto = TestDto::createShared();
dto->testValue = param;
return createDtoResponse(Status::CODE_200, dto);
}
ENDPOINT("GET", "headers", getWithHeaders,
HEADER(String, param, "X-TEST-HEADER")) {
OATPP_LOGD(TAG, "GET headers {X-TEST-HEADER: %s}", param->c_str());
auto dto = TestDto::createShared();
dto->testValue = param;
return createDtoResponse(Status::CODE_200, dto);
}
ENDPOINT("POST", "body", postBody,
BODY_STRING(String, body)) {
OATPP_LOGD(TAG, "POST body %s", body->c_str());
auto dto = TestDto::createShared();
dto->testValue = body;
return createDtoResponse(Status::CODE_200, dto);
}
#include OATPP_CODEGEN_END(ApiController)
};
}}}}
#endif /* oatpp_test_web_app_Controller_hpp */

View File

@ -0,0 +1,115 @@
/***************************************************************************
*
* 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_test_web_app_ControllerAsync_hpp
#define oatpp_test_web_app_ControllerAsync_hpp
#include "./DTOs.hpp"
#include "oatpp/web/server/api/ApiController.hpp"
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
#include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/macro/component.hpp"
namespace oatpp { namespace test { namespace web { namespace app {
class ControllerAsync : public oatpp::web::server::api::ApiController {
private:
static constexpr const char* TAG = "test::web::app::ControllerAsync";
public:
ControllerAsync(const std::shared_ptr<ObjectMapper>& objectMapper)
: oatpp::web::server::api::ApiController(objectMapper)
{}
public:
static std::shared_ptr<ControllerAsync> createShared(const std::shared_ptr<ObjectMapper>& objectMapper){
return std::make_shared<ControllerAsync>(objectMapper);
}
#include OATPP_CODEGEN_BEGIN(ApiController)
ENDPOINT_ASYNC("GET", "/", Root) {
ENDPOINT_ASYNC_INIT(Root)
Action act() {
OATPP_LOGD(TAG, "GET '/'");
return _return(controller->createResponse(Status::CODE_200, "Hello World Async!!!"));
}
};
ENDPOINT_ASYNC("GET", "params/{param}", GetWithParams) {
ENDPOINT_ASYNC_INIT(GetWithParams)
Action act() {
auto param = request->getPathVariable("param");
OATPP_LOGD(TAG, "GET params/%s", param->c_str());
auto dto = TestDto::createShared();
dto->testValue = param;
return _return(controller->createDtoResponse(Status::CODE_200, dto));
}
};
ENDPOINT_ASYNC("GET", "headers", GetWithHeaders) {
ENDPOINT_ASYNC_INIT(GetWithHeaders)
Action act() {
auto param = request->getHeader("X-TEST-HEADER");
OATPP_LOGD(TAG, "GET headers {X-TEST-HEADER: %s}", param->c_str());
auto dto = TestDto::createShared();
dto->testValue = param;
return _return(controller->createDtoResponse(Status::CODE_200, dto));
}
};
ENDPOINT_ASYNC("POST", "body", PostBody) {
ENDPOINT_ASYNC_INIT(PostBody)
Action act() {
OATPP_LOGD(TAG, "POST body. Reading body...");
return request->readBodyToStringAsync(this, &PostBody::onBodyRead);
}
Action onBodyRead(const String& body) {
OATPP_LOGD(TAG, "POST body %s", body->c_str());
auto dto = TestDto::createShared();
dto->testValue = body;
return _return(controller->createDtoResponse(Status::CODE_200, dto));
}
};
#include OATPP_CODEGEN_END(ApiController)
};
}}}}
#endif /* oatpp_test_web_app_ControllerAsync_hpp */

47
test/web/app/DTOs.hpp Normal file
View File

@ -0,0 +1,47 @@
/***************************************************************************
*
* 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_test_web_app_DTOs_hpp
#define oatpp_test_web_app_DTOs_hpp
#include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp"
namespace oatpp { namespace test { namespace web { namespace app {
#include OATPP_CODEGEN_BEGIN(DTO)
class TestDto : public oatpp::data::mapping::type::Object {
DTO_INIT(TestDto, Object)
DTO_FIELD(String, testValue);
};
#include OATPP_CODEGEN_END(DTO)
}}}}
#endif /* oatpp_test_web_app_DTOs_hpp */

View File

@ -112,22 +112,17 @@ void ApiClient::addPathQueryParams(oatpp::data::stream::OutputStream* stream,
}
std::shared_ptr<ApiClient::StringToStringMap> ApiClient::convertParamsMap(const std::shared_ptr<StringToParamMap>& params) {
if(!params){
return nullptr;
oatpp::web::protocol::http::Protocol::Headers ApiClient::convertParamsMap(const std::shared_ptr<StringToParamMap>& params) {
oatpp::web::protocol::http::Protocol::Headers result;
if(params) {
auto curr = params->getFirstEntry();
while (curr != nullptr) {
result[curr->getKey()] = oatpp::utils::conversion::primitiveToStr(curr->getValue());
curr = curr->getNext();
}
}
auto result = StringToStringMap::createShared();
auto curr = params->getFirstEntry();
while (curr != nullptr) {
result->put(curr->getKey(), oatpp::utils::conversion::primitiveToStr(curr->getValue()));
curr = curr->getNext();
}
return result;
}

View File

@ -106,7 +106,7 @@ private:
void addPathQueryParams(oatpp::data::stream::OutputStream* stream,
const std::shared_ptr<StringToParamMap>& params);
std::shared_ptr<StringToStringMap> convertParamsMap(const std::shared_ptr<StringToParamMap>& params);
oatpp::web::protocol::http::Protocol::Headers convertParamsMap(const std::shared_ptr<StringToParamMap>& params);
protected:

View File

@ -24,6 +24,7 @@
#include "HttpRequestExecutor.hpp"
#include "oatpp/web/protocol/http/incoming/ResponseHeadersReader.hpp"
#include "oatpp/web/protocol/http/outgoing/Request.hpp"
#include "oatpp/web/protocol/http/outgoing/BufferBody.hpp"
@ -79,7 +80,7 @@ HttpRequestExecutor::Action HttpRequestExecutor::getConnectionAsync(oatpp::async
std::shared_ptr<HttpRequestExecutor::Response>
HttpRequestExecutor::execute(const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle) {
@ -96,9 +97,8 @@ HttpRequestExecutor::execute(const String& method,
}
auto request = oatpp::web::protocol::http::outgoing::Request::createShared(method, path, headers, body);
request->headers->putIfNotExists(oatpp::web::protocol::http::Header::HOST, m_connectionProvider->getHost());
request->headers->putIfNotExists(oatpp::web::protocol::http::Header::CONNECTION,
oatpp::web::protocol::http::Header::Value::CONNECTION_KEEP_ALIVE);
request->putHeaderIfNotExists(oatpp::web::protocol::http::Header::HOST, m_connectionProvider->getProperty("host"));
request->putHeaderIfNotExists(oatpp::web::protocol::http::Header::CONNECTION, oatpp::web::protocol::http::Header::Value::CONNECTION_KEEP_ALIVE);
auto ioBuffer = oatpp::data::buffer::IOBuffer::createShared();
@ -106,37 +106,28 @@ HttpRequestExecutor::execute(const String& method,
request->send(upStream);
upStream->flush();
auto readCount = connection->read(ioBuffer->getData(), ioBuffer->getSize());
oatpp::web::protocol::http::incoming::ResponseHeadersReader headerReader(ioBuffer->getData(), ioBuffer->getSize(), 4096);
oatpp::web::protocol::http::HttpError::Info error;
const auto& result = headerReader.readHeaders(connection, error);
if(readCount == 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_NO_RESPONSE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: No response from server");
} else if(readCount < 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_READ_RESPONSE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to read response. Check out the RequestExecutionError::getReadErrorCode() for more information", (v_int32) readCount);
}
oatpp::parser::ParsingCaret caret((p_char8) ioBuffer->getData(), ioBuffer->getSize());
auto line = protocol::http::Protocol::parseResponseStartingLine(caret);
if(!line){
if(error.status.code != 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to parse response. Invalid starting line");
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to parse response. Invalid response headers");
}
oatpp::web::protocol::http::Status error;
auto responseHeaders = protocol::http::Protocol::parseHeaders(caret, error);
if(error.code != 0){
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_HEADERS,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to parse response. Invalid headers section");
if(error.ioStatus < 0) {
throw RequestExecutionError(RequestExecutionError::ERROR_CODE_CANT_PARSE_STARTING_LINE,
"[oatpp::web::client::HttpRequestExecutor::execute()]: Failed to read response.");
}
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(connection,
ioBuffer,
caret.getPosition(),
(v_int32) readCount);
result.bufferPosStart,
result.bufferPosEnd);
return Response::createShared(line->statusCode, line->description, responseHeaders, bodyStream, m_bodyDecoder);
return Response::createShared(result.startingLine.statusCode,
result.startingLine.description.toString(),
result.headers, bodyStream, m_bodyDecoder);
}
@ -144,16 +135,18 @@ oatpp::async::Action HttpRequestExecutor::executeAsync(oatpp::async::AbstractCor
AsyncCallback callback,
const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle) {
typedef protocol::http::incoming::ResponseHeadersReader ResponseHeadersReader;
class ExecutorCoroutine : public oatpp::async::CoroutineWithResult<ExecutorCoroutine, std::shared_ptr<HttpRequestExecutor::Response>> {
private:
std::shared_ptr<oatpp::network::ClientConnectionProvider> m_connectionProvider;
String m_method;
String m_path;
std::shared_ptr<Headers> m_headers;
Headers m_headers;
std::shared_ptr<Body> m_body;
std::shared_ptr<const oatpp::web::protocol::http::incoming::BodyDecoder> m_bodyDecoder;
std::shared_ptr<ConnectionHandle> m_connectionHandle;
@ -167,7 +160,7 @@ oatpp::async::Action HttpRequestExecutor::executeAsync(oatpp::async::AbstractCor
ExecutorCoroutine(const std::shared_ptr<oatpp::network::ClientConnectionProvider>& connectionProvider,
const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<const oatpp::web::protocol::http::incoming::BodyDecoder>& bodyDecoder,
const std::shared_ptr<ConnectionHandle>& connectionHandle)
@ -197,8 +190,8 @@ oatpp::async::Action HttpRequestExecutor::executeAsync(oatpp::async::AbstractCor
Action onConnectionReady(const std::shared_ptr<oatpp::data::stream::IOStream>& connection) {
m_connection = connection;
auto request = oatpp::web::protocol::http::outgoing::Request::createShared(m_method, m_path, m_headers, m_body);
request->headers->putIfNotExists(String(Header::HOST, false), m_connectionProvider->getHost());
request->headers->putIfNotExists(String(Header::CONNECTION, false), String(Header::Value::CONNECTION_KEEP_ALIVE, false));
request->putHeaderIfNotExists(Header::HOST, m_connectionProvider->getProperty("host"));
request->putHeaderIfNotExists(Header::CONNECTION, Header::Value::CONNECTION_KEEP_ALIVE);
m_ioBuffer = oatpp::data::buffer::IOBuffer::createShared();
auto upStream = oatpp::data::stream::OutputStreamBufferedProxy::createShared(connection, m_ioBuffer);
m_bufferPointer = m_ioBuffer->getData();
@ -207,39 +200,21 @@ oatpp::async::Action HttpRequestExecutor::executeAsync(oatpp::async::AbstractCor
}
Action readResponse() {
return oatpp::data::stream::
readSomeDataAsyncInline(m_connection.get(), m_bufferPointer, m_bufferBytesLeftToRead, yieldTo(&ExecutorCoroutine::parseResponse));
ResponseHeadersReader::AsyncCallback callback = static_cast<ResponseHeadersReader::AsyncCallback>(&ExecutorCoroutine::onHeadersParsed);
ResponseHeadersReader headersReader(m_ioBuffer->getData(), m_ioBuffer->getSize(), 4096);
return headersReader.readHeadersAsync(this, callback, m_connection);
}
Action parseResponse() {
Action onHeadersParsed(const ResponseHeadersReader::Result& result) {
os::io::Library::v_size readCount = m_ioBuffer->getSize() - m_bufferBytesLeftToRead;
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(m_connection,
m_ioBuffer,
result.bufferPosStart,
result.bufferPosEnd);
if(readCount > 0) {
oatpp::parser::ParsingCaret caret((p_char8) m_ioBuffer->getData(), m_ioBuffer->getSize());
auto line = protocol::http::Protocol::parseResponseStartingLine(caret);
if(!line){
return error("Invalid starting line");
}
oatpp::web::protocol::http::Status err;
auto headers = protocol::http::Protocol::parseHeaders(caret, err);
if(err.code != 0){
return error("can't parse headers");
}
auto bodyStream = oatpp::data::stream::InputStreamBufferedProxy::createShared(m_connection,
m_ioBuffer,
caret.getPosition(),
(v_int32) readCount);
return _return(Response::createShared(line->statusCode, line->description, headers, bodyStream, m_bodyDecoder));
}
return error("Read zero bytes from Response");
return _return(Response::createShared(result.startingLine.statusCode,
result.startingLine.description.toString(),
result.headers, bodyStream, m_bodyDecoder));
}

View File

@ -71,7 +71,7 @@ public:
*/
std::shared_ptr<Response> execute(const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle = nullptr) override;
@ -79,7 +79,7 @@ public:
AsyncCallback callback,
const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle = nullptr) override;

View File

@ -110,7 +110,7 @@ public:
virtual std::shared_ptr<Response> execute(const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle = nullptr) = 0;
@ -118,7 +118,7 @@ public:
AsyncCallback callback,
const String& method,
const String& path,
const std::shared_ptr<Headers>& headers,
const Headers& headers,
const std::shared_ptr<Body>& body,
const std::shared_ptr<ConnectionHandle>& connectionHandle = nullptr) = 0;

View File

@ -0,0 +1,43 @@
/***************************************************************************
*
* 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 "CommunicationError.hpp"
namespace oatpp { namespace web { namespace protocol {
CommunicationError::CommunicationError(oatpp::os::io::Library::v_size ioStatus, const oatpp::String& message)
:std::runtime_error(message->std_str())
, m_ioStatus(ioStatus)
, m_message(message)
{}
oatpp::os::io::Library::v_size CommunicationError::getIOStatus() {
return m_ioStatus;
}
oatpp::String& CommunicationError::getMessage(){
return m_message;
}
}}}

View File

@ -0,0 +1,82 @@
/***************************************************************************
*
* 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_web_protocol_CommunicationError_hpp
#define oatpp_web_protocol_CommunicationError_hpp
#include "oatpp/core/Types.hpp"
#include "oatpp/core/os/io/Library.hpp"
namespace oatpp { namespace web { namespace protocol {
class CommunicationError : public std::runtime_error {
private:
oatpp::os::io::Library::v_size m_ioStatus;
oatpp::String m_message;
public:
CommunicationError(oatpp::os::io::Library::v_size ioStatus, const oatpp::String& message);
oatpp::os::io::Library::v_size getIOStatus();
oatpp::String& getMessage();
};
template<class Status>
class ProtocolError : public CommunicationError {
public:
struct Info {
Info()
: ioStatus(0)
{}
Info(oatpp::os::io::Library::v_size pIOStatus, const Status& pStatus)
: ioStatus(pIOStatus)
, status(pStatus)
{}
oatpp::os::io::Library::v_size ioStatus;
Status status;
};
private:
Info m_info;
public:
ProtocolError(const Info& info, const oatpp::String& message)
: CommunicationError(info.ioStatus, message)
, m_info(info)
{}
Info getInfo() {
return m_info;
}
};
}}}
#endif /* oatpp_web_protocol_CommunicationError_hpp */

View File

@ -237,118 +237,118 @@ ContentRange ContentRange::parse(const oatpp::String& str) {
return parse(caret);
}
std::shared_ptr<RequestStartingLine> Protocol::parseRequestStartingLine(oatpp::parser::ParsingCaret& caret) {
oatpp::data::share::StringKeyLabelCI_FAST Protocol::parseHeaderNameLabel(const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret) {
p_char8 data = caret.getData();
for(v_int32 i = caret.getPosition(); i < caret.getSize(); i++) {
v_char8 a = data[i];
if(a == ':' || a == ' '){
oatpp::data::share::StringKeyLabelCI_FAST label(headersText, &data[caret.getPosition()], i - caret.getPosition());
caret.setPosition(i);
return label;
}
}
return oatpp::data::share::StringKeyLabelCI_FAST(nullptr, nullptr, 0);
}
void Protocol::parseRequestStartingLine(RequestStartingLine& line,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error) {
auto line = RequestStartingLine::createShared();
oatpp::parser::ParsingCaret::Label methodLabel(caret);
if(caret.findChar(' ')){
line->method = methodLabel.toString(true);
line.method = oatpp::data::share::StringKeyLabel(headersText, methodLabel.getData(), methodLabel.getSize());
caret.inc();
} else {
caret.setError("Invalid starting line");
return nullptr;
error = Status::CODE_400;
return;
}
oatpp::parser::ParsingCaret::Label pathLabel(caret);
if(caret.findChar(' ')){
line->path = pathLabel.toString(true);
line.path = oatpp::data::share::StringKeyLabel(headersText, pathLabel.getData(), pathLabel.getSize());
caret.inc();
} else {
caret.setError("Invalid starting line");
return nullptr;
error = Status::CODE_400;
return;
}
oatpp::parser::ParsingCaret::Label protocolLabel(caret);
if(caret.findRN()){
line->protocol = protocolLabel.toString(true);
line.protocol = oatpp::data::share::StringKeyLabel(headersText, protocolLabel.getData(), protocolLabel.getSize());
caret.skipRN();
} else {
caret.setError("Invalid starting line");
return nullptr;
error = Status::CODE_400;
return;
}
return line;
}
std::shared_ptr<ResponseStartingLine> Protocol::parseResponseStartingLine(oatpp::parser::ParsingCaret& caret) {
auto line = ResponseStartingLine::createShared();
void Protocol::parseResponseStartingLine(ResponseStartingLine& line,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error) {
oatpp::parser::ParsingCaret::Label protocolLabel(caret);
if(caret.findChar(' ')){
line->protocol = protocolLabel.toString(true);
line.protocol = oatpp::data::share::StringKeyLabel(headersText, protocolLabel.getData(), protocolLabel.getSize());
caret.inc();
if(!line->protocol->startsWith((p_char8)"HTTP", 4)){
caret.setError("Unknown protocol");
return nullptr;
}
} else {
caret.setError("Invalid starting line");
return nullptr;
error = Status::CODE_400;
return;
}
line->statusCode = caret.parseInt32();
line.statusCode = caret.parseInt32();
oatpp::parser::ParsingCaret::Label descriptionLabel(caret);
if(caret.findRN()){
line->description = descriptionLabel.toString(true);
line.description = oatpp::data::share::StringKeyLabel(headersText, descriptionLabel.getData(), descriptionLabel.getSize());
caret.skipRN();
} else {
caret.setError("Invalid starting line");
return nullptr;
error = Status::CODE_400;
return;
}
return line;
}
oatpp::String Protocol::parseHeaderName(oatpp::parser::ParsingCaret& caret) {
p_char8 data = caret.getData();
oatpp::parser::ParsingCaret::Label label(caret);
for(v_int32 i = caret.getPosition(); i < caret.getSize(); i++) {
v_char8 a = data[i];
if(a == ':' || a == ' '){
caret.setPosition(i);
label.end();
return label.toString(true);
}
}
return nullptr;
}
void Protocol::parseOneHeader(Headers& headers, oatpp::parser::ParsingCaret& caret, Status& error) {
void Protocol::parseOneHeader(Headers& headers,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error) {
caret.findNotSpaceChar();
auto name = parseHeaderName(caret);
if(name) {
auto name = parseHeaderNameLabel(headersText, caret);
if(name.getData() != nullptr) {
caret.findNotSpaceChar();
if(!caret.canContinueAtChar(':', 1)) {
error = Status::CODE_400;
return;
}
caret.findNotSpaceChar();
oatpp::parser::ParsingCaret::Label label(caret);
v_int32 valuePos0 = caret.getPosition();
caret.findRN();
headers.put(name, label.toString(true));
headers[name] = oatpp::data::share::StringKeyLabel(headersText, &caret.getData()[valuePos0], caret.getPosition() - valuePos0);
caret.skipRN();
} else {
error = Status::CODE_431;
return;
}
}
std::shared_ptr<Protocol::Headers> Protocol::parseHeaders(oatpp::parser::ParsingCaret& caret, Status& error) {
auto headers = Headers::createShared();
void Protocol::parseHeaders(Headers& headers,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error) {
while (!caret.isAtRN()) {
parseOneHeader(*headers, caret, error);
parseOneHeader(headers, headersText, caret, error);
if(error.code != 0) {
return nullptr;
return;
}
}
caret.skipRN();
return headers;
}

View File

@ -22,17 +22,21 @@
*
***************************************************************************/
#ifndef oatpp_network_http_Protocol_hpp
#define oatpp_network_http_Protocol_hpp
#ifndef oatpp_web_protocol_http_Http_hpp
#define oatpp_web_protocol_http_Http_hpp
#include "oatpp/network/Connection.hpp"
#include "oatpp/core/parser/ParsingCaret.hpp"
#include "oatpp/web/protocol/CommunicationError.hpp"
#include "oatpp/core/parser/ParsingCaret.hpp"
#include "oatpp/core/data/share/MemoryLabel.hpp"
#include "oatpp/core/data/stream/Delegate.hpp"
#include "oatpp/core/collection/ListMap.hpp"
#include "oatpp/core/Types.hpp"
#include <unordered_map>
namespace oatpp { namespace web { namespace protocol { namespace http {
class Status{
@ -128,6 +132,22 @@ public:
};
class HttpError : public protocol::ProtocolError<Status> {
public:
HttpError(const Info& info, const oatpp::String& message)
: protocol::ProtocolError<Status>(info, message)
{}
HttpError(const Status& status, const oatpp::String& message)
: protocol::ProtocolError<Status>(Info(0, status), message)
{}
};
#define OATPP_ASSERT_HTTP(COND, STATUS, MESSAGE) \
if(!(COND)) { throw oatpp::web::protocol::http::HttpError(STATUS, MESSAGE); }
class Header {
public:
class Value {
@ -226,59 +246,45 @@ public:
};
class RequestStartingLine : public base::Controllable {
public:
OBJECT_POOL(RequestStartingLine_Pool, RequestStartingLine, 32)
SHARED_OBJECT_POOL(Shared_RequestStartingLine_Pool, RequestStartingLine, 32)
public:
RequestStartingLine()
{}
public:
static std::shared_ptr<RequestStartingLine> createShared(){
return Shared_RequestStartingLine_Pool::allocateShared();
}
oatpp::String method; // GET, POST ...
oatpp::String path;
oatpp::String protocol;
struct RequestStartingLine {
oatpp::data::share::StringKeyLabel method; // GET, POST ...
oatpp::data::share::StringKeyLabel path;
oatpp::data::share::StringKeyLabel protocol;
};
class ResponseStartingLine : public base::Controllable {
public:
OBJECT_POOL(ResponseStartingLine_Pool, ResponseStartingLine, 32)
SHARED_OBJECT_POOL(Shared_ResponseStartingLine_Pool, ResponseStartingLine, 32)
public:
ResponseStartingLine()
{}
public:
static std::shared_ptr<ResponseStartingLine> createShared(){
return Shared_ResponseStartingLine_Pool::allocateShared();
}
oatpp::String protocol;
struct ResponseStartingLine {
oatpp::data::share::StringKeyLabel protocol;
v_int32 statusCode;
oatpp::String description;
oatpp::data::share::StringKeyLabel description;
};
class Protocol {
public:
typedef oatpp::collection::ListMap<oatpp::String, oatpp::String> Headers;
typedef std::unordered_map<oatpp::data::share::StringKeyLabelCI_FAST, oatpp::data::share::StringKeyLabel> Headers;
private:
static oatpp::String parseHeaderName(oatpp::parser::ParsingCaret& caret);
static oatpp::data::share::StringKeyLabelCI_FAST parseHeaderNameLabel(const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret);
public:
static std::shared_ptr<RequestStartingLine> parseRequestStartingLine(oatpp::parser::ParsingCaret& caret);
static std::shared_ptr<ResponseStartingLine> parseResponseStartingLine(oatpp::parser::ParsingCaret& caret);
static void parseRequestStartingLine(RequestStartingLine& line,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error);
/**
* Parse header and store it in headers map
*/
static void parseOneHeader(Headers& headers, oatpp::parser::ParsingCaret& caret, Status& error);
static std::shared_ptr<Headers> parseHeaders(oatpp::parser::ParsingCaret& caret, Status& error);
static void parseResponseStartingLine(ResponseStartingLine& line,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error);
static void parseOneHeader(Headers& headers,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error);
static void parseHeaders(Headers& headers,
const std::shared_ptr<oatpp::base::StrBuffer>& headersText,
oatpp::parser::ParsingCaret& caret,
Status& error);
};
@ -299,4 +305,4 @@ namespace std {
};
}
#endif /* oatpp_network_http_Protocol_hpp */
#endif /* oatpp_web_protocol_http_Http_hpp */

View File

@ -37,13 +37,13 @@ private:
class ToStringDecoder : public oatpp::async::CoroutineWithResult<ToStringDecoder, oatpp::String> {
private:
const BodyDecoder* m_decoder;
std::shared_ptr<Protocol::Headers> m_headers;
Protocol::Headers m_headers;
std::shared_ptr<oatpp::data::stream::InputStream> m_bodyStream;
std::shared_ptr<oatpp::data::stream::ChunkedBuffer> m_chunkedBuffer = oatpp::data::stream::ChunkedBuffer::createShared();
public:
ToStringDecoder(const BodyDecoder* decoder,
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream)
: m_decoder(decoder)
, m_headers(headers)
@ -64,14 +64,14 @@ private:
class ToDtoDecoder : public oatpp::async::CoroutineWithResult<ToDtoDecoder<Type>, typename Type::ObjectWrapper> {
private:
const BodyDecoder* m_decoder;
std::shared_ptr<Protocol::Headers> m_headers;
Protocol::Headers m_headers;
std::shared_ptr<oatpp::data::stream::InputStream> m_bodyStream;
std::shared_ptr<oatpp::data::mapping::ObjectMapper> m_objectMapper;
std::shared_ptr<oatpp::data::stream::ChunkedBuffer> m_chunkedBuffer = oatpp::data::stream::ChunkedBuffer::createShared();
public:
ToDtoDecoder(const BodyDecoder* decoder,
const std::shared_ptr<Protocol::Headers>& headers,
Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper)
: m_decoder(decoder)
@ -98,17 +98,17 @@ private:
public:
virtual void decode(const std::shared_ptr<Protocol::Headers>& headers,
virtual void decode(const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const = 0;
virtual oatpp::async::Action decodeAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
const oatpp::async::Action& actionOnReturn,
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const = 0;
oatpp::String decodeToString(const std::shared_ptr<Protocol::Headers>& headers,
oatpp::String decodeToString(const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream) const {
auto chunkedBuffer = oatpp::data::stream::ChunkedBuffer::createShared();
decode(headers, bodyStream, chunkedBuffer);
@ -116,7 +116,7 @@ public:
}
template<class Type>
typename Type::ObjectWrapper decodeToDto(const std::shared_ptr<Protocol::Headers>& headers,
typename Type::ObjectWrapper decodeToDto(const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper) const {
return objectMapper->readFromString<Type>(decodeToString(headers, bodyStream));
@ -125,7 +125,7 @@ public:
template<typename ParentCoroutineType>
oatpp::async::Action decodeToStringAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
oatpp::async::Action (ParentCoroutineType::*callback)(const oatpp::String&),
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream) const {
return parentCoroutine->startCoroutineForResult<ToStringDecoder>(callback, this, headers, bodyStream);
}
@ -133,7 +133,7 @@ public:
template<class DtoType, typename ParentCoroutineType>
oatpp::async::Action decodeToDtoAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
oatpp::async::Action (ParentCoroutineType::*callback)(const typename DtoType::ObjectWrapper&),
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper) const {
return parentCoroutine->startCoroutineForResult<ToDtoDecoder<DtoType>>(callback, this, headers, bodyStream, objectMapper);

View File

@ -40,9 +40,9 @@ public:
: bodyDecoder(pBodyDecoder)
{}
Request(const std::shared_ptr<http::RequestStartingLine>& pStartingLine,
Request(const http::RequestStartingLine& pStartingLine,
const std::shared_ptr<url::mapping::Pattern::MatchMap>& pPathVariables,
const std::shared_ptr<http::Protocol::Headers>& pHeaders,
const http::Protocol::Headers& pHeaders,
const std::shared_ptr<oatpp::data::stream::InputStream>& pBodyStream,
const std::shared_ptr<const http::incoming::BodyDecoder>& pBodyDecoder)
: startingLine(pStartingLine)
@ -53,17 +53,17 @@ public:
{}
public:
static std::shared_ptr<Request> createShared(const std::shared_ptr<http::RequestStartingLine>& startingLine,
static std::shared_ptr<Request> createShared(const http::RequestStartingLine& startingLine,
const std::shared_ptr<url::mapping::Pattern::MatchMap>& pathVariables,
const std::shared_ptr<http::Protocol::Headers>& headers,
const http::Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<const http::incoming::BodyDecoder>& bodyDecoder) {
return Shared_Incoming_Request_Pool::allocateShared(startingLine, pathVariables, headers, bodyStream, bodyDecoder);
}
std::shared_ptr<http::RequestStartingLine> startingLine;
http::RequestStartingLine startingLine;
std::shared_ptr<url::mapping::Pattern::MatchMap> pathVariables;
std::shared_ptr<http::Protocol::Headers> headers;
http::Protocol::Headers headers;
std::shared_ptr<oatpp::data::stream::InputStream> bodyStream;
/**
@ -73,9 +73,9 @@ public:
std::shared_ptr<const http::incoming::BodyDecoder> bodyDecoder;
oatpp::String getHeader(const oatpp::String& headerName) const{
auto entry = headers->find(headerName);
if(entry != nullptr) {
return entry->getValue();
auto it = headers.find(headerName);
if(it != headers.end()) {
return it->second.toString();
}
return nullptr;
}

View File

@ -0,0 +1,184 @@
/***************************************************************************
*
* 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 "RequestHeadersReader.hpp"
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming {
os::io::Library::v_size RequestHeadersReader::readHeadersSection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
oatpp::data::stream::OutputStream* bufferStream,
Result& result) {
v_word32 sectionEnd = ('\r' << 24) | ('\n' << 16) | ('\r' << 8) | ('\n');
v_word32 accumulator = 0;
v_int32 progress = 0;
os::io::Library::v_size res;
while (true) {
v_int32 desiredToRead = m_bufferSize;
if(progress + desiredToRead > m_maxHeadersSize) {
desiredToRead = m_maxHeadersSize - progress;
if(desiredToRead <= 0) {
return -1;
}
}
res = connection->read(m_buffer, desiredToRead);
if(res > 0) {
bufferStream->write(m_buffer, res);
for(v_int32 i = 0; i < res; i ++) {
accumulator <<= 8;
accumulator |= m_buffer[i];
if(accumulator == sectionEnd) {
result.bufferPosStart = i + 1;
result.bufferPosEnd = (v_int32) res;
return res;
}
}
} else if(res == oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY || res == oatpp::data::stream::Errors::ERROR_IO_RETRY) {
continue;
} else {
break;
}
}
return res;
}
RequestHeadersReader::Result RequestHeadersReader::readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
http::HttpError::Info& error) {
RequestHeadersReader::Result result;
oatpp::data::stream::ChunkedBuffer buffer;
error.ioStatus = readHeadersSection(connection, &buffer, result);
if(error.ioStatus > 0) {
auto headersText = buffer.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;
http::Protocol::parseRequestStartingLine(result.startingLine, headersText.getPtr(), caret, status);
if(status.code == 0) {
http::Protocol::parseHeaders(result.headers, headersText.getPtr(), caret, status);
}
}
return result;
}
RequestHeadersReader::Action RequestHeadersReader::readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection)
{
class ReaderCoroutine : public oatpp::async::CoroutineWithResult<ReaderCoroutine, Result> {
private:
std::shared_ptr<oatpp::data::stream::IOStream> m_connection;
p_char8 m_buffer;
v_int32 m_bufferSize;
v_int32 m_maxHeadersSize;
v_word32 m_accumulator;
v_int32 m_progress;
RequestHeadersReader::Result m_result;
oatpp::data::stream::ChunkedBuffer m_bufferStream;
public:
ReaderCoroutine(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
p_char8 buffer, v_int32 bufferSize, v_int32 maxHeadersSize)
: m_connection(connection)
, m_buffer(buffer)
, m_bufferSize(bufferSize)
, m_maxHeadersSize(maxHeadersSize)
, m_accumulator(0)
, m_progress(0)
{}
Action act() override {
v_int32 desiredToRead = m_bufferSize;
if(m_progress + desiredToRead > m_maxHeadersSize) {
desiredToRead = m_maxHeadersSize - m_progress;
if(desiredToRead <= 0) {
return error("Headers section is too large");
}
}
auto res = m_connection->read(m_buffer, desiredToRead);
if(res > 0) {
m_bufferStream.write(m_buffer, res);
for(v_int32 i = 0; i < res; i ++) {
m_accumulator <<= 8;
m_accumulator |= m_buffer[i];
if(m_accumulator == SECTION_END) {
m_result.bufferPosStart = i + 1;
m_result.bufferPosEnd = (v_int32) res;
return yieldTo(&ReaderCoroutine::parseHeaders);
}
}
return waitRetry();
} else if(res == oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY || res == oatpp::data::stream::Errors::ERROR_IO_RETRY) {
return waitRetry();
} else {
return abort();
}
}
Action parseHeaders() {
auto headersText = m_bufferStream.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;
http::Protocol::parseRequestStartingLine(m_result.startingLine, headersText.getPtr(), caret, status);
if(status.code == 0) {
http::Protocol::parseHeaders(m_result.headers, headersText.getPtr(), caret, status);
if(status.code == 0) {
return _return(m_result);
} else {
return error("error occurred while parsing headers");
}
} else {
return error("can't parse starting line");
}
}
};
return parentCoroutine->startCoroutineForResult<ReaderCoroutine>(callback, connection, m_buffer, m_bufferSize, m_maxHeadersSize);
}
}}}}}

View File

@ -0,0 +1,74 @@
/***************************************************************************
*
* 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_web_protocol_http_incoming_RequestHeadersReader_hpp
#define oatpp_web_protocol_http_incoming_RequestHeadersReader_hpp
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/core/async/Coroutine.hpp"
namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming {
class RequestHeadersReader {
public:
typedef oatpp::async::Action Action;
private:
static constexpr v_int32 SECTION_END = ('\r' << 24) | ('\n' << 16) | ('\r' << 8) | ('\n');
public:
struct Result {
http::RequestStartingLine startingLine;
http::Protocol::Headers headers;
v_int32 bufferPosStart;
v_int32 bufferPosEnd;
};
public:
typedef Action (oatpp::async::AbstractCoroutine::*AsyncCallback)(const Result&);
private:
os::io::Library::v_size readHeadersSection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
oatpp::data::stream::OutputStream* bufferStream,
Result& result);
private:
p_char8 m_buffer;
v_int32 m_bufferSize;
v_int32 m_maxHeadersSize;
public:
RequestHeadersReader(void* buffer, v_int32 bufferSize, v_int32 maxHeadersSize)
: m_buffer((p_char8) buffer)
, m_bufferSize(bufferSize)
, m_maxHeadersSize(maxHeadersSize)
{}
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::HttpError::Info& error);
Action readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection);
};
}}}}}
#endif /* oatpp_web_protocol_http_incoming_RequestHeadersReader_hpp */

View File

@ -37,7 +37,7 @@ public:
public:
Response(v_int32 pStatusCode,
const oatpp::String& pStatusDescription,
const std::shared_ptr<http::Protocol::Headers>& pHeaders,
const http::Protocol::Headers& pHeaders,
const std::shared_ptr<oatpp::data::stream::InputStream>& pBodyStream,
const std::shared_ptr<const http::incoming::BodyDecoder>& pBodyDecoder)
: statusCode(pStatusCode)
@ -50,7 +50,7 @@ public:
static std::shared_ptr<Response> createShared(v_int32 statusCode,
const oatpp::String& statusDescription,
const std::shared_ptr<http::Protocol::Headers>& headers,
const http::Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<const http::incoming::BodyDecoder>& bodyDecoder) {
return Shared_Incoming_Response_Pool::allocateShared(statusCode, statusDescription, headers, bodyStream, bodyDecoder);
@ -58,7 +58,7 @@ public:
const v_int32 statusCode;
const oatpp::String statusDescription;
const std::shared_ptr<http::Protocol::Headers> headers;
const http::Protocol::Headers headers;
const std::shared_ptr<oatpp::data::stream::InputStream> bodyStream;
/**

View File

@ -0,0 +1,184 @@
/***************************************************************************
*
* 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 "ResponseHeadersReader.hpp"
#include "oatpp/core/data/stream/ChunkedBuffer.hpp"
namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming {
os::io::Library::v_size ResponseHeadersReader::readHeadersSection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
oatpp::data::stream::OutputStream* bufferStream,
Result& result) {
v_word32 sectionEnd = ('\r' << 24) | ('\n' << 16) | ('\r' << 8) | ('\n');
v_word32 accumulator = 0;
v_int32 progress = 0;
os::io::Library::v_size res;
while (true) {
v_int32 desiredToRead = m_bufferSize;
if(progress + desiredToRead > m_maxHeadersSize) {
desiredToRead = m_maxHeadersSize - progress;
if(desiredToRead <= 0) {
return -1;
}
}
res = connection->read(m_buffer, desiredToRead);
if(res > 0) {
bufferStream->write(m_buffer, res);
for(v_int32 i = 0; i < res; i ++) {
accumulator <<= 8;
accumulator |= m_buffer[i];
if(accumulator == sectionEnd) {
result.bufferPosStart = i + 1;
result.bufferPosEnd = (v_int32) res;
return res;
}
}
} else if(res == oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY || res == oatpp::data::stream::Errors::ERROR_IO_RETRY) {
continue;
} else {
break;
}
}
return res;
}
ResponseHeadersReader::Result ResponseHeadersReader::readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
http::HttpError::Info& error) {
Result result;
oatpp::data::stream::ChunkedBuffer buffer;
error.ioStatus = readHeadersSection(connection, &buffer, result);
if(error.ioStatus > 0) {
auto headersText = buffer.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;
http::Protocol::parseResponseStartingLine(result.startingLine, headersText.getPtr(), caret, status);
if(status.code == 0) {
http::Protocol::parseHeaders(result.headers, headersText.getPtr(), caret, status);
}
}
return result;
}
ResponseHeadersReader::Action ResponseHeadersReader::readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection)
{
class ReaderCoroutine : public oatpp::async::CoroutineWithResult<ReaderCoroutine, Result> {
private:
std::shared_ptr<oatpp::data::stream::IOStream> m_connection;
p_char8 m_buffer;
v_int32 m_bufferSize;
v_int32 m_maxHeadersSize;
v_word32 m_accumulator;
v_int32 m_progress;
ResponseHeadersReader::Result m_result;
oatpp::data::stream::ChunkedBuffer m_bufferStream;
public:
ReaderCoroutine(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
p_char8 buffer, v_int32 bufferSize, v_int32 maxHeadersSize)
: m_connection(connection)
, m_buffer(buffer)
, m_bufferSize(bufferSize)
, m_maxHeadersSize(maxHeadersSize)
, m_accumulator(0)
, m_progress(0)
{}
Action act() override {
v_int32 desiredToRead = m_bufferSize;
if(m_progress + desiredToRead > m_maxHeadersSize) {
desiredToRead = m_maxHeadersSize - m_progress;
if(desiredToRead <= 0) {
return error("Headers section is too large");
}
}
auto res = m_connection->read(m_buffer, desiredToRead);
if(res > 0) {
m_bufferStream.write(m_buffer, res);
for(v_int32 i = 0; i < res; i ++) {
m_accumulator <<= 8;
m_accumulator |= m_buffer[i];
if(m_accumulator == SECTION_END) {
m_result.bufferPosStart = i + 1;
m_result.bufferPosEnd = (v_int32) res;
return yieldTo(&ReaderCoroutine::parseHeaders);
}
}
return waitRetry();
} else if(res == oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY || res == oatpp::data::stream::Errors::ERROR_IO_RETRY) {
return waitRetry();
} else {
return abort();
}
}
Action parseHeaders() {
auto headersText = m_bufferStream.toString();
oatpp::parser::ParsingCaret caret (headersText);
http::Status status;
http::Protocol::parseResponseStartingLine(m_result.startingLine, headersText.getPtr(), caret, status);
if(status.code == 0) {
http::Protocol::parseHeaders(m_result.headers, headersText.getPtr(), caret, status);
if(status.code == 0) {
return _return(m_result);
} else {
return error("error occurred while parsing headers");
}
} else {
return error("can't parse starting line");
}
}
};
return parentCoroutine->startCoroutineForResult<ReaderCoroutine>(callback, connection, m_buffer, m_bufferSize, m_maxHeadersSize);
}
}}}}}

View File

@ -0,0 +1,74 @@
/***************************************************************************
*
* 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_web_protocol_http_incoming_ResponseHeadersReader_hpp
#define oatpp_web_protocol_http_incoming_ResponseHeadersReader_hpp
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/core/async/Coroutine.hpp"
namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming {
class ResponseHeadersReader {
public:
typedef oatpp::async::Action Action;
private:
static constexpr v_int32 SECTION_END = ('\r' << 24) | ('\n' << 16) | ('\r' << 8) | ('\n');
public:
struct Result {
http::ResponseStartingLine startingLine;
http::Protocol::Headers headers;
v_int32 bufferPosStart;
v_int32 bufferPosEnd;
};
public:
typedef Action (oatpp::async::AbstractCoroutine::*AsyncCallback)(const Result&);
private:
os::io::Library::v_size readHeadersSection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
oatpp::data::stream::OutputStream* bufferStream,
Result& result);
private:
p_char8 m_buffer;
v_int32 m_bufferSize;
v_int32 m_maxHeadersSize;
public:
ResponseHeadersReader(void* buffer, v_int32 bufferSize, v_int32 maxHeadersSize)
: m_buffer((p_char8) buffer)
, m_bufferSize(bufferSize)
, m_maxHeadersSize(maxHeadersSize)
{}
Result readHeaders(const std::shared_ptr<oatpp::data::stream::IOStream>& connection, http::HttpError::Info& error);
Action readHeadersAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
AsyncCallback callback,
const std::shared_ptr<oatpp::data::stream::IOStream>& connection);
};
}}}}}
#endif /* oatpp_web_protocol_http_incoming_ResponseHeadersReader_hpp */

View File

@ -83,21 +83,21 @@ void SimpleBodyDecoder::doChunkedDecoding(const std::shared_ptr<oatpp::data::str
}
void SimpleBodyDecoder::decode(const std::shared_ptr<Protocol::Headers>& headers,
void SimpleBodyDecoder::decode(const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const {
auto transferEncoding = headers->get(Header::TRANSFER_ENCODING, nullptr);
if(transferEncoding && transferEncoding->equals(Header::Value::TRANSFER_ENCODING_CHUNKED)) {
auto transferEncodingIt = headers.find(Header::TRANSFER_ENCODING);
if(transferEncodingIt != headers.end() && transferEncodingIt->second == Header::Value::TRANSFER_ENCODING_CHUNKED) {
doChunkedDecoding(bodyStream, toStream);
} else {
oatpp::os::io::Library::v_size contentLength = 0;
auto contentLengthStr = headers->get(Header::CONTENT_LENGTH, nullptr);
if(!contentLengthStr) {
auto contentLengthStrIt = headers.find(Header::CONTENT_LENGTH);
if(contentLengthStrIt == headers.end()) {
return; // DO NOTHING // it is an empty or invalid body
} else {
bool success;
contentLength = oatpp::utils::conversion::strToInt64(contentLengthStr, success);
contentLength = oatpp::utils::conversion::strToInt64(contentLengthStrIt->second.toString(), success);
if(!success){
return; // it is an invalid request/response
}
@ -217,20 +217,20 @@ oatpp::async::Action SimpleBodyDecoder::doChunkedDecodingAsync(oatpp::async::Abs
oatpp::async::Action SimpleBodyDecoder::decodeAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
const oatpp::async::Action& actionOnReturn,
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const {
auto transferEncoding = headers->get(Header::TRANSFER_ENCODING, nullptr);
if(transferEncoding && transferEncoding->equals(Header::Value::TRANSFER_ENCODING_CHUNKED)) {
auto transferEncodingIt = headers.find(Header::TRANSFER_ENCODING);
if(transferEncodingIt != headers.end() && transferEncodingIt->second == Header::Value::TRANSFER_ENCODING_CHUNKED) {
return doChunkedDecodingAsync(parentCoroutine, actionOnReturn, bodyStream, toStream);
} else {
oatpp::os::io::Library::v_size contentLength = 0;
auto contentLengthStr = headers->get(Header::CONTENT_LENGTH, nullptr);
if(!contentLengthStr) {
auto contentLengthStrIt = headers.find(Header::CONTENT_LENGTH);
if(contentLengthStrIt == headers.end()) {
return actionOnReturn; // DO NOTHING // it is an empty or invalid body
} else {
bool success;
contentLength = oatpp::utils::conversion::strToInt64(contentLengthStr, success);
contentLength = oatpp::utils::conversion::strToInt64(contentLengthStrIt->second.toString(), success);
if(!success){
return oatpp::async::Action(oatpp::async::Error("Invalid 'Content-Length' Header"));
}

View File

@ -43,13 +43,13 @@ private:
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream);
public:
void decode(const std::shared_ptr<Protocol::Headers>& headers,
void decode(const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const override;
oatpp::async::Action decodeAsync(oatpp::async::AbstractCoroutine* parentCoroutine,
const oatpp::async::Action& actionOnReturn,
const std::shared_ptr<Protocol::Headers>& headers,
const Protocol::Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const override;

View File

@ -25,6 +25,8 @@
#ifndef oatpp_web_protocol_http_outgoing_Body_hpp
#define oatpp_web_protocol_http_outgoing_Body_hpp
#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"
@ -35,14 +37,14 @@ class Body {
protected:
typedef oatpp::async::Action Action;
protected:
typedef oatpp::collection::ListMap<oatpp::String, oatpp::String> Headers;
typedef http::Protocol::Headers Headers;
typedef oatpp::data::stream::OutputStream OutputStream;
public:
/**
* declare headers describing body
*/
virtual void declareHeaders(const std::shared_ptr<Headers>& headers) noexcept = 0;
virtual void declareHeaders(Headers& headers) noexcept = 0;
/**
* write content to stream

View File

@ -47,9 +47,8 @@ public:
return Shared_Http_Outgoing_BufferBody_Pool::allocateShared(buffer);
}
void declareHeaders(const std::shared_ptr<Headers>& headers) noexcept override {
headers->put(oatpp::String(oatpp::web::protocol::http::Header::CONTENT_LENGTH, false),
oatpp::utils::conversion::int32ToStr(m_buffer->getSize()));
void declareHeaders(Headers& headers) noexcept override {
headers[oatpp::web::protocol::http::Header::CONTENT_LENGTH] = oatpp::utils::conversion::int32ToStr(m_buffer->getSize());
}
void writeToStream(const std::shared_ptr<OutputStream>& stream) noexcept override {

View File

@ -58,13 +58,11 @@ public:
return Shared_Http_Outgoing_ChunkedBufferBody_Pool::allocateShared(buffer, chunked);
}
void declareHeaders(const std::shared_ptr<Headers>& headers) noexcept override {
void declareHeaders(Headers& headers) noexcept override {
if(m_chunked){
headers->put(oatpp::web::protocol::http::Header::TRANSFER_ENCODING,
oatpp::web::protocol::http::Header::Value::TRANSFER_ENCODING_CHUNKED);
headers[oatpp::web::protocol::http::Header::TRANSFER_ENCODING] = oatpp::web::protocol::http::Header::Value::TRANSFER_ENCODING_CHUNKED;
} else {
headers->put(oatpp::web::protocol::http::Header::CONTENT_LENGTH,
oatpp::utils::conversion::int64ToStr(m_buffer->getSize()));
headers[oatpp::web::protocol::http::Header::CONTENT_LENGTH] = oatpp::utils::conversion::int64ToStr(m_buffer->getSize());
}
}

View File

@ -26,6 +26,11 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
bool CommunicationUtils::headerEqualsCI_FAST(const oatpp::data::share::MemoryLabel& headerValue, const char* value) {
v_int32 size = (v_int32) std::strlen(value);
return size == headerValue.getSize() && oatpp::base::StrBuffer::equalsCI_FAST(headerValue.getData(), value, size);
}
bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<protocol::http::incoming::Request>& request,
const std::shared_ptr<protocol::http::outgoing::Response>& response){
@ -33,24 +38,24 @@ bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<proto
/* Set keep-alive to value specified in the client's request, if no Connection header present in response. */
/* Set keep-alive to value specified in response otherwise */
auto& inKeepAlive = request->headers->get(String(Header::CONNECTION, false), nullptr);
if(inKeepAlive && oatpp::base::StrBuffer::equalsCI_FAST(inKeepAlive.get(), Header::Value::CONNECTION_KEEP_ALIVE)) {
if(response->headers->putIfNotExists(String(Header::CONNECTION, false), inKeepAlive)){
auto it = request->headers.find(Header::CONNECTION);
if(it != request->headers.end() && headerEqualsCI_FAST(it->second, Header::Value::CONNECTION_KEEP_ALIVE)) {
if(response->putHeaderIfNotExists(Header::CONNECTION, it->second)){
return true;
} else {
auto& outKeepAlive = response->headers->get(Header::CONNECTION, nullptr);
return (outKeepAlive && oatpp::base::StrBuffer::equalsCI_FAST(outKeepAlive.get(), Header::Value::CONNECTION_KEEP_ALIVE));
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
}
/* If protocol == HTTP/1.1 */
/* 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 */
String& protocol = request->startingLine->protocol;
if(protocol && oatpp::base::StrBuffer::equalsCI_FAST(protocol.get(), "HTTP/1.1")) {
if(!response->headers->putIfNotExists(String(Header::CONNECTION, false), String(Header::Value::CONNECTION_KEEP_ALIVE, false))) {
auto& outKeepAlive = response->headers->get(String(Header::CONNECTION, false), nullptr);
return (outKeepAlive && oatpp::base::StrBuffer::equalsCI_FAST(outKeepAlive.get(), Header::Value::CONNECTION_KEEP_ALIVE));
auto& protocol = request->startingLine.protocol;
if(protocol.getData() != nullptr && headerEqualsCI_FAST(protocol, "HTTP/1.1")) {
if(!response->putHeaderIfNotExists(Header::CONNECTION, Header::Value::CONNECTION_KEEP_ALIVE)) {
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
return true;
}
@ -60,9 +65,9 @@ bool CommunicationUtils::considerConnectionKeepAlive(const std::shared_ptr<proto
/* If protocol != HTTP/1.1 */
/* Set default Connection header value (Close), if no Connection header present in response. */
/* Set keep-alive to value specified in response otherwise */
if(!response->headers->putIfNotExists(String(Header::CONNECTION, false), String(Header::Value::CONNECTION_CLOSE, false))) {
auto& outKeepAlive = response->headers->get(String(Header::CONNECTION, false), nullptr);
return (outKeepAlive && oatpp::base::StrBuffer::equalsCI_FAST(outKeepAlive.get(), Header::Value::CONNECTION_KEEP_ALIVE));
if(!response->putHeaderIfNotExists(Header::CONNECTION, Header::Value::CONNECTION_CLOSE)) {
auto outKeepAlive = response->getHeaders().find(Header::CONNECTION);
return (outKeepAlive != response->getHeaders().end() && headerEqualsCI_FAST(outKeepAlive->second, Header::Value::CONNECTION_KEEP_ALIVE));
}
return false;

View File

@ -31,6 +31,8 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace outgoing {
class CommunicationUtils {
private:
static bool headerEqualsCI_FAST(const oatpp::data::share::MemoryLabel& headerValue, const char* value);
public:
/**

View File

@ -60,12 +60,16 @@ public:
return Shared_Http_Outgoing_DtoBody_Pool::allocateShared(dto, objectMapper, chunked);
}
void declareHeaders(const std::shared_ptr<Headers>& headers) noexcept override {
void declareHeaders(Headers& headers) noexcept override {
if(m_dto) {
m_objectMapper->write(m_buffer, m_dto);
}
ChunkedBufferBody::declareHeaders(headers);
headers->putIfNotExists(Header::CONTENT_TYPE, m_objectMapper->getInfo().http_content_type);
auto it = headers.find(Header::CONTENT_TYPE);
if(it == headers.end()) {
headers[Header::CONTENT_TYPE] = m_objectMapper->getInfo().http_content_type;
}
}
};

View File

@ -29,31 +29,31 @@ namespace oatpp { namespace web { namespace protocol { namespace http { namespac
void Request::send(const std::shared_ptr<data::stream::OutputStream>& stream){
if(body){
body->declareHeaders(headers);
if(m_body){
m_body->declareHeaders(m_headers);
} else {
headers->put(Header::CONTENT_LENGTH, "0");
m_headers[Header::CONTENT_LENGTH] = "0";
}
stream->write(method);
stream->write(m_method.getData(), m_method.getSize());
stream->write(" /", 2);
stream->write(path);
stream->write(m_path.getData(), m_path.getSize());
stream->write(" ", 1);
stream->write("HTTP/1.1", 8);
stream->write("\r\n", 2);
auto curr = headers->getFirstEntry();
while(curr != nullptr){
stream->write(curr->getKey()->getData(), curr->getKey()->getSize());
auto it = m_headers.begin();
while(it != m_headers.end()){
stream->write(it->first.getData(), it->first.getSize());
stream->write(": ", 2);
stream->write(curr->getValue()->getData(), curr->getValue()->getSize());
stream->write(it->second.getData(), it->second.getSize());
stream->write("\r\n", 2);
curr = curr->getNext();
it ++;
}
stream->write("\r\n", 2);
if(body) {
body->writeToStream(stream);
if(m_body) {
m_body->writeToStream(stream);
}
}
@ -78,26 +78,26 @@ oatpp::async::Action Request::sendAsync(oatpp::async::AbstractCoroutine* parentC
Action act() {
if(m_request->body){
m_request->body->declareHeaders(m_request->headers);
if(m_request->m_body){
m_request->m_body->declareHeaders(m_request->m_headers);
} else {
m_request->headers->put(Header::CONTENT_LENGTH, "0");
m_request->m_headers[Header::CONTENT_LENGTH] = "0";
}
m_buffer->data::stream::OutputStream::write(m_request->method);
m_buffer->write(m_request->m_method.getData(), m_request->m_method.getSize());
m_buffer->write(" /", 2);
m_buffer->data::stream::OutputStream::write(m_request->path);
m_buffer->write(m_request->m_path.getData(), m_request->m_path.getSize());
m_buffer->write(" ", 1);
m_buffer->write("HTTP/1.1", 8);
m_buffer->write("\r\n", 2);
auto curr = m_request->headers->getFirstEntry();
while(curr != nullptr){
m_buffer->write(curr->getKey()->getData(), curr->getKey()->getSize());
auto it = m_request->m_headers.begin();
while(it != m_request->m_headers.end()){
m_buffer->write(it->first.getData(), it->first.getSize());
m_buffer->write(": ", 2);
m_buffer->write(curr->getValue()->getData(), curr->getValue()->getSize());
m_buffer->write(it->second.getData(), it->second.getSize());
m_buffer->write("\r\n", 2);
curr = curr->getNext();
it++;
}
m_buffer->write("\r\n", 2);
@ -111,8 +111,8 @@ oatpp::async::Action Request::sendAsync(oatpp::async::AbstractCoroutine* parentC
}
Action writeBody() {
if(m_request->body) {
return m_request->body->writeToStreamAsync(this, finish(), m_stream);
if(m_request->m_body) {
return m_request->m_body->writeToStreamAsync(this, finish(), m_stream);
}
return finish();
}

View File

@ -36,33 +36,62 @@ public:
public:
OBJECT_POOL(Outgoing_Request_Pool, Request, 32)
SHARED_OBJECT_POOL(Shared_Outgoing_Request_Pool, Request, 32)
private:
oatpp::data::share::StringKeyLabel m_method;
oatpp::data::share::StringKeyLabel m_path;
Headers m_headers;
std::shared_ptr<Body> m_body;
public:
Request() {}
Request(const oatpp::String& pMethod,
const oatpp::String& pPath,
const std::shared_ptr<Headers>& pHeaders,
const std::shared_ptr<Body>& pBody)
: method(pMethod)
, path(pPath)
, headers((!pHeaders) ? (Headers::createShared()) : (pHeaders))
, body(pBody)
Request(const oatpp::data::share::StringKeyLabel& method,
const oatpp::data::share::StringKeyLabel& path,
const Headers& headers,
const std::shared_ptr<Body>& body)
: m_method(method)
, m_path(path)
, m_headers(headers)
, m_body(body)
{}
public:
static std::shared_ptr<Request> createShared(const oatpp::String& method,
const oatpp::String& path,
const std::shared_ptr<Headers>& headers,
const std::shared_ptr<Body>& body) {
static std::shared_ptr<Request> createShared(const oatpp::data::share::StringKeyLabel& method,
const oatpp::data::share::StringKeyLabel& path,
const Headers& headers,
const std::shared_ptr<Body>& body) {
return Shared_Outgoing_Request_Pool::allocateShared(method, path, headers, body);
}
const oatpp::String method;
const oatpp::String path;
oatpp::data::share::StringKeyLabel getMethod() {
return m_method;
}
const std::shared_ptr<Headers> headers;
const std::shared_ptr<Body> body;
oatpp::data::share::StringKeyLabel getPath() {
return m_path;
}
Headers& getHeaders() {
return m_headers;
}
void putHeader(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers[key] = value;
}
bool putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
auto it = m_headers.find(key);
if(it == m_headers.end()) {
m_headers.insert({key, value});
return true;
}
return false;
}
std::shared_ptr<Body> getBody() {
return m_body;
}
void send(const std::shared_ptr<data::stream::OutputStream>& stream);

View File

@ -30,30 +30,30 @@ namespace oatpp { namespace web { namespace protocol { namespace http { namespac
void Response::send(const std::shared_ptr<data::stream::OutputStream>& stream) {
if(body){
body->declareHeaders(headers);
if(m_body){
m_body->declareHeaders(m_headers);
} else {
headers->put(Header::CONTENT_LENGTH, "0");
m_headers[Header::CONTENT_LENGTH] = "0";
}
stream->write("HTTP/1.1 ", 9);
stream->writeAsString(status.code);
stream->writeAsString(m_status.code);
stream->write(" ", 1);
stream->OutputStream::write(status.description);
stream->OutputStream::write(m_status.description);
stream->write("\r\n", 2);
auto curr = headers->getFirstEntry();
while(curr != nullptr){
stream->write(curr->getKey()->getData(), curr->getKey()->getSize());
auto it = m_headers.begin();
while(it != m_headers.end()) {
stream->write(it->first.getData(), it->first.getSize());
stream->write(": ", 2);
stream->write(curr->getValue()->getData(), curr->getValue()->getSize());
stream->write(it->second.getData(), it->second.getSize());
stream->write("\r\n", 2);
curr = curr->getNext();
it ++;
}
stream->write("\r\n", 2);
if(body) {
body->writeToStream(stream);
if(m_body) {
m_body->writeToStream(stream);
}
}
@ -78,25 +78,25 @@ oatpp::async::Action Response::sendAsync(oatpp::async::AbstractCoroutine* parent
Action act() {
if(m_response->body){
m_response->body->declareHeaders(m_response->headers);
if(m_response->m_body){
m_response->m_body->declareHeaders(m_response->m_headers);
} else {
m_response->headers->put(Header::CONTENT_LENGTH, "0");
m_response->m_headers[Header::CONTENT_LENGTH] = "0";
}
m_buffer->write("HTTP/1.1 ", 9);
m_buffer->writeAsString(m_response->status.code);
m_buffer->writeAsString(m_response->m_status.code);
m_buffer->write(" ", 1);
m_buffer->OutputStream::write(m_response->status.description);
m_buffer->OutputStream::write(m_response->m_status.description);
m_buffer->write("\r\n", 2);
auto curr = m_response->headers->getFirstEntry();
while(curr != nullptr){
m_buffer->write(curr->getKey()->getData(), curr->getKey()->getSize());
auto it = m_response->m_headers.begin();
while(it != m_response->m_headers.end()) {
m_buffer->write(it->first.getData(), it->first.getSize());
m_buffer->write(": ", 2);
m_buffer->write(curr->getValue()->getData(), curr->getValue()->getSize());
m_buffer->write(it->second.getData(), it->second.getSize());
m_buffer->write("\r\n", 2);
curr = curr->getNext();
it ++;
}
m_buffer->write("\r\n", 2);
@ -110,8 +110,8 @@ oatpp::async::Action Response::sendAsync(oatpp::async::AbstractCoroutine* parent
}
Action writeBody() {
if(m_response->body) {
return m_response->body->writeToStreamAsync(this, finish(), m_stream);
if(m_response->m_body) {
return m_response->m_body->writeToStreamAsync(this, finish(), m_stream);
}
return finish();
}

View File

@ -33,27 +33,47 @@ namespace oatpp { namespace web { namespace protocol { namespace http { namespac
class Response : public oatpp::base::Controllable {
public:
typedef oatpp::collection::ListMap<oatpp::String, oatpp::String> Headers;
typedef http::Protocol::Headers Headers;
public:
OBJECT_POOL(Outgoing_Response_Pool, Response, 32)
SHARED_OBJECT_POOL(Shared_Outgoing_Response_Pool, Response, 32)
private:
Status m_status;
Headers m_headers;
std::shared_ptr<Body> m_body;
public:
Response(const Status& pStatus,
const std::shared_ptr<Body>& pBody)
: status(pStatus)
, headers(Headers::createShared())
, body(pBody)
Response(const Status& status,
const std::shared_ptr<Body>& body)
: m_status(status)
, m_body(body)
{}
public:
static std::shared_ptr<Response> createShared(const Status& status,
const std::shared_ptr<Body>& body) {
const std::shared_ptr<Body>& body) {
return Shared_Outgoing_Response_Pool::allocateShared(status, body);
}
const Status status;
const std::shared_ptr<Headers> headers;
const std::shared_ptr<Body> body;
Status getStatus() {
return m_status;
}
Headers& getHeaders() {
return m_headers;
}
void putHeader(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
m_headers[key] = value;
}
bool putHeaderIfNotExists(const oatpp::data::share::StringKeyLabelCI_FAST& key, const oatpp::data::share::StringKeyLabel& value) {
auto it = m_headers.find(key);
if(it == m_headers.end()) {
m_headers.insert({key, value});
return true;
}
return false;
}
void send(const std::shared_ptr<data::stream::OutputStream>& stream);

View File

@ -28,7 +28,6 @@
#include "oatpp/web/protocol/http/incoming/Request.hpp"
#include "oatpp/web/protocol/http/Http.hpp"
#include "oatpp/web/server/HttpError.hpp"
#include "oatpp/network/Connection.hpp"
#include "oatpp/test/Checker.hpp"
@ -65,7 +64,7 @@ void AsyncHttpConnectionHandler::Task::run(){
oatpp::async::Processor processor;
while(true) {
while(m_isRunning) {
/* Load all waiting connections into processor */
consumeConnections(processor);
@ -78,10 +77,7 @@ void AsyncHttpConnectionHandler::Task::run(){
std::unique_lock<std::mutex> lock(m_taskMutex);
if(processor.isEmpty()) {
/* No tasks in the processor. Wait for incoming connections */
//OATPP_LOGD("proc", "waiting for new connections");
while (m_connections.getFirstNode() == nullptr) {
m_taskCondition.wait(lock);
}
m_taskCondition.wait_for(lock, std::chrono::milliseconds(500));
} else {
/* There is still something in slow queue. Wait and get back to processing */
/* Waiting for IO is not Applicable here as slow queue may contain NON-IO tasks */

View File

@ -63,6 +63,7 @@ private:
oatpp::concurrency::SpinLock::Atom m_atom;
Connections m_connections;
private:
bool m_isRunning;
HttpRouter* m_router;
std::shared_ptr<const oatpp::web::protocol::http::incoming::BodyDecoder> m_bodyDecoder;
std::shared_ptr<handler::ErrorHandler> m_errorHandler;
@ -75,6 +76,7 @@ private:
const std::shared_ptr<handler::ErrorHandler>& errorHandler,
HttpProcessor::RequestInterceptors* requestInterceptors)
: m_atom(false)
, m_isRunning(true)
, m_router(router)
, m_bodyDecoder(bodyDecoder)
, m_errorHandler(errorHandler)
@ -97,6 +99,10 @@ private:
m_taskCondition.notify_one();
}
void stop() {
m_isRunning = false;
}
};
private:
@ -150,6 +156,12 @@ public:
void handleConnection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection) override;
void stop() {
for(v_int32 i = 0; i < m_threadCount; i ++) {
m_tasks[i]->stop();
}
}
};
}}}

View File

@ -23,7 +23,6 @@
***************************************************************************/
#include "./HttpConnectionHandler.hpp"
#include "./HttpError.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBufferBody.hpp"
#include "oatpp/web/protocol/http/incoming/Request.hpp"

View File

@ -23,7 +23,6 @@
***************************************************************************/
#include "HttpProcessor.hpp"
#include "./HttpError.hpp"
#include "oatpp/web/protocol/http/outgoing/CommunicationUtils.hpp"
@ -42,102 +41,81 @@ HttpProcessor::processRequest(HttpRouter* router,
const std::shared_ptr<oatpp::data::stream::InputStreamBufferedProxy>& inStream,
bool& keepAlive) {
keepAlive = false;
auto readCount = connection->read(buffer, bufferSize);
if(readCount > 0) {
oatpp::parser::ParsingCaret caret((p_char8)buffer, bufferSize);
auto line = protocol::http::Protocol::parseRequestStartingLine(caret);
if(!line){
return errorHandler->handleError(protocol::http::Status::CODE_400, "Can't read starting line");
}
auto route = router->getRoute(line->method, line->path);
if(route) {
oatpp::web::protocol::http::Status error;
auto headers = protocol::http::Protocol::parseHeaders(caret, error);
if(error.code != 0){
return errorHandler->handleError(error, " Can't parse headers");
}
auto bodyStream = inStream;
bodyStream->setBufferPosition(caret.getPosition(), (v_int32) readCount);
auto request = protocol::http::incoming::Request::createShared(line, route.matchMap, headers, bodyStream, bodyDecoder);
std::shared_ptr<protocol::http::outgoing::Response> response;
try{
auto currInterceptor = requestInterceptors->getFirstNode();
while (currInterceptor != nullptr) {
response = currInterceptor->getData()->intercept(request);
if(response) {
break;
}
currInterceptor = currInterceptor->getNext();
}
if(!response) {
response = route.processUrl(request);
}
} catch (HttpError& error) {
return errorHandler->handleError(error.getStatus(), error.getMessage());
} catch (std::exception& error) {
return errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
} catch (...) {
return errorHandler->handleError(protocol::http::Status::CODE_500, "Unknown error");
}
response->headers->putIfNotExists(oatpp::String(protocol::http::Header::SERVER, false),
oatpp::String(protocol::http::Header::Value::SERVER, false));
keepAlive = oatpp::web::protocol::http::outgoing::CommunicationUtils::considerConnectionKeepAlive(request, response);
return response;
} else {
return errorHandler->handleError(protocol::http::Status::CODE_404, "Current url has no mapping");
}
} if(readCount == oatpp::data::stream::Errors::ERROR_IO_NOTHING_TO_READ) {
keepAlive = true;
RequestHeadersReader headersReader(buffer, bufferSize, 4096);
oatpp::web::protocol::http::HttpError::Info error;
auto headersReadResult = headersReader.readHeaders(connection, error);
if(error.status.code != 0) {
return errorHandler->handleError(error.status, "Invalid request headers");
}
return nullptr;
if(error.ioStatus < 0) {
keepAlive = false;
return nullptr;
}
auto route = router->getRoute(headersReadResult.startingLine.method.toString(), headersReadResult.startingLine.path.toString());
if(!route) {
return errorHandler->handleError(protocol::http::Status::CODE_404, "Current url has no mapping");
}
auto& bodyStream = inStream;
bodyStream->setBufferPosition(headersReadResult.bufferPosStart, headersReadResult.bufferPosEnd);
auto request = protocol::http::incoming::Request::createShared(headersReadResult.startingLine,
route.matchMap,
headersReadResult.headers,
bodyStream,
bodyDecoder);
std::shared_ptr<protocol::http::outgoing::Response> response;
try{
auto currInterceptor = requestInterceptors->getFirstNode();
while (currInterceptor != nullptr) {
response = currInterceptor->getData()->intercept(request);
if(response) {
break;
}
currInterceptor = currInterceptor->getNext();
}
if(!response) {
response = route.processUrl(request);
}
} catch (oatpp::web::protocol::http::HttpError& error) {
return errorHandler->handleError(error.getInfo().status, error.getMessage());
} catch (std::exception& error) {
return errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
} catch (...) {
return errorHandler->handleError(protocol::http::Status::CODE_500, "Unknown error");
}
response->putHeaderIfNotExists(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER);
keepAlive = oatpp::web::protocol::http::outgoing::CommunicationUtils::considerConnectionKeepAlive(request, response);
return response;
}
// HttpProcessor::Coroutine
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::parseRequest(v_int32 readCount) {
oatpp::async::Action HttpProcessor::Coroutine::onHeadersParsed(const RequestHeadersReader::Result& headersReadResult) {
oatpp::parser::ParsingCaret caret((p_char8) m_ioBuffer->getData(), readCount);
auto line = protocol::http::Protocol::parseRequestStartingLine(caret);
if(!line){
m_currentResponse = m_errorHandler->handleError(protocol::http::Status::CODE_400, "Can't read starting line");
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
}
m_currentRoute = m_router->getRoute(line->method, line->path);
m_currentRoute = m_router->getRoute(headersReadResult.startingLine.method.toString(), headersReadResult.startingLine.path.toString());
if(!m_currentRoute) {
m_currentResponse = m_errorHandler->handleError(protocol::http::Status::CODE_404, "Current url has no mapping");
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
}
oatpp::web::protocol::http::Status error;
auto headers = protocol::http::Protocol::parseHeaders(caret, error);
auto& bodyStream = m_inStream;
bodyStream->setBufferPosition(headersReadResult.bufferPosStart, headersReadResult.bufferPosEnd);
if(error.code != 0){
m_currentResponse = m_errorHandler->handleError(error, " Can't parse headers");
return yieldTo(&HttpProcessor::Coroutine::onResponseFormed);
}
auto bodyStream = m_inStream;
bodyStream->setBufferPosition(caret.getPosition(), readCount);
m_currentRequest = protocol::http::incoming::Request::createShared(line, m_currentRoute.matchMap, headers, bodyStream, m_bodyDecoder);
m_currentRequest = protocol::http::incoming::Request::createShared(headersReadResult.startingLine,
m_currentRoute.matchMap,
headersReadResult.headers,
bodyStream,
m_bodyDecoder);
auto currInterceptor = m_requestInterceptors->getFirstNode();
while (currInterceptor != nullptr) {
@ -149,19 +127,13 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::parseRequest(v_int32
}
return yieldTo(&HttpProcessor::Coroutine::onRequestFormed);
}
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::act() {
auto readCount = m_connection->read(m_ioBuffer->getData(), m_ioBuffer->getSize());
if(readCount > 0) {
m_currentResponse = nullptr;
return parseRequest((v_int32)readCount);
} else if(readCount == oatpp::data::stream::Errors::ERROR_IO_WAIT_RETRY) {
return waitRetry();
} else if(readCount == oatpp::data::stream::Errors::ERROR_IO_RETRY) {
return repeat();
}
return abort();
RequestHeadersReader::AsyncCallback callback = static_cast<RequestHeadersReader::AsyncCallback>(&HttpProcessor::Coroutine::onHeadersParsed);
RequestHeadersReader headersReader(m_ioBuffer->getData(), m_ioBuffer->getSize(), 4096);
return headersReader.readHeadersAsync(this, callback, m_connection);
}
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onRequestFormed() {
@ -177,8 +149,7 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponse(const std:
HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::onResponseFormed() {
m_currentResponse->headers->putIfNotExists(protocol::http::Header::SERVER,
protocol::http::Header::Value::SERVER);
m_currentResponse->putHeaderIfNotExists(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER);
m_keepAlive = oatpp::web::protocol::http::outgoing::CommunicationUtils::considerConnectionKeepAlive(m_currentRequest, m_currentResponse);
m_outStream->setBufferPosition(0, 0);
return m_currentResponse->sendAsync(this,
@ -208,8 +179,8 @@ HttpProcessor::Coroutine::Action HttpProcessor::Coroutine::handleError(const oat
if (error.isExceptionThrown) {
try{
throw;
} catch (HttpError& error) {
m_currentResponse = m_errorHandler->handleError(error.getStatus(), error.getMessage());
} catch (oatpp::web::protocol::http::HttpError& error) {
m_currentResponse = m_errorHandler->handleError(error.getInfo().status, error.getMessage());
} catch (std::exception& error) {
m_currentResponse = m_errorHandler->handleError(protocol::http::Status::CODE_500, error.what());
} catch (...) {

View File

@ -30,6 +30,8 @@
#include "./handler/Interceptor.hpp"
#include "./handler/ErrorHandler.hpp"
#include "oatpp/web/protocol/http/incoming/RequestHeadersReader.hpp"
#include "oatpp/web/protocol/http/incoming/Request.hpp"
#include "oatpp/web/protocol/http/outgoing/Response.hpp"
@ -43,6 +45,7 @@ public:
static const char* RETURN_KEEP_ALIVE;
public:
typedef oatpp::collection::LinkedList<std::shared_ptr<oatpp::web::server::handler::RequestInterceptor>> RequestInterceptors;
typedef oatpp::web::protocol::http::incoming::RequestHeadersReader RequestHeadersReader;
public:
class ConnectionState {
@ -64,8 +67,6 @@ public:
public:
class Coroutine : public oatpp::async::Coroutine<HttpProcessor::Coroutine> {
private:
Action parseRequest(v_int32 readCount);
private:
HttpRouter* m_router;
std::shared_ptr<const oatpp::web::protocol::http::incoming::BodyDecoder> m_bodyDecoder;
@ -103,6 +104,8 @@ public:
Action act() override;
Action onHeadersParsed(const RequestHeadersReader::Result& headersReadResult);
Action onRequestFormed();
Action onResponse(const std::shared_ptr<protocol::http::outgoing::Response>& response);
Action onResponseFormed();

View File

@ -51,7 +51,7 @@ std::shared_ptr<ApiController::OutgoingResponse> ApiController::handleError(cons
if(m_errorHandler) {
return m_errorHandler->handleError(status, message);
}
throw oatpp::web::server::HttpError(status, message);
throw oatpp::web::protocol::http::HttpError(status, message);
}
void ApiController::setErrorHandler(const std::shared_ptr<handler::ErrorHandler>& errorHandler){

View File

@ -27,7 +27,6 @@
#include "./Endpoint.hpp"
#include "oatpp/web/server/HttpError.hpp"
#include "oatpp/web/server/handler/ErrorHandler.hpp"
#include "oatpp/web/server/HttpConnectionHandler.hpp"
#include "oatpp/web/url/mapping/Router.hpp"
@ -117,7 +116,11 @@ protected:
}
std::shared_ptr<OutgoingResponse> processUrl(const std::shared_ptr<protocol::http::incoming::Request>& request) override {
return (m_controller->*m_method)(request);
if(m_method != nullptr) {
return (m_controller->*m_method)(request);
} else {
return m_controller->handleError(Status::CODE_500, "Using simple model for Async endpoint");
}
}
Action processUrlAsync(oatpp::async::AbstractCoroutine* parentCoroutine,

View File

@ -42,8 +42,8 @@ DefaultErrorHandler::handleError(const protocol::http::Status& status, const oat
auto response = protocol::http::outgoing::Response::createShared
(status, protocol::http::outgoing::ChunkedBufferBody::createShared(stream));
response->headers->put(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER);
response->headers->put(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE);
response->putHeader(protocol::http::Header::SERVER, protocol::http::Header::Value::SERVER);
response->putHeader(protocol::http::Header::CONNECTION, protocol::http::Header::Value::CONNECTION_CLOSE);
return response;