1pub use crate::server::grpc::http1::bounded_headers;
3use bytes::{Buf, Bytes};
4use http::{HeaderMap, HeaderName, HeaderValue, Request, Response, StatusCode};
5use http_body_util::{BodyExt, Full, Limited, StreamBody};
6use hyper::body::Frame;
7use serde::Deserialize;
8use serde_json::{json, Value};
9use std::{
10 collections::BTreeMap,
11 io::Read,
12 sync::{
13 atomic::{AtomicBool, Ordering},
14 Arc, Mutex,
15 },
16};
17use tonic::{body::BoxBody, Code, Status};
18pub const MAX_MESSAGE_BYTES: usize = crate::server::grpc::stream_codec::MAX_MESSAGE_BYTES;
19pub const MAX_END_BYTES: usize = 16 * 1024;
20pub const MAX_MESSAGES: usize = 256;
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Shape {
23 Unary,
24 Stream,
25}
26impl Shape {
27 pub fn content_type(self) -> &'static str {
28 match self {
29 Self::Unary => "application/proto",
30 Self::Stream => "application/connect+proto",
31 }
32 }
33}
34pub fn shape(headers: &HeaderMap) -> Result<Shape, Status> {
35 match unique(headers, "content-type")? {
36 Some("application/proto") => Ok(Shape::Unary),
37 Some("application/connect+proto") => Ok(Shape::Stream),
38 _ => Err(Status::unimplemented(
39 "binary protobuf Connect content type required",
40 )),
41 }
42}
43pub fn unique<'a>(headers: &'a HeaderMap, name: &str) -> Result<Option<&'a str>, Status> {
44 let mut values = headers.get_all(name).iter();
45 let value = values.next();
46 if values.next().is_some() {
47 return Err(Status::invalid_argument("duplicate reserved header"));
48 }
49 value
50 .map(|value| {
51 value
52 .to_str()
53 .map_err(|_| Status::invalid_argument("invalid reserved header"))
54 })
55 .transpose()
56}
57pub fn encoding(headers: &HeaderMap, name: &str) -> Result<bool, Status> {
58 match unique(headers, name)? {
59 None | Some("identity") => Ok(false),
60 Some("gzip") => Ok(true),
61 _ => Err(Status::unimplemented("supported encodings: identity, gzip")),
62 }
63}
64pub fn accepts_gzip(headers: &HeaderMap, name: &str, fallback: bool) -> Result<bool, Status> {
65 let Some(value) = unique(headers, name)? else {
66 return Ok(fallback);
67 };
68 if value.len() > 1024 || value.split(',').count() > 16 {
69 return Err(Status::invalid_argument("encoding list exceeds bound"));
70 }
71 Ok(value.split(',').any(|value| value.trim() == "gzip"))
72}
73pub fn timeout_ms(headers: &HeaderMap) -> Result<Option<u64>, Status> {
74 let Some(value) = unique(headers, "connect-timeout-ms")? else {
75 return Ok(None);
76 };
77 if value.is_empty() || value.len() > 10 || !value.bytes().all(|byte| byte.is_ascii_digit()) {
78 return Err(Status::invalid_argument(
79 "connect-timeout-ms must have 1..10 decimal digits",
80 ));
81 }
82 let n = value
83 .parse::<u64>()
84 .map_err(|_| Status::invalid_argument("invalid Connect timeout"))?;
85 if n == 0 {
86 return Err(Status::invalid_argument("Connect timeout must be positive"));
87 }
88 Ok(Some(n))
89}
90pub fn inflate(input: &[u8], maximum: usize) -> Result<Vec<u8>, Status> {
91 if input.is_empty() {
92 return Ok(Vec::new());
93 }
94 let mut output = Vec::new();
95 flate2::bufread::MultiGzDecoder::new(input)
96 .take((maximum + 1) as u64)
97 .read_to_end(&mut output)
98 .map_err(|_| Status::invalid_argument("invalid gzip member or checksum"))?;
99 if output.len() > maximum {
100 return Err(Status::resource_exhausted("expanded payload exceeds bound"));
101 }
102 Ok(output)
103}
104pub fn frame(flag: u8, payload: &[u8]) -> Bytes {
105 let mut bytes = Vec::with_capacity(payload.len() + 5);
106 bytes.push(flag);
107 bytes.extend((payload.len() as u32).to_be_bytes());
108 bytes.extend(payload);
109 bytes.into()
110}
111pub fn body(bytes: Bytes) -> BoxBody {
112 Full::new(bytes)
113 .map_err(|never| match never {})
114 .boxed_unsync()
115}
116pub async fn collect(body: BoxBody, maximum: usize) -> Result<Bytes, Status> {
117 Limited::new(request_body(body), maximum + 1)
118 .collect()
119 .await
120 .map_err(|_| Status::resource_exhausted("body exceeds bound or has HTTP trailers"))
121 .map(|collected| collected.to_bytes())
122}
123pub fn request_body(body: BoxBody) -> BoxBody {
124 StreamBody::new(futures::stream::try_unfold(body, |mut body| async move {
125 let Some(frame) = body.frame().await else {
126 return Ok(None);
127 };
128 let frame = frame?;
129 if !frame.is_data() {
130 return Err(Status::invalid_argument("Connect forbids HTTP trailers"));
131 }
132 Ok(Some((frame, body)))
133 }))
134 .boxed_unsync()
135}
136pub fn unary_request(bytes: &[u8], compressed: bool) -> Result<Bytes, Status> {
137 if bytes.len() > MAX_MESSAGE_BYTES {
138 return Err(Status::resource_exhausted("request exceeds 4 MiB"));
139 }
140 let expanded;
141 let bytes = if compressed {
142 expanded = inflate(bytes, MAX_MESSAGE_BYTES)?;
143 expanded.as_slice()
144 } else {
145 bytes
146 };
147 Ok(frame(0, bytes))
148}
149pub fn streaming_request(bytes: &[u8], gzip: bool) -> Result<Bytes, Status> {
150 if bytes.len() < 5 {
151 return Err(Status::invalid_argument(
152 "one complete request envelope required",
153 ));
154 }
155 let length = u32::from_be_bytes(bytes[1..5].try_into().unwrap()) as usize;
156 if length > MAX_MESSAGE_BYTES || bytes.len() > MAX_MESSAGE_BYTES + 5 {
157 return Err(Status::resource_exhausted("request exceeds 4 MiB"));
158 }
159 if !matches!(bytes[0], 0 | 1) || (bytes[0] == 1 && !gzip) || length != bytes.len() - 5 {
160 return Err(Status::invalid_argument(
161 "one protobuf request envelope and EOF required",
162 ));
163 }
164 if bytes[0] == 1 {
166 inflate(&bytes[5..], MAX_MESSAGE_BYTES)?;
167 }
168 Ok(Bytes::copy_from_slice(bytes))
169}
170pub fn code_name(code: Code) -> &'static str {
171 match code {
172 Code::Ok => "ok",
173 Code::Cancelled => "canceled",
174 Code::Unknown => "unknown",
175 Code::InvalidArgument => "invalid_argument",
176 Code::DeadlineExceeded => "deadline_exceeded",
177 Code::NotFound => "not_found",
178 Code::AlreadyExists => "already_exists",
179 Code::PermissionDenied => "permission_denied",
180 Code::ResourceExhausted => "resource_exhausted",
181 Code::FailedPrecondition => "failed_precondition",
182 Code::Aborted => "aborted",
183 Code::OutOfRange => "out_of_range",
184 Code::Unimplemented => "unimplemented",
185 Code::Internal => "internal",
186 Code::Unavailable => "unavailable",
187 Code::DataLoss => "data_loss",
188 Code::Unauthenticated => "unauthenticated",
189 }
190}
191fn named_code(name: &str) -> Option<Code> {
192 (1..=16)
193 .map(Code::from_i32)
194 .find(|code| code_name(*code) == name)
195}
196pub fn http_status(code: Code) -> StatusCode {
197 StatusCode::from_u16(match code {
198 Code::Ok => 200,
199 Code::Cancelled => 499,
200 Code::InvalidArgument | Code::FailedPrecondition | Code::OutOfRange => 400,
201 Code::DeadlineExceeded => 504,
202 Code::NotFound => 404,
203 Code::AlreadyExists | Code::Aborted => 409,
204 Code::PermissionDenied => 403,
205 Code::ResourceExhausted => 429,
206 Code::Unimplemented => 501,
207 Code::Unavailable => 503,
208 Code::Unauthenticated => 401,
209 _ => 500,
210 })
211 .unwrap()
212}
213pub fn inferred_code(status: StatusCode) -> Code {
214 match status.as_u16() {
215 400 => Code::Internal,
216 401 => Code::Unauthenticated,
217 403 => Code::PermissionDenied,
218 404 => Code::Unimplemented,
219 429 | 502 | 503 | 504 => Code::Unavailable,
220 _ => Code::Unknown,
221 }
222}
223pub fn error_json(status: &Status) -> Value {
224 json!({"code":code_name(status.code()),"message":crate::utils::truncate_for_llm(status.message(),4096)})
225}
226#[derive(Deserialize)]
227struct Error {
228 code: String,
229 #[serde(default)]
230 message: String,
231 #[serde(default)]
232 details: Vec<Value>,
233}
234impl Error {
235 fn status(self) -> Result<Status, Status> {
236 let code =
237 named_code(&self.code).ok_or_else(|| Status::internal("invalid Connect error code"))?;
238 if self.message.len() > 4096 || !self.details.is_empty() {
239 return Err(Status::internal(
240 "error message bound or unsupported binary details",
241 ));
242 }
243 Ok(Status::new(code, self.message))
244 }
245}
246#[derive(Default, Clone, Debug)]
247pub struct Metadata(pub BTreeMap<String, Vec<String>>);
248impl<'de> Deserialize<'de> for Metadata {
249 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
250 struct Visitor;
251 impl<'de> serde::de::Visitor<'de> for Visitor {
252 type Value = Metadata;
253 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
254 f.write_str("bounded ASCII metadata arrays")
255 }
256 fn visit_map<A: serde::de::MapAccess<'de>>(
257 self,
258 mut map: A,
259 ) -> Result<Metadata, A::Error> {
260 let mut result = Metadata::default();
261 let mut total = 0usize;
262 let mut values = 0usize;
263 while let Some(key) = map.next_key::<String>()? {
264 let key = key.to_ascii_lowercase();
265 if result.0.len() >= 16 || !metadata_key(&key) || result.0.contains_key(&key) {
266 return Err(serde::de::Error::custom(
267 "metadata name/count/duplicate bound",
268 ));
269 }
270 let entries = map.next_value::<Vec<String>>()?;
271 if entries.is_empty() {
272 return Err(serde::de::Error::custom("metadata needs a value"));
273 }
274 for value in &entries {
275 values += 1;
276 total = total.saturating_add(key.len() + value.len());
277 if values > 32 || total > 8192 || !metadata_value(value) {
278 return Err(serde::de::Error::custom("metadata value/total bound"));
279 }
280 }
281 result.0.insert(key, entries);
282 }
283 Ok(result)
284 }
285 }
286 deserializer.deserialize_map(Visitor)
287 }
288}
289fn metadata_key(key: &str) -> bool {
290 !key.is_empty()
291 && key.len() <= 128
292 && key.bytes().all(|byte| {
293 byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'_' | b'-' | b'.')
294 })
295 && !key.ends_with("-bin")
296 && !key.starts_with("connect-")
297 && !key.starts_with("grpc-")
298 && !key.starts_with("trailer-")
299 && !matches!(
300 key,
301 "host"
302 | "te"
303 | "content-type"
304 | "content-length"
305 | "transfer-encoding"
306 | "content-encoding"
307 | "accept-encoding"
308 | "connection"
309 | "origin"
310 | "accept"
311 | "user-agent"
312 | "x-user-agent"
313 | "x-grpc-web"
314 )
315}
316fn metadata_value(value: &str) -> bool {
317 !value.is_empty() && value.len() <= 1024 && value.bytes().all(|byte| (32..=126).contains(&byte))
318}
319impl Metadata {
320 pub fn json(&self) -> Value {
321 json!(self.0)
322 }
323 pub fn from_action(value: &Value) -> Result<Self, Status> {
324 let object = value
325 .as_object()
326 .ok_or_else(|| Status::invalid_argument("metadata must be an object"))?;
327 let arrays: serde_json::Map<String, Value> = object
328 .iter()
329 .map(|(key, value)| (key.clone(), json!([value])))
330 .collect();
331 serde_json::from_value(Value::Object(arrays))
332 .map_err(|_| Status::invalid_argument("metadata exceeds selected ASCII bounds"))
333 }
334 pub fn append_headers(&self, headers: &mut HeaderMap, prefix: &str) -> Result<(), Status> {
335 for (key, values) in &self.0 {
336 let name: HeaderName = format!("{prefix}{key}")
337 .parse()
338 .map_err(|_| Status::internal("invalid metadata name"))?;
339 for value in values {
340 headers.append(
341 name.clone(),
342 HeaderValue::from_str(value)
343 .map_err(|_| Status::internal("invalid metadata value"))?,
344 );
345 }
346 }
347 Ok(())
348 }
349 pub fn from_headers(headers: &HeaderMap, prefix: &str) -> Result<Self, Status> {
350 let mut value = serde_json::Map::new();
351 for key in headers.keys() {
352 let name = key.as_str();
353 let selected = if prefix.is_empty() {
354 if metadata_key(name)
355 && !name.starts_with("access-control-")
356 && !matches!(
357 name,
358 "date" | "server" | "vary" | "retry-after" | "keep-alive"
359 )
360 {
361 Some(name)
362 } else {
363 None
364 }
365 } else {
366 name.strip_prefix(prefix)
367 };
368 if let Some(name) = selected {
369 let values = headers
370 .get_all(key)
371 .iter()
372 .map(|value| {
373 value
374 .to_str()
375 .map(Value::from)
376 .map_err(|_| Status::internal("non-ASCII metadata"))
377 })
378 .collect::<Result<Vec<_>, _>>()?;
379 value.insert(name.into(), Value::Array(values));
380 }
381 }
382 serde_json::from_value(Value::Object(value))
383 .map_err(|_| Status::resource_exhausted("response metadata exceeds bounds"))
384 }
385}
386fn present_error<'de, D: serde::Deserializer<'de>>(d: D) -> Result<Option<Error>, D::Error> {
387 Error::deserialize(d).map(Some)
388}
389#[derive(Deserialize)]
390struct EndStream {
391 #[serde(default, deserialize_with = "present_error")]
392 error: Option<Error>,
393 #[serde(default)]
394 metadata: Metadata,
395}
396pub fn end_stream(bytes: &[u8]) -> Result<HeaderMap, Status> {
397 if bytes.len() > MAX_END_BYTES {
398 return Err(Status::resource_exhausted("EndStream exceeds 16 KiB"));
399 }
400 let end: EndStream =
401 serde_json::from_slice(bytes).map_err(|_| Status::internal("invalid EndStream JSON"))?;
402 let status = match end.error {
403 Some(error) => error.status()?,
404 None => Status::ok(""),
405 };
406 let mut headers = HeaderMap::new();
407 end.metadata.append_headers(&mut headers, "")?;
408 status.add_header(&mut headers)?;
409 Ok(headers)
410}
411#[derive(Default)]
412pub struct ResponseMetadata {
413 pub leading: Metadata,
414 pub trailing: Metadata,
415 pub sealed: bool,
416}
417#[derive(Clone)]
418pub struct RpcMetadata {
419 pub request: Value,
420 pub response: Arc<Mutex<ResponseMetadata>>,
421}
422impl RpcMetadata {
423 pub fn new(headers: &HeaderMap) -> Result<Self, Status> {
424 Ok(Self {
425 request: Metadata::from_headers(headers, "")?.json(),
426 response: Arc::new(Mutex::new(ResponseMetadata::default())),
427 })
428 }
429 pub fn apply(&self, action: &Value) -> Result<(), Status> {
430 let value = Metadata::from_action(&action["metadata"])?;
431 let mut response = self.response.lock().unwrap_or_else(|e| e.into_inner());
432 match action["phase"].as_str() {
433 Some("headers") if !response.sealed => response.leading = value,
434 Some("trailers") => response.trailing = value,
435 _ => {
436 return Err(Status::failed_precondition(
437 "leading metadata requires first response not yet emitted",
438 ))
439 }
440 }
441 Ok(())
442 }
443}
444pub fn metadata_action() -> crate::llm::actions::ActionDefinition {
445 use crate::llm::actions::{ActionDefinition, Parameter};
446 use crate::protocol::LogTemplate;
447 ActionDefinition {
448 name: "connect_rpc_metadata".into(),
449 description: "Replace bounded ASCII response metadata; headers before first message, trailers before finish".into(),
450 parameters: vec![
451 Parameter {
452 name: "phase".into(),
453 type_hint: "string".into(),
454 description: "Set headers before the first response message, or trailers before finishing the RPC".into(),
455 required: true,
456 },
457 Parameter {
458 name: "metadata".into(),
459 type_hint: "object".into(),
460 description: "At most 16 lowercase ASCII keys/string values, 8 KiB; no reserved or binary names".into(),
461 required: true,
462 },
463 ],
464 example: json!({"type":"connect_rpc_metadata","phase":"trailers","metadata":{"x-note":"done"}}),
465 log_template: Some(LogTemplate::new().with_info("ConnectRPC response metadata replaced")),
466 }
467}
468pub fn validate_metadata_action(action: &Value) -> anyhow::Result<()> {
469 anyhow::ensure!(
470 matches!(action["phase"].as_str(), Some("headers" | "trailers")),
471 "phase must be headers or trailers"
472 );
473 Metadata::from_action(&action["metadata"])?;
474 Ok(())
475}
476
477enum Item {
478 Data(u8, Bytes),
479 Trailers(HeaderMap),
480 Eof,
481}
482struct Reader {
483 body: BoxBody,
484 chunk: Bytes,
485 trailers: Option<HeaderMap>,
486 ended: bool,
487 native: bool,
488}
489impl Reader {
490 fn new(body: BoxBody, native: bool) -> Self {
491 Self {
492 body,
493 chunk: Bytes::new(),
494 trailers: None,
495 ended: false,
496 native,
497 }
498 }
499 async fn available(&mut self) -> Result<bool, Status> {
500 while self.chunk.is_empty() && !self.ended && self.trailers.is_none() {
501 match self.body.frame().await {
502 None => self.ended = true,
503 Some(Err(status)) => return Err(status),
504 Some(Ok(frame)) => match frame.into_data() {
505 Ok(data) => self.chunk = data,
506 Err(frame) => {
507 if !self.native {
508 return Err(Status::internal("Connect forbids HTTP trailers"));
509 }
510 self.trailers = Some(
511 frame
512 .into_trailers()
513 .map_err(|_| Status::internal("invalid native frame"))?,
514 );
515 }
516 },
517 }
518 }
519 Ok(!self.chunk.is_empty())
520 }
521 async fn read(&mut self, n: usize, out: &mut Vec<u8>) -> Result<(), Status> {
522 let mut left = n;
523 while left > 0 {
524 if !self.available().await? {
525 return Err(Status::internal("truncated envelope"));
526 }
527 let n = left.min(self.chunk.len());
528 out.extend(&self.chunk[..n]);
529 self.chunk.advance(n);
530 left -= n;
531 }
532 Ok(())
533 }
534 async fn next(&mut self) -> Result<Item, Status> {
535 if !self.available().await? {
536 return Ok(match self.trailers.take() {
537 Some(headers) => Item::Trailers(headers),
538 None => Item::Eof,
539 });
540 }
541 let mut prefix = Vec::with_capacity(5);
542 self.read(5, &mut prefix).await?;
543 let flag = prefix[0];
544 if (self.native && !matches!(flag, 0 | 1)) || (!self.native && flag > 3) {
545 return Err(Status::internal("unsupported envelope flag"));
546 }
547 let length = u32::from_be_bytes(prefix[1..5].try_into().unwrap()) as usize;
548 let maximum = if !self.native && flag & 2 != 0 {
549 MAX_END_BYTES
550 } else {
551 MAX_MESSAGE_BYTES
552 };
553 if length > maximum {
554 return Err(Status::resource_exhausted("envelope prefix exceeds bound"));
555 }
556 let mut payload = Vec::with_capacity(length);
557 self.read(length, &mut payload).await?;
558 Ok(Item::Data(flag, payload.into()))
559 }
560}
561pub fn client_stream_body(body: BoxBody, gzip: bool, complete: Arc<AtomicBool>) -> BoxBody {
562 StreamBody::new(futures::stream::try_unfold(
563 (Reader::new(body, false), 0usize, false),
564 move |(mut reader, count, ended)| {
565 let complete = complete.clone();
566 async move {
567 if ended {
568 return Ok(None);
569 }
570 let Item::Data(flag, payload) = reader.next().await? else {
571 return Err(Status::internal("response lacks final EndStream"));
572 };
573 if flag & 1 != 0 && !gzip {
574 return Err(Status::internal(
575 "compression flag without gzip negotiation",
576 ));
577 }
578 if flag & 2 != 0 {
579 let expanded;
580 let payload = if flag & 1 != 0 {
581 expanded = inflate(&payload, MAX_END_BYTES)?;
582 expanded.as_slice()
583 } else {
584 payload.as_ref()
585 };
586 let trailers = end_stream(payload)?;
587 if !matches!(reader.next().await?, Item::Eof) {
588 return Err(Status::internal("data follows EndStream"));
589 }
590 complete.store(true, Ordering::Relaxed);
591 Ok(Some((Frame::trailers(trailers), (reader, count, true))))
592 } else {
593 if count >= MAX_MESSAGES {
594 return Err(Status::resource_exhausted("response exceeds 256 messages"));
595 }
596 Ok(Some((
597 Frame::data(frame(flag, &payload)),
598 (reader, count + 1, false),
599 )))
600 }
601 }
602 },
603 ))
604 .boxed_unsync()
605}
606pub fn failure(status: Status, shape: Shape, close: bool) -> Response<BoxBody> {
607 let value = if shape == Shape::Stream {
608 json!({"error":error_json(&status)})
609 } else {
610 error_json(&status)
611 };
612 let bytes = serde_json::to_vec(&value).expect("bounded error JSON");
613 let mut response = Response::new(body(if shape == Shape::Stream {
614 frame(2, &bytes)
615 } else {
616 bytes.into()
617 }));
618 *response.status_mut() = if shape == Shape::Stream {
619 StatusCode::OK
620 } else {
621 http_status(status.code())
622 };
623 response.headers_mut().insert(
624 "content-type",
625 HeaderValue::from_static(if shape == Shape::Stream {
626 shape.content_type()
627 } else {
628 "application/json"
629 }),
630 );
631 if close {
632 response
633 .headers_mut()
634 .insert("connection", HeaderValue::from_static("close"));
635 }
636 response
637}
638pub async fn server_response(
639 response: Response<BoxBody>,
640 shape: Shape,
641 metadata: RpcMetadata,
642) -> Result<Response<BoxBody>, Status> {
643 let gzip = encoding(response.headers(), "grpc-encoding")?;
644 if let Some(status) = Status::from_header_map(response.headers()) {
645 return Ok(failure(status, shape, false));
646 }
647 let mut reader = Reader::new(response.into_body(), true);
648 if shape == Shape::Unary {
649 let mut result = None;
650 let mut count = 0;
651 let status = loop {
652 match reader.next().await? {
653 Item::Data(flag, payload) => {
654 count += 1;
655 if count > 1 {
656 return Err(Status::internal("unary requires one response"));
657 }
658 if flag == 1 && !gzip {
659 return Err(Status::internal("unnegotiated response compression"));
660 }
661 result = Some((flag, payload));
662 }
663 Item::Trailers(headers) => {
664 break Status::from_header_map(&headers)
665 .ok_or_else(|| Status::internal("missing native status"))?
666 }
667 Item::Eof => return Err(Status::internal("missing native status")),
668 }
669 };
670 if !matches!(reader.next().await?, Item::Eof) {
671 return Err(Status::internal("data after native status"));
672 }
673 if status.code() != Code::Ok {
674 let mut response = failure(status, shape, false);
675 let mut md = metadata.response.lock().unwrap_or_else(|e| e.into_inner());
676 md.sealed = true;
677 md.leading.append_headers(response.headers_mut(), "")?;
678 md.trailing
679 .append_headers(response.headers_mut(), "trailer-")?;
680 return Ok(response);
681 }
682 let (flag, payload) =
683 result.ok_or_else(|| Status::internal("unary requires one response"))?;
684 let mut response = Response::new(body(payload));
685 response.headers_mut().insert(
686 "content-type",
687 HeaderValue::from_static(shape.content_type()),
688 );
689 if flag == 1 {
690 response
691 .headers_mut()
692 .insert("content-encoding", HeaderValue::from_static("gzip"));
693 }
694 let mut md = metadata.response.lock().unwrap_or_else(|e| e.into_inner());
695 md.sealed = true;
696 md.leading.append_headers(response.headers_mut(), "")?;
697 md.trailing
698 .append_headers(response.headers_mut(), "trailer-")?;
699 Ok(response)
700 } else {
701 let first = reader.next().await?;
703 let mut response = Response::new(tonic::body::empty_body());
704 response.headers_mut().insert(
705 "content-type",
706 HeaderValue::from_static(shape.content_type()),
707 );
708 if gzip {
709 response
710 .headers_mut()
711 .insert("connect-content-encoding", HeaderValue::from_static("gzip"));
712 }
713 {
714 let mut md = metadata.response.lock().unwrap_or_else(|e| e.into_inner());
715 md.sealed = true;
716 md.leading.append_headers(response.headers_mut(), "")?;
717 }
718 *response.body_mut() = StreamBody::new(futures::stream::try_unfold(
719 (reader, Some(first), 0usize, false),
720 move |(mut reader, first, count, ended)| {
721 let metadata = metadata.clone();
722 async move {
723 if ended {
724 return Ok(None);
725 }
726 let item = match first {
727 Some(item) => item,
728 None => reader.next().await?,
729 };
730 match item {
731 Item::Data(flag, payload) => {
732 if count >= MAX_MESSAGES {
733 return Err(Status::resource_exhausted(
734 "response count exceeds 256",
735 ));
736 }
737 Ok(Some((
738 Frame::data(frame(flag, &payload)),
739 (reader, None, count + 1, false),
740 )))
741 }
742 Item::Trailers(headers) => {
743 let status = Status::from_header_map(&headers)
744 .ok_or_else(|| Status::internal("missing native status"))?;
745 if !matches!(reader.next().await?, Item::Eof) {
746 return Err(Status::internal("data after native status"));
747 }
748 let md = metadata.response.lock().unwrap_or_else(|e| e.into_inner());
749 let mut end = json!({"metadata":md.trailing.0});
750 if status.code() != Code::Ok {
751 end["error"] = error_json(&status);
752 }
753 let bytes = serde_json::to_vec(&end)
754 .map_err(|_| Status::internal("EndStream encoding failed"))?;
755 if bytes.len() > MAX_END_BYTES {
756 return Err(Status::resource_exhausted("EndStream exceeds bound"));
757 }
758 Ok(Some((
759 Frame::data(frame(2, &bytes)),
760 (reader, None, count, true),
761 )))
762 }
763 Item::Eof => Err(Status::internal("missing native status")),
764 }
765 }
766 },
767 ))
768 .boxed_unsync();
769 Ok(response)
770 }
771}
772pub async fn client_request(
773 mut request: Request<BoxBody>,
774 shape: Shape,
775) -> Result<Request<BoxBody>, Status> {
776 let gzip = encoding(request.headers(), "grpc-encoding")?;
777 let bytes = collect(
778 std::mem::replace(request.body_mut(), tonic::body::empty_body()),
779 MAX_MESSAGE_BYTES + 5,
780 )
781 .await?;
782 let bytes = streaming_request(&bytes, gzip)?;
783 let timeout = crate::server::grpc::streaming::timeout(
784 request.headers(),
785 std::time::Duration::from_secs(3600),
786 )?;
787 for name in [
788 "grpc-encoding",
789 "grpc-accept-encoding",
790 "grpc-timeout",
791 "content-type",
792 "te",
793 "user-agent",
794 ] {
795 request.headers_mut().remove(name);
796 }
797 request.headers_mut().insert(
798 "content-type",
799 HeaderValue::from_static(shape.content_type()),
800 );
801 request
802 .headers_mut()
803 .insert("connect-protocol-version", HeaderValue::from_static("1"));
804 request.headers_mut().insert(
805 "connect-timeout-ms",
806 HeaderValue::from_str(&timeout.as_millis().max(1).to_string()).unwrap(),
807 );
808 let actual_gzip = bytes[0] == 1;
809 if shape == Shape::Unary {
810 *request.body_mut() = body(bytes.slice(5..));
811 if actual_gzip {
812 request
813 .headers_mut()
814 .insert("content-encoding", HeaderValue::from_static("gzip"));
815 }
816 request
817 .headers_mut()
818 .insert("accept-encoding", HeaderValue::from_static("gzip"));
819 } else {
820 *request.body_mut() = body(bytes);
821 if gzip {
822 request
823 .headers_mut()
824 .insert("connect-content-encoding", HeaderValue::from_static("gzip"));
825 }
826 request
827 .headers_mut()
828 .insert("connect-accept-encoding", HeaderValue::from_static("gzip"));
829 }
830 Ok(request)
831}
832pub async fn client_response(
833 response: Response<BoxBody>,
834 shape: Shape,
835 complete: Arc<AtomicBool>,
836) -> Result<Response<BoxBody>, Status> {
837 if !bounded_headers(response.headers()) {
838 return Err(Status::resource_exhausted("response headers exceed bound"));
839 }
840 let status = response.status();
841 let ct = unique(response.headers(), "content-type")?.map(str::to_owned);
842 let gzip = encoding(
843 response.headers(),
844 if shape == Shape::Unary {
845 "content-encoding"
846 } else {
847 "connect-content-encoding"
848 },
849 )?;
850 if shape == Shape::Stream && unique(response.headers(), "content-encoding")?.is_some() {
851 return Err(Status::internal("stream HTTP content encoding excluded"));
852 }
853 let leading = Metadata::from_headers(response.headers(), "")?;
854 let trailing = Metadata::from_headers(response.headers(), "trailer-")?;
855 let (mut parts, body) = response.into_parts();
856 parts.status = StatusCode::OK;
857 parts.headers = HeaderMap::new();
858 parts
859 .headers
860 .insert("content-type", HeaderValue::from_static("application/grpc"));
861 leading.append_headers(&mut parts.headers, "")?;
862 if shape == Shape::Unary || status != StatusCode::OK {
863 let maximum = if status == StatusCode::OK {
864 MAX_MESSAGE_BYTES
865 } else {
866 MAX_END_BYTES
867 };
868 let bytes = collect(body, maximum).await?;
869 if bytes.len() > maximum {
870 return Err(Status::resource_exhausted("response body exceeds bound"));
871 }
872 let expanded;
873 let bytes = if gzip {
874 expanded = inflate(&bytes, maximum)?;
875 expanded.as_slice()
876 } else {
877 bytes.as_ref()
878 };
879 complete.store(true, Ordering::Relaxed);
880 if status != StatusCode::OK {
881 let fallback = Status::new(inferred_code(status), "Connect HTTP error");
882 let error = if ct.as_deref() == Some("application/json") {
883 serde_json::from_slice::<Error>(bytes)
884 .ok()
885 .and_then(|e| e.status().ok())
886 .unwrap_or(fallback)
887 } else {
888 fallback
889 };
890 error.add_header(&mut parts.headers)?;
891 trailing.append_headers(&mut parts.headers, "")?;
892 return Ok(Response::from_parts(parts, tonic::body::empty_body()));
893 }
894 if ct.as_deref() != Some(shape.content_type()) {
895 return Err(Status::internal("expected binary Connect response"));
896 }
897 let mut headers = HeaderMap::new();
898 trailing.append_headers(&mut headers, "")?;
899 Status::ok("").add_header(&mut headers)?;
900 let body = StreamBody::new(futures::stream::iter([
901 Ok::<_, Status>(Frame::data(frame(0, bytes))),
902 Ok(Frame::trailers(headers)),
903 ]))
904 .boxed_unsync();
905 Ok(Response::from_parts(parts, body))
906 } else {
907 if ct.as_deref() != Some(shape.content_type()) {
908 return Err(Status::internal("expected binary Connect stream"));
909 }
910 if gzip {
911 parts
912 .headers
913 .insert("grpc-encoding", HeaderValue::from_static("gzip"));
914 }
915 Ok(Response::from_parts(
916 parts,
917 client_stream_body(body, gzip, complete),
918 ))
919 }
920}