views:

61

answers:

2

I am trying to log into a reports system using java. So far, I have tried MANY different implementations that have used HttpURLConnection. The HTML I am trying to post to boils down to:

<form name="logonForm" method="POST" action="http://remoteserver/logon.object"&gt;
<input type="hidden" name="qryStr" value=""> 
<input type="hidden" name="cmsVisible" value="true"> 
<input type="hidden" name="authenticationVisible" value="true"> 
<input type="hidden" name="referer" value="">
<input type="hidden" name="refererFormData" value="">
<input type="hidden" name="isFromLogonPage" value="true">
<input type="text" name="cms" value="xxxxx" class="textfield" id="apsTextEdit">
<input type="text" name="username" value="xxxxx"class="textfield" id="usernameTextEdit">
<input type="text" name="password" value="xxxxx"class="textfield" id="passwordTextEdit">
<select name="authType"id="authenticationSelectBox">
<option selected value='xxxxx'>xxxxx</select>
<input type="submit" name="SUBMIT">
</form>

I know this form provides valid input, because when I click submit in a browser, it logs me into my reports system. However, I always get the above html back as a response when I send a programmatic request. Note I have tried about a million implementations that have used HttpURLRequest. One example:

        OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream());
        wr.write(parameters);
        wr.flush();

        // Get the response
        BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String line;
        String allLines = "";
        while ((line = rd.readLine()) != null) {
            allLines += "\n" + line; // Process line...
        }
        wr.close();
        rd.close();
        return allLines;

Where 'parameters' ranges from nothing to every URL-encoded parameter from the HTML I posted, and connection is (currently, as in I've tried other configurations) set up like:

        connection = (HttpURLConnection) url.openConnection();
        HttpURLConnection.setFollowRedirects(true);
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setRequestMethod("POST");
        connection.setAllowUserInteraction(false);
        connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=" + "UTF-8");
        connection.setUseCaches(false);
        connection.connect();

I know the details are a little sparse, so let me know if you need any more info and thanks very much for any input!

+2  A: 

You need to ensure that you also take the name=value pairs of all <input type="hidden"> elements and the one <input type="submit"> you'd like to "press" programmatically in the query string. The button has only a name, but regardless, you need to pass it along. This way the server side can distinguish if a button is pressed and if so, which one.

Besides, if the form requires a session (it might use a request based token), then you need to maintain the cookies yourself as well. Check and get the Set-Cookie from the header of the first response and set it as Cookie header of the subsequent requests.

For easier and less verbose HTTP/form processing, consider Apache HttpComponents Client.

See also:

BalusC
To clarify....For my POST to work properly, I would need to supply a name-value pair for every input (e.g. qryStr) even if the values are empty? Also, how does a name-value pair work for a submit button?
Jon
Yes. That's also what a decent webbrowser does. The submit button has also a `name` and `value` attribute. You need to send it as well (like as you do for every other input element). In your case, the `value` attribute is omitted from the button, but you need to send the `name` anyway. Just append `SUBMIT=` to the query string to indicate that you've "pressed" the button. See also the given link. It contains lot of hints and code examples as well.
BalusC
@Jon: since you seem to have switched the accepted answer, I'm curious to the exact cause of the problem. Or was it a combination of both? You were correctly flushing the output before getting the response. Closing is not necessary to get the response. Even more, the default `HttpURLConnection` implementation already implicitly flushes the output before sending request.
BalusC
I was doing a lot wrong... really I just needed to properly monitor what was going on in the browser, which is why I set ZZ's answer as correct. However, I did end up switching to HttpClient, and you did provide overall more helpful information... changing answer again...
Jon
+1  A: 

Your code may deadlock in certain circumstances. You should close the output before you read from input. Without closing the stream, server doesn't know your request is complete.

If you need to keep output open for some reason, you need to send "Content-length" header to inform server how much to expect.

Parameters must be properly encoded. I normally put in a map and serialize it like this,

            StringBuilder sb = new StringBuilder(baseUrl);

            for (Parameter p : parameters) {
                    if (sb.length() > 0)
                        sb.append('&');
                    try {
                            sb.append(URLEncoder.encode(p.name, "UTF-8"));
                            if (p.value != null) {
                                sb.append('=');
                                sb.append(URLEncoder.encode(p.value, "UTF-8"));
                            }
                    } catch (UnsupportedEncodingException e) {
                        // Not really possible, throw unchecked exception
                        throw new IllegalStateException("No UTF-8");
                    }
            }

The best way to figure out what parameters to post is to capture a trace of the posting in browser and see how browser does it. FireBug/DebugBar are your friends.

ZZ Coder