Skip to main content

HttpConnectionManager

Struct HttpConnectionManager 

Source
pub struct HttpConnectionManager { /* private fields */ }
Expand description

HTTP 连接管理器

提供 HTTP 连接的获取、释放、池化管理和重定向跟随功能。 支持 Keep-Alive 连接复用、LRU 淘汰策略和循环重定向检测。

§线程安全

HttpConnectionManager 内部使用 tokio::sync::Mutex 保护共享状态, 可以安全地在多个异步任务之间共享。

§性能特性

  • 连接复用: 通过 Keep-Alive 头部检查,避免重复建立 TCP 连接
  • LRU 淘汰: 自动清理空闲超时的连接,防止资源泄漏
  • 三级超时: 分别控制连接、读取、写入三个阶段的超时时间

§示例

use aria2_core::http::connection::{HttpConnectionManager, HttpConfig};
use std::time::Duration;
use url::Url;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = HttpConfig {
        max_connections: 8,
        ..Default::default()
    };

    let mut manager = HttpConnectionManager::new(&config);
    let url = Url::parse("https://example.com/file")?;

    let conn = manager.acquire(&url).await?;
    // 使用连接进行 HTTP 请求...
    manager.release(conn.id).await;

    Ok(())
}

Implementations§

Source§

impl HttpConnectionManager

Source

pub fn new(config: &HttpConfig) -> Self

创建新的 HTTP 连接管理器

§参数
  • config - HTTP 连接配置,包含超时、最大连接数等参数
§返回值

返回初始化完成的连接管理器实例

§示例
use aria2_core::http::connection::{HttpConnectionManager, HttpConfig};
use std::time::Duration;

let config = HttpConfig {
    max_connections: 10,
    connect_timeout: Duration::from_secs(15),
    read_timeout: Duration::from_secs(30),
    write_timeout: Duration::from_secs(30),
    idle_timeout: Duration::from_secs(120),
};

let manager = HttpConnectionManager::new(&config);
assert_eq!(manager.max_connections(), 10);
Source

pub fn max_connections(&self) -> usize

获取最大连接数配置

Source

pub fn active_count(&self) -> usize

获取当前活动连接数

Source

pub fn pool_size(&self) -> usize

获取连接池大小(包含空闲和使用中的连接)

Source

pub async fn acquire(&mut self, url: &Url) -> Result<ActiveConnection>

从连接池获取或新建一个到指定 URL 的连接

该方法会尝试从连接池中查找可复用的空闲连接(基于主机名匹配), 如果没有可用连接且未达到最大连接数限制,则创建新连接。

§参数
  • url - 目标 URL,用于提取主机名和端口信息
§错误
§返回值

返回可用的活动连接实例

§Keep-Alive 复用逻辑
  1. 提取 URL 的 host:port 作为连接标识
  2. 在连接池中查找该主机的空闲连接
  3. 验证连接有效性(检查 socket 是否正常)
  4. 更新 last_used 时间戳并返回
  5. 如果无可用连接,创建新的 TCP 连接
§示例
use aria2_core::http::connection::HttpConnectionManager;
use url::Url;

#[tokio::main]
async fn main() {
    let mut manager = HttpConnectionManager::new(&Default::default());
    let url = Url::parse("https://example.com/resource").unwrap();

    match manager.acquire(&url).await {
        Ok(conn) => println!("获取连接成功: id={}", conn.id),
        Err(e) => eprintln!("获取连接失败: {}", e),
    }
}
Source

pub async fn release(&mut self, conn_id: u64)

归还连接到连接池

将使用完毕的连接归还到连接池中,以便后续复用。 如果连接已失效(socket 关闭或出错),会自动从池中移除。

§参数
  • conn_id - 要归还的连接 ID
§行为
  1. 根据连接 ID 在池中查找对应连接
  2. 更新 last_used 为当前时间
  3. 将连接标记为空闲状态
  4. 如果连接无效,自动清理资源
§示例
use aria2_core::http::connection::HttpConnectionManager;
use url::Url;

#[tokio::main]
async fn main() {
    let mut manager = HttpConnectionManager::new(&Default::default());
    let url = Url::parse("https://example.com").unwrap();

    let conn = manager.acquire(&url).await.unwrap();
    // 使用连接完成请求后...
    manager.release(conn.id).await;
}
Source

pub fn follow_redirects( &self, response: &HttpResponse, current_url: &Url, redirect_chain: &HashSet<Url>, redirect_count: u32, ) -> Result<Url>

跟随 HTTP 重定向

解析响应中的 Location 头部,构建新的 URL 并验证重定向合法性。 支持相对路径和绝对路径的重定向,自动处理循环重定向检测。

§参数
  • response - HTTP 响应对象,需包含 Location 头部
  • current_url - 当前请求的 URL(用于解析相对路径)
  • redirect_chain - 已访问过的 URL 集合(用于循环检测)
§错误
§返回值

返回重定向目标的新 URL

§重定向链检测机制
  1. 使用 HashSet 记录所有已访问 URL
  2. 每次重定向前检查新 URL 是否已在集合中
  3. 维护跳数计数器,超过阈值返回错误
  4. 支持最多 5 次 301/302/303/307/308 重定向
§示例
use aria2_core::http::connection::{HttpConnectionManager, HttpResponse};
use std::collections::HashSet;
use url::Url;

#[tokio::main]
async fn main() {
    let manager = HttpConnectionManager::new(&Default::default());
    let current_url = Url::parse("http://example.com/old").unwrap();
    let mut chain = HashSet::new();
    chain.insert(current_url.clone());

    let mut response = HttpResponse::new(301, "Moved".to_string());
    response.headers.push((
        "Location".to_string(),
        "/new-path".to_string(),
    ));

    match manager.follow_redirects(&response, &current_url, &chain, 1) {
        Ok(new_url) => println!("重定向到: {}", new_url),
        Err(e) => eprintln!("重定向失败: {}", e),
    }
}
Source

pub async fn follow_redirects_iterative<F, Fut>( &self, initial_url: &Url, get_response: F, ) -> Result<HttpResponse>
where F: FnMut(&Url) -> Fut, Fut: Future<Output = Result<HttpResponse>>,

Iteratively follow HTTP redirects with loop detection

This method replaces recursive redirect following with an iterative approach, eliminating stack overflow risk for deep redirect chains.

§Arguments
  • initial_url - The starting URL for the request
  • get_response - Async closure that fetches the HTTP response for a given URL
§Returns

The final non-redirect HttpResponse, or an error if:

  • Too many redirects (exceeds MAX_REDIRECTS limit)
  • Redirect loop detected (same URL visited twice)
  • Missing Location header in redirect response
  • Invalid URL in Location header
§Performance characteristics
  • Uses HashSet for O(1) loop detection instead of linear scan
  • Iterative loop with bounded iterations prevents stack growth
  • Maximum 5 redirects as per RFC 7231 recommendation
Source

pub fn build_range_header(&self, start: u64, end: Option<u64>) -> String

构建 Range 请求头

根据 start 和 end 字节位置构建符合 RFC 7233 规范的 Range 头部字符串。 用于断点续传和分块下载场景。

§参数
  • start - 起始字节位置(包含)
  • end - 结束字节位置(包含),如果为 None 则表示到文件末尾
§返回值

返回格式化的 Range 头部值,例如 "bytes=0-499""bytes=500-"

§格式规范
  • bytes=start-end: 指定范围 [start, end]
  • bytes=start-: 从 start 到文件末尾
§示例
use aria2_core::http::connection::HttpConnectionManager;

let manager = HttpConnectionManager::new(&Default::default());

// 完整范围
assert_eq!(
    manager.build_range_header(0, Some(499)),
    "bytes=0-499"
);

// 开放结束范围
assert_eq!(
    manager.build_range_header(1000, None),
    "bytes=1000-"
);

// 单字节范围
assert_eq!(
    manager.build_range_header(42, Some(42)),
    "bytes=42-42"
);
Source

pub fn parse_content_range(&self, header: &str) -> Option<(u64, u64, u64)>

解析 Content-Range 响应头

解析服务器返回的 Content-Range 头部值,提取范围信息和总大小。 用于验证服务器是否正确支持 Range 请求。

§参数
  • header - Content-Range 头部的原始字符串值
§返回值

如果解析成功,返回元组 (start, end, total):

  • start: 范围起始字节(包含)
  • end: 范围结束字节(包含)
  • total: 文件总字节数(如果未知则为 u64::MAX)

如果格式无效,返回 None

§支持格式
  • bytes 0-499/1000: 已知总大小的范围
  • bytes 0-499/*: 未知总大小的范围
§示例
use aria2_core::http::connection::HttpConnectionManager;

let manager = HttpConnectionManager::new(&Default::default());

// 解析已知总大小
let result = manager.parse_content_range("bytes 0-499/1000");
assert_eq!(result, Some((0, 499, 1000)));

// 解析未知总大小
let result = manager.parse_content_range("bytes 500-999/*");
assert_eq!(result, Some((500, 999, u64::MAX)));

// 无效格式
assert_eq!(manager.parse_content_range("invalid"), None);
assert_eq!(manager.parse_content_range("bits 0-99/1000"), None);
Source

pub async fn cleanup(&mut self)

清理所有空闲连接

关闭连接池中的所有连接,释放系统资源。 通常在下载任务完成或程序退出时调用。

Source

pub async fn close_connection(&mut self, conn_id: u64)

Force close a specific connection

Remove the connection from the pool and close the underlying TCP connection. Used for error handling or abnormal termination.

§Arguments
  • conn_id - The ID of the connection to close

Set the cookie jar for automatic cookie management on HTTP requests.

Once set, the connection manager will automatically:

  • Attach Cookie headers with matching cookies when building outgoing requests
  • Parse and store cookies from Set-Cookie response headers
§Arguments
  • jar - The CookieJar instance to use for cookie storage and matching
§Example
use aria2_core::http::connection::{HttpConnectionManager, HttpConfig};
use aria2_core::http::cookie_storage::CookieJar;

let mut manager = HttpConnectionManager::new(&Default::default());
let jar = CookieJar::new();
manager.set_cookie_jar(Some(jar));
Source

pub fn cookie_jar(&self) -> &Option<CookieJar>

Get a reference to the current cookie jar, if one is set.

Source

pub fn cookie_jar_mut(&mut self) -> &mut Option<CookieJar>

Get a mutable reference to the current cookie jar, if one is set.

Source

pub fn attach_cookies_to_request(&self, url: &Url) -> Option<String>

Attach matching cookies from the jar to an HTTP request as a Cookie header string.

Call this method before sending an HTTP request to include any stored cookies that match the target URL. The returned string can be used directly as the value of the Cookie request header.

§Arguments
  • url - The target URL for the HTTP request
§Returns

Some(header_value) containing "name1=val1; name2=val2" format if matching cookies exist, or None if no cookies match or no jar is configured.

§Example
use aria2_core::http::connection::HttpConnectionManager;
use url::Url;

let manager = HttpConnectionManager::new(&Default::default());
let url = Url::parse("https://example.com/api").unwrap();

if let Some(cookie_header) = manager.attach_cookies_to_request(&url) {
    // Add "Cookie: {cookie_header}" to your HTTP request headers
    println!("Cookie: {}", cookie_header);
}
Source

pub fn extract_cookies_from_response( &mut self, response_headers: &[(String, String)], _request_url: &Url, ) -> usize

Extract cookies from response Set-Cookie headers and store them in the jar.

Call this method after receiving an HTTP response to persist any cookies set by the server. Each Set-Cookie header value is parsed and stored in the cookie jar for future requests.

§Arguments
  • response_headers - The response headers as a slice of (name, value) tuples
  • request_url - The original request URL (used as default domain/path context)
§Returns

The number of cookies successfully extracted and stored.

§Example
use aria2_core::http::connection::HttpConnectionManager;

// After receiving an HTTP response:
let headers = vec![
    ("Set-Cookie".to_string(), "session=abc; Domain=example.com".to_string()),
    ("Set-Cookie".to_string(), "theme=dark".to_string()),
];
let url = url::Url::parse("https://example.com/").unwrap();

let mut manager = HttpConnectionManager::new(&Default::default());
manager.set_cookie_jar(Some(aria2_core::http::cookie_storage::CookieJar::new()));
let count = manager.extract_cookies_from_response(&headers, &url);
println!("Stored {} cookies", count); // Prints: Stored 2 cookies

Trait Implementations§

Source§

impl Debug for HttpConnectionManager

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for HttpConnectionManager

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more