blob: d0575125c452f709c73876fa65cc0004078d6d20 [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;
18
Dave Borowitz9de65952012-08-13 16:09:45 -070019import com.google.common.base.Strings;
Dave Borowitz5db0eed2012-12-28 15:08:39 -080020import com.google.common.collect.ImmutableList;
Shawn Pearce45e83752015-02-20 17:59:05 -080021import com.google.common.collect.ImmutableMap;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080022import com.google.common.collect.Maps;
Jakub Vrana9d219fb2019-04-02 14:26:05 +020023import com.google.common.html.types.SafeHtml;
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070024import com.google.gitiles.DateFormatter.Format;
Masaya Suzuki5cecb862019-03-25 17:35:44 -070025import com.google.gitiles.GitilesRequestFailureException.FailureReason;
Shawn Pearce47fd6562016-05-28 14:15:15 -070026import com.google.gitiles.doc.MarkdownConfig;
Kevin Graney77dbea92014-07-08 14:07:40 -040027import com.google.gson.reflect.TypeToken;
Dave Borowitz3b744b12016-08-19 16:11:10 -040028import java.io.IOException;
29import java.io.OutputStream;
30import java.io.Writer;
31import java.util.List;
32import java.util.Map;
David Pursehouse7a7f5472016-10-14 09:59:20 +090033import java.util.Optional;
Dave Borowitz3b744b12016-08-19 16:11:10 -040034import javax.servlet.http.HttpServletRequest;
35import javax.servlet.http.HttpServletResponse;
Shawn Pearce45e83752015-02-20 17:59:05 -080036import org.eclipse.jgit.errors.IncorrectObjectTypeException;
Dave Borowitz9de65952012-08-13 16:09:45 -070037import org.eclipse.jgit.http.server.ServletUtils;
Shawn Pearce45e83752015-02-20 17:59:05 -080038import org.eclipse.jgit.lib.Config;
Dave Borowitz9de65952012-08-13 16:09:45 -070039import org.eclipse.jgit.lib.Constants;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080040import org.eclipse.jgit.lib.ObjectId;
41import org.eclipse.jgit.lib.Repository;
42import org.eclipse.jgit.revwalk.RevCommit;
43import org.eclipse.jgit.revwalk.RevObject;
Shawn Pearce45e83752015-02-20 17:59:05 -080044import org.eclipse.jgit.revwalk.RevTree;
Dave Borowitz14ce8282012-12-20 14:08:25 -080045import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070046
Dave Borowitz9de65952012-08-13 16:09:45 -070047/** Serves the index page for a repository, if accessed directly by a browser. */
48public class RepositoryIndexServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080049 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070050
Dave Borowitz209d0aa2012-12-28 14:28:53 -080051 static final int REF_LIMIT = 10;
52 private static final int LOG_LIMIT = 20;
Shawn Pearce45e83752015-02-20 17:59:05 -080053 private static final int LOG_WITH_README_LIMIT = 5;
Dave Borowitz209d0aa2012-12-28 14:28:53 -080054
Dave Borowitz14ce8282012-12-20 14:08:25 -080055 private final TimeCache timeCache;
56
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020057 public RepositoryIndexServlet(
58 GitilesAccess.Factory accessFactory, Renderer renderer, TimeCache timeCache) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070059 super(renderer, accessFactory);
Dave Borowitz14ce8282012-12-20 14:08:25 -080060 this.timeCache = checkNotNull(timeCache, "timeCache");
Dave Borowitz9de65952012-08-13 16:09:45 -070061 }
62
63 @Override
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020064 protected void doHead(HttpServletRequest req, HttpServletResponse res) throws IOException {
Shawn Pearce10e68e62016-01-02 09:37:58 -080065 // If the repository didn't exist a prior filter would have 404 replied.
66 Optional<FormatType> format = getFormat(req);
67 if (!format.isPresent()) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -070068 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080069 }
70 switch (format.get()) {
71 case HTML:
72 case JSON:
73 res.setStatus(HttpServletResponse.SC_OK);
74 res.setContentType(format.get().getMimeType());
75 break;
76 case TEXT:
David Pursehousecb91aaf2016-06-15 22:05:24 +090077 case DEFAULT:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020078 default:
Masaya Suzuki5cecb862019-03-25 17:35:44 -070079 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_RESPONSE_FORMAT);
Shawn Pearce10e68e62016-01-02 09:37:58 -080080 }
81 }
82
83 @Override
Kevin Graney77dbea92014-07-08 14:07:40 -040084 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070085 GitilesView view = ViewFilter.getView(req);
86 Repository repo = ServletUtils.getRepository(req);
87 GitilesAccess access = getAccess(req);
88 RepositoryDescription desc = access.getRepositoryDescription();
89
Shawn Pearceb5ad0a02015-05-24 20:33:17 -070090 try (RevWalk walk = new RevWalk(repo)) {
91 Paginator paginator = null;
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -070092 Map<String, Object> data = Maps.newHashMapWithExpectedSize(7);
93 List<Map<String, Object>> tags = RefServlet.getTagsSoyData(req, timeCache, walk, REF_LIMIT);
94 ObjectId headId = repo.resolve(Constants.HEAD);
95 if (headId != null) {
96 RevObject head = walk.parseAny(headId);
Shawn Pearce45e83752015-02-20 17:59:05 -080097 int limit = LOG_LIMIT;
Shawn Pearcec68ad0b2016-05-28 16:52:47 -070098 Map<String, Object> readme = renderReadme(req, walk, view, access.getConfig(), head);
Shawn Pearce45e83752015-02-20 17:59:05 -080099 if (readme != null) {
100 data.putAll(readme);
101 limit = LOG_WITH_README_LIMIT;
102 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700103 // TODO(dborowitz): Handle non-commit or missing HEAD?
104 if (head.getType() == Constants.OBJ_COMMIT) {
105 walk.reset();
106 walk.markStart((RevCommit) head);
Shawn Pearce45e83752015-02-20 17:59:05 -0800107 paginator = new Paginator(walk, limit, null);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700108 }
109 }
110 if (!data.containsKey("entries")) {
111 data.put("entries", ImmutableList.of());
112 }
113 List<Map<String, Object>> branches = RefServlet.getBranchesSoyData(req, REF_LIMIT);
114
115 data.put("cloneUrl", desc.cloneUrl);
116 data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl));
117 data.put("description", Strings.nullToEmpty(desc.description));
118 data.put("branches", trim(branches));
119 if (branches.size() > REF_LIMIT) {
120 data.put("moreBranchesUrl", GitilesView.refs().copyFrom(view).toUrl());
121 }
122 data.put("tags", trim(tags));
123 data.put("hasLog", paginator != null);
124 if (tags.size() > REF_LIMIT) {
125 data.put("moreTagsUrl", GitilesView.refs().copyFrom(view).toUrl());
126 }
127 GitilesConfig.putVariant(getAccess(req).getConfig(), "logEntry", "logEntryVariant", data);
128
129 if (paginator != null) {
130 DateFormatter df = new DateFormatter(access, Format.DEFAULT);
Dave Borowitze360d5c2015-09-16 16:53:30 -0400131 try (OutputStream out =
132 startRenderStreamingHtml(req, res, "gitiles.repositoryIndex", data)) {
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700133 Writer w = newWriter(out, res);
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700134 new LogSoyData(req, access, "oneline")
Dave Borowitz571b7a02018-02-09 15:18:10 -0500135 .renderStreaming(
136 paginator, "HEAD", renderer, w, df, LogSoyData.FooterBehavior.LOG_HEAD);
Shawn Pearce4c2eb852014-08-26 15:35:33 -0700137 w.flush();
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700138 }
139 } else {
140 renderHtml(req, res, "gitiles.repositoryIndex", data);
141 }
Dave Borowitzf6dcf7a2014-07-30 10:26:58 -0700142 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700143 }
144
Kevin Graney77dbea92014-07-08 14:07:40 -0400145 @Override
146 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
147 GitilesAccess access = getAccess(req);
148 RepositoryDescription desc = access.getRepositoryDescription();
149 renderJson(req, res, desc, new TypeToken<RepositoryDescription>() {}.getType());
150 }
151
Dave Borowitz209d0aa2012-12-28 14:28:53 -0800152 private static <T> List<T> trim(List<T> list) {
153 return list.size() > REF_LIMIT ? list.subList(0, REF_LIMIT) : list;
Dave Borowitz14ce8282012-12-20 14:08:25 -0800154 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800155
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200156 private static Map<String, Object> renderReadme(
Shawn Pearcec68ad0b2016-05-28 16:52:47 -0700157 HttpServletRequest req, RevWalk walk, GitilesView view, Config cfg, RevObject head)
158 throws IOException {
Shawn Pearce45e83752015-02-20 17:59:05 -0800159 RevTree rootTree;
160 try {
161 rootTree = walk.parseTree(head);
162 } catch (IncorrectObjectTypeException notTreeish) {
163 return null;
164 }
165
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200166 ReadmeHelper readme =
167 new ReadmeHelper(
168 walk.getObjectReader(),
169 GitilesView.path().copyFrom(view).setRevision(Revision.HEAD).setPathPart("/").build(),
Shawn Pearce47fd6562016-05-28 14:15:15 -0700170 MarkdownConfig.get(cfg),
Shawn Pearcec68ad0b2016-05-28 16:52:47 -0700171 rootTree,
172 req.getRequestURI());
Shawn Pearce45e83752015-02-20 17:59:05 -0800173 readme.scanTree(rootTree);
174 if (readme.isPresent()) {
Jakub Vrana9d219fb2019-04-02 14:26:05 +0200175 SafeHtml html = readme.render();
Shawn Pearce08e38f22016-05-24 16:40:18 -0700176 if (html != null) {
177 return ImmutableMap.<String, Object>of("readmeHtml", html);
178 }
Shawn Pearce45e83752015-02-20 17:59:05 -0800179 }
180 return null;
181 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700182}