summaryrefslogtreecommitdiff
path: root/src/main/scala/spray
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/spray')
-rw-r--r--src/main/scala/spray/json/JsonParser.scala42
-rw-r--r--src/main/scala/spray/json/JsonParserSettings.scala19
2 files changed, 42 insertions, 19 deletions
diff --git a/src/main/scala/spray/json/JsonParser.scala b/src/main/scala/spray/json/JsonParser.scala
index f29c062..9fe8a72 100644
--- a/src/main/scala/spray/json/JsonParser.scala
+++ b/src/main/scala/spray/json/JsonParser.scala
@@ -46,7 +46,7 @@ class JsonParser(input: ParserInput, settings: JsonParserSettings = JsonParserSe
def parseJsValue(allowTrailingInput: Boolean): JsValue = {
ws()
- `value`()
+ `value`(settings.maxDepth)
if (!allowTrailingInput)
require(EOI)
jsValue
@@ -57,27 +57,33 @@ class JsonParser(input: ParserInput, settings: JsonParserSettings = JsonParserSe
private final val EOI = '\uFFFF' // compile-time constant
// http://tools.ietf.org/html/rfc4627#section-2.1
- private def `value`(): Unit = {
- val mark = input.cursor
- def simpleValue(matched: Boolean, value: JsValue) = if (matched) jsValue = value else fail("JSON Value", mark)
- (cursorChar: @switch) match {
- case 'f' => simpleValue(`false`(), JsFalse)
- case 'n' => simpleValue(`null`(), JsNull)
- case 't' => simpleValue(`true`(), JsTrue)
- case '{' => advance(); `object`()
- case '[' => advance(); `array`()
- case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '-' => `number`()
- case '"' => `string`(); jsValue = if (sb.length == 0) JsString.empty else JsString(sb.toString)
- case _ => fail("JSON Value")
+ private def `value`(remainingNesting: Int): Unit =
+ if (remainingNesting == 0)
+ throw new ParsingException(
+ "JSON input nested too deeply",
+ s"JSON input was nested more deeply than the configured limit of maxNesting = ${settings.maxDepth}"
+ )
+ else {
+ val mark = input.cursor
+ def simpleValue(matched: Boolean, value: JsValue) = if (matched) jsValue = value else fail("JSON Value", mark)
+ (cursorChar: @switch) match {
+ case 'f' => simpleValue(`false`(), JsFalse)
+ case 'n' => simpleValue(`null`(), JsNull)
+ case 't' => simpleValue(`true`(), JsTrue)
+ case '{' => advance(); `object`(remainingNesting)
+ case '[' => advance(); `array`(remainingNesting)
+ case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '-' => `number`()
+ case '"' => `string`(); jsValue = if (sb.length == 0) JsString.empty else JsString(sb.toString)
+ case _ => fail("JSON Value")
+ }
}
- }
private def `false`() = advance() && ch('a') && ch('l') && ch('s') && ws('e')
private def `null`() = advance() && ch('u') && ch('l') && ws('l')
private def `true`() = advance() && ch('r') && ch('u') && ws('e')
// http://tools.ietf.org/html/rfc4627#section-2.2
- private def `object`(): Unit = {
+ private def `object`(remainingNesting: Int): Unit = {
ws()
jsValue = if (cursorChar != '}') {
@tailrec def members(map: Map[String, JsValue]): Map[String, JsValue] = {
@@ -85,7 +91,7 @@ class JsonParser(input: ParserInput, settings: JsonParserSettings = JsonParserSe
require(':')
ws()
val key = sb.toString
- `value`()
+ `value`(remainingNesting - 1)
val nextMap = map.updated(key, jsValue)
if (ws(',')) members(nextMap) else nextMap
}
@@ -101,12 +107,12 @@ class JsonParser(input: ParserInput, settings: JsonParserSettings = JsonParserSe
}
// http://tools.ietf.org/html/rfc4627#section-2.3
- private def `array`(): Unit = {
+ private def `array`(remainingNesting: Int): Unit = {
ws()
jsValue = if (cursorChar != ']') {
val list = Vector.newBuilder[JsValue]
@tailrec def values(): Unit = {
- `value`()
+ `value`(remainingNesting - 1)
list += jsValue
if (ws(',')) values()
}
diff --git a/src/main/scala/spray/json/JsonParserSettings.scala b/src/main/scala/spray/json/JsonParserSettings.scala
index 31692fd..d07075e 100644
--- a/src/main/scala/spray/json/JsonParserSettings.scala
+++ b/src/main/scala/spray/json/JsonParserSettings.scala
@@ -1,10 +1,27 @@
package spray.json
trait JsonParserSettings {
+ /**
+ * The JsonParser uses recursive decent parsing that keeps intermediate values on the stack. To prevent
+ * StackOverflowExceptions a limit is enforced on the depth of the parsed JSON structure.
+ *
+ * As a guideline we tested that one level of depth needs about 300 bytes of stack space.
+ *
+ * The default is a depth of 1000.
+ */
+ def maxDepth: Int
+ /**
+ * Return a copy of this settings object with the `maxDepth` setting changed to the new value.
+ */
+ def withMaxDepth(newValue: Int): JsonParserSettings
}
object JsonParserSettings {
val default: JsonParserSettings = SettingsImpl()
- private case class SettingsImpl() extends JsonParserSettings
+ private case class SettingsImpl(
+ maxDepth: Int = 1000
+ ) extends JsonParserSettings {
+ override def withMaxDepth(newValue: Int): JsonParserSettings = copy(maxDepth = newValue)
+ }
} \ No newline at end of file