Title: [260518] trunk/Tools
Revision
260518
Author
[email protected]
Date
2020-04-22 09:56:00 -0700 (Wed, 22 Apr 2020)

Log Message

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:]):

Modified Paths

Added Paths

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)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to