mirror of
https://github.com/oatpp/oatpp.git
synced 2025-03-13 18:06:47 +08:00
Tests. Simple And Async Multipart APIs.
This commit is contained in:
parent
fa95bf6589
commit
464fc4da13
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;.
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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"))
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user