/* * Copyright 2016 ThoughtWorks, Inc. * * Licensed 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 com.thoughtworks.go.server.web; import com.google.gson.JsonObject; import com.thoughtworks.go.server.service.BackupService; import com.thoughtworks.go.util.FileUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import org.springframework.web.util.HtmlUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @understands redirecting all requests to a service unavailable page when the server is being backed up. */ public class BackupFilter implements Filter { public static final String JSON = "json"; public static final String XML = "xml"; @Autowired private BackupService backupService; private final static Logger LOGGER = LoggerFactory.getLogger(BackupFilter.class); // For the Test public static final Pattern PATTERN = Pattern.compile("^((.*/api/.*)|(.*[^/]+\\.(xml|json)(\\?.*)?))$"); // For the Test protected BackupFilter(BackupService backupService) { this.backupService = backupService; } public BackupFilter() { } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (backupService == null) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); } String url = ((HttpServletRequest) request).getRequestURI(); if (isBackupFinishJsonUrl(url)) { ((HttpServletResponse) response).setHeader("Cache-Control", "private, max-age=0, no-cache"); ((HttpServletResponse) response).setDateHeader("Expires", 0); generateResponseForIsBackupFinishedAPI(response); return; } if (backupService.isBackingUp()) { ((HttpServletResponse) response).setHeader("Cache-Control", "private, max-age=0, no-cache"); ((HttpServletResponse) response).setDateHeader("Expires", 0); if (isAPIUrl(url) && !isMessagesJson(url)) { generateAPIResponse(request, response); } else { generateHTMLResponse(response); } } else { chain.doFilter(request, response); } } private void generateHTMLResponse(ServletResponse response) { String path = "backup_in_progress.html"; InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(path); response.setContentType("text/html"); try { String content = FileUtil.readToEnd(resourceAsStream); content = replaceStringLiterals(content); response.getWriter().print(content); resourceAsStream.close(); } catch (IOException e) { LOGGER.error(String.format("General IOException: %s", e.getMessage())); } } String replaceStringLiterals(String content) { content = content.replaceAll("%backup_initiated_by%", HtmlUtils.htmlEscape(backupService.backupRunningSinceISO8601())); content = content.replaceAll("%backup_started_by%", HtmlUtils.htmlEscape(backupService.backupStartedBy())); return content; } private void generateAPIResponse(ServletRequest request, ServletResponse response) { try { HttpServletRequest httpRequest = (HttpServletRequest) request; String message = "Server is under maintenance mode, please try later."; if (requestIsOfType(JSON, httpRequest)) { response.setContentType("application/json"); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("message", message); response.getWriter().print(jsonObject); } else if (requestIsOfType(XML, httpRequest)) { response.setContentType("application/xml"); String xml = String.format("<message> %s </message>", message); response.getWriter().print(xml); } else { generateHTMLResponse(response); } } catch (IOException e) { LOGGER.error(String.format("General IOException: %s", e.getMessage())); } ((HttpServletResponse) response).setStatus(503); } private boolean requestIsOfType(String type, HttpServletRequest request) { String header = request.getHeader("Accept"); String contentType = request.getContentType(); String url = request.getRequestURI(); return header != null && header.contains(type) || url != null && url.endsWith(type) || contentType != null && contentType.contains(type); } private boolean isBackupFinishJsonUrl(String url) { return "/go/is_backup_finished.json".equals(url); } private void generateResponseForIsBackupFinishedAPI(ServletResponse response) { response.setContentType("application/json"); JsonObject json = new JsonObject(); json.addProperty("is_backing_up", backupService.isBackingUp()); try { response.getWriter().print(json); } catch (IOException e) { LOGGER.error(String.format("General IOException: %s", e.getMessage())); } } private boolean isAPIUrl(String url) { Matcher matcher = PATTERN.matcher(url); return matcher.matches(); } private boolean isMessagesJson(String url) { return "/go/server/messages.json".equals(url); } @Override public void destroy() { } }