Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add randomPort function, and add quiet option for startServer #234

Merged
merged 4 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Suggests:
Collate:
'RcppExports.R'
'httpuv.R'
'random_port.R'
'server.R'
'static_paths.R'
'utils.R'
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export(getRNGState)
export(interrupt)
export(ipFamily)
export(listServers)
export(randomPort)
export(rawToBase64)
export(runServer)
export(service)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ httpuv 1.5.1.9001

* Fixed [#224](https://github.com/rstudio/httpuv/issues/224): Static file serving on Windows did not work correctly if it was from a path that contained non-ASCII characters. ([#227](https://github.com/rstudio/httpuv/pull/227))

* Resolved [#194](https://github.com/rstudio/httpuv/issues/194), [#233](https://github.com/rstudio/httpuv/issues/233): Added a `quiet` option to `startServer`, which suppresses startup error messages that are normally printed to console (and can't be intercepted with `capture.output()`). ([#234](https://github.com/rstudio/httpuv/pull/234))

* Added a new function `randomPort()`, which returns a random available port for listening on. ([#234](https://github.com/rstudio/httpuv/pull/234))

* Added a new (unexported) function `logLevel()`, for controlling debugging information that will be printed to the console. Previously, httpuv occasionally printed messages like `ERROR: [uv_write] broken pipe` and `ERROR: [uv_write] bad file descriptor` by default. This happened when the server tried to write to a pipe that was already closed, but the situation was not harmful, and was already being handled correctly. Now these messages are printed only if the log level is set to `INFO` or `DEBUG`. ([#223](https://github.com/rstudio/httpuv/pull/223))

httpuv 1.5.1
Expand Down
8 changes: 4 additions & 4 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ closeWS <- function(conn, code, reason) {
invisible(.Call('_httpuv_closeWS', PACKAGE = 'httpuv', conn, code, reason))
}

makeTcpServer <- function(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions) {
.Call('_httpuv_makeTcpServer', PACKAGE = 'httpuv', host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions)
makeTcpServer <- function(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet) {
.Call('_httpuv_makeTcpServer', PACKAGE = 'httpuv', host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet)
}

makePipeServer <- function(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions) {
.Call('_httpuv_makePipeServer', PACKAGE = 'httpuv', name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions)
makePipeServer <- function(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet) {
.Call('_httpuv_makePipeServer', PACKAGE = 'httpuv', name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet)
}

stopServer_ <- function(handle) {
Expand Down
16 changes: 9 additions & 7 deletions R/httpuv.R
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,10 @@ WebSocket <- R6Class(
#' X, port numbers smaller than 1025 require root privileges.
#' @param app A collection of functions that define your application. See
#' Details.
#' @param quiet If \code{TRUE}, suppress error messages from starting app.
#' @return A handle for this server that can be passed to
#' \code{\link{stopServer}} to shut the server down.
#'

#' @details \code{startServer} binds the specified port and listens for
#' connections on an thread running in the background. This background thread
#' handles the I/O, and when it receives a HTTP request, it will schedule a
Expand Down Expand Up @@ -525,8 +525,8 @@ WebSocket <- R6Class(
#' s$stop()
#' }
#' @export
startServer <- function(host, port, app) {
WebServer$new(host, port, app)
startServer <- function(host, port, app, quiet = FALSE) {
WebServer$new(host, port, app, quiet)
}

#' @param name A string that indicates the path for the domain socket (on
Expand All @@ -539,8 +539,8 @@ startServer <- function(host, port, app) {
#' umask is left unchanged. (This parameter has no effect on Windows.)
#' @rdname startServer
#' @export
startPipeServer <- function(name, mask, app) {
PipeServer$new(name, mask, app)
startPipeServer <- function(name, mask, app, quiet = FALSE) {
PipeServer$new(name, mask, app, quiet)
}

#' Process requests
Expand Down Expand Up @@ -613,8 +613,10 @@ service <- function(timeoutMs = ifelse(interactive(), 100, 1000)) {
#' If you have multiple hosts and/or ports to listen on, call the individual
#' functions instead of \code{runServer}.
#'
#' @param host A string that is a valid IPv4 address that is owned by this
#' server, or \code{"0.0.0.0"} to listen on all IP addresses.
#' @param host A string that is a valid IPv4 or IPv6 address that is owned by
#' this server, which the application will listen on. \code{"0.0.0.0"}
#' represents all IPv4 addresses and \code{"::/0"} represents all IPv6
#' addresses.
#' @param port A number or integer that indicates the server port that should be
#' listened on. Note that on most Unix-like systems including Linux and Mac OS
#' X, port numbers smaller than 1025 require root privileges.
Expand Down
117 changes: 117 additions & 0 deletions R/random_port.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#' Find an open TCP port
#'
#' Finds a random available TCP port for listening on, within a specified range
#' of ports. The default range of ports to check is 1024 to 49151, which is the
#' set of TCP User Ports. This function automatically excludes some ports which
#' are considered unsafe by web browsers.
#'
#' @inheritParams runServer
#' @param min Minimum port number.
#' @param max Maximum port number.
#' @param n Number of ports to try before giving up.
cpsievert marked this conversation as resolved.
Show resolved Hide resolved
#'
#' @return A port that is available to listen on.
#'
#' @examples
#' \dontrun{
#' s <- startServer("127.0.0.1", randomPort(), list())
#' browseURL(paste0("http://127.0.0.1:", s$getPort()))
#'
#' s$stop()
#' }
#'
#' @export
randomPort <- function(min = 1024L, max = 49151L, host = "127.0.0.1", n = 20) {
valid_ports <- setdiff(seq.int(min, max), unsafe_ports)

n <- min(n, length(valid_ports))
# Try up to n ports
for (port in sample(valid_ports, n)) {
s <- NULL

# Check if port is open
tryCatch(
s <- startServer(host, port, list(), quiet = TRUE),
error = function(e) { }
)
if (!is.null(s)) {
s$stop()
return(port)
}
}

stop("Cannot find an available port.")
}

# Ports that are considered unsafe by Chrome
# http://superuser.com/questions/188058/which-ports-are-considered-unsafe-on-chrome
# https://github.com/rstudio/shiny/issues/1784
unsafe_ports <- c(
1,
7,
9,
11,
13,
15,
17,
19,
20,
21,
22,
23,
25,
37,
42,
43,
53,
77,
79,
87,
95,
101,
102,
103,
104,
109,
110,
111,
113,
115,
117,
119,
123,
135,
139,
143,
179,
389,
427,
465,
512,
513,
514,
515,
526,
530,
531,
532,
540,
548,
556,
563,
587,
601,
636,
993,
995,
2049,
3659,
4045,
6000,
6665,
6666,
6667,
6668,
6669,
6697
)
10 changes: 6 additions & 4 deletions R/server.R
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ WebServer <- R6Class("WebServer",
cloneable = FALSE,
inherit = Server,
public = list(
initialize = function(host, port, app) {
initialize = function(host, port, app, quiet = FALSE) {
private$host <- host
private$port <- port
private$appWrapper <- AppWrapper$new(app)
Expand All @@ -175,7 +175,8 @@ WebServer <- R6Class("WebServer",
private$appWrapper$onWSMessage,
private$appWrapper$onWSClose,
private$appWrapper$staticPaths,
private$appWrapper$staticPathOptions
private$appWrapper$staticPathOptions,
quiet
)

if (is.null(private$handle)) {
Expand Down Expand Up @@ -251,7 +252,7 @@ PipeServer <- R6Class("PipeServer",
cloneable = FALSE,
inherit = Server,
public = list(
initialize = function(name, mask, app) {
initialize = function(name, mask, app, quiet = FALSE) {
if (is.null(mask)) {
mask <- -1
}
Expand All @@ -267,7 +268,8 @@ PipeServer <- R6Class("PipeServer",
private$appWrapper$onWSMessage,
private$appWrapper$onWSClose,
private$appWrapper$staticPaths,
private$appWrapper$staticPathOptions
private$appWrapper$staticPathOptions,
quiet
)

# Save the full path. normalizePath must be called after makePipeServer
Expand Down
38 changes: 38 additions & 0 deletions man/randomPort.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/runServer.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion man/startDaemonizedServer.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/startServer.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 10 additions & 8 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ BEGIN_RCPP
END_RCPP
}
// makeTcpServer
Rcpp::RObject makeTcpServer(const std::string& host, int port, Rcpp::Function onHeaders, Rcpp::Function onBodyData, Rcpp::Function onRequest, Rcpp::Function onWSOpen, Rcpp::Function onWSMessage, Rcpp::Function onWSClose, Rcpp::List staticPaths, Rcpp::List staticPathOptions);
RcppExport SEXP _httpuv_makeTcpServer(SEXP hostSEXP, SEXP portSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP, SEXP staticPathsSEXP, SEXP staticPathOptionsSEXP) {
Rcpp::RObject makeTcpServer(const std::string& host, int port, Rcpp::Function onHeaders, Rcpp::Function onBodyData, Rcpp::Function onRequest, Rcpp::Function onWSOpen, Rcpp::Function onWSMessage, Rcpp::Function onWSClose, Rcpp::List staticPaths, Rcpp::List staticPathOptions, bool quiet);
RcppExport SEXP _httpuv_makeTcpServer(SEXP hostSEXP, SEXP portSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP, SEXP staticPathsSEXP, SEXP staticPathOptionsSEXP, SEXP quietSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Expand All @@ -45,13 +45,14 @@ BEGIN_RCPP
Rcpp::traits::input_parameter< Rcpp::Function >::type onWSClose(onWSCloseSEXP);
Rcpp::traits::input_parameter< Rcpp::List >::type staticPaths(staticPathsSEXP);
Rcpp::traits::input_parameter< Rcpp::List >::type staticPathOptions(staticPathOptionsSEXP);
rcpp_result_gen = Rcpp::wrap(makeTcpServer(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions));
Rcpp::traits::input_parameter< bool >::type quiet(quietSEXP);
rcpp_result_gen = Rcpp::wrap(makeTcpServer(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet));
return rcpp_result_gen;
END_RCPP
}
// makePipeServer
Rcpp::RObject makePipeServer(const std::string& name, int mask, Rcpp::Function onHeaders, Rcpp::Function onBodyData, Rcpp::Function onRequest, Rcpp::Function onWSOpen, Rcpp::Function onWSMessage, Rcpp::Function onWSClose, Rcpp::List staticPaths, Rcpp::List staticPathOptions);
RcppExport SEXP _httpuv_makePipeServer(SEXP nameSEXP, SEXP maskSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP, SEXP staticPathsSEXP, SEXP staticPathOptionsSEXP) {
Rcpp::RObject makePipeServer(const std::string& name, int mask, Rcpp::Function onHeaders, Rcpp::Function onBodyData, Rcpp::Function onRequest, Rcpp::Function onWSOpen, Rcpp::Function onWSMessage, Rcpp::Function onWSClose, Rcpp::List staticPaths, Rcpp::List staticPathOptions, bool quiet);
RcppExport SEXP _httpuv_makePipeServer(SEXP nameSEXP, SEXP maskSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP, SEXP staticPathsSEXP, SEXP staticPathOptionsSEXP, SEXP quietSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Expand All @@ -65,7 +66,8 @@ BEGIN_RCPP
Rcpp::traits::input_parameter< Rcpp::Function >::type onWSClose(onWSCloseSEXP);
Rcpp::traits::input_parameter< Rcpp::List >::type staticPaths(staticPathsSEXP);
Rcpp::traits::input_parameter< Rcpp::List >::type staticPathOptions(staticPathOptionsSEXP);
rcpp_result_gen = Rcpp::wrap(makePipeServer(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions));
Rcpp::traits::input_parameter< bool >::type quiet(quietSEXP);
rcpp_result_gen = Rcpp::wrap(makePipeServer(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose, staticPaths, staticPathOptions, quiet));
return rcpp_result_gen;
END_RCPP
}
Expand Down Expand Up @@ -251,8 +253,8 @@ RcppExport SEXP httpuv_decodeURIComponent(SEXP);
static const R_CallMethodDef CallEntries[] = {
{"_httpuv_sendWSMessage", (DL_FUNC) &_httpuv_sendWSMessage, 3},
{"_httpuv_closeWS", (DL_FUNC) &_httpuv_closeWS, 3},
{"_httpuv_makeTcpServer", (DL_FUNC) &_httpuv_makeTcpServer, 10},
{"_httpuv_makePipeServer", (DL_FUNC) &_httpuv_makePipeServer, 10},
{"_httpuv_makeTcpServer", (DL_FUNC) &_httpuv_makeTcpServer, 11},
{"_httpuv_makePipeServer", (DL_FUNC) &_httpuv_makePipeServer, 11},
{"_httpuv_stopServer_", (DL_FUNC) &_httpuv_stopServer_, 1},
{"_httpuv_getStaticPaths_", (DL_FUNC) &_httpuv_getStaticPaths_, 1},
{"_httpuv_setStaticPaths_", (DL_FUNC) &_httpuv_setStaticPaths_, 2},
Expand Down
Loading