diff --git a/DESCRIPTION b/DESCRIPTION index 04df5f9d..d4b16095 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: httpuv Type: Package Title: HTTP and WebSocket server library -Version: 1.3.1 +Version: 1.3.1.9000 Date: 2014-07-11 Author: RStudio, Inc. Copyright: RStudio, Inc.; Joyent, Inc.; Nginx Inc.; Igor Sysoev; Niels Provos; diff --git a/NAMESPACE b/NAMESPACE index 1619ba6a..47934588 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,3 +1,9 @@ +# Generated by roxygen2 (4.0.2): do not edit by hand + +export(decodeURI) +export(decodeURIComponent) +export(encodeURI) +export(encodeURIComponent) export(interrupt) export(rawToBase64) export(runServer) @@ -7,7 +13,7 @@ export(startPipeServer) export(startServer) export(stopDaemonizedServer) export(stopServer) +exportClasses(WebSocket) import(methods) importFrom(Rcpp,evalCpp) -export(WebSocket) useDynLib(httpuv) diff --git a/NEWS b/NEWS index afb9052a..54b84faa 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,10 @@ +httpuv 1.3.1.9000 +------------------------------------------------------------------------ + +* Add encodeURI, encodeURIComponent, decodeURI, and decodeURIComponent + functions. + + httpuv 1.3.1 ------------------------------------------------------------------------ diff --git a/R/RcppExports.R b/R/RcppExports.R index 85460ca8..2039f1a4 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -41,3 +41,55 @@ destroyDaemonizedServer <- function(handle) { invisible(.Call('httpuv_destroyDaemonizedServer', PACKAGE = 'httpuv', handle)) } +#' URI encoding/decoding +#' +#' Encodes/decodes strings using URI encoding/decoding in the same way that web +#' browsers do. The precise behaviors of these functions can be found at +#' developer.mozilla.org: +#' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI}{encodeURI}, +#' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent}{encodeURIComponent}, +#' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI}{decodeURI}, +#' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent}{decodeURIComponent} +#' +#' Intended as a faster replacement for \code{\link[utils]{URLencode}} and +#' \code{\link[utils]{URLdecode}}. +#' +#' encodeURI differs from encodeURIComponent in that the former will not encode +#' reserved characters: \code{;,/?:@@&=+$} +#' +#' decodeURI differs from decodeURIComponent in that it will refuse to decode +#' encoded sequences that decode to a reserved character. (If in doubt, use +#' decodeURIComponent.) +#' +#' The only way these functions differ from web browsers is in the encoding of +#' non-ASCII characters. All non-ASCII characters will be escaped byte-by-byte. +#' If conformant non-ASCII behavior is important, ensure that your input vector +#' is UTF-8 encoded before calling encodeURI or encodeURIComponent. +#' +#' @param value Character vector to be encoded or decoded. +#' @return Encoded or decoded character vector of the same length as the +#' input value. +#' +#' @export +encodeURI <- function(value) { + .Call('httpuv_encodeURI', PACKAGE = 'httpuv', value) +} + +#' @rdname encodeURI +#' @export +encodeURIComponent <- function(value) { + .Call('httpuv_encodeURIComponent', PACKAGE = 'httpuv', value) +} + +#' @rdname encodeURI +#' @export +decodeURI <- function(value) { + .Call('httpuv_decodeURI', PACKAGE = 'httpuv', value) +} + +#' @rdname encodeURI +#' @export +decodeURIComponent <- function(value) { + .Call('httpuv_decodeURIComponent', PACKAGE = 'httpuv', value) +} + diff --git a/man/WebSocket-class.Rd b/man/WebSocket-class.Rd new file mode 100644 index 00000000..79edf43e --- /dev/null +++ b/man/WebSocket-class.Rd @@ -0,0 +1,55 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand +\docType{class} +\name{WebSocket-class} +\alias{WebSocket} +\alias{WebSocket-class} +\title{WebSocket object} +\arguments{ +\item{...}{For internal use only.} +} +\description{ +An object that represents a single WebSocket connection. The object can be +used to send messages and close the connection, and to receive notifications +when messages are received or the connection is closed. +} +\details{ +WebSocket objects should never be created directly. They are obtained by +passing an \code{onWSOpen} function to \code{\link{startServer}}. +} +\section{Fields}{ + + + \describe{ + \item{\code{request}}{ + The Rook request environment that opened the connection. This can be + used to inspect HTTP headers, for example. + } + } +} + +\section{Methods}{ + + + \describe{ + \item{\code{onMessage(func)}}{ + Registers a callback function that will be invoked whenever a message + is received on this connection. The callback function will be invoked + with two arguments. The first argument is \code{TRUE} if the message + is binary and \code{FALSE} if it is text. The second argument is either + a raw vector (if the message is binary) or a character vector. + } + \item{\code{onClose(func)}}{ + Registers a callback function that will be invoked when the connection + is closed. + } + \item{\code{send(message)}}{ + Begins sending the given message over the websocket. The message must + be either a raw vector, or a single-element character vector that is + encoded in UTF-8. + } + \item{\code{close()}}{ + Closes the websocket connection. + } + } +} + diff --git a/man/WebSocket.Rd b/man/WebSocket.Rd deleted file mode 100644 index c995d517..00000000 --- a/man/WebSocket.Rd +++ /dev/null @@ -1,43 +0,0 @@ -\name{WebSocket} -\alias{WebSocket} -\title{WebSocket object} -\usage{ - WebSocket(...) -} -\arguments{ - \item{...}{For internal use only.} -} -\description{ - An object that represents a single WebSocket connection. - The object can be used to send messages and close the - connection, and to receive notifications when messages - are received or the connection is closed. -} -\details{ - WebSocket objects should never be created directly. They - are obtained by passing an \code{onWSOpen} function to - \code{\link{startServer}}. -} -\section{Fields}{ - \describe{ \item{\code{request}}{ The Rook request - environment that opened the connection. This can be used - to inspect HTTP headers, for example. } } -} - -\section{Methods}{ - \describe{ \item{\code{onMessage(func)}}{ Registers a - callback function that will be invoked whenever a message - is received on this connection. The callback function - will be invoked with two arguments. The first argument is - \code{TRUE} if the message is binary and \code{FALSE} if - it is text. The second argument is either a raw vector - (if the message is binary) or a character vector. } - \item{\code{onClose(func)}}{ Registers a callback - function that will be invoked when the connection is - closed. } \item{\code{send(message)}}{ Begins sending - the given message over the websocket. The message must be - either a raw vector, or a single-element character vector - that is encoded in UTF-8. } \item{\code{close()}}{ - Closes the websocket connection. } } -} - diff --git a/man/encodeURI.Rd b/man/encodeURI.Rd new file mode 100644 index 00000000..7ff25992 --- /dev/null +++ b/man/encodeURI.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand +\name{encodeURI} +\alias{decodeURI} +\alias{decodeURIComponent} +\alias{encodeURI} +\alias{encodeURIComponent} +\title{URI encoding/decoding} +\usage{ +encodeURI(value) + +encodeURIComponent(value) + +decodeURI(value) + +decodeURIComponent(value) +} +\arguments{ +\item{value}{Character vector to be encoded or decoded.} +} +\value{ +Encoded or decoded character vector of the same length as the + input value. +} +\description{ +Encodes/decodes strings using URI encoding/decoding in the same way that web +browsers do. The precise behaviors of these functions can be found at +developer.mozilla.org: +\href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI}{encodeURI}, +\href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent}{encodeURIComponent}, +\href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI}{decodeURI}, +\href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent}{decodeURIComponent} +} +\details{ +Intended as a faster replacement for \code{\link[utils]{URLencode}} and +\code{\link[utils]{URLdecode}}. + +encodeURI differs from encodeURIComponent in that the former will not encode +reserved characters: \code{;,/?:@&=+$} + +decodeURI differs from decodeURIComponent in that it will refuse to decode +encoded sequences that decode to a reserved character. (If in doubt, use +decodeURIComponent.) + +The only way these functions differ from web browsers is in the encoding of +non-ASCII characters. All non-ASCII characters will be escaped byte-by-byte. +If conformant non-ASCII behavior is important, ensure that your input vector +is UTF-8 encoded before calling encodeURI or encodeURIComponent. +} + diff --git a/man/httpuv-package.Rd b/man/httpuv-package.Rd index 00a395c6..530a41e8 100644 --- a/man/httpuv-package.Rd +++ b/man/httpuv-package.Rd @@ -1,23 +1,21 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \docType{package} \name{httpuv-package} \alias{httpuv} \alias{httpuv-package} \title{HTTP and WebSocket server} \description{ - HTTP and WebSocket server +HTTP and WebSocket server } \details{ - Allows R code to listen for and interact with HTTP and - WebSocket clients, so you can serve web traffic directly - out of your R process. Implementation is based on - \href{https://github.com/joyent/libuv}{libuv} and - \href{https://github.com/joyent/http-parser}{http-parser}. +Allows R code to listen for and interact with HTTP and WebSocket clients, so +you can serve web traffic directly out of your R process. Implementation is +based on \href{https://github.com/joyent/libuv}{libuv} and +\href{https://github.com/joyent/http-parser}{http-parser}. - This is a low-level library that provides little more - than network I/O and implementations of the HTTP and - WebSocket protocols. For an easy way to create web - applications, try \href{http://rstudio.com/shiny/}{Shiny} - instead. +This is a low-level library that provides little more than network I/O and +implementations of the HTTP and WebSocket protocols. For an easy way to +create web applications, try \href{http://rstudio.com/shiny/}{Shiny} instead. } \examples{ \dontrun{ @@ -25,10 +23,10 @@ demo("echo", package="httpuv") } } \author{ - Joe Cheng \email{joe@rstudio.com} +Joe Cheng \email{joe@rstudio.com} } \seealso{ - startServer +startServer } \keyword{package} diff --git a/man/interrupt.Rd b/man/interrupt.Rd index 4187d574..11ecabd6 100644 --- a/man/interrupt.Rd +++ b/man/interrupt.Rd @@ -1,15 +1,15 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{interrupt} \alias{interrupt} \title{Interrupt httpuv runloop} \usage{ - interrupt() +interrupt() } \description{ - Interrupts the currently running httpuv runloop, meaning - \code{\link{runServer}} or \code{\link{service}} will - return control back to the caller and no further tasks - will be processed until those methods are called again. - Note that this may cause in-process uploads or downloads - to be interrupted in mid-request. +Interrupts the currently running httpuv runloop, meaning +\code{\link{runServer}} or \code{\link{service}} will return control back to +the caller and no further tasks will be processed until those methods are +called again. Note that this may cause in-process uploads or downloads to be +interrupted in mid-request. } diff --git a/man/rawToBase64.Rd b/man/rawToBase64.Rd index 981381d6..6f96aa65 100644 --- a/man/rawToBase64.Rd +++ b/man/rawToBase64.Rd @@ -1,15 +1,16 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{rawToBase64} \alias{rawToBase64} \title{Convert raw vector to Base64-encoded string} \usage{ - rawToBase64(x) +rawToBase64(x) } \arguments{ - \item{x}{A raw vector.} +\item{x}{A raw vector.} } \description{ - Converts a raw vector to its Base64 encoding as a - single-element character vector. +Converts a raw vector to its Base64 encoding as a single-element character +vector. } \examples{ set.seed(100) diff --git a/man/runServer.Rd b/man/runServer.Rd index 22051f42..a447da44 100644 --- a/man/runServer.Rd +++ b/man/runServer.Rd @@ -1,40 +1,37 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{runServer} \alias{runServer} \title{Run a server} \usage{ - runServer(host, port, app, - interruptIntervalMs = ifelse(interactive(), 100, 1000)) +runServer(host, port, app, interruptIntervalMs = ifelse(interactive(), 100, + 1000)) } \arguments{ - \item{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.} +\item{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.} - \item{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.} +\item{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.} - \item{app}{A collection of functions that define your - application. See Details.} +\item{app}{A collection of functions that define your application. See +Details.} - \item{interruptIntervalMs}{How often to check for - interrupt. The default should be appropriate for most - situations.} +\item{interruptIntervalMs}{How often to check for interrupt. The default + should be appropriate for most situations.} } \description{ - This is a convenience function that provides a simple way - to call \code{\link{startServer}}, \code{\link{service}}, - and \code{\link{stopServer}} in the correct sequence. It - does not return unless interrupted or an error occurs. +This is a convenience function that provides a simple way to call +\code{\link{startServer}}, \code{\link{service}}, and +\code{\link{stopServer}} in the correct sequence. It does not return unless +interrupted or an error occurs. } \details{ - If you have multiple hosts and/or ports to listen on, - call the individual functions instead of - \code{runServer}. +If you have multiple hosts and/or ports to listen on, call the individual +functions instead of \code{runServer}. } \seealso{ - \code{\link{startServer}}, \code{\link{service}}, +\code{\link{startServer}}, \code{\link{service}}, \code{\link{stopServer}} } diff --git a/man/service.Rd b/man/service.Rd index 5957c876..947d48a9 100644 --- a/man/service.Rd +++ b/man/service.Rd @@ -1,27 +1,24 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{service} \alias{service} \title{Process requests} \usage{ - service(timeoutMs = ifelse(interactive(), 100, 1000)) +service(timeoutMs = ifelse(interactive(), 100, 1000)) } \arguments{ - \item{timeoutMs}{Approximate number of milliseconds to - run before returning. If 0, then the function will - continually process requests without returning unless an - error occurs.} +\item{timeoutMs}{Approximate number of milliseconds to run before returning. + If 0, then the function will continually process requests without returning + unless an error occurs.} } \description{ - Process HTTP requests and WebSocket messages. Even if a - server exists, no requests are serviced unless and until - \code{service} is called. +Process HTTP requests and WebSocket messages. Even if a server exists, no +requests are serviced unless and until \code{service} is called. } \details{ - Note that while \code{service} is waiting for a new - request, the process is not interruptible using normal R - means (Esc, Ctrl+C, etc.). If being interruptible is a - requirement, then call \code{service} in a while loop - with a very short but non-zero \code{\link{Sys.sleep}} - during each iteration. +Note that while \code{service} is waiting for a new request, the process is +not interruptible using normal R means (Esc, Ctrl+C, etc.). If being +interruptible is a requirement, then call \code{service} in a while loop +with a very short but non-zero \code{\link{Sys.sleep}} during each iteration. } \examples{ \dontrun{ diff --git a/man/startDaemonizedServer.Rd b/man/startDaemonizedServer.Rd index 9d9b1459..82d5c079 100644 --- a/man/startDaemonizedServer.Rd +++ b/man/startDaemonizedServer.Rd @@ -1,64 +1,57 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{startDaemonizedServer} \alias{startDaemonizedServer} \title{Create an HTTP/WebSocket daemonized server (experimental)} \usage{ - startDaemonizedServer(host, port, app) +startDaemonizedServer(host, port, app) } \arguments{ - \item{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.} +\item{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.} - \item{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.} +\item{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.} - \item{app}{A collection of functions that define your - application. See Details.} +\item{app}{A collection of functions that define your application. See +Details.} } \value{ - A handle for this server that can be passed to - \code{\link{stopDaemonizedServer}} to shut the server - down. +A handle for this server that can be passed to + \code{\link{stopDaemonizedServer}} to shut the server down. } \description{ - Creates an HTTP/WebSocket server on the specified host - and port. The server is daemonized so R interactive - sessions are not blocked to handle requests. +Creates an HTTP/WebSocket server on the specified host and port. The server is daemonized +so R interactive sessions are not blocked to handle requests. } \details{ - In contrast to servers created by - \code{\link{startServer}}, calls to \code{\link{service}} - are not needed to accept and handle connections. If the - port cannot be bound (most likely due to permissions or - because it is already bound), an error is raised. +In contrast to servers created by \code{\link{startServer}}, calls to \code{\link{service}} + are not needed to accept and handle connections. If the port + cannot be bound (most likely due to permissions or because it is already + bound), an error is raised. - The \code{app} parameter is where your application logic - will be provided to the server. This can be a list, - environment, or reference class that contains the - following named functions/methods: + The \code{app} parameter is where your application logic will be provided + to the server. This can be a list, environment, or reference class that + contains the following named functions/methods: - \describe{ \item{\code{call(req)}}{Process the given HTTP - request, and return an HTTP response. This method should - be implemented in accordance with the - \href{https://github.com/jeffreyhorner/Rook/blob/a5e45f751/README.md}{Rook} - specification.} \item{\code{onHeaders(req)}}{Optional. - Similar to \code{call}, but occurs when headers are - received. Return \code{NULL} to continue normal - processing of the request, or a Rook response to send - that response, stop processing the request, and ask the - client to close the connection. (This can be used to - implement upload size limits, for example.)} - \item{\code{onWSOpen(ws)}}{Called back when a WebSocket - connection is established. The given object can be used - to be notified when a message is received from the - client, to send messages to the client, etc. See - \code{\link{WebSocket}}.} } + \describe{ + \item{\code{call(req)}}{Process the given HTTP request, and return an + HTTP response. This method should be implemented in accordance with the + \href{https://github.com/jeffreyhorner/Rook/blob/a5e45f751/README.md}{Rook} + specification.} + \item{\code{onHeaders(req)}}{Optional. Similar to \code{call}, but occurs + when headers are received. Return \code{NULL} to continue normal + processing of the request, or a Rook response to send that response, + stop processing the request, and ask the client to close the connection. + (This can be used to implement upload size limits, for example.)} + \item{\code{onWSOpen(ws)}}{Called back when a WebSocket connection is established. + The given object can be used to be notified when a message is received from + the client, to send messages to the client, etc. See \code{\link{WebSocket}}.} + } The \code{startPipeServer} variant is not supported yet. } \seealso{ - \code{\link{startServer}} +\code{\link{startServer}} } diff --git a/man/startServer.Rd b/man/startServer.Rd index 13bbe514..bb564f20 100644 --- a/man/startServer.Rd +++ b/man/startServer.Rd @@ -1,81 +1,72 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{startServer} \alias{startPipeServer} \alias{startServer} \title{Create an HTTP/WebSocket server} \usage{ - startServer(host, port, app) +startServer(host, port, app) - startPipeServer(name, mask, app) +startPipeServer(name, mask, app) } \arguments{ - \item{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.} +\item{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.} - \item{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.} +\item{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.} - \item{app}{A collection of functions that define your - application. See Details.} +\item{app}{A collection of functions that define your application. See +Details.} - \item{name}{A string that indicates the path for the - domain socket (on Unix-like systems) or the name of the - named pipe (on Windows).} +\item{name}{A string that indicates the path for the domain socket (on +Unix-like systems) or the name of the named pipe (on Windows).} - \item{mask}{If non-\code{NULL} and non-negative, this - numeric value is used to temporarily modify the process's - umask while the domain socket is being created. To ensure - that only root can access the domain socket, use - \code{strtoi("777", 8)}; or to allow owner and group - read/write access, use \code{strtoi("117", 8)}. If the - value is \code{NULL} then the process's umask is left - unchanged. (This parameter has no effect on Windows.)} +\item{mask}{If non-\code{NULL} and non-negative, this numeric value is used +to temporarily modify the process's umask while the domain socket is being +created. To ensure that only root can access the domain socket, use +\code{strtoi("777", 8)}; or to allow owner and group read/write access, use +\code{strtoi("117", 8)}. If the value is \code{NULL} then the process's +umask is left unchanged. (This parameter has no effect on Windows.)} } \value{ - A handle for this server that can be passed to +A handle for this server that can be passed to \code{\link{stopServer}} to shut the server down. } \description{ - Creates an HTTP/WebSocket server on the specified host - and port. +Creates an HTTP/WebSocket server on the specified host and port. } \details{ - \code{startServer} binds the specified port, but no - connections are actually accepted. See - \code{\link{service}}, which should be called repeatedly - in order to actually accept and handle connections. If - the port cannot be bound (most likely due to permissions - or because it is already bound), an error is raised. +\code{startServer} binds the specified port, but no connections are + actually accepted. See \code{\link{service}}, which should be called + repeatedly in order to actually accept and handle connections. If the port + cannot be bound (most likely due to permissions or because it is already + bound), an error is raised. - The \code{app} parameter is where your application logic - will be provided to the server. This can be a list, - environment, or reference class that contains the - following named functions/methods: + The \code{app} parameter is where your application logic will be provided + to the server. This can be a list, environment, or reference class that + contains the following named functions/methods: - \describe{ \item{\code{call(req)}}{Process the given HTTP - request, and return an HTTP response. This method should - be implemented in accordance with the - \href{https://github.com/jeffreyhorner/Rook/blob/a5e45f751/README.md}{Rook} - specification.} \item{\code{onHeaders(req)}}{Optional. - Similar to \code{call}, but occurs when headers are - received. Return \code{NULL} to continue normal - processing of the request, or a Rook response to send - that response, stop processing the request, and ask the - client to close the connection. (This can be used to - implement upload size limits, for example.)} - \item{\code{onWSOpen(ws)}}{Called back when a WebSocket - connection is established. The given object can be used - to be notified when a message is received from the - client, to send messages to the client, etc. See - \code{\link{WebSocket}}.} } + \describe{ + \item{\code{call(req)}}{Process the given HTTP request, and return an + HTTP response. This method should be implemented in accordance with the + \href{https://github.com/jeffreyhorner/Rook/blob/a5e45f751/README.md}{Rook} + specification.} + \item{\code{onHeaders(req)}}{Optional. Similar to \code{call}, but occurs + when headers are received. Return \code{NULL} to continue normal + processing of the request, or a Rook response to send that response, + stop processing the request, and ask the client to close the connection. + (This can be used to implement upload size limits, for example.)} + \item{\code{onWSOpen(ws)}}{Called back when a WebSocket connection is established. + The given object can be used to be notified when a message is received from + the client, to send messages to the client, etc. See \code{\link{WebSocket}}.} + } The \code{startPipeServer} variant can be used instead of - \code{startServer} to listen on a Unix domain socket or - named pipe rather than a TCP socket (this is not common). + \code{startServer} to listen on a Unix domain socket or named pipe rather + than a TCP socket (this is not common). } \seealso{ - \code{\link{runServer}} +\code{\link{runServer}} } diff --git a/man/stopDaemonizedServer.Rd b/man/stopDaemonizedServer.Rd index 499bd7a1..09e1ac29 100644 --- a/man/stopDaemonizedServer.Rd +++ b/man/stopDaemonizedServer.Rd @@ -1,20 +1,19 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{stopDaemonizedServer} \alias{stopDaemonizedServer} \title{Stop a running daemonized server in Unix environments} \usage{ - stopDaemonizedServer(server) +stopDaemonizedServer(server) } \arguments{ - \item{server}{A handle that was previously returned from +\item{server}{A handle that was previously returned from \code{\link{startDaemonizedServer}}.} } \description{ - Given a handle that was returned from a previous - invocation of \code{\link{startDaemonizedServer}}, closes - all open connections for that server, removes listeners - in the R event loop and unbinds the port. \strong{Be - careful not to call \code{stopDaemonizedServer} more than - once on a handle, as this will cause the R process to - crash!} +Given a handle that was returned from a previous invocation of +\code{\link{startDaemonizedServer}}, closes all open connections for that server, +removes listeners in the R event loop and +unbinds the port. \strong{Be careful not to call \code{stopDaemonizedServer} more than +once on a handle, as this will cause the R process to crash!} } diff --git a/man/stopServer.Rd b/man/stopServer.Rd index 6929577e..858874b2 100644 --- a/man/stopServer.Rd +++ b/man/stopServer.Rd @@ -1,19 +1,18 @@ +% Generated by roxygen2 (4.0.2): do not edit by hand \name{stopServer} \alias{stopServer} \title{Stop a running server} \usage{ - stopServer(handle) +stopServer(handle) } \arguments{ - \item{handle}{A handle that was previously returned from +\item{handle}{A handle that was previously returned from \code{\link{startServer}}.} } \description{ - Given a handle that was returned from a previous - invocation of \code{\link{startServer}}, closes all open - connections for that server and unbinds the port. - \strong{Be careful not to call \code{stopServer} more - than once on a handle, as this will cause the R process - to crash!} +Given a handle that was returned from a previous invocation of +\code{\link{startServer}}, closes all open connections for that server and +unbinds the port. \strong{Be careful not to call \code{stopServer} more than +once on a handle, as this will cause the R process to crash!} } diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index b308a076..70bc4109 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -9,11 +9,13 @@ using namespace Rcpp; void sendWSMessage(std::string conn, bool binary, Rcpp::RObject message); RcppExport SEXP httpuv_sendWSMessage(SEXP connSEXP, SEXP binarySEXP, SEXP messageSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - std::string conn = Rcpp::as(connSEXP); - bool binary = Rcpp::as(binarySEXP); - Rcpp::RObject message = Rcpp::as(messageSEXP); - sendWSMessage(conn, binary, message); + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::string >::type conn(connSEXP ); + Rcpp::traits::input_parameter< bool >::type binary(binarySEXP ); + Rcpp::traits::input_parameter< Rcpp::RObject >::type message(messageSEXP ); + sendWSMessage(conn, binary, message); + } return R_NilValue; END_RCPP } @@ -21,9 +23,11 @@ END_RCPP void closeWS(std::string conn); RcppExport SEXP httpuv_closeWS(SEXP connSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - std::string conn = Rcpp::as(connSEXP); - closeWS(conn); + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::string >::type conn(connSEXP ); + closeWS(conn); + } return R_NilValue; END_RCPP } @@ -31,43 +35,55 @@ END_RCPP 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); RcppExport SEXP httpuv_makeTcpServer(SEXP hostSEXP, SEXP portSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - std::string host = Rcpp::as(hostSEXP); - int port = Rcpp::as(portSEXP); - Rcpp::Function onHeaders = Rcpp::as(onHeadersSEXP); - Rcpp::Function onBodyData = Rcpp::as(onBodyDataSEXP); - Rcpp::Function onRequest = Rcpp::as(onRequestSEXP); - Rcpp::Function onWSOpen = Rcpp::as(onWSOpenSEXP); - Rcpp::Function onWSMessage = Rcpp::as(onWSMessageSEXP); - Rcpp::Function onWSClose = Rcpp::as(onWSCloseSEXP); - Rcpp::RObject __result = makeTcpServer(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose); - return Rcpp::wrap(__result); + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< const std::string& >::type host(hostSEXP ); + Rcpp::traits::input_parameter< int >::type port(portSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onHeaders(onHeadersSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onBodyData(onBodyDataSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onRequest(onRequestSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSOpen(onWSOpenSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSMessage(onWSMessageSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSClose(onWSCloseSEXP ); + Rcpp::RObject __result = makeTcpServer(host, port, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; 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); RcppExport SEXP httpuv_makePipeServer(SEXP nameSEXP, SEXP maskSEXP, SEXP onHeadersSEXP, SEXP onBodyDataSEXP, SEXP onRequestSEXP, SEXP onWSOpenSEXP, SEXP onWSMessageSEXP, SEXP onWSCloseSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - std::string name = Rcpp::as(nameSEXP); - int mask = Rcpp::as(maskSEXP); - Rcpp::Function onHeaders = Rcpp::as(onHeadersSEXP); - Rcpp::Function onBodyData = Rcpp::as(onBodyDataSEXP); - Rcpp::Function onRequest = Rcpp::as(onRequestSEXP); - Rcpp::Function onWSOpen = Rcpp::as(onWSOpenSEXP); - Rcpp::Function onWSMessage = Rcpp::as(onWSMessageSEXP); - Rcpp::Function onWSClose = Rcpp::as(onWSCloseSEXP); - Rcpp::RObject __result = makePipeServer(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose); - return Rcpp::wrap(__result); + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< const std::string& >::type name(nameSEXP ); + Rcpp::traits::input_parameter< int >::type mask(maskSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onHeaders(onHeadersSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onBodyData(onBodyDataSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onRequest(onRequestSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSOpen(onWSOpenSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSMessage(onWSMessageSEXP ); + Rcpp::traits::input_parameter< Rcpp::Function >::type onWSClose(onWSCloseSEXP ); + Rcpp::RObject __result = makePipeServer(name, mask, onHeaders, onBodyData, onRequest, onWSOpen, onWSMessage, onWSClose); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; END_RCPP } // destroyServer void destroyServer(std::string handle); RcppExport SEXP httpuv_destroyServer(SEXP handleSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - std::string handle = Rcpp::as(handleSEXP); - destroyServer(handle); + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::string >::type handle(handleSEXP ); + destroyServer(handle); + } return R_NilValue; END_RCPP } @@ -75,18 +91,25 @@ END_RCPP bool run(uint32_t timeoutMillis); RcppExport SEXP httpuv_run(SEXP timeoutMillisSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - uint32_t timeoutMillis = Rcpp::as(timeoutMillisSEXP); - bool __result = run(timeoutMillis); - return Rcpp::wrap(__result); + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< uint32_t >::type timeoutMillis(timeoutMillisSEXP ); + bool __result = run(timeoutMillis); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; END_RCPP } // stopLoop void stopLoop(); RcppExport SEXP httpuv_stopLoop() { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - stopLoop(); + { + Rcpp::RNGScope __rngScope; + stopLoop(); + } return R_NilValue; END_RCPP } @@ -94,10 +117,15 @@ END_RCPP std::string base64encode(const Rcpp::RawVector& x); RcppExport SEXP httpuv_base64encode(SEXP xSEXP) { BEGIN_RCPP - Rcpp::RNGScope __rngScope; - Rcpp::RawVector x = Rcpp::as(xSEXP); - std::string __result = base64encode(x); - return Rcpp::wrap(__result); + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< const Rcpp::RawVector& >::type x(xSEXP ); + std::string __result = base64encode(x); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; END_RCPP } // daemonize @@ -127,3 +155,63 @@ BEGIN_RCPP return R_NilValue; END_RCPP } +// encodeURI +std::vector encodeURI(std::vector value); +RcppExport SEXP httpuv_encodeURI(SEXP valueSEXP) { +BEGIN_RCPP + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::vector >::type value(valueSEXP ); + std::vector __result = encodeURI(value); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; +END_RCPP +} +// encodeURIComponent +std::vector encodeURIComponent(std::vector value); +RcppExport SEXP httpuv_encodeURIComponent(SEXP valueSEXP) { +BEGIN_RCPP + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::vector >::type value(valueSEXP ); + std::vector __result = encodeURIComponent(value); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; +END_RCPP +} +// decodeURI +std::vector decodeURI(std::vector value); +RcppExport SEXP httpuv_decodeURI(SEXP valueSEXP) { +BEGIN_RCPP + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::vector >::type value(valueSEXP ); + std::vector __result = decodeURI(value); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; +END_RCPP +} +// decodeURIComponent +std::vector decodeURIComponent(std::vector value); +RcppExport SEXP httpuv_decodeURIComponent(SEXP valueSEXP) { +BEGIN_RCPP + SEXP __sexp_result; + { + Rcpp::RNGScope __rngScope; + Rcpp::traits::input_parameter< std::vector >::type value(valueSEXP ); + std::vector __result = decodeURIComponent(value); + PROTECT(__sexp_result = Rcpp::wrap(__result)); + } + UNPROTECT(1); + return __sexp_result; +END_RCPP +} diff --git a/src/httpuv.cpp b/src/httpuv.cpp index 3da556d2..d28f07c4 100644 --- a/src/httpuv.cpp +++ b/src/httpuv.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -531,4 +532,205 @@ void destroyDaemonizedServer(std::string handle) { delete dServer; } +static std::string allowed = ";,/?:@&=+$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.!~*'()"; + +bool isReservedUrlChar(char c) { + switch (c) { + case ';': + case ',': + case '/': + case '?': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + return true; + default: + return false; + } +} + +bool needsEscape(char c, bool encodeReserved) { + if (c >= 'a' && c <= 'z') + return false; + if (c >= 'A' && c <= 'Z') + return false; + if (c >= '0' && c <= '9') + return false; + if (isReservedUrlChar(c)) + return encodeReserved; + switch (c) { + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + return false; + } + return true; +} + +std::string doEncodeURI(std::string value, bool encodeReserved) { + std::ostringstream os; + os << std::hex << std::uppercase; + for (std::string::const_iterator it = value.begin(); + it != value.end(); + it++) { + + if (!needsEscape(*it, encodeReserved)) { + os << *it; + } else { + os << '%' << std::setw(2) << (int)*it; + } + } + return os.str(); +} + +//' URI encoding/decoding +//' +//' Encodes/decodes strings using URI encoding/decoding in the same way that web +//' browsers do. The precise behaviors of these functions can be found at +//' developer.mozilla.org: +//' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI}{encodeURI}, +//' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent}{encodeURIComponent}, +//' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI}{decodeURI}, +//' \href{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent}{decodeURIComponent} +//' +//' Intended as a faster replacement for \code{\link[utils]{URLencode}} and +//' \code{\link[utils]{URLdecode}}. +//' +//' encodeURI differs from encodeURIComponent in that the former will not encode +//' reserved characters: \code{;,/?:@@&=+$} +//' +//' decodeURI differs from decodeURIComponent in that it will refuse to decode +//' encoded sequences that decode to a reserved character. (If in doubt, use +//' decodeURIComponent.) +//' +//' The only way these functions differ from web browsers is in the encoding of +//' non-ASCII characters. All non-ASCII characters will be escaped byte-by-byte. +//' If conformant non-ASCII behavior is important, ensure that your input vector +//' is UTF-8 encoded before calling encodeURI or encodeURIComponent. +//' +//' @param value Character vector to be encoded or decoded. +//' @return Encoded or decoded character vector of the same length as the +//' input value. +//' +//' @export +// [[Rcpp::export]] +std::vector encodeURI(std::vector value) { + for (std::vector::iterator it = value.begin(); + it != value.end(); + it++) { + + *it = doEncodeURI(*it, false); + } + + return value; +} + +//' @rdname encodeURI +//' @export +// [[Rcpp::export]] +std::vector encodeURIComponent(std::vector value) { + for (std::vector::iterator it = value.begin(); + it != value.end(); + it++) { + + *it = doEncodeURI(*it, true); + } + + return value; +} + +int hexToInt(char c) { + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': case 'a': return 10; + case 'B': case 'b': return 11; + case 'C': case 'c': return 12; + case 'D': case 'd': return 13; + case 'E': case 'e': return 14; + case 'F': case 'f': return 15; + default: return -1; + } +} + +std::string doDecodeURI(std::string value, bool component) { + std::ostringstream os; + for (std::string::const_iterator it = value.begin(); + it != value.end(); + it++) { + + // If there aren't enough characters left for this to be a + // valid escape code, just use the character and move on + if (it > value.end() - 3) { + os << *it; + continue; + } + + if (*it == '%') { + char hi = *(++it); + char lo = *(++it); + int iHi = hexToInt(hi); + int iLo = hexToInt(lo); + if (iHi < 0 || iLo < 0) { + // Invalid escape sequence + os << '%' << hi << lo; + continue; + } + char c = (char)(iHi << 4 | iLo); + if (!component && isReservedUrlChar(c)) { + os << '%' << hi << lo; + } else { + os << c; + } + } else { + os << *it; + } + } + + return os.str(); +} + +//' @rdname encodeURI +//' @export +// [[Rcpp::export]] +std::vector decodeURI(std::vector value) { + for (std::vector::iterator it = value.begin(); + it != value.end(); + it++) { + + *it = doDecodeURI(*it, false); + } + + return value; +} + +//' @rdname encodeURI +//' @export +// [[Rcpp::export]] +std::vector decodeURIComponent(std::vector value) { + for (std::vector::iterator it = value.begin(); + it != value.end(); + it++) { + *it = doDecodeURI(*it, true); + } + + return value; +}