Diff
Modified: trunk/Tools/ChangeLog (260517 => 260518)
--- trunk/Tools/ChangeLog 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/ChangeLog 2020-04-22 16:56:00 UTC (rev 260518)
@@ -1,3 +1,46 @@
+2020-04-22 Alex Christensen <[email protected]>
+
+ Add unit test for resuming downloads
+ https://bugs.webkit.org/show_bug.cgi?id=210852
+
+ Reviewed by Brady Eidson.
+
+ This is a test that we should've written years ago.
+ I made HTTPServer able to take a Function that takes a nw_connection_t to give it more power than declarative request/response pairs.
+ I made TestDownloadDelegate to be reused by future tests, like those I'm going to add for bug 210313.
+
+ * TestWebKitAPI/SourcesCocoa.txt:
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebKitCocoa/Download.mm:
+ (longString):
+ (TEST):
+ * TestWebKitAPI/cocoa/HTTPServer.h:
+ (TestWebKitAPI::HTTPServer::totalRequests const): Deleted.
+ * TestWebKitAPI/cocoa/HTTPServer.mm:
+ (TestWebKitAPI::HTTPServer::RequestData::RequestData):
+ (TestWebKitAPI::HTTPServer::listenerParameters):
+ (TestWebKitAPI::startListening):
+ (TestWebKitAPI::HTTPServer::HTTPServer):
+ (TestWebKitAPI::HTTPServer::totalRequests const):
+ (TestWebKitAPI::dataFromString):
+ (TestWebKitAPI::nullTerminatedRequest):
+ (TestWebKitAPI::HTTPServer::respondToRequests):
+ * TestWebKitAPI/cocoa/TestDownloadDelegate.h: Added.
+ * TestWebKitAPI/cocoa/TestDownloadDelegate.mm: Added.
+ (-[TestDownloadDelegate _downloadDidStart:]):
+ (-[TestDownloadDelegate _download:didReceiveServerRedirectToURL:]):
+ (-[TestDownloadDelegate _download:didReceiveResponse:]):
+ (-[TestDownloadDelegate _download:didReceiveData:]):
+ (-[TestDownloadDelegate _download:decideDestinationWithSuggestedFilename:completionHandler:]):
+ (-[TestDownloadDelegate _downloadDidFinish:]):
+ (-[TestDownloadDelegate _download:didFailWithError:]):
+ (-[TestDownloadDelegate _downloadDidCancel:]):
+ (-[TestDownloadDelegate _download:didReceiveAuthenticationChallenge:completionHandler:]):
+ (-[TestDownloadDelegate _download:didCreateDestination:]):
+ * TestWebKitAPI/cocoa/TestNavigationDelegate.h:
+ * TestWebKitAPI/cocoa/TestNavigationDelegate.mm:
+ (-[TestNavigationDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]):
+
2020-04-22 Saam Barati <[email protected]>
makeValueRefForValue should be robust against the type encoding of a NSNumber backed by a boolean
Modified: trunk/Tools/TestWebKitAPI/SourcesCocoa.txt (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/SourcesCocoa.txt 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/SourcesCocoa.txt 2020-04-22 16:56:00 UTC (rev 260518)
@@ -26,6 +26,7 @@
cocoa/PlatformUtilitiesCocoa.mm
cocoa/TestCocoa.mm
+cocoa/TestDownloadDelegate.mm
cocoa/TestNavigationDelegate.mm
cocoa/TestProtocol.mm
cocoa/TestUIDelegate.mm
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-04-22 16:56:00 UTC (rev 260518)
@@ -2064,6 +2064,8 @@
5C69BDD41F82A7EB000F4F4B /* _javascript_DuringNavigation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _javascript_DuringNavigation.mm; sourceTree = "<group>"; };
5C6E27A6224EEBEA00128736 /* URLCanonicalization.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = URLCanonicalization.mm; sourceTree = "<group>"; };
5C7148942123A40700FDE3C5 /* WKWebsiteDatastore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebsiteDatastore.mm; sourceTree = "<group>"; };
+ 5C72E8CD244FFCE300381EB7 /* TestDownloadDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestDownloadDelegate.h; path = cocoa/TestDownloadDelegate.h; sourceTree = "<group>"; };
+ 5C72E8CE244FFCE400381EB7 /* TestDownloadDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestDownloadDelegate.mm; path = cocoa/TestDownloadDelegate.mm; sourceTree = "<group>"; };
5C73A81A2323059800DEA85A /* TLSDeprecation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TLSDeprecation.mm; sourceTree = "<group>"; };
5C75715F221249BD00B9E5AC /* BundleRetainPagePlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BundleRetainPagePlugIn.mm; sourceTree = "<group>"; };
5C79640F1EB0269B0075D74C /* EventModifiers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventModifiers.cpp; sourceTree = "<group>"; };
@@ -2919,6 +2921,8 @@
CE640CA62370A4F300C5CAA4 /* TestCocoa.mm */,
5CE7594822A883A500C12409 /* TestContextMenuDriver.h */,
5CE7594722A883A500C12409 /* TestContextMenuDriver.mm */,
+ 5C72E8CD244FFCE300381EB7 /* TestDownloadDelegate.h */,
+ 5C72E8CE244FFCE400381EB7 /* TestDownloadDelegate.mm */,
2D1C04A51D76298B000A6816 /* TestNavigationDelegate.h */,
2D1C04A61D76298B000A6816 /* TestNavigationDelegate.mm */,
516281232325C17A00BB7E42 /* TestPDFDocument.h */,
@@ -4881,6 +4885,7 @@
7CCE7EC01A411A7E00447C4C /* FragmentNavigation.mm in Sources */,
7CCE7EF61A411AE600447C4C /* FrameMIMETypeHTML.cpp in Sources */,
7CCE7EF71A411AE600447C4C /* FrameMIMETypePNG.cpp in Sources */,
+ CDCF78A8244A32F700480311 /* FullscreenAlert.mm in Sources */,
CD78E11D1DB7EA660014A2DE /* FullscreenDelegate.mm in Sources */,
CDBFCC451A9FF45300A7B691 /* FullscreenZoomInitialFrame.mm in Sources */,
83DB79691EF63B3C00BFA5E5 /* Function.cpp in Sources */,
@@ -5045,7 +5050,6 @@
7C83E0C11D0A652F00FEBCF3 /* ProvisionalURLNotChange.mm in Sources */,
5CFACF65226FD2DC0056C7D0 /* Proxy.mm in Sources */,
041A1E34216FFDBC00789E0A /* PublicSuffix.cpp in Sources */,
- CDCF78A8244A32F700480311 /* FullscreenAlert.mm in Sources */,
7C83E0C21D0A653500FEBCF3 /* QuickLook.mm in Sources */,
6B4E861C2220A5520022F389 /* RegistrableDomain.cpp in Sources */,
7CCE7F0D1A411AE600447C4C /* ReloadPageAfterCrash.cpp in Sources */,
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm 2020-04-22 16:56:00 UTC (rev 260518)
@@ -32,6 +32,8 @@
#import "PlatformUtilities.h"
#import "TCPServer.h"
#import "Test.h"
+#import "TestDownloadDelegate.h"
+#import "TestNavigationDelegate.h"
#import "TestProtocol.h"
#import "TestWKWebView.h"
#import <WebKit/WKErrorPrivate.h>
@@ -46,6 +48,7 @@
#import <WebKit/_WKDownloadDelegate.h>
#import <WebKit/_WKProcessPoolConfiguration.h>
#import <WebKit/_WKWebsiteDataStoreConfiguration.h>
+#import <wtf/BlockPtr.h>
#import <wtf/FileSystem.h>
#import <wtf/MainThread.h>
#import <wtf/MonotonicTime.h>
@@ -1157,6 +1160,135 @@
Util::run(&isDone);
}
+#if HAVE(NETWORK_FRAMEWORK)
+
+template<size_t length>
+String longString(LChar c)
+{
+ Vector<LChar> vector(length, c);
+ return String(vector.data(), length);
+}
+
+TEST(_WKDownload, Resume)
+{
+ using namespace TestWebKitAPI;
+ HTTPServer server([connectionCount = 0](nw_connection_t connection) mutable {
+ switch (++connectionCount) {
+ case 1:
+ nw_connection_receive(connection, 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([connection = retainPtr(connection)](dispatch_data_t content, nw_content_context_t context, bool, nw_error_t error) {
+ EXPECT_TRUE(content);
+ EXPECT_FALSE(error);
+ auto data = ""
+ "HTTP/1.1 200 OK\r\n"
+ "ETag: test\r\n"
+ "Content-Length: 10000\r\n"
+ "Content-Disposition: attachment; filename=\"example.txt\"\r\n"
+ "\r\n", longString<5000>('a')));
+ nw_connection_send(connection.get(), data.get(), context, false, ^(nw_error_t error) {
+ ASSERT(!error);
+ });
+ }).get());
+ break;
+ case 2:
+ nw_connection_receive(connection, 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([connection = retainPtr(connection)](dispatch_data_t content, nw_content_context_t context, bool, nw_error_t error) {
+ EXPECT_TRUE(content);
+ EXPECT_FALSE(error);
+
+ auto request = nullTerminatedRequest(content);
+ EXPECT_TRUE(strstr(request.data(), "Range: bytes=5000-\r\n"));
+
+ auto data = ""
+ "HTTP/1.1 206 Partial Content\r\n"
+ "ETag: test\r\n"
+ "Content-Length: 5000\r\n"
+ "Content-Range: bytes 5000-9999/10000\r\n"
+ "\r\n", longString<5000>('b')));
+ nw_connection_send(connection.get(), data.get(), context, true, ^(nw_error_t error) {
+ ASSERT(!error);
+ });
+ }).get());
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ });
+
+ NSURL *tempDir = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"DownloadResumeTest"] isDirectory:YES];
+ [[NSFileManager defaultManager] createDirectoryAtURL:tempDir withIntermediateDirectories:YES attributes:nil error:nil];
+ NSURL *expectedDownloadFile = [tempDir URLByAppendingPathComponent:@"example.txt"];
+ [[NSFileManager defaultManager] removeItemAtURL:expectedDownloadFile error:nil];
+
+ auto navigationDelegate = adoptNS([TestNavigationDelegate new]);
+ navigationDelegate.get().decidePolicyForNavigationResponse = ^(WKNavigationResponse *, void (^completionHandler)(WKNavigationResponsePolicy)) {
+ completionHandler(_WKNavigationResponsePolicyBecomeDownload);
+ };
+
+ enum class Callback : uint8_t { Start, ReceiveData, DecideDestination, CreateDestination, Cancel, Finish };
+ __block Vector<Callback> callbacks;
+ __block bool didCancel = false;
+ __block bool didFinish = false;
+ __block bool receivedData = false;
+ __block RetainPtr<_WKDownload> download;
+ __block RetainPtr<NSData> resumeData;
+
+ auto downloadDelegate = adoptNS([TestDownloadDelegate new]);
+ downloadDelegate.get().decideDestinationWithSuggestedFilename = ^(_WKDownload *, NSString *suggestedFilename, void (^completionHandler)(BOOL, NSString *)) {
+ callbacks.append(Callback::DecideDestination);
+ completionHandler(YES, [tempDir URLByAppendingPathComponent:suggestedFilename].path);
+ };
+ downloadDelegate.get().didReceiveData = ^(_WKDownload *download, uint64_t length) {
+ callbacks.append(Callback::ReceiveData);
+ EXPECT_EQ(length, 5000u);
+ receivedData = true;
+ };
+ downloadDelegate.get().downloadDidStart = ^(_WKDownload *downloadFromDelegate) {
+ callbacks.append(Callback::Start);
+ download = downloadFromDelegate;
+ };
+ downloadDelegate.get().didCreateDestination = ^(_WKDownload *, NSString *destination) {
+ callbacks.append(Callback::CreateDestination);
+ EXPECT_WK_STREQ(destination, [tempDir URLByAppendingPathComponent:@"example.txt"].path);
+ };
+ downloadDelegate.get().downloadDidCancel = ^(_WKDownload *download) {
+ callbacks.append(Callback::Cancel);
+ resumeData = download.resumeData;
+ didCancel = true;
+ };
+ downloadDelegate.get().downloadDidFinish = ^(_WKDownload *) {
+ callbacks.append(Callback::Finish);
+ didFinish = true;
+ };
+
+ auto webView = adoptNS([WKWebView new]);
+ [webView setNavigationDelegate:navigationDelegate.get()];
+ [webView configuration].processPool._downloadDelegate = downloadDelegate.get();
+ [webView loadRequest:server.request()];
+ Util::run(&receivedData);
+ [download cancel];
+ Util::run(&didCancel);
+
+ [[webView configuration].processPool _resumeDownloadFromData:resumeData.get() websiteDataStore:[WKWebsiteDataStore defaultDataStore] path:expectedDownloadFile.path originatingWebView:webView.get()];
+ Util::run(&didFinish);
+
+ EXPECT_EQ(callbacks.size(), 7u);
+ EXPECT_EQ(callbacks[0], Callback::Start);
+ EXPECT_EQ(callbacks[1], Callback::DecideDestination);
+ EXPECT_EQ(callbacks[2], Callback::CreateDestination);
+ EXPECT_EQ(callbacks[3], Callback::ReceiveData);
+ EXPECT_EQ(callbacks[4], Callback::Cancel);
+ EXPECT_EQ(callbacks[5], Callback::ReceiveData);
+ EXPECT_EQ(callbacks[6], Callback::Finish);
+
+ NSData *fileContents = [NSData dataWithContentsOfURL:expectedDownloadFile];
+ EXPECT_EQ(fileContents.length, 10000u);
+ for (size_t i = 0; i < 5000; i++)
+ EXPECT_EQ(static_cast<const char*>(fileContents.bytes)[i], 'a');
+ for (size_t i = 5000; i < 10000; i++)
+ EXPECT_EQ(static_cast<const char*>(fileContents.bytes)[i], 'b');
+}
+
+#endif // HAVE(NETWORK_FRAMEWORK)
+
@interface DownloadTestSchemeDelegate : NSObject <WKNavigationDelegate>
@end
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2020-04-22 16:56:00 UTC (rev 260518)
@@ -39,22 +39,29 @@
class HTTPServer {
public:
struct HTTPResponse;
+ struct RequestData;
enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS };
- HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>>, Protocol = Protocol::Http, Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)>&& = nullptr);
+ using CertificateVerifier = Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)>;
+
+ HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>>, Protocol = Protocol::Http, CertificateVerifier&& = nullptr);
+ HTTPServer(Function<void(nw_connection_t)>&&, Protocol = Protocol::Http);
+ ~HTTPServer();
uint16_t port() const;
NSURLRequest *request() const;
- size_t totalRequests() const { return m_totalRequests; }
-
+ size_t totalRequests() const;
+
private:
- void respondToRequests(nw_connection_t);
-
+ static RetainPtr<nw_parameters_t> listenerParameters(Protocol, CertificateVerifier&&);
+ static void respondToRequests(nw_connection_t, RefPtr<RequestData>);
+
+ RefPtr<RequestData> m_requestData;
RetainPtr<nw_listener_t> m_listener;
- const Protocol m_protocol;
- const Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)> m_certVerifier;
- const HashMap<String, HTTPResponse> m_requestResponseMap;
- size_t m_totalRequests { 0 };
+ Protocol m_protocol { Protocol::Http };
};
+RetainPtr<dispatch_data_t> dataFromString(String&&);
+Vector<char> nullTerminatedRequest(dispatch_data_t);
+
struct HTTPServer::HTTPResponse {
enum class TerminateConnection { No, Yes };
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2020-04-22 16:56:00 UTC (rev 260518)
@@ -37,17 +37,22 @@
namespace TestWebKitAPI {
-HTTPServer::HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>> responses, Protocol protocol, Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)>&& verify)
- : m_protocol(protocol)
- , m_certVerifier(WTFMove(verify))
- , m_requestResponseMap([](std::initializer_list<std::pair<String, HTTPServer::HTTPResponse>> list) {
+struct HTTPServer::RequestData : public RefCounted<RequestData> {
+ RequestData(std::initializer_list<std::pair<String, HTTPResponse>> responses)
+ : requestMap([](std::initializer_list<std::pair<String, HTTPServer::HTTPResponse>> list) {
HashMap<String, HTTPServer::HTTPResponse> map;
for (auto& pair : list)
map.add(pair.first, pair.second);
return map;
- }(responses))
+ }(responses)) { }
+
+ size_t requestCount { 0 };
+ const HashMap<String, HTTPResponse> requestMap;
+};
+
+RetainPtr<nw_parameters_t> HTTPServer::listenerParameters(Protocol protocol, CertificateVerifier&& verifier)
{
- auto configureTLS = protocol == Protocol::Http ? NW_PARAMETERS_DISABLE_PROTOCOL : ^(nw_protocol_options_t protocolOptions) {
+ auto configureTLS = protocol == Protocol::Http ? makeBlockPtr(NW_PARAMETERS_DISABLE_PROTOCOL) : makeBlockPtr([protocol, verifier = WTFMove(verifier)] (nw_protocol_options_t protocolOptions) mutable {
#if HAVE(TLS_PROTOCOL_VERSION_T)
auto options = adoptNS(nw_tls_copy_sec_protocol_options(protocolOptions));
auto identity = adoptNS(sec_identity_create(testIdentity().get()));
@@ -54,35 +59,68 @@
sec_protocol_options_set_local_identity(options.get(), identity.get());
if (protocol == Protocol::HttpsWithLegacyTLS)
sec_protocol_options_set_max_tls_protocol_version(options.get(), tls_protocol_version_TLSv10);
- if (m_certVerifier) {
+ if (verifier) {
sec_protocol_options_set_peer_authentication_required(options.get(), true);
- sec_protocol_options_set_verify_block(options.get(), ^(sec_protocol_metadata_t metadata, sec_trust_t trust, sec_protocol_verify_complete_t completion) {
- m_certVerifier(metadata, trust, completion);
- }, dispatch_get_main_queue());
+ sec_protocol_options_set_verify_block(options.get(), makeBlockPtr([verifier = WTFMove(verifier)](sec_protocol_metadata_t metadata, sec_trust_t trust, sec_protocol_verify_complete_t completion) {
+ verifier(metadata, trust, completion);
+ }).get(), dispatch_get_main_queue());
}
#else
UNUSED_PARAM(protocolOptions);
ASSERT_UNUSED(protocol, protocol != Protocol::HttpsWithLegacyTLS);
#endif
- };
- auto parameters = adoptNS(nw_parameters_create_secure_tcp(configureTLS, NW_PARAMETERS_DEFAULT_CONFIGURATION));
- m_listener = adoptNS(nw_listener_create(parameters.get()));
- nw_listener_set_queue(m_listener.get(), dispatch_get_main_queue());
- nw_listener_set_new_connection_handler(m_listener.get(), ^(nw_connection_t connection) {
- nw_connection_set_queue(connection, dispatch_get_main_queue());
- nw_connection_start(connection);
- respondToRequests(connection);
});
+ return adoptNS(nw_parameters_create_secure_tcp(configureTLS.get(), NW_PARAMETERS_DEFAULT_CONFIGURATION));
+}
+
+static void startListening(nw_listener_t listener)
+{
__block bool ready = false;
- nw_listener_set_state_changed_handler(m_listener.get(), ^(nw_listener_state_t state, nw_error_t error) {
+ nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) {
ASSERT_UNUSED(error, !error);
if (state == nw_listener_state_ready)
ready = true;
});
- nw_listener_start(m_listener.get());
+ nw_listener_start(listener);
Util::run(&ready);
}
+HTTPServer::HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>> responses, Protocol protocol, CertificateVerifier&& verifier)
+ : m_requestData(adoptRef(new RequestData(responses)))
+ , m_listener(adoptNS(nw_listener_create(listenerParameters(protocol, WTFMove(verifier)).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](nw_connection_t connection) {
+ nw_connection_set_queue(connection, dispatch_get_main_queue());
+ nw_connection_start(connection);
+ respondToRequests(connection, requestData);
+ }).get());
+ startListening(m_listener.get());
+}
+
+HTTPServer::HTTPServer(Function<void(nw_connection_t)>&& connectionHandler, Protocol protocol)
+ : m_listener(adoptNS(nw_listener_create(listenerParameters(protocol, 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([connectionHandler = WTFMove(connectionHandler)] (nw_connection_t connection) {
+ nw_connection_set_queue(connection, dispatch_get_main_queue());
+ nw_connection_start(connection);
+ connectionHandler(connection);
+ }).get());
+ startListening(m_listener.get());
+}
+
+HTTPServer::~HTTPServer() = default;
+
+size_t HTTPServer::totalRequests() const
+{
+ if (!m_requestData)
+ return 0;
+ return m_requestData->requestCount;
+}
+
static String statusText(unsigned statusCode)
{
switch (statusCode) {
@@ -97,19 +135,34 @@
return { };
}
-void HTTPServer::respondToRequests(nw_connection_t connection)
+RetainPtr<dispatch_data_t> dataFromString(String&& s)
{
- nw_connection_receive(connection, 1, std::numeric_limits<uint32_t>::max(), ^(dispatch_data_t content, nw_content_context_t context, bool complete, nw_error_t error) {
+ auto impl = s.releaseImpl();
+ ASSERT(impl->is8Bit());
+ return adoptNS(dispatch_data_create(impl->characters8(), impl->length(), dispatch_get_main_queue(), ^{
+ (void)impl;
+ }));
+}
+
+Vector<char> nullTerminatedRequest(dispatch_data_t content)
+{
+ __block Vector<char> request;
+ dispatch_data_apply(content, ^bool(dispatch_data_t, size_t, const void* buffer, size_t size) {
+ request.append(static_cast<const char*>(buffer), size);
+ return true;
+ });
+ request.append('\0');
+ return request;
+}
+
+void HTTPServer::respondToRequests(nw_connection_t connection, RefPtr<RequestData> requestData)
+{
+ nw_connection_receive(connection, 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([connection = retainPtr(connection), requestData](dispatch_data_t content, nw_content_context_t context, bool complete, nw_error_t error) {
if (error || !content)
return;
- __block Vector<char> request;
- dispatch_data_apply(content, ^bool(dispatch_data_t, size_t, const void* buffer, size_t size) {
- request.append(static_cast<const char*>(buffer), size);
- return true;
- });
- request.append('\0');
- m_totalRequests++;
+ requestData->requestCount++;
+ auto request = nullTerminatedRequest(content);
const char* getPathPrefix = "GET ";
const char* postPathPrefix = "POST ";
@@ -126,10 +179,10 @@
ASSERT_WITH_MESSAGE(pathPrefixLength, "HTTPServer assumes request is GET or POST");
size_t pathLength = pathEnd - request.data() - pathPrefixLength;
String path(request.data() + pathPrefixLength, pathLength);
- ASSERT_WITH_MESSAGE(m_requestResponseMap.contains(path), "This HTTPServer does not know how to respond to a request for %s", path.utf8().data());
+ ASSERT_WITH_MESSAGE(requestData->requestMap.contains(path), "This HTTPServer does not know how to respond to a request for %s", path.utf8().data());
- CompletionHandler<void()> sendResponse = [this, connection = retainPtr(connection), context = retainPtr(context), path = WTFMove(path)] {
- auto response = m_requestResponseMap.get(path);
+ CompletionHandler<void()> sendResponse = [connection, context = retainPtr(context), path = WTFMove(path), requestData] {
+ auto response = requestData->requestMap.get(path);
if (response.terminateConnection == HTTPResponse::TerminateConnection::Yes) {
nw_connection_cancel(connection.get());
return;
@@ -150,13 +203,9 @@
}
responseBuilder.append("\r\n");
responseBuilder.append(response.body);
- auto responseBodyAndHeader = responseBuilder.toString().releaseImpl();
- auto responseData = adoptNS(dispatch_data_create(responseBodyAndHeader->characters8(), responseBodyAndHeader->length(), dispatch_get_main_queue(), ^{
- (void)responseBodyAndHeader;
- }));
- nw_connection_send(connection.get(), responseData.get(), context.get(), true, ^(nw_error_t error) {
+ nw_connection_send(connection.get(), dataFromString(responseBuilder.toString()).get(), context.get(), true, ^(nw_error_t error) {
ASSERT(!error);
- respondToRequests(connection.get());
+ respondToRequests(connection.get(), requestData);
});
};
@@ -165,7 +214,7 @@
size_t headerLength = doubleNewline - request.data() + strlen("\r\n\r\n");
constexpr size_t nullTerminationLength = 1;
if (request.size() - nullTerminationLength - headerLength < contentLength) {
- nw_connection_receive(connection, 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([sendResponse = WTFMove(sendResponse)] (dispatch_data_t content, nw_content_context_t context, bool complete, nw_error_t error) mutable {
+ nw_connection_receive(connection.get(), 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([sendResponse = WTFMove(sendResponse)] (dispatch_data_t content, nw_content_context_t context, bool complete, nw_error_t error) mutable {
if (error || !content)
return;
sendResponse();
@@ -174,7 +223,7 @@
}
}
sendResponse();
- });
+ }).get());
}
uint16_t HTTPServer::port() const
Added: trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h (0 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h (rev 0)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h 2020-04-22 16:56:00 UTC (rev 260518)
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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 <WebKit/WebKit.h>
+#import <WebKit/_WKDownloadDelegate.h>
+
+@interface TestDownloadDelegate : NSObject<_WKDownloadDelegate>
+@property (nonatomic, copy) void (^downloadDidStart)(_WKDownload *);
+@property (nonatomic, copy) void (^didReceiveServerRedirectToURL)(_WKDownload *, NSURL *);
+@property (nonatomic, copy) void (^didReceiveResponse)(_WKDownload *, NSURLResponse *);
+@property (nonatomic, copy) void (^didReceiveData)(_WKDownload *, uint64_t);
+@property (nonatomic, copy) void (^decideDestinationWithSuggestedFilename)(_WKDownload *, NSString *, void (^)(BOOL, NSString *));
+@property (nonatomic, copy) void (^downloadDidFinish)(_WKDownload *);
+@property (nonatomic, copy) void (^didFailWithError)(_WKDownload *, NSError *);
+@property (nonatomic, copy) void (^downloadDidCancel)(_WKDownload *);
+@property (nonatomic, copy) void (^didReceiveAuthenticationChallenge)(_WKDownload *, NSURLAuthenticationChallenge *, void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential*));
+@property (nonatomic, copy) void (^didCreateDestination)(_WKDownload *, NSString *);
+@end
Added: trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm (0 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm 2020-04-22 16:56:00 UTC (rev 260518)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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 "TestDownloadDelegate.h"
+
+@implementation TestDownloadDelegate
+
+- (void)_downloadDidStart:(_WKDownload *)download
+{
+ if (_downloadDidStart)
+ _downloadDidStart(download);
+}
+
+- (void)_download:(_WKDownload *)download didReceiveServerRedirectToURL:(NSURL *)url
+{
+ if (_didReceiveServerRedirectToURL)
+ _didReceiveServerRedirectToURL(download, url);
+}
+
+- (void)_download:(_WKDownload *)download didReceiveResponse:(NSURLResponse *)response
+{
+ if (_didReceiveResponse)
+ _didReceiveResponse(download, response);
+}
+
+- (void)_download:(_WKDownload *)download didReceiveData:(uint64_t)length
+{
+ if (_didReceiveData)
+ _didReceiveData(download, length);
+}
+
+- (void)_download:(_WKDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename completionHandler:(void (^)(BOOL allowOverwrite, NSString *destination))completionHandler
+{
+ ASSERT(_decideDestinationWithSuggestedFilename);
+ _decideDestinationWithSuggestedFilename(download, filename, completionHandler);
+}
+
+- (void)_downloadDidFinish:(_WKDownload *)download
+{
+ if (_downloadDidFinish)
+ _downloadDidFinish(download);
+}
+
+- (void)_download:(_WKDownload *)download didFailWithError:(NSError *)error
+{
+ if (_didFailWithError)
+ _didFailWithError(download, error);
+}
+
+- (void)_downloadDidCancel:(_WKDownload *)download
+{
+ if (_downloadDidCancel)
+ _downloadDidCancel(download);
+}
+
+- (void)_download:(_WKDownload *)download didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential*))completionHandler
+{
+ if (_didReceiveAuthenticationChallenge)
+ _didReceiveAuthenticationChallenge(download, challenge, completionHandler);
+ else
+ completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+}
+
+- (void)_download:(_WKDownload *)download didCreateDestination:(NSString *)destination
+{
+ if (_didCreateDestination)
+ _didCreateDestination(download, destination);
+}
+
+@end
Modified: trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.h (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.h 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.h 2020-04-22 16:56:00 UTC (rev 260518)
@@ -30,6 +30,7 @@
@property (nonatomic, copy) void (^decidePolicyForNavigationAction)(WKNavigationAction *, void (^)(WKNavigationActionPolicy));
@property (nonatomic, copy) void (^decidePolicyForNavigationActionWithPreferences)(WKNavigationAction *, WKWebpagePreferences *, void (^)(WKNavigationActionPolicy, WKWebpagePreferences *));
+@property (nonatomic, copy) void (^decidePolicyForNavigationResponse)(WKNavigationResponse *, void (^)(WKNavigationResponsePolicy));
@property (nonatomic, copy) void (^didFailProvisionalNavigation)(WKWebView *, WKNavigation *, NSError *);
@property (nonatomic, copy) void (^didStartProvisionalNavigation)(WKWebView *, WKNavigation *);
@property (nonatomic, copy) void (^didCommitNavigation)(WKWebView *, WKNavigation *);
Modified: trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.mm (260517 => 260518)
--- trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.mm 2020-04-22 16:34:47 UTC (rev 260517)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestNavigationDelegate.mm 2020-04-22 16:56:00 UTC (rev 260518)
@@ -44,6 +44,14 @@
decisionHandler(WKNavigationActionPolicyAllow, preferences);
}
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+ if (_decidePolicyForNavigationResponse)
+ _decidePolicyForNavigationResponse(navigationResponse, decisionHandler);
+ else
+ decisionHandler(WKNavigationResponsePolicyAllow);
+}
+
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
if (_didStartProvisionalNavigation)