1use core::{future::Future, mem};
18
19use alloc::{boxed::Box, string::String, vec, vec::Vec};
20
21use std::io::{self, Read, Write};
22
23use thiserror::Error;
24use url::Url;
25
26use crate::{
27 coroutine::*,
28 rfc1945::send::*,
29 rfc9110::{
30 headers::HTTP_TRANSFER_ENCODING,
31 request::HttpRequest,
32 response::HttpResponse,
33 send::{HttpSendOutput, HttpSendYield},
34 },
35 rfc9112::{chunk_stream::*, read_headers::*, send::*},
36 sse::frame::*,
37};
38
39#[cfg(any(
40 feature = "rustls-aws",
41 feature = "rustls-ring",
42 feature = "native-tls"
43))]
44mod connect;
45
46const READ_BUFFER_SIZE: usize = 16 * 1024;
47
48#[derive(Debug, Error)]
50pub enum HttpClientError {
51 #[error(transparent)]
53 Http10Send(#[from] Http10SendError),
54 #[error(transparent)]
56 Http11Send(#[from] Http11SendError),
57 #[error(transparent)]
59 Io(#[from] io::Error),
60 #[cfg(any(
62 feature = "rustls-aws",
63 feature = "rustls-ring",
64 feature = "native-tls"
65 ))]
66 #[error(transparent)]
67 Tls(#[from] anyhow::Error),
68 #[error("HTTP URL `{0}` has no host")]
70 UrlMissingHost(String),
71 #[error("HTTP URL `{0}` has unsupported scheme `{1}` (expected `http` or `https`)")]
73 UrlUnsupportedScheme(String, String),
74 #[error("HTTP server redirected to `{url}` (status `{code}`)")]
76 UnexpectedRedirect {
77 url: Url,
79 code: u16,
81 },
82 #[error("HTTP streaming requires `Transfer-Encoding: chunked` (got status `{0}`)")]
84 StreamingNotChunked(u16),
85 #[error(transparent)]
87 ChunkStream(#[from] Http11ChunksReadStreamError),
88 #[error(transparent)]
94 Transport(Box<dyn core::error::Error + Send + Sync>),
95}
96
97pub trait HttpClient {
121 fn run<C, T, E>(&mut self, coroutine: C) -> Result<T, HttpClientError>
124 where
125 C: HttpCoroutine<Yield = HttpYield, Return = Result<T, E>>,
126 HttpClientError: From<E>;
127
128 fn run_send<C, E>(&mut self, coroutine: C) -> Result<HttpSendOutput, HttpClientError>
131 where
132 C: HttpCoroutine<Yield = HttpSendYield, Return = Result<HttpSendOutput, E>>,
133 HttpClientError: From<E>;
134
135 fn send(&mut self, request: HttpRequest) -> Result<HttpSendOutput, HttpClientError> {
137 self.run_send(Http11Send::new(request))
138 }
139
140 fn send_http10(&mut self, request: HttpRequest) -> Result<HttpSendOutput, HttpClientError> {
142 self.run_send(Http10Send::new(request))
143 }
144}
145
146pub trait HttpClientAsync: Send {
163 fn run<C, T, E>(
166 &mut self,
167 coroutine: C,
168 ) -> impl Future<Output = Result<T, HttpClientError>> + Send
169 where
170 C: HttpCoroutine<Yield = HttpYield, Return = Result<T, E>> + Send,
171 T: Send,
172 E: Send,
173 HttpClientError: From<E>;
174
175 fn run_send<C, E>(
178 &mut self,
179 coroutine: C,
180 ) -> impl Future<Output = Result<HttpSendOutput, HttpClientError>> + Send
181 where
182 C: HttpCoroutine<Yield = HttpSendYield, Return = Result<HttpSendOutput, E>> + Send,
183 E: Send,
184 HttpClientError: From<E>;
185
186 fn send(
188 &mut self,
189 request: HttpRequest,
190 ) -> impl Future<Output = Result<HttpSendOutput, HttpClientError>> + Send {
191 self.run_send(Http11Send::new(request))
192 }
193
194 fn send_http10(
196 &mut self,
197 request: HttpRequest,
198 ) -> impl Future<Output = Result<HttpSendOutput, HttpClientError>> + Send {
199 self.run_send(Http10Send::new(request))
200 }
201}
202
203pub struct HttpClientStd {
205 stream: Box<dyn HttpStream>,
206}
207
208impl HttpClientStd {
209 pub fn new<S: Read + Write + Send + 'static>(stream: S) -> Self {
211 Self {
212 stream: Box::new(stream),
213 }
214 }
215
216 pub fn default_alpn() -> Vec<String> {
221 vec![String::from("http/1.1")]
222 }
223
224 pub fn set_stream<S: Read + Write + Send + 'static>(&mut self, stream: S) {
227 self.stream = Box::new(stream);
228 }
229}
230
231impl HttpClient for HttpClientStd {
232 fn run<C, T, E>(&mut self, mut coroutine: C) -> Result<T, HttpClientError>
233 where
234 C: HttpCoroutine<Yield = HttpYield, Return = Result<T, E>>,
235 HttpClientError: From<E>,
236 {
237 let mut buf = [0u8; READ_BUFFER_SIZE];
238 let mut arg: Option<&[u8]> = None;
239
240 loop {
241 match coroutine.resume(arg.take()) {
242 HttpCoroutineState::Complete(Ok(out)) => return Ok(out),
243 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
244 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
245 let n = self.stream.read(&mut buf)?;
246 arg = Some(&buf[..n]);
247 }
248 HttpCoroutineState::Yielded(HttpYield::WantsWrite(bytes)) => {
249 self.stream.write_all(&bytes)?;
250 arg = None;
251 }
252 }
253 }
254 }
255
256 fn run_send<C, E>(&mut self, mut coroutine: C) -> Result<HttpSendOutput, HttpClientError>
262 where
263 C: HttpCoroutine<Yield = HttpSendYield, Return = Result<HttpSendOutput, E>>,
264 HttpClientError: From<E>,
265 {
266 let mut buf = [0u8; READ_BUFFER_SIZE];
267 let mut arg: Option<&[u8]> = None;
268
269 loop {
270 match coroutine.resume(arg.take()) {
271 HttpCoroutineState::Complete(Ok(out)) => return Ok(out),
272 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
273 HttpCoroutineState::Yielded(HttpSendYield::WantsRead) => {
274 let n = self.stream.read(&mut buf)?;
275 arg = Some(&buf[..n]);
276 }
277 HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes)) => {
278 self.stream.write_all(&bytes)?;
279 arg = None;
280 }
281 HttpCoroutineState::Yielded(HttpSendYield::WantsRedirect {
282 url, response, ..
283 }) => {
284 return Err(HttpClientError::UnexpectedRedirect {
285 url,
286 code: *response.status,
287 });
288 }
289 }
290 }
291 }
292}
293
294impl HttpClientStd {
295 pub fn send_streaming(self, request: HttpRequest) -> Result<SseStream, HttpClientError> {
298 let HttpClientStd { mut stream } = self;
299
300 let req_bytes = request.to_http_11_vec();
301 stream.write_all(&req_bytes)?;
302
303 let mut read_headers = Http11HeadersRead::default();
304 let mut buf = [0u8; READ_BUFFER_SIZE];
305 let mut arg: Option<&[u8]> = None;
306
307 let out = loop {
308 match read_headers.resume(arg.take()) {
309 HttpCoroutineState::Complete(Ok(out)) => break out,
310 HttpCoroutineState::Complete(Err(err)) => {
311 return Err(Http11SendError::from(err).into());
312 }
313 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
314 let n = stream.read(&mut buf)?;
315 if n == 0 {
316 return Err(Http11SendError::Eof.into());
317 }
318 arg = Some(&buf[..n]);
319 }
320 HttpCoroutineState::Yielded(HttpYield::WantsWrite(_)) => {
321 unreachable!("Http11HeadersRead never writes");
322 }
323 }
324 };
325
326 let chunked = out
327 .response
328 .header(HTTP_TRANSFER_ENCODING)
329 .is_some_and(|enc| enc.eq_ignore_ascii_case("chunked"));
330
331 if !chunked {
332 return Err(HttpClientError::StreamingNotChunked(*out.response.status));
333 }
334
335 Ok(SseStream {
336 stream,
337 chunk_stream: Http11ChunksReadStream::default(),
338 sse_parser: SseFrameParser::default(),
339 pending: None,
340 preread: out.remaining,
341 response: out.response,
342 keep_alive: out.keep_alive,
343 done: false,
344 })
345 }
346}
347
348pub struct SseStream {
352 stream: Box<dyn HttpStream>,
353 chunk_stream: Http11ChunksReadStream,
354 sse_parser: SseFrameParser,
355 pending: Option<Vec<u8>>,
356 preread: Vec<u8>,
357 response: HttpResponse,
358 keep_alive: bool,
359 done: bool,
360}
361
362impl SseStream {
363 pub fn response(&self) -> &HttpResponse {
365 &self.response
366 }
367
368 pub fn keep_alive(&self) -> bool {
370 self.keep_alive
371 }
372
373 pub fn last_event_id(&self) -> Option<&str> {
375 self.sse_parser.last_event_id()
376 }
377
378 pub fn next_frame(&mut self) -> Result<Option<SseFrame>, HttpClientError> {
381 if self.done {
382 return Ok(None);
383 }
384
385 loop {
386 let arg = self.pending.take();
387 match self.sse_parser.resume(arg.as_deref()) {
388 HttpCoroutineState::Yielded(SseFrameParserYield::Frame(frame)) => {
389 return Ok(Some(frame));
390 }
391 HttpCoroutineState::Yielded(SseFrameParserYield::WantsBytes) => {
392 match self.pull_chunk()? {
393 Some(body) => self.pending = Some(body),
394 None => {
395 self.done = true;
396 return Ok(None);
397 }
398 }
399 }
400 HttpCoroutineState::Complete(never) => match never {},
401 }
402 }
403 }
404
405 pub fn close(self) {
407 drop(self);
408 }
409
410 fn pull_chunk(&mut self) -> Result<Option<Vec<u8>>, HttpClientError> {
411 let mut tmp = [0u8; READ_BUFFER_SIZE];
412 let preread = mem::take(&mut self.preread);
413 let mut arg: Option<&[u8]> = if preread.is_empty() {
414 None
415 } else {
416 Some(&preread)
417 };
418
419 loop {
420 match self.chunk_stream.resume(arg.take()) {
421 HttpCoroutineState::Yielded(Http11ChunksReadStreamYield::Frame { body }) => {
422 return Ok(Some(body));
423 }
424 HttpCoroutineState::Complete(Ok(_remaining)) => return Ok(None),
425 HttpCoroutineState::Yielded(Http11ChunksReadStreamYield::WantsRead) => {
426 let n = self.stream.read(&mut tmp)?;
427 if n == 0 {
428 return Ok(None);
429 }
430 arg = Some(&tmp[..n]);
431 }
432 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
433 }
434 }
435 }
436}
437
438impl Iterator for SseStream {
439 type Item = Result<SseFrame, HttpClientError>;
440
441 fn next(&mut self) -> Option<Self::Item> {
442 match self.next_frame() {
443 Ok(Some(frame)) => Some(Ok(frame)),
444 Ok(None) => None,
445 Err(err) => Some(Err(err)),
446 }
447 }
448}
449
450trait HttpStream: Read + Write + Send {}
454impl<T: Read + Write + Send + ?Sized> HttpStream for T {}