Skip to content

Commit 6ba7d7f

Browse files
autoantwortgittiver
authored andcommitted
websockets: Allow sending a response when rejecting a websocket request (Closes #1097)
1 parent 2a5a051 commit 6ba7d7f

4 files changed

Lines changed: 169 additions & 131 deletions

File tree

include/crow/http_connection.h

Lines changed: 1 addition & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -283,111 +283,7 @@ namespace crow
283283
//delete this;
284284
return;
285285
}
286-
// TODO(EDev): HTTP version in status codes should be dynamic
287-
// Keep in sync with common.h/status
288-
static std::unordered_map<int, std::string> statusCodes = {
289-
{status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
290-
{status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
291-
292-
{status::OK, "HTTP/1.1 200 OK\r\n"},
293-
{status::CREATED, "HTTP/1.1 201 Created\r\n"},
294-
{status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
295-
{status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
296-
{status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
297-
{status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
298-
{status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
299-
300-
{status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
301-
{status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
302-
{status::FOUND, "HTTP/1.1 302 Found\r\n"},
303-
{status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
304-
{status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
305-
{status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
306-
{status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
307-
308-
{status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
309-
{status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
310-
{status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
311-
{status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
312-
{status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
313-
{status::NOT_ACCEPTABLE, "HTTP/1.1 406 Not Acceptable\r\n"},
314-
{status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
315-
{status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
316-
{status::GONE, "HTTP/1.1 410 Gone\r\n"},
317-
{status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
318-
{status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
319-
{status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
320-
{status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
321-
{status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
322-
{status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
323-
{status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
324-
325-
{status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
326-
{status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
327-
{status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
328-
{status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
329-
{status::GATEWAY_TIMEOUT, "HTTP/1.1 504 Gateway Timeout\r\n"},
330-
{status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
331-
};
332-
333-
static const std::string seperator = ": ";
334-
335-
buffers_.clear();
336-
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
337-
338-
if (!statusCodes.count(res.code))
339-
{
340-
CROW_LOG_WARNING << this << " status code "
341-
<< "(" << res.code << ")"
342-
<< " not defined, returning 500 instead";
343-
res.code = 500;
344-
}
345-
346-
auto& status = statusCodes.find(res.code)->second;
347-
buffers_.emplace_back(status.data(), status.size());
348-
349-
if (res.code >= 400 && res.body.empty())
350-
res.body = statusCodes[res.code].substr(9);
351-
352-
for (auto& kv : res.headers)
353-
{
354-
buffers_.emplace_back(kv.first.data(), kv.first.size());
355-
buffers_.emplace_back(seperator.data(), seperator.size());
356-
buffers_.emplace_back(kv.second.data(), kv.second.size());
357-
buffers_.emplace_back(crlf.data(), crlf.size());
358-
}
359-
360-
if (!res.manual_length_header && !res.headers.count("content-length"))
361-
{
362-
content_length_ = std::to_string(res.body.size());
363-
static std::string content_length_tag = "Content-Length: ";
364-
buffers_.emplace_back(content_length_tag.data(), content_length_tag.size());
365-
buffers_.emplace_back(content_length_.data(), content_length_.size());
366-
buffers_.emplace_back(crlf.data(), crlf.size());
367-
}
368-
if (!res.headers.count("server") && !server_name_.empty())
369-
{
370-
static std::string server_tag = "Server: ";
371-
buffers_.emplace_back(server_tag.data(), server_tag.size());
372-
buffers_.emplace_back(server_name_.data(), server_name_.size());
373-
buffers_.emplace_back(crlf.data(), crlf.size());
374-
}
375-
if (!res.headers.count("date"))
376-
{
377-
static std::string date_tag = "Date: ";
378-
date_str_ = get_cached_date_str();
379-
buffers_.emplace_back(date_tag.data(), date_tag.size());
380-
buffers_.emplace_back(date_str_.data(), date_str_.size());
381-
buffers_.emplace_back(crlf.data(), crlf.size());
382-
}
383-
if (add_keep_alive_)
384-
{
385-
static std::string keep_alive_tag = "Connection: Keep-Alive";
386-
buffers_.emplace_back(keep_alive_tag.data(), keep_alive_tag.size());
387-
buffers_.emplace_back(crlf.data(), crlf.size());
388-
}
389-
390-
buffers_.emplace_back(crlf.data(), crlf.size());
286+
res.write_header_into_buffer(buffers_, content_length_, add_keep_alive_, server_name_);
391287
}
392288

393289
void do_write_static()

include/crow/http_response.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ namespace crow
2727
template<typename Adaptor, typename Handler, typename... Middlewares>
2828
class Connection;
2929

30+
namespace websocket
31+
{
32+
template<typename Adaptor, typename Handler>
33+
class Connection;
34+
}
35+
3036
class Router;
3137

3238
/// HTTP response
@@ -35,6 +41,9 @@ namespace crow
3541
template<typename Adaptor, typename Handler, typename... Middlewares>
3642
friend class crow::Connection;
3743

44+
template<typename Adaptor, typename Handler>
45+
friend class websocket::Connection;
46+
3847
friend class Router;
3948

4049
int code{200}; ///< The Status code for the response.
@@ -328,6 +337,115 @@ namespace crow
328337
}
329338

330339
private:
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)
341+
{
342+
// TODO(EDev): HTTP version in status codes should be dynamic
343+
// Keep in sync with common.h/status
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"},
347+
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"},
355+
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"},
363+
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"},
380+
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"},
387+
};
388+
389+
static const std::string seperator = ": ";
390+
391+
buffers.clear();
392+
buffers.reserve(4 * (headers.size() + 5) + 3);
393+
394+
if (!statusCodes.count(code))
395+
{
396+
CROW_LOG_WARNING << this << " status code "
397+
<< "(" << code << ")"
398+
<< " not defined, returning 500 instead";
399+
code = 500;
400+
}
401+
402+
auto& status = statusCodes.find(code)->second;
403+
buffers.emplace_back(status.data(), status.size());
404+
405+
if (code >= 400 && body.empty())
406+
body = statusCodes[code].substr(9);
407+
408+
for (auto& kv : headers)
409+
{
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());
414+
}
415+
416+
if (!manual_length_header && !headers.count("content-length"))
417+
{
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());
423+
}
424+
if (!headers.count("server") && !server_name.empty())
425+
{
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());
430+
}
431+
/*if (!headers.count("date"))
432+
{
433+
static std::string date_tag = "Date: ";
434+
date_str_ = get_cached_date_str();
435+
buffers.emplace_back(date_tag.data(), date_tag.size());
436+
buffers.emplace_back(date_str_.data(), date_str_.size());
437+
buffers.emplace_back(crlf.data(), crlf.size());
438+
}*/
439+
if (add_keep_alive)
440+
{
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());
444+
}
445+
446+
buffers.emplace_back(crlf.data(), crlf.size());
447+
}
448+
331449
bool completed_{};
332450
std::function<void()> complete_request_handler_;
333451
std::function<bool()> is_alive_helper_;

include/crow/routing.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <vector>
99
#include <algorithm>
1010
#include <type_traits>
11+
#include <optional>
1112

1213
#include "crow/common.h"
1314
#include "crow/http_response.h"
@@ -502,10 +503,21 @@ namespace crow // NOTE: Already documented in "crow/app.h"
502503
return *this;
503504
}
504505

505-
template<typename Func>
506-
self_t& onaccept(Func f)
506+
507+
self_t& onaccept(std::function<void(const crow::request&, std::optional<crow::response>&, void**)>&& callback)
507508
{
508-
accept_handler_ = f;
509+
accept_handler_ = std::move(callback);
510+
return *this;
511+
}
512+
513+
self_t& onaccept(std::function<bool(const crow::request&, void**)>&& callback)
514+
{
515+
onaccept([callback](const crow::request& req, std::optional<crow::response>& res, void** p) {
516+
if (!callback(req, p))
517+
{
518+
res = crow::response(400);
519+
}
520+
});
509521
return *this;
510522
}
511523

@@ -521,7 +533,7 @@ namespace crow // NOTE: Already documented in "crow/app.h"
521533
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
522534
std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler_;
523535
std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
524-
std::function<bool(const crow::request&, void**)> accept_handler_;
536+
std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler_;
525537
bool mirror_protocols_ = false;
526538
uint64_t max_payload_;
527539
bool max_payload_override_ = false;

0 commit comments

Comments
 (0)