diff --git a/mk/target.mk b/mk/target.mk index 5ddc825d335bd..7cfa44f52bd94 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -9,7 +9,7 @@ # the snapshot runtime (resp. corelib) rather than the runtime # (resp. corelib) from the working directory. USE_SNAPSHOT_RUNTIME=0 -USE_SNAPSHOT_CORELIB=0 +USE_SNAPSHOT_CORELIB=1 USE_SNAPSHOT_STDLIB=0 define TARGET_STAGE_N diff --git a/src/libcore/extfmt.rs b/src/libcore/extfmt.rs index d738b4f661510..e7263243323d7 100644 --- a/src/libcore/extfmt.rs +++ b/src/libcore/extfmt.rs @@ -264,19 +264,17 @@ mod ct { // conditions can be evaluated at compile-time. For now though it's cleaner to // implement it 0this way, I think. mod rt { - enum flag { - flag_left_justify, - flag_left_zero_pad, - flag_space_for_sign, - flag_sign_always, - flag_alternate, - } + const flag_none : u32 = 0u32; + const flag_left_justify : u32 = 0b00000000000000000000000000000001u32; + const flag_left_zero_pad : u32 = 0b00000000000000000000000000000010u32; + const flag_space_for_sign : u32 = 0b00000000000000000000000000000100u32; + const flag_sign_always : u32 = 0b00000000000000000000000000001000u32; + const flag_alternate : u32 = 0b00000000000000000000000000010000u32; + enum count { count_is(int), count_implied, } enum ty { ty_default, ty_bits, ty_hex_upper, ty_hex_lower, ty_octal, } - // FIXME (#1993): May not want to use a vector here for flags; instead - // just use a bool per flag. - type conv = {flags: [flag]/~, width: count, precision: count, ty: ty}; + type conv = {flags: u32, width: count, precision: count, ty: ty}; fn conv_int(cv: conv, i: int) -> str { let radix = 10u; @@ -307,7 +305,6 @@ mod rt { let s = if b { "true" } else { "false" }; // run the boolean conversion through the string conversion logic, // giving it the same rules for precision, etc. - ret conv_str(cv, s); } fn conv_char(cv: conv, c: char) -> str { @@ -430,9 +427,8 @@ mod rt { } ret padstr + s; } - fn have_flag(flags: [flag]/~, f: flag) -> bool { - for vec::each(flags) {|candidate| if candidate == f { ret true; } } - ret false; + fn have_flag(flags: u32, f: u32) -> bool { + flags & f != 0 } } diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index e12a62fc41db7..152f1ed5daf6b 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -31,6 +31,7 @@ export splitn; export rsplit; export rsplitn; export shift; +export unshift; export pop; export push, push_all, push_all_move; export grow; diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs index 64cded848d1ab..70003a224828d 100644 --- a/src/libstd/net_ip.rs +++ b/src/libstd/net_ip.rs @@ -4,16 +4,35 @@ Types/fns concerning Internet Protocol (IP), versions 4 & 6 import vec; import uint; +import iotask = uv::iotask::iotask; +import interact = uv::iotask::interact; +import comm::methods; + +import sockaddr_in = uv::ll::sockaddr_in; +import sockaddr_in6 = uv::ll::sockaddr_in6; +import addrinfo = uv::ll::addrinfo; +import uv_getaddrinfo_t = uv::ll::uv_getaddrinfo_t; +import uv_ip4_addr = uv::ll::ip4_addr; +import uv_ip4_name = uv::ll::ip4_name; +import uv_ip6_addr = uv::ll::ip6_addr; +import uv_ip6_name = uv::ll::ip6_name; +import uv_getaddrinfo = uv::ll::getaddrinfo; +import uv_freeaddrinfo = uv::ll::freeaddrinfo; +import create_uv_getaddrinfo_t = uv::ll::getaddrinfo_t; +import set_data_for_req = uv::ll::set_data_for_req; +import get_data_for_req = uv::ll::get_data_for_req; +import ll = uv::ll; export ip_addr, parse_addr_err; export format_addr; -export v4; +export v4, v6; +export get_addr; #[doc = "An IP address"] enum ip_addr { #[doc="An IPv4 address"] - ipv4(u8, u8, u8, u8), - ipv6(u16,u16,u16,u16,u16,u16,u16,u16) + ipv4(sockaddr_in), + ipv6(sockaddr_in6) } #[doc=" @@ -32,22 +51,88 @@ Convert a `ip_addr` to a str "] fn format_addr(ip: ip_addr) -> str { alt ip { - ipv4(a, b, c, d) { - #fmt["%u.%u.%u.%u", a as uint, b as uint, c as uint, d as uint] + ipv4(addr) { + unsafe { + let result = uv_ip4_name(&addr); + if result == "" { + fail "failed to convert inner sockaddr_in address to str" + } + result + } } - ipv6(_, _, _, _, _, _, _, _) { - fail "FIXME (#2651) impl parsing of ipv6 addr"; + ipv6(addr) { + unsafe { + let result = uv_ip6_name(&addr); + if result == "" { + fail "failed to convert inner sockaddr_in address to str" + } + result + } } } } +#[doc=" +Represents errors returned from `net::ip::get_addr()` +"] +enum ip_get_addr_err { + get_addr_unknown_error +} + +#[doc=" +Attempts name resolution on the provided `node` string + +# Arguments + +* `node` - a string representing some host address +* `iotask` - a `uv::iotask` used to interact with the underlying event loop + +# Returns + +A `result<[ip_addr]/~, ip_get_addr_err>` instance that will contain +a vector of `ip_addr` results, in the case of success, or an error +object in the case of failure +"] +fn get_addr(++node: str, iotask: iotask) + -> result::result<[ip_addr]/~, ip_get_addr_err> unsafe { + comm::listen {|output_ch| + str::unpack_slice(node) {|node_ptr, len| + log(debug, #fmt("slice len %?", len)); + let handle = create_uv_getaddrinfo_t(); + let handle_ptr = ptr::addr_of(handle); + let handle_data: get_addr_data = { + output_ch: output_ch + }; + let handle_data_ptr = ptr::addr_of(handle_data); + interact(iotask) {|loop_ptr| + let result = uv_getaddrinfo( + loop_ptr, + handle_ptr, + get_addr_cb, + node_ptr, + ptr::null(), + ptr::null()); + alt result { + 0i32 { + set_data_for_req(handle_ptr, handle_data_ptr); + } + _ { + output_ch.send(result::err(get_addr_unknown_error)); + } + } + }; + output_ch.recv() + } + } +} + mod v4 { #[doc = " Convert a str to `ip_addr` # Failure -j Fails if the string is not a valid IPv4 address + Fails if the string is not a valid IPv4 address # Arguments @@ -59,13 +144,22 @@ j Fails if the string is not a valid IPv4 address "] fn parse_addr(ip: str) -> ip_addr { alt try_parse_addr(ip) { - result::ok(addr) { addr } + result::ok(addr) { copy(addr) } result::err(err_data) { fail err_data.err_msg } } } - fn try_parse_addr(ip: str) -> result::result { + // the simple, old style numberic representation of + // ipv4 + type ipv4_rep = { a: u8, b: u8, c: u8, d:u8 }; + impl x for ipv4_rep { + // this is pretty dastardly, i know + unsafe fn as_u32() -> u32 { + *((ptr::addr_of(self)) as *u32) + } + } + fn parse_to_ipv4_rep(ip: str) -> result::result { let parts = vec::map(str::split_char(ip, '.'), {|s| alt uint::from_str(s) { some(n) if n <= 255u { n } @@ -73,31 +167,230 @@ j Fails if the string is not a valid IPv4 address } }); if vec::len(parts) != 4u { - result::err({err_msg: #fmt("'%s' doesn't have 4 parts", - ip)}) - } + result::err(#fmt("'%s' doesn't have 4 parts", ip)) + } else if vec::contains(parts, 256u) { - result::err({err_msg: #fmt("invalid octal in provided addr '%s'", - ip)}) + result::err(#fmt("invalid octal in addr '%s'", ip)) + } + else { + result::ok({a: parts[0] as u8, b: parts[1] as u8, + c: parts[2] as u8, d: parts[3] as u8}) + } + } + fn try_parse_addr(ip: str) -> result::result { + unsafe { + let INADDR_NONE = ll::get_INADDR_NONE(); + let ip_rep_result = parse_to_ipv4_rep(ip); + if result::is_err(ip_rep_result) { + let err_str = result::get_err(ip_rep_result); + ret result::err({err_msg: err_str}) + } + // ipv4_rep.as_u32 is unsafe :/ + let input_is_inaddr_none = + result::get(ip_rep_result).as_u32() == INADDR_NONE; + + let new_addr = uv_ip4_addr(ip, 22); + let reformatted_name = uv_ip4_name(&new_addr); + log(debug, #fmt("try_parse_addr: input ip: %s reparsed ip: %s", + ip, reformatted_name)); + let ref_ip_rep_result = parse_to_ipv4_rep(reformatted_name); + if result::is_err(ref_ip_rep_result) { + let err_str = result::get_err(ref_ip_rep_result); + ret result::err({err_msg: err_str}) + } + if result::get(ref_ip_rep_result).as_u32() == INADDR_NONE && + !input_is_inaddr_none { + ret result::err( + {err_msg: "uv_ip4_name produced invalid result."}) + } + else { + result::ok(ipv4(copy(new_addr))) + } + } + } +} +mod v6 { + #[doc = " + Convert a str to `ip_addr` + + # Failure + + Fails if the string is not a valid IPv6 address + + # Arguments + + * ip - an ipv6 string. See RFC2460 for spec. + + # Returns + + * an `ip_addr` of the `ipv6` variant + "] + fn parse_addr(ip: str) -> ip_addr { + alt try_parse_addr(ip) { + result::ok(addr) { copy(addr) } + result::err(err_data) { + fail err_data.err_msg + } + } + } + fn try_parse_addr(ip: str) -> result::result { + unsafe { + // need to figure out how to establish a parse failure.. + let new_addr = uv_ip6_addr(ip, 22); + let reparsed_name = uv_ip6_name(&new_addr); + log(debug, #fmt("v6::try_parse_addr ip: '%s' reparsed '%s'", + ip, reparsed_name)); + // '::' appears to be uv_ip6_name() returns for bogus + // parses.. + if ip != "::" && reparsed_name == "::" { + result::err({err_msg:#fmt("failed to parse '%s'", + ip)}) + } + else { + result::ok(ipv6(new_addr)) + } + } + } +} + +type get_addr_data = { + output_ch: comm::chan> +}; + +crust fn get_addr_cb(handle: *uv_getaddrinfo_t, status: libc::c_int, + res: *addrinfo) unsafe { + log(debug, "in get_addr_cb"); + let handle_data = get_data_for_req(handle) as + *get_addr_data; + if status == 0i32 { + if res != (ptr::null::()) { + let mut out_vec = []/~; + log(debug, #fmt("initial addrinfo: %?", res)); + let mut curr_addr = res; + loop { + let new_ip_addr = if ll::is_ipv4_addrinfo(curr_addr) { + ipv4(copy(( + *ll::addrinfo_as_sockaddr_in(curr_addr)))) + } + else if ll::is_ipv6_addrinfo(curr_addr) { + ipv6(copy(( + *ll::addrinfo_as_sockaddr_in6(curr_addr)))) + } + else { + log(debug, "curr_addr is not of family AF_INET or "+ + "AF_INET6. Error."); + (*handle_data).output_ch.send( + result::err(get_addr_unknown_error)); + break; + }; + out_vec += [new_ip_addr]/~; + + let next_addr = ll::get_next_addrinfo(curr_addr); + if next_addr == ptr::null::() as *addrinfo { + log(debug, "null next_addr encountered. no mas"); + break; + } + else { + curr_addr = next_addr; + log(debug, #fmt("next_addr addrinfo: %?", curr_addr)); + } + } + log(debug, #fmt("successful process addrinfo result, len: %?", + vec::len(out_vec))); + (*handle_data).output_ch.send(result::ok(out_vec)); } else { - result::ok(ipv4(parts[0] as u8, parts[1] as u8, - parts[2] as u8, parts[3] as u8)) + log(debug, "addrinfo pointer is NULL"); + (*handle_data).output_ch.send( + result::err(get_addr_unknown_error)); } } + else { + log(debug, "status != 0 error in get_addr_cb"); + (*handle_data).output_ch.send( + result::err(get_addr_unknown_error)); + } + if res != (ptr::null::()) { + uv_freeaddrinfo(res); + } + log(debug, "leaving get_addr_cb"); } #[cfg(test)] mod test { #[test] - fn test_format_ip() { - assert (format_addr(ipv4(127u8, 0u8, 0u8, 1u8)) - == "127.0.0.1") + fn test_ip_ipv4_parse_and_format_ip() { + let localhost_str = "127.0.0.1"; + assert (format_addr(v4::parse_addr(localhost_str)) + == localhost_str) + } + #[test] + fn test_ip_ipv6_parse_and_format_ip() { + let localhost_str = "::1"; + let format_result = format_addr(v6::parse_addr(localhost_str)); + log(debug, #fmt("results: expected: '%s' actual: '%s'", + localhost_str, format_result)); + assert format_result == localhost_str; + } + #[test] + fn test_ip_ipv4_bad_parse() { + alt v4::try_parse_addr("b4df00d") { + result::err(err_info) { + log(debug, #fmt("got error as expected %?", err_info)); + assert true; + } + result::ok(addr) { + fail #fmt("Expected failure, but got addr %?", addr); + } + } + } + #[test] + #[ignore(target_os="win32")] + fn test_ip_ipv6_bad_parse() { + alt v6::try_parse_addr("::,~2234k;") { + result::err(err_info) { + log(debug, #fmt("got error as expected %?", err_info)); + assert true; + } + result::ok(addr) { + fail #fmt("Expected failure, but got addr %?", addr); + } + } + } + #[test] + fn test_ip_get_addr() { + let localhost_name = "localhost"; + let iotask = uv::global_loop::get(); + let ga_result = get_addr(localhost_name, iotask); + if result::is_err(ga_result) { + fail "got err result from net::ip::get_addr();" + } + // note really sure how to realiably test/assert + // this.. mostly just wanting to see it work, atm. + let results = result::unwrap(ga_result); + log(debug, #fmt("test_get_addr: Number of results for %s: %?", + localhost_name, vec::len(results))); + for vec::each(results) {|r| + let ipv_prefix = alt r { + ipv4(_) { + "IPv4" + } + ipv6(_) { + "IPv6" + } + }; + log(debug, #fmt("test_get_addr: result %s: '%s'", + ipv_prefix, format_addr(r))); + } + // at least one result.. this is going to vary from system + // to system, based on stuff like the contents of /etc/hosts + assert vec::len(results) > 0; } - #[test] - fn test_parse_ip() { - assert (v4::parse_addr("127.0.0.1") == - ipv4(127u8, 0u8, 0u8, 1u8)); + fn test_ip_get_addr_bad_input() { + let localhost_name = "sjkl234m,./sdf"; + let iotask = uv::global_loop::get(); + let ga_result = get_addr(localhost_name, iotask); + assert result::is_err(ga_result); } } \ No newline at end of file diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs index 8c637f0089ecc..76d5ccd3fcc46 100644 --- a/src/libstd/net_tcp.rs +++ b/src/libstd/net_tcp.rs @@ -6,21 +6,26 @@ import ip = net_ip; import uv::iotask; import uv::iotask::iotask; import comm::methods; -import future_spawn = future::spawn; import future::future; -import result::{result,err,ok,extensions}; - -// data -export tcp_socket, tcp_conn_port, tcp_err_data; +import future_spawn = future::spawn; +// FIXME #1935 +// should be able to, but can't atm, replace w/ result::{result, extensions}; +import result::*; +import libc::size_t; +import str::extensions; + +// tcp interfaces +export tcp_socket; +// buffered socket +export tcp_socket_buf, socket_buf; +// errors +export tcp_err_data, tcp_connect_err_data; // operations on a tcp_socket export write, write_future, read_start, read_stop; // tcp server stuff -export listen_for_conn, accept; -export new_listener, conn_recv, conn_recv_spawn, conn_peek; +export listen, accept; // tcp client stuff export connect; -// helper methods -export conn_port_methods, sock_methods; #[nolink] native mod rustrt { @@ -41,44 +46,20 @@ class tcp_socket { new(socket_data: @tcp_socket_data) { self.socket_data = socket_data; } drop { unsafe { - let closed_po = comm::port::<()>(); - let closed_ch = comm::chan(closed_po); - let close_data = { - closed_ch: closed_ch - }; - let close_data_ptr = ptr::addr_of(close_data); - let stream_handle_ptr = (*(self.socket_data)).stream_handle_ptr; - iotask::interact((*(self.socket_data)).iotask) {|loop_ptr| - log(debug, #fmt("interact dtor for tcp_socket stream %? loop %?", - stream_handle_ptr, loop_ptr)); - uv::ll::set_data_for_uv_handle(stream_handle_ptr, - close_data_ptr); - uv::ll::close(stream_handle_ptr, tcp_socket_dtor_close_cb); - }; - comm::recv(closed_po); - log(debug, #fmt("about to free socket_data at %?", self.socket_data)); - rustrt::rust_uv_current_kernel_free(stream_handle_ptr - as *libc::c_void); - log(debug, "exiting dtor for tcp_socket"); + tear_down_socket_data(self.socket_data) } } } -class tcp_conn_port { - let conn_data: @tcp_conn_port_data; - new(conn_data: @tcp_conn_port_data) { self.conn_data = conn_data; } - drop unsafe { - let conn_data_ptr = ptr::addr_of(*(self.conn_data)); - let server_stream_ptr = ptr::addr_of((*conn_data_ptr).server_stream); - let stream_closed_po = (*(self.conn_data)).stream_closed_po; - let iotask = (*conn_data_ptr).iotask; - iotask::interact(iotask) {|loop_ptr| - log(debug, #fmt("dtor for tcp_conn_port loop: %?", - loop_ptr)); - uv::ll::close(server_stream_ptr, tcp_nl_close_cb); - } - comm::recv(stream_closed_po); - } +#[doc=" +A buffered wrapper for `net::tcp::tcp_socket` + +It is created with a call to `net::tcp::socket_buf()` and has impls that +satisfy both the `io::reader` and `io::writer` ifaces. +"] +class tcp_socket_buf { + let data: @tcp_buffered_socket_data; + new(data: @tcp_buffered_socket_data) { self.data = data; } } #[doc=" @@ -88,25 +69,69 @@ type tcp_err_data = { err_name: str, err_msg: str }; +#[doc=" +Details returned as part of a `result::err` result from `tcp::listen` +"] +enum tcp_listen_err_data { + #[doc=" + Some unplanned-for error. The first and second fields correspond + to libuv's `err_name` and `err_msg` fields, respectively. + "] + generic_listen_err(str, str), + #[doc=" + Failed to bind to the requested IP/Port, because it is already in use. + + # Possible Causes + + * Attempting to bind to a port already bound to another listener + "] + address_in_use, + #[doc=" + Request to bind to an IP/Port was denied by the system. + + # Possible Causes + + * Attemping to binding to an IP/Port as a non-Administrator + on Windows Vista+ + * Attempting to bind, as a non-priv'd + user, to 'privileged' ports (< 1024) on *nix + "] + access_denied +} +#[doc=" +Details returned as part of a `result::err` result from `tcp::connect` +"] +enum tcp_connect_err_data { + #[doc=" + Some unplanned-for error. The first and second fields correspond + to libuv's `err_name` and `err_msg` fields, respectively. + "] + generic_connect_err(str, str), + #[doc=" + Invalid IP or invalid port + "] + connection_refused +} #[doc=" Initiate a client connection over TCP/IP # Arguments -* `ip` - The IP address (versions 4 or 6) of the remote host +* `input_ip` - The IP address (versions 4 or 6) of the remote host * `port` - the unsigned integer of the desired remote host port * `iotask` - a `uv::iotask` that the tcp request will run on # Returns -A `result` that, if the operation succeeds, contains a `tcp_socket` that -can be used to send and receive data to/from the remote host. In the event -of failure, a `tcp_err_data` will be returned +A `result` that, if the operation succeeds, contains a `net::net::tcp_socket` +that can be used to send and receive data to/from the remote host. In the +event of failure, a `net::tcp::tcp_connect_err_data` instance will be +returned "] -fn connect(input_ip: ip::ip_addr, port: uint, +fn connect(-input_ip: ip::ip_addr, port: uint, iotask: iotask) - -> result::result unsafe { + -> result::result unsafe { let result_po = comm::port::(); let closed_signal_po = comm::port::<()>(); let conn_data = { @@ -141,16 +166,35 @@ fn connect(input_ip: ip::ip_addr, port: uint, alt input_ip { ipv4 { log(debug, "dealing w/ ipv4 connection.."); - let tcp_addr = ipv4_ip_addr_to_sockaddr_in(input_ip, - port); - let tcp_addr_ptr = ptr::addr_of(tcp_addr); let connect_req_ptr = ptr::addr_of((*socket_data_ptr).connect_req); - alt uv::ll::tcp_connect( - connect_req_ptr, - stream_handle_ptr, - tcp_addr_ptr, - tcp_connect_on_connect_cb) { + let addr_str = ip::format_addr(input_ip); + let connect_result = alt input_ip { + ip::ipv4(addr) { + // have to "recreate" the sockaddr_in/6 + // since the ip_addr discards the port + // info.. should probably add an additional + // rust type that actually is closer to + // what the libuv API expects (ip str + port num) + log(debug, #fmt("addr: %?", addr)); + let in_addr = uv::ll::ip4_addr(addr_str, port as int); + uv::ll::tcp_connect( + connect_req_ptr, + stream_handle_ptr, + ptr::addr_of(in_addr), + tcp_connect_on_connect_cb) + } + ip::ipv6(addr) { + log(debug, #fmt("addr: %?", addr)); + let in_addr = uv::ll::ip6_addr(addr_str, port as int); + uv::ll::tcp_connect6( + connect_req_ptr, + stream_handle_ptr, + ptr::addr_of(in_addr), + tcp_connect_on_connect_cb) + } + }; + alt connect_result { 0i32 { log(debug, "tcp_connect successful"); // reusable data that we'll have for the @@ -196,7 +240,14 @@ fn connect(input_ip: ip::ip_addr, port: uint, conn_failure(err_data) { comm::recv(closed_signal_po); log(debug, "tcp::connect - received failure on result_po"); - result::err(err_data.to_tcp_err()) + // still have to free the malloc'd stream handle.. + rustrt::rust_uv_current_kernel_free(stream_handle_ptr + as *libc::c_void); + let tcp_conn_err = alt err_data.err_name { + "ECONNREFUSED" { connection_refused } + _ { generic_connect_err(err_data.err_name, err_data.err_msg) } + }; + result::err(tcp_conn_err) } } } @@ -226,9 +277,12 @@ Write binary data to tcp stream; Returns a `future::future` value immediately # Safety -This function can produce unsafe results if the call to `write_future` is -made, the `future::future` value returned is never resolved via -`future::get`, and then the `tcp_socket` passed in to `write_future` leaves +This function can produce unsafe results if: + +1. the call to `write_future` is made +2. the `future::future` value returned is never resolved via +`future::get` +3. and then the `tcp_socket` passed in to `write_future` leaves scope and is destructed before the task that runs the libuv write operation completes. @@ -252,7 +306,8 @@ fn write_future(sock: tcp_socket, raw_write_data: [u8]/~) -> future> unsafe { let socket_data_ptr = ptr::addr_of(*(sock.socket_data)); future_spawn {|| - write_common_impl(socket_data_ptr, raw_write_data) + let data_copy = copy(raw_write_data); + write_common_impl(socket_data_ptr, data_copy) } } @@ -283,9 +338,11 @@ Stop reading from an open TCP connection; used with `read_start` * `sock` - a `net::tcp::tcp_socket` that you wish to stop reading on "] -fn read_stop(sock: tcp_socket) -> +fn read_stop(sock: tcp_socket, + -read_port: comm::port>) -> result::result<(), tcp_err_data> unsafe { - let socket_data = ptr::addr_of(*(sock.socket_data)); + log(debug, #fmt("taking the read_port out of commission %?", read_port)); + let socket_data = ptr::addr_of(*sock.socket_data); read_stop_common_impl(socket_data) } @@ -345,182 +402,6 @@ fn read_future(sock: tcp_socket, timeout_msecs: uint) } } -#[doc=" -Bind to a given IP/port and listen for new connections - -# Arguments - -* `host_ip` - a `net::ip::ip_addr` representing a unique IP -(versions 4 or 6) -* `port` - a uint representing the port to listen on -* `backlog` - a uint representing the number of incoming connections -to cache in memory -* `hl_loop` - a `uv::hl::high_level_loop` that the tcp request will run on - -# Returns - -A `result` instance containing either a `tcp_conn_port` which can used -to listen for, and accept, new connections, or a `tcp_err_data` if -failure to create the tcp listener occurs -"] -fn new_listener(host_ip: ip::ip_addr, port: uint, backlog: uint, - iotask: iotask) - -> result::result unsafe { - let stream_closed_po = comm::port::<()>(); - let stream_closed_ch = comm::chan(stream_closed_po); - let new_conn_po = comm::port::>(); - let new_conn_ch = comm::chan(new_conn_po); - // FIXME (#2656): This shared box should not be captured in the i/o - // task Make it a unique pointer. - let server_data: @tcp_conn_port_data = @{ - server_stream: uv::ll::tcp_t(), - stream_closed_po: stream_closed_po, - stream_closed_ch: stream_closed_ch, - iotask: iotask, - new_conn_po: new_conn_po, - new_conn_ch: new_conn_ch - }; - let server_data_ptr = ptr::addr_of(*server_data); - let server_stream_ptr = ptr::addr_of((*server_data_ptr) - .server_stream); - - let setup_po = comm::port::>(); - let setup_ch = comm::chan(setup_po); - iotask::interact(iotask) {|loop_ptr| - let tcp_addr = ipv4_ip_addr_to_sockaddr_in(host_ip, - port); - alt uv::ll::tcp_init(loop_ptr, server_stream_ptr) { - 0i32 { - alt uv::ll::tcp_bind(server_stream_ptr, - ptr::addr_of(tcp_addr)) { - 0i32 { - alt uv::ll::listen(server_stream_ptr, - backlog as libc::c_int, - tcp_nl_on_connection_cb) { - 0i32 { - uv::ll::set_data_for_uv_handle( - server_stream_ptr, - server_data_ptr); - comm::send(setup_ch, none); - } - _ { - log(debug, "failure to uv_listen()"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - comm::send(setup_ch, some(err_data)); - } - } - } - _ { - log(debug, "failure to uv_tcp_bind"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - comm::send(setup_ch, some(err_data)); - } - } - } - _ { - log(debug, "failure to uv_tcp_init"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - comm::send(setup_ch, some(err_data)); - } - } - }; - alt comm::recv(setup_po) { - some(err_data) { - // we failed to bind/list w/ libuv - result::err(err_data.to_tcp_err()) - } - none { - result::ok(tcp_conn_port(server_data)) - } - } -} - -#[doc=" -Block on a `net::tcp::tcp_conn_port` until a new connection arrives - -This function behaves similarly to `comm::recv()` - -# Arguments - -* server_port -- a `net::tcp::tcp_conn_port` that you wish to listen -on for an incoming connection - -# Returns - -A `result` object containing a `net::tcp::tcp_socket`, ready for immediate -use, as the `ok` varient, or a `net::tcp::tcp_err_data` for the `err` -variant -"] -fn conn_recv(server_port: tcp_conn_port) - -> result::result { - let new_conn_po = (*(server_port.conn_data)).new_conn_po; - let iotask = (*(server_port.conn_data)).iotask; - let new_conn_result = comm::recv(new_conn_po); - alt new_conn_result { - ok(client_stream_ptr) { - conn_port_new_tcp_socket(client_stream_ptr, iotask) - } - err(err_data) { - result::err(err_data) - } - } -} - -#[doc=" -Identical to `net::tcp::conn_recv`, but ran on a new task - -The recv'd tcp_socket is created with a new task on the current scheduler, -and given as a parameter to the provided callback - -# Arguments - -* `server_port` -- a `net::tcp::tcp_conn_port` that you wish to listen -on for an incoming connection -* `cb` -- a callback that will be ran, in a new task on the current scheduler, -once a new connection is recv'd. Its parameter: - * A `result` object containing a `net::tcp::tcp_socket`, ready for immediate - use, as the `ok` varient, or a `net::tcp::tcp_err_data` for the `err` - variant -"] -fn conn_recv_spawn(server_port: tcp_conn_port, - +cb: fn~(result::result)) { - let new_conn_po = (*(server_port.conn_data)).new_conn_po; - let iotask = (*(server_port.conn_data)).iotask; - let new_conn_result = comm::recv(new_conn_po); - task::spawn {|| - let sock_create_result = alt new_conn_result { - ok(client_stream_ptr) { - conn_port_new_tcp_socket(client_stream_ptr, iotask) - } - err(err_data) { - result::err(err_data) - } - }; - cb(sock_create_result); - }; -} - -#[doc=" -Check if a `net::tcp::tcp_conn_port` has one-or-more pending, new connections - -This function behaves similarly to `comm::peek()` - -# Arguments - -* `server_port` -- a `net::tcp::tcp_conn_port` representing a server -connection - -# Returns - -`true` if there are one-or-more pending connections, `false` if there are -none. -"] -fn conn_peek(server_port: tcp_conn_port) -> bool { - let new_conn_po = (*(server_port.conn_data)).new_conn_po; - comm::peek(new_conn_po) -} - #[doc=" Bind an incoming client connection to a `net::tcp::tcp_socket` @@ -546,12 +427,20 @@ Here, the `new_conn` is used in conjunction with `accept` from within a task spawned by the `new_connect_cb` passed into `listen` ~~~~~~~~~~~ -net::tcp::listen(remote_ip, remote_port, backlog) {|new_conn, kill_ch| +net::tcp::listen(remote_ip, remote_port, backlog) + // this callback is ran once after the connection is successfully + // set up + {|kill_ch| + // pass the kill_ch to your main loop or wherever you want + // to be able to externally kill the server from + } + // this callback is ran when a new connection arrives + {|new_conn, kill_ch| let cont_po = comm::port::>(); let cont_ch = comm::chan(cont_po); task::spawn {|| let accept_result = net::tcp::accept(new_conn); - if accept_result.is_failure() { + if accept_result.is_err() { comm::send(cont_ch, result::get_err(accept_result)); // fail? } @@ -576,13 +465,11 @@ net::tcp::listen(remote_ip, remote_port, backlog) {|new_conn, kill_ch| # Returns -* Success - * On success, this function will return a `net::tcp::tcp_socket` as the - `ok` variant of a `result`. The `net::tcp::tcp_socket` is anchored within - the task that `accept` was called within for its lifetime. -* Failure - * On failure, this function will return a `net::tcp::tcp_err_data` record - as the `err` variant of a `result`. +On success, this function will return a `net::tcp::tcp_socket` as the +`ok` variant of a `result`. The `net::tcp::tcp_socket` is anchored within +the task that `accept` was called within for its lifetime. On failure, +this function will return a `net::tcp::tcp_err_data` record +as the `err` variant of a `result`. "] fn accept(new_conn: tcp_new_connection) -> result::result unsafe { @@ -683,15 +570,31 @@ callback's arguments are: # returns a `result` instance containing empty data of type `()` on a -successful/normal shutdown, and a `tcp_err_data` record in the event +successful/normal shutdown, and a `tcp_listen_err_data` enum in the event of listen exiting because of an error "] -fn listen_for_conn(host_ip: ip::ip_addr, port: uint, backlog: uint, +fn listen(-host_ip: ip::ip_addr, port: uint, backlog: uint, iotask: iotask, on_establish_cb: fn~(comm::chan>), +new_connect_cb: fn~(tcp_new_connection, comm::chan>)) - -> result::result<(), tcp_err_data> unsafe { + -> result::result<(), tcp_listen_err_data> unsafe { + listen_common(host_ip, port, backlog, iotask, on_establish_cb) + // on_connect_cb + {|handle| + let server_data_ptr = uv::ll::get_data_for_uv_handle(handle) + as *tcp_listen_fc_data; + let new_conn = new_tcp_conn(handle); + let kill_ch = (*server_data_ptr).kill_ch; + new_connect_cb(new_conn, kill_ch); + } +} + +fn listen_common(-host_ip: ip::ip_addr, port: uint, backlog: uint, + iotask: iotask, + on_establish_cb: fn~(comm::chan>), + -on_connect_cb: fn~(*uv::ll::uv_tcp_t)) + -> result::result<(), tcp_listen_err_data> unsafe { let stream_closed_po = comm::port::<()>(); let kill_po = comm::port::>(); let kill_ch = comm::chan(kill_po); @@ -701,56 +604,97 @@ fn listen_for_conn(host_ip: ip::ip_addr, port: uint, backlog: uint, server_stream_ptr: server_stream_ptr, stream_closed_ch: comm::chan(stream_closed_po), kill_ch: kill_ch, - new_connect_cb: new_connect_cb, + on_connect_cb: on_connect_cb, iotask: iotask, mut active: true }; let server_data_ptr = ptr::addr_of(server_data); - let setup_po = comm::port::>(); - let setup_ch = comm::chan(setup_po); - iotask::interact(iotask) {|loop_ptr| - let tcp_addr = ipv4_ip_addr_to_sockaddr_in(host_ip, - port); - alt uv::ll::tcp_init(loop_ptr, server_stream_ptr) { - 0i32 { - alt uv::ll::tcp_bind(server_stream_ptr, - ptr::addr_of(tcp_addr)) { + let setup_result = comm::listen {|setup_ch| + // this is to address a compiler warning about + // an implicit copy.. it seems that double nested + // will defeat a move sigil, as is done to the host_ip + // arg above.. this same pattern works w/o complaint in + // tcp::connect (because the iotask::interact cb isn't + // nested within a comm::listen block) + let loc_ip = copy(host_ip); + iotask::interact(iotask) {|loop_ptr| + alt uv::ll::tcp_init(loop_ptr, server_stream_ptr) { 0i32 { - alt uv::ll::listen(server_stream_ptr, - backlog as libc::c_int, - tcp_lfc_on_connection_cb) { + uv::ll::set_data_for_uv_handle( + server_stream_ptr, + server_data_ptr); + let addr_str = ip::format_addr(loc_ip); + let bind_result = alt loc_ip { + ip::ipv4(addr) { + log(debug, #fmt("addr: %?", addr)); + let in_addr = uv::ll::ip4_addr(addr_str, port as int); + uv::ll::tcp_bind(server_stream_ptr, + ptr::addr_of(in_addr)) + } + ip::ipv6(addr) { + log(debug, #fmt("addr: %?", addr)); + let in_addr = uv::ll::ip6_addr(addr_str, port as int); + uv::ll::tcp_bind6(server_stream_ptr, + ptr::addr_of(in_addr)) + } + }; + alt bind_result { 0i32 { - uv::ll::set_data_for_uv_handle( - server_stream_ptr, - server_data_ptr); - comm::send(setup_ch, none); + alt uv::ll::listen(server_stream_ptr, + backlog as libc::c_int, + tcp_lfc_on_connection_cb) { + 0i32 { + comm::send(setup_ch, none); + } + _ { + log(debug, "failure to uv_listen()"); + let err_data = uv::ll::get_last_err_data(loop_ptr); + comm::send(setup_ch, some(err_data)); + } + } } _ { - log(debug, "failure to uv_listen()"); + log(debug, "failure to uv_tcp_bind"); let err_data = uv::ll::get_last_err_data(loop_ptr); comm::send(setup_ch, some(err_data)); } } } _ { - log(debug, "failure to uv_tcp_bind"); + log(debug, "failure to uv_tcp_init"); let err_data = uv::ll::get_last_err_data(loop_ptr); comm::send(setup_ch, some(err_data)); } } + }; + setup_ch.recv() + }; + alt setup_result { + some(err_data) { + iotask::interact(iotask) {|loop_ptr| + log(debug, #fmt("tcp::listen post-kill recv hl interact %?", + loop_ptr)); + (*server_data_ptr).active = false; + uv::ll::close(server_stream_ptr, tcp_lfc_close_cb); + }; + stream_closed_po.recv(); + alt err_data.err_name { + "EACCES" { + log(debug, "Got EACCES error"); + result::err(access_denied) + } + "EADDRINUSE" { + log(debug, "Got EADDRINUSE error"); + result::err(address_in_use) } _ { - log(debug, "failure to uv_tcp_init"); - let err_data = uv::ll::get_last_err_data(loop_ptr); - comm::send(setup_ch, some(err_data)); + log(debug, #fmt("Got '%s' '%s' libuv error", + err_data.err_name, err_data.err_msg)); + result::err( + generic_listen_err(err_data.err_name, err_data.err_msg)) } } - }; - alt comm::recv(setup_po) { - some(err_data) { - // we failed to bind/list w/ libuv - result::err(err_data.to_tcp_err()) } none { on_establish_cb(kill_ch); @@ -761,11 +705,12 @@ fn listen_for_conn(host_ip: ip::ip_addr, port: uint, backlog: uint, (*server_data_ptr).active = false; uv::ll::close(server_stream_ptr, tcp_lfc_close_cb); }; - comm::recv(stream_closed_po); + stream_closed_po.recv(); alt kill_result { // some failure post bind/listen some(err_data) { - result::err(err_data) + result::err(generic_listen_err(err_data.err_name, + err_data.err_msg)) } // clean exit none { @@ -777,33 +722,43 @@ fn listen_for_conn(host_ip: ip::ip_addr, port: uint, backlog: uint, } #[doc=" -Convenience methods extending `net::tcp::tcp_conn_port` +Convert a `net::tcp::tcp_socket` to a `net::tcp::tcp_socket_buf`. + +This function takes ownership of a `net::tcp::tcp_socket`, returning it +stored within a buffered wrapper, which can be converted to a `io::reader` +or `io::writer` + +# Arguments + +* `sock` -- a `net::tcp::tcp_socket` that you want to buffer + +# Returns + +A buffered wrapper that you can cast as an `io::reader` or `io::writer` "] -impl conn_port_methods for tcp_conn_port { - fn recv() -> result::result { conn_recv(self) } - fn recv_spawn(+cb: fn~(result::result)) - { conn_recv_spawn(self, cb); } - fn peek() -> bool { conn_peek(self) } +fn socket_buf(-sock: tcp_socket) -> tcp_socket_buf { + tcp_socket_buf(@{ sock: sock, mut buf: []/~ }) } #[doc=" Convenience methods extending `net::tcp::tcp_socket` "] -impl sock_methods for tcp_socket { +impl tcp_socket for tcp_socket { fn read_start() -> result::result>, tcp_err_data> { read_start(self) } - fn read_stop() -> + fn read_stop(-read_port: + comm::port>) -> result::result<(), tcp_err_data> { - read_stop(self) + read_stop(self, read_port) } fn read(timeout_msecs: uint) -> result::result<[u8]/~, tcp_err_data> { read(self, timeout_msecs) } fn read_future(timeout_msecs: uint) -> - future> { + future::future> { read_future(self, timeout_msecs) } fn write(raw_write_data: [u8]/~) @@ -811,12 +766,110 @@ impl sock_methods for tcp_socket { write(self, raw_write_data) } fn write_future(raw_write_data: [u8]/~) - -> future> { + -> future::future> { write_future(self, raw_write_data) } } + +#[doc=" +Implementation of `io::reader` iface for a buffered `net::tcp::tcp_socket` +"] +impl tcp_socket_buf of io::reader for @tcp_socket_buf { + fn read_bytes(amt: uint) -> [u8]/~ { + let has_amt_available = + vec::len((*(self.data)).buf) >= amt; + if has_amt_available { + // no arbitrary-length shift in vec::? + let mut ret_buf = []/~; + while vec::len(ret_buf) < amt { + ret_buf += [vec::shift((*(self.data)).buf)]/~; + } + ret_buf + } + else { + let read_result = read((*(self.data)).sock, 0u); + if read_result.is_err() { + let err_data = read_result.get_err(); + log(debug, #fmt("ERROR sock_buf as io::reader.read err %? %?", + err_data.err_name, err_data.err_msg)); + []/~ + } + else { + let new_chunk = result::unwrap(read_result); + (*(self.data)).buf += new_chunk; + self.read_bytes(amt) + } + } + } + fn read_byte() -> int { + self.read_bytes(1u)[0] as int + } + fn unread_byte(amt: int) { + vec::unshift((*(self.data)).buf, amt as u8); + } + fn eof() -> bool { + false // noop + } + fn seek(dist: int, seek: io::seek_style) { + log(debug, #fmt("tcp_socket_buf seek stub %? %?", dist, seek)); + // noop + } + fn tell() -> uint { + 0u // noop + } +} + +#[doc=" +Implementation of `io::reader` iface for a buffered `net::tcp::tcp_socket` +"] +impl tcp_socket_buf of io::writer for @tcp_socket_buf { + fn write(data: [const u8]/&) unsafe { + let socket_data_ptr = + ptr::addr_of(*((*(self.data)).sock).socket_data); + let w_result = write_common_impl(socket_data_ptr, + vec::slice(data, 0, vec::len(data))); + if w_result.is_err() { + let err_data = w_result.get_err(); + log(debug, #fmt("ERROR sock_buf as io::writer.writer err: %? %?", + err_data.err_name, err_data.err_msg)); + } + } + fn seek(dist: int, seek: io::seek_style) { + log(debug, #fmt("tcp_socket_buf seek stub %? %?", dist, seek)); + // noop + } + fn tell() -> uint { + 0u + } + fn flush() -> int { + 0 + } +} + // INTERNAL API +fn tear_down_socket_data(socket_data: @tcp_socket_data) unsafe { + let closed_po = comm::port::<()>(); + let closed_ch = comm::chan(closed_po); + let close_data = { + closed_ch: closed_ch + }; + let close_data_ptr = ptr::addr_of(close_data); + let stream_handle_ptr = (*socket_data).stream_handle_ptr; + iotask::interact((*socket_data).iotask) {|loop_ptr| + log(debug, #fmt("interact dtor for tcp_socket stream %? loop %?", + stream_handle_ptr, loop_ptr)); + uv::ll::set_data_for_uv_handle(stream_handle_ptr, + close_data_ptr); + uv::ll::close(stream_handle_ptr, tcp_socket_dtor_close_cb); + }; + comm::recv(closed_po); + log(debug, #fmt("about to free socket_data at %?", socket_data)); + rustrt::rust_uv_current_kernel_free(stream_handle_ptr + as *libc::c_void); + log(debug, "exiting dtor for tcp_socket"); +} + // shared implementation for tcp::read fn read_common_impl(socket_data: *tcp_socket_data, timeout_msecs: uint) -> result::result<[u8]/~,tcp_err_data> unsafe { @@ -919,6 +972,8 @@ fn read_start_common_impl(socket_data: *tcp_socket_data) } } +// helper to convert a "class" vector of [u8] to a *[uv::ll::uv_buf_t] + // shared implementation used by write and write_future fn write_common_impl(socket_data_ptr: *tcp_socket_data, raw_write_data: [u8]/~) @@ -963,56 +1018,15 @@ fn write_common_impl(socket_data_ptr: *tcp_socket_data, } } -// various recv_* can use a tcp_conn_port can re-use this.. -fn conn_port_new_tcp_socket( - stream_handle_ptr: *uv::ll::uv_tcp_t, - iotask: iotask) - -> result::result unsafe { - // tcp_nl_on_connection_cb - let reader_po = comm::port::>(); - let client_socket_data = @{ - reader_po : reader_po, - reader_ch : comm::chan(reader_po), - stream_handle_ptr : stream_handle_ptr, - connect_req : uv::ll::connect_t(), - write_req : uv::ll::write_t(), - iotask : iotask - }; - let client_socket_data_ptr = ptr::addr_of(*client_socket_data); - comm::listen {|cont_ch| - iotask::interact(iotask) {|loop_ptr| - log(debug, #fmt("in interact cb 4 conn_port_new_tcp.. loop %?", - loop_ptr)); - uv::ll::set_data_for_uv_handle(stream_handle_ptr, - client_socket_data_ptr); - cont_ch.send(()); - }; - cont_ch.recv() - }; - result::ok(tcp_socket(client_socket_data)) -} - enum tcp_new_connection { new_tcp_conn(*uv::ll::uv_tcp_t) } -type tcp_conn_port_data = { - server_stream: uv::ll::uv_tcp_t, - stream_closed_po: comm::port<()>, - stream_closed_ch: comm::chan<()>, - iotask: iotask, - new_conn_po: comm::port>, - new_conn_ch: comm::chan> -}; - type tcp_listen_fc_data = { server_stream_ptr: *uv::ll::uv_tcp_t, stream_closed_ch: comm::chan<()>, kill_ch: comm::chan>, - new_connect_cb: fn~(tcp_new_connection, - comm::chan>), + on_connect_cb: fn~(*uv::ll::uv_tcp_t), iotask: iotask, mut active: bool }; @@ -1031,8 +1045,7 @@ crust fn tcp_lfc_on_connection_cb(handle: *uv::ll::uv_tcp_t, if (*server_data_ptr).active { alt status { 0i32 { - let new_conn = new_tcp_conn(handle); - (*server_data_ptr).new_connect_cb(new_conn, kill_ch); + (*server_data_ptr).on_connect_cb(handle); } _ { let loop_ptr = uv::ll::get_loop_for_uv_handle(handle); @@ -1045,66 +1058,11 @@ crust fn tcp_lfc_on_connection_cb(handle: *uv::ll::uv_tcp_t, } } -crust fn tcp_nl_close_cb(handle: *uv::ll::uv_tcp_t) unsafe { - let conn_data_ptr = uv::ll::get_data_for_uv_handle( - handle) as *tcp_conn_port_data; - comm::send((*conn_data_ptr).stream_closed_ch, ()); -} - fn malloc_uv_tcp_t() -> *uv::ll::uv_tcp_t unsafe { rustrt::rust_uv_current_kernel_malloc( rustrt::rust_uv_helper_uv_tcp_t_size()) as *uv::ll::uv_tcp_t } -crust fn tcp_nl_on_connection_cb(server_handle_ptr: *uv::ll::uv_tcp_t, - status: libc::c_int) unsafe { - let server_data_ptr = uv::ll::get_data_for_uv_handle(server_handle_ptr) - as *tcp_conn_port_data; - let new_conn_ch = (*server_data_ptr).new_conn_ch; - let loop_ptr = uv::ll::get_loop_for_uv_handle(server_handle_ptr); - alt status { - 0i32 { - let client_stream_handle_ptr = malloc_uv_tcp_t(); - *(client_stream_handle_ptr as *mut uv::ll::uv_tcp_t) = - uv::ll::tcp_t(); - alt uv::ll::tcp_init(loop_ptr, client_stream_handle_ptr) { - 0i32 { - log(debug, "uv_tcp_init successful for client stream"); - alt uv::ll::accept( - server_handle_ptr as *libc::c_void, - client_stream_handle_ptr as *libc::c_void) { - 0i32 { - log(debug, "successfully accepted client connection"); - comm::send(new_conn_ch, - result::ok(client_stream_handle_ptr)); - } - _ { - log(debug, "failed to accept client conn"); - comm::send( - new_conn_ch, - result::err(uv::ll::get_last_err_data(loop_ptr) - .to_tcp_err())); - } - } - } - _ { - log(debug, "failed to init client stream"); - comm::send( - new_conn_ch, - result::err(uv::ll::get_last_err_data(loop_ptr) - .to_tcp_err())); - } - } - } - _ { - comm::send( - new_conn_ch, - result::err(uv::ll::get_last_err_data(loop_ptr) - .to_tcp_err())); - } - } -} - enum tcp_connect_result { tcp_connected(tcp_socket), tcp_connect_error(tcp_err_data) @@ -1171,7 +1129,7 @@ crust fn on_tcp_read_cb(stream: *uv::ll::uv_stream_t, } crust fn on_alloc_cb(handle: *libc::c_void, - ++suggested_size: libc::size_t) + ++suggested_size: size_t) -> uv::ll::uv_buf_t unsafe { log(debug, "tcp read on_alloc_cb!"); let char_ptr = uv::ll::malloc_buf_base_of(suggested_size); @@ -1273,21 +1231,12 @@ type tcp_socket_data = { iotask: iotask }; -// convert rust ip_addr to libuv's native representation -fn ipv4_ip_addr_to_sockaddr_in(input_ip: ip::ip_addr, - port: uint) -> uv::ll::sockaddr_in unsafe { - // FIXME (#2656): ipv6 - alt input_ip { - ip::ipv4(_,_,_,_) { - uv::ll::ip4_addr(ip::format_addr(input_ip), port as int) - } - ip::ipv6(_,_,_,_,_,_,_,_) { - fail "FIXME (#2656) ipv6 not yet supported"; - } - } -} +type tcp_buffered_socket_data = { + sock: tcp_socket, + mut buf: [u8]/~ +}; -#[cfg(test)] +//#[cfg(test)] mod test { // FIXME don't run on fbsd or linux 32 bit (#2064) #[cfg(target_os="win32")] @@ -1301,9 +1250,22 @@ mod test { impl_gl_tcp_ipv4_server_and_client(); } #[test] - fn test_gl_tcp_server_listener_and_client_ipv4() unsafe { - impl_gl_tcp_ipv4_server_listener_and_client(); + fn test_gl_tcp_ipv4_client_error_connection_refused() unsafe { + impl_gl_tcp_ipv4_client_error_connection_refused(); + } + #[test] + fn test_gl_tcp_server_address_in_use() unsafe { + impl_gl_tcp_ipv4_server_address_in_use(); + } + #[test] + fn test_gl_tcp_server_access_denied() unsafe { + impl_gl_tcp_ipv4_server_access_denied(); + } + #[test] + fn test_gl_tcp_ipv4_server_client_reader_writer() { + impl_gl_tcp_ipv4_server_client_reader_writer(); } + } #[cfg(target_arch="x86")] mod impl32 { @@ -1314,8 +1276,23 @@ mod test { } #[test] #[ignore(cfg(target_os = "linux"))] - fn test_gl_tcp_server_listener_and_client_ipv4() unsafe { - impl_gl_tcp_ipv4_server_listener_and_client(); + fn test_gl_tcp_ipv4_client_error_connection_refused() unsafe { + impl_gl_tcp_ipv4_client_error_connection_refused(); + } + #[test] + #[ignore(cfg(target_os = "linux"))] + fn test_gl_tcp_server_address_in_use() unsafe { + impl_gl_tcp_ipv4_server_address_in_use(); + } + #[test] + #[ignore(cfg(target_os = "linux"))] + fn test_gl_tcp_server_access_denied() unsafe { + impl_gl_tcp_ipv4_server_access_denied(); + } + #[test] + #[ignore(cfg(target_os = "linux"))] + fn test_gl_tcp_ipv4_server_client_reader_writer() { + impl_gl_tcp_ipv4_server_client_reader_writer(); } } } @@ -1347,7 +1324,7 @@ mod test { comm::recv(cont_po); // client log(debug, "server started, firing up client.."); - let actual_resp = comm::listen {|client_ch| + let actual_resp_result = comm::listen {|client_ch| run_tcp_test_client( server_ip, server_port, @@ -1355,6 +1332,8 @@ mod test { client_ch, hl_loop) }; + assert actual_resp_result.is_ok(); + let actual_resp = actual_resp_result.get(); let actual_req = comm::recv(server_result_po); log(debug, #fmt("REQ: expected: '%s' actual: '%s'", expected_req, actual_req)); @@ -1363,11 +1342,34 @@ mod test { assert str::contains(actual_req, expected_req); assert str::contains(actual_resp, expected_resp); } - fn impl_gl_tcp_ipv4_server_listener_and_client() { + fn impl_gl_tcp_ipv4_client_error_connection_refused() { let hl_loop = uv::global_loop::get(); let server_ip = "127.0.0.1"; let server_port = 8889u; let expected_req = "ping"; + // client + log(debug, "firing up client.."); + let actual_resp_result = comm::listen {|client_ch| + run_tcp_test_client( + server_ip, + server_port, + expected_req, + client_ch, + hl_loop) + }; + alt actual_resp_result.get_err() { + connection_refused { + } + _ { + fail "unknown error.. expected connection_refused" + } + } + } + fn impl_gl_tcp_ipv4_server_address_in_use() { + let hl_loop = uv::global_loop::get(); + let server_ip = "127.0.0.1"; + let server_port = 8890u; + let expected_req = "ping"; let expected_resp = "pong"; let server_result_po = comm::port::(); @@ -1378,7 +1380,7 @@ mod test { // server task::spawn_sched(task::manual_threads(1u)) {|| let actual_req = comm::listen {|server_ch| - run_tcp_test_server_listener( + run_tcp_test_server( server_ip, server_port, expected_resp, @@ -1389,9 +1391,14 @@ mod test { server_result_ch.send(actual_req); }; comm::recv(cont_po); - // client + // this one should fail.. + let listen_err = run_tcp_test_server_fail( + server_ip, + server_port, + hl_loop); + // client.. just doing this so that the first server tears down log(debug, "server started, firing up client.."); - let actual_resp = comm::listen {|client_ch| + comm::listen {|client_ch| run_tcp_test_client( server_ip, server_port, @@ -1399,6 +1406,76 @@ mod test { client_ch, hl_loop) }; + alt listen_err { + address_in_use { + assert true; + } + _ { + fail "expected address_in_use listen error,"+ + "but got a different error varient. check logs."; + } + } + } + fn impl_gl_tcp_ipv4_server_access_denied() { + let hl_loop = uv::global_loop::get(); + let server_ip = "127.0.0.1"; + let server_port = 80u; + // this one should fail.. + let listen_err = run_tcp_test_server_fail( + server_ip, + server_port, + hl_loop); + alt listen_err { + access_denied { + assert true; + } + _ { + fail "expected address_in_use listen error,"+ + "but got a different error varient. check logs."; + } + } + } + fn impl_gl_tcp_ipv4_server_client_reader_writer() { + let iotask = uv::global_loop::get(); + let server_ip = "127.0.0.1"; + let server_port = 8891u; + let expected_req = "ping"; + let expected_resp = "pong"; + + let server_result_po = comm::port::(); + let server_result_ch = comm::chan(server_result_po); + + let cont_po = comm::port::<()>(); + let cont_ch = comm::chan(cont_po); + // server + task::spawn_sched(task::manual_threads(1u)) {|| + let actual_req = comm::listen {|server_ch| + run_tcp_test_server( + server_ip, + server_port, + expected_resp, + server_ch, + cont_ch, + iotask) + }; + server_result_ch.send(actual_req); + }; + comm::recv(cont_po); + // client + let server_addr = ip::v4::parse_addr(server_ip); + let conn_result = connect(server_addr, server_port, iotask); + if result::is_err(conn_result) { + assert false; + } + let sock_buf = @socket_buf(result::unwrap(conn_result)); + buf_write(sock_buf as io::writer, expected_req); + + // so contrived! + let actual_resp = str::as_bytes(expected_resp) {|resp_buf| + buf_read(sock_buf as io::reader, + vec::len(resp_buf)) + }; + let actual_req = comm::recv(server_result_po); log(debug, #fmt("REQ: expected: '%s' actual: '%s'", expected_req, actual_req)); @@ -1408,139 +1485,133 @@ mod test { assert str::contains(actual_resp, expected_resp); } + fn buf_write(+w: io::writer, val: str) { + log(debug, #fmt("BUF_WRITE: val len %?", str::len(val))); + str::byte_slice(val) {|b_slice| + log(debug, #fmt("BUF_WRITE: b_slice len %?", + vec::len(b_slice))); + w.write(b_slice) + } + } + + fn buf_read(+r: io::reader, len: uint) -> str { + let new_bytes = r.read_bytes(len); + log(debug, #fmt("in buf_read.. new_bytes len: %?", + vec::len(new_bytes))); + str::from_bytes(new_bytes) + } + fn run_tcp_test_server(server_ip: str, server_port: uint, resp: str, server_ch: comm::chan, cont_ch: comm::chan<()>, iotask: iotask) -> str { - - task::spawn_sched(task::manual_threads(1u)) {|| - let server_ip_addr = ip::v4::parse_addr(server_ip); - let listen_result = - listen_for_conn(server_ip_addr, server_port, 128u, - iotask, - // on_establish_cb -- called when listener is set up - {|kill_ch| - log(debug, #fmt("establish_cb %?", - kill_ch)); - comm::send(cont_ch, ()); - }, - // risky to run this on the loop, but some users - // will want the POWER - {|new_conn, kill_ch| - log(debug, "SERVER: new connection!"); - comm::listen {|cont_ch| - task::spawn_sched(task::manual_threads(1u)) {|| - log(debug, "SERVER: starting worker for new req"); - - let accept_result = accept(new_conn); - log(debug, "SERVER: after accept()"); - if result::is_err(accept_result) { - log(debug, "SERVER: error accept connection"); - let err_data = result::get_err(accept_result); + let server_ip_addr = ip::v4::parse_addr(server_ip); + let listen_result = listen(server_ip_addr, server_port, 128u, iotask, + // on_establish_cb -- called when listener is set up + {|kill_ch| + log(debug, #fmt("establish_cb %?", + kill_ch)); + comm::send(cont_ch, ()); + }, + // risky to run this on the loop, but some users + // will want the POWER + {|new_conn, kill_ch| + log(debug, "SERVER: new connection!"); + comm::listen {|cont_ch| + task::spawn_sched(task::manual_threads(1u)) {|| + log(debug, "SERVER: starting worker for new req"); + + let accept_result = accept(new_conn); + log(debug, "SERVER: after accept()"); + if result::is_err(accept_result) { + log(debug, "SERVER: error accept connection"); + let err_data = result::get_err(accept_result); + comm::send(kill_ch, some(err_data)); + log(debug, + "SERVER/WORKER: send on err cont ch"); + cont_ch.send(()); + } + else { + log(debug, + "SERVER/WORKER: send on cont ch"); + cont_ch.send(()); + let sock = result::unwrap(accept_result); + log(debug, "SERVER: successfully accepted"+ + "connection!"); + let received_req_bytes = read(sock, 0u); + alt received_req_bytes { + result::ok(data) { + log(debug, "SERVER: got REQ str::from_bytes.."); + log(debug, #fmt("SERVER: REQ data len: %?", + vec::len(data))); + server_ch.send( + str::from_bytes(data)); + log(debug, "SERVER: before write"); + tcp_write_single(sock, str::bytes(resp)); + log(debug, "SERVER: after write.. die"); + comm::send(kill_ch, none); + } + result::err(err_data) { + log(debug, #fmt("SERVER: error recvd: %s %s", + err_data.err_name, err_data.err_msg)); comm::send(kill_ch, some(err_data)); - log(debug, - "SERVER/WORKER: send on err cont ch"); - cont_ch.send(()); - } - else { - log(debug, - "SERVER/WORKER: send on cont ch"); - cont_ch.send(()); - let sock = result::unwrap(accept_result); - log(debug, "SERVER: successfully accepted"+ - "connection!"); - let received_req_bytes = sock.read(0u); - alt received_req_bytes { - result::ok(data) { - server_ch.send( - str::from_bytes(data)); - log(debug, "SERVER: before write"); - tcp_write_single(sock, str::bytes(resp)); - log(debug, "SERVER: after write.. die"); - comm::send(kill_ch, none); - } - result::err(err_data) { - log(debug, #fmt("SERVER: error recvd: %s %s", - err_data.err_name, err_data.err_msg)); - comm::send(kill_ch, some(err_data)); - server_ch.send(""); - } - } - log(debug, "SERVER: worker spinning down"); + server_ch.send(""); + } } + log(debug, "SERVER: worker spinning down"); } - log(debug, "SERVER: waiting to recv on cont_ch"); - cont_ch.recv() - }; - log(debug, "SERVER: recv'd on cont_ch..leaving listen cb"); - }); - // err check on listen_result - if result::is_err(listen_result) { - let err_data = result::get_err(listen_result); - log(debug, #fmt("SERVER: exited abnormally name %s msg %s", - err_data.err_name, err_data.err_msg)); - } - }; - let ret_val = server_ch.recv(); - log(debug, #fmt("SERVER: exited and got ret val: '%s'", ret_val)); - ret_val - } - - fn run_tcp_test_server_listener(server_ip: str, - server_port: uint, resp: str, - server_ch: comm::chan, - cont_ch: comm::chan<()>, - iotask: iotask) -> str { - - task::spawn_sched(task::manual_threads(1u)) {|| - let server_ip_addr = ip::v4::parse_addr(server_ip); - let new_listener_result = - new_listener(server_ip_addr, server_port, 128u, iotask); - if result::is_err(new_listener_result) { - let err_data = result::get_err(new_listener_result); - log(debug, #fmt("SERVER: exited abnormally name %s msg %s", - err_data.err_name, err_data.err_msg)); - fail "couldn't set up new listener"; - } - let server_port = result::unwrap(new_listener_result); - cont_ch.send(()); - // receive a single new connection.. normally this'd be - // in a loop {}, but we're just going to take a single - // client.. get their req, write a resp and then exit - let new_conn_result = server_port.recv(); - if result::is_err(new_conn_result) { - let err_data = result::get_err(new_conn_result); - log(debug, #fmt("SERVER: exited abnormally name %s msg %s", - err_data.err_name, err_data.err_msg)); - fail "couldn't recv new conn"; - } - let sock = result::unwrap(new_conn_result); - log(debug, "SERVER: successfully accepted"+ - "connection!"); - let received_req_bytes = - sock.read(0u); - alt received_req_bytes { - result::ok(data) { - server_ch.send( - str::from_bytes(data)); - log(debug, "SERVER: before write"); - tcp_write_single(sock, str::bytes(resp)); - log(debug, "SERVER: after write.. die"); + } + log(debug, "SERVER: waiting to recv on cont_ch"); + cont_ch.recv() + }; + log(debug, "SERVER: recv'd on cont_ch..leaving listen cb"); + }); + // err check on listen_result + if result::is_err(listen_result) { + alt result::get_err(listen_result) { + generic_listen_err(name, msg) { + fail #fmt("SERVER: exited abnormally name %s msg %s", + name, msg); + } + access_denied { + fail "SERVER: exited abnormally, got access denied.."; } - result::err(err_data) { - server_ch.send(""); + address_in_use { + fail "SERVER: exited abnormally, got address in use..."; } } - }; + } let ret_val = server_ch.recv(); log(debug, #fmt("SERVER: exited and got ret val: '%s'", ret_val)); ret_val } + fn run_tcp_test_server_fail(server_ip: str, server_port: uint, + iotask: iotask) -> tcp_listen_err_data { + let server_ip_addr = ip::v4::parse_addr(server_ip); + let listen_result = listen(server_ip_addr, server_port, 128u, iotask, + // on_establish_cb -- called when listener is set up + {|kill_ch| + log(debug, #fmt("establish_cb %?", + kill_ch)); + }, + {|new_conn, kill_ch| + fail #fmt("SERVER: shouldn't be called.. %? %?", + new_conn, kill_ch); + }); + // err check on listen_result + if result::is_err(listen_result) { + result::get_err(listen_result) + } + else { + fail "SERVER: did not fail as expected" + } + } + fn run_tcp_test_client(server_ip: str, server_port: uint, resp: str, client_ch: comm::chan, - iotask: iotask) -> str { - + iotask: iotask) -> result::result { let server_ip_addr = ip::v4::parse_addr(server_ip); log(debug, "CLIENT: starting.."); @@ -1548,9 +1619,7 @@ mod test { if result::is_err(connect_result) { log(debug, "CLIENT: failed to connect"); let err_data = result::get_err(connect_result); - log(debug, #fmt("CLIENT: connect err name: %s msg: %s", - err_data.err_name, err_data.err_msg)); - "" + err(err_data) } else { let sock = result::unwrap(connect_result); @@ -1559,14 +1628,14 @@ mod test { let read_result = sock.read(0u); if read_result.is_err() { log(debug, "CLIENT: failure to read"); - "" + ok("") } else { client_ch.send(str::from_bytes(read_result.get())); let ret_val = client_ch.recv(); log(debug, #fmt("CLIENT: after client_ch recv ret: '%s'", ret_val)); - ret_val + ok(ret_val) } } } diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 2effb04cd3f77..3470727121755 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -15,7 +15,7 @@ use core(vers = "0.2"); import core::*; -export net, net_tcp; +export net, net_tcp, net_ip; export uv, uv_ll, uv_iotask, uv_global_loop; export c_vec, util, timer; export bitv, deque, fun_treemap, list, map, smallintmap, sort, treemap; diff --git a/src/libstd/uv_ll.rs b/src/libstd/uv_ll.rs index db0dcf7f4f458..4c0f3835f32e7 100644 --- a/src/libstd/uv_ll.rs +++ b/src/libstd/uv_ll.rs @@ -223,10 +223,81 @@ type sockaddr_in = { mut sin_zero: (u8, u8, u8, u8, u8, u8, u8, u8) }; -// unix size: 28 .. make due w/ 32 +// unix size: 28 .. FIXME #1645 +// stuck with 32 becuse of rust padding structs? +#[cfg(target_arch="x86_64")] +type sockaddr_in6 = { + a0: *u8, a1: *u8, + a2: *u8, a3: *u8 +}; +#[cfg(target_arch="x86")] type sockaddr_in6 = { a0: *u8, a1: *u8, - a2: *u8, a3: (u8, u8, u8, u8) + a2: *u8, a3: *u8, + a4: *u8, a5: *u8, + a6: *u8, a7: *u8 +}; + +// unix size: 28 .. FIXME #1645 +// stuck with 32 becuse of rust padding structs? +type addr_in = addr_in_impl::addr_in; +#[cfg(unix)] +mod addr_in_impl { + #[cfg(target_arch="x86_64")] + type addr_in = { + a0: *u8, a1: *u8, + a2: *u8, a3: *u8 + }; + #[cfg(target_arch="x86")] + type addr_in = { + a0: *u8, a1: *u8, + a2: *u8, a3: *u8, + a4: *u8, a5: *u8, + a6: *u8, a7: *u8, + }; +} +#[cfg(windows)] +mod addr_in_impl { + type addr_in = { + a0: *u8, a1: *u8, + a2: *u8, a3: *u8 + }; +} + +// unix size: 48, 32bit: 32 +type addrinfo = addrinfo_impl::addrinfo; +#[cfg(target_os="linux")] +mod addrinfo_impl { + #[cfg(target_arch="x86_64")] + type addrinfo = { + a00: *u8, a01: *u8, a02: *u8, a03: *u8, + a04: *u8, a05: *u8 + }; + #[cfg(target_arch="x86")] + type addrinfo = { + a00: *u8, a01: *u8, a02: *u8, a03: *u8, + a04: *u8, a05: *u8, a06: *u8, a07: *u8 + }; +} +#[cfg(target_os="macos")] +mod addrinfo_impl { + type addrinfo = { + a00: *u8, a01: *u8, a02: *u8, a03: *u8, + a04: *u8, a05: *u8 + }; +} +#[cfg(windows)] +mod addrinfo_impl { + type addrinfo = { + a00: *u8, a01: *u8, a02: *u8, a03: *u8, + a04: *u8, a05: *u8 + }; +} + +// unix size: 72 +type uv_getaddrinfo_t = { + a00: *u8, a01: *u8, a02: *u8, a03: *u8, a04: *u8, a05: *u8, + a06: *u8, a07: *u8, a08: *u8 }; mod uv_ll_struct_stubgen { @@ -474,10 +545,18 @@ mod uv_ll_struct_stubgen { a12: 0 as *u8 }; } + fn gen_stub_uv_getaddrinfo_t() -> uv_getaddrinfo_t { + { + a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, a03: 0 as *u8, + a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, a07: 0 as *u8, + a08: 0 as *u8 + } + } } #[nolink] native mod rustrt { + // libuv public API fn rust_uv_loop_new() -> *libc::c_void; fn rust_uv_loop_delete(lp: *libc::c_void); fn rust_uv_loop_refcount(loop_ptr: *libc::c_void) -> libc::c_int; @@ -500,6 +579,12 @@ native mod rustrt { fn rust_uv_err_name(err: *uv_err_t) -> *libc::c_char; fn rust_uv_ip4_addr(ip: *u8, port: libc::c_int) -> sockaddr_in; + fn rust_uv_ip6_addr(ip: *u8, port: libc::c_int) + -> sockaddr_in6; + fn rust_uv_ip4_name(src: *sockaddr_in, dst: *u8, size: libc::size_t) + -> libc::c_int; + fn rust_uv_ip6_name(src: *sockaddr_in6, dst: *u8, size: libc::size_t) + -> libc::c_int; // FIXME ref #2064 fn rust_uv_tcp_connect(connect_ptr: *uv_connect_t, tcp_handle_ptr: *uv_tcp_t, @@ -508,6 +593,14 @@ native mod rustrt { // FIXME ref #2064 fn rust_uv_tcp_bind(tcp_server: *uv_tcp_t, ++addr: *sockaddr_in) -> libc::c_int; + // FIXME ref #2064 + fn rust_uv_tcp_connect6(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + ++after_cb: *u8, + ++addr: *sockaddr_in6) -> libc::c_int; + // FIXME ref #2064 + fn rust_uv_tcp_bind6(tcp_server: *uv_tcp_t, + ++addr: *sockaddr_in6) -> libc::c_int; fn rust_uv_listen(stream: *libc::c_void, backlog: libc::c_int, cb: *u8) -> libc::c_int; fn rust_uv_accept(server: *libc::c_void, client: *libc::c_void) @@ -527,7 +620,22 @@ native mod rustrt { repeat: libc::c_uint) -> libc::c_int; fn rust_uv_timer_stop(handle: *uv_timer_t) -> libc::c_int; + fn rust_uv_getaddrinfo(loop_ptr: *libc::c_void, + handle: *uv_getaddrinfo_t, + cb: *u8, + node_name_ptr: *u8, + service_name_ptr: *u8, + // should probably only pass ptr::null() + hints: *addrinfo) -> libc::c_int; + fn rust_uv_freeaddrinfo(res: *addrinfo); + // data accessors/helpers for rust-mapped uv structs + fn rust_uv_helper_get_INADDR_NONE() -> u32; + fn rust_uv_is_ipv4_addrinfo(input: *addrinfo) -> bool; + fn rust_uv_is_ipv6_addrinfo(input: *addrinfo) -> bool; + fn rust_uv_get_next_addrinfo(input: *addrinfo) -> *addrinfo; + fn rust_uv_addrinfo_as_sockaddr_in(input: *addrinfo) -> *sockaddr_in; + fn rust_uv_addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6; fn rust_uv_malloc_buf_base_of(sug_size: libc::size_t) -> *u8; fn rust_uv_free_base_of_buf(++buf: uv_buf_t); fn rust_uv_get_stream_handle_from_connect_req( @@ -558,8 +666,12 @@ native mod rustrt { fn rust_uv_helper_uv_write_t_size() -> libc::c_uint; fn rust_uv_helper_uv_err_t_size() -> libc::c_uint; fn rust_uv_helper_sockaddr_in_size() -> libc::c_uint; + fn rust_uv_helper_sockaddr_in6_size() -> libc::c_uint; fn rust_uv_helper_uv_async_t_size() -> libc::c_uint; fn rust_uv_helper_uv_timer_t_size() -> libc::c_uint; + fn rust_uv_helper_uv_getaddrinfo_t_size() -> libc::c_uint; + fn rust_uv_helper_addrinfo_size() -> libc::c_uint; + fn rust_uv_helper_addr_in_size() -> libc::c_uint; } unsafe fn loop_new() -> *libc::c_void { @@ -598,11 +710,26 @@ unsafe fn tcp_connect(connect_ptr: *uv_connect_t, after_connect_cb, addr_ptr); } // FIXME ref #2064 +unsafe fn tcp_connect6(connect_ptr: *uv_connect_t, + tcp_handle_ptr: *uv_tcp_t, + addr_ptr: *sockaddr_in6, + ++after_connect_cb: *u8) +-> libc::c_int { + ret rustrt::rust_uv_tcp_connect6(connect_ptr, tcp_handle_ptr, + after_connect_cb, addr_ptr); +} +// FIXME ref #2064 unsafe fn tcp_bind(tcp_server_ptr: *uv_tcp_t, addr_ptr: *sockaddr_in) -> libc::c_int { ret rustrt::rust_uv_tcp_bind(tcp_server_ptr, addr_ptr); } +// FIXME ref #2064 +unsafe fn tcp_bind6(tcp_server_ptr: *uv_tcp_t, + addr_ptr: *sockaddr_in6) -> libc::c_int { + ret rustrt::rust_uv_tcp_bind6(tcp_server_ptr, + addr_ptr); +} unsafe fn listen(stream: *T, backlog: libc::c_int, cb: *u8) -> libc::c_int { @@ -677,14 +804,59 @@ unsafe fn buf_init(++input: *u8, len: uint) -> uv_buf_t { } unsafe fn ip4_addr(ip: str, port: int) -> sockaddr_in { - let mut addr_vec = str::bytes(ip); - vec::push(addr_vec, 0u8); // add null terminator - let addr_vec_ptr = vec::unsafe::to_ptr(addr_vec); - let ip_back = str::from_bytes(addr_vec); - log(debug, #fmt("vec val: '%s' length: %u", - ip_back, vec::len(addr_vec))); - ret rustrt::rust_uv_ip4_addr(addr_vec_ptr, - port as libc::c_int); + str::as_c_str(ip) {|ip_buf| + rustrt::rust_uv_ip4_addr(ip_buf as *u8, + port as libc::c_int) + } +} +unsafe fn ip6_addr(ip: str, port: int) +-> sockaddr_in6 { + str::as_c_str(ip) {|ip_buf| + rustrt::rust_uv_ip6_addr(ip_buf as *u8, + port as libc::c_int) + } +} +unsafe fn ip4_name(src: &sockaddr_in) -> str { + // ipv4 addr max size: 15 + 1 trailing null byte + let dst: [u8]/~ = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8]/~; + let size = 16 as libc::size_t; + vec::as_buf(dst) {|dst_buf| + rustrt::rust_uv_ip4_name(src as *sockaddr_in, + dst_buf, size); + // seems that checking the result of uv_ip4_name + // doesn't work too well.. + // you're stuck looking at the value of dst_buf + // to see if it is the string representation of + // INADDR_NONE (0xffffffff or 255.255.255.255 on + // many platforms) + str::unsafe::from_buf(dst_buf) + } +} +unsafe fn ip6_name(src: &sockaddr_in6) -> str { + // ipv6 addr max size: 45 + 1 trailing null byte + let dst: [u8]/~ = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8, + 0u8,0u8,0u8,0u8,0u8,0u8]/~; + let size = 46 as libc::size_t; + vec::as_buf(dst) {|dst_buf| + let src_unsafe_ptr = src as *sockaddr_in6; + log(debug, #fmt("val of src *sockaddr_in6: %? sockaddr_in6: %?", + src_unsafe_ptr, src)); + let result = rustrt::rust_uv_ip6_name(src_unsafe_ptr, + dst_buf, size); + alt result { + 0i32 { + str::unsafe::from_buf(dst_buf) + } + _ { + "" + } + } + } } unsafe fn timer_init(loop_ptr: *libc::c_void, @@ -699,6 +871,22 @@ unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint, unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> libc::c_int { ret rustrt::rust_uv_timer_stop(timer_ptr); } +unsafe fn getaddrinfo(loop_ptr: *libc::c_void, + handle: *uv_getaddrinfo_t, + cb: *u8, + node_name_ptr: *u8, + service_name_ptr: *u8, + hints: *addrinfo) -> libc::c_int { + rustrt::rust_uv_getaddrinfo(loop_ptr, + handle, + cb, + node_name_ptr, + service_name_ptr, + hints) +} +unsafe fn freeaddrinfo(res: *addrinfo) { + rustrt::rust_uv_freeaddrinfo(res); +} // libuv struct initializers unsafe fn tcp_t() -> uv_tcp_t { @@ -716,6 +904,9 @@ unsafe fn async_t() -> uv_async_t { unsafe fn timer_t() -> uv_timer_t { ret uv_ll_struct_stubgen::gen_stub_uv_timer_t(); } +unsafe fn getaddrinfo_t() -> uv_getaddrinfo_t { + ret uv_ll_struct_stubgen::gen_stub_uv_getaddrinfo_t(); +} // data access helpers unsafe fn get_loop_for_uv_handle(handle: *T) @@ -791,6 +982,25 @@ type uv_err_data = { err_msg: str }; +unsafe fn is_ipv4_addrinfo(input: *addrinfo) -> bool { + rustrt::rust_uv_is_ipv4_addrinfo(input) +} +unsafe fn is_ipv6_addrinfo(input: *addrinfo) -> bool { + rustrt::rust_uv_is_ipv6_addrinfo(input) +} +unsafe fn get_INADDR_NONE() -> u32 { + rustrt::rust_uv_helper_get_INADDR_NONE() +} +unsafe fn get_next_addrinfo(input: *addrinfo) -> *addrinfo { + rustrt::rust_uv_get_next_addrinfo(input) +} +unsafe fn addrinfo_as_sockaddr_in(input: *addrinfo) -> *sockaddr_in { + rustrt::rust_uv_addrinfo_as_sockaddr_in(input) +} +unsafe fn addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6 { + rustrt::rust_uv_addrinfo_as_sockaddr_in6(input) +} + #[cfg(test)] mod test { enum tcp_read_data { @@ -1366,6 +1576,33 @@ mod test { log(debug, output); assert foreign_handle_size as uint == rust_handle_size; } + #[test] + #[ignore(cfg(target_os = "freebsd"))] + fn test_uv_ll_struct_size_sockaddr_in6() { + let native_handle_size = + rustrt::rust_uv_helper_sockaddr_in6_size(); + let rust_handle_size = sys::size_of::(); + let output = #fmt("sockaddr_in6 -- native: %u rust: %u", + native_handle_size as uint, rust_handle_size); + log(debug, output); + // FIXME #1645 .. rust appears to pad structs to the nearest byte..? + // .. can't get the uv::ll::sockaddr_in6 to == 28 :/ + // .. so the type always appears to be 32 in size.. which is + // good, i guess.. better too big than too little + assert (4u+native_handle_size as uint) == rust_handle_size; + } + #[test] + #[ignore(cfg(target_os = "freebsd"))] + fn test_uv_ll_struct_size_addr_in() { + let native_handle_size = + rustrt::rust_uv_helper_addr_in_size(); + let rust_handle_size = sys::size_of::(); + let output = #fmt("addr_in -- native: %u rust: %u", + native_handle_size as uint, rust_handle_size); + log(debug, output); + // FIXME #1645 .. see note above about struct padding + assert (4u+native_handle_size as uint) == rust_handle_size; + } #[test] #[ignore(cfg(target_os = "freebsd"))] @@ -1390,4 +1627,27 @@ mod test { log(debug, output); assert foreign_handle_size as uint == rust_handle_size; } + + #[test] + #[ignore(cfg(target_os = "freebsd"))] + fn test_uv_ll_struct_size_uv_getaddrinfo_t() { + let native_handle_size = + rustrt::rust_uv_helper_uv_getaddrinfo_t_size(); + let rust_handle_size = sys::size_of::(); + let output = #fmt("uv_getaddrinfo_t -- native: %u rust: %u", + native_handle_size as uint, rust_handle_size); + log(debug, output); + assert native_handle_size as uint == rust_handle_size; + } + #[test] + #[ignore(cfg(target_os = "freebsd"))] + fn test_uv_ll_struct_size_addrinfo() { + let native_handle_size = + rustrt::rust_uv_helper_addrinfo_size(); + let rust_handle_size = sys::size_of::(); + let output = #fmt("addrinfo -- native: %u rust: %u", + native_handle_size as uint, rust_handle_size); + log(debug, output); + assert native_handle_size as uint == rust_handle_size; + } } diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs index 09ebc0b79cd6b..bac880b60a8c8 100644 --- a/src/libsyntax/ext/fmt.rs +++ b/src/libsyntax/ext/fmt.rs @@ -50,19 +50,19 @@ fn pieces_to_expr(cx: ext_ctxt, sp: span, fn make_rt_conv_expr(cx: ext_ctxt, sp: span, cnv: conv) -> @ast::expr { fn make_flags(cx: ext_ctxt, sp: span, flags: [flag]/~) -> @ast::expr { - let mut flagexprs: [@ast::expr]/~ = []/~; + let mut tmp_expr = make_rt_path_expr(cx, sp, @"flag_none"); for flags.each {|f| - let mut fstr; - alt f { - flag_left_justify { fstr = "flag_left_justify"; } - flag_left_zero_pad { fstr = "flag_left_zero_pad"; } - flag_space_for_sign { fstr = "flag_space_for_sign"; } - flag_sign_always { fstr = "flag_sign_always"; } - flag_alternate { fstr = "flag_alternate"; } - } - vec::push(flagexprs, make_rt_path_expr(cx, sp, @fstr)); + let fstr = alt f { + flag_left_justify { "flag_left_justify" } + flag_left_zero_pad { "flag_left_zero_pad" } + flag_space_for_sign { "flag_space_for_sign" } + flag_sign_always { "flag_sign_always" } + flag_alternate { "flag_alternate" } + }; + tmp_expr = mk_binary(cx, sp, ast::bitor, tmp_expr, + make_rt_path_expr(cx, sp, @fstr)); } - ret mk_uniq_vec_e(cx, sp, flagexprs); + ret tmp_expr; } fn make_count(cx: ext_ctxt, sp: span, cnt: count) -> @ast::expr { alt cnt { diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 1873ae33e9c16..706e8ff43807f 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -238,32 +238,36 @@ rust_uv_tcp_connect(uv_connect_t* connect_ptr, uv_tcp_t* tcp_ptr, uv_connect_cb cb, sockaddr_in* addr_ptr) { - rust_task* task = rust_get_current_task(); - LOG(task, stdlib, "inside rust_uv_tcp_connect"); // FIXME ref #2064 sockaddr_in addr = *addr_ptr; - LOG(task, stdlib, "before tcp_connect .. port: %d", - addr.sin_port); - LOG(task, stdlib, "before tcp_connect.. tcp stream:" \ - "%lu cb ptr: %lu", - (unsigned long int)tcp_ptr, (unsigned long int)cb); int result = uv_tcp_connect(connect_ptr, tcp_ptr, addr, cb); - LOG(task, stdlib, "leaving rust_uv_tcp_connect.." \ - "and result: %d", - result); return result; } extern "C" int rust_uv_tcp_bind(uv_tcp_t* tcp_server, sockaddr_in* addr_ptr) { // FIXME ref #2064 - rust_task* task = rust_get_current_task(); sockaddr_in addr = *addr_ptr; - LOG(task, stdlib, "before uv_tcp_bind .. tcp_server:" \ - "%lu port: %d", - (unsigned long int)tcp_server, addr.sin_port); return uv_tcp_bind(tcp_server, addr); } +extern "C" int +rust_uv_tcp_connect6(uv_connect_t* connect_ptr, + uv_tcp_t* tcp_ptr, + uv_connect_cb cb, + sockaddr_in6* addr_ptr) { + // FIXME ref #2064 + sockaddr_in6 addr = *addr_ptr; + int result = uv_tcp_connect6(connect_ptr, tcp_ptr, addr, cb); + return result; +} + +extern "C" int +rust_uv_tcp_bind6 +(uv_tcp_t* tcp_server, sockaddr_in6* addr_ptr) { + // FIXME ref #2064 + sockaddr_in6 addr = *addr_ptr; + return uv_tcp_bind6(tcp_server, addr); +} extern "C" int rust_uv_listen(uv_stream_t* stream, int backlog, @@ -301,6 +305,10 @@ rust_uv_helper_sockaddr_in_size() { return sizeof(sockaddr_in); } extern "C" size_t +rust_uv_helper_sockaddr_in6_size() { + return sizeof(sockaddr_in6); +} +extern "C" size_t rust_uv_helper_uv_async_t_size() { return sizeof(uv_async_t); } @@ -308,7 +316,22 @@ extern "C" size_t rust_uv_helper_uv_timer_t_size() { return sizeof(uv_timer_t); } - +extern "C" size_t +rust_uv_helper_addr_in_size() { + return sizeof(sockaddr_in6); +} +extern "C" size_t +rust_uv_helper_uv_getaddrinfo_t_size() { + return sizeof(uv_getaddrinfo_t); +} +extern "C" size_t +rust_uv_helper_addrinfo_size() { + return sizeof(addrinfo); +} +extern "C" unsigned int +rust_uv_helper_get_INADDR_NONE() { + return INADDR_NONE; +} extern "C" uv_stream_t* rust_uv_get_stream_handle_from_connect_req(uv_connect_t* connect) { return connect->handle; @@ -436,11 +459,27 @@ extern "C" struct sockaddr_in rust_uv_ip4_addr(const char* ip, int port) { rust_task* task = rust_get_current_task(); LOG(task, stdlib, "before creating addr_ptr.. ip %s" \ - "port %d", ip, port); + " port %d\n", ip, port); struct sockaddr_in addr = uv_ip4_addr(ip, port); LOG(task, stdlib, "after creating .. port: %d", addr.sin_port); return addr; } +extern "C" struct sockaddr_in6 +rust_uv_ip6_addr(const char* ip, int port) { + rust_task* task = rust_get_current_task(); + LOG(task, stdlib, "before creating addr_ptr.. ip %s" \ + " port %d\n", ip, port); + return uv_ip6_addr(ip, port); +} +extern "C" int +rust_uv_ip4_name(struct sockaddr_in* src, char* dst, size_t size) { + return uv_ip4_name(src, dst, size); +} +extern "C" int +rust_uv_ip6_name(struct sockaddr_in6* src, char* dst, size_t size) { + int result = uv_ip6_name(src, dst, size); + return result; +} extern "C" uintptr_t* rust_uv_get_kernel_global_chan_ptr() { @@ -460,3 +499,35 @@ extern "C" void rust_uv_current_kernel_free(void* mem) { current_kernel_free(mem); } + +extern "C" int +rust_uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* handle, + uv_getaddrinfo_cb cb, + char* node, char* service, + addrinfo* hints) { + return uv_getaddrinfo(loop, handle, cb, node, service, hints); +} +extern "C" void +rust_uv_freeaddrinfo(addrinfo* res) { + uv_freeaddrinfo(res); +} +extern "C" bool +rust_uv_is_ipv4_addrinfo(addrinfo* input) { + return input->ai_family == AF_INET; +} +extern "C" bool +rust_uv_is_ipv6_addrinfo(addrinfo* input) { + return input->ai_family == AF_INET6; +} +extern "C" addrinfo* +rust_uv_get_next_addrinfo(addrinfo* input) { + return input->ai_next; +} +extern "C" sockaddr_in* +rust_uv_addrinfo_as_sockaddr_in(addrinfo* input) { + return (sockaddr_in*)input->ai_addr; +} +extern "C" sockaddr_in6* +rust_uv_addrinfo_as_sockaddr_in6(addrinfo* input) { + return (sockaddr_in6*)input->ai_addr; +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 0be74d20e24d3..a218782dcbe63 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -111,8 +111,13 @@ rust_uv_last_error rust_uv_strerror rust_uv_err_name rust_uv_ip4_addr +rust_uv_ip4_name +rust_uv_ip6_addr +rust_uv_ip6_name rust_uv_tcp_connect rust_uv_tcp_bind +rust_uv_tcp_connect6 +rust_uv_tcp_bind6 rust_uv_listen rust_uv_accept rust_uv_write @@ -120,12 +125,22 @@ rust_uv_read_start rust_uv_read_stop rust_uv_malloc_buf_base_of rust_uv_free_base_of_buf +rust_uv_is_ipv4_addrinfo +rust_uv_is_ipv6_addrinfo +rust_uv_get_next_addrinfo +rust_uv_addrinfo_as_sockaddr_in +rust_uv_addrinfo_as_sockaddr_in6 rust_uv_helper_uv_tcp_t_size rust_uv_helper_uv_connect_t_size rust_uv_helper_uv_buf_t_size rust_uv_helper_uv_write_t_size rust_uv_helper_uv_err_t_size rust_uv_helper_sockaddr_in_size +rust_uv_helper_sockaddr_in6_size +rust_uv_helper_addr_in_size +rust_uv_helper_addrinfo_size +rust_uv_helper_uv_getaddrinfo_t_size +rust_uv_helper_get_INADDR_NONE rust_uv_helper_uv_async_t_size rust_uv_helper_uv_timer_t_size rust_uv_get_stream_handle_from_connect_req @@ -142,6 +157,8 @@ rust_uv_get_len_from_buf rust_uv_get_kernel_global_chan_ptr rust_uv_current_kernel_malloc rust_uv_current_kernel_free +rust_uv_getaddrinfo +rust_uv_freeaddrinfo rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock diff --git a/src/snapshots.txt b/src/snapshots.txt index fe90dae15cffb..3e344f6b398d2 100644 --- a/src/snapshots.txt +++ b/src/snapshots.txt @@ -1,3 +1,10 @@ +S 2012-06-28 810677e + macos-x86_64 ee659583a09bb8466985428e4baa16498eedf4fb + macos-i386 8e97646a4a87c239ce5075c24b8bfb490dd90cf9 + linux-x86_64 0520e6f907981b6900a1b98eee43d5416c47c801 + linux-i386 fa960de5a5e21a822aca6c2924426bef2cc74367 + winnt-i386 d331a09b93fa6081908a8f1c06e4d67565256074 + S 2012-06-26 b9d3ad0 macos-x86_64 48206274146453f19f35be553469ac40d6319884 macos-i386 042bc8d4275947e74f65e52eda30eb0780e8f385