[Mac] Add an --allowed-host argument to DRT and WKTR to allow tests to connect to...
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 18:12:56 +0000 (18:12 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Mar 2015 18:12:56 +0000 (18:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142931

Reviewed by Brent Fulgham.

Currently, both DRT and WKTR will refuse to allow network connections to non-localhost servers
over HTTP/HTTPS. For certain testing scenarios, however, it would be useful if both DRT and
WKTR could be allowed to make HTTP/HTTPS connections to certain, specific servers defined at
runtime.

To allow this, add an optional argument to DRT and WKTR, --allowed-host, which will add the specified
hostname to a whitelist; requests to these hosts will allowed to proceed normally.

Drive-by fix: in InjectedBundlePage::willSendRequestForFrame, we get the top loading frame from the
injected bundle. But after the main resource load completes, the bundle nulls out it's pointer to the
top loading frame, which causes a subsequent crash when further resources are requested. Instead, get
the top loading frame from the page, as we do elsewhere in this class.

* DumpRenderTree/TestRunner.h:
(TestRunner::allowedHosts):
(TestRunner::setAllowedHosts):
* DumpRenderTree/mac/DumpRenderTree.mm:
(initializeGlobalsFromCommandLineOptions):
(runTest):
* DumpRenderTree/mac/ResourceLoadDelegate.mm:
(isAllowedHost):
(-[ResourceLoadDelegate webView:resource:willSendRequest:redirectResponse:fromDataSource:]):
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::didReceiveMessage):
(WTR::InjectedBundle::isAllowedHost):
* WebKitTestRunner/InjectedBundle/InjectedBundle.h:
* WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp:
(WTR::isAllowedHost):
(WTR::InjectedBundlePage::willSendRequestForFrame):
* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/Options.cpp:
(WTR::handleOptionAllowedHost):
(WTR::OptionsHandler::OptionsHandler):
* WebKitTestRunner/Options.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::initialize):
(WTR::TestController::resetStateToConsistentValues):
* WebKitTestRunner/TestController.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182017 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Tools/ChangeLog
Tools/DumpRenderTree/TestRunner.h
Tools/DumpRenderTree/mac/DumpRenderTree.mm
Tools/DumpRenderTree/mac/ResourceLoadDelegate.mm
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h
Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/Options.cpp
Tools/WebKitTestRunner/Options.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h

index 3c5c9af..2b86951 100644 (file)
@@ -1,3 +1,49 @@
+2015-03-26  Jer Noble  <jer.noble@apple.com>
+
+        [Mac] Add an --allowed-host argument to DRT and WKTR to allow tests to connect to non-localhost servers
+        https://bugs.webkit.org/show_bug.cgi?id=142931
+
+        Reviewed by Brent Fulgham.
+
+        Currently, both DRT and WKTR will refuse to allow network connections to non-localhost servers
+        over HTTP/HTTPS. For certain testing scenarios, however, it would be useful if both DRT and
+        WKTR could be allowed to make HTTP/HTTPS connections to certain, specific servers defined at
+        runtime.
+
+        To allow this, add an optional argument to DRT and WKTR, --allowed-host, which will add the specified
+        hostname to a whitelist; requests to these hosts will allowed to proceed normally.
+
+        Drive-by fix: in InjectedBundlePage::willSendRequestForFrame, we get the top loading frame from the
+        injected bundle. But after the main resource load completes, the bundle nulls out it's pointer to the
+        top loading frame, which causes a subsequent crash when further resources are requested. Instead, get
+        the top loading frame from the page, as we do elsewhere in this class.
+
+        * DumpRenderTree/TestRunner.h:
+        (TestRunner::allowedHosts):
+        (TestRunner::setAllowedHosts):
+        * DumpRenderTree/mac/DumpRenderTree.mm:
+        (initializeGlobalsFromCommandLineOptions):
+        (runTest):
+        * DumpRenderTree/mac/ResourceLoadDelegate.mm:
+        (isAllowedHost):
+        (-[ResourceLoadDelegate webView:resource:willSendRequest:redirectResponse:fromDataSource:]):
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::InjectedBundle::didReceiveMessage):
+        (WTR::InjectedBundle::isAllowedHost):
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.h:
+        * WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp:
+        (WTR::isAllowedHost):
+        (WTR::InjectedBundlePage::willSendRequestForFrame):
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/Options.cpp:
+        (WTR::handleOptionAllowedHost):
+        (WTR::OptionsHandler::OptionsHandler):
+        * WebKitTestRunner/Options.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::initialize):
+        (WTR::TestController::resetStateToConsistentValues):
+        * WebKitTestRunner/TestController.h:
+
 2015-03-26  Brady Eidson  <beidson@apple.com>
 
         Apply ContentExtension actions after redirects.
index f663ffb..cdefcb0 100644 (file)
@@ -52,6 +52,8 @@ public:
     void makeWindowObject(JSContextRef, JSObjectRef windowObject, JSValueRef* exception);
 
     void addDisallowedURL(JSStringRef url);
+    const std::set<std::string>& allowedHosts() const { return m_allowedHosts; }
+    void setAllowedHosts(std::set<std::string> hosts) { m_allowedHosts = WTF::move(hosts); }
     void addURLToRedirect(std::string origin, std::string destination);
     const std::string& redirectionDestinationForURL(std::string);
     void clearAllApplicationCaches();
@@ -418,11 +420,12 @@ private:
     std::string m_titleTextDirection;
 
     std::set<std::string> m_willSendRequestClearHeaders;
+    std::set<std::string> m_allowedHosts;
 
     std::vector<char> m_audioResult;
 
     std::map<std::string, std::string> m_URLsToRedirect;
-    
+
     static JSClassRef getJSClass();
     static JSStaticValue* staticValues();
     static JSStaticFunction* staticFunctions();
index e22e3a7..937fb5a 100644 (file)
@@ -202,6 +202,7 @@ static int useAcceleratedDrawing;
 static int gcBetweenTests;
 static BOOL printSeparators;
 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
+static std::set<std::string> allowedHosts;
 
 static WebHistoryItem *prevTestBFItem = nil;  // current b/f item at the end of the previous test
 
@@ -1080,6 +1081,7 @@ static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[]
         {"accelerated-drawing", no_argument, &useAcceleratedDrawing, YES},
         {"gc-between-tests", no_argument, &gcBetweenTests, YES},
         {"no-timeout", no_argument, &useTimeoutWatchdog, NO},
+        {"allowed-host", required_argument, nullptr, 'a'},
         {nullptr, 0, nullptr, 0}
     };
     
@@ -1090,6 +1092,9 @@ static void initializeGlobalsFromCommandLineOptions(int argc, const char *argv[]
             case ':':   // missing argument
                 exit(1);
                 break;
+            case 'a': // "allowed-host"
+                allowedHosts.insert(optarg);
+                break;
         }
     }
 }
@@ -1880,6 +1885,7 @@ static void runTest(const string& inputLine)
 #endif
 
     gTestRunner = TestRunner::create(testURL, command.expectedPixelHash);
+    gTestRunner->setAllowedHosts(allowedHosts);
     gTestRunner->setCustomTimeout(command.timeout);
     topLoadingFrame = nil;
 #if !PLATFORM(IOS)
index 9dd2b40..3f1a049 100644 (file)
@@ -137,6 +137,11 @@ BOOL hostIsUsedBySomeTestsToGenerateError(NSString *host)
     return NSOrderedSame == [host compare:@"255.255.255.255"];
 }
 
+BOOL isAllowedHost(NSString *host)
+{
+    return gTestRunner->allowedHosts().count(host.UTF8String);
+}
+
 -(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource
 {
     if (!done && gTestRunner->dumpResourceLoadCallbacks()) {
@@ -165,7 +170,7 @@ BOOL hostIsUsedBySomeTestsToGenerateError(NSString *host)
         NSString *testHost = 0;
         if ([lowercaseTestURL hasPrefix:@"http:"] || [lowercaseTestURL hasPrefix:@"https:"])
             testHost = [[NSURL URLWithString:testURL] host];
-        if (!isLocalhost(host) && !hostIsUsedBySomeTestsToGenerateError(host) && (!testHost || isLocalhost(testHost))) {
+        if (!isLocalhost(host) && !hostIsUsedBySomeTestsToGenerateError(host) && !isAllowedHost(host) && (!testHost || isLocalhost(testHost))) {
             printf("Blocked access to external URL %s\n", [[url absoluteString] cStringUsingEncoding:NSUTF8StringEncoding]);
             return nil;
         }
index d57081b..8cfccf0 100644 (file)
@@ -166,6 +166,17 @@ void InjectedBundle::didReceiveMessage(WKStringRef messageName, WKTypeRef messag
         if (shouldGC)
             WKBundleGarbageCollectJavaScriptObjects(m_bundle);
 
+        WKRetainPtr<WKStringRef> allowedHostsKey(AdoptWK, WKStringCreateWithUTF8CString("AllowedHosts"));
+        WKTypeRef allowedHostsValue = WKDictionaryGetItemForKey(messageBodyDictionary, allowedHostsKey.get());
+        if (allowedHostsValue && WKGetTypeID(allowedHostsValue) == WKArrayGetTypeID()) {
+            WKArrayRef allowedHostsArray = static_cast<WKArrayRef>(allowedHostsValue);
+            for (size_t i = 0, size = WKArrayGetSize(allowedHostsArray); i < size; ++i) {
+                WKTypeRef item = WKArrayGetItemAtIndex(allowedHostsArray, i);
+                if (item && WKGetTypeID(item) == WKStringGetTypeID())
+                    m_allowedHosts.append(toWTFString(static_cast<WKStringRef>(item)));
+            }
+        }
+
         m_state = Idle;
         m_dumpPixels = false;
 
@@ -599,4 +610,11 @@ void InjectedBundle::queueNonLoadingScript(WKStringRef script)
     WKBundlePostMessage(m_bundle, messageName.get(), script);
 }
 
+bool InjectedBundle::isAllowedHost(WKStringRef host)
+{
+    if (m_allowedHosts.isEmpty())
+        return false;
+    return m_allowedHosts.contains(toWTFString(host));
+}
+
 } // namespace WTR
index d7d4bad..281918a 100644 (file)
@@ -115,6 +115,8 @@ public:
     void queueLoadingScript(WKStringRef script);
     void queueNonLoadingScript(WKStringRef script);
 
+    bool isAllowedHost(WKStringRef);
+
 private:
     InjectedBundle();
     ~InjectedBundle();
@@ -165,6 +167,8 @@ private:
     WKRetainPtr<WKDataRef> m_audioResult;
     WKRetainPtr<WKImageRef> m_pixelResult;
     WKRetainPtr<WKArrayRef> m_repaintRects;
+
+    Vector<String> m_allowedHosts;
 };
 
 } // namespace WTR
index 6aa7948..b8b826e 100644 (file)
@@ -1077,6 +1077,11 @@ static inline bool isHTTPOrHTTPSScheme(WKStringRef scheme)
     return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "https");
 }
 
+static inline bool isAllowedHost(WKStringRef host)
+{
+    return InjectedBundle::singleton().isAllowedHost(host);
+}
+
 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef response)
 {
     auto& injectedBundle = InjectedBundle::singleton();
@@ -1111,7 +1116,7 @@ WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page
         && !isLocalHost(host.get())) {
         bool mainFrameIsExternal = false;
         if (injectedBundle.isTestRunning()) {
-            WKBundleFrameRef mainFrame = injectedBundle.topLoadingFrame();
+            WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(m_page);
             WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
             if (!mainFrameURL || WKStringIsEqualToUTF8CString(adoptWK(WKURLCopyString(mainFrameURL.get())).get(), "about:blank"))
                 mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
@@ -1120,7 +1125,7 @@ WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page
             WKRetainPtr<WKStringRef> mainFrameScheme = adoptWK(WKURLCopyScheme(mainFrameURL.get()));
             mainFrameIsExternal = isHTTPOrHTTPSScheme(mainFrameScheme.get()) && !isLocalHost(mainFrameHost.get());
         }
-        if (!mainFrameIsExternal) {
+        if (!mainFrameIsExternal && !isAllowedHost(host.get())) {
             StringBuilder stringBuilder;
             stringBuilder.appendLiteral("Blocked access to external URL ");
             stringBuilder.append(toWTFString(urlString));
index c5a8fa7..a2c7f9d 100644 (file)
@@ -331,6 +331,8 @@ private:
     bool m_userStyleSheetEnabled;
     WKRetainPtr<WKStringRef> m_userStyleSheetLocation;
 
+    WKRetainPtr<WKArrayRef> m_allowedHosts;
+
     PlatformTimerRef m_waitToDumpWatchdogTimer;
 };
 
index be3e237..286cd45 100644 (file)
@@ -99,6 +99,12 @@ bool handleOptionRemoteLayerTree(Options& options, const char*, const char*)
     return true;
 }
 
+bool handleOptionAllowedHost(Options& options, const char*, const char* host)
+{
+    options.allowedHosts.push_back(host);
+    return true;
+}
+
 bool handleOptionUnmatched(Options& options, const char* option, const char*)
 {
     if (option[0] && option[1] && option[0] == '-' && option[1] == '-')
@@ -120,6 +126,8 @@ OptionsHandler::OptionsHandler(Options& o)
     optionList.append(Option("--complex-text", "Force complex tests.", handleOptionComplexText));
     optionList.append(Option("--accelerated-drawing", "Use accelerated drawing.", handleOptionAcceleratedDrawing));
     optionList.append(Option("--remote-layer-tree", "Use remote layer tree.", handleOptionRemoteLayerTree));
+    optionList.append(Option("--allowed-host", "Allows access to the specified host from tests.", handleOptionAllowedHost, true));
+
     optionList.append(Option(0, 0, handleOptionUnmatched));
 }
 
index 5ba6aad..ef09630 100644 (file)
@@ -47,6 +47,7 @@ struct Options {
     bool shouldUseAcceleratedDrawing;
     bool shouldUseRemoteLayerTree;
     std::vector<std::string> paths;
+    std::vector<std::string> allowedHosts;
 };
 
 class Option {
index 938c2b7..f943d28 100644 (file)
@@ -325,6 +325,7 @@ void TestController::initialize(int argc, const char* argv[])
     m_shouldUseAcceleratedDrawing = options.shouldUseAcceleratedDrawing;
     m_shouldUseRemoteLayerTree = options.shouldUseRemoteLayerTree;
     m_paths = options.paths;
+    m_allowedHosts = options.allowedHosts;
 
     if (options.printSupportedFeatures) {
         // FIXME: On Windows, DumpRenderTree uses this to expose whether it supports 3d
@@ -612,6 +613,14 @@ bool TestController::resetStateToConsistentValues()
     WKRetainPtr<WKBooleanRef> shouldGCValue = adoptWK(WKBooleanCreate(m_gcBetweenTests));
     WKDictionarySetItem(resetMessageBody.get(), shouldGCKey.get(), shouldGCValue.get());
 
+    WKRetainPtr<WKStringRef> allowedHostsKey = adoptWK(WKStringCreateWithUTF8CString("AllowedHosts"));
+    WKRetainPtr<WKMutableArrayRef> allowedHostsValue = adoptWK(WKMutableArrayCreate());
+    for (auto& host : m_allowedHosts) {
+        WKRetainPtr<WKStringRef> wkHost = adoptWK(WKStringCreateWithUTF8CString(host.c_str()));
+        WKArrayAppendItem(allowedHostsValue.get(), wkHost.get());
+    }
+    WKDictionarySetItem(resetMessageBody.get(), allowedHostsKey.get(), allowedHostsValue.get());
+
     WKContextPostMessageToInjectedBundle(TestController::singleton().context(), messageName.get(), resetMessageBody.get());
 
     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
index 0c362f0..5675aef 100644 (file)
@@ -214,6 +214,7 @@ private:
     bool m_gcBetweenTests;
     bool m_shouldDumpPixelsForAllTests;
     std::vector<std::string> m_paths;
+    std::vector<std::string> m_allowedHosts;
     WKRetainPtr<WKStringRef> m_injectedBundlePath;
     WKRetainPtr<WKStringRef> m_testPluginDirectory;