|
17 | 17 |
|
18 | 18 | use fmt;
|
19 | 19 | use from_str::FromStr;
|
| 20 | +use io::{mod, IoResult, IoError}; |
| 21 | +use io::net; |
20 | 22 | use iter::Iterator;
|
21 | 23 | use option::{Option, None, Some};
|
| 24 | +use result::{Ok, Err}; |
22 | 25 | use str::StrSlice;
|
23 | 26 | use slice::{MutableCloneableSlice, MutableSlice, ImmutableSlice};
|
| 27 | +use vec::Vec; |
24 | 28 |
|
25 | 29 | pub type Port = u16;
|
26 | 30 |
|
@@ -348,6 +352,189 @@ impl FromStr for SocketAddr {
|
348 | 352 | }
|
349 | 353 | }
|
350 | 354 |
|
| 355 | +/// A trait for objects which can be converted or resolved to one or more `SocketAddr` values. |
| 356 | +/// |
| 357 | +/// Implementing types minimally have to implement either `to_socket_addr` or `to_socket_addr_all` |
| 358 | +/// method, and its trivial counterpart will be available automatically. |
| 359 | +/// |
| 360 | +/// This trait is used for generic address resolution when constructing network objects. |
| 361 | +/// By default it is implemented for the following types: |
| 362 | +/// |
| 363 | +/// * `SocketAddr` - `to_socket_addr` is identity function. |
| 364 | +/// |
| 365 | +/// * `(IpAddr, u16)` - `to_socket_addr` constructs `SocketAddr` trivially. |
| 366 | +/// |
| 367 | +/// * `(&str, u16)` - the string should be either a string representation of an IP address |
| 368 | +/// expected by `FromStr` implementation for `IpAddr` or a host name. |
| 369 | +/// |
| 370 | +/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding |
| 371 | +/// to that IP address joined with the given port. |
| 372 | +/// |
| 373 | +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses |
| 374 | +/// for the host name, each joined with the given port. |
| 375 | +/// |
| 376 | +/// * `&str` - the string should be either a string representation of a `SocketAddr` as |
| 377 | +/// expected by its `FromStr` implementation or a string like `<host_name>:<port>` pair |
| 378 | +/// where `<port>` is a `u16` value. |
| 379 | +/// |
| 380 | +/// For the former, `to_socker_addr_all` returns a vector with a single element corresponding |
| 381 | +/// to that socker address. |
| 382 | +/// |
| 383 | +/// For the latter, it tries to resolve the host name and returns a vector of all IP addresses |
| 384 | +/// for the host name, each joined with the port. |
| 385 | +/// |
| 386 | +/// |
| 387 | +/// This trait allows constructing network objects like `TcpStream` or `UdpSocket` easily with |
| 388 | +/// values of various types for the bind/connection address. It is needed because sometimes |
| 389 | +/// one type is more appropriate than the other: for simple uses a string like `"localhost:12345"` |
| 390 | +/// is much nicer than manual construction of the corresponding `SocketAddr`, but sometimes |
| 391 | +/// `SocketAddr` value is *the* main source of the address, and converting it to some other type |
| 392 | +/// (e.g. a string) just for it to be converted back to `SocketAddr` in constructor methods |
| 393 | +/// is pointless. |
| 394 | +/// |
| 395 | +/// Some examples: |
| 396 | +/// |
| 397 | +/// ```rust,no_run |
| 398 | +/// # #![allow(unused_must_use)] |
| 399 | +/// |
| 400 | +/// use std::io::{TcpStream, TcpListener}; |
| 401 | +/// use std::io::net::udp::UdpSocket; |
| 402 | +/// use std::io::net::ip::{Ipv4Addr, SocketAddr}; |
| 403 | +/// |
| 404 | +/// fn main() { |
| 405 | +/// // The following lines are equivalent modulo possible "localhost" name resolution |
| 406 | +/// // differences |
| 407 | +/// let tcp_s = TcpStream::connect(SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 12345 }); |
| 408 | +/// let tcp_s = TcpStream::connect((Ipv4Addr(127, 0, 0, 1), 12345u16)); |
| 409 | +/// let tcp_s = TcpStream::connect(("127.0.0.1", 12345u16)); |
| 410 | +/// let tcp_s = TcpStream::connect(("localhost", 12345u16)); |
| 411 | +/// let tcp_s = TcpStream::connect("127.0.0.1:12345"); |
| 412 | +/// let tcp_s = TcpStream::connect("localhost:12345"); |
| 413 | +/// |
| 414 | +/// // TcpListener::bind(), UdpSocket::bind() and UdpSocket::send_to() behave similarly |
| 415 | +/// let tcp_l = TcpListener::bind("localhost:12345"); |
| 416 | +/// |
| 417 | +/// let mut udp_s = UdpSocket::bind(("127.0.0.1", 23451u16)).unwrap(); |
| 418 | +/// udp_s.send_to([7u8, 7u8, 7u8].as_slice(), (Ipv4Addr(127, 0, 0, 1), 23451u16)); |
| 419 | +/// } |
| 420 | +/// ``` |
| 421 | +pub trait ToSocketAddr { |
| 422 | + /// Converts this object to single socket address value. |
| 423 | + /// |
| 424 | + /// If more than one value is available, this method returns the first one. If no |
| 425 | + /// values are available, this method returns an `IoError`. |
| 426 | + /// |
| 427 | + /// By default this method delegates to `to_socket_addr_all` method, taking the first |
| 428 | + /// item from its result. |
| 429 | + fn to_socket_addr(&self) -> IoResult<SocketAddr> { |
| 430 | + self.to_socket_addr_all() |
| 431 | + .and_then(|v| v.into_iter().next().ok_or_else(|| IoError { |
| 432 | + kind: io::InvalidInput, |
| 433 | + desc: "no address available", |
| 434 | + detail: None |
| 435 | + })) |
| 436 | + } |
| 437 | + |
| 438 | + /// Converts this object to all available socket address values. |
| 439 | + /// |
| 440 | + /// Some values like host name string naturally corrrespond to multiple IP addresses. |
| 441 | + /// This method tries to return all available addresses corresponding to this object. |
| 442 | + /// |
| 443 | + /// By default this method delegates to `to_socket_addr` method, creating a singleton |
| 444 | + /// vector from its result. |
| 445 | + #[inline] |
| 446 | + fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> { |
| 447 | + self.to_socket_addr().map(|a| vec![a]) |
| 448 | + } |
| 449 | +} |
| 450 | + |
| 451 | +impl ToSocketAddr for SocketAddr { |
| 452 | + #[inline] |
| 453 | + fn to_socket_addr(&self) -> IoResult<SocketAddr> { Ok(*self) } |
| 454 | +} |
| 455 | + |
| 456 | +impl ToSocketAddr for (IpAddr, u16) { |
| 457 | + #[inline] |
| 458 | + fn to_socket_addr(&self) -> IoResult<SocketAddr> { |
| 459 | + let (ip, port) = *self; |
| 460 | + Ok(SocketAddr { ip: ip, port: port }) |
| 461 | + } |
| 462 | +} |
| 463 | + |
| 464 | +fn resolve_socket_addr(s: &str, p: u16) -> IoResult<Vec<SocketAddr>> { |
| 465 | + net::get_host_addresses(s) |
| 466 | + .map(|v| v.into_iter().map(|a| SocketAddr { ip: a, port: p }).collect()) |
| 467 | +} |
| 468 | + |
| 469 | +fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> { |
| 470 | + macro_rules! try_opt( |
| 471 | + ($e:expr, $msg:expr) => ( |
| 472 | + match $e { |
| 473 | + Some(r) => r, |
| 474 | + None => return Err(IoError { |
| 475 | + kind: io::InvalidInput, |
| 476 | + desc: $msg, |
| 477 | + detail: None |
| 478 | + }) |
| 479 | + } |
| 480 | + ) |
| 481 | + ) |
| 482 | + |
| 483 | + // split the string by ':' and convert the second part to u16 |
| 484 | + let mut parts_iter = s.rsplitn(2, ':'); |
| 485 | + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); |
| 486 | + let host = try_opt!(parts_iter.next(), "invalid socket address"); |
| 487 | + let port: u16 = try_opt!(FromStr::from_str(port_str), "invalid port value"); |
| 488 | + resolve_socket_addr(host, port) |
| 489 | +} |
| 490 | + |
| 491 | +impl<'a> ToSocketAddr for (&'a str, u16) { |
| 492 | + fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> { |
| 493 | + let (host, port) = *self; |
| 494 | + |
| 495 | + // try to parse the host as a regular IpAddr first |
| 496 | + match FromStr::from_str(host) { |
| 497 | + Some(addr) => return Ok(vec![SocketAddr { |
| 498 | + ip: addr, |
| 499 | + port: port |
| 500 | + }]), |
| 501 | + None => {} |
| 502 | + } |
| 503 | + |
| 504 | + resolve_socket_addr(host, port) |
| 505 | + } |
| 506 | +} |
| 507 | + |
| 508 | +// accepts strings like 'localhost:12345' |
| 509 | +impl<'a> ToSocketAddr for &'a str { |
| 510 | + fn to_socket_addr(&self) -> IoResult<SocketAddr> { |
| 511 | + // try to parse as a regular SocketAddr first |
| 512 | + match FromStr::from_str(*self) { |
| 513 | + Some(addr) => return Ok(addr), |
| 514 | + None => {} |
| 515 | + } |
| 516 | + |
| 517 | + parse_and_resolve_socket_addr(*self) |
| 518 | + .and_then(|v| v.into_iter().next() |
| 519 | + .ok_or_else(|| IoError { |
| 520 | + kind: io::InvalidInput, |
| 521 | + desc: "no address available", |
| 522 | + detail: None |
| 523 | + }) |
| 524 | + ) |
| 525 | + } |
| 526 | + |
| 527 | + fn to_socket_addr_all(&self) -> IoResult<Vec<SocketAddr>> { |
| 528 | + // try to parse as a regular SocketAddr first |
| 529 | + match FromStr::from_str(*self) { |
| 530 | + Some(addr) => return Ok(vec![addr]), |
| 531 | + None => {} |
| 532 | + } |
| 533 | + |
| 534 | + parse_and_resolve_socket_addr(*self) |
| 535 | + } |
| 536 | +} |
| 537 | + |
351 | 538 |
|
352 | 539 | #[cfg(test)]
|
353 | 540 | mod test {
|
@@ -457,4 +644,48 @@ mod test {
|
457 | 644 | assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(),
|
458 | 645 | "8:9:a:b:c:d:e:f".to_string());
|
459 | 646 | }
|
| 647 | + |
| 648 | + #[test] |
| 649 | + fn to_socket_addr_socketaddr() { |
| 650 | + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 12345 }; |
| 651 | + assert_eq!(Ok(a), a.to_socket_addr()); |
| 652 | + assert_eq!(Ok(vec![a]), a.to_socket_addr_all()); |
| 653 | + } |
| 654 | + |
| 655 | + #[test] |
| 656 | + fn to_socket_addr_ipaddr_u16() { |
| 657 | + let a = Ipv4Addr(77, 88, 21, 11); |
| 658 | + let p = 12345u16; |
| 659 | + let e = SocketAddr { ip: a, port: p }; |
| 660 | + assert_eq!(Ok(e), (a, p).to_socket_addr()); |
| 661 | + assert_eq!(Ok(vec![e]), (a, p).to_socket_addr_all()); |
| 662 | + } |
| 663 | + |
| 664 | + #[test] |
| 665 | + fn to_socket_addr_str_u16() { |
| 666 | + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; |
| 667 | + assert_eq!(Ok(a), ("77.88.21.11", 24352u16).to_socket_addr()); |
| 668 | + assert_eq!(Ok(vec![a]), ("77.88.21.11", 24352u16).to_socket_addr_all()); |
| 669 | + |
| 670 | + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; |
| 671 | + assert_eq!(Ok(a), ("2a02:6b8:0:1::1", 53).to_socket_addr()); |
| 672 | + assert_eq!(Ok(vec![a]), ("2a02:6b8:0:1::1", 53).to_socket_addr_all()); |
| 673 | + |
| 674 | + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; |
| 675 | + assert!(("localhost", 23924u16).to_socket_addr_all().unwrap().contains(&a)); |
| 676 | + } |
| 677 | + |
| 678 | + #[test] |
| 679 | + fn to_socket_addr_str() { |
| 680 | + let a = SocketAddr { ip: Ipv4Addr(77, 88, 21, 11), port: 24352 }; |
| 681 | + assert_eq!(Ok(a), "77.88.21.11:24352".to_socket_addr()); |
| 682 | + assert_eq!(Ok(vec![a]), "77.88.21.11:24352".to_socket_addr_all()); |
| 683 | + |
| 684 | + let a = SocketAddr { ip: Ipv6Addr(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), port: 53 }; |
| 685 | + assert_eq!(Ok(a), "[2a02:6b8:0:1::1]:53".to_socket_addr()); |
| 686 | + assert_eq!(Ok(vec![a]), "[2a02:6b8:0:1::1]:53".to_socket_addr_all()); |
| 687 | + |
| 688 | + let a = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 23924 }; |
| 689 | + assert!("localhost:23924".to_socket_addr_all().unwrap().contains(&a)); |
| 690 | + } |
460 | 691 | }
|
0 commit comments