James Moger
2012-10-22 eba89539a29deba954035056437279088c3e047b
commit | author | age
11924d 1 /*
JM 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
18 import java.io.IOException;
19 import java.text.MessageFormat;
20 import java.text.ParseException;
21
22 import javax.servlet.ServletContext;
23 import javax.servlet.ServletException;
24 import javax.servlet.http.HttpServlet;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.eclipse.jgit.lib.Repository;
29 import org.eclipse.jgit.revwalk.RevCommit;
30 import org.eclipse.jgit.revwalk.RevTree;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.gitblit.models.RefModel;
35 import com.gitblit.utils.ArrayUtils;
36 import com.gitblit.utils.JGitUtils;
37 import com.gitblit.utils.MarkdownUtils;
38 import com.gitblit.utils.StringUtils;
39
40 /**
41  * Serves the content of a gh-pages branch.
42  * 
43  * @author James Moger
44  * 
45  */
46 public class PagesServlet extends HttpServlet {
47
48     private static final long serialVersionUID = 1L;
49
50     private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);
51
52     public PagesServlet() {
53         super();
54     }
55
56     /**
57      * Returns an url to this servlet for the specified parameters.
58      * 
59      * @param baseURL
60      * @param repository
61      * @param path
62      * @return an url
63      */
64     public static String asLink(String baseURL, String repository, String path) {
65         if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
66             baseURL = baseURL.substring(0, baseURL.length() - 1);
67         }
68         return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));
69     }
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
94         // determine repository and resource from url
95         String repository = "";
96         String resource = "";
97         Repository r = null;
98         int offset = 0;
99         while (r == null) {
100             int slash = path.indexOf('/', offset);
101             if (slash == -1) {
102                 repository = path;
103             } else {
104                 repository = path.substring(0, slash);
105             }
106             r = GitBlit.self().getRepository(repository, false);
107             offset = slash + 1;
108             if (offset > 0) {
109                 resource = path.substring(offset);
110             }
111             if (repository.equals(path)) {
112                 // either only repository in url or no repository found
113                 break;
114             }
115         }
116
117         ServletContext context = request.getSession().getServletContext();
118
119         try {
120             if (r == null) {
121                 // repository not found!
122                 String mkd = MessageFormat.format(
123                         "# Error\nSorry, no valid **repository** specified in this url: {0}!",
124                         repository);
125                 error(response, mkd);
126                 return;
127             }
128
129             // retrieve the content from the repository
130             RefModel pages = JGitUtils.getPagesBranch(r);
131             RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());
132
133             if (commit == null) {
134                 // branch not found!
135                 String mkd = MessageFormat.format(
136                         "# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",
137                         repository);
138                 error(response, mkd);
139                 r.close();
140                 return;
141             }
142             response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
143
ae9e15 144             String [] encodings = GitBlit.getEncodings();
JM 145
11924d 146             RevTree tree = commit.getTree();
JM 147             byte[] content = null;
148             if (StringUtils.isEmpty(resource)) {
149                 // find resource
150                 String[] files = { "index.html", "index.htm", "index.mkd" };
151                 for (String file : files) {
ae9e15 152                     content = JGitUtils.getStringContent(r, tree, file, encodings)
11924d 153                             .getBytes(Constants.ENCODING);
JM 154                     if (content != null) {
155                         resource = file;
156                         // assume text/html unless the servlet container
157                         // overrides
158                         response.setContentType("text/html; charset=" + Constants.ENCODING);
159                         break;
160                     }
161                 }
162             } else {
163                 // specific resource
d35134 164                 try {
JM 165                     String contentType = context.getMimeType(resource);
166                     if (contentType == null) {
167                         contentType = "text/plain";
168                     }
169                     if (contentType.startsWith("text")) {
ae9e15 170                         content = JGitUtils.getStringContent(r, tree, resource, encodings).getBytes(
d35134 171                                 Constants.ENCODING);
JM 172                     } else {
173                         content = JGitUtils.getByteContent(r, tree, resource);
174                     }
175                     response.setContentType(contentType);
176                 } catch (Exception e) {
11924d 177                 }
JM 178             }
179
180             // no content, try custom 404 page
181             if (ArrayUtils.isEmpty(content)) {
ae9e15 182                 String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);
d35134 183                 if (!StringUtils.isEmpty(custom404)) {
JM 184                     content = custom404.getBytes(Constants.ENCODING);
185                 }
186
11924d 187                 // still no content
JM 188                 if (ArrayUtils.isEmpty(content)) {
d35134 189                     String str = MessageFormat.format(
11924d 190                             "# Error\nSorry, the requested resource **{0}** was not found.",
d35134 191                             resource);
JM 192                     content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);
11924d 193                 }
d35134 194
JM 195                 try {
196                     // output the content
197                     logger.warn("Pages 404: " + resource);
198                     response.setStatus(HttpServletResponse.SC_NOT_FOUND);
199                     response.getOutputStream().write(content);
200                     response.flushBuffer();
201                 } catch (Throwable t) {
202                     logger.error("Failed to write page to client", t);
203                 }
204                 return;
11924d 205             }
JM 206
207             // check to see if we should transform markdown files
208             for (String ext : GitBlit.getStrings(Keys.web.markdownExtensions)) {
209                 if (resource.endsWith(ext)) {
210                     String mkd = new String(content, Constants.ENCODING);
211                     content = MarkdownUtils.transformMarkdown(mkd).getBytes(Constants.ENCODING);
212                     break;
213                 }
214             }
215
216             try {
217                 // output the content
218                 response.getOutputStream().write(content);
219                 response.flushBuffer();
220             } catch (Throwable t) {
221                 logger.error("Failed to write page to client", t);
222             }
223
224             // close the repository
225             r.close();
226         } catch (Throwable t) {
227             logger.error("Failed to write page to client", t);
228         }
229     }
230
231     private void error(HttpServletResponse response, String mkd) throws ServletException,
232             IOException, ParseException {
233         String content = MarkdownUtils.transformMarkdown(mkd);
234         response.setContentType("text/html; charset=" + Constants.ENCODING);
235         response.getWriter().write(content);
236     }
237
238     @Override
239     protected void doPost(HttpServletRequest request, HttpServletResponse response)
240             throws ServletException, IOException {
241         processRequest(request, response);
242     }
243
244     @Override
245     protected void doGet(HttpServletRequest request, HttpServletResponse response)
246             throws ServletException, IOException {
247         processRequest(request, response);
248     }
249 }