Rust HTTP归档库har的使用:高效解析与生成HTTP请求/响应数据格式

Rust HTTP归档库har的使用:高效解析与生成HTTP请求/响应数据格式

HTTP归档格式(HAR)序列化与反序列化库,使用Rust编写。

Latest version Documentation License

安装

在您的Cargo.toml文件中添加以下内容:

[dependencies]
har = "0.8"

使用

最简单的示例:

use har::from_path;

fn main() {
  match har::from_path("path/to/file.har") {
    Ok(spec) => println!("spec: {:?}", spec),
    Err(err) => println!("error: {}", err)
  }
}

完整示例代码

基于提供的示例,以下是一个更完整的HAR文件解析和生成示例:

use har::{Har, from_path, to_string};
use std::fs::File;
use std::io::Read;
use std::path::Path;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 示例1: 从文件路径解析HAR文件
    let har_path = "example.har";
    
    match from_path(har_path) {
        Ok(har) => {
            println!("成功解析HAR文件:");
            println!("版本: {}", har.log.version);
            println!("创建者: {}", har.log.creator.name);
            println!("条目数量: {}", har.log.entries.len());
            
            // 打印所有请求的URL
            for (i, entry) in har.log.entries.iter().enumerate() {
                println!("条目 {}: {}", i + 1, entry.request.url);
            }
        }
        Err(err) => println!("解析错误: {}", err),
    }

    // 示例2: 从字符串内容解析HAR
    let har_content = r#"
    {
        "log": {
            "version": "1.2",
            "creator": {
                "name": "Example Creator",
                "version": "1.0"
            },
            "entries": [
                {
                    "request": {
                        "method": "GET",
                        "url": "https://example.com",
                        "httpVersion": "HTTP/1.1",
                        "headers": [],
                        "queryString": [],
                        "cookies": [],
                        "headersSize": 150,
                        "bodySize": 0
                    },
                    "response": {
                        "status": 200,
                        "statusText": "OK",
                        "httpVersion": "HTTP/1.1",
                        "headers": [],
                        "cookies": [],
                        "content": {
                            "size": 1270,
                            "mimeType": "text/html"
                        },
                        "redirectURL": "",
                        "headersSize": 150,
                        "bodySize": 1120
                    },
                    "startedDateTime": "2024-01-01T00:00:00.000Z",
                    "time": 50,
                    "cache": {},
                    "timings": {
                        "send": 10,
                        "wait": 30,
                        "receive": 10
                    }
                }
            ]
        }
    }"#;

    // 解析字符串内容
    let har: Har = serde_json::from_str(har_content)?;
    println!("\n从字符串解析的HAR:");
    println!("版本: {}", har.log.version);

    // 示例3: 生成HAR字符串
    let generated_har = to_string(&har)?;
    println!("\n生成的HAR字符串:");
    println!("{}", generated_har);

    Ok(())
}

// 示例4: 创建新的HAR条目
fn create_har_example() -> Har {
    Har {
        log: har::Log {
            version: "1.2".to_string(),
            creator: har::Creator {
                name: "Rust HAR Library".to_string(),
                version: "0.8.0".to_string(),
                comment: None,
            },
            browser: None,
            pages: None,
            entries: vec![],
            comment: None,
        },
    }
}

贡献

该项目遵循语义化版本控制、约定式提交和使用mandrean/semantic-rs进行语义发布。

注意

灵感来源于softprops/openapi。


1 回复

Rust HTTP归档库har的使用:高效解析与生成HTTP请求/响应数据格式

概述

har是一个用于解析和生成HTTP归档格式(HTTP Archive format)的Rust库。该格式用于记录HTTP请求和响应的详细信息,常用于性能分析、调试和测试场景。

安装方法

在Cargo.toml中添加依赖:

[dependencies]
har = "0.1"

核心功能

1. 解析HAR文件

use har::Har;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file = File::open("example.har")?;
    let har: Har = serde_json::from_reader(file)?;
    
    println!("HAR版本: {}", har.version);
    println!("包含{}个请求", har.log.entries.len());
    
    Ok(())
}

2. 生成HAR文件

use har::{Har, Log, Entry, Request, Response};
use std::time::SystemTime;

fn create_sample_har() -> Har {
    Har {
        version: "1.2".to_string(),
        log: Log {
            version: "1.2".to_string(),
            creator: har::Creator {
                name: "my-app".to_string(),
                version: "1.0".to_string(),
            },
            entries: vec![Entry {
                started_date_time: SystemTime::now(),
                time: 100.0,
                request: Request {
                    method: "GET".to_string(),
                    url: "https://example.com/api/data".to_string(),
                    headers: vec![],
                    query_string: vec![],
                    post_data: None,
                    headers_size: -1,
                    body_size: -1,
                },
                response: Response {
                    status: 200,
                    status_text: "OK".to_string(),
                    headers: vec![],
                    content: har::Content {
                        size: 1024,
                        mime_type: "application/json".to_string(),
                        text: Some(r#"{"data": "example"}"#.to_string()),
                    },
                    redirect_url: "".to_string(),
                    headers_size: -1,
                    body_size: -1,
                },
                cache: har::Cache::default(),
                timings: har::Timings {
                    blocked: -1.0,
                    dns: -1.0,
                    connect: -1.0,
                    send: 10.0,
                    wait: 50.0,
                    receive: 40.0,
                    ssl: -1.0,
                },
                server_ip_address: None,
                connection: None,
                comment: None,
            }],
        },
    }
}

3. 处理请求和响应数据

use har::Har;

fn analyze_har(har: &Har) {
    for (i, entry) in har.log.entries.iter().enumerate() {
        println!("请求 {}: {}", i + 1, entry.request.url);
        println!("方法: {}", entry.request.method);
        println!("状态码: {}", entry.response.status);
        println!("响应时间: {}ms", entry.time);
        println!("---");
    }
}

4. 过滤和搜索条目

use har::Har;

fn find_requests_by_status(har: &Har, status_code: u16) -> Vec<&Entry> {
    har.log.entries
        .iter()
        .filter(|entry| entry.response.status == status_code)
        .collect()
}

fn find_requests_by_url(har: &Har, url_pattern: &str) -> Vec<&Entry> {
    har.log.entries
        .iter()
        .filter(|entry| entry.request.url.contains(url_pattern))
        .collect()
}

完整示例:读取和分析HAR文件

use har::Har;
use std::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 读取HAR文件
    let file = File::open("network_logs.har")?;
    let har: Har = serde_json::from_reader(file)?;
    
    // 分析性能数据
    let total_time: f64 = har.log.entries.iter().map(|e| e.time).sum();
    let avg_time = total_time / har.log.entries.len() as f64;
    
    println!("总请求数: {}", har.log.entries.len());
    println!("总耗时: {:.2}ms", total_time);
    println!("平均请求时间: {:.2}ms", avg_time);
    
    // 统计状态码分布
    let mut status_counts = std::collections::HashMap::new();
    for entry in &har.log.entries {
        *status_counts.entry(entry.response.status).or_insert(0) += 1;
    }
    
    println!("\n状态码分布:");
    for (status, count) in status_counts {
        println!("{}: {} 次请求", status, count);
    }
    
    Ok(())
}

最佳实践

  1. 错误处理:始终处理可能的解析错误
  2. 内存管理:对于大型HAR文件,考虑流式处理
  3. 数据验证:验证HAR文件的结构完整性
  4. 性能优化:在处理大量数据时使用适当的缓存策略

注意事项

  • HAR文件可能包含敏感信息,处理时需注意数据安全
  • 确保HAR文件符合规范格式,避免解析错误
  • 考虑使用异步处理来提高大文件的处理性能

这个库为处理HTTP归档数据提供了强大而灵活的工具,特别适合网络性能分析和调试场景。

完整示例Demo

use har::{Har, Log, Entry, Request, Response, Creator, Content, Cache, Timings};
use std::fs::File;
use std::io::Write;
use std::time::SystemTime;
use std::collections::HashMap;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 示例1: 创建并写入HAR文件
    println!("创建示例HAR文件...");
    let har = create_sample_har();
    
    // 将HAR对象序列化为JSON并写入文件
    let mut file = File::create("sample.har")?;
    let json = serde_json::to_string_pretty(&har)?;
    file.write_all(json.as_bytes())?;
    println!("HAR文件已创建: sample.har");
    
    // 示例2: 读取并解析HAR文件
    println!("\n读取并解析HAR文件...");
    let file = File::open("sample.har")?;
    let parsed_har: Har = serde_json::from_reader(file)?;
    
    // 显示基本信息
    println!("HAR版本: {}", parsed_har.version);
    println!("创建者: {} {}", parsed_har.log.creator.name, parsed_har.log.creator.version);
    println!("包含{}个请求条目", parsed_har.log.entries.len());
    
    // 示例3: 分析HAR数据
    println!("\n分析HAR数据...");
    analyze_har(&parsed_har);
    
    // 示例4: 过滤请求
    println!("\n过滤状态码为200的请求...");
    let successful_requests = find_requests_by_status(&parsed_har, 200);
    println!("找到{}个成功的请求", successful_requests.len());
    
    // 示例5: 性能统计
    println!("\n性能统计:");
    let total_time: f64 = parsed_har.log.entries.iter().map(|e| e.time).sum();
    let avg_time = total_time / parsed_har.log.entries.len() as f64;
    
    println!("总请求数: {}", parsed_har.log.entries.len());
    println!("总耗时: {:.2}ms", total_time);
    println!("平均请求时间: {:.2}ms", avg_time);
    
    // 统计状态码分布
    let mut status_counts = HashMap::new();
    for entry in &parsed_har.log.entries {
        *status_counts.entry(entry.response.status).or_insert(0) += 1;
    }
    
    println!("\n状态码分布:");
    for (status, count) in status_counts {
        println!("{}: {} 次请求", status, count);
    }
    
    Ok(())
}

// 创建示例HAR文件
fn create_sample_har() -> Har {
    Har {
        version: "1.2".to_string(),
        log: Log {
            version: "1.2".to_string(),
            creator: Creator {
                name: "har-demo-app".to_string(),
                version: "1.0".to_string(),
            },
            entries: vec![
                // 第一个请求条目
                Entry {
                    started_date_time: SystemTime::now(),
                    time: 150.0,
                    request: Request {
                        method: "GET".to_string(),
                        url: "https://api.example.com/users".to_string(),
                        headers: vec![],
                        query_string: vec![],
                        post_data: None,
                        headers_size: -1,
                        body_size: -1,
                    },
                    response: Response {
                        status: 200,
                        status_text: "OK".to_string(),
                        headers: vec![],
                        content: Content {
                            size: 2048,
                            mime_type: "application/json".to_string(),
                            text: Some(r#"{"users": ["user1", "user2", "user3"]}"#.to_string()),
                        },
                        redirect_url: "".to_string(),
                        headers_size: -1,
                        body_size: -1,
                    },
                    cache: Cache::default(),
                    timings: Timings {
                        blocked: -1.0,
                        dns: 10.0,
                        connect: 20.0,
                        send: 5.0,
                        wait: 80.0,
                        receive: 35.0,
                        ssl: -1.0,
                    },
                    server_ip_address: None,
                    connection: None,
                    comment: None,
                },
                // 第二个请求条目
                Entry {
                    started_date_time: SystemTime::now(),
                    time: 80.0,
                    request: Request {
                        method: "POST".to_string(),
                        url: "https://api.example.com/login".to_string(),
                        headers: vec![],
                        query_string: vec![],
                        post_data: None,
                        headers_size: -1,
                        body_size: -1,
                    },
                    response: Response {
                        status: 401,
                        status_text: "Unauthorized".to_string(),
                        headers: vec![],
                        content: Content {
                            size: 512,
                            mime_type: "application/json".to_string(),
                            text: Some(r#"{"error": "Invalid credentials"}"#.to_string()),
                        },
                        redirect_url: "".to_string(),
                        headers_size: -1,
                        body_size: -1,
                    },
                    cache: Cache::default(),
                    timings: Timings {
                        blocked: -1.0,
                        dns: 5.0,
                        connect: 15.0,
                        send: 3.0,
                        wait: 40.0,
                        receive: 17.0,
                        ssl: -1.0,
                    },
                    server_ip_address: None,
                    connection: None,
                    comment: None,
                }
            ],
        },
    }
}

// 分析HAR文件内容
fn analyze_har(har: &Har) {
    for (i, entry) in har.log.entries.iter().enumerate() {
        println!("条目 {}:", i + 1);
        println!("  URL: {}", entry.request.url);
        println!("  方法: {}", entry.request.method);
        println!("  状态码: {}", entry.response.status);
        println!("  响应大小: {} bytes", entry.response.content.size);
        println!("  总时间: {}ms", entry.time);
        println!("  时间分解 - DNS: {}ms, 连接: {}ms, 发送: {}ms, 等待: {}ms, 接收: {}ms",
                entry.timings.dns, entry.timings.connect, 
                entry.timings.send, entry.timings.wait, entry.timings.receive);
        println!("---");
    }
}

// 根据状态码过滤请求
fn find_requests_by_status(har: &Har, status_code: u16) -> Vec<&Entry> {
    har.log.entries
        .iter()
        .filter(|entry| entry.response.status == status_code)
        .collect()
}

// 根据URL模式过滤请求
fn find_requests_by_url(har: &Har, url_pattern: &str) -> Vec<&Entry> {
    har.log.entries
        .iter()
        .filter(|entry| entry.request.url.contains(url_pattern))
        .collect()
}
回到顶部