Bug 1030050

Summary: The FormAuthenticator loses post data
Product: [JBoss] JBoss Enterprise Application Platform 6 Reporter: Derek Horton <dehort>
Component: WebAssignee: Rémy Maucherat <rmaucher>
Status: CLOSED CURRENTRELEASE QA Contact: Radim Hatlapatka <rhatlapa>
Severity: unspecified Docs Contact: Russell Dickenson <rdickens>
Priority: unspecified    
Version: 6.1.1CC: aogburn, asaldhan, jkudrnac, vtunka
Target Milestone: ER9   
Target Release: EAP 6.3.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2014-08-06 14:39:05 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1076163    

Description Derek Horton 2013-11-13 19:48:29 UTC
Description of problem:

The FormAuthenticator loses post data.

Here is the setup:

example.html is a unprotected form that posts to a protected servlet
(ExampleServlet).  The servlet simply prints out the form data that it
receives.

When I submit the form, I am correctly prompted for a username/password.
On successful authentication, my servlet is called but it cannot
retrieve the form/post data.

I have recreated this issue on EAP 6.1.0 and 6.1.1.  However, the same
application works fine on EAP 5.2.0.

It looks like the FormAuthenticator is losing the post data because the
following if statement evaluates to false in saveRequest():

            if (body.getLength() == 0) {
                // It means that parameters have already been parsed.
                Enumeration e = request.getParameterNames();
                for ( ; e.hasMoreElements() ;) {
                    String name = (String) e.nextElement();
                    String[] val = request.getParameterValues(name);
                    saved.addParameter(name, val);
                }
            }

As a result, the parameters are not stored in the SavedRequest object.

The interesting thing is that if I call Request.getParameterMap() in the 
authenticator, then the if body.getLength() is 0 and the if statement is
entered (storing the parameters).

Comment 1 Aaron Ogburn 2013-11-13 22:22:30 UTC
When the parameters aren't parsed, the request body should be stored in a SavedRequest object.  After authentication, FormAuthenticator.restoreRequest should restore that body.  Debugging locally, I could clearly see the parameter there in the body set in the SavedRequest, so it's a matter of the request parsing the parameter from that saved body.  But it looks like connector.Request.parseParameters() was returning at this line, which is before it would ready the body:

         if (usingInputStream || usingReader)
             return;


The FormAuthenticator uses the inputstream to set the usingInputStream flag so looks like the issue is around here.  Haven't had a chance to test on EAP 5 to see how parseParameters or the inputstream flag is behaving differently.  But if you force the FormAuthenticator to read the body and get the parameters, the parameters themselves are then placed in and fetched from the SavedRequest object, so we don't reach the failure point trying to parse the parameters later from the restored request body.

Comment 2 Derek Horton 2013-12-03 22:40:54 UTC
The EAP 6 version of the FormAuthenticator has the following loop just above where the POST is handled in the restoreRequest method:

498         // Swallow any request body since we will be replacing it
499         byte[] buffer = new byte[4096];
500         InputStream is = request.getInputStream();
501         while (is.read(buffer) >= 0) {
502             // Ignore request body
503         }
504 
505         if ("POST".equalsIgnoreCase(saved.getMethod())) {

If I comment out that while loop, then the FormAuthenticator does not
lose the post data.

Comment 3 Rémy Maucherat 2013-12-05 15:27:08 UTC
I synced stuff with r2320, so maybe you can test it. Not using createInputStream could cause problems, that's one of the improvements.

Comment 4 Derek Horton 2013-12-05 21:46:46 UTC
It looks like r2320 only made changes to the FormAuthenticator so I grabbed it and ran a quick test.  Unfortunately, it did not resolve the issue.

Using createInputStream() changes the behavior up a bit but it
doesn't resolve the issue.

I think I have narrowed the issue down to the "BODY_REPLAY" logic.  The
POST section of the FormAuthenticator.restoreRequest() appears to
trigger this logic:

        if ("POST".equalsIgnoreCase(saved.getMethod())) {                                                              
            ByteChunk body = saved.getBody();                                                                          
                                                                                                                       
            if (body != null && body.getLength() > 0) {                                                                
                request.clearParameters();                                                                             
                request.getCoyoteRequest().action                                                                      
                    (ActionCode.ACTION_REQ_SET_BODY_REPLAY, body);    

This causes the Http11Processor to create a SavedRequestInputFilter and
add it as an ActiveFilter to the request's InternalInputBuffer.

Unfortunately, during the remaining processing, the
SavedRequestInputFilter's doRead() method is never called.

The problem appears to be that the connector.InputBuffer's eof variable
is set to true too early??  As a result,
connector.InputBuffer.realReadBytes() returns with a -1 before
coyoteRequest.doRead() is called.  I was able to use jdb to set eof to
false which allowed coyoteRequest.doRead() to get called.  This resulted
in the SavedRequestInputFilter.doRead() getting called which appears to
have correctly restored the request.

I'm not sure how this should be resolved in the code though.  Perhaps
connector.InputBuffer.recycle() needs to be called?

Comment 7 Radim Hatlapatka 2014-07-14 16:24:41 UTC
Verified in EAP 6.3.0.ER9