Гипер-клиент не может найти информацию об адресе для сервера, работающего на локальном хосте 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, которая является небезопасным языком, но пока не появятся лучшие альтернативы, это хороший обходной путь.