2020-10-17 22:34:58 +00:00
|
|
|
#pragma once
|
2021-01-05 14:49:10 +00:00
|
|
|
|
2020-10-17 22:34:58 +00:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <sstream>
|
2021-01-05 14:49:10 +00:00
|
|
|
|
2020-10-17 22:34:58 +00:00
|
|
|
#include "crow/http_request.h"
|
2021-01-05 14:49:10 +00:00
|
|
|
#include "crow/returnable.h"
|
2020-10-17 22:34:58 +00:00
|
|
|
|
|
|
|
namespace crow
|
|
|
|
{
|
2021-11-25 11:45:38 +00:00
|
|
|
/// Encapsulates anything related to processing and organizing `multipart/xyz` messages
|
2020-10-17 22:34:58 +00:00
|
|
|
namespace multipart
|
|
|
|
{
|
|
|
|
const std::string dd = "--";
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// The first part in a section, contains metadata about the part
|
2020-10-17 22:34:58 +00:00
|
|
|
struct header
|
|
|
|
{
|
2021-11-25 11:45:38 +00:00
|
|
|
std::pair<std::string, std::string> value; ///< The first part of the header, usually `Content-Type` or `Content-Disposition`
|
|
|
|
std::unordered_map<std::string, std::string> params; ///< The parameters of the header, come after the `value`
|
2020-10-17 22:34:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
///One part of the multipart message
|
2021-12-03 03:39:23 +00:00
|
|
|
|
2020-10-17 22:34:58 +00:00
|
|
|
///
|
2021-11-25 11:45:38 +00:00
|
|
|
/// It is usually separated from other sections by a `boundary`
|
2020-10-17 22:34:58 +00:00
|
|
|
struct part
|
|
|
|
{
|
2022-02-05 15:15:19 +00:00
|
|
|
//TODO(EDev): restructure this to an `unordered_map<string, header>` with string being `header::value.first`
|
2020-10-17 22:34:58 +00:00
|
|
|
std::vector<header> headers; ///< (optional) The first part before the data, Contains information regarding the type of data and encoding
|
2021-11-25 11:45:38 +00:00
|
|
|
std::string body; ///< The actual data in the part
|
2020-10-17 22:34:58 +00:00
|
|
|
};
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// The parsed multipart request/response
|
2020-12-18 09:28:37 +00:00
|
|
|
struct message : public returnable
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
2022-02-05 15:15:19 +00:00
|
|
|
ci_map headers; ///< The request/response headers
|
2021-11-25 11:45:38 +00:00
|
|
|
std::string boundary; ///< The text boundary that separates different `parts`
|
2020-10-17 22:34:58 +00:00
|
|
|
std::vector<part> parts; ///< The individual parts of the message
|
|
|
|
|
|
|
|
const std::string& get_header_value(const std::string& key) const
|
|
|
|
{
|
|
|
|
return crow::get_header_value(headers, key);
|
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// Represent all parts as a string (**does not include message headers**)
|
2021-05-18 16:10:39 +00:00
|
|
|
std::string dump() const override
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
std::stringstream str;
|
2020-10-18 00:04:18 +00:00
|
|
|
std::string delimiter = dd + boundary;
|
2020-10-17 22:34:58 +00:00
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
for (unsigned i = 0; i < parts.size(); i++)
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
2020-10-17 23:36:39 +00:00
|
|
|
str << delimiter << crlf;
|
2020-10-17 22:34:58 +00:00
|
|
|
str << dump(i);
|
|
|
|
}
|
2020-10-18 00:04:18 +00:00
|
|
|
str << delimiter << dd << crlf;
|
2020-10-17 22:34:58 +00:00
|
|
|
return str.str();
|
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// Represent an individual part as a string
|
2021-05-18 16:10:39 +00:00
|
|
|
std::string dump(int part_) const
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
std::stringstream str;
|
|
|
|
part item = parts[part_];
|
2021-11-25 11:45:38 +00:00
|
|
|
for (header item_h : item.headers)
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
str << item_h.value.first << ": " << item_h.value.second;
|
2021-11-25 11:45:38 +00:00
|
|
|
for (auto& it : item_h.params)
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
str << "; " << it.first << '=' << pad(it.second);
|
|
|
|
}
|
2020-10-17 23:36:39 +00:00
|
|
|
str << crlf;
|
2020-10-17 22:34:58 +00:00
|
|
|
}
|
2020-10-17 23:36:39 +00:00
|
|
|
str << crlf;
|
|
|
|
str << item.body << crlf;
|
2020-10-17 22:34:58 +00:00
|
|
|
return str.str();
|
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// Default constructor using default values
|
|
|
|
message(const ci_map& headers, const std::string& boundary, const std::vector<part>& sections):
|
|
|
|
returnable("multipart/form-data"), headers(headers), boundary(boundary), parts(sections) {}
|
2020-10-17 22:34:58 +00:00
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
/// Create a multipart message from a request data
|
|
|
|
message(const request& req):
|
|
|
|
returnable("multipart/form-data"),
|
|
|
|
headers(req.headers),
|
|
|
|
boundary(get_boundary(get_header_value("Content-Type"))),
|
|
|
|
parts(parse_body(req.body))
|
2021-11-27 12:28:50 +00:00
|
|
|
{}
|
2020-10-17 22:34:58 +00:00
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
private:
|
2021-05-18 16:10:39 +00:00
|
|
|
std::string get_boundary(const std::string& header) const
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
2022-02-10 21:56:30 +00:00
|
|
|
constexpr char boundary_text[] = "boundary=";
|
|
|
|
size_t found = header.find(boundary_text);
|
2020-10-17 22:34:58 +00:00
|
|
|
if (found)
|
2022-02-05 15:15:19 +00:00
|
|
|
{
|
2022-02-10 21:56:30 +00:00
|
|
|
std::string to_return(header.substr(found + strlen(boundary_text)));
|
2022-02-05 15:15:19 +00:00
|
|
|
if (to_return[0] == '\"')
|
|
|
|
{
|
2022-02-06 19:29:46 +00:00
|
|
|
to_return = to_return.substr(1, to_return.length() - 2);
|
2022-02-05 15:15:19 +00:00
|
|
|
}
|
|
|
|
return to_return;
|
|
|
|
}
|
2020-10-18 00:04:18 +00:00
|
|
|
return std::string();
|
2020-10-17 22:34:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<part> parse_body(std::string body)
|
|
|
|
{
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
std::vector<part> sections;
|
|
|
|
|
|
|
|
std::string delimiter = dd + boundary;
|
|
|
|
|
|
|
|
while (body != (crlf))
|
|
|
|
{
|
|
|
|
size_t found = body.find(delimiter);
|
|
|
|
std::string section = body.substr(0, found);
|
|
|
|
|
|
|
|
// +2 is the CRLF.
|
|
|
|
// We don't check it and delete it so that the same delimiter can be used for The last delimiter (--delimiter--CRLF).
|
|
|
|
body.erase(0, found + delimiter.length() + 2);
|
|
|
|
if (!section.empty())
|
|
|
|
{
|
|
|
|
sections.emplace_back(parse_section(section));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sections;
|
2020-10-17 22:34:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
part parse_section(std::string& section)
|
|
|
|
{
|
|
|
|
struct part to_return;
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
size_t found = section.find(crlf + crlf);
|
|
|
|
std::string head_line = section.substr(0, found + 2);
|
2020-10-17 22:34:58 +00:00
|
|
|
section.erase(0, found + 4);
|
|
|
|
|
|
|
|
parse_section_head(head_line, to_return);
|
2021-11-25 11:45:38 +00:00
|
|
|
to_return.body = section.substr(0, section.length() - 2);
|
2020-10-17 22:34:58 +00:00
|
|
|
return to_return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void parse_section_head(std::string& lines, part& part)
|
|
|
|
{
|
2020-10-18 00:04:18 +00:00
|
|
|
while (!lines.empty())
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
header to_add;
|
|
|
|
|
|
|
|
size_t found = lines.find(crlf);
|
|
|
|
std::string line = lines.substr(0, found);
|
2021-11-25 11:45:38 +00:00
|
|
|
lines.erase(0, found + 2);
|
|
|
|
// Add the header if available
|
2020-10-18 00:04:18 +00:00
|
|
|
if (!line.empty())
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
size_t found = line.find("; ");
|
|
|
|
std::string header = line.substr(0, found);
|
|
|
|
if (found != std::string::npos)
|
2021-11-25 11:45:38 +00:00
|
|
|
line.erase(0, found + 2);
|
2020-10-17 22:34:58 +00:00
|
|
|
else
|
|
|
|
line = std::string();
|
|
|
|
|
|
|
|
size_t header_split = header.find(": ");
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
to_add.value = std::pair<std::string, std::string>(header.substr(0, header_split), header.substr(header_split + 2));
|
2020-10-17 22:34:58 +00:00
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
// Add the parameters
|
2020-10-18 00:04:18 +00:00
|
|
|
while (!line.empty())
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
|
|
|
size_t found = line.find("; ");
|
|
|
|
std::string param = line.substr(0, found);
|
|
|
|
if (found != std::string::npos)
|
2021-11-25 11:45:38 +00:00
|
|
|
line.erase(0, found + 2);
|
2020-10-17 22:34:58 +00:00
|
|
|
else
|
|
|
|
line = std::string();
|
|
|
|
|
|
|
|
size_t param_split = param.find('=');
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
std::string value = param.substr(param_split + 1);
|
2020-10-17 22:34:58 +00:00
|
|
|
|
|
|
|
to_add.params.emplace(param.substr(0, param_split), trim(value));
|
|
|
|
}
|
|
|
|
part.headers.emplace_back(to_add);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
inline std::string trim(std::string& string, const char& excess = '"') const
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
2021-11-25 11:45:38 +00:00
|
|
|
if (string.length() > 1 && string[0] == excess && string[string.length() - 1] == excess)
|
|
|
|
return string.substr(1, string.length() - 2);
|
2020-10-17 22:34:58 +00:00
|
|
|
return string;
|
|
|
|
}
|
|
|
|
|
2021-11-25 11:45:38 +00:00
|
|
|
inline std::string pad(std::string& string, const char& padding = '"') const
|
2020-10-17 22:34:58 +00:00
|
|
|
{
|
2021-11-25 11:45:38 +00:00
|
|
|
return (padding + string + padding);
|
2020-10-17 22:34:58 +00:00
|
|
|
}
|
|
|
|
};
|
2021-11-25 11:45:38 +00:00
|
|
|
} // namespace multipart
|
|
|
|
} // namespace crow
|