blob: 3ee27ae070a226f741ad66f81de7058dd0c3d0b2 [file] [log] [blame] [edit]
/*
* Copyright (C) 2024 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "WebTransportServer.h"
#if HAVE(WEB_TRANSPORT)
#import "HTTPServer.h"
#import "Utilities.h"
#import <pal/spi/cocoa/NetworkSPI.h>
#import <wtf/BlockPtr.h>
#import <wtf/darwin/DispatchExtras.h>
namespace TestWebKitAPI {
struct WebTransportServer::Data : public RefCounted<WebTransportServer::Data> {
static Ref<Data> create(Function<ConnectionTask(ConnectionGroup)>&& connectionGroupHandler) { return adoptRef(*new Data(WTF::move(connectionGroupHandler))); }
Data(Function<ConnectionTask(ConnectionGroup)>&& connectionGroupHandler)
: connectionGroupHandler(WTF::move(connectionGroupHandler)) { }
Function<ConnectionTask(ConnectionGroup)> connectionGroupHandler;
RetainPtr<nw_listener_t> listener;
Vector<ConnectionGroup> connectionGroups;
Vector<CoroutineHandle<ConnectionTask::promise_type>> coroutineHandles;
};
WebTransportServer::WebTransportServer(Function<ConnectionTask(ConnectionGroup)>&& connectionGroupHandler, sec_identity_t identity)
: m_data(Data::create(WTF::move(connectionGroupHandler)))
{
auto configureWebTransport = [](nw_protocol_options_t options) {
nw_webtransport_options_set_is_datagram(options, true);
nw_webtransport_options_set_is_unidirectional(options, false);
nw_webtransport_options_set_connection_max_sessions(options, 1);
};
auto configureTLS = [identity = RetainPtr { identity }] (nw_protocol_options_t options) {
RetainPtr securityOptions = adoptNS(nw_tls_copy_sec_protocol_options(options));
sec_protocol_options_set_local_identity(securityOptions.get(), identity ? identity.get() : adoptNS(sec_identity_create(testIdentity().get())).get());
};
auto configureQUIC = [](nw_protocol_options_t options) {
nw_quic_set_initial_max_streams_bidirectional(options, std::numeric_limits<uint32_t>::max());
nw_quic_set_initial_max_streams_unidirectional(options, std::numeric_limits<uint32_t>::max());
nw_quic_set_max_datagram_frame_size(options, std::numeric_limits<uint16_t>::max());
};
RetainPtr parameters = adoptNS(nw_parameters_create_webtransport_http(configureWebTransport, configureTLS, configureQUIC, NW_PARAMETERS_DEFAULT_CONFIGURATION));
ASSERT(parameters);
nw_parameters_set_server_mode(parameters.get(), true);
RetainPtr listener = adoptNS(nw_listener_create(parameters.get()));
// FIXME: Verify the incoming CONNECT request has an Origin header once rdar://141457647 is available in OS builds.
nw_listener_set_new_connection_group_handler(listener.get(), [data = m_data] (nw_connection_group_t incomingConnectionGroup) {
ConnectionGroup connectionGroup = ConnectionGroup(incomingConnectionGroup);
data->connectionGroups.append(connectionGroup);
nw_connection_group_set_state_changed_handler(incomingConnectionGroup, [connectionGroup, data] (nw_connection_group_state_t state, nw_error_t error) mutable {
switch (state) {
case nw_connection_group_state_ready:
data->coroutineHandles.append(data->connectionGroupHandler(connectionGroup).handle);
break;
case nw_connection_group_state_failed:
connectionGroup.markAsFailed();
break;
default:
break;
}
});
nw_connection_group_set_new_connection_handler(incomingConnectionGroup, [connectionGroup] (nw_connection_t incomingConnection) mutable {
connectionGroup.receiveIncomingConnection(incomingConnection);
});
nw_connection_group_set_queue(incomingConnectionGroup, mainDispatchQueueSingleton());
nw_connection_group_start(incomingConnectionGroup);
});
nw_listener_set_queue(listener.get(), mainDispatchQueueSingleton());
__block bool ready = false;
nw_listener_set_state_changed_handler(listener.get(), ^(nw_listener_state_t state, nw_error_t error) {
ASSERT_UNUSED(error, !error);
if (state == nw_listener_state_ready)
ready = true;
});
nw_listener_start(listener.get());
Util::run(&ready);
m_data->listener = WTF::move(listener);
}
WebTransportServer::~WebTransportServer() = default;
uint16_t WebTransportServer::port() const
{
return nw_listener_get_port(m_data->listener.get());
}
} // namespace TestWebKitAPI
#endif // HAVE(WEB_TRANSPORT)