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, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.http; 019 020import java.io.File; 021import java.io.IOException; 022import java.util.regex.Pattern; 023import javax.servlet.ServletException; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpServletResponse; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.DefaultServlet; 031 032/** 033 * Servlet to serve files generated by {@link ProfileServlet} 034 */ 035@InterfaceAudience.Private 036public class ProfileOutputServlet extends DefaultServlet { 037 private static final long serialVersionUID = 1L; 038 private static final Logger LOG = LoggerFactory.getLogger(ProfileOutputServlet.class); 039 private static final int REFRESH_PERIOD = 2; 040 // Alphanumeric characters, plus percent (url-encoding), equals, ampersand, dot and hyphen 041 private static final Pattern ALPHA_NUMERIC = Pattern.compile("[a-zA-Z0-9%=&.\\-]*"); 042 043 @Override 044 protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) 045 throws ServletException, IOException { 046 String absoluteDiskPath = getServletContext().getRealPath(req.getPathInfo()); 047 File requestedFile = new File(absoluteDiskPath); 048 // async-profiler version 1.4 writes 'Started [cpu] profiling' to output file when profiler is 049 // running which gets replaced by final output. If final output is not ready yet, the file size 050 // will be <100 bytes (in all modes). 051 if (requestedFile.length() < 100) { 052 LOG.info(requestedFile + " is incomplete. Sending auto-refresh header."); 053 String refreshUrl = req.getRequestURI(); 054 // Rebuild the query string (if we have one) 055 if (req.getQueryString() != null) { 056 refreshUrl += "?" + sanitize(req.getQueryString()); 057 } 058 ProfileServlet.setResponseHeader(resp); 059 resp.setHeader("Refresh", REFRESH_PERIOD + ";" + refreshUrl); 060 resp.getWriter().write("This page will be auto-refreshed every " + REFRESH_PERIOD 061 + " seconds until the output file is ready. Redirecting to " + refreshUrl); 062 } else { 063 super.doGet(req, resp); 064 } 065 } 066 067 static String sanitize(String input) { 068 // Basic test to try to avoid any XSS attacks or HTML content showing up. 069 // Duplicates HtmlQuoting a little, but avoid destroying ampersand. 070 if (ALPHA_NUMERIC.matcher(input).matches()) { 071 return input; 072 } 073 throw new RuntimeException("Non-alphanumeric data found in input, aborting."); 074 } 075}