Merge pull request #496 from oatpp/auto_handle_expect_100

SimpleBodyDecoder: automatically handle 'Expect: 100' header.
This commit is contained in:
Leonid Stryzhevskyi 2021-10-21 02:57:27 +03:00 committed by GitHub
commit ebabbe8517
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 125 additions and 46 deletions

View File

@ -106,6 +106,8 @@ const char* const Header::Value::USER_AGENT = "oatpp/" OATPP_VERSION;
const char* const Header::Value::TRANSFER_ENCODING_CHUNKED = "chunked";
const char* const Header::Value::CONTENT_TYPE_APPLICATION_JSON = "application/json";
const char* const Header::Value::EXPECT_100_CONTINUE = "100-continue";
const char* const Header::ACCEPT = "Accept";
const char* const Header::AUTHORIZATION = "Authorization";
@ -130,6 +132,8 @@ const char* const Header::CORS_MAX_AGE = "Access-Control-Max-Age";
const char* const Header::ACCEPT_ENCODING = "Accept-Encoding";
const char* const Header::EXPECT = "Expect";
const char* const Range::UNIT_BYTES = "bytes";
const char* const ContentRange::UNIT_BYTES = "bytes";

View File

@ -481,6 +481,8 @@ public:
static const char* const TRANSFER_ENCODING_CHUNKED;
static const char* const CONTENT_TYPE_APPLICATION_JSON;
static const char* const EXPECT_100_CONTINUE;
};
public:
static const char* const ACCEPT; // "Accept"
@ -502,6 +504,7 @@ public:
static const char* const CORS_HEADERS; // Access-Control-Allow-Headers
static const char* const CORS_MAX_AGE; // Access-Control-Max-Age
static const char* const ACCEPT_ENCODING; // Accept-Encoding
static const char* const EXPECT; // Expect
};
class Range {

View File

@ -30,27 +30,34 @@ namespace oatpp { namespace web { namespace protocol { namespace http { namespac
// BodyDecoder
oatpp::async::CoroutineStarterForResult<const oatpp::String&>
BodyDecoder::decodeToStringAsync(const Headers& headers, const std::shared_ptr<data::stream::InputStream>& bodyStream) const {
BodyDecoder::decodeToStringAsync(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::IOStream>& connection) const
{
class ToStringDecoder : public oatpp::async::CoroutineWithResult<ToStringDecoder, const oatpp::String&> {
private:
const BodyDecoder* m_decoder;
Headers m_headers;
std::shared_ptr<oatpp::data::stream::InputStream> m_bodyStream;
std::shared_ptr<oatpp::data::stream::ChunkedBuffer> m_outputStream;
std::shared_ptr<data::stream::InputStream> m_bodyStream;
std::shared_ptr<data::stream::IOStream> m_connection;
std::shared_ptr<data::stream::ChunkedBuffer> m_outputStream;
public:
ToStringDecoder(const BodyDecoder* decoder,
const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream)
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::IOStream>& connection)
: m_decoder(decoder)
, m_headers(headers)
, m_bodyStream(bodyStream)
, m_connection(connection)
, m_outputStream(std::make_shared<data::stream::ChunkedBuffer>())
{}
Action act() override {
return m_decoder->decodeAsync(m_headers, m_bodyStream, m_outputStream).next(yieldTo(&ToStringDecoder::onDecoded));
return m_decoder->decodeAsync(m_headers, m_bodyStream, m_outputStream, m_connection)
.next(yieldTo(&ToStringDecoder::onDecoded));
}
Action onDecoded() {
@ -59,7 +66,7 @@ BodyDecoder::decodeToStringAsync(const Headers& headers, const std::shared_ptr<d
};
return ToStringDecoder::startForResult(this, headers, bodyStream);
return ToStringDecoder::startForResult(this, headers, bodyStream, connection);
}

View File

@ -46,24 +46,28 @@ private:
private:
const BodyDecoder* m_decoder;
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_outputStream;
std::shared_ptr<data::stream::InputStream> m_bodyStream;
std::shared_ptr<data::stream::IOStream> m_connection;
std::shared_ptr<data::mapping::ObjectMapper> m_objectMapper;
std::shared_ptr<data::stream::ChunkedBuffer> m_outputStream;
public:
ToDtoDecoder(const BodyDecoder* decoder,
Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper)
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::IOStream>& connection,
const std::shared_ptr<data::mapping::ObjectMapper>& objectMapper)
: m_decoder(decoder)
, m_headers(headers)
, m_bodyStream(bodyStream)
, m_connection(connection)
, m_objectMapper(objectMapper)
, m_outputStream(std::make_shared<oatpp::data::stream::ChunkedBuffer>())
, m_outputStream(std::make_shared<data::stream::ChunkedBuffer>())
{}
oatpp::async::Action act() override {
return m_decoder->decodeAsync(m_headers, m_bodyStream, m_outputStream).next(this->yieldTo(&ToDtoDecoder::onDecoded));
return m_decoder->decodeAsync(m_headers, m_bodyStream, m_outputStream, m_connection)
.next(this->yieldTo(&ToDtoDecoder::onDecoded));
}
oatpp::async::Action onDecoded() {
@ -90,29 +94,39 @@ public:
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - pointer to &id:oatpp::data::stream::InputStream;.
* @param writeCallback - &id:oatpp::data::stream::WriteCallback;.
* @param connection
*/
virtual void decode(const Headers& headers, data::stream::InputStream* bodyStream, data::stream::WriteCallback* writeCallback) const = 0;
virtual void decode(const Headers& headers,
data::stream::InputStream* bodyStream,
data::stream::WriteCallback* writeCallback,
data::stream::IOStream* connection) const = 0;
/**
* Implement this method! Same as &l:BodyDecoder::decode (); but Async.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - `std::shared_ptr` to &id:oatpp::data::stream::InputStream;.
* @param writeCallback - `std::shared_ptr` to &id:oatpp::data::stream::WriteCallback;.
* @param connection
* @return - &id:oatpp::async::CoroutineStarter;.
*/
virtual oatpp::async::CoroutineStarter decodeAsync(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::WriteCallback>& writeCallback) const = 0;
const std::shared_ptr<data::stream::WriteCallback>& writeCallback,
const std::shared_ptr<data::stream::IOStream>& connection) const = 0;
/**
* Read body stream and decode it to string.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - pointer to &id:oatpp::data::stream::InputStream;.
* @param connection
* @return - &oatpp::String;.
*/
oatpp::String decodeToString(const Headers& headers, data::stream::InputStream* bodyStream) const {
oatpp::String decodeToString(const Headers& headers,
data::stream::InputStream* bodyStream,
data::stream::IOStream* connection) const
{
oatpp::data::stream::ChunkedBuffer stream;
decode(headers, bodyStream, &stream);
decode(headers, bodyStream, &stream, connection);
return stream.toString();
}
@ -121,30 +135,37 @@ public:
* @tparam Wrapper - ObjectWrapper type.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - pointer to &id:oatpp::data::stream::InputStream;.
* @param connection
* @param objectMapper - pointer to &id:oatpp::data::mapping::ObjectMapper;.
* @return - deserialized DTO object.
*/
template<class Wrapper>
Wrapper decodeToDto(const Headers& headers,
data::stream::InputStream* bodyStream,
data::mapping::ObjectMapper* objectMapper) const {
return objectMapper->readFromString<Wrapper>(decodeToString(headers, bodyStream));
data::stream::IOStream* connection,
data::mapping::ObjectMapper* objectMapper) const
{
return objectMapper->readFromString<Wrapper>(decodeToString(headers, bodyStream, connection));
}
/**
* Same as &l:BodyDecoder::decodeToString (); but Async.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - `std::shared_ptr` to &id:oatpp::data::stream::InputStream;.
* @param connection
* @return - &id:oatpp::async::CoroutineStarterForResult;.
*/
oatpp::async::CoroutineStarterForResult<const oatpp::String&>
decodeToStringAsync(const Headers& headers, const std::shared_ptr<data::stream::InputStream>& bodyStream) const;
decodeToStringAsync(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::IOStream>& connection) const;
/**
* Same as &l:BodyDecoder::decodeToDto (); but Async.
* @tparam Wrapper - ObjectWrapper type.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - `std::shared_ptr` to &id:oatpp::data::stream::InputStream;.
* @param connection
* @param objectMapper - `std::shared_ptr` to &id:oatpp::data::mapping::ObjectMapper;.
* @return - &id:oatpp::async::CoroutineStarterForResult;.
*/
@ -152,8 +173,10 @@ public:
oatpp::async::CoroutineStarterForResult<const Wrapper&>
decodeToDtoAsync(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::mapping::ObjectMapper>& objectMapper) const {
return ToDtoDecoder<Wrapper>::startForResult(this, headers, bodyStream, objectMapper);
const std::shared_ptr<data::stream::IOStream>& connection,
const std::shared_ptr<data::mapping::ObjectMapper>& objectMapper) const
{
return ToDtoDecoder<Wrapper>::startForResult(this, headers, bodyStream, connection, objectMapper);
}
};

View File

@ -131,27 +131,27 @@ oatpp::String Request::getPathTail() const {
}
void Request::transferBody(data::stream::WriteCallback* writeCallback) const {
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback);
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback, m_connection.get());
}
void Request::transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const {
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream);
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream, m_connection.get());
}
oatpp::String Request::readBodyToString() const {
return m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get());
return m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get(), m_connection.get());
}
async::CoroutineStarter Request::transferBodyAsync(const std::shared_ptr<data::stream::WriteCallback>& writeCallback) const {
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, writeCallback);
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, writeCallback, m_connection);
}
async::CoroutineStarter Request::transferBodyToStreamAsync(const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const {
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, toStream);
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, toStream, m_connection);
}
async::CoroutineStarterForResult<const oatpp::String&> Request::readBodyToStringAsync() const {
return m_bodyDecoder->decodeToStringAsync(m_headers, m_bodyStream);
return m_bodyDecoder->decodeToStringAsync(m_headers, m_bodyStream, m_connection);
}
}}}}}

View File

@ -235,7 +235,7 @@ public:
*/
template<class Wrapper>
Wrapper readBodyToDto(data::mapping::ObjectMapper* objectMapper) const {
return objectMapper->readFromString<Wrapper>(m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get()));
return objectMapper->readFromString<Wrapper>(m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get(), m_connection.get()));
}
// Async
@ -270,7 +270,7 @@ public:
template<class Wrapper>
oatpp::async::CoroutineStarterForResult<const Wrapper&>
readBodyToDtoAsync(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper) const {
return m_bodyDecoder->decodeToDtoAsync<Wrapper>(m_headers, m_bodyStream, objectMapper);
return m_bodyDecoder->decodeToDtoAsync<Wrapper>(m_headers, m_bodyStream, m_connection, objectMapper);
}
};

View File

@ -95,23 +95,23 @@ std::shared_ptr<const http::incoming::BodyDecoder> Response::getBodyDecoder() co
}
void Response::transferBody(data::stream::WriteCallback* writeCallback) const {
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback);
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), writeCallback, m_connection.get());
}
void Response::transferBodyToStream(oatpp::data::stream::OutputStream* toStream) const {
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream);
m_bodyDecoder->decode(m_headers, m_bodyStream.get(), toStream, m_connection.get());
}
oatpp::String Response::readBodyToString() const {
return m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get());
return m_bodyDecoder->decodeToString(m_headers, m_bodyStream.get(), m_connection.get());
}
async::CoroutineStarter Response::transferBodyAsync(const std::shared_ptr<data::stream::WriteCallback>& writeCallback) const {
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, writeCallback);
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, writeCallback, m_connection);
}
oatpp::async::CoroutineStarter Response::transferBodyToStreamAsync(const std::shared_ptr<oatpp::data::stream::OutputStream>& toStream) const {
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, toStream);
return m_bodyDecoder->decodeAsync(m_headers, m_bodyStream, toStream, m_connection);
}
}}}}}

View File

@ -194,7 +194,7 @@ public:
*/
template<class Wrapper>
Wrapper readBodyToDto(oatpp::data::mapping::ObjectMapper* objectMapper) const {
return m_bodyDecoder->decodeToDto<Wrapper>(m_headers, m_bodyStream.get(), objectMapper);
return m_bodyDecoder->decodeToDto<Wrapper>(m_headers, m_bodyStream.get(), m_connection.get(), objectMapper);
}
// Async
@ -220,7 +220,7 @@ public:
* @return - &id:oatpp::async::CoroutineStarterForResult;.
*/
oatpp::async::CoroutineStarterForResult<const oatpp::String&> readBodyToStringAsync() const {
return m_bodyDecoder->decodeToStringAsync(m_headers, m_bodyStream);
return m_bodyDecoder->decodeToStringAsync(m_headers, m_bodyStream, m_connection);
}
/**
@ -232,7 +232,7 @@ public:
template<class Wrapper>
oatpp::async::CoroutineStarterForResult<const Wrapper&>
readBodyToDtoAsync(const std::shared_ptr<oatpp::data::mapping::ObjectMapper>& objectMapper) const {
return m_bodyDecoder->decodeToDtoAsync<Wrapper>(m_headers, m_bodyStream, objectMapper);
return m_bodyDecoder->decodeToDtoAsync<Wrapper>(m_headers, m_bodyStream, m_connection, objectMapper);
}
};

View File

@ -31,6 +31,8 @@
namespace oatpp { namespace web { namespace protocol { namespace http { namespace incoming {
const std::string SimpleBodyDecoder::RESPONSE_100_CONTINUE = "HTTP/1.1 100 Continue\r\n";
SimpleBodyDecoder::SimpleBodyDecoder(const std::shared_ptr<encoding::ProviderCollection>& contentDecoders)
: m_contentDecoders(contentDecoders)
{}
@ -77,10 +79,36 @@ SimpleBodyDecoder::getStreamProcessor (const data::share::StringKeyLabelCI& tran
}
void SimpleBodyDecoder::handleExpectHeader(const Headers& headers, data::stream::IOStream* connection) const {
auto expect = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::EXPECT);
if(expect == Header::Value::EXPECT_100_CONTINUE) {
auto res = connection->writeExactSizeDataSimple(RESPONSE_100_CONTINUE.data(), RESPONSE_100_CONTINUE.size());
if(res != RESPONSE_100_CONTINUE.size()) {
throw std::runtime_error("[oatpp::web::protocol::http::incoming::SimpleBodyDecoder::handleExpectHeader()]: "
"Error. Unable to send 100-continue response.");
}
}
}
oatpp::async::CoroutineStarter SimpleBodyDecoder::handleExpectHeaderAsync(const Headers& headers,
const std::shared_ptr<data::stream::IOStream>& connection) const
{
auto expect = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::EXPECT);
if(expect == Header::Value::EXPECT_100_CONTINUE) {
return connection->writeExactSizeDataAsync(RESPONSE_100_CONTINUE.data(), RESPONSE_100_CONTINUE.size());
}
return nullptr;
}
void SimpleBodyDecoder::decode(const Headers& headers,
data::stream::InputStream* bodyStream,
data::stream::WriteCallback* writeCallback) const {
data::stream::WriteCallback* writeCallback,
data::stream::IOStream* connection) const
{
handleExpectHeader(headers, connection);
auto transferEncoding = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::TRANSFER_ENCODING);
if(transferEncoding) {
@ -131,8 +159,11 @@ void SimpleBodyDecoder::decode(const Headers& headers,
async::CoroutineStarter SimpleBodyDecoder::decodeAsync(const Headers& headers,
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::WriteCallback>& writeCallback) const {
const std::shared_ptr<data::stream::WriteCallback>& writeCallback,
const std::shared_ptr<data::stream::IOStream>& connection) const
{
auto pipeline = handleExpectHeaderAsync(headers, connection);
auto transferEncoding = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::TRANSFER_ENCODING);
if(transferEncoding) {
@ -140,7 +171,7 @@ async::CoroutineStarter SimpleBodyDecoder::decodeAsync(const Headers& headers,
auto contentEncoding = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::CONTENT_ENCODING);
auto processor = getStreamProcessor(transferEncoding, contentEncoding);
auto buffer = data::buffer::IOBuffer::createShared();
return data::stream::transferAsync(bodyStream, writeCallback, 0 /* read until error */, buffer, processor);
return std::move(pipeline.next(data::stream::transferAsync(bodyStream, writeCallback, 0 /* read until error */, buffer, processor)));
} else {
@ -155,7 +186,7 @@ async::CoroutineStarter SimpleBodyDecoder::decodeAsync(const Headers& headers,
auto contentEncoding = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::CONTENT_ENCODING);
auto processor = getStreamProcessor(nullptr, contentEncoding);
auto buffer = data::buffer::IOBuffer::createShared();
return data::stream::transferAsync(bodyStream, writeCallback, contentLength, buffer, processor);
return std::move(pipeline.next(data::stream::transferAsync(bodyStream, writeCallback, contentLength, buffer, processor)));
}
@ -168,7 +199,7 @@ async::CoroutineStarter SimpleBodyDecoder::decodeAsync(const Headers& headers,
auto contentEncoding = headers.getAsMemoryLabel<data::share::StringKeyLabelCI>(Header::CONTENT_ENCODING);
auto processor = getStreamProcessor(nullptr, contentEncoding);
auto buffer = data::buffer::IOBuffer::createShared();
return data::stream::transferAsync(bodyStream, writeCallback, 0 /* read until error */, buffer, processor);
return std::move(pipeline.next(data::stream::transferAsync(bodyStream, writeCallback, 0 /* read until error */, buffer, processor)));
}

View File

@ -34,11 +34,16 @@ namespace oatpp { namespace web { namespace protocol { namespace http { namespac
* Default implementation of &id:oatpp::web::protocol::http::incoming::BodyDecoder;.
*/
class SimpleBodyDecoder : public BodyDecoder {
private:
static const std::string RESPONSE_100_CONTINUE;
private:
std::shared_ptr<encoding::ProviderCollection> m_contentDecoders;
private:
base::ObjectHandle<data::buffer::Processor> getStreamProcessor(const data::share::StringKeyLabelCI& transferEncoding,
const data::share::StringKeyLabelCI& contentEncoding) const;
void handleExpectHeader(const Headers& headers, data::stream::IOStream* connection) const;
oatpp::async::CoroutineStarter handleExpectHeaderAsync(const Headers& headers,
const std::shared_ptr<data::stream::IOStream>& connection) const;
public:
/**
@ -52,19 +57,25 @@ public:
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - pointer to &id:oatpp::data::stream::InputStream;.
* @param writeCallback - &id:oatpp::data::stream::WriteCallback;.
* @param connection
*/
void decode(const Headers& headers, data::stream::InputStream* bodyStream, data::stream::WriteCallback* writeCallback) const override;
void decode(const Headers& headers,
data::stream::InputStream* bodyStream,
data::stream::WriteCallback* writeCallback,
data::stream::IOStream* connection) const override;
/**
* Same as &l:SimpleBodyDecoder::decode (); but Async.
* @param headers - Headers map. &id:oatpp::web::protocol::http::Headers;.
* @param bodyStream - `std::shared_ptr` to &id:oatpp::data::stream::InputStream;.
* @param writeCallback - `std::shared_ptr` to &id:oatpp::data::stream::WriteCallback;.
* @param connection
* @return - &id:oatpp::async::CoroutineStarter;.
*/
oatpp::async::CoroutineStarter decodeAsync(const Headers& headers,
const std::shared_ptr<oatpp::data::stream::InputStream>& bodyStream,
const std::shared_ptr<oatpp::data::stream::WriteCallback>& writeCallback) const override;
const std::shared_ptr<data::stream::InputStream>& bodyStream,
const std::shared_ptr<data::stream::WriteCallback>& writeCallback,
const std::shared_ptr<data::stream::IOStream>& connection) const override;
};