92 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Rust
		
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Rust
		
	
	
/// Refs https://github.dev/maoertel/diqwest/blob/main/src/blocking.rs
 | 
						|
use anyhow::{anyhow, Result};
 | 
						|
use digest_auth::{AuthContext, AuthorizationHeader, HttpMethod};
 | 
						|
use hyper::{header::AUTHORIZATION, HeaderMap, StatusCode};
 | 
						|
use reqwest::blocking::{RequestBuilder, Response};
 | 
						|
use url::Position;
 | 
						|
 | 
						|
pub fn send_with_digest_auth(
 | 
						|
    request_builder: RequestBuilder,
 | 
						|
    username: &str,
 | 
						|
    password: &str,
 | 
						|
) -> Result<Response> {
 | 
						|
    let first_response = try_clone_request_builder(&request_builder)?.send()?;
 | 
						|
    match first_response.status() {
 | 
						|
        StatusCode::UNAUTHORIZED => {
 | 
						|
            try_digest_auth(request_builder, first_response, username, password)
 | 
						|
        }
 | 
						|
        _ => Ok(first_response),
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn try_digest_auth(
 | 
						|
    request_builder: RequestBuilder,
 | 
						|
    first_response: Response,
 | 
						|
    username: &str,
 | 
						|
    password: &str,
 | 
						|
) -> Result<Response> {
 | 
						|
    if let Some(answer) = get_answer(
 | 
						|
        &request_builder,
 | 
						|
        first_response.headers(),
 | 
						|
        username,
 | 
						|
        password,
 | 
						|
    )? {
 | 
						|
        return Ok(request_builder
 | 
						|
            .header(AUTHORIZATION, answer.to_header_string())
 | 
						|
            .send()?);
 | 
						|
    };
 | 
						|
 | 
						|
    Ok(first_response)
 | 
						|
}
 | 
						|
 | 
						|
fn try_clone_request_builder(request_builder: &RequestBuilder) -> Result<RequestBuilder> {
 | 
						|
    request_builder
 | 
						|
        .try_clone()
 | 
						|
        .ok_or_else(|| anyhow!("Request body must not be a stream"))
 | 
						|
}
 | 
						|
 | 
						|
fn get_answer(
 | 
						|
    request_builder: &RequestBuilder,
 | 
						|
    first_response: &HeaderMap,
 | 
						|
    username: &str,
 | 
						|
    password: &str,
 | 
						|
) -> Result<Option<AuthorizationHeader>> {
 | 
						|
    let answer = calculate_answer(request_builder, first_response, username, password);
 | 
						|
    match answer {
 | 
						|
        Ok(answer) => Ok(Some(answer)),
 | 
						|
        Err(error) => Err(error),
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fn calculate_answer(
 | 
						|
    request_builder: &RequestBuilder,
 | 
						|
    headers: &HeaderMap,
 | 
						|
    username: &str,
 | 
						|
    password: &str,
 | 
						|
) -> Result<AuthorizationHeader> {
 | 
						|
    let request = try_clone_request_builder(request_builder)?.build()?;
 | 
						|
    let path = &request.url()[Position::AfterPort..];
 | 
						|
    let method = HttpMethod::from(request.method().as_str());
 | 
						|
    let body = request.body().and_then(|b| b.as_bytes());
 | 
						|
 | 
						|
    parse_digest_auth_header(headers, path, method, body, username, password)
 | 
						|
}
 | 
						|
 | 
						|
fn parse_digest_auth_header(
 | 
						|
    header: &HeaderMap,
 | 
						|
    path: &str,
 | 
						|
    method: HttpMethod,
 | 
						|
    body: Option<&[u8]>,
 | 
						|
    username: &str,
 | 
						|
    password: &str,
 | 
						|
) -> Result<AuthorizationHeader> {
 | 
						|
    let www_auth = header
 | 
						|
        .get("www-authenticate")
 | 
						|
        .ok_or_else(|| anyhow!("The header 'www-authenticate' is missing."))?
 | 
						|
        .to_str()?;
 | 
						|
    let context = AuthContext::new_with_method(username, password, path, body, method);
 | 
						|
    let mut prompt = digest_auth::parse(www_auth)?;
 | 
						|
 | 
						|
    Ok(prompt.respond(&context)?)
 | 
						|
}
 |