You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PagesServlet.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. * Copyright 2012 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.servlet;
  17. import java.io.IOException;
  18. import java.text.MessageFormat;
  19. import java.text.ParseException;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Set;
  23. import java.util.TreeSet;
  24. import javax.servlet.ServletContext;
  25. import javax.servlet.ServletException;
  26. import javax.servlet.http.HttpServletRequest;
  27. import javax.servlet.http.HttpServletResponse;
  28. import org.eclipse.jgit.lib.FileMode;
  29. import org.eclipse.jgit.lib.Repository;
  30. import org.eclipse.jgit.revwalk.RevCommit;
  31. import org.eclipse.jgit.revwalk.RevTree;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;
  34. import com.gitblit.Constants;
  35. import com.gitblit.IStoredSettings;
  36. import com.gitblit.Keys;
  37. import com.gitblit.dagger.DaggerServlet;
  38. import com.gitblit.manager.IRepositoryManager;
  39. import com.gitblit.models.PathModel;
  40. import com.gitblit.models.RefModel;
  41. import com.gitblit.utils.ArrayUtils;
  42. import com.gitblit.utils.ByteFormat;
  43. import com.gitblit.utils.JGitUtils;
  44. import com.gitblit.utils.MarkdownUtils;
  45. import com.gitblit.utils.StringUtils;
  46. import com.gitblit.wicket.MarkupProcessor;
  47. import com.gitblit.wicket.MarkupProcessor.MarkupDocument;
  48. import dagger.ObjectGraph;
  49. /**
  50. * Serves the content of a gh-pages branch.
  51. *
  52. * @author James Moger
  53. *
  54. */
  55. public class PagesServlet extends DaggerServlet {
  56. private static final long serialVersionUID = 1L;
  57. private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);
  58. private IStoredSettings settings;
  59. private IRepositoryManager repositoryManager;
  60. @Override
  61. protected void inject(ObjectGraph dagger) {
  62. this.settings = dagger.get(IStoredSettings.class);
  63. this.repositoryManager = dagger.get(IRepositoryManager.class);
  64. }
  65. /**
  66. * Returns an url to this servlet for the specified parameters.
  67. *
  68. * @param baseURL
  69. * @param repository
  70. * @param path
  71. * @return an url
  72. */
  73. public static String asLink(String baseURL, String repository, String path) {
  74. if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
  75. baseURL = baseURL.substring(0, baseURL.length() - 1);
  76. }
  77. return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));
  78. }
  79. /**
  80. * Retrieves the specified resource from the gh-pages branch of the
  81. * repository.
  82. *
  83. * @param request
  84. * @param response
  85. * @throws javax.servlet.ServletException
  86. * @throws java.io.IOException
  87. */
  88. private void processRequest(HttpServletRequest request, HttpServletResponse response)
  89. throws ServletException, IOException {
  90. String path = request.getPathInfo();
  91. if (path.toLowerCase().endsWith(".git")) {
  92. // forward to url with trailing /
  93. // this is important for relative pages links
  94. response.sendRedirect(request.getServletPath() + path + "/");
  95. return;
  96. }
  97. if (path.charAt(0) == '/') {
  98. // strip leading /
  99. path = path.substring(1);
  100. }
  101. // determine repository and resource from url
  102. String repository = "";
  103. String resource = "";
  104. Repository r = null;
  105. int offset = 0;
  106. while (r == null) {
  107. int slash = path.indexOf('/', offset);
  108. if (slash == -1) {
  109. repository = path;
  110. } else {
  111. repository = path.substring(0, slash);
  112. }
  113. r = repositoryManager.getRepository(repository, false);
  114. offset = slash + 1;
  115. if (offset > 0) {
  116. resource = path.substring(offset);
  117. }
  118. if (repository.equals(path)) {
  119. // either only repository in url or no repository found
  120. break;
  121. }
  122. }
  123. ServletContext context = request.getSession().getServletContext();
  124. try {
  125. if (r == null) {
  126. // repository not found!
  127. String mkd = MessageFormat.format(
  128. "# Error\nSorry, no valid **repository** specified in this url: {0}!",
  129. repository);
  130. error(response, mkd);
  131. return;
  132. }
  133. // retrieve the content from the repository
  134. RefModel pages = JGitUtils.getPagesBranch(r);
  135. RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());
  136. if (commit == null) {
  137. // branch not found!
  138. String mkd = MessageFormat.format(
  139. "# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",
  140. repository);
  141. error(response, mkd);
  142. r.close();
  143. return;
  144. }
  145. MarkupProcessor processor = new MarkupProcessor(settings);
  146. String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);
  147. RevTree tree = commit.getTree();
  148. String res = resource;
  149. if (res.endsWith("/")) {
  150. res = res.substring(0, res.length() - 1);
  151. }
  152. Set<String> names = new TreeSet<String>();
  153. for (PathModel entry : JGitUtils.getFilesInPath(r, res, commit)) {
  154. names.add(entry.name);
  155. }
  156. byte[] content = null;
  157. if (names.isEmpty()) {
  158. // not a path, a specific resource
  159. try {
  160. String contentType = context.getMimeType(resource);
  161. if (contentType == null) {
  162. contentType = "text/plain";
  163. }
  164. if (contentType.startsWith("text")) {
  165. content = JGitUtils.getStringContent(r, tree, resource, encodings).getBytes(
  166. Constants.ENCODING);
  167. } else {
  168. content = JGitUtils.getByteContent(r, tree, resource, false);
  169. }
  170. response.setContentType(contentType);
  171. } catch (Exception e) {
  172. }
  173. } else {
  174. // path
  175. List<String> extensions = new ArrayList<String>();
  176. extensions.add("html");
  177. extensions.add("htm");
  178. extensions.addAll(processor.getMarkupExtensions());
  179. for (String ext : extensions) {
  180. String file = "index." + ext;
  181. if (names.contains(file)) {
  182. String stringContent = JGitUtils.getStringContent(r, tree, file, encodings);
  183. if (stringContent == null) {
  184. continue;
  185. }
  186. content = stringContent.getBytes(Constants.ENCODING);
  187. if (content != null) {
  188. resource = file;
  189. // assume text/html unless the servlet container
  190. // overrides
  191. response.setContentType("text/html; charset=" + Constants.ENCODING);
  192. break;
  193. }
  194. }
  195. }
  196. }
  197. // no content, try custom 404 page
  198. if (ArrayUtils.isEmpty(content)) {
  199. String ext = StringUtils.getFileExtension(resource);
  200. if (StringUtils.isEmpty(ext)) {
  201. // document list
  202. response.setContentType("text/html");
  203. response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");
  204. response.getWriter().append("<table>");
  205. response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");
  206. response.getWriter().append("</thead>");
  207. response.getWriter().append("<tbody>");
  208. String pattern = "<tr><td><a href=\"{0}\">{1}</a></td><td>{2}</td><td>{3}</td></tr>";
  209. final ByteFormat byteFormat = new ByteFormat();
  210. List<PathModel> entries = JGitUtils.getFilesInPath(r, resource, commit);
  211. if (!entries.isEmpty()) {
  212. if (entries.get(0).path.indexOf('/') > -1) {
  213. // we are in a subdirectory, add parent directory link
  214. entries.add(0, new PathModel("..", resource + "/..", 0, FileMode.TREE.getBits(), null, null));
  215. }
  216. }
  217. for (PathModel entry : entries) {
  218. response.getWriter().append(MessageFormat.format(pattern, entry.path, entry.name, JGitUtils.getPermissionsFromMode(entry.mode), byteFormat.format(entry.size)));
  219. }
  220. response.getWriter().append("</tbody>");
  221. response.getWriter().append("</table>");
  222. } else {
  223. // 404
  224. String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);
  225. if (!StringUtils.isEmpty(custom404)) {
  226. content = custom404.getBytes(Constants.ENCODING);
  227. }
  228. // still no content
  229. if (ArrayUtils.isEmpty(content)) {
  230. String str = MessageFormat.format(
  231. "# Error\nSorry, the requested resource **{0}** was not found.",
  232. resource);
  233. content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);
  234. }
  235. try {
  236. // output the content
  237. logger.warn("Pages 404: " + resource);
  238. response.setStatus(HttpServletResponse.SC_NOT_FOUND);
  239. response.getOutputStream().write(content);
  240. response.flushBuffer();
  241. } catch (Throwable t) {
  242. logger.error("Failed to write page to client", t);
  243. }
  244. }
  245. return;
  246. }
  247. // check to see if we should transform markup files
  248. String ext = StringUtils.getFileExtension(resource);
  249. if (processor.getMarkupExtensions().contains(ext)) {
  250. String markup = new String(content, Constants.ENCODING);
  251. MarkupDocument markupDoc = processor.parse(repository, commit.getName(), resource, markup);
  252. content = markupDoc.html.getBytes("UTF-8");
  253. response.setContentType("text/html; charset=" + Constants.ENCODING);
  254. }
  255. try {
  256. // output the content
  257. response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
  258. response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
  259. response.getOutputStream().write(content);
  260. response.flushBuffer();
  261. } catch (Throwable t) {
  262. logger.error("Failed to write page to client", t);
  263. }
  264. // close the repository
  265. r.close();
  266. } catch (Throwable t) {
  267. logger.error("Failed to write page to client", t);
  268. }
  269. }
  270. private void error(HttpServletResponse response, String mkd) throws ServletException,
  271. IOException, ParseException {
  272. String content = MarkdownUtils.transformMarkdown(mkd);
  273. response.setContentType("text/html; charset=" + Constants.ENCODING);
  274. response.getWriter().write(content);
  275. }
  276. @Override
  277. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  278. throws ServletException, IOException {
  279. processRequest(request, response);
  280. }
  281. @Override
  282. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  283. throws ServletException, IOException {
  284. processRequest(request, response);
  285. }
  286. }