118 lines
3.1 KiB
C++
118 lines
3.1 KiB
C++
#pragma once
|
|
#include "define.h"
|
|
|
|
namespace cinatra {
|
|
|
|
template <typename T>
|
|
class multipart_reader_t {
|
|
public:
|
|
multipart_reader_t(T *conn)
|
|
: conn_(conn),
|
|
head_buf_(conn_->head_buf_),
|
|
chunked_buf_(conn_->chunked_buf_) {}
|
|
|
|
async_simple::coro::Lazy<part_head_t> read_part_head() {
|
|
if (head_buf_.size() > 0) {
|
|
const char *data_ptr = asio::buffer_cast<const char *>(head_buf_.data());
|
|
chunked_buf_.sputn(data_ptr, head_buf_.size());
|
|
head_buf_.consume(head_buf_.size());
|
|
}
|
|
|
|
part_head_t result{};
|
|
std::error_code ec{};
|
|
size_t last_size = chunked_buf_.size();
|
|
size_t size;
|
|
|
|
auto get_part_name = [](std::string_view data, std::string_view name,
|
|
size_t start) {
|
|
start += name.length();
|
|
size_t end = data.find("\"", start);
|
|
return data.substr(start, end - start);
|
|
};
|
|
|
|
constexpr std::string_view name = "name=\"";
|
|
constexpr std::string_view filename = "filename=\"";
|
|
|
|
while (true) {
|
|
if (std::tie(ec, size) =
|
|
co_await conn_->async_read_until(chunked_buf_, CRCF);
|
|
ec) {
|
|
result.ec = ec;
|
|
conn_->close();
|
|
co_return result;
|
|
}
|
|
|
|
const char *data_ptr =
|
|
asio::buffer_cast<const char *>(chunked_buf_.data());
|
|
chunked_buf_.consume(size);
|
|
if (*data_ptr == '-') {
|
|
continue;
|
|
}
|
|
std::string_view data{data_ptr, size};
|
|
if (size == 2) { // got the head end: \r\n\r\n
|
|
break;
|
|
}
|
|
|
|
if (size_t pos = data.find("name"); pos != std::string_view::npos) {
|
|
result.name = get_part_name(data, name, pos);
|
|
|
|
if (size_t pos = data.find("filename"); pos != std::string_view::npos) {
|
|
result.filename = get_part_name(data, filename, pos);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
co_return result;
|
|
}
|
|
|
|
async_simple::coro::Lazy<chunked_result> read_part_body(
|
|
std::string_view boundary) {
|
|
chunked_result result{};
|
|
std::error_code ec{};
|
|
size_t size = 0;
|
|
|
|
if (std::tie(ec, size) =
|
|
co_await conn_->async_read_until(chunked_buf_, boundary);
|
|
ec) {
|
|
result.ec = ec;
|
|
conn_->close();
|
|
co_return result;
|
|
}
|
|
|
|
const char *data_ptr = asio::buffer_cast<const char *>(chunked_buf_.data());
|
|
chunked_buf_.consume(size);
|
|
result.data = std::string_view{
|
|
data_ptr, size - boundary.size() - 4}; //-- boundary \r\n
|
|
|
|
if (std::tie(ec, size) =
|
|
co_await conn_->async_read_until(chunked_buf_, CRCF);
|
|
ec) {
|
|
result = {};
|
|
result.ec = ec;
|
|
conn_->close();
|
|
co_return result;
|
|
}
|
|
|
|
data_ptr = asio::buffer_cast<const char *>(chunked_buf_.data());
|
|
std::string data{data_ptr, size};
|
|
if (size > 2) {
|
|
constexpr std::string_view complete_flag = "--\r\n";
|
|
if (data == complete_flag) {
|
|
result.eof = true;
|
|
}
|
|
}
|
|
|
|
chunked_buf_.consume(size);
|
|
co_return result;
|
|
}
|
|
|
|
private:
|
|
T *conn_;
|
|
asio::streambuf &head_buf_;
|
|
asio::streambuf &chunked_buf_;
|
|
};
|
|
|
|
template <typename T>
|
|
multipart_reader_t(T *con) -> multipart_reader_t<T>;
|
|
} // namespace cinatra
|