- Revision
- 294492
- Author
- achristen...@apple.com
- Date
- 2022-05-19 10:40:17 -0700 (Thu, 19 May 2022)
Log Message
Begin using coroutines in testing code
https://bugs.webkit.org/show_bug.cgi?id=235730
Reviewed by Darin Adler.
co_await is nicer than nested lambdas.
This begins WebKit's use of C++ coroutines in a small, incomplete, and low-risk way by using it with test code.
Canonical link: https://commits.webkit.org/250751@main
Modified Paths
Diff
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm (294491 => 294492)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm 2022-05-19 17:13:30 UTC (rev 294491)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm 2022-05-19 17:40:17 UTC (rev 294492)
@@ -336,14 +336,12 @@
// Evaluating _javascript_ in such a document should fail and result in an error.
using namespace TestWebKitAPI;
- HTTPServer server([](Connection connection) {
- connection.receiveHTTPRequest([=](Vector<char>&&) {
- constexpr auto response = "HTTP/1.1 200 OK\r\n"
- "Content-Length: 12\r\n"
- "Content-Disposition: attachment; filename=fromHeader.txt;\r\n\r\n"
- "Hello world!"_s;
- connection.send(response);
- });
+ HTTPServer server(HTTPServer::UseCoroutines::Yes, [](Connection connection) -> Task {
+ co_await connection.awaitableReceiveHTTPRequest();
+ co_await connection.awaitableSend("HTTP/1.1 200 OK\r\n"
+ "Content-Length: 12\r\n"
+ "Content-Disposition: attachment; filename=fromHeader.txt;\r\n\r\n"
+ "Hello world!"_s);
});
auto webView = adoptNS([TestWKWebView new]);
[webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h (294491 => 294492)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2022-05-19 17:13:30 UTC (rev 294491)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2022-05-19 17:40:17 UTC (rev 294492)
@@ -26,6 +26,7 @@
#pragma once
#import <Network/Network.h>
+#import <experimental/coroutine>
#import <wtf/CompletionHandler.h>
#import <wtf/Forward.h>
#import <wtf/HashMap.h>
@@ -39,6 +40,35 @@
class Connection;
struct HTTPResponse;
+template<typename PromiseType>
+struct CoroutineHandle {
+ CoroutineHandle(std::experimental::coroutine_handle<PromiseType>&& handle)
+ : handle(WTFMove(handle)) { }
+ CoroutineHandle(const CoroutineHandle&) = delete;
+ CoroutineHandle(CoroutineHandle&& other)
+ : handle(std::exchange(other.handle, nullptr)) { }
+ ~CoroutineHandle()
+ {
+ if (handle)
+ handle.destroy();
+ }
+ std::experimental::coroutine_handle<PromiseType> handle;
+};
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() { return { std::experimental::coroutine_handle<promise_type>::from_promise(*this) }; }
+ std::experimental::suspend_never initial_suspend() { return { }; }
+ std::experimental::suspend_never final_suspend() noexcept { return { }; }
+ void unhandled_exception() { }
+ void return_void() { }
+ };
+ CoroutineHandle<promise_type> handle;
+};
+
+class ReceiveOperation;
+class SendOperation;
+
class HTTPServer {
WTF_MAKE_FAST_ALLOCATED;
public:
@@ -48,6 +78,8 @@
HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>>, Protocol = Protocol::Http, CertificateVerifier&& = nullptr, RetainPtr<SecIdentityRef>&& = nullptr, std::optional<uint16_t> port = { });
HTTPServer(Function<void(Connection)>&&, Protocol = Protocol::Http);
+ enum class UseCoroutines : bool { Yes };
+ HTTPServer(UseCoroutines, Function<Task(Connection)>&&, Protocol = Protocol::Http);
~HTTPServer();
uint16_t port() const;
String origin() const;
@@ -80,9 +112,12 @@
public:
void send(String&&, CompletionHandler<void()>&& = nullptr) const;
void send(Vector<uint8_t>&&, CompletionHandler<void()>&& = nullptr) const;
+ void send(RetainPtr<dispatch_data_t>&&, CompletionHandler<void(bool)>&& = nullptr) const;
+ SendOperation awaitableSend(String&&);
void sendAndReportError(Vector<uint8_t>&&, CompletionHandler<void(bool)>&&) const;
void receiveBytes(CompletionHandler<void(Vector<uint8_t>&&)>&&, size_t minimumSize = 1) const;
void receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&&, Vector<char>&& buffer = { }) const;
+ ReceiveOperation awaitableReceiveHTTPRequest() const;
void webSocketHandshake(CompletionHandler<void()>&& = { });
void terminate();
void cancel();
@@ -92,11 +127,33 @@
Connection(nw_connection_t connection)
: m_connection(connection) { }
- void send(RetainPtr<dispatch_data_t>&&, CompletionHandler<void(bool)>&&) const;
-
RetainPtr<nw_connection_t> m_connection;
};
+class ReceiveOperation : public std::experimental::suspend_never {
+public:
+ ReceiveOperation(const Connection& connection)
+ : m_connection(connection) { }
+ bool await_ready() { return false; }
+ void await_suspend(std::experimental::coroutine_handle<>);
+ Vector<char> await_resume() { return WTFMove(m_result); }
+private:
+ Connection m_connection;
+ Vector<char> m_result;
+};
+
+class SendOperation : public std::experimental::suspend_never {
+public:
+ SendOperation(RetainPtr<dispatch_data_t>&& data, const Connection& connection)
+ : m_data(WTFMove(data))
+ , m_connection(connection) { }
+ bool await_ready() { return false; }
+ void await_suspend(std::experimental::coroutine_handle<>);
+private:
+ RetainPtr<dispatch_data_t> m_data;
+ Connection m_connection;
+};
+
struct HTTPResponse {
enum class TerminateConnection { No, Yes };
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm (294491 => 294492)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2022-05-19 17:13:30 UTC (rev 294491)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2022-05-19 17:40:17 UTC (rev 294492)
@@ -46,10 +46,11 @@
map.add(pair.first, pair.second);
return map;
}(responses)) { }
-
+
size_t requestCount { 0 };
HashMap<String, HTTPResponse> requestMap;
Vector<Connection> connections;
+ Vector<CoroutineHandle<Task::promise_type>> coroutineHandles;
};
static RetainPtr<nw_protocol_definition_t> proxyDefinition(HTTPServer::Protocol protocol)
@@ -214,6 +215,21 @@
startListening(m_listener.get());
}
+HTTPServer::HTTPServer(UseCoroutines, WTF::Function<Task(Connection)>&& connectionHandler, Protocol protocol)
+ : m_requestData(adoptRef(*new RequestData({ })))
+ , m_listener(adoptNS(nw_listener_create(listenerParameters(protocol, nullptr, nullptr, { }).get())))
+ , m_protocol(protocol)
+{
+ nw_listener_set_queue(m_listener.get(), dispatch_get_main_queue());
+ nw_listener_set_new_connection_handler(m_listener.get(), makeBlockPtr([requestData = m_requestData, connectionHandler = WTFMove(connectionHandler)] (nw_connection_t connection) {
+ requestData->connections.append(Connection(connection));
+ nw_connection_set_queue(connection, dispatch_get_main_queue());
+ nw_connection_start(connection);
+ requestData->coroutineHandles.append(connectionHandler(Connection(connection)).handle);
+ }).get());
+ startListening(m_listener.get());
+}
+
HTTPServer::~HTTPServer() = default;
void HTTPServer::addResponse(String&& path, HTTPResponse&& response)
@@ -423,6 +439,31 @@
});
}
+ReceiveOperation Connection::awaitableReceiveHTTPRequest() const
+{
+ return { *this };
+}
+
+void ReceiveOperation::await_suspend(std::experimental::coroutine_handle<> handle)
+{
+ m_connection.receiveHTTPRequest([this, handle](Vector<char>&& result) mutable {
+ m_result = WTFMove(result);
+ handle();
+ });
+}
+
+void SendOperation::await_suspend(std::experimental::coroutine_handle<> handle)
+{
+ m_connection.send(WTFMove(m_data), [handle] (bool) mutable {
+ handle();
+ });
+}
+
+SendOperation Connection::awaitableSend(String&& message)
+{
+ return { dataFromString(WTFMove(message)), *this };
+}
+
void Connection::send(String&& message, CompletionHandler<void()>&& completionHandler) const
{
send(dataFromString(WTFMove(message)), [completionHandler = WTFMove(completionHandler)] (bool) mutable {