Modified: trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h (220582 => 220583)
--- trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2017-08-11 10:06:12 UTC (rev 220583)
@@ -22,9 +22,9 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AuthenticationChallenge_h
-#define AuthenticationChallenge_h
+#pragma once
+
#include "AuthenticationChallengeBase.h"
#include "AuthenticationClient.h"
@@ -33,7 +33,7 @@
namespace WebCore {
-class AuthenticationChallenge : public AuthenticationChallengeBase {
+class AuthenticationChallenge final : public AuthenticationChallengeBase {
public:
AuthenticationChallenge()
{
@@ -46,6 +46,7 @@
AuthenticationChallenge(SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient* = nullptr);
AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); }
+ SoupMessage* soupMessage() const { return m_soupMessage.get(); }
SoupAuth* soupAuth() const { return m_soupAuth.get(); }
void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
@@ -53,10 +54,10 @@
friend class AuthenticationChallengeBase;
static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&);
+ GRefPtr<SoupMessage> m_soupMessage;
GRefPtr<SoupAuth> m_soupAuth;
RefPtr<AuthenticationClient> m_authenticationClient;
};
-}
+} // namespace WebCore
-#endif
Modified: trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp (220582 => 220583)
--- trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp 2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp 2017-08-11 10:06:12 UTC (rev 220583)
@@ -445,7 +445,12 @@
void NetworkDataTaskSoup::authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
{
ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
- if (soupMessage != task->m_soupMessage.get())
+
+ // We don't return early here in case the given soupMessage is different to m_soupMessage when
+ // it's proxy authentication and the request URL is HTTPS, because in that case libsoup uses a
+ // tunnel internally and the SoupMessage used for the authentication is the tunneling one.
+ // See https://bugs.webkit.org/show_bug.cgi?id=175378.
+ if (soupMessage != task->m_soupMessage.get() && (soupMessage->status_code != SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || !task->m_currentRequest.url().protocolIs("https")))
return;
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
@@ -487,7 +492,7 @@
}
}
- soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+ soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
// We could also do this before we even start the request, but that would be at the expense
// of all request latency, versus a one-time latency for the small subset of requests that
@@ -541,7 +546,7 @@
soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
}
- soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+ soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), challenge.soupMessage());
});
}
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp (220582 => 220583)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp 2017-08-11 08:15:40 UTC (rev 220582)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestAuthentication.cpp 2017-08-11 10:06:12 UTC (rev 220583)
@@ -252,14 +252,74 @@
g_assert_cmpstr(webkit_web_view_get_title(test->m_webView), ==, authExpectedSuccessTitle);
}
-static void serverCallback(SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, void*)
+class Tunnel {
+public:
+ Tunnel(SoupServer* server, SoupMessage* message)
+ : m_server(server)
+ , m_message(message)
+ {
+ soup_server_pause_message(m_server.get(), m_message.get());
+ }
+
+ ~Tunnel()
+ {
+ soup_server_unpause_message(m_server.get(), m_message.get());
+ }
+
+ void connect(Function<void (const char*)>&& completionHandler)
+ {
+ m_completionHandler = WTFMove(completionHandler);
+ GRefPtr<GSocketClient> client = adoptGRef(g_socket_client_new());
+ auto* uri = soup_message_get_uri(m_message.get());
+ g_socket_client_connect_to_host_async(client.get(), uri->host, uri->port, nullptr, [](GObject* source, GAsyncResult* result, gpointer userData) {
+ auto* tunnel = static_cast<Tunnel*>(userData);
+ GUniqueOutPtr<GError> error;
+ GRefPtr<GSocketConnection> connection = adoptGRef(g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), result, &error.outPtr()));
+ tunnel->connected(!connection ? error->message : nullptr);
+ }, this);
+ }
+
+ void connected(const char* errorMessage)
+ {
+ auto completionHandler = std::exchange(m_completionHandler, nullptr);
+ completionHandler(errorMessage);
+ }
+
+ GRefPtr<SoupServer> m_server;
+ GRefPtr<SoupMessage> m_message;
+ Function<void (const char*)> m_completionHandler;
+};
+
+unsigned gProxyServerPort;
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext* context, void*)
{
+ if (message->method == SOUP_METHOD_CONNECT) {
+ g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
+ auto tunnel = std::make_unique<Tunnel>(server, message);
+ auto* tunnelPtr = tunnel.get();
+ tunnelPtr->connect([tunnel = WTFMove(tunnel)](const char* errorMessage) {
+ if (errorMessage) {
+ soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_BAD_GATEWAY);
+ soup_message_set_response(tunnel->m_message.get(), "text/plain", SOUP_MEMORY_COPY, errorMessage, strlen(errorMessage));
+ } else {
+ soup_message_headers_append(tunnel->m_message->response_headers, "Proxy-Authenticate", "Basic realm=\"Proxy realm\"");
+ soup_message_set_status(tunnel->m_message.get(), SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED);
+ }
+ });
+ return;
+ }
+
if (message->method != SOUP_METHOD_GET) {
soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
return;
}
- if (!strcmp(path, "/auth-test.html") || !strcmp(path, "/empty-realm.html")) {
+ if (g_str_has_suffix(path, "/auth-test.html") || g_str_has_suffix(path, "/empty-realm.html")) {
+ bool isProxy = g_str_has_prefix(path, "/proxy");
+ if (isProxy)
+ g_assert_cmpuint(soup_server_get_port(server), ==, gProxyServerPort);
+
const char* authorization = soup_message_headers_get_one(message->request_headers, "Authorization");
// Require authentication.
if (!g_strcmp0(authorization, authExpectedAuthorization)) {
@@ -269,11 +329,11 @@
AuthenticationTest::authenticationRetries = 0;
} else if (++AuthenticationTest::authenticationRetries < 3) {
// No or invalid authorization header provided by the client, request authentication twice then fail.
- soup_message_set_status(message, SOUP_STATUS_UNAUTHORIZED);
+ soup_message_set_status(message, isProxy ? SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED : SOUP_STATUS_UNAUTHORIZED);
if (!strcmp(path, "/empty-realm.html"))
soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic");
else
- soup_message_headers_append(message->response_headers, "WWW-Authenticate", "Basic realm=\"my realm\"");
+ soup_message_headers_append(message->response_headers, isProxy ? "Proxy-Authenticate" : "WWW-Authenticate", isProxy ? "Basic realm=\"Proxy realm\"" : "Basic realm=\"my realm\"");
// Include a failure message in case the user attempts to proceed without authentication.
soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, authFailureHTMLString, strlen(authFailureHTMLString));
} else {
@@ -287,6 +347,64 @@
soup_message_body_complete(message->response_body);
}
+class ProxyAuthenticationTest : public AuthenticationTest {
+public:
+ MAKE_GLIB_TEST_FIXTURE(ProxyAuthenticationTest);
+
+ ProxyAuthenticationTest()
+ {
+ m_proxyServer.run(serverCallback);
+ g_assert(m_proxyServer.baseURI());
+ gProxyServerPort = soup_uri_get_port(m_proxyServer.baseURI());
+ GUniquePtr<char> proxyURI(soup_uri_to_string(m_proxyServer.baseURI(), FALSE));
+ WebKitNetworkProxySettings* settings = webkit_network_proxy_settings_new(proxyURI.get(), nullptr);
+ webkit_web_context_set_network_proxy_settings(m_webContext.get(), WEBKIT_NETWORK_PROXY_MODE_CUSTOM, settings);
+ webkit_network_proxy_settings_free(settings);
+ }
+
+ ~ProxyAuthenticationTest()
+ {
+ gProxyServerPort = 0;
+ }
+
+ GUniquePtr<char> proxyServerPortAsString()
+ {
+ GUniquePtr<char> port(g_strdup_printf("%u", soup_uri_get_port(m_proxyServer.baseURI())));
+ return port;
+ }
+
+ WebKitTestServer m_proxyServer;
+};
+
+static void testWebViewAuthenticationProxy(ProxyAuthenticationTest* test, gconstpointer)
+{
+ test->loadURI(kServer->getURIForPath("/proxy/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ // FIXME: the uri and host should the proxy ones, not the requested ones.
+ g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(kServer->baseURI()));
+ g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(kServer->baseURI()));
+ g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
+ g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
+ g_assert(webkit_authentication_request_is_for_proxy(request));
+ g_assert(!webkit_authentication_request_is_retry(request));
+}
+
+static void testWebViewAuthenticationProxyHTTPS(ProxyAuthenticationTest* test, gconstpointer)
+{
+ auto httpsServer = std::make_unique<WebKitTestServer>(WebKitTestServer::ServerHTTPS);
+ httpsServer->run(serverCallback);
+
+ test->loadURI(httpsServer->getURIForPath("/proxy/auth-test.html").data());
+ WebKitAuthenticationRequest* request = test->waitForAuthenticationRequest();
+ // FIXME: the uri and host should the proxy ones, not the requested ones.
+ g_assert_cmpstr(webkit_authentication_request_get_host(request), ==, soup_uri_get_host(httpsServer->baseURI()));
+ g_assert_cmpuint(webkit_authentication_request_get_port(request), ==, soup_uri_get_port(httpsServer->baseURI()));
+ g_assert_cmpstr(webkit_authentication_request_get_realm(request), ==, "Proxy realm");
+ g_assert(webkit_authentication_request_get_scheme(request) == WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC);
+ g_assert(webkit_authentication_request_is_for_proxy(request));
+ g_assert(!webkit_authentication_request_is_retry(request));
+}
+
void beforeAll()
{
kServer = new WebKitTestServer();
@@ -300,6 +418,8 @@
AuthenticationTest::add("WebKitWebView", "authentication-no-credential", testWebViewAuthenticationNoCredential);
AuthenticationTest::add("WebKitWebView", "authentication-storage", testWebViewAuthenticationStorage);
AuthenticationTest::add("WebKitWebView", "authentication-empty-realm", testWebViewAuthenticationEmptyRealm);
+ ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy", testWebViewAuthenticationProxy);
+ ProxyAuthenticationTest::add("WebKitWebView", "authentication-proxy-https", testWebViewAuthenticationProxyHTTPS);
}
void afterAll()