1use crate::{Error, Exchange, wire};
5use base_endpoint::stream::WritableStream;
6use base_http::{
7 InterimHandler, Response, ResponseMetadata, Status, Version,
8 body::{BodyFrame, ReadableBody},
9};
10use base_scheduler::Cancellation;
11use std::{
12 future::{Future, poll_fn},
13 io,
14 pin::pin,
15 task::Poll,
16};
17
18async fn wait<F: Future>(future: F, cancellation: &Cancellation) -> Result<F::Output, Error> {
19 if cancellation.is_never() {
20 return Ok(future.await);
21 }
22 let mut future = pin!(future);
23 let mut cancelled = pin!(cancellation.cancelled());
24 poll_fn(|cx| {
25 if cancelled.as_mut().poll(cx).is_ready() {
26 return Poll::Ready(Err(Error::Cancelled));
27 }
28 future.as_mut().poll(cx).map(Ok)
29 })
30 .await
31}
32
33const OUTPUT_BATCH_LIMIT: usize = 16 * 1024;
35
36async fn send_all<W: WritableStream>(
37 writer: &mut W,
38 mut bytes: &[u8],
39 cancellation: &Cancellation,
40) -> Result<(), Error> {
41 while !bytes.is_empty() {
42 let n = writer.write(bytes, cancellation).await?;
43 if n == 0 {
44 return Err(
45 io::Error::new(io::ErrorKind::WriteZero, "HTTP output made no progress").into(),
46 );
47 }
48 bytes = &bytes[n..];
49 }
50 Ok(())
51}
52
53pub struct ResponseWriter<W> {
56 writer: W,
57 pending: Vec<u8>,
58 usable: bool,
59 continue_sent: bool,
60}
61
62impl<W: WritableStream> ResponseWriter<W> {
63 pub fn new(writer: W) -> Self {
64 Self {
65 writer,
66 pending: Vec::new(),
67 usable: true,
68 continue_sent: false,
69 }
70 }
71 pub fn is_usable(&self) -> bool {
72 self.usable
73 }
74 pub fn interim(&mut self, version: Version) -> InterimWriter<'_, W> {
75 InterimWriter {
76 output: self,
77 version,
78 }
79 }
80
81 pub async fn reject(
83 &mut self,
84 status: Status,
85 cancellation: &Cancellation,
86 ) -> Result<(), Error> {
87 cancellation.check()?;
88 if !self.usable {
89 return Err(Error::Invalid("response output is not reusable"));
90 }
91 self.usable = false;
92 self.write_start_line(status, Version::Http11);
93 self.buffer(b"Content-Length: 0\r\nConnection: close\r\n\r\n");
94 self.flush(cancellation).await
95 }
96
97 pub(crate) async fn write_all(
98 &mut self,
99 bytes: &[u8],
100 cancellation: &Cancellation,
101 ) -> Result<(), Error> {
102 cancellation.check()?;
103 if bytes.len() > OUTPUT_BATCH_LIMIT - self.pending.len() {
104 self.send_pending(cancellation).await?;
105 }
106 if bytes.len() >= OUTPUT_BATCH_LIMIT {
107 return send_all(&mut self.writer, bytes, cancellation).await;
109 }
110 self.buffer(bytes);
111 Ok(())
112 }
113
114 fn reserve(&mut self, additional: usize) {
115 let required = self.pending.len() + additional;
116 if required > self.pending.capacity() {
117 let capacity = required
118 .max(self.pending.capacity().saturating_mul(2))
119 .clamp(256, OUTPUT_BATCH_LIMIT);
120 self.pending.reserve_exact(capacity - self.pending.len());
121 }
122 }
123
124 async fn send_pending(&mut self, cancellation: &Cancellation) -> Result<(), Error> {
125 send_all(&mut self.writer, &self.pending, cancellation).await?;
128 self.pending.clear();
129 Ok(())
130 }
131
132 fn buffer(&mut self, bytes: &[u8]) {
133 self.reserve(bytes.len());
134 self.pending.extend_from_slice(bytes);
135 }
136
137 fn write_start_line(&mut self, status: Status, version: Version) {
139 if status == Status::OK && version == Version::Http11 {
140 self.buffer(b"HTTP/1.1 200 OK\r\n");
141 return;
142 }
143 let version = match version {
144 Version::Http10 => b"HTTP/1.0 ",
145 Version::Http11 => b"HTTP/1.1 ",
146 _ => unreachable!("HTTP/1 exchanges and interim writers are HTTP/1.0 or HTTP/1.1"),
147 };
148 let status_code = status.as_u16();
149 let code = [
150 b'0' + (status_code / 100) as u8,
151 b'0' + (status_code / 10 % 10) as u8,
152 b'0' + (status_code % 10) as u8,
153 b' ',
154 ];
155 let reason = status.description().unwrap_or("").as_bytes();
156 self.reserve(version.len() + code.len() + reason.len() + 2);
157 self.pending.extend_from_slice(version);
158 self.pending.extend_from_slice(&code);
159 self.pending.extend_from_slice(reason);
160 self.pending.extend_from_slice(b"\r\n");
161 }
162
163 async fn write_fields(
164 &mut self,
165 fields: &[base_http::Field],
166 cancellation: &Cancellation,
167 ) -> Result<(), Error> {
168 for field in fields {
169 self.write_field(field.name(), field.value(), cancellation)
170 .await?;
171 }
172 Ok(())
173 }
174
175 async fn write_field(
176 &mut self,
177 name: &str,
178 value: &[u8],
179 cancellation: &Cancellation,
180 ) -> Result<(), Error> {
181 let size = name.len().saturating_add(value.len()).saturating_add(4);
182 if size <= OUTPUT_BATCH_LIMIT - self.pending.len() {
183 self.reserve(size);
184 self.pending.extend_from_slice(name.as_bytes());
185 self.pending.extend_from_slice(b": ");
186 self.pending.extend_from_slice(value);
187 self.pending.extend_from_slice(b"\r\n");
188 return Ok(());
189 }
190 self.write_all(name.as_bytes(), cancellation).await?;
191 self.write_all(b": ", cancellation).await?;
192 self.write_all(value, cancellation).await?;
193 self.write_all(b"\r\n", cancellation).await
194 }
195
196 async fn write_plan(
197 &mut self,
198 plan: &Plan<'_>,
199 cancellation: &Cancellation,
200 ) -> Result<(), Error> {
201 self.write_start_line(plan.response.status, plan.version);
202 for field in plan.response.fields.pairs() {
203 if (plan.upgrade.is_some() && field.name().eq_ignore_ascii_case("upgrade"))
204 || (matches!(plan.framing, Framing::Chunked(_))
205 && field.name().eq_ignore_ascii_case("content-length"))
206 {
207 continue;
208 }
209 self.write_field(field.name(), field.value(), cancellation)
210 .await?;
211 }
212 if let Some(protocol) = plan.upgrade {
213 self.write_field("Upgrade", protocol, cancellation).await?;
214 }
215 if plan.connection_upgrade {
216 self.write_field("Connection", b"upgrade", cancellation)
217 .await?;
218 }
219 if let Some(length) = plan.content_length {
220 let mut digits = [0; 20];
221 let bytes = decimal(length, &mut digits);
222 self.write_field("Content-Length", bytes, cancellation)
223 .await?;
224 }
225 if plan.chunked {
226 self.write_field("Transfer-Encoding", b"chunked", cancellation)
227 .await?;
228 }
229 if let Some(option) = plan.connection {
230 self.write_field("Connection", option, cancellation).await?;
231 }
232 self.write_all(b"\r\n", cancellation).await
233 }
234
235 pub async fn write_response<B: ReadableBody>(
239 &mut self,
240 exchange: &Exchange,
241 response: &mut Response<B>,
242 cancellation: &Cancellation,
243 ) -> Result<bool, Error> {
244 let result = self
245 .write_response_inner(exchange, response, cancellation)
246 .await;
247 response.body.close(None);
248 result
249 }
250
251 async fn write_response_inner<B: ReadableBody>(
252 &mut self,
253 exchange: &Exchange,
254 response: &mut Response<B>,
255 cancellation: &Cancellation,
256 ) -> Result<bool, Error> {
257 cancellation.check()?;
258 if !self.usable {
259 return Err(Error::Invalid("response output is not reusable"));
260 }
261 let (framing, persistent, tunnel) = {
262 let plan = plan(
263 exchange,
264 &response.metadata,
265 response.body.length(),
266 response.body.may_have_trailers(),
267 )?;
268 if plan.tunnel
269 && response.metadata.status == Status::SWITCHING_PROTOCOLS
270 && !self.continue_sent
271 && exchange.expects_continue()
272 {
273 self.interim(Version::Http11)
274 .on_interim_response(ResponseMetadata::new(Status::CONTINUE), cancellation)
275 .await?;
276 }
277 self.usable = false;
278 self.write_plan(&plan, cancellation).await?;
279 if plan.tunnel {
280 self.flush(cancellation).await?;
282 exchange.switch();
283 }
284 (plan.framing, plan.persistent, plan.tunnel)
285 };
286 self.write_body(
287 &mut response.body,
288 framing,
289 !tunnel && response.metadata.status == Status::RESET_CONTENT,
290 cancellation,
291 )
292 .await?;
293 self.usable = persistent;
294 self.continue_sent = false;
295 Ok(persistent)
296 }
297
298 pub(crate) fn begin(&mut self) -> Result<(), Error> {
299 if !self.usable {
300 return Err(Error::Invalid("output is not reusable"));
301 }
302 self.usable = false;
303 Ok(())
304 }
305
306 pub(crate) fn completed(&mut self, persistent: bool) {
307 self.usable = persistent;
308 }
309
310 pub(crate) async fn flush(&mut self, cancellation: &Cancellation) -> Result<(), Error> {
311 self.send_pending(cancellation).await?;
312 Ok(self.writer.flush(cancellation).await?)
313 }
314
315 pub(crate) async fn write_body<B: ReadableBody>(
316 &mut self,
317 body: &mut B,
318 framing: Framing,
319 forbid_content: bool,
320 cancellation: &Cancellation,
321 ) -> Result<(), Error> {
322 if !matches!(framing, Framing::Absent) {
323 let mut encoding = BodyEncoder::new(framing, forbid_content);
324 loop {
325 if !body.is_ready() {
326 self.flush(cancellation).await?;
327 }
328 let frame = {
329 let reading = pin!(body.read(cancellation));
330 let mut read = pin!(wait(reading, cancellation));
331 let ready = poll_fn(|cx| Poll::Ready(read.as_mut().poll(cx))).await;
332 match ready {
333 Poll::Ready(result) => result,
334 Poll::Pending => {
335 self.flush(cancellation).await?;
336 read.await
337 }
338 }
339 }?
340 .map_err(|e| {
341 if cancellation.is_cancelled() {
342 Error::Cancelled
343 } else {
344 Error::Body(Box::new(e))
345 }
346 })?;
347 let end = frame.is_end();
348 encoding.write_frame(self, frame, cancellation).await?;
349 if end {
350 break;
351 }
352 }
353 }
354 self.flush(cancellation).await?;
355 Ok(())
356 }
357
358 pub async fn close(&mut self) -> io::Result<()> {
359 self.usable = false;
360 self.pending.clear();
361 self.writer.close().await
362 }
363}
364
365pub struct InterimWriter<'a, W> {
367 output: &'a mut ResponseWriter<W>,
368 version: Version,
369}
370
371impl<W: WritableStream> InterimHandler for InterimWriter<'_, W> {
372 type Error = Error;
373 async fn on_interim_response(
374 &mut self,
375 response: ResponseMetadata,
376 cancellation: &Cancellation,
377 ) -> Result<(), Error> {
378 cancellation.check()?;
379 if !self.output.usable {
380 return Err(Error::Invalid("response output is not reusable"));
381 }
382 if !response.status.is_informational() || response.status == Status::SWITCHING_PROTOCOLS {
383 return Err(Error::Invalid("invalid interim status"));
384 }
385 if self.version != Version::Http11 {
386 return Err(Error::Invalid("HTTP/1.0 interim response"));
387 }
388 if response.fields.contains("content-length")
389 || response.fields.contains("transfer-encoding")
390 || response.content_length.is_some()
391 || response.protocol.is_some()
392 {
393 return Err(Error::Invalid("invalid interim framing"));
394 }
395 wire::output_fields(response.fields.pairs())?;
396 self.output.usable = false;
397 self.output.write_start_line(response.status, self.version);
398 self.output
399 .write_fields(response.fields.pairs(), cancellation)
400 .await?;
401 self.output.write_all(b"\r\n", cancellation).await?;
402 self.output.flush(cancellation).await?;
403 if response.status == Status::CONTINUE {
404 self.output.continue_sent = true;
405 }
406 self.output.usable = true;
407 Ok(())
408 }
409}
410
411#[derive(Clone, Copy)]
412pub(crate) enum Framing {
413 Absent,
414 Fixed(u64),
415 Chunked(Option<u64>),
416 Raw,
417}
418pub(crate) struct BodyEncoder {
421 framing: Framing,
422 total: u64,
423 trailers: Option<base_http::Fields>,
424 forbid_content: bool,
425}
426impl BodyEncoder {
427 pub(crate) fn end_requires_output(&self) -> bool {
428 matches!(self.framing, Framing::Chunked(_))
429 }
430 pub(crate) fn new(framing: Framing, forbid_content: bool) -> Self {
431 Self {
432 framing,
433 total: 0,
434 trailers: None,
435 forbid_content,
436 }
437 }
438 pub(crate) async fn write_frame<W: WritableStream, D: AsRef<[u8]>>(
439 &mut self,
440 output: &mut ResponseWriter<W>,
441 frame: BodyFrame<D>,
442 cancellation: &Cancellation,
443 ) -> Result<(), Error> {
444 let end = frame.is_end();
445 match frame {
446 BodyFrame::Data { data, .. } => {
447 if self.trailers.is_some() {
448 return Err(Error::Invalid("data after trailing headers"));
449 }
450 let bytes = data.as_ref();
451 self.total = self.total.saturating_add(bytes.len() as u64);
453 if self.forbid_content && self.total != 0 {
454 return Err(Error::Invalid("content on 205"));
455 }
456 if let Framing::Fixed(length) | Framing::Chunked(Some(length)) = self.framing {
457 if self.total > length {
458 return Err(Error::LengthMismatch);
459 }
460 }
461 if !bytes.is_empty() {
462 if matches!(self.framing, Framing::Chunked(_)) {
463 output
464 .write_all(format!("{:x}\r\n", bytes.len()).as_bytes(), cancellation)
465 .await?;
466 output.write_all(bytes, cancellation).await?;
467 output.write_all(b"\r\n", cancellation).await?;
468 } else {
469 output.write_all(bytes, cancellation).await?;
470 }
471 }
472 }
473 BodyFrame::Headers { fields, .. } => {
474 if self.trailers.is_some() {
475 return Err(Error::Invalid("repeated trailing headers"));
476 }
477 if !matches!(self.framing, Framing::Chunked(_)) {
478 return Err(Error::Invalid("trailers require chunked framing"));
479 }
480 validate_trailer_fields(fields.pairs())?;
481 wire::output_fields(fields.pairs())?;
482 self.trailers = Some(fields);
483 }
484 BodyFrame::End => {}
485 }
486 if end {
487 if let Framing::Fixed(length) | Framing::Chunked(Some(length)) = self.framing {
488 if self.total != length {
489 return Err(Error::LengthMismatch);
490 }
491 }
492 if matches!(self.framing, Framing::Chunked(_)) {
493 output.write_all(b"0\r\n", cancellation).await?;
494 if let Some(trailers) = &self.trailers {
495 output.write_fields(trailers.pairs(), cancellation).await?;
496 }
497 output.write_all(b"\r\n", cancellation).await?;
498 }
499 }
500 Ok(())
501 }
502}
503
504struct Plan<'a> {
507 response: &'a ResponseMetadata,
508 version: Version,
509 framing: Framing,
510 persistent: bool,
511 tunnel: bool,
512 content_length: Option<u64>,
513 chunked: bool,
514 upgrade: Option<&'a [u8]>,
515 connection_upgrade: bool,
516 connection: Option<&'static [u8]>,
517}
518
519fn plan<'a>(
520 exchange: &Exchange,
521 response: &'a ResponseMetadata,
522 length: Option<u64>,
523 may_have_trailers: bool,
524) -> Result<Plan<'a>, Error> {
525 if !response.status.is_final() {
526 return Err(Error::Invalid("final response required"));
527 }
528 let fields = &response.fields;
529 wire::output_fields(fields.pairs())?;
530 let version = exchange.version();
531 if response.version.is_some_and(|v| v != version) {
532 return Err(Error::Invalid("response version differs from request"));
533 }
534 let facts = wire::response_framing(fields)?;
536 let declared = wire::declared_length(response.content_length, facts.length)?;
537 if declared.is_some() && facts.chunked {
538 return Err(Error::Invalid("content-length with transfer-encoding"));
539 }
540 let chunked = facts.chunked;
541 let connect = exchange.is_connect() && response.status.is_success();
542 let upgrade = response.status == Status::SWITCHING_PROTOCOLS;
543 let tunnel = connect || upgrade;
544 let status_bodyless =
545 response.status == Status::NO_CONTENT || response.status == Status::NOT_MODIFIED;
546 let absent = !tunnel && (exchange.is_head() || status_bodyless);
547 let mut persistent = exchange.is_persistent()
548 && !exchange.is_abandoned()
549 && response.persistent != Some(false)
550 && !facts.close
551 && !tunnel;
552 let mut content_length = response.content_length.filter(|_| facts.length.is_none());
553 let mut generated_chunked = false;
554 let mut selected_upgrade = None;
555 let mut connection_upgrade = false;
556 let mut connection = None;
557 let framing;
558 if tunnel {
559 if !exchange.is_complete() {
560 return Err(Error::Invalid("switch with unread request body"));
561 }
562 if declared.is_some() || chunked {
563 return Err(Error::Invalid("framing fields on tunnel response"));
564 }
565 if upgrade {
566 if version != Version::Http11 || !exchange.has_upgrade() {
567 return Err(Error::Invalid("unsolicited upgrade"));
568 }
569 if facts.duplicate_upgrade {
570 return Err(Error::Invalid("duplicate singleton field"));
571 }
572 let selected = response
573 .protocol
574 .as_ref()
575 .map(|p| p.as_str().as_bytes())
576 .or(facts.upgrade)
577 .ok_or(Error::Invalid("missing selected protocol"))?;
578 if !wire::token(selected) || !exchange.accepts_upgrade(selected) {
579 return Err(Error::Invalid("upgrade protocol was not offered"));
580 }
581 selected_upgrade = Some(selected);
582 connection_upgrade = !facts.connection_upgrade;
583 } else if response.protocol.is_some() {
584 return Err(Error::Invalid("protocol on traditional CONNECT"));
585 }
586 framing = Framing::Raw;
587 } else {
588 if response.protocol.is_some() {
589 return Err(Error::Invalid("protocol without upgrade status"));
590 }
591 if facts.upgrade.is_some() {
592 connection_upgrade = !facts.connection_upgrade;
593 }
594 if chunked && version != Version::Http11 {
595 return Err(Error::Invalid("HTTP/1.0 transfer-encoding"));
596 }
597 if absent {
598 if chunked {
599 return Err(Error::Invalid(
600 "trailers or transfer-encoding on bodyless response",
601 ));
602 }
603 if response.status == Status::NO_CONTENT {
604 if declared.is_some() {
605 return Err(Error::Invalid("content-length on 204"));
606 }
607 } else if declared.is_none() {
608 content_length = length;
609 }
610 framing = Framing::Absent;
611 } else {
612 if let (Some(actual), Some(declared)) = (length, declared) {
613 if actual != declared {
614 return Err(Error::LengthMismatch);
615 }
616 }
617 if response.status == Status::RESET_CONTENT
618 && (length.is_some_and(|n| n != 0) || declared.is_some_and(|n| n != 0))
619 {
620 return Err(Error::Invalid("content on 205"));
621 }
622 if response.status == Status::RESET_CONTENT
623 && declared.is_none()
624 && !chunked
625 && !may_have_trailers
626 && !facts.trailer
627 {
628 content_length = Some(0);
629 framing = Framing::Fixed(0);
630 } else if chunked || (may_have_trailers && version == Version::Http11) || facts.trailer
631 {
632 if version != Version::Http11 {
633 return Err(Error::Invalid("trailers require HTTP/1.1 chunked framing"));
634 }
635 content_length = None;
636 generated_chunked = !chunked;
637 framing = Framing::Chunked(declared.or(length));
638 } else if let Some(length) = declared.or(length) {
639 if declared.is_none() {
640 content_length = Some(length);
641 }
642 framing = Framing::Fixed(length);
643 } else if version == Version::Http11 {
644 generated_chunked = true;
645 framing = Framing::Chunked(declared.or(length));
646 } else {
647 persistent = false;
648 framing = Framing::Raw;
649 }
650 }
651 if !persistent {
652 if !facts.close {
653 connection = Some(&b"close"[..]);
654 }
655 } else if version == Version::Http10 && !facts.keep_alive {
656 connection = Some(&b"keep-alive"[..]);
657 }
658 }
659 Ok(Plan {
660 response,
661 version,
662 framing,
663 persistent,
664 tunnel,
665 content_length,
666 chunked: generated_chunked,
667 upgrade: selected_upgrade,
668 connection_upgrade,
669 connection,
670 })
671}
672
673fn decimal(mut value: u64, digits: &mut [u8; 20]) -> &[u8] {
674 let mut start = digits.len();
675 loop {
676 start -= 1;
677 digits[start] = b'0' + (value % 10) as u8;
678 value /= 10;
679 if value == 0 {
680 return &digits[start..];
681 }
682 }
683}
684
685pub(crate) fn validate_trailer_fields(fields: &[base_http::Field]) -> Result<(), Error> {
686 let policy = base_http::headers::Policy::default();
687 for field in fields {
688 if !policy.get(field.name()).trailer
689 || ["keep-alive", "proxy-connection"]
690 .iter()
691 .any(|name| field.name().eq_ignore_ascii_case(name))
692 {
693 return Err(base_http::FieldError::InvalidTrailer(field.name().to_owned()).into());
694 }
695 }
696 Ok(())
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702 use crate::{Options, RequestReader};
703 use base_endpoint::stream::ReadableStream;
704 use base_http::{Fields, Protocol, body::BufferedBody};
705 use base_scheduler::OperationResult;
706 use futures_lite::future::block_on;
707
708 struct Bytes(&'static [u8]);
709 impl ReadableStream for Bytes {
710 async fn read_into(
711 &mut self,
712 buffer: &mut [u8],
713 _: &Cancellation,
714 ) -> OperationResult<usize> {
715 let n = self.0.len().min(buffer.len());
716 buffer[..n].copy_from_slice(&self.0[..n]);
717 self.0 = &self.0[n..];
718 Ok(n)
719 }
720 async fn close(&mut self) -> io::Result<()> {
721 Ok(())
722 }
723 }
724
725 struct Refusing;
727 impl WritableStream for Refusing {
728 async fn write(&mut self, _: &[u8], _: &Cancellation) -> OperationResult<usize> {
729 Err(io::Error::from(io::ErrorKind::BrokenPipe).into())
730 }
731 async fn flush(&mut self, _: &Cancellation) -> OperationResult<()> {
732 Ok(())
733 }
734 async fn close(&mut self) -> io::Result<()> {
735 Ok(())
736 }
737 }
738
739 fn exchange(bytes: &'static [u8]) -> Exchange {
740 let mut input = RequestReader::new(Bytes(bytes), Options::default()).unwrap();
741 let exchange = block_on(input.read_request(&Cancellation::never()))
742 .unwrap()
743 .unwrap()
744 .1;
745 block_on(input.close()).unwrap();
746 exchange
747 }
748
749 fn filler(size: usize) -> (&'static str, Vec<u8>) {
751 ("x", vec![b'v'; size - 5])
752 }
753
754 fn is_broken_pipe(result: Result<impl Sized, Error>) -> bool {
755 matches!(result, Err(Error::Io(error)) if error.kind() == io::ErrorKind::BrokenPipe)
756 }
757
758 #[test]
759 fn a_head_field_crossing_the_batch_limit_sends_the_batch_first() {
760 const GET: &[u8] = b"GET / HTTP/1.1\r\nHost: x\r\n\r\n";
761 const UPGRADE: &[u8] =
762 b"GET / HTTP/1.1\r\nHost: x\r\nConnection: upgrade\r\nUpgrade: echo\r\n\r\n";
763 const OK: usize = b"HTTP/1.1 200 OK\r\n".len();
764 const SWITCHING: usize = b"HTTP/1.1 101 Switching Protocols\r\n".len();
765 let full = |start: usize| filler(OUTPUT_BATCH_LIMIT - 1 - start);
767 type Case = (&'static [u8], Response<BufferedBody<Vec<u8>>>);
768 let mut cases: Vec<Case> = Vec::new();
769
770 let mut upgrade = Response::new(Status::SWITCHING_PROTOCOLS, BufferedBody::default());
771 upgrade.protocol = Some(Protocol::new("echo").unwrap());
772 let (name, value) = full(SWITCHING);
773 upgrade.fields.add(name, value).unwrap();
774 cases.push((UPGRADE, upgrade));
775
776 let mut advertised = Response::new(Status::OK, BufferedBody::default());
777 advertised.fields.add("Upgrade", "h2c").unwrap();
778 let (name, value) = full(OK + "Upgrade: h2c\r\n".len());
779 advertised.fields.add(name, value).unwrap();
780 cases.push((GET, advertised));
781
782 let mut length = Response::new(Status::OK, BufferedBody::from(b"hi".to_vec()));
783 let (name, value) = full(OK);
784 length.fields.add(name, value).unwrap();
785 cases.push((GET, length));
786
787 let mut chunked = Response::new(Status::OK, BufferedBody::default());
788 chunked.fields.add("Trailer", "x-digest").unwrap();
789 let (name, value) = full(OK + "Trailer: x-digest\r\n".len());
790 chunked.fields.add(name, value).unwrap();
791 cases.push((GET, chunked));
792
793 let mut close = Response::new(Status::OK, BufferedBody::default());
794 close.persistent = Some(false);
795 close.fields.add("Content-Length", "0").unwrap();
796 let (name, value) = full(OK + "Content-Length: 0\r\n".len());
797 close.fields.add(name, value).unwrap();
798 cases.push((GET, close));
799
800 let mut value = Response::new(Status::OK, BufferedBody::default());
802 value
803 .fields
804 .add("x", vec![b'v'; OUTPUT_BATCH_LIMIT + 1])
805 .unwrap();
806 cases.push((GET, value));
807 let mut name = Response::new(Status::OK, BufferedBody::default());
808 name.fields
809 .add("x".repeat(OUTPUT_BATCH_LIMIT), "v")
810 .unwrap();
811 cases.push((GET, name));
812 let mut separator = Response::new(Status::OK, BufferedBody::default());
813 separator
814 .fields
815 .add("x".repeat(OUTPUT_BATCH_LIMIT - 1 - OK), "v")
816 .unwrap();
817 cases.push((GET, separator));
818
819 for (index, (request, mut response)) in cases.into_iter().enumerate() {
820 let mut output = ResponseWriter::new(Refusing);
821 let result = block_on(output.write_response(
822 &exchange(request),
823 &mut response,
824 &Cancellation::never(),
825 ));
826 assert!(is_broken_pipe(result), "case {index}");
827 assert!(!output.is_usable());
828 }
829 }
830
831 #[test]
832 fn interim_heads_crossing_the_batch_limit_send_the_batch_first() {
833 const EARLY_HINTS: usize = b"HTTP/1.1 103 Early Hints\r\n".len();
834 let large = ("link", vec![b'v'; OUTPUT_BATCH_LIMIT + 1]);
835 let full = filler(OUTPUT_BATCH_LIMIT - 1 - EARLY_HINTS);
836 for (name, value) in [large, full] {
837 let mut output = ResponseWriter::new(Refusing);
838 let mut response = ResponseMetadata::new(Status::new(103).unwrap());
839 response.fields.add(name, value).unwrap();
840 let result = block_on(
841 output
842 .interim(Version::Http11)
843 .on_interim_response(response, &Cancellation::never()),
844 );
845 assert!(is_broken_pipe(result));
846 assert!(!output.is_usable());
847 }
848 }
849
850 #[test]
851 fn body_frames_crossing_the_batch_limit_send_the_batch_first() {
852 let data = |bytes: &[u8]| BodyFrame::Data {
853 data: bytes.to_vec(),
854 end: false,
855 };
856 let mut trailers = Fields::new();
857 trailers
858 .add("x-digest", vec![b'v'; OUTPUT_BATCH_LIMIT + 1])
859 .unwrap();
860 let large = vec![b'v'; OUTPUT_BATCH_LIMIT];
861 let cases = [
863 (Framing::Chunked(None), OUTPUT_BATCH_LIMIT - 2, data(b"abc")),
864 (Framing::Chunked(None), 1, data(&large)),
865 (Framing::Chunked(None), OUTPUT_BATCH_LIMIT - 7, data(b"abc")),
866 (Framing::Fixed(OUTPUT_BATCH_LIMIT as u64), 1, data(&large)),
867 (
868 Framing::Chunked(None),
869 OUTPUT_BATCH_LIMIT - 2,
870 BodyFrame::End,
871 ),
872 (
873 Framing::Chunked(None),
874 OUTPUT_BATCH_LIMIT - 4,
875 BodyFrame::End,
876 ),
877 (
878 Framing::Chunked(None),
879 1,
880 BodyFrame::Headers {
881 fields: trailers,
882 end: true,
883 },
884 ),
885 ];
886 for (index, (framing, pending, frame)) in cases.into_iter().enumerate() {
887 let never = Cancellation::never();
888 let mut output = ResponseWriter::new(Refusing);
889 block_on(output.write_all(&vec![b'.'; pending], &never)).unwrap();
890 let mut encoder = BodyEncoder::new(framing, false);
891 let result = block_on(encoder.write_frame(&mut output, frame, &never));
892 assert!(is_broken_pipe(result), "case {index}");
893 }
894 }
895
896 #[test]
897 fn empty_fixed_data_needs_no_transport_write() {
898 let never = Cancellation::never();
899 let mut output = ResponseWriter::new(Refusing);
900 let mut encoder = BodyEncoder::new(Framing::Fixed(0), false);
901 let frame = BodyFrame::Data {
902 data: Vec::new(),
903 end: true,
904 };
905 block_on(encoder.write_frame(&mut output, frame, &never)).unwrap();
906 block_on(output.flush(&never)).unwrap();
907 block_on(output.close()).unwrap();
908 }
909}