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 a request 
35   * to the appropriate authentication mechanism logic handler.
36   * 
37   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
38   * @since MINA 2.0.0-M3
39   */
40  public class HttpSmartProxyHandler extends AbstractHttpLogicHandler {
41      private final static Logger logger = LoggerFactory
42              .getLogger(HttpSmartProxyHandler.class);
43  
44      /**
45       * Has the HTTP proxy request been sent ?
46       */
47      private boolean requestSent = false;
48  
49      /**
50       * The automatically selected http authentication logic handler. 
51       */
52      private AbstractAuthLogicHandler authHandler;
53  
54      public HttpSmartProxyHandler(final ProxyIoSession proxyIoSession) {
55          super(proxyIoSession);
56      }
57  
58      /**
59       * Performs the handshake processing.
60       * 
61       * @param nextFilter the next filter
62       */
63      public void doHandshake(final NextFilter nextFilter)
64              throws ProxyAuthException {
65          logger.debug(" doHandshake()");
66  
67          if (authHandler != null) {
68              authHandler.doHandshake(nextFilter);
69          } else {
70              if (requestSent) {
71                  // Safety check
72                  throw new ProxyAuthException(
73                          "Authentication request already sent");
74              }
75  
76              logger.debug("  sending HTTP request");
77  
78              // Compute request headers
79              HttpProxyRequest req = (HttpProxyRequest) getProxyIoSession()
80                      .getRequest();
81              Map<String, List<String>> headers = req.getHeaders() != null ? req
82                      .getHeaders() : new HashMap<String, List<String>>();
83  
84              AbstractAuthLogicHandler.addKeepAliveHeaders(headers);
85              req.setHeaders(headers);
86  
87              // Write request to the proxy
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       * @param response the proxy response
99       */
100     private void autoSelectAuthHandler(final HttpProxyResponse response)
101             throws ProxyAuthException {
102         // Get the Proxy-Authenticate header
103         List<String> values = response.getHeaders().get("Proxy-Authenticate");
104         ProxyIoSession proxyIoSession = getProxyIoSession();
105 
106         if (values == null || values.size() == 0) {
107             authHandler = HttpAuthenticationMethods.NO_AUTH
108                     .getNewHandler(proxyIoSession);
109 
110         } else if (getProxyIoSession().getPreferedOrder() == null) {
111             // No preference order set for auth mechanisms
112             int method = -1;
113 
114             // Test which auth mechanism to use. First found is the first used
115             // that's why we test in a decreasing security quality order.
116             for (String proxyAuthHeader : values) {
117                 proxyAuthHeader = proxyAuthHeader.toLowerCase();
118 
119                 if (proxyAuthHeader.contains("ntlm")) {
120                     method = HttpAuthenticationMethods.NTLM.getId();
121                     break;
122                 } else if (proxyAuthHeader.contains("digest")
123                         && method != HttpAuthenticationMethods.NTLM.getId()) {
124                     method = HttpAuthenticationMethods.DIGEST.getId();
125                 } else if (proxyAuthHeader.contains("basic") && method == -1) {
126                     method = HttpAuthenticationMethods.BASIC.getId();
127                 }
128             }
129 
130             if (method != -1) {
131                 try {
132                     authHandler = HttpAuthenticationMethods.getNewHandler(
133                             method, proxyIoSession);
134                 } catch (Exception ex) {
135                     logger.debug("Following exception occured:", ex);
136                 }
137             }
138 
139             if (authHandler == null) {
140                 authHandler = HttpAuthenticationMethods.NO_AUTH
141                         .getNewHandler(proxyIoSession);
142             }
143 
144         } else {
145             for (HttpAuthenticationMethods method : proxyIoSession
146                     .getPreferedOrder()) {
147                 if (authHandler != null) {
148                     break;
149                 }
150 
151                 if (method == HttpAuthenticationMethods.NO_AUTH) {
152                     authHandler = HttpAuthenticationMethods.NO_AUTH
153                             .getNewHandler(proxyIoSession);
154                     break;
155                 }
156 
157                 for (String proxyAuthHeader : values) {
158                     proxyAuthHeader = proxyAuthHeader.toLowerCase();
159 
160                     try {
161                         // test which auth mechanism to use
162                         if (proxyAuthHeader.contains("basic")
163                                 && method == HttpAuthenticationMethods.BASIC) {
164                             authHandler = HttpAuthenticationMethods.BASIC
165                                     .getNewHandler(proxyIoSession);
166                             break;
167                         } else if (proxyAuthHeader.contains("digest")
168                                 && method == HttpAuthenticationMethods.DIGEST) {
169                             authHandler = HttpAuthenticationMethods.DIGEST
170                                     .getNewHandler(proxyIoSession);
171                             break;
172                         } else if (proxyAuthHeader.contains("ntlm")
173                                 && method == HttpAuthenticationMethods.NTLM) {
174                             authHandler = HttpAuthenticationMethods.NTLM
175                                     .getNewHandler(proxyIoSession);
176                             break;
177                         }
178                     } catch (Exception ex) {
179                         logger.debug("Following exception occured:", ex);
180                     }
181                 }
182             }
183 
184         }
185 
186         if (authHandler == null) {
187             throw new ProxyAuthException(
188                     "Unknown authentication mechanism(s): " + values);
189         }
190     }
191 
192     /**
193      * Handle a HTTP response from the proxy server.
194      * 
195      * @param response The proxy response.
196      */
197     @Override
198     public void handleResponse(final HttpProxyResponse response)
199             throws ProxyAuthException {
200         if (!isHandshakeComplete()
201                 && ("close".equalsIgnoreCase(StringUtilities
202                         .getSingleValuedHeader(response.getHeaders(),
203                                 "Proxy-Connection")) || "close"
204                         .equalsIgnoreCase(StringUtilities
205                                 .getSingleValuedHeader(response.getHeaders(),
206                                         "Connection")))) {
207             getProxyIoSession().setReconnectionNeeded(true);
208         }
209 
210         if (response.getStatusCode() == 407) {
211             if (authHandler == null) {
212                 autoSelectAuthHandler(response);
213             }
214             authHandler.handleResponse(response);
215         } else {
216             throw new ProxyAuthException("Error: unexpected response code "
217                     + response.getStatusLine() + " received from proxy.");
218         }
219     }
220 }