/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.yarn.server.webproxy; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.util.TrackingUriPlugin; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.util.List; import static org.apache.hadoop.yarn.util.StringHelper.ujoin; public class ProxyUriUtils { @SuppressWarnings("unused") private static final Log LOG = LogFactory.getLog(ProxyUriUtils.class); /**Name of the servlet to use when registering the proxy servlet. */ public static final String PROXY_SERVLET_NAME = "proxy"; /**Base path where the proxy servlet will handle requests.*/ public static final String PROXY_BASE = "/proxy/"; /**Path Specification for the proxy servlet.*/ public static final String PROXY_PATH_SPEC = PROXY_BASE+"*"; /**Query Parameter indicating that the URI was approved.*/ public static final String PROXY_APPROVAL_PARAM = "proxyapproved"; private static String uriEncode(Object o) { try { assert (o != null) : "o canot be null"; return URLEncoder.encode(o.toString(), "UTF-8"); } catch (UnsupportedEncodingException e) { //This should never happen throw new RuntimeException("UTF-8 is not supported by this system?", e); } } /** * Get the proxied path for an application. * @param id the application id to use. * @return the base path to that application through the proxy. */ public static String getPath(ApplicationId id) { if(id == null) { throw new IllegalArgumentException("Application id cannot be null "); } return ujoin(PROXY_BASE, uriEncode(id)); } /** * Get the proxied path for an application. * @param id the application id to use. * @param path the rest of the path to the application. * @return the base path to that application through the proxy. */ public static String getPath(ApplicationId id, String path) { if(path == null) { return getPath(id); } else { return ujoin(getPath(id), path); } } /** * Get the proxied path for an application * @param id the id of the application * @param path the path of the application. * @param query the query parameters * @param approved true if the user has approved accessing this app. * @return the proxied path for this app. */ public static String getPathAndQuery(ApplicationId id, String path, String query, boolean approved) { StringBuilder newp = new StringBuilder(); newp.append(getPath(id, path)); boolean first = appendQuery(newp, query, true); if(approved) { appendQuery(newp, PROXY_APPROVAL_PARAM+"=true", first); } return newp.toString(); } private static boolean appendQuery(StringBuilder builder, String query, boolean first) { if(query != null && !query.isEmpty()) { if(first && !query.startsWith("?")) { builder.append('?'); } if(!first && !query.startsWith("&")) { builder.append('&'); } builder.append(query); return false; } return first; } /** * Get a proxied URI for the original URI. * @param originalUri the original URI to go through the proxy, or null if * a default path "/" can be used. * @param proxyUri the URI of the proxy itself, scheme, host and port are used. * @param id the id of the application * @return the proxied URI */ public static URI getProxyUri(URI originalUri, URI proxyUri, ApplicationId id) { try { String path = getPath(id, originalUri == null ? "/" : originalUri.getPath()); return new URI(proxyUri.getScheme(), proxyUri.getAuthority(), path, originalUri == null ? null : originalUri.getQuery(), originalUri == null ? null : originalUri.getFragment()); } catch (URISyntaxException e) { throw new RuntimeException("Could not proxify "+originalUri,e); } } /** * Create a URI form a no scheme Url, such as is returned by the AM. * @param url the URL format returned by an AM. This may or may not contain * scheme. * @return a URI with an http scheme * @throws URISyntaxException if the url is not formatted correctly. */ public static URI getUriFromAMUrl(String url) throws URISyntaxException { if (getSchemeFromUrl(url).isEmpty()) { /* * check is made to make sure if AM reports with scheme then it will be * used by default otherwise it will default to the one configured using * "yarn.http.policy". */ return new URI(HttpConfig.getSchemePrefix() + url); } else { return new URI(url); } } /** * Create a URI form a no scheme Url, such as is returned by the AM. * @param noSchemeUrl the URL formate returned by an AM * @return a URI with an http scheme * @throws URISyntaxException if the url is not formatted correctly. */ public static URI getUriFromAMUrl(String scheme, String noSchemeUrl) throws URISyntaxException { if (getSchemeFromUrl(noSchemeUrl).isEmpty()) { /* * check is made to make sure if AM reports with scheme then it will be * used by default otherwise it will default to the one configured using * "yarn.http.policy". */ return new URI(scheme + "://" + noSchemeUrl); } else { return new URI(noSchemeUrl); } } /** * Returns the first valid tracking link, if any, from the given id from the * given list of plug-ins, if any. * * @param id the id of the application for which the tracking link is desired * @param trackingUriPlugins list of plugins from which to get the tracking link * @return the desired link if possible, otherwise null * @throws URISyntaxException */ public static URI getUriFromTrackingPlugins(ApplicationId id, List<TrackingUriPlugin> trackingUriPlugins) throws URISyntaxException { URI toRet = null; for(TrackingUriPlugin plugin : trackingUriPlugins) { toRet = plugin.getTrackingUri(id); if (toRet != null) { return toRet; } } return null; } /** * Returns the scheme if present in the url * eg. "https://issues.apache.org/jira/browse/YARN" > "https" */ public static String getSchemeFromUrl(String url) { int index = 0; if (url != null) { index = url.indexOf("://"); } if (index > 0) { return url.substring(0, index); } else { return ""; } } }