1mod presence;
10
11use std::borrow::Cow;
12use std::collections::HashMap;
13
14use prost_reflect::{
15 DynamicMessage, FieldDescriptor, Kind, MessageDescriptor, ReflectMessage, Value,
16};
17use serde::de::value::StrDeserializer;
18use serde::Deserializer;
19use serde_json::Value as JsonValue;
20
21use presence::{has_special_json, OneEntry, Presence, Recording};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum BodyMapping {
26 None,
28 Root,
30 Field(String),
32}
33
34impl BodyMapping {
35 pub fn parse(raw: &str) -> Self {
40 match raw {
41 "" => BodyMapping::None,
42 "*" => BodyMapping::Root,
43 field => BodyMapping::Field(field.to_string()),
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum Body<'a> {
51 Absent,
55 Json(&'a [u8]),
58 Form(&'a [u8]),
61}
62
63impl<'a> Body<'a> {
64 pub fn new(content_type: Option<&str>, bytes: &'a [u8]) -> Self {
77 match content_type {
78 Some(ct) if ct.starts_with("application/x-www-form-urlencoded") => Body::Form(bytes),
79 _ => Body::Json(bytes),
80 }
81 }
82}
83
84pub fn build_request_message(
153 input: &MessageDescriptor,
154 mapping: &BodyMapping,
155 body: Body<'_>,
156 path_params: &HashMap<String, String>,
157 raw_query: Option<&str>,
158) -> Result<DynamicMessage, String> {
159 let query: Vec<(Cow<'_, str>, Cow<'_, str>)> = match raw_query {
161 Some(q) => url::form_urlencoded::parse(q.as_bytes()).collect(),
162 None => Vec::new(),
163 };
164 build(input, mapping, body, path_params, &query)
165}
166
167#[derive(Clone, Copy, PartialEq, Eq)]
169enum Binding {
170 Set,
172 Fill,
174}
175
176fn build(
177 input: &MessageDescriptor,
178 mapping: &BodyMapping,
179 body: Body<'_>,
180 path_params: &HashMap<String, String>,
181 query: &[(Cow<'_, str>, Cow<'_, str>)],
182) -> Result<DynamicMessage, String> {
183 let body_len = match body {
185 Body::Json(bytes) | Body::Form(bytes) => bytes.len(),
186 Body::Absent => 0,
187 };
188 let mut presence = (!query.is_empty()).then(|| Presence::for_body(body_len));
189 let mut path = Vec::new();
191
192 let mut inside = false;
194
195 let mut message = match (mapping, body) {
196 (BodyMapping::None, _) => DynamicMessage::new(input.clone()),
197 (BodyMapping::Field(name), Body::Absent) => {
198 if let (Some(presence), Some(field)) = (&mut presence, input.get_field_by_name(name)) {
199 presence.record_path(&[field], false);
200 }
201 DynamicMessage::new(input.clone())
202 }
203 (BodyMapping::Root, Body::Absent) => DynamicMessage::new(input.clone()),
204 (_, Body::Json(bytes) | Body::Form(bytes)) if bytes.is_empty() => {
206 json_body(input, mapping, b"{}", path_params, presence.as_mut())?
207 }
208 (_, Body::Json(bytes)) => json_body(input, mapping, bytes, path_params, presence.as_mut())?,
209 (_, Body::Form(bytes)) => {
210 let pairs: Vec<(Cow<'_, str>, Cow<'_, str>)> =
211 url::form_urlencoded::parse(bytes).collect();
212 let form = Form {
213 pairs: &pairs,
214 path_params,
215 };
216 let (message, form_inside) =
217 form_body(input, mapping, form, presence.as_mut(), &mut path)?;
218 inside = form_inside;
219 message
220 }
221 };
222
223 let root_inside = has_special_json(input);
224 for (key, raw) in path_params {
226 let target = Target {
227 root: input,
228 inside: root_inside,
229 prefix: None,
230 key,
231 values: &[raw.as_str()],
232 };
233 inside |= bind(
234 &mut message,
235 target,
236 Binding::Set,
237 presence.as_mut(),
238 &mut path,
239 )?;
240 }
241
242 if let Some(presence) = &mut presence {
243 let start = Start {
244 root: input,
245 inside: root_inside,
246 prefix: None,
247 };
248 inside |= bind_pairs(
249 &mut message,
250 query,
251 start,
252 Binding::Fill,
253 Some(presence),
254 None,
255 &mut path,
256 )?;
257 }
258 if inside {
259 check_well_known(&message)?;
260 }
261 Ok(message)
262}
263
264fn check_well_known(message: &DynamicMessage) -> Result<(), String> {
269 let invalid = |e: &dyn std::fmt::Display| format!("invalid value for a well-known type: {e}");
270 check_time_ranges(message).map_err(|e| invalid(&e))?;
271 serde_json::to_writer(std::io::sink(), message).map_err(|e| invalid(&e))
275}
276
277fn check_time_ranges(message: &DynamicMessage) -> Result<(), &'static str> {
281 let field = |name| message.get_field_by_name(name);
282 match message.descriptor().full_name() {
283 "google.protobuf.Timestamp" => {
286 let seconds = field("seconds").and_then(|v| v.as_i64()).unwrap_or(0);
287 let nanos = field("nanos").and_then(|v| v.as_i32()).unwrap_or(0);
288 if !(-62_135_596_800..=253_402_300_799).contains(&seconds)
289 || !(0..=999_999_999).contains(&nanos)
290 {
291 return Err("timestamp out of range");
292 }
293 }
294 "google.protobuf.Duration" => {
298 let seconds = field("seconds").and_then(|v| v.as_i64()).unwrap_or(0);
299 let nanos = field("nanos").and_then(|v| v.as_i32()).unwrap_or(0);
300 if seconds.unsigned_abs() > 315_576_000_000
301 || nanos.unsigned_abs() > 999_999_999
302 || (seconds > 0 && nanos < 0)
303 || (seconds < 0 && nanos > 0)
304 {
305 return Err("duration out of range");
306 }
307 }
308 _ => {}
309 }
310 for (_, value) in message.fields() {
311 check_value_time_ranges(value)?;
312 }
313 Ok(())
314}
315
316fn check_value_time_ranges(value: &Value) -> Result<(), &'static str> {
317 match value {
318 Value::Message(inner) => check_time_ranges(inner),
319 Value::List(items) => items.iter().try_for_each(check_value_time_ranges),
320 Value::Map(entries) => entries.values().try_for_each(check_value_time_ranges),
321 _ => Ok(()),
322 }
323}
324
325#[derive(Clone, Copy)]
328struct Target<'a> {
329 root: &'a MessageDescriptor,
330 inside: bool,
332 prefix: Option<&'a FieldDescriptor>,
333 key: &'a str,
334 values: &'a [&'a str],
336}
337
338fn json_body(
345 input: &MessageDescriptor,
346 mapping: &BodyMapping,
347 bytes: &[u8],
348 path_params: &HashMap<String, String>,
349 mut presence: Option<&mut Presence>,
350) -> Result<DynamicMessage, String> {
351 if matches!(mapping, BodyMapping::Root) && trim_json_whitespace(bytes) == b"null" {
353 return Ok(DynamicMessage::new(input.clone()));
354 }
355 let mut de = serde_json::Deserializer::from_slice(bytes);
356 let first = deserialize(input, mapping, &mut de, presence.as_deref_mut())
357 .and_then(|message| de.end().map(|()| message));
358 let error = match first {
359 Ok(message) => return Ok(message),
360 Err(error) => error,
361 };
362 let fail = |e: serde_json::Error| format!("failed to decode request body: {e}");
363 if path_params.is_empty() {
364 return Err(fail(error));
365 }
366 let Ok(mut value) = serde_json::from_slice::<JsonValue>(bytes) else {
368 return Err(fail(error));
369 };
370 strip_path_fields(&mut value, input, mapping, path_params);
371 if let Some(presence) = presence.as_deref_mut() {
372 presence.clear();
373 }
374 deserialize(input, mapping, value, presence).map_err(fail)
375}
376
377fn strip_path_fields(
380 body: &mut JsonValue,
381 input: &MessageDescriptor,
382 mapping: &BodyMapping,
383 path_params: &HashMap<String, String>,
384) {
385 for key in path_params.keys() {
386 let mut segments = key.split('.');
387 let desc = match mapping {
388 BodyMapping::Root => input.clone(),
389 BodyMapping::Field(name) => {
392 if segments.next() != Some(name.as_str()) {
393 continue;
394 }
395 match input.get_field_by_name(name).map(|f| f.kind()) {
396 Some(Kind::Message(inner)) => inner,
397 _ => continue,
398 }
399 }
400 BodyMapping::None => return,
401 };
402 remove_path(body, desc, segments);
403 }
404}
405
406fn remove_path<'k>(
408 mut value: &mut JsonValue,
409 mut desc: MessageDescriptor,
410 segments: impl Iterator<Item = &'k str>,
411) {
412 let mut segments = segments.peekable();
413 while let Some(segment) = segments.next() {
414 let Some(field) = field_named(&desc, segment, &mut false) else {
415 return;
416 };
417 let Some(object) = value.as_object_mut() else {
418 return;
419 };
420 if segments.peek().is_none() {
421 object.remove(field.json_name());
422 object.remove(field.name());
423 return;
424 }
425 let Kind::Message(inner) = field.kind() else {
426 return;
427 };
428 let key = if object.contains_key(field.json_name()) {
429 field.json_name()
430 } else {
431 field.name()
432 };
433 let Some(next) = object.get_mut(key) else {
434 return;
435 };
436 value = next;
437 desc = inner;
438 }
439}
440
441fn trim_json_whitespace(bytes: &[u8]) -> &[u8] {
442 let ws = |b: &u8| matches!(b, b' ' | b'\t' | b'\n' | b'\r');
443 let start = bytes.iter().position(|b| !ws(b)).unwrap_or(bytes.len());
444 let end = bytes.iter().rposition(|b| !ws(b)).map_or(start, |i| i + 1);
445 &bytes[start..end]
446}
447
448fn deserialize<'de, D: Deserializer<'de>>(
451 input: &MessageDescriptor,
452 mapping: &BodyMapping,
453 body: D,
454 presence: Option<&mut Presence>,
455) -> Result<DynamicMessage, D::Error> {
456 match mapping {
457 BodyMapping::Field(name) => {
458 let field = input.get_field_by_name(name);
461 let key = field.as_ref().map_or(name.as_str(), |f| f.json_name());
462 deserialize_input(input, OneEntry { key, value: body }, presence)
463 }
464 _ => deserialize_input(input, body, presence),
465 }
466}
467
468fn deserialize_input<'de, D: Deserializer<'de>>(
469 input: &MessageDescriptor,
470 de: D,
471 presence: Option<&mut Presence>,
472) -> Result<DynamicMessage, D::Error> {
473 let Some(presence) = presence else {
474 return DynamicMessage::deserialize(input.clone(), de);
475 };
476 if has_special_json(input) {
477 presence.record_whole();
478 return DynamicMessage::deserialize(input.clone(), de);
479 }
480 DynamicMessage::deserialize(input.clone(), Recording::root(de, presence))
481}
482
483fn form_body(
486 input: &MessageDescriptor,
487 mapping: &BodyMapping,
488 form: Form<'_, '_>,
489 mut presence: Option<&mut Presence>,
490 path: &mut Vec<FieldDescriptor>,
491) -> Result<(DynamicMessage, bool), String> {
492 let Form { pairs, path_params } = form;
493 let mut message = DynamicMessage::new(input.clone());
494 let (prefix, start_inside) = match mapping {
495 BodyMapping::Field(name) => {
496 let (field, inner) = input
497 .get_field_by_name(name)
498 .filter(|f| !f.is_list() && !f.is_map())
499 .and_then(|f| match f.kind() {
500 Kind::Message(inner) => Some((f, inner)),
501 _ => None,
502 })
503 .ok_or_else(|| format!("a form body cannot fill field `{name}`"))?;
504 message.get_field_mut(&field);
506 if let Some(presence) = presence.as_deref_mut() {
507 presence.record_path(std::slice::from_ref(&field), true);
508 }
509 (Some(field), has_special_json(&inner))
510 }
511 _ => (None, has_special_json(input)),
512 };
513 let start = Start {
514 root: input,
515 inside: start_inside,
516 prefix: prefix.as_ref(),
517 };
518 let inside = bind_pairs(
519 &mut message,
520 pairs,
521 start,
522 Binding::Set,
523 presence,
524 Some(path_params),
525 path,
526 )?;
527 Ok((message, inside))
528}
529
530#[derive(Clone, Copy)]
532struct Form<'p, 'q> {
533 pairs: &'p [(Cow<'q, str>, Cow<'q, str>)],
534 path_params: &'p HashMap<String, String>,
535}
536
537fn covers(bound: &str, path: &[FieldDescriptor]) -> bool {
540 let mut fields = path.iter();
541 bound.split('.').all(|segment| {
542 fields
543 .next()
544 .is_some_and(|f| segment == f.name() || segment == f.json_name())
545 })
546}
547
548#[derive(Clone, Copy)]
551struct Start<'a> {
552 root: &'a MessageDescriptor,
553 inside: bool,
554 prefix: Option<&'a FieldDescriptor>,
555}
556
557fn bind_pairs(
565 message: &mut DynamicMessage,
566 pairs: &[(Cow<'_, str>, Cow<'_, str>)],
567 start: Start<'_>,
568 binding: Binding,
569 mut presence: Option<&mut Presence>,
570 path_params: Option<&HashMap<String, String>>,
571 path: &mut Vec<FieldDescriptor>,
572) -> Result<bool, String> {
573 let mut order: Vec<usize> = (0..pairs.len()).collect();
575 order.sort_by(|&a, &b| pairs[a].0.cmp(&pairs[b].0));
576 let mut values = Vec::new();
577 let mut inside = false;
578 let mut aliased: Vec<(String, &[usize])> = Vec::new();
581 let mut failed: Vec<(&str, String)> = Vec::new();
583 for run in order.chunk_by(|&a, &b| pairs[a].0 == pairs[b].0) {
584 values.clear();
585 values.extend(run.iter().map(|&i| pairs[i].1.as_ref()));
586 let target = Target {
587 root: start.root,
588 inside: start.inside,
589 prefix: start.prefix,
590 key: &pairs[run[0]].0,
591 values: &values,
592 };
593 let Some(resolved) = resolve(target, path) else {
594 continue;
595 };
596 if resolved.aliased {
597 let own = &path[usize::from(start.prefix.is_some())..];
598 let canonical = own.iter().map(|f| f.name()).collect::<Vec<_>>().join(".");
599 aliased.push((canonical, run));
600 continue;
601 }
602 let bound = bind_resolved(
603 message,
604 path,
605 &values,
606 binding,
607 presence.as_deref_mut(),
608 path_params,
609 );
610 match bound {
611 Ok(set) => inside |= set && resolved.inside,
612 Err(e) => failed.push((pairs[run[0]].0.as_ref(), e)),
617 }
618 }
619 aliased.sort_by(|a, b| a.0.cmp(&b.0));
620 let covered = |key: &str| {
622 aliased
623 .binary_search_by(|(c, _)| c.as_str().cmp(key))
624 .is_ok()
625 };
626 if let Some((_, e)) = failed.into_iter().find(|(key, _)| !covered(key)) {
627 return Err(e);
628 }
629 if aliased.is_empty() {
630 return Ok(inside);
631 }
632 let mut indices = Vec::new();
633 for group in aliased.chunk_by(|a, b| a.0 == b.0) {
634 let canonical = group[0].0.as_str();
635 indices.clear();
636 for (_, run) in group {
637 indices.extend_from_slice(run);
638 }
639 let from = order.partition_point(|&i| pairs[i].0.as_ref() < canonical);
641 let to = order.partition_point(|&i| pairs[i].0.as_ref() <= canonical);
642 indices.extend_from_slice(&order[from..to]);
643 indices.sort_unstable();
644 values.clear();
645 values.extend(indices.iter().map(|&i| pairs[i].1.as_ref()));
646 let target = Target {
647 root: start.root,
648 inside: start.inside,
649 prefix: start.prefix,
650 key: canonical,
651 values: &values,
652 };
653 let Some(resolved) = resolve(target, path) else {
654 continue;
655 };
656 inside |= bind_resolved(
657 message,
658 path,
659 &values,
660 binding,
661 presence.as_deref_mut(),
662 path_params,
663 )? && resolved.inside;
664 }
665 Ok(inside)
666}
667
668fn bind_resolved(
671 message: &mut DynamicMessage,
672 path: &[FieldDescriptor],
673 values: &[&str],
674 binding: Binding,
675 presence: Option<&mut Presence>,
676 path_params: Option<&HashMap<String, String>>,
677) -> Result<bool, String> {
678 if path_params.is_some_and(|bound| bound.keys().any(|key| covers(key, path))) {
681 return Ok(false);
682 }
683 assign(message, path, values, binding, presence)?;
684 Ok(true)
685}
686
687fn bind(
691 message: &mut DynamicMessage,
692 target: Target<'_>,
693 binding: Binding,
694 presence: Option<&mut Presence>,
695 path: &mut Vec<FieldDescriptor>,
696) -> Result<bool, String> {
697 let Some(resolved) = resolve(target, path) else {
698 return Ok(false);
699 };
700 assign(message, path, target.values, binding, presence)?;
701 Ok(resolved.inside)
702}
703
704#[derive(Clone, Copy)]
706struct Resolved {
707 inside: bool,
709 aliased: bool,
711}
712
713fn resolve(target: Target<'_>, path: &mut Vec<FieldDescriptor>) -> Option<Resolved> {
723 path.clear();
724 let mut inside = target.inside;
725 let mut aliased = false;
726 let mut owned;
727 let mut desc = match target.prefix {
728 Some(field) => {
729 let Kind::Message(inner) = field.kind() else {
730 return None;
731 };
732 path.push(field.clone());
733 owned = inner;
734 &owned
735 }
736 None => target.root,
737 };
738 let mut segments = target.key.split('.');
739 let mut leaf = segments.next().unwrap_or_default();
740 for next in segments {
741 let field = field_named(desc, leaf, &mut aliased)?;
742 let Kind::Message(inner) = field.kind() else {
743 return None;
744 };
745 if field.is_list() || field.is_map() {
746 return None;
747 }
748 inside |= has_special_json(&inner);
749 path.push(field);
750 owned = inner;
751 desc = &owned;
752 leaf = next;
753 }
754 path.push(field_named(desc, leaf, &mut aliased)?);
755 Some(Resolved { inside, aliased })
756}
757
758fn field_named(
761 desc: &MessageDescriptor,
762 name: &str,
763 aliased: &mut bool,
764) -> Option<FieldDescriptor> {
765 if let Some(field) = desc.get_field_by_name(name) {
766 return Some(field);
767 }
768 let field = desc.get_field_by_json_name(name)?;
769 *aliased = true;
770 Some(field)
771}
772
773fn assign(
777 message: &mut DynamicMessage,
778 path: &[FieldDescriptor],
779 values: &[&str],
780 binding: Binding,
781 mut presence: Option<&mut Presence>,
782) -> Result<(), String> {
783 if binding == Binding::Fill && presence.as_deref_mut().is_some_and(|p| p.blocks(path)) {
784 return Ok(());
785 }
786
787 let (field, parents) = path
788 .split_last()
789 .expect("a resolved path ends in its field");
790 let value = field_value(field, values)?;
791 let mut holder = message;
792 for parent in parents {
793 check_oneof(holder, parent)?;
794 holder = match holder.get_field_mut(parent) {
795 Value::Message(inner) => inner,
796 _ => unreachable!("a singular message field holds a message"),
797 };
798 }
799 check_oneof(holder, field)?;
800 holder.set_field(field, value);
801 if binding == Binding::Set {
802 if let Some(presence) = presence {
803 presence.record_path(path, false);
804 }
805 }
806 Ok(())
807}
808
809fn check_oneof(message: &DynamicMessage, field: &FieldDescriptor) -> Result<(), String> {
812 let Some(oneof) = field.containing_oneof() else {
813 return Ok(());
814 };
815 if oneof
816 .fields()
817 .any(|other| other.number() != field.number() && message.has_field(&other))
818 {
819 return Err(format!(
820 "multiple fields provided for oneof '{}'",
821 oneof.name()
822 ));
823 }
824 Ok(())
825}
826
827fn field_value(field: &FieldDescriptor, raw: &[&str]) -> Result<Value, String> {
829 let invalid = |e: String| format!("invalid value for field `{}`: {e}", field.name());
830 if field.is_map() {
831 return Err(invalid("a map field cannot be set from a string".into()));
832 }
833 let kind = field.kind();
834 if field.is_list() {
835 return raw
836 .iter()
837 .map(|raw| scalar(&kind, raw))
838 .collect::<Result<Vec<_>, _>>()
839 .map(Value::List)
840 .map_err(invalid);
841 }
842 let raw = raw.last().expect("a bound key has a value");
843 scalar(&kind, raw).map_err(invalid)
844}
845
846fn scalar(kind: &Kind, raw: &str) -> Result<Value, String> {
851 Ok(match kind {
852 Kind::Double => Value::F64(parse_f64(raw)?),
853 Kind::Float => Value::F32(parse_f32(raw)?),
854 Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => Value::I32(parse(raw)?),
855 Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => Value::I64(parse(raw)?),
856 Kind::Uint32 | Kind::Fixed32 => Value::U32(parse(raw)?),
857 Kind::Uint64 | Kind::Fixed64 => Value::U64(parse(raw)?),
858 Kind::Bool => Value::Bool(parse(raw)?),
859 Kind::String => Value::String(raw.to_owned()),
860 Kind::Bytes => Value::Bytes(decode_base64(raw)?.into()),
861 Kind::Enum(desc) => Value::EnumNumber(
862 desc.get_value_by_name(raw)
863 .ok_or_else(|| format!("unrecognized enum value '{raw}'"))?
864 .number(),
865 ),
866 Kind::Message(desc) => Value::Message(
867 DynamicMessage::deserialize(
868 desc.clone(),
869 StrDeserializer::<serde::de::value::Error>::new(raw),
870 )
871 .map_err(|e| e.to_string())?,
872 ),
873 })
874}
875
876fn parse<T: std::str::FromStr>(raw: &str) -> Result<T, String>
877where
878 T::Err: std::fmt::Display,
879{
880 raw.parse().map_err(|e: T::Err| e.to_string())
881}
882
883fn special_float(raw: &str) -> Option<f64> {
885 match raw {
886 "Infinity" => Some(f64::INFINITY),
887 "-Infinity" => Some(f64::NEG_INFINITY),
888 "NaN" => Some(f64::NAN),
889 _ => None,
890 }
891}
892
893fn parse_f64(raw: &str) -> Result<f64, String> {
898 if let Some(special) = special_float(raw) {
899 return Ok(special);
900 }
901 match raw.parse::<f64>() {
902 Ok(value) if value.is_finite() => Ok(value),
903 Ok(_) => Err("value out of range, or not a decimal number".to_string()),
904 Err(e) => Err(e.to_string()),
905 }
906}
907
908fn parse_f32(raw: &str) -> Result<f32, String> {
911 let wide = parse_f64(raw)?;
912 if wide.is_finite() && (wide < f64::from(f32::MIN) || wide > f64::from(f32::MAX)) {
913 return Err("float value out of range".to_string());
914 }
915 Ok(wide as f32)
918}
919
920fn decode_base64(raw: &str) -> Result<Vec<u8>, String> {
923 use base64::alphabet;
924 use base64::engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig};
925 use base64::{DecodeError, Engine};
926
927 const CONFIG: GeneralPurposeConfig = GeneralPurposeConfig::new()
928 .with_decode_allow_trailing_bits(true)
929 .with_decode_padding_mode(DecodePaddingMode::Indifferent);
930 const STANDARD: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, CONFIG);
931 const URL_SAFE: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, CONFIG);
932
933 let mut buf = Vec::new();
934 match STANDARD.decode_vec(raw, &mut buf) {
935 Ok(()) => Ok(buf),
936 Err(DecodeError::InvalidByte(_, b'-' | b'_')) => {
937 buf.clear();
938 URL_SAFE
939 .decode_vec(raw, &mut buf)
940 .map(|()| buf)
941 .map_err(|e| format!("invalid base64: {e}"))
942 }
943 Err(e) => Err(format!("invalid base64: {e}")),
944 }
945}
946
947#[cfg(test)]
948mod tests;