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, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.yarn.server.resourcemanager.webapp; 20 21 import java.io.IOException; 22 import java.io.PrintWriter; 23 import java.net.InetSocketAddress; 24 import java.net.URI; 25 import java.net.URISyntaxException; 26 import java.util.Random; 27 import java.util.Set; 28 29 import javax.inject.Inject; 30 import javax.inject.Singleton; 31 import javax.servlet.FilterChain; 32 import javax.servlet.ServletException; 33 import javax.servlet.http.HttpServletRequest; 34 import javax.servlet.http.HttpServletResponse; 35 36 import org.apache.hadoop.conf.Configuration; 37 import org.apache.hadoop.http.HtmlQuoting; 38 import org.apache.hadoop.yarn.conf.YarnConfiguration; 39 import org.apache.hadoop.yarn.server.webproxy.ProxyUriUtils; 40 import org.apache.hadoop.yarn.webapp.YarnWebParams; 41 42 import com.google.common.collect.Sets; 43 import com.google.inject.Injector; 44 import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; 45 46 @Singleton 47 public class RMWebAppFilter extends GuiceContainer { 48 49 private Injector injector; 50 /** 51 * 52 */ 53 private static final long serialVersionUID = 1L; 54 55 // define a set of URIs which do not need to do redirection 56 private static final Set<String> NON_REDIRECTED_URIS = Sets.newHashSet( 57 "/conf", "/stacks", "/logLevel", "/logs"); 58 private String path; 59 private static final int BASIC_SLEEP_TIME = 5; 60 private static final int MAX_SLEEP_TIME = 5 * 60; 61 private static final Random randnum = new Random(); 62 63 @Inject RMWebAppFilter(Injector injector, Configuration conf)64 public RMWebAppFilter(Injector injector, Configuration conf) { 65 super(injector); 66 this.injector=injector; 67 InetSocketAddress sock = YarnConfiguration.useHttps(conf) 68 ? conf.getSocketAddr(YarnConfiguration.RM_WEBAPP_HTTPS_ADDRESS, 69 YarnConfiguration.DEFAULT_RM_WEBAPP_HTTPS_ADDRESS, 70 YarnConfiguration.DEFAULT_RM_WEBAPP_HTTPS_PORT) 71 : conf.getSocketAddr(YarnConfiguration.RM_WEBAPP_ADDRESS, 72 YarnConfiguration.DEFAULT_RM_WEBAPP_ADDRESS, 73 YarnConfiguration.DEFAULT_RM_WEBAPP_PORT); 74 75 path = sock.getHostName() + ":" + Integer.toString(sock.getPort()); 76 path = YarnConfiguration.useHttps(conf) 77 ? "https://" + path 78 : "http://" + path; 79 } 80 81 @Override doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)82 public void doFilter(HttpServletRequest request, 83 HttpServletResponse response, FilterChain chain) throws IOException, 84 ServletException { 85 response.setCharacterEncoding("UTF-8"); 86 String uri = HtmlQuoting.quoteHtmlChars(request.getRequestURI()); 87 88 if (uri == null) { 89 uri = "/"; 90 } 91 RMWebApp rmWebApp = injector.getInstance(RMWebApp.class); 92 rmWebApp.checkIfStandbyRM(); 93 if (rmWebApp.isStandby() 94 && shouldRedirect(rmWebApp, uri)) { 95 96 String redirectPath = rmWebApp.getRedirectPath(); 97 98 if (redirectPath != null && !redirectPath.isEmpty()) { 99 redirectPath += uri; 100 String redirectMsg = 101 "This is standby RM. The redirect url is: " + redirectPath; 102 PrintWriter out = response.getWriter(); 103 out.println(redirectMsg); 104 response.setHeader("Location", redirectPath); 105 response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); 106 return; 107 } else { 108 boolean doRetry = true; 109 String retryIntervalStr = 110 request.getParameter(YarnWebParams.NEXT_REFRESH_INTERVAL); 111 int retryInterval = 0; 112 if (retryIntervalStr != null) { 113 try { 114 retryInterval = Integer.parseInt(retryIntervalStr.trim()); 115 } catch (NumberFormatException ex) { 116 doRetry = false; 117 } 118 } 119 int next = calculateExponentialTime(retryInterval); 120 121 String redirectUrl = 122 appendOrReplaceParamter(path + uri, 123 YarnWebParams.NEXT_REFRESH_INTERVAL + "=" + (retryInterval + 1)); 124 if (redirectUrl == null || next > MAX_SLEEP_TIME) { 125 doRetry = false; 126 } 127 String redirectMsg = 128 doRetry ? "Can not find any active RM. Will retry in next " + next 129 + " seconds." : "There is no active RM right now."; 130 redirectMsg += "\nHA Zookeeper Connection State: " 131 + rmWebApp.getHAZookeeperConnectionState(); 132 PrintWriter out = response.getWriter(); 133 out.println(redirectMsg); 134 if (doRetry) { 135 response.setHeader("Refresh", next + ";url=" + redirectUrl); 136 response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); 137 } 138 } 139 return; 140 } 141 142 super.doFilter(request, response, chain); 143 } 144 shouldRedirect(RMWebApp rmWebApp, String uri)145 private boolean shouldRedirect(RMWebApp rmWebApp, String uri) { 146 return !uri.equals("/" + rmWebApp.wsName() + "/v1/cluster/info") 147 && !uri.equals("/" + rmWebApp.name() + "/cluster") 148 && !uri.startsWith(ProxyUriUtils.PROXY_BASE) 149 && !NON_REDIRECTED_URIS.contains(uri); 150 } 151 appendOrReplaceParamter(String uri, String newQuery)152 private String appendOrReplaceParamter(String uri, String newQuery) { 153 if (uri.contains(YarnWebParams.NEXT_REFRESH_INTERVAL + "=")) { 154 return uri.replaceAll(YarnWebParams.NEXT_REFRESH_INTERVAL + "=[^&]+", 155 newQuery); 156 } 157 try { 158 URI oldUri = new URI(uri); 159 String appendQuery = oldUri.getQuery(); 160 if (appendQuery == null) { 161 appendQuery = newQuery; 162 } else { 163 appendQuery += "&" + newQuery; 164 } 165 166 URI newUri = 167 new URI(oldUri.getScheme(), oldUri.getAuthority(), oldUri.getPath(), 168 appendQuery, oldUri.getFragment()); 169 170 return newUri.toString(); 171 } catch (URISyntaxException e) { 172 return null; 173 } 174 } 175 calculateExponentialTime(int retries)176 private static int calculateExponentialTime(int retries) { 177 long baseTime = BASIC_SLEEP_TIME * (1L << retries); 178 return (int) (baseTime * (randnum.nextDouble() + 0.5)); 179 } 180 }