// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use option::{Option, Some, None}; use result::{Ok, Err}; use rt::io::net::ip::SocketAddr; use rt::io::{Reader, Writer}; use rt::io::{io_error, read_error, EndOfFile}; use rt::rtio::{RtioSocket, RtioUdpSocketObject, RtioUdpSocket, IoFactory, IoFactoryObject}; use rt::local::Local; pub struct UdpSocket(~RtioUdpSocketObject); impl UdpSocket { pub fn bind(addr: SocketAddr) -> Option { let socket = unsafe { let factory: *mut IoFactoryObject = Local::unsafe_borrow(); (*factory).udp_bind(addr) }; match socket { Ok(s) => Some(UdpSocket(s)), Err(ioerr) => { io_error::cond.raise(ioerr); None } } } pub fn recvfrom(&mut self, buf: &mut [u8]) -> Option<(uint, SocketAddr)> { match (**self).recvfrom(buf) { Ok((nread, src)) => Some((nread, src)), Err(ioerr) => { // EOF is indicated by returning None if ioerr.kind != EndOfFile { read_error::cond.raise(ioerr); } None } } } pub fn sendto(&mut self, buf: &[u8], dst: SocketAddr) { match (**self).sendto(buf, dst) { Ok(_) => (), Err(ioerr) => io_error::cond.raise(ioerr), } } pub fn connect(self, other: SocketAddr) -> UdpStream { UdpStream { socket: self, connectedTo: other } } pub fn socket_name(&mut self) -> Option { match (***self).socket_name() { Ok(sn) => Some(sn), Err(ioerr) => { rtdebug!("failed to get socket name: {:?}", ioerr); io_error::cond.raise(ioerr); None } } } } pub struct UdpStream { socket: UdpSocket, connectedTo: SocketAddr } impl UdpStream { pub fn as_socket(&mut self, f: &fn(&mut UdpSocket) -> T) -> T { f(&mut self.socket) } pub fn disconnect(self) -> UdpSocket { self.socket } } impl Reader for UdpStream { fn read(&mut self, buf: &mut [u8]) -> Option { let peer = self.connectedTo; do self.as_socket |sock| { match sock.recvfrom(buf) { Some((_nread, src)) if src != peer => Some(0), Some((nread, _src)) => Some(nread), None => None, } } } fn eof(&mut self) -> bool { fail2!() } } impl Writer for UdpStream { fn write(&mut self, buf: &[u8]) { do self.as_socket |sock| { sock.sendto(buf, self.connectedTo); } } fn flush(&mut self) { fail2!() } } #[cfg(test)] mod test { use super::*; use rt::test::*; use rt::io::net::ip::{Ipv4Addr, SocketAddr}; use rt::io::*; use option::{Some, None}; use rt::comm::oneshot; use cell::Cell; #[test] #[ignore] fn bind_error() { do run_in_mt_newsched_task { let mut called = false; do io_error::cond.trap(|e| { assert!(e.kind == PermissionDenied); called = true; }).inside { let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 }; let socket = UdpSocket::bind(addr); assert!(socket.is_none()); } assert!(called); } } #[test] fn socket_smoke_test_ip4() { do run_in_mt_newsched_task { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); let (port, chan) = oneshot(); let port = Cell::new(port); let chan = Cell::new(chan); do spawntask { match UdpSocket::bind(server_ip) { Some(ref mut server) => { chan.take().send(()); let mut buf = [0]; match server.recvfrom(buf) { Some((nread, src)) => { assert_eq!(nread, 1); assert_eq!(buf[0], 99); assert_eq!(src, client_ip); } None => fail2!() } } None => fail2!() } } do spawntask { match UdpSocket::bind(client_ip) { Some(ref mut client) => { port.take().recv(); client.sendto([99], server_ip) } None => fail2!() } } } } #[test] fn socket_smoke_test_ip6() { do run_in_mt_newsched_task { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); let (port, chan) = oneshot(); let port = Cell::new(port); let chan = Cell::new(chan); do spawntask { match UdpSocket::bind(server_ip) { Some(ref mut server) => { chan.take().send(()); let mut buf = [0]; match server.recvfrom(buf) { Some((nread, src)) => { assert_eq!(nread, 1); assert_eq!(buf[0], 99); assert_eq!(src, client_ip); } None => fail2!() } } None => fail2!() } } do spawntask { match UdpSocket::bind(client_ip) { Some(ref mut client) => { port.take().recv(); client.sendto([99], server_ip) } None => fail2!() } } } } #[test] fn stream_smoke_test_ip4() { do run_in_mt_newsched_task { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); let (port, chan) = oneshot(); let port = Cell::new(port); let chan = Cell::new(chan); do spawntask { match UdpSocket::bind(server_ip) { Some(server) => { let server = ~server; let mut stream = server.connect(client_ip); chan.take().send(()); let mut buf = [0]; match stream.read(buf) { Some(nread) => { assert_eq!(nread, 1); assert_eq!(buf[0], 99); } None => fail2!() } } None => fail2!() } } do spawntask { match UdpSocket::bind(client_ip) { Some(client) => { let client = ~client; let mut stream = client.connect(server_ip); port.take().recv(); stream.write([99]); } None => fail2!() } } } } #[test] fn stream_smoke_test_ip6() { do run_in_mt_newsched_task { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); let (port, chan) = oneshot(); let port = Cell::new(port); let chan = Cell::new(chan); do spawntask { match UdpSocket::bind(server_ip) { Some(server) => { let server = ~server; let mut stream = server.connect(client_ip); chan.take().send(()); let mut buf = [0]; match stream.read(buf) { Some(nread) => { assert_eq!(nread, 1); assert_eq!(buf[0], 99); } None => fail2!() } } None => fail2!() } } do spawntask { match UdpSocket::bind(client_ip) { Some(client) => { let client = ~client; let mut stream = client.connect(server_ip); port.take().recv(); stream.write([99]); } None => fail2!() } } } } #[cfg(test)] fn socket_name(addr: SocketAddr) { do run_in_mt_newsched_task { do spawntask { let server = UdpSocket::bind(addr); assert!(server.is_some()); let mut server = server.unwrap(); // Make sure socket_name gives // us the socket we binded to. let so_name = server.socket_name(); assert!(so_name.is_some()); assert_eq!(addr, so_name.unwrap()); } } } #[test] fn socket_name_ip4() { socket_name(next_test_ip4()); } #[test] fn socket_name_ip6() { socket_name(next_test_ip6()); } }