• rust Попробовала склеить два разных примера для работы с http и https (client) в одном коде, минимизируя повторяющийся код. Сначала это получилось, потом все сломалось, когда начала обрабатывать 302 редирект — так и не смогла догнать, как типизировать разные вещества из hyper и hyper-tls. Завтра буду пробовать другую библиотеку, request, согласно доке она "на батарейках". Надеюсь, с ней будет сильно поменьше рукописного кода, как это было в golang.
    ♡ recommended by @Anonymous, @O01eg

Replies (25)

  • @ermine, hyper должен слушать https не через hyper-tls, если я не ошибаюсь. hyper-tls предоставляет только клиентскую часть
  • @O01eg, А мне клиент нужен
  • @ermine, Так там проще
  • @O01eg, Это очень сложно с hyper
  • @ermine, А в чём сложжность?
  • @O01eg, Скомпилируй, плиз!

    use std::env;
    use std::io::{self, Write};

    use hyper::rt::{Future, Stream};
    use hyper::Client;

    fn main() {
    pretty_env_logger::init();

    let url = match env::args().nth(1) {
    Some(url) => url,
    None => {
    println!("Usage: client <url>");
    return;
    }
    };
    hyper::rt::run(fetch(&url));
    }

    fn fetch(url: &str) -> impl Future<Item = (), Error = ()> {
    let url = url.parse::<hyper::Uri>().unwrap();
    if url.scheme_part().map(|s| s.as_ref()) != Some("http") {
    fetch_https(url)
    } else {
    fetch_http(url)
    }
    }

    fn fetch_https(url: http::uri::Uri) -> impl Future<Item = (), Error = ()> {
    let https = hyper_tls::HttpsConnector::new(4).unwrap();
    let client = hyper::Client::builder().build::<_, hyper::Body>(https);
    client_handle(&client, url)
    }

    fn fetch_http(url: http::uri::Uri) -> impl Future<Item = (), Error = ()> {
    let client = Client::new();
    client_handle(&client, url)
    }

    fn client_handle<'a, T: 'static>(
    client: &'a Client<T>,
    url: http::uri::Uri,
    ) -> impl Future<Item = (), Error = ()>
    where
    T: hyper::client::connect::Connect,
    {
    client
    .get(url)
    .and_then(|res| {
    println!("Response: {}", res.status());
    println!("Headers: {:#?}", res.headers());
    if res.status() == 302 {
    let location: &str = res.headers()[http::header::LOCATION].to_str().unwrap();
    fetch(location)
    } else {
    res.into_body().for_each(|chunk| {
    io::stdout()
    .write_all(&chunk)
    .map_err(|e| panic!("example expects stdout is open, err={}", e))
    })
    }
    })
    .map(|_| {
    println!("\n\nDone");
    })
    .map_err(|err| {
    eprintln!("Error {}", err);
    })
    }
  • @ermine, А почему у тебя fetch не возвращает hyper::error::Error?
  • @ermine, Я часть ошибок поправил play.rust-lang.org
  • @O01eg, ой, твоя правка выглядит чисто академической! такое можно освоить только за 10 лет упорного писания кода на расте. И почему только часть? А остальное?
  • @ermine, Немного занят, да и код потом тебе ещё писать. см. вопрос в /8
  • @O01eg, ну потому что изначальный код был из hyper/examples/client.rs с добавлением туда из примера hyper_tls/examples/client.rs
  • @ermine, Тогда как-то так play.rust-lang.org
  • @O01eg, н-да, совсем не изящная рекурсия в расте
  • @ermine, А там нет проблем с рекурсией
  • @O01eg, я так и не смогла докомпилить этот пример, хотя по дороге осознала полтора нюанса про повадки вывода типов в густе. Щас я уткнулась в то, что у hyper::error::Error нет доступных функций, чтобы создавать этот тип. Может, есть где-то уже кем-то выстраданный похожий код?
  • @ermine, А зачем тебе создавать hyper::error::Error?
  • @O01eg, Оно не дает скопились!!!! Я пропустила hyper::error::Error в Future<Item=(), Error=hyper::error::Error>, добавила еще одну функцию, чтобы в rt::run было без этого. Поэтому первый бранч if в последней функции больше не жалуется на этот тип, а в else он жалуется на io::Error. Попыталась конвертировать его в hyper::error::Error. Не дает!
  • @ermine, Скинь ссылку на play.rust-lang.org
  • @O01eg, а где там волшебная кнопочка с "Post new Code" или что-нить в этом духе?
  • @ermine, Волшебная кнопочка называется "Share"
  • @O01eg, Не, я искала как создать пустую страницу, но плюнула и заместила содержимое. play.rust-lang.org
  • @ermine, А, понял, ты ошибку со stdout хочешь пробросить. Тогда тебе нужно ошибку и hyper, и std::io::Error к одному типу преобразовывать. Как один из вариантов, если не использовать свой тип и не использовать failure doc.rust-lang.org
  • @ermine, Типа вот так play.rust-lang.org
  • @O01eg, Ура! Код наконец скомпилился! Спасибо большое! Правда, код выглядит мерзко, наверное потом перепишу, когда IQ немного подрастет.