Merge "Update bucklets"
diff --git a/Documentation/markdown.md b/Documentation/markdown.md
index bed3fe7..e1c6da3 100644
--- a/Documentation/markdown.md
+++ b/Documentation/markdown.md
@@ -768,6 +768,17 @@
imageLimit = 256K
```
+### Parsing timeout
+
+Parsing Markdown can be expensive so this implementation places
+a default upper bound of 2 seconds on running time per document.
+This is measured in wall clock time from the start of the request.
+
+```
+[markdown]
+ parseTimeout = 2s
+```
+
### Google Analytics
[Google Analytics](https://www.google.com/analytics/) can be
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
index 7013918..91795bb 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ReadmeHelper.java
@@ -31,6 +31,7 @@
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.RawParseUtils;
+import org.joda.time.Duration;
import org.pegdown.ast.RootNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -89,10 +90,13 @@
SanitizedContent render() {
try {
+ Duration parseTimeout = ConfigUtil.getDuration(cfg, "markdown", null,
+ "parseTimeout", Duration.standardSeconds(2));
int inputLimit = cfg.getInt("markdown", "inputLimit", 5 << 20);
byte[] raw = reader.open(readmeId, Constants.OBJ_BLOB).getCachedBytes(inputLimit);
String md = RawParseUtils.decode(raw);
- RootNode root = GitilesMarkdown.parseFile(view, readmePath, md);
+ RootNode root =
+ GitilesMarkdown.parseFile(parseTimeout, view, readmePath, md);
if (root == null) {
return null;
}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
index 7b7f974..59b8b5c 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/DocServlet.java
@@ -29,6 +29,7 @@
import com.google.common.hash.Hashing;
import com.google.common.net.HttpHeaders;
import com.google.gitiles.BaseServlet;
+import com.google.gitiles.ConfigUtil;
import com.google.gitiles.FormatType;
import com.google.gitiles.GitilesAccess;
import com.google.gitiles.GitilesView;
@@ -47,6 +48,7 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.RawParseUtils;
+import org.joda.time.Duration;
import org.pegdown.ast.RootNode;
import java.io.IOException;
@@ -108,10 +110,12 @@
return;
}
+ Duration parseTimeout = ConfigUtil.getDuration(cfg, "markdown", null,
+ "parseTimeout", Duration.standardSeconds(2));
view = view.toBuilder().setPathPart(srcmd.path).build();
int inputLimit = cfg.getInt("markdown", "inputLimit", 5 << 20);
RootNode doc = GitilesMarkdown.parseFile(
- view, srcmd.path,
+ parseTimeout, view, srcmd.path,
srcmd.read(rw.getObjectReader(), inputLimit));
if (doc == null) {
res.sendRedirect(GitilesView.show().copyFrom(view).toUrl());
@@ -121,7 +125,7 @@
RootNode nav = null;
if (navmd != null) {
nav = GitilesMarkdown.parseFile(
- view, navmd.path,
+ parseTimeout, view, navmd.path,
navmd.read(rw.getObjectReader(), inputLimit));
if (nav == null) {
res.setStatus(SC_INTERNAL_SERVER_ERROR);
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java b/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
index dcef245..91168b8 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/doc/GitilesMarkdown.java
@@ -17,6 +17,7 @@
import com.google.common.base.Throwables;
import com.google.gitiles.GitilesView;
+import org.joda.time.Duration;
import org.parboiled.Rule;
import org.parboiled.common.Factory;
import org.parboiled.errors.ParserRuntimeException;
@@ -50,20 +51,22 @@
// this impacting the rendered formatting.
private static final int MD_OPTIONS = (ALL | SUPPRESS_ALL_HTML) & ~(HARDWRAPS);
- public static RootNode parseFile(GitilesView view, String path, String md) {
+ public static RootNode parseFile(Duration parseTimeout, GitilesView view,
+ String path, String md) {
if (md == null) {
return null;
}
try {
try {
- return newParser().parseMarkdown(md.toCharArray());
+ return newParser(parseTimeout).parseMarkdown(md.toCharArray());
} catch (ParserRuntimeException e) {
Throwables.propagateIfInstanceOf(e.getCause(), ParsingTimeoutException.class);
throw e;
}
} catch (ParsingTimeoutException e) {
- log.error("timeout rendering {}/{} at {}",
+ log.error("timeout {} ms rendering {}/{} at {}",
+ parseTimeout.getMillis(),
view.getRepositoryName(),
path,
view.getRevision().getName());
@@ -71,17 +74,19 @@
}
}
- private static PegDownProcessor newParser() {
+ private static PegDownProcessor newParser(Duration parseDeadline) {
PegDownPlugins plugins = new PegDownPlugins.Builder()
- .withPlugin(GitilesMarkdown.class)
+ .withPlugin(GitilesMarkdown.class, parseDeadline)
.build();
- return new PegDownProcessor(MD_OPTIONS, plugins);
+ return new PegDownProcessor(MD_OPTIONS, parseDeadline.getMillis(), plugins);
}
+ private final Duration parseTimeout;
private PegDownProcessor parser;
- GitilesMarkdown() {
- super(MD_OPTIONS, 2000L, DefaultParseRunnerProvider);
+ GitilesMarkdown(Duration parseTimeout) {
+ super(MD_OPTIONS, parseTimeout.getMillis(), DefaultParseRunnerProvider);
+ this.parseTimeout = parseTimeout;
}
@Override
@@ -235,7 +240,7 @@
// use its existing parsing rules. Recurse manually for inner text
// parsing within a block.
if (parser == null) {
- parser = newParser();
+ parser = newParser(parseTimeout);
}
return parser.parseMarkdown(body.getChars()).getChildren();
}