2014-04-01 12:25:16 +00:00
|
|
|
#pragma once
|
|
|
|
#include <string>
|
|
|
|
#include <unordered_map>
|
2020-10-10 21:29:40 +00:00
|
|
|
#include <ios>
|
|
|
|
#include <fstream>
|
2020-10-21 01:02:09 +00:00
|
|
|
#include <sstream>
|
2016-09-21 14:11:06 +00:00
|
|
|
|
|
|
|
#include "crow/json.h"
|
|
|
|
#include "crow/http_request.h"
|
|
|
|
#include "crow/ci_map.h"
|
2014-04-01 12:25:16 +00:00
|
|
|
|
2020-10-04 12:05:26 +00:00
|
|
|
#include "crow/socket_adaptors.h"
|
|
|
|
#include "crow/logging.h"
|
2020-10-04 16:11:18 +00:00
|
|
|
#include "crow/mime_types.h"
|
2020-10-04 12:05:26 +00:00
|
|
|
#include <sys/stat.h>
|
2020-11-13 05:42:02 +00:00
|
|
|
|
2020-10-04 12:05:26 +00:00
|
|
|
|
2014-04-26 17:19:59 +00:00
|
|
|
namespace crow
|
2014-04-01 12:25:16 +00:00
|
|
|
{
|
2015-09-20 13:06:00 +00:00
|
|
|
template <typename Adaptor, typename Handler, typename ... Middlewares>
|
2014-08-05 18:54:38 +00:00
|
|
|
class Connection;
|
2020-11-14 02:28:07 +00:00
|
|
|
|
|
|
|
/// HTTP response
|
2014-04-01 12:25:16 +00:00
|
|
|
struct response
|
|
|
|
{
|
2015-09-20 13:06:00 +00:00
|
|
|
template <typename Adaptor, typename Handler, typename ... Middlewares>
|
2014-08-05 18:54:38 +00:00
|
|
|
friend class crow::Connection;
|
|
|
|
|
2020-11-18 22:13:57 +00:00
|
|
|
int code{200}; ///< The Status code for the response.
|
|
|
|
std::string body; ///< The actual payload containing the response data.
|
|
|
|
json::wvalue json_value; ///< if the response body is JSON, this would be it.
|
|
|
|
ci_map headers; ///< HTTP headers.
|
2021-01-02 19:12:04 +00:00
|
|
|
bool compressed = true; ///< If compression is enabled and this is false, the individual response will not be compressed.
|
2014-09-10 21:32:41 +00:00
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Set the value of an existing header in the response.
|
2014-09-10 21:32:41 +00:00
|
|
|
void set_header(std::string key, std::string value)
|
|
|
|
{
|
|
|
|
headers.erase(key);
|
|
|
|
headers.emplace(std::move(key), std::move(value));
|
|
|
|
}
|
2020-11-14 02:28:07 +00:00
|
|
|
|
|
|
|
/// Add a new header to the response.
|
2014-09-10 21:32:41 +00:00
|
|
|
void add_header(std::string key, std::string value)
|
|
|
|
{
|
|
|
|
headers.emplace(std::move(key), std::move(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& get_header_value(const std::string& key)
|
|
|
|
{
|
|
|
|
return crow::get_header_value(headers, key);
|
|
|
|
}
|
|
|
|
|
2014-08-05 13:38:51 +00:00
|
|
|
|
2014-04-01 12:25:16 +00:00
|
|
|
response() {}
|
2014-04-14 20:11:37 +00:00
|
|
|
explicit response(int code) : code(code) {}
|
2014-04-09 23:17:08 +00:00
|
|
|
response(std::string body) : body(std::move(body)) {}
|
2015-12-24 11:42:21 +00:00
|
|
|
response(json::wvalue&& json_value) : json_value(std::move(json_value))
|
2015-04-13 04:23:45 +00:00
|
|
|
{
|
2015-12-24 11:42:21 +00:00
|
|
|
json_mode();
|
2015-04-13 04:23:45 +00:00
|
|
|
}
|
2015-12-24 11:42:21 +00:00
|
|
|
response(int code, std::string body) : code(code), body(std::move(body)) {}
|
|
|
|
response(const json::wvalue& json_value) : body(json::dump(json_value))
|
2014-11-04 17:12:52 +00:00
|
|
|
{
|
2015-04-13 04:23:45 +00:00
|
|
|
json_mode();
|
2014-11-04 17:12:52 +00:00
|
|
|
}
|
2015-09-27 03:33:09 +00:00
|
|
|
response(int code, const json::wvalue& json_value) : code(code), body(json::dump(json_value))
|
|
|
|
{
|
|
|
|
json_mode();
|
|
|
|
}
|
2014-08-05 13:38:51 +00:00
|
|
|
|
2014-04-17 14:06:41 +00:00
|
|
|
response(response&& r)
|
|
|
|
{
|
|
|
|
*this = std::move(r);
|
|
|
|
}
|
2014-08-05 13:38:51 +00:00
|
|
|
|
2014-08-05 18:54:38 +00:00
|
|
|
response& operator = (const response& r) = delete;
|
|
|
|
|
2014-09-06 19:30:53 +00:00
|
|
|
response& operator = (response&& r) noexcept
|
2014-04-17 14:06:41 +00:00
|
|
|
{
|
|
|
|
body = std::move(r.body);
|
|
|
|
json_value = std::move(r.json_value);
|
|
|
|
code = r.code;
|
|
|
|
headers = std::move(r.headers);
|
2015-02-20 03:00:15 +00:00
|
|
|
completed_ = r.completed_;
|
2014-04-17 14:06:41 +00:00
|
|
|
return *this;
|
|
|
|
}
|
2014-08-05 13:38:51 +00:00
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Check if the response has completed (whether response.end() has been called)
|
2014-09-06 19:30:53 +00:00
|
|
|
bool is_completed() const noexcept
|
|
|
|
{
|
|
|
|
return completed_;
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:54:38 +00:00
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
body.clear();
|
|
|
|
json_value.clear();
|
|
|
|
code = 200;
|
|
|
|
headers.clear();
|
|
|
|
completed_ = false;
|
|
|
|
}
|
|
|
|
|
2016-10-03 13:32:16 +00:00
|
|
|
void redirect(const std::string& location)
|
|
|
|
{
|
|
|
|
code = 301;
|
|
|
|
set_header("Location", location);
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:54:38 +00:00
|
|
|
void write(const std::string& body_part)
|
2014-08-05 13:38:51 +00:00
|
|
|
{
|
|
|
|
body += body_part;
|
|
|
|
}
|
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Set the response completion flag and call the handler (to send the response).
|
2014-08-05 13:38:51 +00:00
|
|
|
void end()
|
|
|
|
{
|
2014-08-05 18:54:38 +00:00
|
|
|
if (!completed_)
|
|
|
|
{
|
2014-09-13 18:15:37 +00:00
|
|
|
completed_ = true;
|
2015-12-24 11:42:21 +00:00
|
|
|
|
2014-08-05 18:54:38 +00:00
|
|
|
if (complete_request_handler_)
|
|
|
|
{
|
|
|
|
complete_request_handler_();
|
|
|
|
}
|
|
|
|
}
|
2014-08-05 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Same as end() except it adds a body part right before ending.
|
2014-08-05 13:38:51 +00:00
|
|
|
void end(const std::string& body_part)
|
|
|
|
{
|
|
|
|
body += body_part;
|
|
|
|
end();
|
|
|
|
}
|
2014-08-05 18:54:38 +00:00
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Check if the connection is still alive (usually by checking the socket status).
|
2014-08-06 16:18:33 +00:00
|
|
|
bool is_alive()
|
|
|
|
{
|
|
|
|
return is_alive_helper_ && is_alive_helper_();
|
|
|
|
}
|
2020-11-12 13:27:26 +00:00
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Check whether the response has a static file defined.
|
|
|
|
bool is_static_type()
|
|
|
|
{
|
|
|
|
return file_info.path.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This constains metadata (coming from the `stat` command) related to any static files associated with this response.
|
|
|
|
|
|
|
|
/// Either a static file or a string body can be returned as 1 response.
|
|
|
|
///
|
2020-10-04 12:05:26 +00:00
|
|
|
struct static_file_info{
|
|
|
|
std::string path = "";
|
|
|
|
struct stat statbuf;
|
|
|
|
int statResult;
|
|
|
|
};
|
|
|
|
|
2020-10-21 01:02:09 +00:00
|
|
|
///Return a static file as the response body
|
2020-10-04 12:05:26 +00:00
|
|
|
void set_static_file_info(std::string path){
|
|
|
|
file_info.path = path;
|
|
|
|
file_info.statResult = stat(file_info.path.c_str(), &file_info.statbuf);
|
|
|
|
if (file_info.statResult == 0)
|
|
|
|
{
|
2020-10-04 16:11:18 +00:00
|
|
|
std::size_t last_dot = path.find_last_of(".");
|
|
|
|
std::string extension = path.substr(last_dot+1);
|
|
|
|
std::string mimeType = "";
|
2020-10-04 12:05:26 +00:00
|
|
|
code = 200;
|
|
|
|
this->add_header("Content-length", std::to_string(file_info.statbuf.st_size));
|
2020-10-21 01:02:09 +00:00
|
|
|
|
2020-10-10 21:54:20 +00:00
|
|
|
if (extension != ""){
|
2020-10-04 16:11:18 +00:00
|
|
|
mimeType = mime_types[extension];
|
|
|
|
if (mimeType != "")
|
|
|
|
this-> add_header("Content-Type", mimeType);
|
2020-10-10 21:54:20 +00:00
|
|
|
else
|
|
|
|
this-> add_header("content-Type", "text/plain");
|
|
|
|
}
|
2020-10-04 12:05:26 +00:00
|
|
|
}
|
2020-10-11 16:00:24 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
code = 404;
|
|
|
|
this->end();
|
|
|
|
}
|
2020-10-04 12:05:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Stream a static file.
|
2020-10-09 13:01:24 +00:00
|
|
|
template<typename Adaptor>
|
2020-10-21 01:02:09 +00:00
|
|
|
void do_stream_file(Adaptor& adaptor)
|
|
|
|
{
|
2020-10-04 12:05:26 +00:00
|
|
|
if (file_info.statResult == 0)
|
|
|
|
{
|
2020-10-10 21:29:40 +00:00
|
|
|
std::ifstream is(file_info.path.c_str(), std::ios::in | std::ios::binary);
|
2020-10-21 01:02:09 +00:00
|
|
|
write_streamed(is, adaptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// Stream the response body (send the body in chunks).
|
2020-10-21 01:02:09 +00:00
|
|
|
template<typename Adaptor>
|
|
|
|
void do_stream_body(Adaptor& adaptor)
|
|
|
|
{
|
|
|
|
if (body.length() > 0)
|
|
|
|
{
|
2020-10-26 23:36:02 +00:00
|
|
|
write_streamed_string(body, adaptor);
|
2020-10-21 01:02:09 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-12 13:27:26 +00:00
|
|
|
|
2020-10-21 01:02:09 +00:00
|
|
|
private:
|
|
|
|
bool completed_{};
|
|
|
|
std::function<void()> complete_request_handler_;
|
|
|
|
std::function<bool()> is_alive_helper_;
|
2020-11-14 02:28:07 +00:00
|
|
|
static_file_info file_info;
|
2020-10-21 01:02:09 +00:00
|
|
|
|
2020-11-14 02:28:07 +00:00
|
|
|
/// In case of a JSON object, set the Content-Type header.
|
2020-10-21 01:02:09 +00:00
|
|
|
void json_mode()
|
|
|
|
{
|
|
|
|
set_header("Content-Type", "application/json");
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Stream, typename Adaptor>
|
|
|
|
void write_streamed(Stream& is, Adaptor& adaptor)
|
|
|
|
{
|
2020-10-11 16:00:24 +00:00
|
|
|
char buf[16384];
|
2020-10-10 21:29:40 +00:00
|
|
|
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
|
|
|
{
|
|
|
|
std::vector<asio::const_buffer> buffers;
|
|
|
|
buffers.push_back(boost::asio::buffer(buf));
|
2020-10-26 23:36:02 +00:00
|
|
|
write_buffer_list(buffers, adaptor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
|
|
|
|
template<typename Adaptor>
|
|
|
|
void write_streamed_string(std::string& is, Adaptor& adaptor)
|
|
|
|
{
|
|
|
|
std::string buf;
|
2020-10-27 13:21:52 +00:00
|
|
|
std::vector<asio::const_buffer> buffers;
|
2020-10-26 23:36:02 +00:00
|
|
|
|
2020-10-27 13:21:52 +00:00
|
|
|
while (is.length() > 16384)
|
2020-10-26 23:36:02 +00:00
|
|
|
{
|
2020-10-31 00:09:32 +00:00
|
|
|
//buf.reserve(16385);
|
2020-10-27 13:21:52 +00:00
|
|
|
buf = is.substr(0, 16384);
|
|
|
|
is = is.substr(16384);
|
|
|
|
push_and_write(buffers, buf, adaptor);
|
2020-10-10 21:29:40 +00:00
|
|
|
}
|
2020-10-31 00:09:32 +00:00
|
|
|
//Collect whatever is left (less than 16KB) and send it down the socket
|
|
|
|
//buf.reserve(is.length());
|
2020-10-27 13:21:52 +00:00
|
|
|
buf = is;
|
|
|
|
is.clear();
|
|
|
|
push_and_write(buffers, buf, adaptor);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Adaptor>
|
|
|
|
inline void push_and_write(std::vector<asio::const_buffer>& buffers, std::string& buf, Adaptor& adaptor)
|
|
|
|
{
|
|
|
|
buffers.clear();
|
|
|
|
buffers.push_back(boost::asio::buffer(buf));
|
|
|
|
write_buffer_list(buffers, adaptor);
|
2020-10-04 12:05:26 +00:00
|
|
|
}
|
2015-12-24 11:42:21 +00:00
|
|
|
|
2020-10-26 23:36:02 +00:00
|
|
|
template<typename Adaptor>
|
|
|
|
inline void write_buffer_list(std::vector<asio::const_buffer>& buffers, Adaptor& adaptor)
|
|
|
|
{
|
|
|
|
boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t)
|
|
|
|
{
|
|
|
|
if (!ec)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CROW_LOG_ERROR << ec << " - happened while sending buffers";
|
|
|
|
this->end();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-04-01 12:25:16 +00:00
|
|
|
};
|
|
|
|
}
|