001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.proxy.handlers.http; 021 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.mina.core.filterchain.IoFilter.NextFilter; 027import org.apache.mina.proxy.ProxyAuthException; 028import org.apache.mina.proxy.session.ProxyIoSession; 029import org.apache.mina.proxy.utils.StringUtilities; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * HttpSmartProxyHandler.java - HTTP proxy handler that automatically handles forwarding a request 035 * to the appropriate authentication mechanism logic handler. 036 * 037 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 038 * @since MINA 2.0.0-M3 039 */ 040public class HttpSmartProxyHandler extends AbstractHttpLogicHandler { 041 private final static Logger logger = LoggerFactory.getLogger(HttpSmartProxyHandler.class); 042 043 /** 044 * Has the HTTP proxy request been sent ? 045 */ 046 private boolean requestSent = false; 047 048 /** 049 * The automatically selected http authentication logic handler. 050 */ 051 private AbstractAuthLogicHandler authHandler; 052 053 public HttpSmartProxyHandler(final ProxyIoSession proxyIoSession) { 054 super(proxyIoSession); 055 } 056 057 /** 058 * Performs the handshake processing. 059 * 060 * @param nextFilter the next filter 061 */ 062 public void doHandshake(final NextFilter nextFilter) throws ProxyAuthException { 063 logger.debug(" doHandshake()"); 064 065 if (authHandler != null) { 066 authHandler.doHandshake(nextFilter); 067 } else { 068 if (requestSent) { 069 // Safety check 070 throw new ProxyAuthException("Authentication request already sent"); 071 } 072 073 logger.debug(" sending HTTP request"); 074 075 // Compute request headers 076 HttpProxyRequest req = (HttpProxyRequest) getProxyIoSession().getRequest(); 077 Map<String, List<String>> headers = req.getHeaders() != null ? req.getHeaders() 078 : new HashMap<String, List<String>>(); 079 080 AbstractAuthLogicHandler.addKeepAliveHeaders(headers); 081 req.setHeaders(headers); 082 083 // Write request to the proxy 084 writeRequest(nextFilter, req); 085 requestSent = true; 086 } 087 } 088 089 /** 090 * Automatic selection of the authentication algorithm. If <code>preferedOrder</code> is set then 091 * algorithms are selected from the list order otherwise the algorithm tries to select the most 092 * secured algorithm available first. 093 * 094 * @param response the proxy response 095 */ 096 private void autoSelectAuthHandler(final HttpProxyResponse response) throws ProxyAuthException { 097 // Get the Proxy-Authenticate header 098 List<String> values = response.getHeaders().get("Proxy-Authenticate"); 099 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}