I have a simple HTTP server using Router and Iron on port 3005. It is not doing anything exciting. I believe it just echoes back the request, but the details are not important.
I have also made a simple client using hyper's client module to send requests to the server.
Whenever I run the server on IPv4 localhost
, I experience no issues. I can query it both with my client and with curl. If I start the server on my IPv6 localhost
(I am using the shortened version ::1
), I am only able to access the server with curl.
This indicates that the server is running properly and responding, but my hyper Client
code to access it fails, reporting:
Err(Io(Error { repr: Custom(Custom { kind: Other, error: StringError("failed to lookup address information: Name or service not known") }) })) thread 'main' panicked at 'called
Result::unwrap()
on anErr
value: Io(Error { repr: Custom(Custom { kind: Other, error: StringError("failed to lookup address information: Name or service not known") }) })', /checkout/src/libcore/result.rs:860
The code I use to send the POST request is as follows:
let addr = "http://[::1]:3005/message";
let mut res = self.client.post(addr).body(s.as_str()).send().unwrap();
Where s
is some payload I am sending.
I have tried the expanded IPv6 address as well ([0:0:0:0:0:0:0:1]
) and I get the same error.
I have also tried both the shortened and the expanded IPv6 addresses without the brackets. I get "invalid port -" with the expanded address and "Empty Host" with the shortened.
To reproduce this behaviour, you can use these small examples (uncomment the commented lines to recieve the error):
Server
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();
}
Client
// 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:
You need hyper 0.10.X in order to get this code running. In my case I was using 0.10.13
Note2:
I am sending GET requests with no payload, in order to abstract out the irrelevant bits of functionality.
Note3:
It seems like hyper 0.10.X and hyper 0.11.X handle the IPv6 server differently. Hyper 0.10.X gives the aforementioned error, while 0.11.X gives me Response Code 400 Bad Request.
IPv6 support seems to be an issue with the previous and current versions of hyperium/hyper (<=0.11.23)
The developers advise using the Reqwest crate for clients using hyper 0.11.X, but since Reqwest builds upon hyper, the results will be the same.
The solution I have found so far is to use the bindings of cURL for Rust as cURL seems to be robust enough. Here is my code for writing a client that sends a simple GET request to an IPv6 server address.
Client
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();
}
This is not the prettiest solution as it uses a library built in C, which is an unsafe language, but until better alternatives come up it is a nice workaround.