View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.proxy.handlers.http;
21  
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
27  import org.apache.mina.proxy.ProxyAuthException;
28  import org.apache.mina.proxy.session.ProxyIoSession;
29  import org.apache.mina.proxy.utils.StringUtilities;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * HttpSmartProxyHandler.java - HTTP proxy handler that automatically handles forwarding request 
35   * to the appropriate authentication mechanism logic handler.
36   * 
37   * @author The Apache MINA Project (dev@mina.apache.org)
38   * @version $Rev: 685703 $, $Date: 2008-08-14 00:14:47 +0200 (Thu, 14 Aug 2008) $
39   * @since MINA 2.0.0-M3
40   */
41  public class HttpSmartProxyHandler extends AbstractHttpLogicHandler {
42      private final static Logger logger = LoggerFactory
43              .getLogger(HttpSmartProxyHandler.class);
44  
45      /**
46       * Has the HTTP proxy request been sent ?
47       */
48      private boolean requestSent = false;
49  
50      /**
51       * The automatically selected http authentication logic handler. 
52       */
53      private AbstractAuthLogicHandler authHandler;
54  
55      public HttpSmartProxyHandler(final ProxyIoSession proxyIoSession) {
56          super(proxyIoSession);
57      }
58  
59      /**
60       * Perform any handshaking processing.
61       */
62      public void doHandshake(final NextFilter nextFilter)
63              throws ProxyAuthException {
64          logger.debug(" doHandshake()");
65  
66          if (authHandler != null) {
67              authHandler.doHandshake(nextFilter);
68          } else {
69              if (requestSent) {
70                  throw new ProxyAuthException(
71                          "Authentication request already sent");
72              }
73  
74              logger.debug("  sending HTTP request");
75  
76              // Send request
77              HttpProxyRequest req = (HttpProxyRequest) getProxyIoSession()
78                      .getRequest();
79              Map<String, List<String>> headers = req.getHeaders() != null ? req
80                      .getHeaders() : new HashMap<String, List<String>>();
81  
82              StringUtilities.addValueToHeader(headers, "Keep-Alive",
83                      HttpProxyConstants.DEFAULT_KEEP_ALIVE_TIME, true);
84              StringUtilities.addValueToHeader(headers, "Proxy-Connection",
85                      "keep-Alive", true);
86              req.setHeaders(headers);
87  
88              writeRequest(nextFilter, req);
89              requestSent = true;
90          }
91      }
92  
93      /**
94       * Automatic selection of the authentication algorithm. If <code>preferedOrder</code> is set then
95       * algorithms are selected from the list order otherwise the algorithm tries to select the most 
96       * secured algorithm available first.
97       */
98      private void autoSelectAuthHandler(final HttpProxyResponse response)
99              throws ProxyAuthException {
100         // Get the Proxy-Authenticate header
101         List<String> values = response.getHeaders().get("Proxy-Authenticate");
102 
103         if (values == null || values.size() == 0) {
104             authHandler = HttpAuthenticationMethods.NO_AUTH
105                     .getNewHandler(getProxyIoSession());
106 
107         } else if (getProxyIoSession().getPreferedOrder() == null) {
108             for (String proxyAuthHeader : values) {
109                 proxyAuthHeader = proxyAuthHeader.toLowerCase();
110 
111                 try {
112                     // Test which auth mechanism to use. First found is the first used that's why we test
113                     // in a decreasing security quality order.
114                     if (proxyAuthHeader.contains("ntlm")) {
115                         authHandler = HttpAuthenticationMethods.NTLM
116                                 .getNewHandler(getProxyIoSession());
117                         break;
118                     } else if (proxyAuthHeader.contains("digest")) {
119                         authHandler = HttpAuthenticationMethods.DIGEST
120                                 .getNewHandler(getProxyIoSession());
121                         break;
122                     } else if (proxyAuthHeader.contains("basic")) {
123                         authHandler = HttpAuthenticationMethods.BASIC
124                                 .getNewHandler(getProxyIoSession());
125                         break;
126                     }
127                 } catch (Exception ex) {
128                     logger.debug("Following exception occured:", ex);
129                 }
130             }
131 
132             if (authHandler == null) {
133                 authHandler = HttpAuthenticationMethods.NO_AUTH
134                         .getNewHandler(getProxyIoSession());
135             }
136 
137         } else {
138             for (HttpAuthenticationMethods method : getProxyIoSession()
139                     .getPreferedOrder()) {
140                 if (authHandler != null) {
141                     break;
142                 }
143 
144                 if (method == HttpAuthenticationMethods.NO_AUTH) {
145                     authHandler = HttpAuthenticationMethods.NO_AUTH
146                             .getNewHandler(getProxyIoSession());
147                     break;
148                 }
149 
150                 for (String proxyAuthHeader : values) {
151                     proxyAuthHeader = proxyAuthHeader.toLowerCase();
152 
153                     try {
154                         // test which auth mechanism to use
155                         if (proxyAuthHeader.contains("basic")
156                                 && method == HttpAuthenticationMethods.BASIC) {
157                             authHandler = HttpAuthenticationMethods.BASIC
158                                     .getNewHandler(getProxyIoSession());
159                             break;
160                         } else if (proxyAuthHeader.contains("digest")
161                                 && method == HttpAuthenticationMethods.DIGEST) {
162                             authHandler = HttpAuthenticationMethods.DIGEST
163                                     .getNewHandler(getProxyIoSession());
164                             break;
165                         } else if (proxyAuthHeader.contains("ntlm")
166                                 && method == HttpAuthenticationMethods.NTLM) {
167                             authHandler = HttpAuthenticationMethods.NTLM
168                                     .getNewHandler(getProxyIoSession());
169                             break;
170                         }
171                     } catch (Exception ex) {
172                         logger.debug("Following exception occured:", ex);
173                     }
174                 }
175             }
176 
177         }
178 
179         if (authHandler == null) {
180             throw new ProxyAuthException(
181                     "Unknown authentication mechanism(s): " + values);
182         }
183     }
184 
185     /**
186      * Handle a HTTP response from the proxy server.
187      * 
188      * @param response The response.
189      */
190     @Override
191     public void handleResponse(final HttpProxyResponse response)
192             throws ProxyAuthException {
193         if (!isHandshakeComplete()
194                 && ("close".equalsIgnoreCase(StringUtilities
195                         .getSingleValuedHeader(response.getHeaders(),
196                                 "Proxy-Connection")) || "close"
197                         .equalsIgnoreCase(StringUtilities
198                                 .getSingleValuedHeader(response.getHeaders(),
199                                         "Connection")))) {
200             getProxyIoSession().setReconnectionNeeded(true);
201         }
202 
203         if (response.getStatusCode() == 407) {
204             if (authHandler == null) {
205                 autoSelectAuthHandler(response);
206             }
207             authHandler.handleResponse(response);
208         } else {
209             throw new ProxyAuthException("Received error response code ("
210                     + response.getStatusLine() + ").");
211         }
212     }
213 }