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 9.9KB

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