Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#692 Add the possibility to specify "Sec-WebSocket-Protocol" #693

Merged
merged 11 commits into from
Sep 10, 2024
5 changes: 5 additions & 0 deletions docs/guides/websockets.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ The maximum payload size that a connection accepts can be adjusted either global

By default, this limit is disabled. To disable the global setting in specific routes, you only need to call `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").max_payload(UINT64_MAX)`.

## Subprotocols
<span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>

Specifies the possible subprotocols that are available for the client. If specified, the first match with the client's requested subprotocols will be returned in the "Sec-WebSocket-Protocol" header of the handshake response. Otherwise, the connection will be closed. If no subprotocol are specified on both the client and the server side, the connection process will continue normally. It can be specified by using `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").subprotocols(<values>)`.


For more info about websocket routes go [here](../reference/classcrow_1_1_web_socket_rule.html).

Expand Down
11 changes: 9 additions & 2 deletions include/crow/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,12 +451,12 @@ namespace crow
void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
{
max_payload_ = max_payload_override_ ? max_payload_ : app_->websocket_max_payload();
new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, max_payload_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
}
#ifdef CROW_ENABLE_SSL
void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
{
new crow::websocket::Connection<SSLAdaptor, App>(req, std::move(adaptor), app_, max_payload_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
new crow::websocket::Connection<SSLAdaptor, App>(req, std::move(adaptor), app_, max_payload_, subprotocols_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
}
#endif

Expand All @@ -468,6 +468,12 @@ namespace crow
return *this;
}

self_t& subprotocols(std::vector<std::string>&& subprotocols)
{
subprotocols_ = subprotocols;
return *this;
}
KaSSaaaa marked this conversation as resolved.
Show resolved Hide resolved

template<typename Func>
self_t& onopen(Func f)
{
Expand Down Expand Up @@ -512,6 +518,7 @@ namespace crow
std::function<bool(const crow::request&, void**)> accept_handler_;
uint64_t max_payload_;
bool max_payload_override_ = false;
std::vector<std::string> subprotocols_;
};

/// Allows the user to assign parameters using functions.
Expand Down
18 changes: 18 additions & 0 deletions include/crow/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -897,5 +897,23 @@ namespace crow

return v.substr(begin, end - begin);
}

/**
* @brief splits a string based on a separator
*/
inline static std::vector<std::string> split(const std::string& v, const std::string& separator)
{
std::vector<std::string> result;
size_t startPos = 0;

for (size_t foundPos = v.find(separator); foundPos != std::string::npos; foundPos = v.find(separator, startPos))
{
result.push_back(v.substr(startPos, foundPos - startPos));
startPos = foundPos + separator.size();
}

result.push_back(v.substr(startPos));
return result;
}
} // namespace utility
} // namespace crow
28 changes: 27 additions & 1 deletion include/crow/websocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ namespace crow
///
/// Requires a request with an "Upgrade: websocket" header.<br>
/// Automatically handles the handshake.
Connection(const crow::request& req, Adaptor&& adaptor, Handler* handler, uint64_t max_payload,
Connection(const crow::request& req, Adaptor&& adaptor, Handler* handler,
uint64_t max_payload, const std::vector<std::string>& subprotocols,
std::function<void(crow::websocket::connection&)> open_handler,
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
Expand All @@ -94,6 +95,24 @@ namespace crow
return;
}

std::string requested_subprotocols_header = req.get_header_value("Sec-WebSocket-Protocol");
if (!subprotocols.empty() || !requested_subprotocols_header.empty())
{
auto requested_subprotocols = utility::split(requested_subprotocols_header, ", ");
auto subprotocol = std::find_first_of(subprotocols.begin(), subprotocols.end(), requested_subprotocols.begin(), requested_subprotocols.end());
KaSSaaaa marked this conversation as resolved.
Show resolved Hide resolved
if (subprotocol == subprotocols.end())
{
adaptor_.close();
handler_->remove_websocket(this);
KaSSaaaa marked this conversation as resolved.
Show resolved Hide resolved
delete this;
return;
}
else
{
subprotocol_ = *subprotocol;
}
}

if (accept_handler_)
{
void* ud = nullptr;
Expand Down Expand Up @@ -265,6 +284,12 @@ namespace crow
write_buffers_.emplace_back(header);
write_buffers_.emplace_back(std::move(hello));
write_buffers_.emplace_back(crlf);
if (!subprotocol_.empty())
{
write_buffers_.emplace_back("Sec-WebSocket-Protocol: ");
write_buffers_.emplace_back(subprotocol_);
write_buffers_.emplace_back(crlf);
}
write_buffers_.emplace_back(crlf);
do_write();
if (open_handler_)
Expand Down Expand Up @@ -721,6 +746,7 @@ namespace crow
uint16_t remaining_length16_{0};
uint64_t remaining_length_{0};
uint64_t max_payload_bytes_{UINT64_MAX};
std::string subprotocol_;
KaSSaaaa marked this conversation as resolved.
Show resolved Hide resolved
bool close_connection_{false};
bool is_reading{false};
bool has_mask_{false};
Expand Down