mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-21 07:29:53 +00:00
RequestServer+LibWebSocket: Drain underlying socket and resulting frames
`curl_easy_recv` must be called in a loop until it returns EAGAIN, because it may cache data, but only activate the read notifier once. Additionally, the data received can contain multiple WebSocket frames and only activate the notifier once, so we have to keep reading frames until there isn't enough data. We also have to do this immediately after connecting a WebSocket, since the server may immediately send data when the WebSocket opens and before we create the read notifier. This makes Discord login faster and more reliable, and makes Discord activities start loading.
This commit is contained in:
parent
484a09d6a2
commit
9c5dff5be0
Notes:
github-actions[bot]
2025-07-22 11:57:12 +00:00
Author: https://github.com/Lubrsi
Commit: 9c5dff5be0
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5560
Reviewed-by: https://github.com/gmta ✅
4 changed files with 63 additions and 29 deletions
|
@ -160,6 +160,43 @@ void WebSocketImplCurl::discard_connection()
|
|||
}
|
||||
}
|
||||
|
||||
void WebSocketImplCurl::read_from_socket()
|
||||
{
|
||||
bool received_data = false;
|
||||
|
||||
// "Wait on the socket only if curl_easy_recv returns CURLE_AGAIN. The reason for this is libcurl or the SSL
|
||||
// library may internally cache some data, therefore you should call curl_easy_recv until all data is read which
|
||||
// would include any cached data."
|
||||
for (;;) {
|
||||
u8 buffer[65536];
|
||||
size_t nread = 0;
|
||||
CURLcode const result = curl_easy_recv(m_easy_handle, buffer, sizeof(buffer), &nread);
|
||||
if (result == CURLE_AGAIN)
|
||||
break;
|
||||
|
||||
if (result != CURLE_OK) {
|
||||
dbgln("Failed to read from WebSocket: {}", curl_easy_strerror(result));
|
||||
on_connection_error();
|
||||
return;
|
||||
}
|
||||
|
||||
// "Reading exactly 0 bytes indicates a closed connection."
|
||||
if (nread == 0) {
|
||||
dbgln("Failed to read from WebSocket: Server closed connection");
|
||||
on_connection_error();
|
||||
return;
|
||||
}
|
||||
|
||||
received_data = true;
|
||||
|
||||
if (auto const err = m_read_buffer.write_until_depleted({ buffer, nread }); err.is_error())
|
||||
on_connection_error();
|
||||
}
|
||||
|
||||
if (received_data)
|
||||
on_ready_to_read();
|
||||
}
|
||||
|
||||
bool WebSocketImplCurl::did_connect()
|
||||
{
|
||||
curl_socket_t socket_fd = CURL_SOCKET_BAD;
|
||||
|
@ -169,21 +206,7 @@ bool WebSocketImplCurl::did_connect()
|
|||
|
||||
m_read_notifier = Core::Notifier::construct(socket_fd, Core::Notifier::Type::Read);
|
||||
m_read_notifier->on_activation = [this] {
|
||||
u8 buffer[65536];
|
||||
size_t nread = 0;
|
||||
CURLcode const result = curl_easy_recv(m_easy_handle, buffer, sizeof(buffer), &nread);
|
||||
if (result == CURLE_AGAIN)
|
||||
return;
|
||||
|
||||
if (result != CURLE_OK) {
|
||||
dbgln("Failed to read from WebSocket: {}", curl_easy_strerror(result));
|
||||
on_connection_error();
|
||||
}
|
||||
|
||||
if (auto const err = m_read_buffer.write_until_depleted({ buffer, nread }); err.is_error())
|
||||
on_connection_error();
|
||||
|
||||
on_ready_to_read();
|
||||
read_from_socket();
|
||||
};
|
||||
m_error_notifier = Core::Notifier::construct(socket_fd, Core::Notifier::Type::Error | Core::Notifier::Type::HangUp);
|
||||
m_error_notifier->on_activation = [this] {
|
||||
|
@ -191,6 +214,11 @@ bool WebSocketImplCurl::did_connect()
|
|||
};
|
||||
|
||||
on_connected();
|
||||
|
||||
// There may be data waiting for us already (e.g. if the server sends us data immediately upon opening a WebSocket),
|
||||
// so try reading immediately.
|
||||
read_from_socket();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue