diff options
Diffstat (limited to 'gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java')
-rw-r--r-- | gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java new file mode 100644 index 0000000000..cc5ff425f2 --- /dev/null +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java @@ -0,0 +1,249 @@ +// Copyright (C) 2008 The Android Open Source Project +// +// 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.google.gerrit.httpd.raw; + +import com.google.gerrit.common.data.GerritConfig; +import com.google.gerrit.common.data.HostPageData; +import com.google.gerrit.httpd.HtmlDomUtil; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.config.CanonicalWebUrl; +import com.google.gerrit.server.config.Nullable; +import com.google.gerrit.server.config.SitePath; +import com.google.gwt.user.server.rpc.RPCServletUtils; +import com.google.gwtjsonrpc.server.JsonServlet; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.security.MessageDigest; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** Sends the Gerrit host page to clients. */ +@SuppressWarnings("serial") +@Singleton +public class HostPageServlet extends HttpServlet { + private final Provider<CurrentUser> currentUser; + private final File sitePath; + private final GerritConfig config; + private final Provider<String> urlProvider; + private final boolean wantSSL; + private final Document hostDoc; + + @Inject + HostPageServlet(final Provider<CurrentUser> cu, @SitePath final File path, + final GerritConfig gc, + @CanonicalWebUrl @Nullable final Provider<String> up, + @CanonicalWebUrl @Nullable final String configuredUrl, + final ServletContext servletContext) throws IOException { + currentUser = cu; + urlProvider = up; + sitePath = path; + config = gc; + wantSSL = configuredUrl != null && configuredUrl.startsWith("https:"); + + final String pageName = "HostPage.html"; + hostDoc = HtmlDomUtil.parseFile(getClass(), pageName); + if (hostDoc == null) { + throw new FileNotFoundException("No " + pageName + " in webapp"); + } + fixModuleReference(hostDoc, servletContext); + injectCssFile(hostDoc, "gerrit_sitecss", sitePath, "GerritSite.css"); + injectXmlFile(hostDoc, "gerrit_header", sitePath, "GerritSiteHeader.html"); + injectXmlFile(hostDoc, "gerrit_footer", sitePath, "GerritSiteFooter.html"); + } + + private void injectXmlFile(final Document hostDoc, final String id, + final File sitePath, final String fileName) throws IOException { + final Element banner = HtmlDomUtil.find(hostDoc, id); + if (banner == null) { + return; + } + + while (banner.getFirstChild() != null) { + banner.removeChild(banner.getFirstChild()); + } + + final Document html = HtmlDomUtil.parseFile(sitePath, fileName); + if (html == null) { + banner.getParentNode().removeChild(banner); + return; + } + + final Element content = html.getDocumentElement(); + banner.appendChild(hostDoc.importNode(content, true)); + } + + private void injectCssFile(final Document hostDoc, final String id, + final File sitePath, final String fileName) throws IOException { + final Element banner = HtmlDomUtil.find(hostDoc, id); + if (banner == null) { + return; + } + + while (banner.getFirstChild() != null) { + banner.removeChild(banner.getFirstChild()); + } + + final String css = HtmlDomUtil.readFile(sitePath, fileName); + if (css == null) { + banner.getParentNode().removeChild(banner); + return; + } + + banner.removeAttribute("id"); + banner.appendChild(hostDoc.createCDATASection("\n" + css + "\n")); + } + + private void injectJson(final Document hostDoc, final String id, + final Object obj) { + final Element scriptNode = HtmlDomUtil.find(hostDoc, id); + if (scriptNode == null) { + return; + } + + while (scriptNode.getFirstChild() != null) { + scriptNode.removeChild(scriptNode.getFirstChild()); + } + + if (obj == null) { + scriptNode.getParentNode().removeChild(scriptNode); + return; + } + + final StringWriter w = new StringWriter(); + w.write("<!--\n"); + w.write("var "); + w.write(id); + w.write("_obj="); + JsonServlet.defaultGsonBuilder().create().toJson(obj, w); + w.write(";\n// -->\n"); + scriptNode.removeAttribute("id"); + scriptNode.setAttribute("type", "text/javascript"); + scriptNode.setAttribute("language", "javascript"); + scriptNode.appendChild(hostDoc.createCDATASection(w.toString())); + } + + private void fixModuleReference(final Document hostDoc, + final ServletContext servletContext) throws IOException { + final Element scriptNode = HtmlDomUtil.find(hostDoc, "gerrit_module"); + if (scriptNode == null) { + throw new IOException("No gerrit_module to rewrite in host document"); + } + scriptNode.removeAttribute("id"); + + final String src = scriptNode.getAttribute("src"); + InputStream in = servletContext.getResourceAsStream("/" + src); + if (in == null) { + throw new IOException("No " + src + " in webapp root"); + } + + final MessageDigest md = Constants.newMessageDigest(); + try { + try { + final byte[] buf = new byte[1024]; + int n; + while ((n = in.read(buf)) > 0) { + md.update(buf, 0, n); + } + } finally { + in.close(); + } + } catch (IOException e) { + throw new IOException("Failed reading " + src, e); + } + + final String vstr = ObjectId.fromRaw(md.digest()).name(); + scriptNode.setAttribute("src", src + "?content=" + vstr); + } + + @Override + protected void doGet(final HttpServletRequest req, + final HttpServletResponse rsp) throws IOException { + // If we wanted SSL, but the user didn't come to us over an SSL channel, + // force it to be SSL by issuing a protocol redirect. Try to keep the + // name "localhost" in case this is an SSH port tunnel. + // + if (wantSSL && !isSecure(req)) { + final StringBuffer reqUrl = req.getRequestURL(); + if (isLocalHost(req)) { + reqUrl.replace(0, reqUrl.indexOf(":"), "https"); + } else { + reqUrl.setLength(0); + reqUrl.append(urlProvider.get()); + } + rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + rsp.setHeader("Location", reqUrl.toString()); + return; + } + + final HostPageData pageData = new HostPageData(); + pageData.config = config; + + final CurrentUser user = currentUser.get(); + if (user instanceof IdentifiedUser) { + pageData.userAccount = ((IdentifiedUser) user).getAccount(); + } + + final Document peruser = HtmlDomUtil.clone(hostDoc); + injectJson(peruser, "gerrit_hostpagedata", pageData); + + final byte[] raw = HtmlDomUtil.toUTF8(peruser); + final byte[] tosend; + if (RPCServletUtils.acceptsGzipEncoding(req)) { + rsp.setHeader("Content-Encoding", "gzip"); + tosend = HtmlDomUtil.compress(raw); + } else { + tosend = raw; + } + + rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT"); + rsp.setHeader("Pragma", "no-cache"); + rsp.setHeader("Cache-Control", "no-cache, must-revalidate"); + rsp.setContentType("text/html"); + rsp.setCharacterEncoding(HtmlDomUtil.ENC); + rsp.setContentLength(tosend.length); + final OutputStream out = rsp.getOutputStream(); + try { + out.write(tosend); + } finally { + out.close(); + } + } + + private static boolean isSecure(final HttpServletRequest req) { + return "https".equals(req.getScheme()) || req.isSecure(); + } + + private static boolean isLocalHost(final HttpServletRequest req) { + return "localhost".equals(req.getServerName()) + || "127.0.0.1".equals(req.getServerName()); + } +} |