Line data Source code
1 : // Copyright (C) 2009 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.httpd; 16 : 17 : import com.google.gerrit.common.Nullable; 18 : import com.google.gerrit.server.config.CanonicalWebUrl; 19 : import com.google.inject.Inject; 20 : import com.google.inject.Provider; 21 : import com.google.inject.Singleton; 22 : import com.google.inject.servlet.ServletModule; 23 : import java.io.IOException; 24 : import javax.servlet.Filter; 25 : import javax.servlet.FilterChain; 26 : import javax.servlet.FilterConfig; 27 : import javax.servlet.ServletException; 28 : import javax.servlet.ServletRequest; 29 : import javax.servlet.ServletResponse; 30 : import javax.servlet.http.HttpServletRequest; 31 : import javax.servlet.http.HttpServletResponse; 32 : 33 : /** Requires the connection to use SSL, redirects if not. */ 34 : @Singleton 35 : public class RequireSslFilter implements Filter { 36 : public static class RequireSslFilterModule extends ServletModule { 37 : private final boolean wantSsl; 38 : 39 : @Inject 40 99 : RequireSslFilterModule(@Nullable @CanonicalWebUrl String canonicalUrl) { 41 99 : this.wantSsl = canonicalUrl != null && canonicalUrl.startsWith("https:"); 42 99 : } 43 : 44 : @Override 45 : protected void configureServlets() { 46 99 : if (wantSsl) { 47 1 : filter("/*").through(RequireSslFilter.class); 48 : } 49 99 : } 50 : } 51 : 52 : private final Provider<String> urlProvider; 53 : 54 : @Inject 55 1 : RequireSslFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider) { 56 1 : this.urlProvider = urlProvider; 57 1 : } 58 : 59 : @Override 60 1 : public void init(FilterConfig filterConfig) {} 61 : 62 : @Override 63 1 : public void destroy() {} 64 : 65 : @Override 66 : public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 67 : throws IOException, ServletException { 68 1 : final HttpServletRequest req = (HttpServletRequest) request; 69 1 : final HttpServletResponse rsp = (HttpServletResponse) response; 70 : 71 1 : if (isSecure(req)) { 72 1 : chain.doFilter(request, response); 73 : 74 : } else { 75 : // If we wanted SSL, but the user didn't come to us over it, 76 : // force SSL by issuing a protocol redirect. Try to keep the 77 : // name "localhost" in case this is an SSH port tunnel. 78 : // 79 : final String url; 80 0 : if (isLocalHost(req)) { 81 0 : url = getLocalHostUrl(req); 82 : } else { 83 0 : url = urlProvider.get() + req.getServletPath(); 84 : } 85 0 : rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); 86 0 : rsp.setHeader("Location", url); 87 : } 88 1 : } 89 : 90 : @SuppressWarnings("JdkObsolete") 91 : private static String getLocalHostUrl(HttpServletRequest req) { 92 0 : StringBuffer b = req.getRequestURL(); 93 0 : b.replace(0, b.indexOf(":"), "https"); 94 0 : return b.toString(); 95 : } 96 : 97 : private static boolean isSecure(HttpServletRequest req) { 98 1 : return "https".equals(req.getScheme()) || req.isSecure(); 99 : } 100 : 101 : private static boolean isLocalHost(HttpServletRequest req) { 102 0 : return "localhost".equals(req.getServerName()) || "127.0.0.1".equals(req.getServerName()); 103 : } 104 : }