Гипер-клиент не может найти информацию об адресе для сервера, работающего на локальном хосте IPv6

У меня есть простой HTTP-сервер, использующий Router и Iron на порту 3005. Он не делает ничего захватывающего. Я считаю, что это просто повторяет запрос, но детали не важны.

Я также сделал простой клиент, использующий клиентский модуль Hyper для отправки запросов на сервер.

Всякий раз, когда я запускаю сервер на IPv4 localhostЯ не испытываю никаких проблем. Я могу запросить это как с моим клиентом, так и с curl. Если я запускаю сервер на моем IPv6 localhost (Я использую сокращенную версию ::1), Я могу получить доступ к серверу только с помощью curl.

Это указывает на то, что сервер работает правильно и отвечает, но мой гипер Client код для доступа к нему не удается, сообщая:

Err (Io (Ошибка { repr: Custom(Custom {вид: Другое, ошибка: StringError("не удалось найти информацию об адресе: имя или служба неизвестна") }) })) поток 'main' запаниковал 'вызвал Result::unwrap() на Err значение: Io(Ошибка { repr: Custom(Custom {вид: Другое, ошибка: StringError("не удалось найти информацию об адресе: имя или служба неизвестна") }) })', /checkout/src/libcore/result.rs:860

Код, который я использую для отправки запроса POST, выглядит следующим образом:

let addr = "http://[::1]:3005/message";
let mut res = self.client.post(addr).body(s.as_str()).send().unwrap();

куда s некоторая полезная нагрузка, которую я посылаю.

Я также пробовал расширенный адрес IPv6 ([0:0:0:0:0:0:0:1]) и я получаю ту же ошибку.

Я также пробовал как сокращенные, так и расширенные адреса IPv6 без скобок. Я получаю "неверный порт -" с расширенным адресом и "Пустой хост" с сокращенным.

Чтобы воспроизвести это поведение, вы можете использовать эти небольшие примеры (раскомментируйте закомментированные строки, чтобы получить ошибку):

сервер

extern crate iron;

use iron::prelude::*;
use iron::status;

fn hello_world(_: &mut Request) -> IronResult<Response> {
    println!("Recvd a request");
    Ok(Response::with((status::Ok, "Hello World!")))
}

fn main() {
    let port = 3000;
    //let addr = format!("{}:{}", "[::1]", port);
    let addr = format!("{}:{}", "localhost", port);

    println!("Server opened on {}", addr);

    Iron::new(hello_world).http(addr).unwrap();
}

клиент

// hyper 0.10.13
extern crate hyper;

use hyper::*;
use std::io::Read;

fn main() {
    let client = Client::new();
    //let mut res = client.get("http://[::1]:3000/").send().unwrap();
    let mut res = client.get("http://localhost:3000/").send().unwrap();

    let mut s = String::new();
    res.read_to_string(&mut s).unwrap();

    println!("response contained: {}", s);
}

ClientV2

// For people that want to try with hyper 0.11.X
extern crate futures;
extern crate hyper;
extern crate tokio_core;

use std::io::{self, Write};
use futures::{Future, Stream};
use hyper::Client;
use tokio_core::reactor::Core;

fn main() {
    let mut core = Core::new().unwrap();
    let client = Client::new(&core.handle());

    let uri = "http://[::1]:3000/".parse().unwrap();
    let work = client.get(uri).and_then(|res| {
        println!("Response: {}", res.status());

        res.body().for_each(|chunk| {
            io::stdout()
                .write_all(&chunk)
                .map(|_| ())
                .map_err(From::from)
        })
    });

    core.run(work).unwrap();

}

Note1:

Вам нужен гипер 0.10.X для запуска этого кода. В моем случае я использовал 0.10.13

Заметка 2:

Я отправляю запросы GET без полезной нагрузки, чтобы абстрагироваться от ненужных битов функциональности.

Заметка 3:

Похоже, гипер 0.10.X и гипер 0.11.X по-разному обрабатывают сервер IPv6. Hyper 0.10.X дает вышеупомянутую ошибку, в то время как 0.11.X выдает мне Response Code 400 Bad Request.

1 ответ

Решение

Поддержка IPv6, по-видимому, является проблемой предыдущих и текущих версий hyperium / hyper (<= 0.11.23)

Разработчики советуют использовать корзину Reqwest для клиентов, использующих гипер 0.11.X, но поскольку Reqwest основывается на гипер, результаты будут такими же.


Решение, которое я нашел до сих пор, состоит в том, чтобы использовать привязки cURL для Rust, поскольку cURL кажется достаточно надежным. Вот мой код для написания клиента, который отправляет простой запрос GET на адрес сервера IPv6.

клиент

extern crate curl;
use std::io::{stdout, Write};
use curl::easy::Easy;

fn main() {
    let mut easy = Easy::new();

    easy.url("https://[::1]:3000").unwrap();
    easy.write_function(|data| {
        stdout().write_all(data).unwrap();
        Ok(data.len())
    }).unwrap();

    easy.perform().unwrap();
}

Это не самое красивое решение, так как в нем используется библиотека, встроенная в C, которая является небезопасным языком, но пока не появятся лучшие альтернативы, это хороший обходной путь.

Другие вопросы по тегам