Tests. Simple And Async Multipart APIs.

This commit is contained in:
lganzzzo 2019-07-25 14:01:13 +04:00
parent fa95bf6589
commit 464fc4da13
12 changed files with 169 additions and 73 deletions

View File

@ -67,8 +67,9 @@ data::v_io_size InMemoryReader::write(const void *data, data::v_io_size count) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AsyncInMemoryReader
AsyncInMemoryReader::AsyncInMemoryReader(Multipart* multipart)
: m_parser(multipart->getBoundary(), std::make_shared<InMemoryParser>(multipart))
AsyncInMemoryReader::AsyncInMemoryReader(const std::shared_ptr<Multipart>& multipart)
: m_parser(multipart->getBoundary(), std::make_shared<InMemoryParser>(multipart.get()))
, m_multipart(multipart)
{}
oatpp::async::Action AsyncInMemoryReader::writeAsyncInline(oatpp::async::AbstractCoroutine* coroutine,

View File

@ -79,13 +79,14 @@ public:
class AsyncInMemoryReader : public oatpp::data::stream::AsyncWriteCallback {
private:
StatefulParser m_parser;
std::shared_ptr<Multipart> m_multipart;
public:
/**
* Constructor.
* @param multipart - Multipart object to save read data to.
*/
AsyncInMemoryReader(Multipart* multipart);
AsyncInMemoryReader(const std::shared_ptr<Multipart>& multipart);
oatpp::async::Action writeAsyncInline(oatpp::async::AbstractCoroutine* coroutine,
oatpp::data::stream::AsyncInlineWriteData& inlineData,

View File

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

View File

@ -113,6 +113,13 @@ public:
*/
std::shared_ptr<const http::incoming::BodyDecoder> getBodyDecoder() const;
/**
* Transfer body. <br>
* Read body chunk by chunk and pass chunks to the `writeCallback`.
* @param writeCallback - &id:oatpp::data::stream::WriteCallback;.
*/
void transferBody(data::stream::WriteCallback* writeCallback) const;
/**
* Decode and transfer body to toStream.
* Use case example - stream huge body directly to file using relatively small buffer.
@ -139,6 +146,15 @@ public:
// Async
/**
* Transfer body in Asynchronous manner. <br>
* Read body chunk by chunk and pass chunks to the `writeCallback`.
* @param writeCallback - `std::shared_ptr` to &id:oatpp::data::stream::AsyncWriteCallback;.
* @return - &id:oatpp::async::CoroutineStarter;.
*/
async::CoroutineStarter transferBodyAsync(const std::shared_ptr<data::stream::AsyncWriteCallback>& writeCallback) const;
/**
* Same as &l:Response::readBodyToDto (); but Async.
* @param toStream - `std::shared_ptr` to &id:oatpp::data::stream::OutputStream;.

View File

@ -119,7 +119,7 @@ oatpp::async::CoroutineStarter SimpleBodyDecoder::doChunkedDecodingAsync(const s
const v_int32 MAX_LINE_SIZE = 8;
private:
std::shared_ptr<oatpp::data::stream::InputStream> m_fromStream;
const std::shared_ptr<oatpp::data::stream::AsyncWriteCallback>& m_writeCallback;
std::shared_ptr<oatpp::data::stream::AsyncWriteCallback> m_writeCallback;
std::shared_ptr<oatpp::data::buffer::IOBuffer> m_buffer = oatpp::data::buffer::IOBuffer::createShared();
v_int32 m_currLineLength;
v_char8 m_lineChar;

View File

@ -141,7 +141,7 @@ oatpp::async::Action MultipartBody::AsyncMultipartReadCallback::readAsyncInline(
Action act() override {
if(m_this->m_state == STATE_FINISHED) {
if(m_inlineData->bytesLeft == 0 || m_this->m_state == STATE_FINISHED) {
return finish();
}
@ -296,10 +296,10 @@ data::v_io_size MultipartBody::readHeaders(const std::shared_ptr<Multipart>& mul
return res;
}
MultipartBody::MultipartBody(const std::shared_ptr<Multipart>& multipart)
MultipartBody::MultipartBody(const std::shared_ptr<Multipart>& multipart, data::v_io_size chunkBufferSize)
: ChunkedBody(std::make_shared<MultipartReadCallback>(multipart),
std::make_shared<AsyncMultipartReadCallback>(multipart),
4096)
chunkBufferSize)
, m_multipart(multipart)
{}

View File

@ -122,8 +122,13 @@ public:
* Constructor.
* @param multipart - multipart object.
*/
MultipartBody(const std::shared_ptr<Multipart>& multipart);
/**
* Constructor.
* @param multipart - multipart object.
* @param chunkBufferSize - buffer used for chunks in the `Transfer-Encoding: chunked` body.
*/
MultipartBody(const std::shared_ptr<Multipart>& multipart, data::v_io_size chunkBufferSize = 4096);
/**
* Declare `Transfer-Encoding: chunked`, `Content-Type: multipart/<type>` header.

View File

@ -50,6 +50,9 @@ namespace oatpp { namespace test { namespace web {
namespace {
typedef oatpp::web::mime::multipart::Multipart Multipart;
typedef oatpp::web::protocol::http::outgoing::MultipartBody MultipartBody;
class TestComponent {
private:
v_int32 m_port;
@ -113,6 +116,24 @@ public:
};
std::shared_ptr<Multipart> createMultipart(const std::unordered_map<oatpp::String, oatpp::String>& map) {
auto multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>("0--qwerty1234--0");
for(auto& pair : map) {
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; name=\"" + pair.first + "\"");
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(pair.second));
}
return multipart;
}
}
void FullAsyncTest::onRun() {
@ -203,6 +224,34 @@ void FullAsyncTest::onRun() {
OATPP_ASSERT(returnedData == data);
}
{ // Multipart body
std::unordered_map<oatpp::String, oatpp::String> map;
map["value1"] = "Hello";
map["value2"] = "World";
auto multipart = createMultipart(map);
auto body = std::make_shared<MultipartBody>(multipart, i + 1);
auto response = client->multipartTest(i + 1, body);
OATPP_ASSERT(response->getStatusCode() == 200);
multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>(response->getHeaders());
oatpp::web::mime::multipart::InMemoryReader multipartReader(multipart.get());
response->transferBody(&multipartReader);
OATPP_ASSERT(multipart->getAllParts().size() == 2);
auto part1 = multipart->getNamedPart("value1");
auto part2 = multipart->getNamedPart("value2");
OATPP_ASSERT(part1);
OATPP_ASSERT(part2);
OATPP_ASSERT(part1->getInMemoryData() == "Hello");
OATPP_ASSERT(part2->getInMemoryData() == "World");
}
if((i + 1) % iterationsStep == 0) {
auto ticks = oatpp::base::Environment::getMicroTickCount() - lastTick;
lastTick = oatpp::base::Environment::getMicroTickCount();

View File

@ -50,6 +50,9 @@ namespace oatpp { namespace test { namespace web {
namespace {
typedef oatpp::web::mime::multipart::Multipart Multipart;
typedef oatpp::web::protocol::http::outgoing::MultipartBody MultipartBody;
class TestComponent {
private:
v_int32 m_port;
@ -108,6 +111,24 @@ public:
};
std::shared_ptr<Multipart> createMultipart(const std::unordered_map<oatpp::String, oatpp::String>& map) {
auto multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>("0--qwerty1234--0");
for(auto& pair : map) {
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; name=\"" + pair.first + "\"");
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(pair.second));
}
return multipart;
}
}
void FullTest::onRun() {
@ -219,6 +240,34 @@ void FullTest::onRun() {
OATPP_ASSERT(returnedData == data);
}
{ // Multipart body
std::unordered_map<oatpp::String, oatpp::String> map;
map["value1"] = "Hello";
map["value2"] = "World";
auto multipart = createMultipart(map);
auto body = std::make_shared<MultipartBody>(multipart, i + 1);
auto response = client->multipartTest(i + 1, body);
OATPP_ASSERT(response->getStatusCode() == 200);
multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>(response->getHeaders());
oatpp::web::mime::multipart::InMemoryReader multipartReader(multipart.get());
response->transferBody(&multipartReader);
OATPP_ASSERT(multipart->getAllParts().size() == 2);
auto part1 = multipart->getNamedPart("value1");
auto part2 = multipart->getNamedPart("value2");
OATPP_ASSERT(part1);
OATPP_ASSERT(part2);
OATPP_ASSERT(part1->getInMemoryData() == "Hello");
OATPP_ASSERT(part2->getInMemoryData() == "World");
}
if((i + 1) % iterationsStep == 0) {
auto ticks = oatpp::base::Environment::getMicroTickCount() - lastTick;
lastTick = oatpp::base::Environment::getMicroTickCount();

View File

@ -25,12 +25,17 @@
#ifndef oatpp_test_web_app_Client_hpp
#define oatpp_test_web_app_Client_hpp
#include "oatpp/web/protocol/http/outgoing/MultipartBody.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 {
public:
typedef oatpp::web::protocol::http::outgoing::MultipartBody MultipartBody;
public:
#include OATPP_CODEGEN_BEGIN(ApiClient)
API_CLIENT_INIT(Client)
@ -42,11 +47,9 @@ class Client : public oatpp::web::client::ApiClient {
API_CALL("GET", "headers", getWithHeaders, HEADER(String, param, "X-TEST-HEADER"))
API_CALL("POST", "body", postBody, BODY_STRING(String, body))
API_CALL("POST", "echo", echoBody, BODY_STRING(String, body))
API_CALL("GET", "header-value-set", headerValueSet, HEADER(String, valueSet, "X-VALUE-SET"))
API_CALL("GET", "chunked/{text-value}/{num-iterations}", getChunked, PATH(String, text, "text-value"), PATH(Int32, numIterations, "num-iterations"))
API_CALL("POST", "test/multipart/{chunk-size}", multipartTest, PATH(Int32, chunkSize, "chunk-size"), BODY(std::shared_ptr<MultipartBody>, body))
API_CALL_ASYNC("GET", "/", getRootAsync)
API_CALL_ASYNC("GET", "/", getRootAsyncWithCKA, HEADER(String, connection, "Connection"))

View File

@ -156,56 +156,25 @@ public:
ENDPOINT("GET", "chunked/{text-value}/{num-iterations}", chunked,
PATH(String, text, "text-value"),
PATH(Int32, numIterations, "num-iterations"),
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
REQUEST(std::shared_ptr<IncomingRequest>, request))
{
auto body = std::make_shared<oatpp::web::protocol::http::outgoing::ChunkedBody>
(std::make_shared<ReadCallback>(text, numIterations->getValue()), nullptr, 1024);
return OutgoingResponse::createShared(Status::CODE_200, body);
}
ENDPOINT("POST", "test/multipart", multipartTest, REQUEST(std::shared_ptr<IncomingRequest>, request)) {
ENDPOINT("POST", "test/multipart/{chunk-size}", multipartTest,
PATH(Int32, chunkSize, "chunk-size"),
REQUEST(std::shared_ptr<IncomingRequest>, request))
{
/*
oatpp::web::mime::multipart::Multipart multipart(request->getHeaders());
oatpp::web::mime::multipart::InMemoryReader multipartReader(&multipart);
auto multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>(request->getHeaders());
oatpp::web::mime::multipart::InMemoryReader multipartReader(multipart.get());
request->transferBody(&multipartReader);
for(auto& part : multipart.getAllParts()) {
OATPP_LOGD("multipart", "name='%s', value='%s'", part->getName()->getData(), part->getInMemoryData()->getData());
}
*/
auto responseBody = std::make_shared<oatpp::web::protocol::http::outgoing::MultipartBody>(multipart, chunkSize->getValue());
oatpp::data::stream::ChunkedBuffer stream;
request->transferBodyToStream(&stream);
return createResponse(Status::CODE_200, stream.toString());
}
ENDPOINT("GET", "test/multipart", multipartGetTest) {
auto multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>("0--qwerty1234--0");
{
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; name=\"part1\"");
oatpp::String data = "Hello";
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(data));
}
{
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; filename=\"file2.txt\"");
oatpp::String data = "World";
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(data));
}
auto body = std::make_shared<oatpp::web::protocol::http::outgoing::MultipartBody>(multipart);
return OutgoingResponse::createShared(Status::CODE_200, body);
return OutgoingResponse::createShared(Status::CODE_200, responseBody);
}

View File

@ -27,6 +27,8 @@
#include "./DTOs.hpp"
#include "oatpp/web/mime/multipart/InMemoryReader.hpp"
#include "oatpp/web/protocol/http/outgoing/MultipartBody.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp"
@ -169,35 +171,28 @@ public:
};
ENDPOINT_ASYNC("GET", "test/multipart", MultipartGetTest) {
ENDPOINT_ASYNC("POST", "test/multipart/{chunk-size}", MultipartTest) {
ENDPOINT_ASYNC_INIT(MultipartGetTest)
ENDPOINT_ASYNC_INIT(MultipartTest)
v_int32 m_chunkSize;
std::shared_ptr<oatpp::web::mime::multipart::Multipart> m_multipart;
Action act() override {
auto multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>("0--qwerty1234--0");
m_chunkSize = oatpp::utils::conversion::strToInt32(request->getPathVariable("chunk-size")->c_str());
{
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; name=\"part1\"");
// oatpp::String data = "";
// part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(data));
}
m_multipart = std::make_shared<oatpp::web::mime::multipart::Multipart>(request->getHeaders());
auto multipartReader = std::make_shared<oatpp::web::mime::multipart::AsyncInMemoryReader>(m_multipart);
{
oatpp::web::mime::multipart::Headers partHeaders;
auto part = std::make_shared<oatpp::web::mime::multipart::Part>(partHeaders);
multipart->addPart(part);
part->putHeader("Content-Disposition", "form-data; filename=\"file2.txt\"");
oatpp::String data = "World";
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(data));
}
return request->transferBodyAsync(multipartReader).next(yieldTo(&MultipartTest::respond));
auto body = std::make_shared<oatpp::web::protocol::http::outgoing::MultipartBody>(multipart);
}
return _return(OutgoingResponse::createShared(Status::CODE_200, body));
Action respond() {
auto responseBody = std::make_shared<oatpp::web::protocol::http::outgoing::MultipartBody>(m_multipart, m_chunkSize);
return _return(OutgoingResponse::createShared(Status::CODE_200, responseBody));
}