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

Implement experimental websocket client #2552

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

p12tic
Copy link
Contributor

@p12tic p12tic commented Nov 25, 2024

This PR consists of 2 parts:

  • general refactoring to make it easier for Websocket client to reuse existing code
  • implementation of the Websocket client itself

It probably makes sense to land the general refactorings in smaller PRs once Websocket client is approved in principle.

One way that the websocket client differs from what is prescribed by RFC 6455 is that the client masking key is not random. The practical reason for that is to avoid needing to pull randomization infrastructure into the Websocket client. This would need additional APIs to control, as it makes sense to turn off randomization in unit tests.

Below is an excerpt from commit description on why this is reasonable solution

https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 requires that
the masking key must be unpredictable and derived from a strong source
of entropy. This requirement arose due to usage of WebSocket protocol in
the browsers, where both server and client payload generation code may
be malicious and the only trusted piece is the browser itself.
Consequently there was a need to make the bytes on the wire
unpredictable for the client code, so that it cannot run attacks against
intermediate proxies that do not understand WebSocket.

In the case of Seastar there is no security boundary between payload
generator and payload serializer in this class, accordingly in terms of
security impact it is sufficient to simply use predictable masking key.
Zero is chosen because it does not change the payload. 

Websocket protocol handshake uses HTTP, thus it makes sense to reuse at
least some of the abstractions and API style.
This will allow to implement Websocket client that reuses existing code.
Current implementation of websocket connection is specific to the
server. Rename to more appropriate name.
Websocket connection handling is mostly not specific to the server and
thus can be reused on the client easily.
Previous name was useful when it's used locally in translation unit. The
new name is exposed in a header and should be more unique.
https://datatracker.ietf.org/doc/html/rfc6455#section-5.3 requires that
the masking key must be unpredictable and derived from a strong source
of entropy. This requirement arose due to usage of WebSocket protocol in
the browsers, where both server and client payload generation code may
be malicious and the only trusted piece is the browser itself.
Consequently there was a need to make the bytes on the wire
unpredictable for the client code, so that it cannot run attacks against
intermediate proxies that do not understand WebSocket.

In the case of Seastar there is no security boundary between payload
generator and payload serializer in this class, accordingly in terms of
security impact it is sufficient to simply use predictable masking key.
Zero is chosen because it does not change the payload.
/*!
* \param fd established socket used for communication
*/
connection(connected_socket&& fd)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be protected as well?

@@ -243,16 +243,19 @@ protected:

sstring _subprotocol;
handler_t _handler;
bool _is_client;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that std::optional<uint32_t> _masking_key would fit better here. Constructor and send_key() would need to be updated accordingly

@@ -168,6 +172,26 @@ future<> connection::send_data(opcodes opcode, temporary_buffer<char>&& buff) {
header[1] = uint8_t(buff.size());
}

temporary_buffer<char> write_buf;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused in this patch. Maybe it's used later, but then it should as well be added "later"

@@ -87,7 +87,7 @@ struct frame_header {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch is missing descriptive comment

@@ -777,6 +777,7 @@ add_library (seastar
src/util/read_first_line.cc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patch comment is missing

*
* \param addr -- host address to connect to
* \param creds -- credentials
* \param host -- optional host name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional local host name ?

@@ -777,6 +777,7 @@ add_library (seastar
src/util/read_first_line.cc
src/util/tmp_file.cc
src/util/short_streams.cc
src/websocket/client.cc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test is missing as well

*/
future<> send_message(temporary_buffer<char> buf, bool flush);

protected:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no private? Are there plans to implement more specific connections inherited from this class?


auto con = seastar::make_shared<client_connection>(*this, std::move(cs), ws_key, handler);

auto sub = as ? as->subscribe([con] () noexcept { con->shutdown_input(); }) : std::nullopt;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sub dies immediately after con->perform_handshake() returns, even if its future doesn't resolve immediately (likely it doesn't)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants