1use core::mem;
9
10#[cfg(any(
11 feature = "rustls-aws",
12 feature = "rustls-ring",
13 feature = "native-tls"
14))]
15use alloc::string::ToString;
16use alloc::{boxed::Box, string::String, vec, vec::Vec};
17
18use std::io::{self, Read, Write};
19
20#[cfg(any(
21 feature = "rustls-aws",
22 feature = "rustls-ring",
23 feature = "native-tls"
24))]
25use pimalaya_stream::{std::stream::StreamStd, tls::Tls};
26use thiserror::Error;
27use url::Url;
28
29use crate::{
30 coroutine::*,
31 rfc1945::send::*,
32 rfc9110::{
33 headers::HTTP_TRANSFER_ENCODING,
34 request::HttpRequest,
35 response::HttpResponse,
36 send::{HttpSendOutput, HttpSendYield},
37 },
38 rfc9112::{chunk_stream::*, read_headers::*, send::*},
39 sse::frame::*,
40};
41
42const READ_BUFFER_SIZE: usize = 16 * 1024;
43
44#[derive(Debug, Error)]
46pub enum HttpClientStdError {
47 #[error(transparent)]
49 Http10Send(#[from] Http10SendError),
50 #[error(transparent)]
52 Http11Send(#[from] Http11SendError),
53 #[error(transparent)]
55 Io(#[from] io::Error),
56 #[cfg(any(
58 feature = "rustls-aws",
59 feature = "rustls-ring",
60 feature = "native-tls"
61 ))]
62 #[error(transparent)]
63 Tls(#[from] anyhow::Error),
64 #[cfg(any(
66 feature = "rustls-aws",
67 feature = "rustls-ring",
68 feature = "native-tls"
69 ))]
70 #[error("HTTP URL `{0}` has no host")]
71 UrlMissingHost(String),
72 #[cfg(any(
74 feature = "rustls-aws",
75 feature = "rustls-ring",
76 feature = "native-tls"
77 ))]
78 #[error("HTTP URL `{0}` has unsupported scheme `{1}` (expected `http` or `https`)")]
79 UrlUnsupportedScheme(String, String),
80 #[error("HTTP server redirected to `{url}` (status `{code}`)")]
82 UnexpectedRedirect {
83 url: Url,
85 code: u16,
87 },
88 #[error("HTTP streaming requires `Transfer-Encoding: chunked` (got status `{0}`)")]
90 StreamingNotChunked(u16),
91 #[error(transparent)]
93 ChunkStream(#[from] Http11ChunksReadStreamError),
94}
95
96pub struct HttpClientStd {
98 stream: Box<dyn HttpStream>,
99}
100
101impl HttpClientStd {
102 pub fn new<S: Read + Write + Send + 'static>(stream: S) -> Self {
104 Self {
105 stream: Box::new(stream),
106 }
107 }
108
109 pub fn default_alpn() -> Vec<String> {
114 vec![String::from("http/1.1")]
115 }
116
117 #[cfg(any(
120 feature = "rustls-aws",
121 feature = "rustls-ring",
122 feature = "native-tls"
123 ))]
124 pub fn connect(url: &Url, tls: &Tls) -> Result<Self, HttpClientStdError> {
125 let host = url
126 .host_str()
127 .ok_or_else(|| HttpClientStdError::UrlMissingHost(url.to_string()))?;
128
129 let stream = match url.scheme() {
130 "http" => StreamStd::connect_tcp(host, url.port_or_known_default().unwrap_or(80))?,
131 "https" => {
132 StreamStd::connect_tls(host, url.port_or_known_default().unwrap_or(443), tls)?
133 }
134 scheme => {
135 return Err(HttpClientStdError::UrlUnsupportedScheme(
136 url.to_string(),
137 scheme.to_string(),
138 ));
139 }
140 };
141
142 Ok(Self {
143 stream: Box::new(stream),
144 })
145 }
146
147 pub fn set_stream<S: Read + Write + Send + 'static>(&mut self, stream: S) {
150 self.stream = Box::new(stream);
151 }
152
153 pub fn run<C, T, E>(&mut self, mut coroutine: C) -> Result<T, HttpClientStdError>
158 where
159 C: HttpCoroutine<Yield = HttpYield, Return = Result<T, E>>,
160 HttpClientStdError: From<E>,
161 {
162 let mut buf = [0u8; READ_BUFFER_SIZE];
163 let mut arg: Option<&[u8]> = None;
164
165 loop {
166 match coroutine.resume(arg.take()) {
167 HttpCoroutineState::Complete(Ok(out)) => return Ok(out),
168 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
169 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
170 let n = self.stream.read(&mut buf)?;
171 arg = Some(&buf[..n]);
172 }
173 HttpCoroutineState::Yielded(HttpYield::WantsWrite(bytes)) => {
174 self.stream.write_all(&bytes)?;
175 arg = None;
176 }
177 }
178 }
179 }
180
181 pub fn send(&mut self, request: HttpRequest) -> Result<HttpSendOutput, HttpClientStdError> {
184 let mut coroutine = Http11Send::new(request);
185 let mut buf = [0u8; READ_BUFFER_SIZE];
186 let mut arg: Option<&[u8]> = None;
187
188 loop {
189 match coroutine.resume(arg.take()) {
190 HttpCoroutineState::Complete(Ok(out)) => return Ok(out),
191 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
192 HttpCoroutineState::Yielded(HttpSendYield::WantsRead) => {
193 let n = self.stream.read(&mut buf)?;
194 arg = Some(&buf[..n]);
195 }
196 HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes)) => {
197 self.stream.write_all(&bytes)?;
198 arg = None;
199 }
200 HttpCoroutineState::Yielded(HttpSendYield::WantsRedirect {
201 url, response, ..
202 }) => {
203 return Err(HttpClientStdError::UnexpectedRedirect {
204 url,
205 code: *response.status,
206 });
207 }
208 }
209 }
210 }
211
212 pub fn send_http10(
214 &mut self,
215 request: HttpRequest,
216 ) -> Result<HttpSendOutput, HttpClientStdError> {
217 let mut coroutine = Http10Send::new(request);
218 let mut buf = [0u8; READ_BUFFER_SIZE];
219 let mut arg: Option<&[u8]> = None;
220
221 loop {
222 match coroutine.resume(arg.take()) {
223 HttpCoroutineState::Complete(Ok(out)) => return Ok(out),
224 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
225 HttpCoroutineState::Yielded(HttpSendYield::WantsRead) => {
226 let n = self.stream.read(&mut buf)?;
227 arg = Some(&buf[..n]);
228 }
229 HttpCoroutineState::Yielded(HttpSendYield::WantsWrite(bytes)) => {
230 self.stream.write_all(&bytes)?;
231 arg = None;
232 }
233 HttpCoroutineState::Yielded(HttpSendYield::WantsRedirect {
234 url, response, ..
235 }) => {
236 return Err(HttpClientStdError::UnexpectedRedirect {
237 url,
238 code: *response.status,
239 });
240 }
241 }
242 }
243 }
244}
245
246impl HttpClientStd {
247 pub fn send_streaming(self, request: HttpRequest) -> Result<SseStream, HttpClientStdError> {
250 let HttpClientStd { mut stream } = self;
251
252 let req_bytes = request.to_http_11_vec();
253 stream.write_all(&req_bytes)?;
254
255 let mut read_headers = Http11HeadersRead::default();
256 let mut buf = [0u8; READ_BUFFER_SIZE];
257 let mut arg: Option<&[u8]> = None;
258
259 let out = loop {
260 match read_headers.resume(arg.take()) {
261 HttpCoroutineState::Complete(Ok(out)) => break out,
262 HttpCoroutineState::Complete(Err(err)) => {
263 return Err(Http11SendError::from(err).into());
264 }
265 HttpCoroutineState::Yielded(HttpYield::WantsRead) => {
266 let n = stream.read(&mut buf)?;
267 if n == 0 {
268 return Err(Http11SendError::Eof.into());
269 }
270 arg = Some(&buf[..n]);
271 }
272 HttpCoroutineState::Yielded(HttpYield::WantsWrite(_)) => {
273 unreachable!("Http11HeadersRead never writes");
274 }
275 }
276 };
277
278 let chunked = out
279 .response
280 .header(HTTP_TRANSFER_ENCODING)
281 .is_some_and(|enc| enc.eq_ignore_ascii_case("chunked"));
282
283 if !chunked {
284 return Err(HttpClientStdError::StreamingNotChunked(
285 *out.response.status,
286 ));
287 }
288
289 Ok(SseStream {
290 stream,
291 chunk_stream: Http11ChunksReadStream::default(),
292 sse_parser: SseFrameParser::default(),
293 pending: None,
294 preread: out.remaining,
295 response: out.response,
296 keep_alive: out.keep_alive,
297 done: false,
298 })
299 }
300}
301
302pub struct SseStream {
306 stream: Box<dyn HttpStream>,
307 chunk_stream: Http11ChunksReadStream,
308 sse_parser: SseFrameParser,
309 pending: Option<Vec<u8>>,
310 preread: Vec<u8>,
311 response: HttpResponse,
312 keep_alive: bool,
313 done: bool,
314}
315
316impl SseStream {
317 pub fn response(&self) -> &HttpResponse {
319 &self.response
320 }
321
322 pub fn keep_alive(&self) -> bool {
324 self.keep_alive
325 }
326
327 pub fn last_event_id(&self) -> Option<&str> {
329 self.sse_parser.last_event_id()
330 }
331
332 pub fn next_frame(&mut self) -> Result<Option<SseFrame>, HttpClientStdError> {
335 if self.done {
336 return Ok(None);
337 }
338
339 loop {
340 let arg = self.pending.take();
341 match self.sse_parser.resume(arg.as_deref()) {
342 HttpCoroutineState::Yielded(SseFrameParserYield::Frame(frame)) => {
343 return Ok(Some(frame));
344 }
345 HttpCoroutineState::Yielded(SseFrameParserYield::WantsBytes) => {
346 match self.pull_chunk()? {
347 Some(body) => self.pending = Some(body),
348 None => {
349 self.done = true;
350 return Ok(None);
351 }
352 }
353 }
354 HttpCoroutineState::Complete(never) => match never {},
355 }
356 }
357 }
358
359 pub fn close(self) {
361 drop(self);
362 }
363
364 fn pull_chunk(&mut self) -> Result<Option<Vec<u8>>, HttpClientStdError> {
365 let mut tmp = [0u8; READ_BUFFER_SIZE];
366 let preread = mem::take(&mut self.preread);
367 let mut arg: Option<&[u8]> = if preread.is_empty() {
368 None
369 } else {
370 Some(&preread)
371 };
372
373 loop {
374 match self.chunk_stream.resume(arg.take()) {
375 HttpCoroutineState::Yielded(Http11ChunksReadStreamYield::Frame { body }) => {
376 return Ok(Some(body));
377 }
378 HttpCoroutineState::Complete(Ok(_remaining)) => return Ok(None),
379 HttpCoroutineState::Yielded(Http11ChunksReadStreamYield::WantsRead) => {
380 let n = self.stream.read(&mut tmp)?;
381 if n == 0 {
382 return Ok(None);
383 }
384 arg = Some(&tmp[..n]);
385 }
386 HttpCoroutineState::Complete(Err(err)) => return Err(err.into()),
387 }
388 }
389 }
390}
391
392impl Iterator for SseStream {
393 type Item = Result<SseFrame, HttpClientStdError>;
394
395 fn next(&mut self) -> Option<Self::Item> {
396 match self.next_frame() {
397 Ok(Some(frame)) => Some(Ok(frame)),
398 Ok(None) => None,
399 Err(err) => Some(Err(err)),
400 }
401 }
402}
403
404trait HttpStream: Read + Write + Send {}
408impl<T: Read + Write + Send + ?Sized> HttpStream for T {}