Skip to main content

suno_core/
http.rs

1//! The HTTP port: the engine's only window to the network.
2//!
3//! The engine builds [`HttpRequest`]s and reads [`HttpResponse`]s but never
4//! performs IO itself. A CLI adapter implements [`Http`] with a real client,
5//! which keeps the engine testable with a simple in-memory double.
6
7use std::future::Future;
8
9/// The HTTP method for a request. Clerk and Suno only need these two.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Method {
12    Get,
13    Post,
14}
15
16/// A request the engine wants an adapter to perform.
17#[derive(Debug, Clone)]
18pub struct HttpRequest {
19    pub method: Method,
20    pub url: String,
21    pub headers: Vec<(String, String)>,
22}
23
24impl HttpRequest {
25    /// A bare GET for a public (unauthenticated) URL: no headers, no token.
26    pub fn get(url: impl Into<String>) -> Self {
27        Self {
28            method: Method::Get,
29            url: url.into(),
30            headers: Vec::new(),
31        }
32    }
33}
34
35/// The response an adapter returns to the engine.
36#[derive(Debug, Clone)]
37pub struct HttpResponse {
38    pub status: u16,
39    pub headers: Vec<(String, String)>,
40    pub body: Vec<u8>,
41}
42
43impl HttpResponse {
44    /// Read a header value by case-insensitive name, if present.
45    ///
46    /// The download executor uses this for `Content-Length` (provider-reported
47    /// size) and `Retry-After` (rate-limit backoff), so the lookup must ignore
48    /// header-name casing the way HTTP does.
49    pub fn header(&self, name: &str) -> Option<&str> {
50        self.headers
51            .iter()
52            .find(|(key, _)| key.eq_ignore_ascii_case(name))
53            .map(|(_, value)| value.as_str())
54    }
55}
56
57/// A failure to complete a request at the transport level.
58#[derive(Debug, thiserror::Error)]
59#[error("{0}")]
60pub struct TransportError(pub String);
61
62/// The HTTP port an adapter implements for the engine.
63pub trait Http {
64    /// Perform `request` and return the response, or a [`TransportError`].
65    fn send(
66        &self,
67        request: HttpRequest,
68    ) -> impl Future<Output = Result<HttpResponse, TransportError>> + Send;
69}