blob: 2cc135142e66cfe19479e87ad8c0419ca73bedce [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;
23
Shawn Pearce10e68e62016-01-02 09:37:58 -080024import com.google.common.base.Optional;
Dave Borowitz9de65952012-08-13 16:09:45 -070025import 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;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070029import com.google.template.soy.data.SoyData;
Dave Borowitz9de65952012-08-13 16:09:45 -070030import com.google.template.soy.data.SoyListData;
31import com.google.template.soy.data.SoyMapData;
32
33import org.eclipse.jgit.errors.RepositoryNotFoundException;
34import org.eclipse.jgit.transport.ServiceMayNotContinueException;
35import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
36import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
40import java.io.IOException;
Dave Borowitz673d1982014-05-02 12:30:49 -070041import java.io.Writer;
Dave Borowitz27058932014-12-03 15:44:46 -080042import java.util.Collections;
Shawn Pearcec709c4c2015-08-28 15:30:42 -070043import java.util.LinkedHashMap;
44import java.util.List;
Dave Borowitz9de65952012-08-13 16:09:45 -070045import java.util.Map;
46import java.util.Set;
47
Shawn Pearcec709c4c2015-08-28 15:30:42 -070048import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070049import javax.servlet.http.HttpServletRequest;
50import javax.servlet.http.HttpServletResponse;
51
52/** Serves the top level index page for a Gitiles host. */
53public class HostIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080054 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070055 private static final Logger log = LoggerFactory.getLogger(HostIndexServlet.class);
56
57 protected final GitilesUrls urls;
Dave Borowitz9de65952012-08-13 16:09:45 -070058
Dave Borowitz8d6d6872014-03-16 15:18:14 -070059 public HostIndexServlet(GitilesAccess.Factory accessFactory, Renderer renderer,
60 GitilesUrls urls) {
61 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070062 this.urls = checkNotNull(urls, "urls");
Dave Borowitz9de65952012-08-13 16:09:45 -070063 }
64
Shawn Pearcec709c4c2015-08-28 15:30:42 -070065 private Map<String, RepositoryDescription> list(
66 HttpServletRequest req, HttpServletResponse res, String prefix,
67 Set<String> branches) throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070068 Map<String, RepositoryDescription> descs;
69 try {
Shawn Pearcec709c4c2015-08-28 15:30:42 -070070 descs = getAccess(req).listRepositories(prefix, branches);
Dave Borowitz9de65952012-08-13 16:09:45 -070071 } catch (RepositoryNotFoundException e) {
72 res.sendError(SC_NOT_FOUND);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080073 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070074 } catch (ServiceNotEnabledException e) {
75 res.sendError(SC_FORBIDDEN);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080076 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070077 } catch (ServiceNotAuthorizedException e) {
78 res.sendError(SC_UNAUTHORIZED);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080079 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070080 } catch (ServiceMayNotContinueException e) {
81 // TODO(dborowitz): Show the error message to the user.
82 res.sendError(SC_FORBIDDEN);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080083 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070084 } catch (IOException err) {
85 String name = urls.getHostName(req);
Jonathan Niederc0aeff12013-05-06 11:00:47 -070086 log.warn("Cannot scan repositories" + (name != null ? " for " + name : ""), err);
Dave Borowitz9de65952012-08-13 16:09:45 -070087 res.sendError(SC_SERVICE_UNAVAILABLE);
Dave Borowitzb1c628f2013-01-11 11:28:20 -080088 return null;
Dave Borowitz9de65952012-08-13 16:09:45 -070089 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -070090 if (prefix != null && descs.isEmpty()) {
91 res.sendError(SC_NOT_FOUND);
92 return null;
93 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -080094 return descs;
Dave Borowitz9de65952012-08-13 16:09:45 -070095 }
96
Shawn Pearcec709c4c2015-08-28 15:30:42 -070097 private SoyMapData toSoyMapData(RepositoryDescription desc,
98 @Nullable String prefix, GitilesView view) {
Dave Borowitz9de65952012-08-13 16:09:45 -070099 return new SoyMapData(
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700100 "name", stripPrefix(prefix, desc.name),
Dave Borowitz9de65952012-08-13 16:09:45 -0700101 "description", Strings.nullToEmpty(desc.description),
102 "url", GitilesView.repositoryIndex()
103 .copyFrom(view)
104 .setRepositoryName(desc.name)
105 .toUrl());
106 }
107
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800108 @Override
Shawn Pearce10e68e62016-01-02 09:37:58 -0800109 protected void doHead(HttpServletRequest req, HttpServletResponse res)
110 throws IOException {
111 Optional<FormatType> format = getFormat(req);
112 if (!format.isPresent()) {
113 res.sendError(SC_BAD_REQUEST);
114 return;
115 }
116
117 GitilesView view = ViewFilter.getView(req);
118 String prefix = view.getRepositoryPrefix();
119 if (prefix != null) {
120 Map<String, RepositoryDescription> descs =
121 list(req, res, prefix, Collections.<String> emptySet());
122 if (descs == null) {
123 return;
124 }
125 }
126 switch (format.get()) {
127 case HTML:
128 case JSON:
129 case TEXT:
130 res.setStatus(HttpServletResponse.SC_OK);
131 res.setContentType(format.get().getMimeType());
132 break;
133 default:
134 res.sendError(SC_BAD_REQUEST);
135 break;
136 }
137 }
138
139 @Override
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800140 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700141 GitilesView view = ViewFilter.getView(req);
142 String prefix = view.getRepositoryPrefix();
143 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800144 if (descs == null) {
145 return;
146 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700147
Dave Borowitz9de65952012-08-13 16:09:45 -0700148 SoyListData repos = new SoyListData();
149 for (RepositoryDescription desc : descs.values()) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900150 if (prefix == null || desc.name.startsWith(prefix)) {
151 repos.add(toSoyMapData(desc, prefix, view));
152 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700153 }
154
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700155 String hostName = urls.getHostName(req);
156 List<Map<String, String>> breadcrumbs = null;
157 if (prefix != null) {
158 hostName = hostName + '/' + prefix;
159 breadcrumbs = view.getBreadcrumbs();
160 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800161 renderHtml(req, res, "gitiles.hostIndex", ImmutableMap.of(
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700162 "hostName", hostName,
163 "breadcrumbs", SoyData.createFromExistingData(breadcrumbs),
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700164 "prefix", prefix != null ? prefix + '/' : "",
Dave Borowitz9de65952012-08-13 16:09:45 -0700165 "repositories", repos));
166 }
167
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800168 @Override
169 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700170 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800171 Set<String> branches = parseShowBranch(req);
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700172 Map<String, RepositoryDescription> descs = list(req, res, prefix, branches);
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800173 if (descs == null) {
174 return;
175 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700176
Dave Borowitz673d1982014-05-02 12:30:49 -0700177 Writer writer = startRenderText(req, res);
Dave Borowitz9de65952012-08-13 16:09:45 -0700178 for (RepositoryDescription repo : descs.values()) {
179 for (String name : branches) {
180 String ref = repo.branches.get(name);
181 if (ref == null) {
182 // Print stub (forty '-' symbols)
183 ref = "----------------------------------------";
184 }
Dave Borowitz673d1982014-05-02 12:30:49 -0700185 writer.write(ref);
186 writer.write(' ');
Dave Borowitz9de65952012-08-13 16:09:45 -0700187 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700188 writer.write(GitilesUrls.NAME_ESCAPER.apply(stripPrefix(prefix, repo.name)));
Dave Borowitz673d1982014-05-02 12:30:49 -0700189 writer.write('\n');
Dave Borowitz9de65952012-08-13 16:09:45 -0700190 }
191 writer.flush();
192 writer.close();
193 }
194
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800195 @Override
196 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700197 String prefix = ViewFilter.getView(req).getRepositoryPrefix();
198 Map<String, RepositoryDescription> descs = list(req, res, prefix, parseShowBranch(req));
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800199 if (descs == null) {
200 return;
201 }
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700202 if (prefix != null) {
203 Map<String, RepositoryDescription> r = new LinkedHashMap<>();
204 for (Map.Entry<String, RepositoryDescription> e : descs.entrySet()) {
205 r.put(stripPrefix(prefix, e.getKey()), e.getValue());
206 }
207 descs = r;
208 }
Dave Borowitzb1c628f2013-01-11 11:28:20 -0800209 renderJson(req, res, descs, new TypeToken<Map<String, RepositoryDescription>>() {}.getType());
Dave Borowitz9de65952012-08-13 16:09:45 -0700210 }
211
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700212 private static String stripPrefix(@Nullable String prefix, String name) {
David Pursehouse969bfc82015-12-08 15:11:17 +0900213 if (prefix != null && name.startsWith(prefix)) {
214 return name.substring(prefix.length() + 1);
215 }
216 return name;
Shawn Pearcec709c4c2015-08-28 15:30:42 -0700217 }
218
Dave Borowitz9de65952012-08-13 16:09:45 -0700219 private static Set<String> parseShowBranch(HttpServletRequest req) {
220 // Roughly match Gerrit Code Review's /projects/ API by supporting
221 // both show-branch and b as query parameters.
222 Set<String> branches = Sets.newLinkedHashSet();
223 String[] values = req.getParameterValues("show-branch");
224 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800225 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700226 }
227 values = req.getParameterValues("b");
228 if (values != null) {
Dave Borowitz27058932014-12-03 15:44:46 -0800229 Collections.addAll(branches, values);
Dave Borowitz9de65952012-08-13 16:09:45 -0700230 }
231 return branches;
232 }
233}