[ 
https://issues.apache.org/jira/browse/DIRMINA-844?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15288962#comment-15288962
 ] 

Antonio Silvestre edited comment on DIRMINA-844 at 5/18/16 1:42 PM:
--------------------------------------------------------------------

I had got the same issue. The problem is an error design into the handshake 
process with an http authentication required proxy. When we try to connect to 
the proxy for the first time, we don't send any authentication header, so we 
receive a 407 code response, and from the "Proxy-Authenticate" header we get 
the required authentication method and set the corresponding AuthHandler class.

The first error is in class 
"org.apache.mina.proxy.handlers.http.AbstractHttpLogicHandler", method "", line 
377:
{code:title=AbstractHttpLogicHandler.java|borderStyle=solid}
    protected HttpProxyResponse decodeResponse(final String response) throws 
Exception {
        LOGGER.debug("  parseResponse()");

        // Break response into lines
        String[] responseLines = response.split(HttpProxyConstants.CRLF);

        // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
        // BUG FIX : Trimed to prevent failures with some proxies that add 
        // extra space chars like "Microsoft-IIS/5.0" ...
        String[] statusLine = responseLines[0].trim().split(" ", 2);

        if (statusLine.length < 2) {
            throw new Exception("Invalid response status line (" + statusLine + 
"). Response: " + response);
        }

        // Status code is 3 digits
        if (!statusLine[1].matches("^\\d\\d\\d")) {
            throw new Exception("Invalid response code (" + statusLine[1] + "). 
Response: " + response);
        }

        Map<String, List<String>> headers = new HashMap<String, List<String>>();

        for (int i = 1; i < responseLines.length; i++) {
            String[] args = responseLines[i].split(":\\s?", 2);
            StringUtilities.addValueToHeader(headers, args[0], args[1], false);
        }

        return new HttpProxyResponse(statusLine[0], statusLine[1], headers);
    }
{code}

The value "statusLine[1]" is expected to be a string starting with the http 
returned code (in this case 407) followed by a space and the corresponding 
message added by the server (as we're splitting the full status line into only 
2 strings). Here you are an example taken from my company proxy:

{{407 Proxy Authentication Required}}

the regular expression to match {noformat}"^\\d\\d\\d"{noformat} is expecting 
only the ciphers, not the text, so I've changed this regular expression to add 
any followed text: {noformat}"^\\d\\d\\d.*".{noformat} The HttpProxyResponse 
constructor gets the substring from the first 3 chars and parses it to the 
integer return code, so the remaining text doesn't affect. Depending of how do 
you handle this exception, you may not see the error trace.

After correcting this error, instead of throwing the exception the code goes on 
and it tries to reconnect to the proxy, creating a new session and rebuilding 
the http request to add the corresponding authentication headers to it, but 
here there's another problem... The process never launch the second http 
request, as the "operationComplete" callback defined into the "reconnect" 
method, line 342 is never called:

{code:title=AbstractHttpLogicHandler.java|borderStyle=solid}
    private void reconnect(final NextFilter nextFilter, final HttpProxyRequest 
request) {
        LOGGER.debug("Reconnecting to proxy ...");

        final ProxyIoSession proxyIoSession = getProxyIoSession();

        // Fires reconnection
        proxyIoSession.getConnector().connect(new 
IoSessionInitializer<ConnectFuture>() {
            public void initializeSession(final IoSession session, 
ConnectFuture future) {
                LOGGER.debug("Initializing new session: {}", session);
                session.setAttribute(ProxyIoSession.PROXY_SESSION, 
proxyIoSession);
                proxyIoSession.setSession(session);
                LOGGER.debug("  setting up proxyIoSession: {}", proxyIoSession);
                future.addListener(new IoFutureListener<ConnectFuture>() {
                    public void operationComplete(ConnectFuture future) {
                        // Reconnection is done so we send the
                        // request to the proxy
                        proxyIoSession.setReconnectionNeeded(false);
                        writeRequest0(nextFilter, request);
                    }
                });
            }
        });
    }
{code}

The operation is considered "completed" when the handshake phase is completed, 
but we're into this phase now, and we need to execute the request to complete 
it, so here we have a deadlock. As we haven't completed the handshake phase, we 
never call the "operationComplete" method and of course we never do the second 
http request with the right headers. To solve this problem we need to remove 
the custom IoFutureListener and put the inner code into the "initializeSession" 
callback directly.

I'm going to make a pull request with this changes. They have been tested and 
they're working.


was (Author: asilvestrer):
I had got the same issue. The problem is an error design into the handshake 
process with an http authentication required proxy. When we try to connect to 
the proxy for the first time, we don't send any authentication header, so we 
receive a 407 code response, and from the "Proxy-Authenticate" header we get 
the required authentication method and set the corresponding AuthHandler class.

The first error is in class 
"org.apache.mina.proxy.handlers.http.AbstractHttpLogicHandler", method "", line 
377:
{code:title=AbstractHttpLogicHandler.java|borderStyle=solid}
    protected HttpProxyResponse decodeResponse(final String response) throws 
Exception {
        LOGGER.debug("  parseResponse()");

        // Break response into lines
        String[] responseLines = response.split(HttpProxyConstants.CRLF);

        // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
        // BUG FIX : Trimed to prevent failures with some proxies that add 
        // extra space chars like "Microsoft-IIS/5.0" ...
        String[] statusLine = responseLines[0].trim().split(" ", 2);

        if (statusLine.length < 2) {
            throw new Exception("Invalid response status line (" + statusLine + 
"). Response: " + response);
        }

        // Status code is 3 digits
        if (!statusLine[1].matches("^\\d\\d\\d")) {
            throw new Exception("Invalid response code (" + statusLine[1] + "). 
Response: " + response);
        }

        Map<String, List<String>> headers = new HashMap<String, List<String>>();

        for (int i = 1; i < responseLines.length; i++) {
            String[] args = responseLines[i].split(":\\s?", 2);
            StringUtilities.addValueToHeader(headers, args[0], args[1], false);
        }

        return new HttpProxyResponse(statusLine[0], statusLine[1], headers);
    }
{code}

The value "statusLine[1]" is expected to be a string starting with the http 
returned code (in this case 407) followed by a space and the corresponding 
message added by the server (as we're splitting the full status line into only 
2 strings). Here you are an example taken from my company proxy:

{{407 Proxy Authentication Required}}

the regular expression to match {noformat}"^\\d\\d\\d" is expecting only the 
ciphers, not the text, so I've changed this regular expression to add any 
followed text: "^\\d\\d\\d.*".{noformat} The HttpProxyResponse constructor gets 
the substring from the first 3 chars and parses it to the integer return code, 
so the remaining text doesn't affect. Depending of how do you handle this 
exception, you may not see the error trace.

After correcting this error, instead of throwing the exception the code goes on 
and it tries to reconnect to the proxy, creating a new session and rebuilding 
the http request to add the corresponding authentication headers to it, but 
here there's another problem... The process never launch the second http 
request, as the "operationComplete" callback defined into the "reconnect" 
method, line 342 is never called:

{code:title=AbstractHttpLogicHandler.java|borderStyle=solid}
    private void reconnect(final NextFilter nextFilter, final HttpProxyRequest 
request) {
        LOGGER.debug("Reconnecting to proxy ...");

        final ProxyIoSession proxyIoSession = getProxyIoSession();

        // Fires reconnection
        proxyIoSession.getConnector().connect(new 
IoSessionInitializer<ConnectFuture>() {
            public void initializeSession(final IoSession session, 
ConnectFuture future) {
                LOGGER.debug("Initializing new session: {}", session);
                session.setAttribute(ProxyIoSession.PROXY_SESSION, 
proxyIoSession);
                proxyIoSession.setSession(session);
                LOGGER.debug("  setting up proxyIoSession: {}", proxyIoSession);
                future.addListener(new IoFutureListener<ConnectFuture>() {
                    public void operationComplete(ConnectFuture future) {
                        // Reconnection is done so we send the
                        // request to the proxy
                        proxyIoSession.setReconnectionNeeded(false);
                        writeRequest0(nextFilter, request);
                    }
                });
            }
        });
    }
{code}

The operation is considered "completed" when the handshake phase is completed, 
but we're into this phase now, and we need to execute the request to complete 
it, so here we have a deadlock. As we haven't completed the handshake phase, we 
never call the "operationComplete" method and of course we never do the second 
http request with the right headers. To solve this problem we need to remove 
the custom IoFutureListener and put the inner code into the "initializeSession" 
callback directly.

I'm going to make a pull request with this changes. They have been tested and 
they're working.

> Http Proxy Authentication failed to complete (see description for exact point 
> of failure)
> -----------------------------------------------------------------------------------------
>
>                 Key: DIRMINA-844
>                 URL: https://issues.apache.org/jira/browse/DIRMINA-844
>             Project: MINA
>          Issue Type: Bug
>          Components: Core
>    Affects Versions: 2.0.3
>         Environment: Mac OS X 10.6.8 
> JDK 1.6
> squid/2.6
> Eclipse
>            Reporter: Parijat Bansal
>             Fix For: 2.0.8
>
>
> When trying to connect through an Squid Http Proxy using ProxyConnector 
> (requiring basic authentication). While debugging issue, I walked through 
> MINA code. Following are my observations:
> 1. First attempt made by MINA do not send any authentication details even 
> though property set, seems like its to get first response mentioning 
> authentication method. 
> 2. So a second connection is initiated from ProxyConnector and this time 
> request contains the authentication header. 
> ISSUE: My client kept waiting at this point and it seems that second 
> connection never got completed. I used eclipse debugger and found that 
> AbstractPollingIoConnector.connect0 method is getting called with proper 
> destination (As I used a NioSocketConnector to construct ProxyConnector) but 
> after that no progress.
> Also  at Line#343 following were the content of request variable (I wonder if 
> this could be of help )
> request       AbstractPollingIoConnector$ConnectionRequest  (id=82)   
>       deadline        1310717009666   
>       firstListener   null    
>       handle  SocketChannelImpl  (id=88)      
>       lock    AbstractPollingIoConnector$ConnectionRequest  (id=82)   
>       otherListeners  null    
>       ready   false   
>       result  null    
>       session null    
>       sessionInitializer      ProxyIoSessionInitializer<T>  (id=104)  
>       this$0  NioSocketConnector  (id=40)     
>       waiters 0       



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to