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