1use crate::{
5 Error, Exchange, IncomingBody, Options, RequestReader, ResponseWriter, input, output, wire,
6};
7use base_endpoint::stream::{ReadableStream, WritableStream};
8use base_http::{
9 InterimHandler, InterimResponses, Method, Protocol, Request, RequestMetadata, Response,
10 ResponseMetadata, Status, Version,
11 body::{BodyFrame, ReadableBody},
12};
13use base_scheduler::Cancellation;
14use std::io;
15
16pub struct RequestWriter<W> {
20 output: ResponseWriter<W>,
21 body: Option<output::BodyEncoder>,
22 persistent: bool,
23 buffer: Vec<u8>,
24}
25
26impl<W: WritableStream> RequestWriter<W> {
27 pub fn new(writer: W) -> Self {
28 Self {
29 output: ResponseWriter::new(writer),
30 body: None,
31 persistent: false,
32 buffer: Vec::new(),
33 }
34 }
35 pub fn is_reusable(&self) -> bool {
38 self.output.is_usable()
39 }
40
41 pub async fn write_request<B: ReadableBody>(
47 &mut self,
48 request: &mut Request<B>,
49 cancellation: &Cancellation,
50 ) -> Result<bool, Error> {
51 let result = self.write_inner(request, cancellation).await;
52 request.body.close(None);
53 result
54 }
55
56 async fn write_inner<B: ReadableBody>(
57 &mut self,
58 request: &mut Request<B>,
59 cancellation: &Cancellation,
60 ) -> Result<bool, Error> {
61 cancellation.check()?;
62 let (framing, persistent) =
63 request_plan(&mut self.buffer, &request.metadata, request.body.length())?;
64 self.output.begin()?;
65 self.output.write_all(&self.buffer, cancellation).await?;
66 self.output.flush(cancellation).await?;
67 let encoding = request_encoder(framing);
68 self.output
69 .write_body(&mut request.body, encoding, cancellation)
70 .await?;
71 self.output.completed(persistent);
72 Ok(persistent)
73 }
74
75 pub async fn begin_request(
80 &mut self,
81 metadata: &RequestMetadata,
82 cancellation: &Cancellation,
83 ) -> Result<(), Error> {
84 cancellation.check()?;
85 let (framing, persistent) =
86 request_plan(&mut self.buffer, metadata, metadata.content_length)?;
87 self.output.begin()?;
88 self.output.write_all(&self.buffer, cancellation).await?;
89 self.output.flush(cancellation).await?;
90 self.persistent = persistent;
92 self.body = Some(request_encoder(framing));
93 Ok(())
94 }
95
96 pub async fn write_frame<D: AsRef<[u8]>>(
99 &mut self,
100 frame: BodyFrame<D>,
101 cancellation: &Cancellation,
102 ) -> Result<(), Error> {
103 cancellation.check()?;
104 let mut body = self
105 .body
106 .take()
107 .ok_or(Error::Invalid("request output is not active"))?;
108 let end = frame.is_end();
109 let flush = !matches!(&frame, BodyFrame::End) || body.end_requires_output();
110 body.write_frame(&mut self.output, frame, cancellation)
111 .await?;
112 if flush {
113 self.output.flush(cancellation).await?;
114 }
115 if end {
116 self.output.completed(self.persistent);
117 } else {
118 self.body = Some(body);
119 }
120 Ok(())
121 }
122
123 pub async fn flush(&mut self, cancellation: &Cancellation) -> Result<(), Error> {
126 let result = self.output.flush(cancellation).await;
127 if result.is_err() {
128 self.body = None;
129 self.output.completed(false);
130 }
131 result
132 }
133
134 pub async fn close(&mut self) -> io::Result<()> {
135 self.output.close().await
136 }
137}
138
139fn request_encoder(framing: output::Framing) -> output::BodyEncoder {
141 let chunked = matches!(framing, output::Framing::Chunked(_));
142 output::BodyEncoder::new(framing, false).discard_trailers(!chunked)
143}
144
145fn request_plan(
146 bytes: &mut Vec<u8>,
147 head: &RequestMetadata,
148 length: Option<u64>,
149) -> Result<(output::Framing, bool), Error> {
150 let version = head.version.unwrap_or(Version::Http11);
151 let label = match version {
152 Version::Http10 => "HTTP/1.0",
153 Version::Http11 => "HTTP/1.1",
154 _ => return Err(Error::Invalid("unsupported request version")),
155 };
156 let fields = &head.fields;
157 let host = wire::singleton(fields, "host")?;
158 if let Some(host) = host {
159 wire::authority(
160 std::str::from_utf8(host).map_err(|_| Error::Invalid("invalid host"))?,
161 false,
162 )?;
163 }
164 let add_host = if let Some(authority) = &head.authority {
165 wire::authority(authority, false)?;
166 if let Some(host) = host {
167 if !host.eq_ignore_ascii_case(authority.as_bytes()) {
168 return Err(Error::Invalid("authority differs from Host"));
169 }
170 None
171 } else {
172 Some(authority.as_str())
173 }
174 } else {
175 None
176 };
177 if version == Version::Http11 && host.is_none() && add_host.is_none() {
178 return Err(Error::Invalid("missing host"));
179 }
180 let upgrade = if let Some(protocol) = &head.protocol {
181 if version != Version::Http11 {
182 return Err(Error::Invalid("upgrade requires HTTP/1.1"));
183 }
184 if let Some(offer) = wire::singleton(fields, "upgrade")? {
185 if !offer.eq_ignore_ascii_case(protocol.as_str().as_bytes()) {
186 return Err(Error::Invalid("conflicting upgrade protocol"));
187 }
188 None
189 } else {
190 Some(protocol.as_str())
191 }
192 } else {
193 None
194 };
195 wire::connection_options(fields)?;
196 let add_upgrade = head.protocol.is_some() && !wire::has_token(fields, "connection", b"upgrade");
197 if version == Version::Http11
198 && (add_upgrade || wire::has_token(fields, "connection", b"upgrade"))
199 && !fields.contains("upgrade")
200 && upgrade.is_none()
201 {
202 return Err(Error::Invalid("missing upgrade protocol"));
203 }
204 let connection = head
205 .persistent
206 .map(|p| if p { "keep-alive" } else { "close" })
207 .filter(|value| !wire::has_token(fields, "connection", value.as_bytes()));
208 let target = if head.method == Method::CONNECT {
209 if head.path.is_some() {
210 return Err(Error::Invalid("CONNECT cannot have a path"));
211 }
212 head.authority
213 .as_deref()
214 .ok_or(Error::Invalid("CONNECT requires authority"))?
215 } else {
216 head.path.as_deref().unwrap_or("/")
217 };
218 wire::request_target(&head.method, target)?;
219 let (raw_length, raw_chunked) = wire::framing(fields)?;
220 if raw_chunked && (head.content_length.is_some() || raw_length.is_some()) {
222 return Err(Error::Invalid("content-length with transfer-encoding"));
223 }
224 let declared = wire::declared_length(head.declared_length(length), raw_length)?;
226 if raw_chunked && version != Version::Http11 {
227 return Err(Error::Invalid("HTTP/1.0 transfer-encoding"));
228 }
229 if let (Some(length), Some(declared)) = (length, declared) {
230 if length != declared {
231 return Err(Error::LengthMismatch);
232 }
233 }
234 let framing = if raw_chunked {
236 output::Framing::Chunked(length)
237 } else if let Some(length) = declared.or(length) {
238 output::Framing::Fixed(length)
239 } else if version == Version::Http11 {
240 output::Framing::Chunked(None)
241 } else {
242 return Err(Error::Invalid("HTTP/1.0 request requires a body length"));
243 };
244 let chunked = matches!(framing, output::Framing::Chunked(_));
245 let persistent = head.persistent != Some(false)
246 && !wire::has_token(fields, "connection", b"close")
247 && (version == Version::Http11
248 || head.persistent == Some(true)
249 || wire::has_token(fields, "connection", b"keep-alive"));
250 wire::output_fields(fields.pairs())?;
251 bytes.clear();
253 use std::io::Write;
254 write!(bytes, "{} {target} {label}\r\n", head.method.as_str()).expect("writing to a Vec");
256 for field in fields.pairs() {
258 bytes.extend_from_slice(field.name().as_bytes());
259 bytes.extend_from_slice(b": ");
260 bytes.extend_from_slice(field.value());
261 bytes.extend_from_slice(b"\r\n");
262 }
263 for (name, value) in [
264 ("Host", add_host),
265 ("Upgrade", upgrade),
266 ("Connection", add_upgrade.then_some("upgrade")),
267 ("Connection", connection),
268 (
269 "Transfer-Encoding",
270 (chunked && !raw_chunked).then_some("chunked"),
271 ),
272 ] {
273 if let Some(value) = value {
274 write!(bytes, "{name}: {value}\r\n").expect("writing to a Vec");
275 }
276 }
277 if let output::Framing::Fixed(length) = framing {
278 if raw_length.is_none()
279 && (declared.is_some()
280 || length != 0
281 || matches!(head.method.as_str(), "POST" | "PUT" | "PATCH"))
282 {
283 write!(bytes, "Content-Length: {length}\r\n").expect("writing to a Vec");
284 }
285 }
286 bytes.extend_from_slice(b"\r\n");
287 Ok((framing, persistent))
288}
289
290pub struct ResponseReader<R> {
296 input: RequestReader<R>,
297 persistent: bool,
298}
299
300impl<R: ReadableStream> ResponseReader<R> {
301 pub fn new(reader: R, options: Options) -> Result<Self, Error> {
302 Ok(Self {
303 input: RequestReader::new(reader, options)?,
304 persistent: true,
305 })
306 }
307 pub fn is_reusable(&self) -> bool {
308 self.persistent && self.input.is_reusable()
309 }
310
311 pub fn probe_viable(&mut self) -> bool {
322 self.persistent && self.input.probe_idle()
323 }
324
325 pub async fn read_response<I: InterimHandler>(
329 &mut self,
330 request: &RequestMetadata,
331 interim: &mut InterimResponses<I>,
332 cancellation: &Cancellation,
333 ) -> Result<Response<IncomingBody<'_, R>>, Error> {
334 cancellation.check()?;
335 if !self.is_reusable() {
336 return Err(Error::Invalid("response input is not reusable"));
337 }
338 let max_fields = self.input.options.max_fields;
339 let head = loop {
340 self.input.poisoned = true;
341 let mut head = self
342 .input
343 .read_section(cancellation, "message head", |bytes| {
344 wire::response(bytes, max_fields)
345 })
346 .await?
347 .ok_or(Error::Truncated)?;
348 if head.status.is_final() {
349 interim.finish();
350 break head;
351 }
352 if head.fields.contains("content-length")
353 || head.fields.contains("transfer-encoding")
354 || head.version != Some(Version::Http11)
355 {
356 return Err(Error::Invalid("invalid interim framing"));
357 }
358 head.fields = wire::application_fields(head.fields)?.0;
359 interim
360 .send_interim_response(head, cancellation)
361 .await
362 .map_err(|error| {
363 if cancellation.is_cancelled() {
364 Error::Cancelled
365 } else {
366 Error::Body(Box::new(error))
367 }
368 })?;
369 };
370 self.response_body(request, head)
371 }
372
373 fn response_body(
374 &mut self,
375 request: &RequestMetadata,
376 mut head: ResponseMetadata,
377 ) -> Result<Response<IncomingBody<'_, R>>, Error> {
378 let connect = request.method == Method::CONNECT && head.status.is_success();
379 let upgrade = head.status == Status::SWITCHING_PROTOCOLS;
380 let tunnel = connect || upgrade;
381 let (length, chunked) = if connect {
383 (None, false)
384 } else {
385 wire::framing(&head.fields)?
386 };
387 if chunked && head.version != Some(Version::Http11) {
388 return Err(Error::Invalid("HTTP/1.0 transfer-encoding"));
389 }
390 if upgrade {
391 if length.is_some()
392 || chunked
393 || !wire::has_token(&head.fields, "connection", b"upgrade")
394 {
395 return Err(Error::Invalid("invalid upgrade response"));
396 }
397 let selected = wire::singleton(&head.fields, "upgrade")?
398 .ok_or(Error::Invalid("missing selected protocol"))?;
399 let selected = std::str::from_utf8(selected)
400 .map_err(|_| Error::Invalid("invalid selected protocol"))?;
401 let offered = request
402 .protocol
403 .as_ref()
404 .is_some_and(|p| p.as_str().eq_ignore_ascii_case(selected))
405 || (wire::has_token(&request.fields, "connection", b"upgrade")
406 && wire::has_token(&request.fields, "upgrade", selected.as_bytes()));
407 if !offered {
408 return Err(Error::Invalid("upgrade protocol was not offered"));
409 }
410 head.protocol = Some(
411 Protocol::new(selected)
412 .map_err(|_| Error::Invalid("unsupported selected protocol"))?,
413 );
414 }
415 let absent = !tunnel
416 && (request.method == Method::HEAD
417 || head.status == Status::NO_CONTENT
418 || head.status == Status::NOT_MODIFIED);
419 if head.status == Status::NO_CONTENT && !connect && (length.is_some() || chunked) {
420 return Err(Error::Invalid("framing fields on 204"));
421 }
422 let raw = tunnel || (!absent && !chunked && length.is_none());
423 let framing = if absent || raw {
424 input::Framing::Fixed(0)
425 } else if chunked {
426 input::Framing::CHUNKED
427 } else {
428 input::Framing::Fixed(length.unwrap())
429 };
430 let body_length = if raw {
431 None
432 } else if absent && head.status == Status::NO_CONTENT {
433 Some(0)
434 } else {
435 length
436 };
437 let response_persistent = !wire::has_token(&head.fields, "connection", b"close")
438 && (head.version == Some(Version::Http11)
439 || wire::has_token(&head.fields, "connection", b"keep-alive"));
440 head.content_length = length;
441 let (fields, trailer_options) = wire::application_fields(head.fields)?;
442 head.fields = fields;
443 let exchange = Exchange::new(request, trailer_options);
444 let request_persistent = exchange.is_persistent();
445 self.persistent = request_persistent && response_persistent && !raw;
446 if tunnel {
447 exchange.switch();
448 }
449 self.input.previous = Some(exchange.clone());
450 self.input.poisoned = false;
451 let forbid_content = head.status == Status::RESET_CONTENT && !tunnel;
452 let body = IncomingBody::new(
453 &mut self.input,
454 exchange,
455 framing,
456 body_length,
457 raw,
458 forbid_content,
459 );
460 Ok(Response::from_parts(head, body))
461 }
462
463 pub async fn close(&mut self) -> io::Result<()> {
464 self.input.close().await
465 }
466}