1use crate::{
2 Endpoint, Error, ErrorKind, Limits, OriginPolicy, RemoteOutcome, RequestOptions, Result,
3 Session,
4 auth::{AuthContext, Authentication, OAuthProvider},
5 config::{DestinationPolicy, DestinationPurpose},
6 transport::{Transport, TransportRequest, read_limited},
7};
8use bytes::Bytes;
9use http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode};
10use serde::{Serialize, de::DeserializeOwned};
11use std::{
12 fmt,
13 io::Write,
14 sync::{
15 Arc,
16 atomic::{AtomicBool, Ordering},
17 },
18 time::{Duration, SystemTime},
19};
20use tokio::sync::Semaphore;
21
22pub(crate) const SDK_USER_AGENT: &str =
23 concat!("PAN-AIRS/", env!("CARGO_PKG_VERSION"), "-rust-sdk");
24fn default_headers() -> HeaderMap {
25 let mut headers = HeaderMap::new();
26 headers.insert(
27 http::header::USER_AGENT,
28 HeaderValue::from_static(SDK_USER_AGENT),
29 );
30 headers.insert("service-name", HeaderValue::from_static("api"));
31 headers
32}
33
34pub struct RawRequest {
37 name: &'static str,
38 method: Method,
39 path: String,
40 segments: Vec<String>,
41 query: Vec<(String, String)>,
42 headers: HeaderMap,
43 body: Option<Bytes>,
44 replay_safe: bool,
45 runtime_retries: Option<u32>,
46}
47impl RawRequest {
48 pub fn new(method: Method, path: impl Into<String>) -> Self {
50 Self {
51 name: "custom.request",
52 method,
53 path: path.into(),
54 segments: Vec::new(),
55 query: Vec::new(),
56 headers: default_headers(),
57 body: None,
58 replay_safe: false,
59 runtime_retries: None,
60 }
61 }
62 pub fn read(path: impl Into<String>) -> Self {
64 Self::new(Method::GET, path)
65 .named("custom.read")
66 .safe_read()
67 }
68 pub fn query(mut self, values: impl IntoIterator<Item = (String, String)>) -> Self {
69 self.query.extend(values);
70 self
71 }
72 pub fn segment(mut self, value: impl AsRef<str>) -> Result<Self> {
75 let value = value.as_ref();
76 crate::config::encode_segment(value)?;
77 self.segments.push(value.to_owned());
78 Ok(self)
79 }
80 pub fn header(mut self, name: &str, value: &str) -> Result<Self> {
82 let name = HeaderName::from_bytes(name.as_bytes())
83 .map_err(|_| Error::new(ErrorKind::Validation))?;
84 if forbidden_header(name.as_str()) {
85 return Err(Error::new(ErrorKind::Validation));
86 }
87 let mut value =
88 HeaderValue::from_str(value).map_err(|_| Error::new(ErrorKind::Validation))?;
89 value.set_sensitive(true);
90 self.headers.append(name, value);
91 Ok(self)
92 }
93 pub fn body(mut self, bytes: Bytes, content_type: &str) -> Result<Self> {
95 content_type
96 .parse::<mime::Mime>()
97 .map_err(|_| Error::new(ErrorKind::Validation))?;
98 let value =
99 HeaderValue::from_str(content_type).map_err(|_| Error::new(ErrorKind::Validation))?;
100 self.headers.insert(http::header::CONTENT_TYPE, value);
101 self.body = Some(bytes);
102 Ok(self)
103 }
104 pub fn json_value(self, value: &serde_json::Value, limit: usize) -> Result<Self> {
106 self.json(value, limit)
107 }
108 pub(crate) fn named(mut self, name: &'static str) -> Self {
109 self.name = name;
110 self
111 }
112 pub(crate) fn safe_read(mut self) -> Self {
113 self.replay_safe = true;
114 self
115 }
116 #[cfg(feature = "gateway")]
117 pub(crate) fn runtime_replay(&mut self, retries: Option<usize>) {
118 self.replay_safe = false;
119 self.runtime_retries = retries.map(|value| value as u32);
120 }
121 #[cfg(feature = "gateway")]
122 pub(crate) fn routing_headers(&mut self, headers: &HeaderMap) -> Result<()> {
123 if headers.keys().any(|name| self.headers.contains_key(name)) {
124 return Err(Error::new(ErrorKind::Validation));
125 }
126 self.headers.extend(headers.clone());
127 if headers_exceed(&self.headers) {
128 return Err(Error::new(ErrorKind::Limit));
129 }
130 Ok(())
131 }
132 #[cfg(feature = "dlp")]
133 pub(crate) fn content_type(mut self, value: &str) -> Result<Self> {
134 value
135 .parse::<mime::Mime>()
136 .map_err(|_| Error::new(ErrorKind::Validation))?;
137 self.headers.insert(
138 http::header::CONTENT_TYPE,
139 HeaderValue::from_str(value).map_err(|_| Error::new(ErrorKind::Validation))?,
140 );
141 Ok(self)
142 }
143 pub(crate) fn json<T: Serialize>(self, value: &T, limit: usize) -> Result<Self> {
144 let content_type = self
145 .headers
146 .get(http::header::CONTENT_TYPE)
147 .and_then(|value| value.to_str().ok())
148 .unwrap_or("application/json")
149 .to_owned();
150 self.body(serialize_bounded(value, limit)?, &content_type)
151 }
152}
153fn runtime_retryable(status: StatusCode) -> bool {
154 matches!(status.as_u16(), 500 | 502 | 503 | 504)
155}
156fn forbidden_header(name: &str) -> bool {
157 matches!(
158 name,
159 "authorization"
160 | "proxy-authorization"
161 | "cookie"
162 | "host"
163 | "content-length"
164 | "content-type"
165 | "transfer-encoding"
166 | "connection"
167 | "upgrade"
168 | "x-pan-token"
169 | "x-payload-hash"
170 | "x-portkey-api-key"
171 | "x-tsg-id"
172 | "service-name"
173 | "user-agent"
174 )
175}
176fn headers_exceed(headers: &HeaderMap) -> bool {
177 headers.len() > 128
178 || headers
179 .iter()
180 .try_fold(0usize, |n, (k, v)| {
181 n.checked_add(k.as_str().len())?
182 .checked_add(v.as_bytes().len())
183 })
184 .is_none_or(|n| n > 16 * 1024)
185}
186impl fmt::Debug for RawRequest {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 f.debug_struct("RawRequest")
189 .field("operation", &self.name)
190 .field("method", &self.method)
191 .field("has_body", &self.body.is_some())
192 .finish_non_exhaustive()
193 }
194}
195
196#[derive(Clone)]
198pub struct Client {
199 inner: Arc<Inner>,
200}
201struct BufferedResponse {
202 body: Bytes,
203 status: u16,
204 request_id: Option<String>,
205}
206impl BufferedResponse {
207 fn annotate(&self, error: Error) -> Error {
208 let error = error
209 .with_status(self.status)
210 .with_metadata(self.request_id.clone(), None, None)
211 .with_outcome(RemoteOutcome::Unknown);
212 crate::observability::failure(crate::observability::Operation::Decode, &error);
213 error
214 }
215}
216#[cfg(any(
217 feature = "scan",
218 feature = "management",
219 feature = "gateway",
220 feature = "dlp",
221 feature = "iam",
222 feature = "model-security",
223 feature = "red-team",
224 feature = "agentguard"
225))]
226pub(crate) enum AuthKind {
227 #[cfg(feature = "scan")]
228 Scan,
229 #[cfg(any(
230 feature = "management",
231 feature = "gateway",
232 feature = "dlp",
233 feature = "iam",
234 feature = "model-security",
235 feature = "red-team",
236 feature = "agentguard"
237 ))]
238 OAuth,
239 #[cfg(feature = "gateway")]
240 Runtime,
241 #[cfg(feature = "gateway")]
242 Public,
243}
244struct Inner {
245 endpoint: Endpoint,
246 auth: Authentication,
247 transport: Arc<dyn Transport>,
248 policy: Arc<dyn DestinationPolicy>,
249 limits: Limits,
250 session: Session,
251 permits: Arc<Semaphore>,
252 validation: Option<crate::validation::ValidationLimits>,
253}
254pub struct ClientBuilder {
255 endpoint: Endpoint,
256 auth: Authentication,
257 transport: Option<Arc<dyn Transport>>,
258 policy: Option<Arc<dyn DestinationPolicy>>,
259 limits: Limits,
260 session: Session,
261 oauth: Option<(AuthContext, crate::Secret, bool)>,
262 oauth_options: crate::auth::OAuthProviderOptions,
263 validation: Option<crate::validation::ValidationLimits>,
264}
265impl ClientBuilder {
266 pub fn new(endpoint: Endpoint) -> Self {
267 Self {
268 endpoint,
269 auth: Authentication::None,
270 transport: None,
271 policy: None,
272 limits: Limits::default(),
273 session: Session::new(),
274 oauth: None,
275 oauth_options: crate::auth::OAuthProviderOptions::default(),
276 validation: None,
277 }
278 }
279 pub fn authentication(mut self, value: Authentication) -> Self {
281 self.auth = value;
282 self.oauth = None;
283 self
284 }
285 pub fn oauth_credentials(
289 mut self,
290 context: AuthContext,
291 secret: crate::Secret,
292 tsg_header: bool,
293 ) -> Self {
294 self.auth = Authentication::None;
295 self.oauth = Some((context, secret, tsg_header));
296 self
297 }
298 pub fn oauth_provider_options(mut self, options: crate::auth::OAuthProviderOptions) -> Self {
300 self.oauth_options = options;
301 self
302 }
303 pub fn transport(mut self, value: Arc<dyn Transport>) -> Self {
304 self.transport = Some(value);
305 self
306 }
307 pub fn destination_policy(mut self, value: Arc<dyn DestinationPolicy>) -> Self {
308 self.policy = Some(value);
309 self
310 }
311 pub fn limits(mut self, value: Limits) -> Self {
312 self.limits = value;
313 self
314 }
315 pub fn validation_limits(mut self, value: crate::validation::ValidationLimits) -> Self {
317 self.validation = Some(value);
318 self
319 }
320 pub fn session(mut self, value: Session) -> Self {
321 self.session = value;
322 self
323 }
324 pub fn build(self) -> Result<Client> {
325 self.limits.validate()?;
326 if let Some(value) = &self.validation {
327 value.validate()?;
328 }
329 let policy = self.policy.unwrap_or_else(|| {
330 let mut endpoints = vec![self.endpoint.clone()];
331 if let Some((context, _, _)) = &self.oauth {
332 endpoints.push(context.issuer().clone());
333 }
334 Arc::new(OriginPolicy::new(endpoints))
335 });
336 let transport = match self.transport {
337 Some(value) => value,
338 None => {
339 #[cfg(feature = "reqwest-transport")]
340 {
341 Arc::new(crate::transport::ReqwestTransport::new()?) as Arc<dyn Transport>
342 }
343 #[cfg(not(feature = "reqwest-transport"))]
344 {
345 return Err(Error::new(ErrorKind::Configuration));
346 }
347 }
348 };
349 let auth = match self.oauth {
350 Some((context, secret, tsg_header)) => {
351 let provider = OAuthProvider::new_with_options(
352 context,
353 secret,
354 Arc::clone(&transport),
355 Arc::clone(&policy),
356 self.session.clone(),
357 self.oauth_options,
358 )?;
359 Authentication::oauth(Arc::new(provider), tsg_header)
360 }
361 None => self.auth,
362 };
363 let permits = Arc::new(Semaphore::new(self.limits.max_concurrency));
364 Ok(Client {
365 inner: Arc::new(Inner {
366 endpoint: self.endpoint,
367 auth,
368 transport,
369 policy,
370 limits: self.limits,
371 session: self.session,
372 permits,
373 validation: self.validation,
374 }),
375 })
376 }
377}
378impl fmt::Debug for ClientBuilder {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 f.write_str("ClientBuilder(..)")
381 }
382}
383impl fmt::Debug for Client {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 f.debug_struct("Client")
386 .field("endpoint", &self.inner.endpoint)
387 .field("limits", &self.inner.limits)
388 .finish_non_exhaustive()
389 }
390}
391impl Client {
392 pub fn builder(endpoint: Endpoint) -> ClientBuilder {
393 ClientBuilder::new(endpoint)
394 }
395 pub fn limits(&self) -> &Limits {
396 &self.inner.limits
397 }
398 pub fn validation_limits(&self) -> Option<&crate::validation::ValidationLimits> {
399 self.inner.validation.as_ref()
400 }
401 #[cfg(any(
402 feature = "scan",
403 feature = "management",
404 feature = "gateway",
405 feature = "dlp",
406 feature = "iam",
407 feature = "model-security",
408 feature = "red-team",
409 feature = "agentguard"
410 ))]
411 pub(crate) fn contract_limits(
412 &self,
413 max_bytes: usize,
414 ) -> Result<crate::validation::ValidationLimits> {
415 let mut limits = self.inner.validation.clone().map_or_else(
416 || crate::validation::ValidationLimits::for_bytes(max_bytes),
417 Ok,
418 )?;
419 limits.max_input_bytes = limits.max_input_bytes.min(max_bytes);
420 limits.validate()?;
421 Ok(limits)
422 }
423 pub fn session(&self) -> Session {
424 self.inner.session.clone()
425 }
426 pub fn endpoint(&self) -> &Endpoint {
427 &self.inner.endpoint
428 }
429 pub fn oauth_context(&self) -> Option<&AuthContext> {
431 match &self.inner.auth {
432 Authentication::OAuth { provider, .. } => Some(provider.context()),
433 _ => None,
434 }
435 }
436 pub fn with_endpoint(&self, endpoint: Endpoint) -> Result<Self> {
439 self.inner
440 .policy
441 .check(endpoint.url(), DestinationPurpose::Api)
442 .map_err(|_| Error::new(ErrorKind::Destination))?;
443 Ok(self.scoped(endpoint, self.inner.auth.clone()))
444 }
445 pub fn with_tsg_header(&self, enabled: bool) -> Result<Self> {
448 let Authentication::OAuth { provider, .. } = &self.inner.auth else {
449 return Err(Error::new(ErrorKind::Configuration));
450 };
451 Ok(self.scoped(
452 self.inner.endpoint.clone(),
453 Authentication::oauth(Arc::clone(provider), enabled),
454 ))
455 }
456 fn scoped(&self, endpoint: Endpoint, auth: Authentication) -> Self {
457 Self {
458 inner: Arc::new(Inner {
459 endpoint,
460 auth,
461 transport: Arc::clone(&self.inner.transport),
462 policy: Arc::clone(&self.inner.policy),
463 limits: self.inner.limits.clone(),
464 session: self.inner.session.clone(),
465 permits: Arc::clone(&self.inner.permits),
466 validation: self.inner.validation.clone(),
467 }),
468 }
469 }
470 pub(crate) fn operation_options(
471 &self,
472 options: Option<&RequestOptions>,
473 ) -> Result<RequestOptions> {
474 options.cloned().map_or_else(
475 || RequestOptions::with_timeout(self.inner.limits.operation_timeout),
476 Ok,
477 )
478 }
479 #[cfg(any(
480 feature = "scan",
481 feature = "management",
482 feature = "gateway",
483 feature = "dlp",
484 feature = "iam",
485 feature = "model-security",
486 feature = "red-team",
487 feature = "agentguard"
488 ))]
489 pub(crate) fn require_auth(&self, kind: AuthKind) -> Result<()> {
490 let valid = match kind {
491 #[cfg(feature = "scan")]
492 AuthKind::Scan => matches!(&self.inner.auth, Authentication::Scan { .. }),
493 #[cfg(any(
494 feature = "management",
495 feature = "gateway",
496 feature = "dlp",
497 feature = "iam",
498 feature = "model-security",
499 feature = "red-team",
500 feature = "agentguard"
501 ))]
502 AuthKind::OAuth => matches!(&self.inner.auth, Authentication::OAuth { .. }),
503 #[cfg(feature = "gateway")]
504 AuthKind::Runtime => matches!(&self.inner.auth, Authentication::RuntimeKey { .. }),
505 #[cfg(feature = "gateway")]
506 AuthKind::Public => matches!(&self.inner.auth, Authentication::None),
507 };
508 if valid {
509 Ok(())
510 } else {
511 Err(Error::new(ErrorKind::Configuration))
512 }
513 }
514 #[cfg(any(
515 feature = "scan",
516 feature = "management",
517 feature = "gateway",
518 feature = "dlp",
519 feature = "iam",
520 feature = "model-security",
521 feature = "red-team",
522 feature = "agentguard"
523 ))]
524 pub(crate) async fn validated_json<T: Serialize>(
525 &self,
526 body: &T,
527 schema: &str,
528 options: &RequestOptions,
529 ) -> Result<Bytes> {
530 options
531 .run(&self.inner.session, async {
532 let bytes = serialize_bounded(body, self.inner.limits.max_request_bytes)?;
533 let value = crate::json_decode::from_slice(&bytes)
534 .map_err(|_| Error::new(ErrorKind::Validation))?;
535 let value = normalize_contract(
536 schema,
537 value,
538 crate::validation::Mode::Request,
539 &self.contract_limits(self.inner.limits.max_request_bytes)?,
540 )?;
541 serialize_bounded(&value, self.inner.limits.max_request_bytes)
542 })
543 .await
544 }
545 #[cfg(any(
546 feature = "scan",
547 feature = "management",
548 feature = "gateway",
549 feature = "dlp",
550 feature = "iam",
551 feature = "model-security",
552 feature = "red-team"
553 ))]
554 async fn prepare_model<T: Serialize>(
555 &self,
556 request: RawRequest,
557 body: &T,
558 schema: &str,
559 options: &RequestOptions,
560 ) -> Result<RawRequest> {
561 let name = request.name;
562 let bytes = self
563 .validated_json(body, schema, options)
564 .await
565 .map_err(|e| e.with_operation(name))?;
566 let content_type = request
567 .headers
568 .get(http::header::CONTENT_TYPE)
569 .and_then(|v| v.to_str().ok())
570 .unwrap_or("application/json")
571 .to_owned();
572 request.body(bytes, &content_type)
573 }
574 #[cfg(any(
575 feature = "scan",
576 feature = "management",
577 feature = "gateway",
578 feature = "dlp",
579 feature = "iam",
580 feature = "model-security",
581 feature = "red-team"
582 ))]
583 pub(crate) async fn send_model<T: Serialize, R: DeserializeOwned>(
584 &self,
585 request: RawRequest,
586 body: &T,
587 request_schema: &str,
588 response_schema: &str,
589 options: Option<&RequestOptions>,
590 ) -> Result<R> {
591 let options = self.operation_options(options)?;
592 let request = self
593 .prepare_model(request, body, request_schema, &options)
594 .await?;
595 self.send_contract(request, response_schema, Some(&options))
596 .await
597 }
598 #[cfg(any(
599 feature = "scan",
600 feature = "management",
601 feature = "gateway",
602 feature = "dlp",
603 feature = "iam",
604 feature = "model-security",
605 feature = "red-team",
606 feature = "agentguard"
607 ))]
608 pub(crate) async fn send_contract<R: DeserializeOwned>(
609 &self,
610 request: RawRequest,
611 schema: &str,
612 options: Option<&RequestOptions>,
613 ) -> Result<R> {
614 let name = request.name;
615 let options = self.operation_options(options)?;
616 let response = self.send_buffered(request, Some(&options)).await?;
617 self.decode_contract(&response, schema, &options)
618 .await
619 .map_err(|e| response.annotate(e).with_operation(name))
620 }
621 #[cfg(feature = "gateway")]
622 pub(crate) async fn send_contract_one_of<R: DeserializeOwned>(
623 &self,
624 request: RawRequest,
625 schemas: &[&str],
626 options: Option<&RequestOptions>,
627 ) -> Result<R> {
628 let name = request.name;
629 let options = self.operation_options(options)?;
630 let response = self.send_buffered(request, Some(&options)).await?;
631 options
632 .run(&self.inner.session, async {
633 let body = if response.body.is_empty() {
634 b"{}".as_slice()
635 } else {
636 &response.body
637 };
638 let value = crate::json_decode::from_slice(body)
639 .map_err(|_| Error::new(ErrorKind::Decode))?;
640 let value = crate::validation::parse_one_of_with_limits(
641 schemas,
642 value,
643 options.response_mode(),
644 &self.contract_limits(self.inner.limits.max_response_bytes)?,
645 )
646 .map_err(|error| {
647 Error::new(if error.kind() == ErrorKind::Limit {
648 ErrorKind::Limit
649 } else {
650 ErrorKind::Decode
651 })
652 })?;
653 crate::json_decode::from_value(value).map_err(|_| Error::new(ErrorKind::Decode))
654 })
655 .await
656 .map_err(|error| response.annotate(error).with_operation(name))
657 }
658 #[cfg(feature = "red-team")]
659 pub(crate) async fn send_model_optional<T: Serialize, R: DeserializeOwned>(
660 &self,
661 request: RawRequest,
662 body: &T,
663 request_schema: &str,
664 response_schema: &str,
665 options: Option<&RequestOptions>,
666 ) -> Result<Option<R>> {
667 let options = self.operation_options(options)?;
668 let request = self
669 .prepare_model(request, body, request_schema, &options)
670 .await?;
671 self.send_optional_contract(request, response_schema, Some(&options))
672 .await
673 }
674 #[cfg(any(feature = "dlp", feature = "red-team"))]
675 pub(crate) async fn send_optional_contract<R: DeserializeOwned>(
676 &self,
677 request: RawRequest,
678 schema: &str,
679 options: Option<&RequestOptions>,
680 ) -> Result<Option<R>> {
681 let name = request.name;
682 let options = self.operation_options(options)?;
683 let response = self.send_buffered(request, Some(&options)).await?;
684 if response.body.is_empty() {
685 return Ok(None);
686 }
687 self.decode_contract(&response, schema, &options)
688 .await
689 .map(Some)
690 .map_err(|e| response.annotate(e).with_operation(name))
691 }
692 #[cfg(any(
693 feature = "scan",
694 feature = "management",
695 feature = "gateway",
696 feature = "dlp",
697 feature = "iam",
698 feature = "model-security",
699 feature = "red-team",
700 feature = "agentguard"
701 ))]
702 async fn decode_contract<R: DeserializeOwned>(
703 &self,
704 response: &BufferedResponse,
705 schema: &str,
706 options: &RequestOptions,
707 ) -> Result<R> {
708 options
709 .run(&self.inner.session, async {
710 let body = if response.body.is_empty() {
712 b"{}".as_slice()
713 } else {
714 response.body.as_ref()
715 };
716 let value = crate::json_decode::from_slice(body)
717 .map_err(|_| Error::new(ErrorKind::Decode))?;
718 let value = normalize_contract(
719 schema,
720 value,
721 options.response_mode(),
722 &self.contract_limits(self.inner.limits.max_response_bytes)?,
723 )
724 .map_err(|e| {
725 Error::new(if e.kind() == ErrorKind::Limit {
726 ErrorKind::Limit
727 } else {
728 ErrorKind::Decode
729 })
730 })?;
731 crate::json_decode::from_value(value).map_err(|_| Error::new(ErrorKind::Decode))
732 })
733 .await
734 }
735 #[cfg(feature = "gateway")]
736 pub(crate) async fn send_contract_stream<R: DeserializeOwned>(
737 &self,
738 request: RawRequest,
739 schema: &'static str,
740 terminal: crate::streaming::TerminalPolicy,
741 options: Option<&RequestOptions>,
742 ) -> Result<crate::streaming::ContractStream<R>> {
743 let options = self.operation_options(options)?;
744 let name = request.name;
745 let response = self.send_stream(request, Some(&options)).await?;
746 let status = response.status().as_u16();
747 let request_id = response.request_id().map(str::to_owned);
748 let stream = crate::streaming::SseStream::new(
749 response,
750 self.inner.limits.max_event_bytes,
751 terminal,
752 )?;
753 let contract = crate::streaming::EventContract {
754 schema,
755 operation: name,
756 status,
757 request_id,
758 limits: self.contract_limits(self.inner.limits.max_event_bytes)?,
759 };
760 Ok(crate::streaming::ContractStream::new(
761 stream,
762 contract,
763 options,
764 self.inner.session.clone(),
765 ))
766 }
767 #[cfg(feature = "realtime")]
768 pub(crate) async fn open_realtime(
769 &self,
770 request: RawRequest,
771 settings: &crate::realtime::GatewayRealtimeOptions,
772 options: &RequestOptions,
773 ) -> Result<crate::realtime::GatewayRealtimeConnection> {
774 let lifecycle = crate::observability::Lifecycle::start(
775 crate::observability::Operation::RealtimeUpgrade,
776 );
777 let result = self
778 .open_realtime_observed(request, settings, options, &lifecycle)
779 .await;
780 lifecycle.finish(&result);
781 result
782 }
783 #[cfg(feature = "realtime")]
784 async fn open_realtime_observed(
785 &self,
786 mut request: RawRequest,
787 settings: &crate::realtime::GatewayRealtimeOptions,
788 options: &RequestOptions,
789 lifecycle: &crate::observability::Lifecycle,
790 ) -> Result<crate::realtime::GatewayRealtimeConnection> {
791 use crate::realtime::{GatewayRealtimeConnection, OPERATION, WebSocketRequest};
792 self.require_auth(AuthKind::Runtime)?;
793 settings.validate()?;
794 let mut settings = settings.clone();
795 settings.max_event_bytes = settings
796 .max_event_bytes
797 .min(self.inner.limits.max_event_bytes)
798 .min(settings.max_buffered_bytes);
799 settings.idle_timeout = settings
800 .idle_timeout
801 .min(self.inner.limits.stream_idle_timeout);
802 let validation = self.contract_limits(settings.max_event_bytes)?;
803 request.headers.remove(http::header::USER_AGENT);
805 request.headers.remove("service-name");
806 let (mut url, body) = self.preflight(&request)?;
807 if request.method != Method::GET || !body.is_empty() {
808 return Err(Error::new(ErrorKind::Validation));
809 }
810 let scheme = match url.scheme() {
811 "https" => "wss",
812 "http" => "ws",
813 _ => return Err(Error::new(ErrorKind::Destination)),
814 };
815 url.set_scheme(scheme)
816 .map_err(|_| Error::new(ErrorKind::Destination))?;
817 self.inner
818 .policy
819 .check(&url, DestinationPurpose::Realtime)
820 .map_err(|_| Error::new(ErrorKind::Destination))?;
821 let sent = AtomicBool::new(false);
822 let deadline = options.deadline().min(
823 tokio::time::Instant::now()
824 .checked_add(settings.handshake_timeout)
825 .ok_or_else(|| Error::new(ErrorKind::Configuration))?,
826 );
827 let result = options
828 .run(&self.inner.session, async {
829 tokio::time::timeout_at(deadline, async {
830 let permit = Arc::clone(&self.inner.permits)
831 .acquire_owned()
832 .await
833 .map_err(|_| Error::new(ErrorKind::Cancelled))?;
834 let mut headers = request.headers;
835 self.inner
836 .auth
837 .prepare(&mut headers, &body, false, options)
838 .await?;
839 if headers_exceed(&headers) {
840 return Err(Error::new(ErrorKind::Limit));
841 }
842 for value in headers.values_mut() {
843 value.set_sensitive(true);
844 }
845 options.run(&self.inner.session, async { Ok(()) }).await?;
846 lifecycle.dispatched();
847 sent.store(true, Ordering::Relaxed);
848 let upgrade = settings
849 .connector
850 .connect(WebSocketRequest {
851 url,
852 headers,
853 handshake_timeout: deadline
854 .saturating_duration_since(tokio::time::Instant::now()),
855 max_event_bytes: settings.max_event_bytes,
856 })
857 .await?;
858 lifecycle.response(
859 upgrade.status.as_u16(),
860 error_metadata(&upgrade.headers, None).0.is_some(),
861 );
862 if upgrade.status != StatusCode::SWITCHING_PROTOCOLS {
863 return Err(Error::new(if upgrade.status.is_redirection() {
864 ErrorKind::Destination
865 } else {
866 ErrorKind::Http
867 })
868 .with_status(upgrade.status.as_u16())
869 .with_metadata(
870 error_metadata(&upgrade.headers, None).0,
871 None,
872 retry_after(&upgrade.headers),
873 )
874 .with_failure_kind(crate::FailureKind::Http)
875 .with_outcome(
876 if upgrade.status.is_client_error() || upgrade.status.is_redirection() {
877 RemoteOutcome::Rejected
878 } else {
879 RemoteOutcome::Unknown
880 },
881 ));
882 }
883 Ok(GatewayRealtimeConnection::new(
884 upgrade,
885 permit,
886 options.clone(),
887 self.inner.session.clone(),
888 settings,
889 validation,
890 ))
891 })
892 .await
893 .unwrap_or_else(|_| Err(Error::new(ErrorKind::Deadline)))
894 })
895 .await;
896 result.map_err(|error| {
897 let outcome = if sent.load(Ordering::Relaxed)
898 && error.remote_outcome() == RemoteOutcome::NotSent
899 {
900 RemoteOutcome::Unknown
901 } else {
902 error.remote_outcome()
903 };
904 error.with_operation(OPERATION).with_outcome(outcome)
905 })
906 }
907
908 fn preflight(&self, request: &RawRequest) -> Result<(url::Url, Bytes)> {
909 let url = self
910 .inner
911 .endpoint
912 .resolve(&request.path, &request.segments, &request.query)?;
913 self.inner.endpoint.admit(&url)?;
914 self.inner
915 .policy
916 .check(&url, DestinationPurpose::Api)
917 .map_err(|_| Error::new(ErrorKind::Destination))?;
918 if request.runtime_retries.is_some()
919 && !matches!(&self.inner.auth, Authentication::RuntimeKey { .. })
920 {
921 return Err(Error::new(ErrorKind::Configuration));
922 }
923 let body = request.body.clone().unwrap_or_default();
924 if body.len() > self.inner.limits.max_request_bytes || headers_exceed(&request.headers) {
925 return Err(Error::new(ErrorKind::Limit).with_operation(request.name));
926 }
927 if request
928 .headers
929 .get(http::header::CONTENT_TYPE)
930 .and_then(|h| h.to_str().ok())
931 .and_then(|v| v.parse::<mime::Mime>().ok())
932 .is_some_and(|v| {
933 v.type_() == mime::APPLICATION
934 && (v.subtype() == mime::JSON || v.suffix() == Some(mime::JSON))
935 })
936 && request.body.is_some()
937 && crate::json_decode::from_slice(&body).is_err()
938 {
939 return Err(Error::new(ErrorKind::Validation).with_operation(request.name));
940 }
941 Ok((url, body))
942 }
943 pub async fn send_stream(
947 &self,
948 request: RawRequest,
949 options: Option<&RequestOptions>,
950 ) -> Result<crate::streaming::ResponseStream> {
951 let lifecycle =
952 crate::observability::Lifecycle::start(crate::observability::Operation::HttpStream);
953 let result = self
954 .send_stream_observed(request, options, &lifecycle)
955 .await;
956 lifecycle.finish(&result);
957 result
958 }
959 async fn send_stream_observed(
960 &self,
961 request: RawRequest,
962 options: Option<&RequestOptions>,
963 lifecycle: &crate::observability::Lifecycle,
964 ) -> Result<crate::streaming::ResponseStream> {
965 let (url, body) = self.preflight(&request)?;
966 let explicit_deadline = options.is_some();
967 let options = match options {
968 Some(v) => v.clone(),
969 None => RequestOptions::with_timeout(self.inner.limits.stream_handshake_timeout)?,
970 };
971 let name = request.name;
972 let sent = AtomicBool::new(false);
973 let received = std::sync::Mutex::new(None::<(u16, Option<String>)>);
974 options
975 .run(&self.inner.session, async {
976 let permit = Arc::clone(&self.inner.permits)
977 .acquire_owned()
978 .await
979 .map_err(|_| Error::new(ErrorKind::Cancelled))?;
980 let max_retries = request.runtime_retries.unwrap_or(0);
981 let mut retries = 0;
982 loop {
983 if let Ok(mut slot) = received.lock() {
984 *slot = None;
985 }
986 let mut headers = request.headers.clone();
987 let generation = self
988 .inner
989 .auth
990 .prepare(&mut headers, &body, request.body.is_some(), &options)
991 .await?;
992 if headers_exceed(&headers) {
993 return Err(Error::new(ErrorKind::Limit));
994 }
995 options.run(&self.inner.session, async { Ok(()) }).await?;
996 lifecycle.dispatched();
997 sent.store(true, Ordering::Relaxed);
998 let mut response = match self
999 .inner
1000 .transport
1001 .send(TransportRequest::new(
1002 request.method.clone(),
1003 url.clone(),
1004 headers,
1005 body.clone(),
1006 name,
1007 ))
1008 .await
1009 {
1010 Ok(response) => response,
1011 Err(error) => {
1012 if error.kind() == ErrorKind::Transport && retries < max_retries {
1013 let delay = self.backoff(retries);
1014 retries += 1;
1015 tokio::time::sleep(delay).await;
1016 continue;
1017 }
1018 return Err(error.with_failure_kind(crate::FailureKind::Network));
1019 }
1020 };
1021 let status = response.status;
1022 let request_id = error_metadata(&response.headers, None).0;
1023 lifecycle.response(status.as_u16(), request_id.is_some());
1024 if let Ok(mut slot) = received.lock() {
1025 *slot = Some((status.as_u16(), request_id));
1026 }
1027 if status.is_redirection() {
1028 return Err(Error::new(ErrorKind::Destination)
1029 .with_status(status.as_u16())
1030 .with_outcome(RemoteOutcome::Rejected));
1031 }
1032 if !status.is_success() {
1033 let bytes =
1034 read_limited(&mut response.body, self.inner.limits.max_error_bytes)
1035 .await
1036 .map_err(body_failure)?;
1037 if auth_rejection(status, &response.headers)
1038 && let Some(generation) = generation
1039 {
1040 self.inner.auth.on_unauthorized(generation);
1041 }
1042 let delay = retry_after(&response.headers);
1043 if runtime_retryable(status) && retries < max_retries {
1044 let wait = delay.unwrap_or_else(|| self.backoff(retries));
1045 if wait <= self.inner.limits.max_retry_delay {
1046 retries += 1;
1047 tokio::time::sleep(wait).await;
1048 continue;
1049 }
1050 }
1051 let (id, code) = error_metadata(&response.headers, Some(&bytes));
1052 return Err(Error::new(ErrorKind::Http)
1053 .with_status(status.as_u16())
1054 .with_metadata(id, code, delay)
1055 .with_http_body(&bytes)
1056 .with_outcome(if status.is_client_error() {
1057 RemoteOutcome::Rejected
1058 } else {
1059 RemoteOutcome::Unknown
1060 }));
1061 }
1062 return Ok(crate::streaming::ResponseStream::new(
1064 response,
1065 permit,
1066 self.inner.session.clone(),
1067 &options,
1068 explicit_deadline,
1069 &self.inner.limits,
1070 name,
1071 ));
1072 }
1073 })
1074 .await
1075 .map_err(|error| {
1076 let outcome = if sent.load(Ordering::Relaxed)
1077 && error.remote_outcome() == RemoteOutcome::NotSent
1078 {
1079 RemoteOutcome::Unknown
1080 } else {
1081 error.remote_outcome()
1082 };
1083 let error = if let Some((status, request_id)) =
1084 received.lock().ok().and_then(|s| s.clone())
1085 {
1086 error.with_response_metadata(status, request_id)
1087 } else {
1088 error
1089 };
1090 error.with_operation(name).with_outcome(outcome)
1091 })
1092 }
1093
1094 pub async fn send_bytes(
1095 &self,
1096 request: RawRequest,
1097 options: Option<&RequestOptions>,
1098 ) -> Result<Bytes> {
1099 self.send_buffered(request, options)
1100 .await
1101 .map(|response| response.body)
1102 }
1103 async fn send_buffered(
1104 &self,
1105 request: RawRequest,
1106 options: Option<&RequestOptions>,
1107 ) -> Result<BufferedResponse> {
1108 let lifecycle =
1109 crate::observability::Lifecycle::start(crate::observability::Operation::Http);
1110 let result = self
1111 .send_buffered_observed(request, options, &lifecycle)
1112 .await;
1113 lifecycle.finish(&result);
1114 result
1115 }
1116 async fn send_buffered_observed(
1117 &self,
1118 request: RawRequest,
1119 options: Option<&RequestOptions>,
1120 lifecycle: &crate::observability::Lifecycle,
1121 ) -> Result<BufferedResponse> {
1122 let default_options;
1123 let options = match options {
1124 Some(v) => v,
1125 None => {
1126 default_options =
1127 RequestOptions::with_timeout(self.inner.limits.operation_timeout)?;
1128 &default_options
1129 }
1130 };
1131 let name = request.name;
1132 let (url, body) = self.preflight(&request)?;
1133 let sent = AtomicBool::new(false);
1134 let received = std::sync::Mutex::new(None::<(u16, Option<String>)>);
1135 let max_retries = request.runtime_retries.unwrap_or_else(|| {
1136 options
1137 .retry_limit()
1138 .unwrap_or(self.inner.limits.max_retries)
1139 });
1140 let task = async {
1141 let _permit = self
1142 .inner
1143 .permits
1144 .acquire()
1145 .await
1146 .map_err(|_| Error::new(ErrorKind::Cancelled))?;
1147 let mut retries = 0;
1148 let mut refreshed = false;
1149 loop {
1150 if let Ok(mut slot) = received.lock() {
1151 *slot = None;
1152 }
1153 let mut headers = request.headers.clone();
1154 let generation = self
1155 .inner
1156 .auth
1157 .prepare(&mut headers, &body, request.body.is_some(), options)
1158 .await?;
1159 if headers_exceed(&headers) {
1160 return Err(Error::new(ErrorKind::Limit));
1161 }
1162 options.run(&self.inner.session, async { Ok(()) }).await?;
1163 lifecycle.dispatched();
1164 sent.store(true, Ordering::Relaxed);
1165 let prepared = TransportRequest::new(
1166 request.method.clone(),
1167 url.clone(),
1168 headers,
1169 body.clone(),
1170 name,
1171 );
1172 let mut response = match self.inner.transport.send(prepared).await {
1173 Ok(value) => value,
1174 Err(error) => {
1175 if error.kind() == ErrorKind::Transport
1176 && (request.replay_safe || request.runtime_retries.is_some())
1177 && retries < max_retries
1178 {
1179 let delay = self.backoff(retries);
1180 retries += 1;
1181 tokio::time::sleep(delay).await;
1182 continue;
1183 }
1184 return Err(error.with_failure_kind(crate::FailureKind::Network));
1185 }
1186 };
1187 let status = response.status;
1188 let request_id = error_metadata(&response.headers, None).0;
1189 lifecycle.response(status.as_u16(), request_id.is_some());
1190 if let Ok(mut slot) = received.lock() {
1191 *slot = Some((status.as_u16(), request_id.clone()));
1192 }
1193 if status.is_redirection() {
1194 return Err(Error::new(ErrorKind::Destination)
1195 .with_status(status.as_u16())
1196 .with_outcome(RemoteOutcome::Rejected));
1197 }
1198 let delay = retry_after(&response.headers);
1199 if status.is_success() {
1200 let body =
1201 read_limited(&mut response.body, self.inner.limits.max_response_bytes)
1202 .await
1203 .map_err(body_failure)?;
1204 return Ok(BufferedResponse {
1205 body,
1206 status: status.as_u16(),
1207 request_id,
1208 });
1209 }
1210 let error_body =
1212 read_limited(&mut response.body, self.inner.limits.max_error_bytes)
1213 .await
1214 .map_err(|e| body_failure(e).with_status(status.as_u16()))?;
1215 let (request_id, code) = error_metadata(&response.headers, Some(&error_body));
1216 if auth_rejection(status, &response.headers)
1217 && let Some(generation) = generation
1218 {
1219 let invalidated = self.inner.auth.on_unauthorized(generation);
1220 if request.replay_safe && !refreshed && invalidated && retries < max_retries {
1221 refreshed = true;
1222 retries += 1;
1223 continue;
1224 }
1225 }
1226 if (request.replay_safe && retryable(status)
1227 || request.runtime_retries.is_some() && runtime_retryable(status))
1228 && retries < max_retries
1229 {
1230 let wait = delay.unwrap_or_else(|| self.backoff(retries));
1231 if wait <= self.inner.limits.max_retry_delay {
1232 retries += 1;
1233 tokio::time::sleep(wait).await;
1234 continue;
1235 }
1236 }
1237 let outcome = if status.is_client_error() {
1238 RemoteOutcome::Rejected
1239 } else {
1240 RemoteOutcome::Unknown
1241 };
1242 return Err(Error::new(ErrorKind::Http)
1243 .with_status(status.as_u16())
1244 .with_metadata(request_id, code, delay)
1245 .with_http_body(&error_body)
1246 .with_outcome(outcome));
1247 }
1248 };
1249 options.run(&self.inner.session, task).await.map_err(|e| {
1250 let outcome =
1251 if sent.load(Ordering::Relaxed) && e.remote_outcome() == RemoteOutcome::NotSent {
1252 RemoteOutcome::Unknown
1253 } else {
1254 e.remote_outcome()
1255 };
1256 let e = if let Some((status, request_id)) =
1257 received.lock().ok().and_then(|slot| slot.clone())
1258 {
1259 e.with_response_metadata(status, request_id)
1260 } else {
1261 e
1262 };
1263 e.with_operation(name).with_outcome(outcome)
1264 })
1265 }
1266
1267 pub async fn send_text(
1270 &self,
1271 request: RawRequest,
1272 options: Option<&RequestOptions>,
1273 ) -> Result<String> {
1274 let name = request.name;
1275 let options = self.operation_options(options)?;
1276 let response = self.send_buffered(request, Some(&options)).await?;
1277 options
1278 .run(&self.inner.session, async {
1279 let body = response
1280 .body
1281 .strip_prefix(&[0xef, 0xbb, 0xbf])
1282 .unwrap_or(&response.body);
1283 Ok(String::from_utf8_lossy(body).into_owned())
1284 })
1285 .await
1286 .map_err(|e| response.annotate(e).with_operation(name))
1287 }
1288 pub async fn send_json<R: DeserializeOwned>(
1289 &self,
1290 request: RawRequest,
1291 options: Option<&RequestOptions>,
1292 ) -> Result<R> {
1293 let name = request.name;
1294 let options = self.operation_options(options)?;
1295 let response = self.send_buffered(request, Some(&options)).await?;
1296 options
1297 .run(&self.inner.session, async {
1298 crate::json_decode::from_slice(if response.body.is_empty() {
1299 b"{}"
1300 } else {
1301 &response.body
1302 })
1303 .and_then(crate::json_decode::from_value)
1304 .map_err(|_| Error::new(ErrorKind::Decode))
1305 })
1306 .await
1307 .map_err(|e| response.annotate(e).with_operation(name))
1308 }
1309 pub async fn send_optional_json<R: DeserializeOwned>(
1310 &self,
1311 request: RawRequest,
1312 options: Option<&RequestOptions>,
1313 ) -> Result<Option<R>> {
1314 let name = request.name;
1315 let options = self.operation_options(options)?;
1316 let response = self.send_buffered(request, Some(&options)).await?;
1317 if response.body.is_empty() {
1318 return Ok(None);
1319 }
1320 options
1321 .run(&self.inner.session, async {
1322 crate::json_decode::from_slice(&response.body)
1323 .and_then(crate::json_decode::from_value)
1324 .map(Some)
1325 .map_err(|_| Error::new(ErrorKind::Decode))
1326 })
1327 .await
1328 .map_err(|e| response.annotate(e).with_operation(name))
1329 }
1330 pub async fn send_empty(
1331 &self,
1332 request: RawRequest,
1333 options: Option<&RequestOptions>,
1334 ) -> Result<()> {
1335 self.send_bytes(request, options).await.map(|_| ())
1336 }
1337 fn backoff(&self, retry: u32) -> Duration {
1338 self.inner
1339 .limits
1340 .retry_base_delay
1341 .saturating_mul(1u32 << retry)
1342 .min(self.inner.limits.max_retry_delay)
1343 }
1344}
1345fn auth_rejection(status: StatusCode, headers: &HeaderMap) -> bool {
1346 status == StatusCode::UNAUTHORIZED
1347 || (status == StatusCode::FORBIDDEN
1348 && headers.get("x-opa-decision").is_none_or(|v| v != "false"))
1349}
1350#[cfg(any(
1351 feature = "scan",
1352 feature = "management",
1353 feature = "gateway",
1354 feature = "dlp",
1355 feature = "iam",
1356 feature = "model-security",
1357 feature = "red-team",
1358 feature = "agentguard"
1359))]
1360fn normalize_contract(
1361 schema: &str,
1362 value: serde_json::Value,
1363 mode: crate::validation::Mode,
1364 limits: &crate::validation::ValidationLimits,
1365) -> Result<serde_json::Value> {
1366 if let Some(element) = schema.strip_suffix("[]") {
1367 crate::validation::parse_array_with_limits(element, value, mode, limits)
1368 } else {
1369 crate::validation::parse_with_limits(schema, value, mode, limits)
1370 }
1371}
1372fn retryable(status: StatusCode) -> bool {
1373 matches!(status.as_u16(), 429 | 502 | 503 | 504)
1374}
1375fn retry_after(headers: &HeaderMap) -> Option<Duration> {
1376 let value = headers
1377 .get(http::header::RETRY_AFTER)?
1378 .to_str()
1379 .ok()?
1380 .trim();
1381 if !value.is_empty()
1382 && value.bytes().all(|byte| byte.is_ascii_digit())
1383 && let Ok(seconds) = value.parse::<u64>()
1384 {
1385 return Some(Duration::from_secs(seconds));
1386 }
1387 Some(
1388 httpdate::parse_http_date(value)
1389 .ok()?
1390 .duration_since(SystemTime::now())
1391 .unwrap_or(Duration::ZERO),
1392 )
1393}
1394pub(crate) fn body_failure(error: Error) -> Error {
1395 let kind = if matches!(error.kind(), ErrorKind::Transport | ErrorKind::Io) {
1396 crate::FailureKind::Network
1397 } else {
1398 crate::FailureKind::Http
1399 };
1400 error.with_failure_kind(kind)
1401}
1402pub(crate) fn error_metadata(
1403 headers: &HeaderMap,
1404 body: Option<&[u8]>,
1405) -> (Option<String>, Option<String>) {
1406 let request_id = ["x-request-id", "x-correlation-id", "request-id"]
1407 .iter()
1408 .find_map(|k| {
1409 headers
1410 .get(*k)
1411 .and_then(|v| v.to_str().ok())
1412 .filter(|v| valid_metadata(v))
1413 .map(str::to_owned)
1414 });
1415 let code = body
1416 .and_then(|v| crate::json_decode::from_slice(v).ok())
1417 .and_then(|v| {
1418 v.get("code")
1419 .or_else(|| v.get("error").and_then(|v| v.get("code")))
1420 .or_else(|| v.get("data").and_then(|v| v.get("errorCode")))
1421 .or_else(|| v.get("errorCode"))
1422 .and_then(|v| v.as_str())
1423 .filter(|v| valid_metadata(v))
1424 .map(str::to_owned)
1425 });
1426 (request_id, code)
1427}
1428fn valid_metadata(value: &str) -> bool {
1429 !value.is_empty()
1430 && value.len() <= 128
1431 && value
1432 .bytes()
1433 .all(|v| v.is_ascii_alphanumeric() || matches!(v, b'-' | b'_' | b'.' | b':'))
1434}
1435
1436pub(crate) fn serialize_bounded<T: Serialize>(value: &T, limit: usize) -> Result<Bytes> {
1437 struct DepthFormatter<'a> {
1440 depth: usize,
1441 exceeded: &'a std::cell::Cell<bool>,
1442 }
1443 impl serde_json::ser::Formatter for DepthFormatter<'_> {
1444 fn begin_array<W: Write + ?Sized>(&mut self, writer: &mut W) -> std::io::Result<()> {
1445 self.depth += 1;
1446 if self.depth > 128 {
1447 self.exceeded.set(true);
1448 return Err(std::io::Error::other("JSON depth limit"));
1449 }
1450 writer.write_all(b"[")
1451 }
1452 fn end_array<W: Write + ?Sized>(&mut self, writer: &mut W) -> std::io::Result<()> {
1453 self.depth -= 1;
1454 writer.write_all(b"]")
1455 }
1456 fn begin_object<W: Write + ?Sized>(&mut self, writer: &mut W) -> std::io::Result<()> {
1457 self.depth += 1;
1458 if self.depth > 128 {
1459 self.exceeded.set(true);
1460 return Err(std::io::Error::other("JSON depth limit"));
1461 }
1462 writer.write_all(b"{")
1463 }
1464 fn end_object<W: Write + ?Sized>(&mut self, writer: &mut W) -> std::io::Result<()> {
1465 self.depth -= 1;
1466 writer.write_all(b"}")
1467 }
1468 }
1469 struct Writer {
1470 value: Vec<u8>,
1471 limit: usize,
1472 exceeded: bool,
1473 }
1474 impl Write for Writer {
1475 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
1476 if self
1477 .value
1478 .len()
1479 .checked_add(bytes.len())
1480 .is_none_or(|n| n > self.limit)
1481 {
1482 self.exceeded = true;
1483 return Err(std::io::Error::other("request byte limit"));
1484 }
1485 self.value.extend_from_slice(bytes);
1486 Ok(bytes.len())
1487 }
1488 fn flush(&mut self) -> std::io::Result<()> {
1489 Ok(())
1490 }
1491 }
1492 let mut writer = Writer {
1493 value: Vec::new(),
1494 limit,
1495 exceeded: false,
1496 };
1497 let depth_exceeded = std::cell::Cell::new(false);
1498 let failed = {
1499 let mut serializer = serde_json::Serializer::with_formatter(
1500 &mut writer,
1501 DepthFormatter {
1502 depth: 0,
1503 exceeded: &depth_exceeded,
1504 },
1505 );
1506 value.serialize(&mut serializer).is_err()
1507 };
1508 if failed {
1509 return Err(Error::new(if writer.exceeded || depth_exceeded.get() {
1510 ErrorKind::Limit
1511 } else {
1512 ErrorKind::Validation
1513 }));
1514 }
1515 Ok(Bytes::from(writer.value))
1516}
1517
1518#[cfg(test)]
1519mod tests;