/* * 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.flink.runtime.webmonitor.handlers; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import org.apache.flink.configuration.ConfigConstants; import org.apache.flink.runtime.akka.AkkaUtils; import org.apache.flink.runtime.instance.ActorGateway; import org.apache.flink.runtime.webmonitor.files.MimeTypes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scala.Tuple2; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.apache.flink.util.Preconditions.checkNotNull; /** * Utilities to extract a redirect address. * * <p>This is necessary at the moment, because many execution graph structures are not serializable. * The proper solution here is to have these serializable and transparently work with the leading * job manager instead of redirecting. */ public class HandlerRedirectUtils { private static final Logger LOG = LoggerFactory.getLogger(HandlerRedirectUtils.class); /** Pattern to extract the host from an remote Akka URL */ private final static Pattern LeaderAddressHostPattern = Pattern.compile("^.+@(.+):([0-9]+)/user/.+$"); public static String getRedirectAddress( String localJobManagerAddress, Tuple2<ActorGateway, Integer> leader) throws Exception { final String leaderAddress = leader._1().path(); final int webMonitorPort = leader._2(); final String jobManagerName = localJobManagerAddress.substring(localJobManagerAddress.lastIndexOf("/") + 1); if (!localJobManagerAddress.equals(leaderAddress) && !leaderAddress.equals(AkkaUtils.getLocalAkkaURL(jobManagerName))) { // We are not the leader and need to redirect Matcher matcher = LeaderAddressHostPattern.matcher(leaderAddress); if (matcher.matches()) { String redirectAddress = String.format("%s:%d", matcher.group(1), webMonitorPort); return redirectAddress; } else { LOG.warn("Unexpected leader address pattern {}. Cannot extract host.", leaderAddress); } } return null; } public static HttpResponse getRedirectResponse(String redirectAddress, String path, boolean httpsEnabled) throws Exception { checkNotNull(redirectAddress, "Redirect address"); checkNotNull(path, "Path"); String protocol = httpsEnabled ? "https" : "http"; String newLocation = String.format("%s://%s%s", protocol, redirectAddress, path); HttpResponse redirectResponse = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.TEMPORARY_REDIRECT); redirectResponse.headers().set(HttpHeaders.Names.LOCATION, newLocation); redirectResponse.headers().set(HttpHeaders.Names.CONTENT_LENGTH, 0); return redirectResponse; } public static HttpResponse getUnavailableResponse() { String result = "Service temporarily unavailable due to an ongoing leader election. Please refresh."; byte[] bytes = result.getBytes(ConfigConstants.DEFAULT_CHARSET); HttpResponse unavailableResponse = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.SERVICE_UNAVAILABLE, Unpooled.wrappedBuffer(bytes)); unavailableResponse.headers().set(HttpHeaders.Names.CONTENT_LENGTH, bytes.length); unavailableResponse.headers().set(HttpHeaders.Names.CONTENT_TYPE, MimeTypes.getMimeTypeForExtension("txt")); return unavailableResponse; } }