mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-09 09:39:39 +00:00
LibWebSocket+RequestServer: Resolve WebSocket hosts using our resolver
Some checks failed
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Build Dev Container Image / build (push) Has been cancelled
Some checks failed
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Build Dev Container Image / build (push) Has been cancelled
This commit is contained in:
parent
eb4c565e57
commit
06faa7b160
Notes:
github-actions[bot]
2025-02-20 22:05:53 +00:00
Author: https://github.com/ADKaster
Commit: 06faa7b160
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3642
Reviewed-by: https://github.com/alimpfard ✅
5 changed files with 82 additions and 42 deletions
|
@ -6,4 +6,4 @@ set(SOURCES
|
|||
)
|
||||
|
||||
serenity_lib(LibWebSocket websocket)
|
||||
target_link_libraries(LibWebSocket PRIVATE LibCore LibCrypto LibTLS LibURL)
|
||||
target_link_libraries(LibWebSocket PRIVATE LibCore LibCrypto LibTLS LibURL LibDNS)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibDNS/Resolver.h>
|
||||
#include <LibHTTP/HeaderMap.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
|
@ -33,6 +34,9 @@ public:
|
|||
Optional<ByteString> const& root_certificates_path() const { return m_root_certificates_path; }
|
||||
void set_root_certificates_path(Optional<ByteString> root_certificates_path) { m_root_certificates_path = move(root_certificates_path); }
|
||||
|
||||
Optional<DNS::LookupResult const&> dns_result() const { return m_dns_result ? Optional<DNS::LookupResult const&>(*m_dns_result) : OptionalNone {}; }
|
||||
void set_dns_result(NonnullRefPtr<DNS::LookupResult const> dns_result) { m_dns_result = move(dns_result); }
|
||||
|
||||
// secure flag - defined in RFC 6455 Section 3
|
||||
bool is_secure() const;
|
||||
|
||||
|
@ -46,6 +50,7 @@ private:
|
|||
Vector<ByteString> m_extensions {};
|
||||
HTTP::HeaderMap m_headers;
|
||||
Optional<ByteString> m_root_certificates_path;
|
||||
RefPtr<DNS::LookupResult const> m_dns_result;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibRequests/NetworkErrorEnum.h>
|
||||
#include <LibRequests/WebSocket.h>
|
||||
#include <LibTLS/TLSv12.h>
|
||||
#include <LibTextCodec/Decoder.h>
|
||||
#include <LibWebSocket/ConnectionInfo.h>
|
||||
|
@ -73,6 +74,24 @@ static NonnullRefPtr<Resolver> default_resolver()
|
|||
return resolver;
|
||||
}
|
||||
|
||||
ByteString build_curl_resolve_list(DNS::LookupResult const& dns_result, StringView host, u16 port)
|
||||
{
|
||||
StringBuilder resolve_opt_builder;
|
||||
resolve_opt_builder.appendff("{}:{}:", host, port);
|
||||
auto first = true;
|
||||
for (auto& addr : dns_result.cached_addresses()) {
|
||||
auto formatted_address = addr.visit(
|
||||
[&](IPv4Address const& ipv4) { return ipv4.to_byte_string(); },
|
||||
[&](IPv6Address const& ipv6) { return MUST(ipv6.to_string()).to_byte_string(); });
|
||||
if (!first)
|
||||
resolve_opt_builder.append(',');
|
||||
first = false;
|
||||
resolve_opt_builder.append(formatted_address);
|
||||
}
|
||||
|
||||
return resolve_opt_builder.to_byte_string();
|
||||
}
|
||||
|
||||
struct ConnectionFromClient::ActiveRequest {
|
||||
CURLM* multi { nullptr };
|
||||
CURL* easy { nullptr };
|
||||
|
@ -463,20 +482,7 @@ void ConnectionFromClient::start_request(i32 request_id, ByteString const& metho
|
|||
set_option(CURLOPT_HEADERFUNCTION, &on_header_received);
|
||||
set_option(CURLOPT_HEADERDATA, reinterpret_cast<void*>(request.ptr()));
|
||||
|
||||
StringBuilder resolve_opt_builder;
|
||||
resolve_opt_builder.appendff("{}:{}:", host, url.port_or_default());
|
||||
auto first = true;
|
||||
for (auto& addr : dns_result->cached_addresses()) {
|
||||
auto formatted_address = addr.visit(
|
||||
[&](IPv4Address const& ipv4) { return ipv4.to_byte_string(); },
|
||||
[&](IPv6Address const& ipv6) { return MUST(ipv6.to_string()).to_byte_string(); });
|
||||
if (!first)
|
||||
resolve_opt_builder.append(',');
|
||||
first = false;
|
||||
resolve_opt_builder.append(formatted_address);
|
||||
}
|
||||
|
||||
auto formatted_address = resolve_opt_builder.to_byte_string();
|
||||
auto formatted_address = build_curl_resolve_list(*dns_result, host, url.port_or_default());
|
||||
if (curl_slist* resolve_list = curl_slist_append(nullptr, formatted_address.characters())) {
|
||||
set_option(CURLOPT_RESOLVE, resolve_list);
|
||||
request->curl_string_lists.append(resolve_list);
|
||||
|
@ -653,37 +659,56 @@ void ConnectionFromClient::ensure_connection(URL::URL const& url, ::RequestServe
|
|||
|
||||
void ConnectionFromClient::websocket_connect(i64 websocket_id, URL::URL const& url, ByteString const& origin, Vector<ByteString> const& protocols, Vector<ByteString> const& extensions, HTTP::HeaderMap const& additional_request_headers)
|
||||
{
|
||||
// FIXME: Use our DNS resolver to resolve the hostname
|
||||
WebSocket::ConnectionInfo connection_info(url);
|
||||
connection_info.set_origin(origin);
|
||||
connection_info.set_protocols(protocols);
|
||||
connection_info.set_extensions(extensions);
|
||||
connection_info.set_headers(additional_request_headers);
|
||||
auto host = url.serialized_host().to_byte_string();
|
||||
|
||||
if (!g_default_certificate_path.is_empty())
|
||||
connection_info.set_root_certificates_path(g_default_certificate_path);
|
||||
// Check if host has the bracket notation for IPV6 addresses and remove them
|
||||
if (host.starts_with("["sv) && host.ends_with("]"sv))
|
||||
host = host.substring(1, host.length() - 2);
|
||||
|
||||
auto impl = WebSocketImplCurl::create(m_curl_multi);
|
||||
auto connection = WebSocket::WebSocket::create(move(connection_info), move(impl));
|
||||
m_resolver->dns.lookup(host, DNS::Messages::Class::IN, { DNS::Messages::ResourceType::A, DNS::Messages::ResourceType::AAAA })
|
||||
->when_rejected([this, websocket_id](auto const& error) {
|
||||
dbgln("WebSocketConnect: DNS lookup failed: {}", error);
|
||||
async_websocket_errored(websocket_id, static_cast<i32>(Requests::WebSocket::Error::CouldNotEstablishConnection));
|
||||
})
|
||||
.when_resolved([this, websocket_id, host, url, origin, protocols, extensions, additional_request_headers](auto dns_result) {
|
||||
if (dns_result->records().is_empty() || dns_result->cached_addresses().is_empty()) {
|
||||
dbgln("WebSocketConnect: DNS lookup failed for '{}'", host);
|
||||
async_websocket_errored(websocket_id, static_cast<i32>(Requests::WebSocket::Error::CouldNotEstablishConnection));
|
||||
return;
|
||||
}
|
||||
|
||||
connection->on_open = [this, websocket_id]() {
|
||||
async_websocket_connected(websocket_id);
|
||||
};
|
||||
connection->on_message = [this, websocket_id](auto message) {
|
||||
async_websocket_received(websocket_id, message.is_text(), message.data());
|
||||
};
|
||||
connection->on_error = [this, websocket_id](auto message) {
|
||||
async_websocket_errored(websocket_id, (i32)message);
|
||||
};
|
||||
connection->on_close = [this, websocket_id](u16 code, ByteString reason, bool was_clean) {
|
||||
async_websocket_closed(websocket_id, code, move(reason), was_clean);
|
||||
};
|
||||
connection->on_ready_state_change = [this, websocket_id](auto state) {
|
||||
async_websocket_ready_state_changed(websocket_id, (u32)state);
|
||||
};
|
||||
WebSocket::ConnectionInfo connection_info(url);
|
||||
connection_info.set_origin(origin);
|
||||
connection_info.set_protocols(protocols);
|
||||
connection_info.set_extensions(extensions);
|
||||
connection_info.set_headers(additional_request_headers);
|
||||
connection_info.set_dns_result(move(dns_result));
|
||||
|
||||
connection->start();
|
||||
m_websockets.set(websocket_id, move(connection));
|
||||
if (!g_default_certificate_path.is_empty())
|
||||
connection_info.set_root_certificates_path(g_default_certificate_path);
|
||||
|
||||
auto impl = WebSocketImplCurl::create(m_curl_multi);
|
||||
auto connection = WebSocket::WebSocket::create(move(connection_info), move(impl));
|
||||
|
||||
connection->on_open = [this, websocket_id]() {
|
||||
async_websocket_connected(websocket_id);
|
||||
};
|
||||
connection->on_message = [this, websocket_id](auto message) {
|
||||
async_websocket_received(websocket_id, message.is_text(), message.data());
|
||||
};
|
||||
connection->on_error = [this, websocket_id](auto message) {
|
||||
async_websocket_errored(websocket_id, (i32)message);
|
||||
};
|
||||
connection->on_close = [this, websocket_id](u16 code, ByteString reason, bool was_clean) {
|
||||
async_websocket_closed(websocket_id, code, move(reason), was_clean);
|
||||
};
|
||||
connection->on_ready_state_change = [this, websocket_id](auto state) {
|
||||
async_websocket_ready_state_changed(websocket_id, (u32)state);
|
||||
};
|
||||
|
||||
connection->start();
|
||||
m_websockets.set(websocket_id, move(connection));
|
||||
});
|
||||
}
|
||||
|
||||
void ConnectionFromClient::websocket_send(i64 websocket_id, bool is_text, ByteBuffer const& data)
|
||||
|
|
|
@ -77,6 +77,8 @@ private:
|
|||
NonnullRefPtr<Resolver> m_resolver;
|
||||
};
|
||||
|
||||
// FIXME: Find a good home for this
|
||||
ByteString build_curl_resolve_list(DNS::LookupResult const&, StringView host, u16 port);
|
||||
constexpr inline uintptr_t websocket_private_tag = 0x1;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "ConnectionFromClient.h"
|
||||
|
||||
#include <RequestServer/WebSocketImplCurl.h>
|
||||
|
||||
namespace RequestServer {
|
||||
|
@ -96,6 +98,12 @@ void WebSocketImplCurl::connect(WebSocket::ConnectionInfo const& info)
|
|||
set_option(CURLOPT_HTTPHEADER, curl_headers);
|
||||
m_curl_string_lists.append(curl_headers);
|
||||
|
||||
if (auto const& dns_info = info.dns_result(); dns_info.has_value()) {
|
||||
auto* resolve_list = curl_slist_append(nullptr, build_curl_resolve_list(*dns_info, url.serialized_host(), url.port_or_default()).characters());
|
||||
set_option(CURLOPT_RESOLVE, resolve_list);
|
||||
m_curl_string_lists.append(resolve_list);
|
||||
}
|
||||
|
||||
CURLMcode const err = curl_multi_add_handle(m_multi_handle, m_easy_handle);
|
||||
VERIFY(err == CURLM_OK);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue