blob: d23aa3c48b9e71c10b7318c4e1dd488b036c5d8b [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
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
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Shawn Pearce10e68e62016-01-02 09:37:58 -080018import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
Dave Borowitz9de65952012-08-13 16:09:45 -070019import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
20import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
21import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
22import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
Masaya Suzukie2d24432017-08-25 14:39:43 -070023import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
Dave Borowitz9de65952012-08-13 16:09:45 -070024
25import com.google.common.base.Strings;
26import com.google.common.collect.ImmutableMap;
27import com.google.common.collect.Sets;
Dave Borowitz9de65952012-08-13 16:09:45 -070028import com.google.gson.reflect.TypeToken;
29import com.google.template.soy.data.SoyListData;
30import com.google.template.soy.data.SoyMapData;
David Pursehousef35b06c2016-08-20 11:10:42 +090031import com.google.template.soy.data.restricted.NullData;
Dave Borowitz9de65952012-08-13 16:09:45 -070032import java.io.IOException;
Dave Borowitz673d1982014-05-02 12:30:49 -070033import java.io.Writer;
Dave Borowitz27058932014-12-03 15:44:46 -080034import java.util.Collections;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070035import java.util.LinkedHashMap;
36import java.util.List;
Dave Borowitz9de65952012-08-13 16:09:45 -070037import java.util.Map;
David Pursehouse7a7f5472016-10-14 09:59:20 +090038import java.util.Optional;
Dave Borowitz9de65952012-08-13 16:09:45 -070039import java.util.Set;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070040import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070041import javax.servlet.http.HttpServletRequest;
42import javax.servlet.http.HttpServletResponse;
Dave Borowitz3b744b12016-08-19 16:11:10 -040043import org.eclipse.jgit.errors.RepositoryNotFoundException;
44import org.eclipse.jgit.transport.ServiceMayNotContinueException;
45import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
46import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
47import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
Dave Borowitz9de65952012-08-13 16:09:45 -070049
50/** Serves the top level index page for a Gitiles host. */
51public class HostIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080052 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070053 private static final Logger log = LoggerFactory.getLogger(HostIndexServlet.class);
54
55 protected final GitilesUrls urls;
Dave Borowitz9de65952012-08-13 16:09:45 -070056
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020057 public HostIndexServlet(
58 GitilesAccess.Factory accessFactory, Renderer renderer, GitilesUrls urls) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070059 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070060 this.urls = checkNotNull(urls, "urls");
Dave Borowitz9de65952012-08-13 16:09:45 -070061 }
62
Shawn Pearcec709c4c2015-08-28 15:30:42 -070063 private Map<String, RepositoryDescription> list(
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020064 HttpServletRequest req, HttpServletResponse res, String prefix, Set<String> branches)
65 throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070066 Map<String, RepositoryDescription> descs;
67 try {
Shawn Pearcec709c4c2015-08-28 15:30:42 -070068 descs = getAccess(req).listRepositories(prefix, branches);
Dave Borowitz9de65952012-08-13 16:09:45 -070069 } catch (RepositoryNotFoundException e) {
70 res.sendError(SC_NOT_FOUND);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080071 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070072 } catch (ServiceNotEnabledException e) {
73 res.sendError(SC_FORBIDDEN);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080074 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070075 } catch (ServiceNotAuthorizedException e) {
76 res.sendError(SC_UNAUTHORIZED);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080077 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070078 } catch (ServiceMayNotContinueException e) {
Masaya Suzukie2d24432017-08-25 14:39:43 -070079 sendError(req, res, e.getStatusCode(), e.getMessage());
Dave Borowitzb1c628f2013-01-11 11:28:20 -080080 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070081 } catch (IOException err) {
82 String name = urls.getHostName(req);
Jonathan Niederc0aeff12013-05-06 11:00:47 -070083 log.warn("Cannot scan repositories" + (name != null ? " for " + name : ""), err);
Dave Borowitz9de65952012-08-13 16:09:45 -070084 res.sendError(SC_SERVICE_UNAVAILABLE);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080085 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070086 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -070087 if (prefix != null && descs.isEmpty()) {
88 res.sendError(SC_NOT_FOUND);
89 return null;
90 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -080091 return descs;
Dave Borowitz9de65952012-08-13 16:09:45 -070092 }
93
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020094 private SoyMapData toSoyMapData(
95 RepositoryDescription desc, @Nullable String prefix, GitilesView view) {
Dave Borowitz9de65952012-08-13 16:09:45 -070096 return new SoyMapData(
Shawn Pearcec709c4c2015-08-28 15:30:42 -070097 "name", stripPrefix(prefix, desc.name),
Dave Borowitz9de65952012-08-13 16:09:45 -070098 "description", Strings.nullToEmpty(desc.description),
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020099 "url", GitilesView.repositoryIndex().copyFrom(view).setRepositoryName(desc.name).toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -0700100 }
101
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800102 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200103 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -0800104 Optional<FormatType> format = getFormat(req);
105 if (!format.isPresent()) {
106 res.sendError(SC_BAD_REQUEST);
107 return;
108 }
109
110 GitilesView view = ViewFilter.getView(req);
111 String prefix = view.getRepositoryPrefix();
112 if (prefix != null) {
113 Map<String, RepositoryDescription> descs =
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200114 list(req, res, prefix, Collections.<String>emptySet());
Shawn Pearce10e68e62016-01-02 09:37:58 -0800115 if (descs == null) {
116 return;
117 }
118 }
119 switch (format.get()) {
120 case HTML:
121 case JSON:
122 case TEXT:
123 res.setStatus(HttpServletResponse.SC_OK);
124 res.setContentType(format.get().getMimeType());
125 break;
David Pursehousecb91aaf2016-06-15 22:05:24 +0900126 case DEFAULT:
Shawn Pearce10e68e62016-01-02 09:37:58 -0800127 default:
128 res.sendError(SC_BAD_REQUEST);
129 break;
130 }
131 }
132
133 @Override
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800134 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700135 GitilesView view = ViewFilter.getView(req);
136 String prefix = view.getRepositoryPrefix();
137 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800138 if (descs == null) {
139 return;
140 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700141
Dave Borowitz9de65952012-08-13 16:09:45 -0700142 SoyListData repos = new SoyListData();
143 for (RepositoryDescription desc : descs.values()) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900144 if (prefix == null || desc.name.startsWith(prefix)) {
145 repos.add(toSoyMapData(desc, prefix, view));
146 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700147 }
148
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700149 String hostName = urls.getHostName(req);
150 List<Map<String, String>> breadcrumbs = null;
151 if (prefix != null) {
152 hostName = hostName + '/' + prefix;
153 breadcrumbs = view.getBreadcrumbs();
154 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200155 renderHtml(
156 req,
157 res,
158 "gitiles.hostIndex",
159 ImmutableMap.of(
160 "hostName",
161 hostName,
162 "breadcrumbs",
David Pursehousef35b06c2016-08-20 11:10:42 +0900163 breadcrumbs != null ? new SoyListData(breadcrumbs) : NullData.INSTANCE,
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200164 "prefix",
165 prefix != null ? prefix + '/' : "",
166 "repositories",
167 repos));
Dave Borowitz9de65952012-08-13 16:09:45 -0700168 }
169
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800170 @Override
171 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700172 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800173 Set<String> branches = parseShowBranch(req);
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700174 Map<String, RepositoryDescription> descs = list(req, res, prefix, branches);
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800175 if (descs == null) {
176 return;
177 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700178
David Pursehousec3e772a2016-06-15 21:49:35 +0900179 try (Writer writer = startRenderText(req, res)) {
180 for (RepositoryDescription repo : descs.values()) {
181 for (String name : branches) {
182 String ref = repo.branches.get(name);
183 if (ref == null) {
184 // Print stub (forty '-' symbols)
185 ref = "----------------------------------------";
186 }
187 writer.write(ref);
188 writer.write(' ');
Dave Borowitz9de65952012-08-13 16:09:45 -0700189 }
David Pursehousefa845722016-10-04 16:26:17 +0900190 writer.write(GitilesUrls.escapeName(stripPrefix(prefix, repo.name)));
David Pursehousec3e772a2016-06-15 21:49:35 +0900191 writer.write('\n');
Dave Borowitz9de65952012-08-13 16:09:45 -0700192 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700193 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700194 }
195
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800196 @Override
197 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700198 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
199 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800200 if (descs == null) {
201 return;
202 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700203 if (prefix != null) {
204 Map<String, RepositoryDescription> r = new LinkedHashMap<>();
205 for (Map.Entry<String, RepositoryDescription> e : descs.entrySet()) {
206 r.put(stripPrefix(prefix, e.getKey()), e.getValue());
207 }
208 descs = r;
209 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800210 renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
Dave Borowitz9de65952012-08-13 16:09:45 -0700211 }
212
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700213 private static String stripPrefix(@Nullable String prefix, String name) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900214 if (prefix != null && name.startsWith(prefix)) {
215 return name.substring(prefix.length() + 1);
216 }
217 return name;
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700218 }
219
Dave Borowitz9de65952012-08-13 16:09:45 -0700220 private static Set<String> parseShowBranch(HttpServletRequest req) {
221 // Roughly match Gerrit Code Review's /projects/ API by supporting
222 // both show-branch and b as query parameters.
223 Set<String> branches = Sets.newLinkedHashSet();
224 String[] values = req.getParameterValues("show-branch");
225 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800226 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700227 }
228 values = req.getParameterValues("b");
229 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800230 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700231 }
232 return branches;
233 }
234}