JDK-4755765 : HttpURLConnection Fails on 401 Response
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 1.4.1
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-10-01
  • Updated: 2002-10-01
  • Resolved: 2002-10-01
Related Reports
Duplicate :  
Description

Name: nt126004			Date: 09/30/2002


FULL PRODUCT VERSION :
C:\ems\spawar\ems\svc\gui\client>java -version
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)

FULL OPERATING SYSTEM VERSION :  Windows 2000 sp2


EXTRA RELEVANT SYSTEM CONFIGURATION :
Web server is Netscape Enterprise Server 6.1 running on
same machine.

A DESCRIPTION OF THE PROBLEM :
A GET request is submitted to a servlet running  Netscape
Enterprise Server 6.1 (HTTP/1.1) for a realm which has
already been successfully authenticated.  A servlet running
within that realm intentionally responds with a 401/WWW-
Authentication response to force a reauthentication.  If
the user enters an incorrect login during the
reauthentication, the connection fails and cannot be
reinitialized without restarting the client.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Start the servlet in a restricted realm
2. Start the client and login to the restricted realm
3. Initiate a GET request to the servlet to force
reauthentication in the restricted realm - a login dialog
appears
4. Login incorrectly when prompted - error occurs


EXPECTED VERSUS ACTUAL BEHAVIOR :
Beginning from step 4, the login prompt should appear until
the user successfully reauthenticates or the connection
expires in which case the user should be notified.

Instead, an uncaught exception occurs resulting in a
connection that can no longer be reinitialized without
restarting the client.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.LinkedList.entry(LinkedList.java:356)
        at java.util.LinkedList.get(LinkedList.java:299)
        at sun.net.www.protocol.http.PathMap.get(AuthenticationInfo.java:355)
        at sun.net.www.protocol.http.AuthenticationInfo.getAuth
(AuthenticationInfo.java:176)
        at sun.net.www.protocol.http.AuthenticationInfo.getServerAuth
(AuthenticationInfo.java:166)
        at sun.net.www.protocol.http.HttpURLConnection.getServerAuthentication
(HttpURLConnection.java:920)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream
(HttpURLConnection.java:600)
        at sun.net.www.protocol.http.HttpURLConnection.getHeaderFields
(HttpURLConnection.java:1133)
        at TestClient.reauthenticate(TestClient.java:30)
        at TestClient.main(TestClient.java:57)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

************************** Client code: **************************************
public class TestClient {
    
    /**
    allows reauthentication.  Used to update an expired user
    token.
    */
    public static void reauthenticate(String url)
    {
        java.net.HttpURLConnection urlConnection = null;

        try
        {
            java.net.URL u = new java.net.URL(url + "?html=authenticate&id=1");
            urlConnection = (java.net.HttpURLConnection)u.openConnection();
            //prevent login/password from being sent
            urlConnection.setRequestProperty("Authorization", "");
            urlConnection.setRequestProperty("Cache-Control", "no-cache");
            urlConnection.setRequestProperty("Pragma", "no-cache");
            urlConnection.connect();
            java.util.Map m = urlConnection.getHeaderFields();
            java.util.Set s = m.keySet();
            java.util.Iterator i = s.iterator();
            while(i.hasNext())
            {
                String key = (String)i.next();
                Object value = m.get(key);
                System.out.println(key + ": " + value);
            }
        }
        catch(Exception e){e.printStackTrace();}
        finally{urlConnection.disconnect();}
    }
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //args[0] must be a reference to a simple html page in a restricted
realm (e.g. /client/index.html)
        //args[1] must be a reference to the TestServlet deeper in the
restricted realm (e.g. /client/control/TestServlet)
        java.net.URLConnection.setDefaultAllowUserInteraction(true);
        java.net.Authenticator.setDefault(new BasicAuthenticator());
 
        javax.swing.JTextPane pane = new javax.swing.JTextPane();
        //authenticate in realm first
        try{pane.setPage(args[0]);}
        catch(java.io.IOException e){}
        TestClient t = new TestClient();
        //attempt to reauthenticate in the same realm
        t.reauthenticate(args[1]);
    }
    
}

************************** Servlet code: **************************************

import java.text.*;
import java.net.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class TestServlet extends javax.servlet.http.HttpServlet
{
    private Hashtable hotswapHash = new Hashtable();

    public synchronized void doGet (HttpServletRequest req, HttpServletResponse
res)	throws ServletException, IOException
    {
        short posit = 0;
        String targetUrl = null;
        String id = null;
        res.setContentType("text/html");

        StringTokenizer qryParams = new StringTokenizer(req.getQueryString
(), "&");
        while(qryParams.hasMoreTokens())
        {
            String qryHash = qryParams.nextToken();
            if ( (posit = (short)qryHash.indexOf("html=")) != -1)
                targetUrl = qryHash.substring(posit+5);
            else if ( (posit = (short)qryHash.indexOf("id=")) != -1)
                id = qryHash.substring(posit+3);
        }

        if(targetUrl.equals("authenticate"))
        {
            /*java.io.File f = new File("c:\\temp\\output.txt");
            java.io.FileOutputStream o = new FileOutputStream(f);
            StringBuffer b = new StringBuffer();
            b.append(
                "\n\nRequest Method: " + req.getMethod() +
                "\nRequest URI: "    + req.getRequestURI() +
                "\nRequest Protocol:"+ req.getProtocol());
                java.util.Enumeration headerNames = req.getHeaderNames();
            while(headerNames.hasMoreElements()) {
              String headerName = (String)headerNames.nextElement();
              b.append("\n" + headerName + ": " + req.getHeader(headerName));
            }
            
            byte output[] = b.toString().getBytes();
            o.write(output, (int)f.length(), output.length);
            o.close();
            */

            //if in reauthenticate hash already, reauthenticate
            //with new login
            if(hotswapHash.containsKey(id))
            {
                //remove flag from hash
                hotswapHash.remove(id);
                //reset id on Access Service by changing the user
                //RTS - no only will this reset token but will
                //pick up any data changes in the LDAP server
                try
                {
                    res.setStatus(res.SC_NO_CONTENT);
                    System.out.println("succeeded.");
                }
                catch(Exception e)
                {
                    //return error which will prompt for reauthentication
                    askForPassword(res);
                    System.out.println("failed");
                }
                finally
                {
                    return;
                }
            }
            //else store in reauthenticate hash and prompt for login
            else
            {
                //store flag in hash
                hotswapHash.put(id, id);
                //return error which will prompt for reauthentication
                askForPassword(res);
                System.out.print("reauthenticating " + id + "...");
                return;
            }
        }
   }
    
    private void askForPassword(HttpServletResponse response)
    {
        response.setStatus(response.SC_UNAUTHORIZED); // Ie 401
        response.setHeader("WWW-Authenticate",
                           "BASIC realm=\"Client Login\"");
    }
}

*************************** Authenticator Code: *******************************

/*
 * BasicAuthenticator.java
 *
 * Created on June 24, 2002, 6:24 PM
 */

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.PasswordAuthentication;

/**
 * Extends Authenticator to allow for user login
 * @author  R. Smudz
 */
public class BasicAuthenticator extends java.net.Authenticator {
    private LoginDialog dialog;
    
    /** Creates a new instance of BasicAuthenticator */
    public BasicAuthenticator() {
    }
    
    protected java.net.PasswordAuthentication getPasswordAuthentication() {
        PasswordAuthentication authentication = createLoginDialog();
        return authentication;
    }

    private PasswordAuthentication createLoginDialog() {
        if (dialog == null)
            dialog = new LoginDialog();

        dialog.show();
        PasswordAuthentication p = dialog.getAuth();
        return p;
    }
}

class LoginDialog extends JDialog implements KeyListener, ActionListener
{
    private	JPanel	north, south;
    private	JLabel	nameLabel, passwordLabel, infoLabel;
    private	JTextField	nameTextField;
    private	JPasswordField  passwordField;
    private	JButton	loginButton, cancelButton;
    private	String	username;
    private char	password[];
    private PasswordAuthentication	privateAuth;

    public LoginDialog()
    {
        setModal(true);
        setTitle("Login Required");
        north = new JPanel();
        north.setLayout(null);
        north.setPreferredSize(new Dimension(277, 110));
        nameLabel = new JLabel("Username:", JLabel.LEFT);
        nameLabel.setBounds(10, 60, 90, 20);
        north.add(nameLabel);
        passwordLabel = new JLabel("Password:", JLabel.LEFT);
        passwordLabel.setBounds(10, 90, 90, 20);
        north.add(passwordLabel);
        nameTextField = new JTextField();
        nameTextField.setBounds(100, 60, 157, 20);
        north.add(nameTextField);
        passwordField = new JPasswordField();
        passwordField.setBounds(100, 90, 157, 20);
        north.add(passwordField);

        getContentPane().add(north,BorderLayout.CENTER);
        south = new JPanel();
        south.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 10));
        loginButton = new JButton("Login");
        loginButton.addActionListener(this);
        south.add(loginButton);
	cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                privateAuth = null;
                hide();
            }
        });

        south.add(cancelButton);
        getContentPane().add(south, BorderLayout.SOUTH);
	loginButton.addKeyListener(this);
        setDefaultCloseOperation(HIDE_ON_CLOSE);
        pack();
        //parent frame may not be up so use center of screen
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        setLocation((int)d.getWidth()/2 - getWidth()/2, (int)d.getHeight()/2 -
getHeight()/2 );
    }

    public void actionPerformed(ActionEvent ae)
    {
        char[] authArray;

        username = nameTextField.getText();
        nameTextField.setText("");
        password = passwordField.getPassword();
        passwordField.setText("");
        privateAuth = new PasswordAuthentication(username, password);
        //  Delete attributes for userID and password
        username = "";
        password = new String("").toCharArray();
        nameTextField.requestFocus();
        hide();
    }

    /**
     *	Returns the actual PasswordAuthentication,
     *	 which was generated from the login dialog.
     *
     *	@return PasswordAuthentication - actual PasswordAuthentication.
     */
    public PasswordAuthentication getAuth()
    {
            return privateAuth;
    }
    
    public void keyPressed(java.awt.event.KeyEvent keyEvent) {
    }
    
    public void keyReleased(java.awt.event.KeyEvent keyEvent) {
    }
    
    public void keyTyped(java.awt.event.KeyEvent keyEvent) {
        if(keyEvent.getKeyCode() == KeyEvent.VK_UNDEFINED || keyEvent.getKeyCode
() == KeyEvent.VK_ENTER)
            actionPerformed(null);
    }
}
---------- END SOURCE ----------
(Review ID: 164925) 
======================================================================

Comments
EVALUATION This is a duplicate of 4678055, which is being fixed in mantis. ###@###.### 2002-10-01
01-10-2002