test_that("empty set", { px <- processx::process$new( px(), c("sleep", "5"), poll_connection = FALSE) on.exit(cleanup_process(px), add = TRUE) pid <- px$get_pid() p <- ps_handle(pid) cl <- ps_connections(p) expect_equal(nrow(cl), 0) expect_s3_class(cl, "data.frame") expect_s3_class(cl, "tbl") expect_equal( names(cl), c("fd", "family", "type", "laddr", "lport", "raddr", "rport", "state")) }) test_that("UNIX sockets", { if (!ps_os_type()[["POSIX"]]) skip("No UNIX sockets") px <- processx::process$new(px(), c("sleep", "5"), stdout = "|") on.exit(cleanup_process(px), add = TRUE) pid <- px$get_pid() p <- ps_handle(pid) cl <- ps_connections(p) expect_equal(nrow(cl), 1) expect_s3_class(cl, "data.frame") expect_s3_class(cl, "tbl") expect_equal(cl$fd, 1) expect_equal(cl$family, "AF_UNIX") expect_equal(cl$type, "SOCK_STREAM") expect_identical(cl$laddr, NA_character_) expect_identical(cl$lport, NA_integer_) expect_identical(cl$raddr, NA_character_) expect_identical(cl$lport, NA_integer_) expect_identical(cl$state, NA_character_) }) test_that("UNIX sockets with path", { if (!ps_os_type()[["POSIX"]]) skip("No UNIX sockets") skip_without_program("socat") skip_if_no_processx() skip_on_cran() sfile <- tempfile() sfile <- file.path(normalizePath(dirname(sfile)), basename(sfile)) on.exit(unlink(sfile, recursive = TRUE), add = TRUE) nc <- processx::process$new( "socat", c("-", paste0("UNIX-LISTEN:", sfile)), stdin = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() ## Might need to wait for socat to start listening on the socket deadline <- Sys.time() + as.difftime(5, units = "secs") while (nc$is_alive() && !file.exists(sfile) && Sys.time() < deadline) { Sys.sleep(0.1) } cl <- ps_connections(p) cl <- cl[!is.na(cl$laddr) & cl$laddr == sfile, ] expect_equal(nrow(cl), 1) }) test_that("TCP", { skip_on_cran() # need to connect now, otherwise the connections needed for webfakes # show up in the list httpbin$url() before <- ps_connections(ps_handle()) cx <- curl::curl(httpbin$url("/drip"), open = "r") on.exit({ close(cx); rm(cx) }, add = TRUE) after <- ps_connections(ps_handle()) new <- after[! after$lport %in% before$lport, ] expect_equal(new$family, "AF_INET") expect_equal(new$type, "SOCK_STREAM") expect_true(is_ipv4_address(new$laddr)) expect_true(is.integer(new$lport)) expect_equal(new$rport, httpbin$get_port()) expect_equal(new$state, "CONN_ESTABLISHED") }) test_that("TCP on loopback", { skip_without_program("socat") skip_if_no_processx() nc <- processx::process$new( "socat", c("-d", "-d", "-ls", "-", "TCP4-LISTEN:0"), stdin = "|", stderr = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() wait_for_string(nc, "listening on", timeout = 2000) cl <- ps_connections(p) cl <- cl[!is.na(cl$state) & cl$state == "CONN_LISTEN", ] expect_equal(nrow(cl), 1) expect_true(cl$state == "CONN_LISTEN") port <- cl$lport nc2 <- processx::process$new( "socat", c("-", paste0("TCP4-CONNECT:127.0.0.1:", port)), stdin = "|") on.exit(cleanup_process(nc2), add = TRUE) p2 <- nc2$as_ps_handle() deadline <- Sys.time() + as.difftime(5, units = "secs") while (Sys.time() < deadline && ! port %in% (cl2 <- ps_connections(p2))$rport) Sys.sleep(0.1) cl2 <- cl2[!is.na(cl2$rport & cl2$rport == port), ] expect_equal(cl2$family, "AF_INET") expect_equal(cl2$type, "SOCK_STREAM") expect_equal(cl2$state, "CONN_ESTABLISHED") }) test_that("UDP", { # does not work offline skip_on_cran() skip_without_program("socat") skip_if_no_processx() if (!pingr::is_online()) skip("Offline") nc <- processx::process$new( "socat", c("-", "UDP4-CONNECT:8.8.8.8:53,pf=ip4"), stdin = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() deadline <- Sys.time() + as.difftime(5, units = "secs") while (Sys.time() < deadline && ! 53 %in% (cl <- ps_connections(p))$rport) { Sys.sleep(.1) } expect_true(deadline > Sys.time()) cl <- cl[!is.na(cl$rport) & cl$rport == 53, ] expect_equal(nrow(cl), 1) expect_equal(cl$family, "AF_INET") expect_equal(cl$type, "SOCK_DGRAM") expect_equal(cl$raddr, "8.8.8.8") }) test_that("UDP on loopback", { skip_without_program("socat") skip_if_no_processx() nc <- processx::process$new( "socat", c("-d", "-d", "-ls", "-", "UDP4-LISTEN:0"), stdin = "|", stderr = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() wait_for_string(nc, "listening on", timeout = 2000) cl <- ps_connections(p) cl <- cl[!is.na(cl$lport) & cl$type == "SOCK_DGRAM", ] port <- cl$lport expect_equal(cl$family, "AF_INET") expect_equal(cl$type, "SOCK_DGRAM") nc2 <- processx::process$new( "socat", c("-", paste0("UDP4-CONNECT:127.0.0.1:", port)), stdin = "|") on.exit(cleanup_process(nc2), add = TRUE) p2 <- nc2$as_ps_handle() deadline <- Sys.time() + as.difftime(5, units = "secs") while (Sys.time() < deadline && ! port %in% (cl2 <- ps_connections(p2))$rport) Sys.sleep(0.1) cl2 <- cl2[!is.na(cl2$rport & cl2$rport == port), ] expect_equal(cl2$family, "AF_INET") expect_equal(cl2$type, "SOCK_DGRAM") }) test_that("TCP6", { skip_without_program("socat") skip_if_no_processx() skip_without_ipv6() skip_without_ipv6_connection() nc <- processx::process$new( "socat", c("-d", "-d", "-", paste0("TCP6:", ipv6_host(), ":443")), stdin = "|", stderr = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() wait_for_string(nc, "starting data transfer", timeout = 3000) cl <- ps_connections(p) cl <- cl[!is.na(cl$rport) & cl$rport == 443, ] expect_equal(nrow(cl), 1) expect_equal(cl$family, "AF_INET6") expect_equal(cl$type, "SOCK_STREAM") }) test_that("TCP6 on loopback", { skip_without_program("socat") skip_if_no_processx() skip_without_ipv6() nc <- processx::process$new( "socat", c("-d", "-d", "-", "TCP6-LISTEN:0"), stdin = "|", stderr = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() wait_for_string(nc, "listening on", timeout = 2000) cl <- ps_connections(p) cl <- cl[!is.na(cl$state) & cl$state == "CONN_LISTEN", ] expect_equal(nrow(cl), 1) expect_true(cl$state == "CONN_LISTEN") port <- cl$lport nc2 <- processx::process$new( "socat", c("-d", "-d", "-", paste0("TCP6-CONNECT:\\:\\:1:", port)), stdin = "|", stderr = "|") on.exit(cleanup_process(nc2), add = TRUE) p2 <- nc2$as_ps_handle() err <- FALSE tryCatch( wait_for_string(nc2, "starting data transfer", timeout = 2000), error = function(e) err <<- TRUE) if (err) skip("Could not bind to IPv6 address") cl2 <- ps_connections(p2) cl2 <- cl2[!is.na(cl2$rport & cl2$rport == port), ] expect_equal(cl2$family, "AF_INET6") expect_equal(cl2$type, "SOCK_STREAM") }) test_that("UDP6", { skip_without_ipv6() skip_without_ipv6_connection() skip_without_program("socat") skip_if_no_processx() nc <- processx::process$new( "socat", c("-", "UDP6:2001\\:4860\\:4860\\:8888:53"), stdin = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() deadline <- Sys.time() + as.difftime(5, units = "secs") while (Sys.time() < deadline && ! 53 %in% (cl <- ps_connections(p))$rport) { Sys.sleep(.1) } expect_true(deadline > Sys.time()) cl <- cl[!is.na(cl$rport) & cl$rport == 53, ] expect_equal(nrow(cl), 1) expect_equal(cl$family, "AF_INET6") expect_equal(cl$type, "SOCK_DGRAM") expect_match(cl$raddr, "2001:4860:4860:8888", fixed = TRUE) }) test_that("UDP6 on loopback", { skip_without_program("socat") skip_if_no_processx() skip_without_ipv6() nc <- processx::process$new( "socat", c("-d", "-d", "-ls", "-", "UDP6-LISTEN:0"), stdin = "|", stderr = "|") on.exit(cleanup_process(nc), add = TRUE) p <- nc$as_ps_handle() wait_for_string(nc, "listening on", timeout = 2000) cl <- ps_connections(p) cl <- cl[!is.na(cl$lport) & cl$type == "SOCK_DGRAM", ] port <- cl$lport expect_equal(cl$family, "AF_INET6") expect_equal(cl$type, "SOCK_DGRAM") nc2 <- processx::process$new( "socat", c("-d", "-d", "-", paste0("UDP6-CONNECT:\\:\\:1:", port)), stdin = "|", stderr = "|") on.exit(cleanup_process(nc2), add = TRUE) p2 <- nc2$as_ps_handle() err <- FALSE tryCatch( wait_for_string(nc2, "starting data transfer", timeout = 2000), error = function(e) err <<- TRUE) if (err) skip("Could not bind to IPv6 address") cl2 <- ps_connections(p2) cl2 <- cl2[!is.na(cl2$rport & cl2$rport == port), ] expect_equal(cl2$family, "AF_INET6") expect_equal(cl2$type, "SOCK_DGRAM") })