mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-10 01:59:31 +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)
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibDNS/Resolver.h>
|
||||||
#include <LibHTTP/HeaderMap.h>
|
#include <LibHTTP/HeaderMap.h>
|
||||||
#include <LibURL/URL.h>
|
#include <LibURL/URL.h>
|
||||||
|
|
||||||
|
@ -33,6 +34,9 @@ public:
|
||||||
Optional<ByteString> const& root_certificates_path() const { return m_root_certificates_path; }
|
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); }
|
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
|
// secure flag - defined in RFC 6455 Section 3
|
||||||
bool is_secure() const;
|
bool is_secure() const;
|
||||||
|
|
||||||
|
@ -46,6 +50,7 @@ private:
|
||||||
Vector<ByteString> m_extensions {};
|
Vector<ByteString> m_extensions {};
|
||||||
HTTP::HeaderMap m_headers;
|
HTTP::HeaderMap m_headers;
|
||||||
Optional<ByteString> m_root_certificates_path;
|
Optional<ByteString> m_root_certificates_path;
|
||||||
|
RefPtr<DNS::LookupResult const> m_dns_result;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <LibCore/Proxy.h>
|
#include <LibCore/Proxy.h>
|
||||||
#include <LibCore/Socket.h>
|
#include <LibCore/Socket.h>
|
||||||
#include <LibRequests/NetworkErrorEnum.h>
|
#include <LibRequests/NetworkErrorEnum.h>
|
||||||
|
#include <LibRequests/WebSocket.h>
|
||||||
#include <LibTLS/TLSv12.h>
|
#include <LibTLS/TLSv12.h>
|
||||||
#include <LibTextCodec/Decoder.h>
|
#include <LibTextCodec/Decoder.h>
|
||||||
#include <LibWebSocket/ConnectionInfo.h>
|
#include <LibWebSocket/ConnectionInfo.h>
|
||||||
|
@ -73,6 +74,24 @@ static NonnullRefPtr<Resolver> default_resolver()
|
||||||
return 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 {
|
struct ConnectionFromClient::ActiveRequest {
|
||||||
CURLM* multi { nullptr };
|
CURLM* multi { nullptr };
|
||||||
CURL* easy { 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_HEADERFUNCTION, &on_header_received);
|
||||||
set_option(CURLOPT_HEADERDATA, reinterpret_cast<void*>(request.ptr()));
|
set_option(CURLOPT_HEADERDATA, reinterpret_cast<void*>(request.ptr()));
|
||||||
|
|
||||||
StringBuilder resolve_opt_builder;
|
auto formatted_address = build_curl_resolve_list(*dns_result, host, url.port_or_default());
|
||||||
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();
|
|
||||||
if (curl_slist* resolve_list = curl_slist_append(nullptr, formatted_address.characters())) {
|
if (curl_slist* resolve_list = curl_slist_append(nullptr, formatted_address.characters())) {
|
||||||
set_option(CURLOPT_RESOLVE, resolve_list);
|
set_option(CURLOPT_RESOLVE, resolve_list);
|
||||||
request->curl_string_lists.append(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)
|
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
|
auto host = url.serialized_host().to_byte_string();
|
||||||
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);
|
|
||||||
|
|
||||||
if (!g_default_certificate_path.is_empty())
|
// Check if host has the bracket notation for IPV6 addresses and remove them
|
||||||
connection_info.set_root_certificates_path(g_default_certificate_path);
|
if (host.starts_with("["sv) && host.ends_with("]"sv))
|
||||||
|
host = host.substring(1, host.length() - 2);
|
||||||
|
|
||||||
auto impl = WebSocketImplCurl::create(m_curl_multi);
|
m_resolver->dns.lookup(host, DNS::Messages::Class::IN, { DNS::Messages::ResourceType::A, DNS::Messages::ResourceType::AAAA })
|
||||||
auto connection = WebSocket::WebSocket::create(move(connection_info), move(impl));
|
->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]() {
|
WebSocket::ConnectionInfo connection_info(url);
|
||||||
async_websocket_connected(websocket_id);
|
connection_info.set_origin(origin);
|
||||||
};
|
connection_info.set_protocols(protocols);
|
||||||
connection->on_message = [this, websocket_id](auto message) {
|
connection_info.set_extensions(extensions);
|
||||||
async_websocket_received(websocket_id, message.is_text(), message.data());
|
connection_info.set_headers(additional_request_headers);
|
||||||
};
|
connection_info.set_dns_result(move(dns_result));
|
||||||
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();
|
if (!g_default_certificate_path.is_empty())
|
||||||
m_websockets.set(websocket_id, move(connection));
|
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)
|
void ConnectionFromClient::websocket_send(i64 websocket_id, bool is_text, ByteBuffer const& data)
|
||||||
|
|
|
@ -77,6 +77,8 @@ private:
|
||||||
NonnullRefPtr<Resolver> m_resolver;
|
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;
|
constexpr inline uintptr_t websocket_private_tag = 0x1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "ConnectionFromClient.h"
|
||||||
|
|
||||||
#include <RequestServer/WebSocketImplCurl.h>
|
#include <RequestServer/WebSocketImplCurl.h>
|
||||||
|
|
||||||
namespace RequestServer {
|
namespace RequestServer {
|
||||||
|
@ -96,6 +98,12 @@ void WebSocketImplCurl::connect(WebSocket::ConnectionInfo const& info)
|
||||||
set_option(CURLOPT_HTTPHEADER, curl_headers);
|
set_option(CURLOPT_HTTPHEADER, curl_headers);
|
||||||
m_curl_string_lists.append(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);
|
CURLMcode const err = curl_multi_add_handle(m_multi_handle, m_easy_handle);
|
||||||
VERIFY(err == CURLM_OK);
|
VERIFY(err == CURLM_OK);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue