Repository: Orange-OpenSource/hurl
File: packages/hurl/src/runner/query.rs (Rust)

Code before:
    if let Some(certificate) = &response.certificate {
        let value = match certificate_attribute {
            CertificateAttributeName::Subject => Value::String(certificate.subject.clone()),
            CertificateAttributeName::Issuer => Value::String(certificate.issuer.clone()),
            CertificateAttributeName::StartDate => Value::Date(certificate.start_date),
            CertificateAttributeName::ExpireDate => Value::Date(certificate.expire_date),
            CertificateAttributeName::SerialNumber => {
                Value::String(certificate.serial_number.clone())
            }
        };
        Ok(Some(value))
    } else {
        Ok(None)
    }
}

/// Evaluates the ip address of the HTTP `response`.
fn eval_ip(response: &http::Response) -> QueryResult {
    Ok(Some(Value::String(response.ip_addr.to_string())))
}

/// Evaluates the redirects within a list of HTTP `responses`
fn eval_redirects(responses: &[&http::Response]) -> QueryResult {
    let mut values: Vec<Value> = responses
        .iter()
        .map(|r| {
            let location = r
                .headers
                .values("Location")
                .pop()
                .and_then(|value| r.url.join(value).ok());
            Value::HttpResponse(HttpResponse::new(location, r.status))
        })
        .collect();
    // `responses` is the chain list of response triggers by following redirection. We want to only keep
    // "redirected" HTTP response so we drop the last, not-redirected, response.
    values.pop();
    Ok(Some(Value::List(values)))
}

fn eval_cookie_attribute_name(
    cookie_attribute_name: CookieAttributeName,
    cookie: http::ResponseCookie,
    query_source_info: SourceInfo,
) -> QueryResult {
    match cookie_attribute_name {
        CookieAttributeName::Value(_) => Ok(Some(Value::String(cookie.value))),
        CookieAttributeName::Expires(_) => {
            if let Some(s) = cookie.expires() {
                if let Ok(v) = chrono::DateTime::parse_from_rfc2822(s.as_str()) {
                    Ok(Some(Value::Date(v.with_timezone(&chrono::Utc))))

Review comment (suggestion):
As this point of the code, we have a list of response already "resolved": their URLs are already resolved and we don't need to reconstruct  form header `Location` etc... To compute the list of redirection, we can use the current response for the status and the next response URL for the location:

```rust
fn eval_redirects(responses: &[&http::Response]) -> QueryResult {
    let mut it = responses.iter().peekable();
    let mut values: Vec<Value> = vec![];
    while let Some(r) = it.next() {
        // We peek the next response in the response chain to use the next URL response
        // as the current response location. The URLS response chain has already been resolved
        // we don't need to reconstruct the location from response headers.
        let location = it.peek().map(|r| r.url.clone());
        // We're only interested to redirection:
        if location.is_some() {
            let response = Value::HttpResponse(HttpResponse::new(location, r.status));
            values.push(response);
        }
    }
    Ok(Some(Value::List(values)))
}
```


Code after:
    if let Some(certificate) = &response.certificate {
        let value = match certificate_attribute {
            CertificateAttributeName::Subject => Value::String(certificate.subject.clone()),
            CertificateAttributeName::Issuer => Value::String(certificate.issuer.clone()),
            CertificateAttributeName::StartDate => Value::Date(certificate.start_date),
            CertificateAttributeName::ExpireDate => Value::Date(certificate.expire_date),
            CertificateAttributeName::SerialNumber => {
                Value::String(certificate.serial_number.clone())
            }
        };
        Ok(Some(value))
    } else {
        Ok(None)
    }
}

/// Evaluates the ip address of the HTTP `response`.
fn eval_ip(response: &http::Response) -> QueryResult {
    Ok(Some(Value::String(response.ip_addr.to_string())))
}

/// Evaluates the redirects within a list of HTTP `responses`
fn eval_redirects(responses: &[&http::Response]) -> QueryResult {
    let mut it = responses.iter().peekable();
    let mut values: Vec<Value> = vec![];
    while let Some(r) = it.next() {
        // We peek the next response in the response chain to use the next URL response
        // as the current response location. The URLs' response chain has already been resolved
        // we don't need to reconstruct the location from response headers.
        let location = it.peek().map(|r| r.url.clone());
        // We're only interested to redirection:
        if location.is_some() {
            let response = Value::HttpResponse(HttpResponse::new(location, r.status));
            values.push(response);
        }
    }
    Ok(Some(Value::List(values)))
}

fn eval_cookie_attribute_name(
    cookie_attribute_name: CookieAttributeName,
    cookie: http::ResponseCookie,
    query_source_info: SourceInfo,
) -> QueryResult {
    match cookie_attribute_name {
        CookieAttributeName::Value(_) => Ok(Some(Value::String(cookie.value))),
        CookieAttributeName::Expires(_) => {
            if let Some(s) = cookie.expires() {
                if let Ok(v) = chrono::DateTime::parse_from_rfc2822(s.as_str()) {
                    Ok(Some(Value::Date(v.with_timezone(&chrono::Utc))))