41 template<
typename Adaptor,
typename Handler,
typename... Middlewares>
44 template<
typename Adaptor,
typename Handler>
53#ifdef CROW_ENABLE_COMPRESSION
63 headers.emplace(std::move(key), std::move(value));
69 headers.emplace(std::move(key), std::move(value));
72 const std::string& get_header_value(
const std::string& key)
78 static bool validate_mime_type(
const std::string& candidate)
noexcept
82 std::array<std::string, 10> valid_parent_types = {
83 "application/",
"audio/",
"font/",
"example/",
84 "image/",
"message/",
"model/",
"multipart/",
86 for (
const std::string& parent : valid_parent_types)
91 if (candidate.size() <= parent.size())
98 if (strncmp(parent.c_str(), candidate.c_str(), parent.size()) == 0)
109 static std::string get_mime_type(
const std::string& contentType)
111 const auto mimeTypeIterator = mime_types.find(contentType);
112 if (mimeTypeIterator != mime_types.end())
114 return mimeTypeIterator->second;
116 else if (validate_mime_type(contentType))
122 CROW_LOG_WARNING <<
"Unable to interpret mime type for content type '" << contentType <<
"'. Defaulting to text/plain.";
130 explicit response(
int code_) :
code(code_) {}
131 response(std::string body_) :
body(std::move(body_)) {}
132 response(
int code_, std::string body_) :
code(code_),
body(std::move(body_)) {}
134 response(returnable&& value)
137 set_header(
"Content-Type", value.content_type);
139 response(returnable& value)
142 set_header(
"Content-Type", value.content_type);
144 response(
int code_, returnable& value):
148 set_header(
"Content-Type", value.content_type);
150 response(
int code_, returnable&& value):
153 set_header(
"Content-Type", std::move(value.content_type));
156 response(response&& r)
158 *
this = std::move(r);
161 response(std::string contentType, std::string body_):
162 body(std::move(body_))
164 set_header(
"Content-Type", get_mime_type(contentType));
167 response(
int code_, std::string contentType, std::string body_):
170 set_header(
"Content-Type", get_mime_type(contentType));
173 response& operator=(
const response& r) =
delete;
175 response& operator=(response&& r)
noexcept
177 body = std::move(r.body);
179 headers = std::move(r.headers);
180 completed_ = r.completed_;
181 file_info = std::move(r.file_info);
197 file_info = static_file_info{};
224 void moved(
const std::string& location)
240 void write(
const std::string& body_part)
257 if (complete_request_handler_)
259 complete_request_handler_();
267 void end(
const std::string& body_part)
276 return is_alive_helper_ && is_alive_helper_();
282 return file_info.path.size();
291 std::string path =
"";
299 utility::sanitize_filename(path);
307 file_info.path = path;
308 file_info.statResult = stat(file_info.path.c_str(), &file_info.statbuf);
309#ifdef CROW_ENABLE_COMPRESSION
312 if (file_info.statResult == 0 && S_ISREG(file_info.statbuf.st_mode))
315 this->
add_header(
"Content-Length", std::to_string(file_info.statbuf.st_size));
317 if (content_type.empty())
319 std::size_t last_dot = path.find_last_of(
'.');
320 std::string extension = path.substr(last_dot + 1);
322 if (!extension.empty())
324 this->
add_header(
"Content-Type", get_mime_type(extension));
329 this->
add_header(
"Content-Type", content_type);
335 file_info.path.clear();
340 void write_header_into_buffer(std::vector<asio::const_buffer>& buffers, std::string& content_length_buffer,
bool add_keep_alive,
const std::string& server_name)
344 static std::unordered_map<int, std::string> statusCodes = {
345 {status::CONTINUE,
"HTTP/1.1 100 Continue\r\n"},
346 {status::SWITCHING_PROTOCOLS,
"HTTP/1.1 101 Switching Protocols\r\n"},
348 {status::OK,
"HTTP/1.1 200 OK\r\n"},
349 {status::CREATED,
"HTTP/1.1 201 Created\r\n"},
350 {status::ACCEPTED,
"HTTP/1.1 202 Accepted\r\n"},
351 {status::NON_AUTHORITATIVE_INFORMATION,
"HTTP/1.1 203 Non-Authoritative Information\r\n"},
352 {status::NO_CONTENT,
"HTTP/1.1 204 No Content\r\n"},
353 {status::RESET_CONTENT,
"HTTP/1.1 205 Reset Content\r\n"},
354 {status::PARTIAL_CONTENT,
"HTTP/1.1 206 Partial Content\r\n"},
356 {status::MULTIPLE_CHOICES,
"HTTP/1.1 300 Multiple Choices\r\n"},
357 {status::MOVED_PERMANENTLY,
"HTTP/1.1 301 Moved Permanently\r\n"},
358 {status::FOUND,
"HTTP/1.1 302 Found\r\n"},
359 {status::SEE_OTHER,
"HTTP/1.1 303 See Other\r\n"},
360 {status::NOT_MODIFIED,
"HTTP/1.1 304 Not Modified\r\n"},
361 {status::TEMPORARY_REDIRECT,
"HTTP/1.1 307 Temporary Redirect\r\n"},
362 {status::PERMANENT_REDIRECT,
"HTTP/1.1 308 Permanent Redirect\r\n"},
364 {status::BAD_REQUEST,
"HTTP/1.1 400 Bad Request\r\n"},
365 {status::UNAUTHORIZED,
"HTTP/1.1 401 Unauthorized\r\n"},
366 {status::FORBIDDEN,
"HTTP/1.1 403 Forbidden\r\n"},
367 {status::NOT_FOUND,
"HTTP/1.1 404 Not Found\r\n"},
368 {status::METHOD_NOT_ALLOWED,
"HTTP/1.1 405 Method Not Allowed\r\n"},
369 {status::NOT_ACCEPTABLE,
"HTTP/1.1 406 Not Acceptable\r\n"},
370 {status::PROXY_AUTHENTICATION_REQUIRED,
"HTTP/1.1 407 Proxy Authentication Required\r\n"},
371 {status::CONFLICT,
"HTTP/1.1 409 Conflict\r\n"},
372 {status::GONE,
"HTTP/1.1 410 Gone\r\n"},
373 {status::PAYLOAD_TOO_LARGE,
"HTTP/1.1 413 Payload Too Large\r\n"},
374 {status::UNSUPPORTED_MEDIA_TYPE,
"HTTP/1.1 415 Unsupported Media Type\r\n"},
375 {status::RANGE_NOT_SATISFIABLE,
"HTTP/1.1 416 Range Not Satisfiable\r\n"},
376 {status::EXPECTATION_FAILED,
"HTTP/1.1 417 Expectation Failed\r\n"},
377 {status::PRECONDITION_REQUIRED,
"HTTP/1.1 428 Precondition Required\r\n"},
378 {status::TOO_MANY_REQUESTS,
"HTTP/1.1 429 Too Many Requests\r\n"},
379 {status::UNAVAILABLE_FOR_LEGAL_REASONS,
"HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
381 {status::INTERNAL_SERVER_ERROR,
"HTTP/1.1 500 Internal Server Error\r\n"},
382 {status::NOT_IMPLEMENTED,
"HTTP/1.1 501 Not Implemented\r\n"},
383 {status::BAD_GATEWAY,
"HTTP/1.1 502 Bad Gateway\r\n"},
384 {status::SERVICE_UNAVAILABLE,
"HTTP/1.1 503 Service Unavailable\r\n"},
385 {status::GATEWAY_TIMEOUT,
"HTTP/1.1 504 Gateway Timeout\r\n"},
386 {status::VARIANT_ALSO_NEGOTIATES,
"HTTP/1.1 506 Variant Also Negotiates\r\n"},
389 static const std::string seperator =
": ";
392 buffers.reserve(4 * (
headers.size() + 5) + 3);
394 if (!statusCodes.count(
code))
396 CROW_LOG_WARNING <<
this <<
" status code "
397 <<
"(" <<
code <<
")"
398 <<
" not defined, returning 500 instead";
402 auto& status = statusCodes.find(
code)->second;
403 buffers.emplace_back(status.data(), status.size());
410 buffers.emplace_back(kv.first.data(), kv.first.size());
411 buffers.emplace_back(seperator.data(), seperator.size());
412 buffers.emplace_back(kv.second.data(), kv.second.size());
413 buffers.emplace_back(crlf.data(), crlf.size());
418 content_length_buffer = std::to_string(
body.size());
419 static std::string content_length_tag =
"Content-Length: ";
420 buffers.emplace_back(content_length_tag.data(), content_length_tag.size());
421 buffers.emplace_back(content_length_buffer.data(), content_length_buffer.size());
422 buffers.emplace_back(crlf.data(), crlf.size());
424 if (!
headers.count(
"server") && !server_name.empty())
426 static std::string server_tag =
"Server: ";
427 buffers.emplace_back(server_tag.data(), server_tag.size());
428 buffers.emplace_back(server_name.data(), server_name.size());
429 buffers.emplace_back(crlf.data(), crlf.size());
441 static std::string keep_alive_tag =
"Connection: Keep-Alive";
442 buffers.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
443 buffers.emplace_back(crlf.data(), crlf.size());
446 buffers.emplace_back(crlf.data(), crlf.size());
450 std::function<void()> complete_request_handler_;
451 std::function<bool()> is_alive_helper_;
452 static_file_info file_info;