# HG changeset patch # User chegar # Date 1270558079 -3600 # Node ID 98b1778f0fd1b55713315388c58e6c65a68d09d0 # Parent 6e8fc7a85bf295bd9e76eee23b32c92d73cad944 6648001: Cancelling HTTP authentication causes subsequent deadlocks Reviewed-by: michaelm diff -r 6e8fc7a85bf2 -r 98b1778f0fd1 jdk/src/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java --- a/jdk/src/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java Mon Apr 05 16:11:59 2010 -0700 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/AuthenticationInfo.java Tue Apr 06 13:47:59 2010 +0100 @@ -270,13 +270,17 @@ * In this case we do not use the path because the protection space * is identified by the host:port:realm only */ - static AuthenticationInfo getServerAuth(URL url, String realm, AuthScheme scheme) { + static String getServerAuthKey(URL url, String realm, AuthScheme scheme) { int port = url.getPort(); if (port == -1) { port = url.getDefaultPort(); } String key = SERVER_AUTHENTICATION + ":" + scheme + ":" + url.getProtocol().toLowerCase() + ":" + url.getHost().toLowerCase() + ":" + port + ":" + realm; + return key; + } + + static AuthenticationInfo getServerAuth(String key) { AuthenticationInfo cached = getAuth(key, null); if ((cached == null) && requestIsInProgress (key)) { /* check the cache again, it might contain an entry */ @@ -314,9 +318,13 @@ * Used in response to a challenge. Note, the protocol field is always * blank for proxies. */ - static AuthenticationInfo getProxyAuth(String host, int port, String realm, AuthScheme scheme) { + static String getProxyAuthKey(String host, int port, String realm, AuthScheme scheme) { String key = PROXY_AUTHENTICATION + ":" + scheme + "::" + host.toLowerCase() + ":" + port + ":" + realm; + return key; + } + + static AuthenticationInfo getProxyAuth(String key) { AuthenticationInfo cached = (AuthenticationInfo) cache.get(key, null); if ((cached == null) && requestIsInProgress (key)) { /* check the cache again, it might contain an entry */ @@ -330,19 +338,20 @@ * Add this authentication to the cache */ void addToCache() { - cache.put (cacheKey(true), this); + String key = cacheKey(true); + cache.put(key, this); if (supportsPreemptiveAuthorization()) { - cache.put (cacheKey(false), this); + cache.put(cacheKey(false), this); } - endAuthRequest(); + endAuthRequest(key); } - void endAuthRequest () { + static void endAuthRequest (String key) { if (!serializeAuth) { return; } synchronized (requests) { - requestCompleted (cacheKey(true)); + requestCompleted(key); } } diff -r 6e8fc7a85bf2 -r 98b1778f0fd1 jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java --- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Mon Apr 05 16:11:59 2010 -0700 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Tue Apr 06 13:47:59 2010 +0100 @@ -249,6 +249,8 @@ boolean isUserServerAuth; boolean isUserProxyAuth; + String serverAuthKey, proxyAuthKey; + /* Progress source */ protected ProgressSource pi; @@ -1503,11 +1505,11 @@ } throw e; } finally { - if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) { - proxyAuthentication.endAuthRequest(); + if (proxyAuthKey != null) { + AuthenticationInfo.endAuthRequest(proxyAuthKey); } - else if (respCode == HTTP_UNAUTHORIZED && serverAuthentication != null) { - serverAuthentication.endAuthRequest(); + if (serverAuthKey != null) { + AuthenticationInfo.endAuthRequest(serverAuthKey); } } } @@ -1720,8 +1722,8 @@ statusLine + "\""); } } finally { - if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) { - proxyAuthentication.endAuthRequest(); + if (proxyAuthKey != null) { + AuthenticationInfo.endAuthRequest(proxyAuthKey); } } @@ -1837,10 +1839,8 @@ if (realm == null) realm = ""; - ret = AuthenticationInfo.getProxyAuth(host, - port, - realm, - authScheme); + proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm, authScheme); + ret = AuthenticationInfo.getProxyAuth(proxyAuthKey); if (ret == null) { switch (authScheme) { case BASIC: @@ -1981,7 +1981,8 @@ domain = p.findValue ("domain"); if (realm == null) realm = ""; - ret = AuthenticationInfo.getServerAuth(url, realm, authScheme); + serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme); + ret = AuthenticationInfo.getServerAuth(serverAuthKey); InetAddress addr = null; if (ret == null) { try { diff -r 6e8fc7a85bf2 -r 98b1778f0fd1 jdk/test/java/net/Authenticator/Deadlock.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/net/Authenticator/Deadlock.java Tue Apr 06 13:47:59 2010 +0100 @@ -0,0 +1,160 @@ +/* + * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * @test + * @bug 6648001 + * @run main/othervm/timeout=20 -ea:sun.net.www.protocol.http.AuthenticationInfo -Dhttp.auth.serializeRequests=true Deadlock + * @summary cancelling HTTP authentication causes deadlock + */ + +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.io.InputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.URL; +import com.sun.net.httpserver.BasicAuthenticator; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpPrincipal; +import com.sun.net.httpserver.HttpServer; + +public class Deadlock { + + public static void main (String[] args) throws Exception { + Handler handler = new Handler(); + InetSocketAddress addr = new InetSocketAddress (0); + HttpServer server = HttpServer.create(addr, 0); + HttpContext ctx = server.createContext("/test", handler); + BasicAuthenticator a = new BasicAuthenticator("foobar@test.realm") { + @Override + public boolean checkCredentials (String username, String pw) { + return "fred".equals(username) && pw.charAt(0) == 'x'; + } + }; + + ctx.setAuthenticator(a); + ExecutorService executor = Executors.newCachedThreadPool(); + server.setExecutor(executor); + server.start (); + java.net.Authenticator.setDefault(new MyAuthenticator()); + + System.out.print("Deadlock: " ); + for (int i=0; i<2; i++) { + Runner t = new Runner(server, i); + t.start(); + t.join(); + } + server.stop(2); + executor.shutdown(); + if (error) { + throw new RuntimeException("test failed error"); + } + + if (count != 2) { + throw new RuntimeException("test failed count = " + count); + } + System.out.println("OK"); + + } + + static class Runner extends Thread { + HttpServer server; + int i; + Runner(HttpServer s, int i) { + server = s; + this.i = i; + } + + @Override + public void run() { + URL url; + HttpURLConnection urlc; + try { + url = new URL("http://localhost:"+server.getAddress().getPort()+"/test/foo.html"); + urlc = (HttpURLConnection)url.openConnection (); + } catch (IOException e) { + error = true; + return; + } + InputStream is = null; + try { + is = urlc.getInputStream(); + while (is.read()!= -1) {} + } catch (IOException e) { + if (i == 1) error = true; + } finally { + if (is != null) try { is.close(); } catch (IOException e) {} + } + } + } + + public static boolean error = false; + public static int count = 0; + + static class MyAuthenticator extends java.net.Authenticator { + @Override + public PasswordAuthentication getPasswordAuthentication() { + PasswordAuthentication pw; + if (!getRequestingPrompt().equals("foobar@test.realm")) { + Deadlock.error = true; + } + if (count == 0) { + pw = null; + } else { + pw = new PasswordAuthentication("fred", "xyz".toCharArray()); + } + count++; + return pw; + } + } + + static class Handler implements HttpHandler { + int invocation = 1; + + @Override + public void handle (HttpExchange t) + throws IOException + { + InputStream is = t.getRequestBody(); + Headers map = t.getRequestHeaders(); + Headers rmap = t.getResponseHeaders(); + while (is.read() != -1); + is.close(); + t.sendResponseHeaders(200, -1); + HttpPrincipal p = t.getPrincipal(); + if (!p.getUsername().equals("fred")) { + error = true; + } + if (!p.getRealm().equals("foobar@test.realm")) { + error = true; + } + t.close(); + } + } +}