use crate::{Error, ErrorKind, Result};
use serde_json::Value;
pub const MAX_WIRE_VALUE_DEPTH: usize = 64;
pub const APPLICATION_INPUT_MAX_BYTES: usize = 1024 * 1024;
pub const DEFAULT_RUNTIME_FRAME_MAX_BYTES: usize = 2 * 1024 * 1024;
pub fn validate_wire_value(value: &Value) -> Result<()> {
fn visit(value: &Value, depth: usize) -> Result<()> {
if matches!(value, Value::Array(_) | Value::Object(_)) && depth >= MAX_WIRE_VALUE_DEPTH {
return Err(Error::new(
ErrorKind::Protocol,
"program output exceeds 64 nested containers",
));
}
match value {
Value::Array(values) => {
for value in values {
visit(value, depth + 1)?;
}
}
Value::Object(values) => {
for value in values.values() {
visit(value, depth + 1)?;
}
}
_ => {}
}
Ok(())
}
visit(value, 0)
}
pub const MAX_RUNTIME_VALUE_DEPTH: usize = 96;
pub const RUNTIME_EXTENSION_MAX_BYTES: usize = 640 * 1024;
pub const RUNTIME_REQUEST_MAX_BYTES: usize = 144 * 1024;
pub fn validate_runtime_payload(value: &Value, max_bytes: usize) -> Result<()> {
fn visit(value: &Value, depth: usize, remaining: &mut usize) -> Result<()> {
if *remaining == 0 {
return Err(Error::new(
ErrorKind::Protocol,
"runtime payload exceeds its byte budget",
));
}
*remaining -= 1;
if matches!(value, Value::Array(_) | Value::Object(_)) && depth >= MAX_RUNTIME_VALUE_DEPTH {
return Err(Error::new(
ErrorKind::Protocol,
"runtime payload exceeds 96 nested containers",
));
}
match value {
Value::Array(values) => {
for value in values {
visit(value, depth + 1, remaining)?;
}
}
Value::Object(values) => {
for value in values.values() {
visit(value, depth + 1, remaining)?;
}
}
_ => {}
}
Ok(())
}
struct Budget(usize);
impl std::io::Write for Budget {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
if bytes.len() > self.0 {
return Err(std::io::Error::other(
"runtime payload exceeds its byte budget",
));
}
self.0 -= bytes.len();
Ok(bytes.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
let mut remaining_nodes = max_bytes;
visit(value, 0, &mut remaining_nodes)?;
serde_json::to_writer(Budget(max_bytes), value).map_err(|error| {
Error::new(
ErrorKind::Protocol,
format!("runtime payload exceeds its byte budget: {error}"),
)
})
}