Is this a good way to parse the username and password from a URL?


#1

I’m learning Rust and writing some code to connect to a URL protected by basic authentication. The environment variable would be URL=http://user:pass@localhost/. Does this code represent an ordinary amount of unwrap() and to_x() for Rust?

extern crate hyper;

use hyper::{Url};
use hyper::header::{Headers, Authorization, Basic};
use std::env;

pub trait FromUrl {
    fn from_url(&Url) -> Self;
}

impl FromUrl for Basic {
    fn from_url(url: &Url) -> Basic {
        Basic {
            username: url.username().to_owned(),
            password: Some(url.password().expect("URL must contain password").to_string()),
        }
    }
}

fn main() {
    let url = Url::parse(&env::var("URL").unwrap()).unwrap();
    let mut headers = Headers::new();
    headers.set(Authorization(Basic::from_url(&url)));
}

#2

You’ve got the right idea using a trait to get basic auth from the url. I’ve made a few simple tweaks to your program that help readability, mostly changing the FromUrl trait to a TryFromUrl trait instead, so it can return an error instead of panicking.

extern crate hyper;

use hyper::{Url};
use hyper::header::{Headers, Authorization, Basic};
use std::env;

pub trait TryFromUrl: Sized {
    fn try_from_url(&Url) -> Result<Self, String>;
}

impl TryFromUrl for Basic {
    fn try_from_url(url: &Url) -> Result<Basic, String> {
        let password = url.password().ok_or("URL must contain password".to_owned())?;

        Ok(Basic {
            username: url.username().to_owned(),
            password: Some(password.to_owned()),
        })
    }
}

fn main() {
    let env_var = env::var("URL").expect("'URL' environment variable must be set");
    let url = Url::parse(&env_var).expect("URL must have the form 'http://user:pass@host'");
    let auth = Basic::try_from_url(&url).expect("Couldn't get basic auth from URL");

    let mut headers = Headers::new();
    headers.set(Authorization(auth));
}

Rust has a bunch of methods on Option and Result for dealing with None or Err values. In this case, I’ve used ok_or to either get the Some value of the url password, or early return the string error with the ? operator.

You can’t really get away with the to_owned calls when building the Basic header, because it expects to own the username and password, but the getters on url only give you borrowed data, in case you don’t actually need to own it. There are a bunch of ways to handle errors in Rust, the book has a lot of detail on it.

Does that make sense?


#3

Thanks, that’s helpful.