Nothing
#' Create a staticPath object
#'
#' The \code{staticPath} function creates a \code{staticPath} object. Note that
#' if any of the arguments (other than \code{path}) are \code{NULL}, then that
#' means that for this particular static path, it should inherit the behavior
#' from the staticPathOptions set for the application as a whole.
#'
#' The \code{excludeStaticPath} function tells the application to ignore a
#' particular path for static serving. This is useful when you want to include a
#' path for static serving (like \code{"/"}) but then exclude a subdirectory of
#' it (like \code{"/dynamic"}) so that the subdirectory will always be passed to
#' the R code for handling requests. \code{excludeStaticPath} can be used not
#' only for directories; it can also exclude specific files.
#'
#' @param path The local path.
#' @inheritParams staticPathOptions
#'
#' @seealso \code{\link{staticPathOptions}}.
#'
#' @export
staticPath <- function(
path,
indexhtml = NULL,
fallthrough = NULL,
html_charset = NULL,
headers = NULL,
validation = NULL
) {
if (!is.character(path) || length(path) != 1 || path == "") {
stop("`path` must be a non-empty string.")
}
path <- normalizePath(path, winslash = "/", mustWork = TRUE)
path <- enc2utf8(path)
structure(
list(
path = path,
options = normalizeStaticPathOptions(staticPathOptions(
indexhtml = indexhtml,
fallthrough = fallthrough,
html_charset = html_charset,
headers = headers,
validation = validation,
exclude = FALSE
))
),
class = "staticPath"
)
}
#' @rdname staticPath
#' @export
excludeStaticPath <- function() {
structure(
list(
path = "",
options = staticPathOptions(
indexhtml = NULL,
fallthrough = NULL,
html_charset = NULL,
headers = NULL,
validation = NULL,
exclude = TRUE
)
),
class = "staticPath"
)
}
as.staticPath <- function(path) {
UseMethod("as.staticPath", path)
}
as.staticPath.staticPath <- function(path) {
path
}
as.staticPath.character <- function(path) {
staticPath(path)
}
as.staticPath.default <- function(path) {
stop("Cannot convert object of class ", class(path), " to a staticPath object.")
}
#' @export
print.staticPath <- function(x, ...) {
cat(format(x, ...), sep = "\n")
invisible(x)
}
#' @export
format.staticPath <- function(x, ...) {
ret <- paste0(
"<staticPath>\n",
" Local path: ", x$path, "\n",
format_opts(x$options)
)
}
#' Create options for static paths
#'
#'
#' @param indexhtml If an index.html file is present, should it be served up
#' when the client requests the static path or any subdirectory?
#' @param fallthrough With the default value, \code{FALSE}, if a request is made
#' for a file that doesn't exist, then httpuv will immediately send a 404
#' response from the background I/O thread, without needing to call back into
#' the main R thread. This offers the best performance. If the value is
#' \code{TRUE}, then instead of sending a 404 response, httpuv will call the
#' application's \code{call} function, and allow it to handle the request.
#' @param html_charset When HTML files are served, the value that will be
#' provided for \code{charset} in the Content-Type header. For example, with
#' the default value, \code{"utf-8"}, the header is \code{Content-Type:
#' text/html; charset=utf-8}. If \code{""} is used, then no \code{charset}
#' will be added in the Content-Type header.
#' @param headers Additional headers and values that will be included in the
#' response.
#' @param validation An optional validation pattern. Presently, the only type of
#' validation supported is an exact string match of a header. For example, if
#' \code{validation} is \code{'"abc" = "xyz"'}, then HTTP requests must have a
#' header named \code{abc} (case-insensitive) with the value \code{xyz}
#' (case-sensitive). If a request does not have a matching header, than httpuv
#' will give a 403 Forbidden response. If the \code{character(0)} (the
#' default), then no validation check will be performed.
#' @param exclude Should this path be excluded from static serving? (This is
#' only to be used internally, for \code{\link{excludeStaticPath}}.)
#'
#' @export
staticPathOptions <- function(
indexhtml = TRUE,
fallthrough = FALSE,
html_charset = "utf-8",
headers = list(),
validation = character(0),
exclude = FALSE
) {
res <- structure(
list(
indexhtml = indexhtml,
fallthrough = fallthrough,
html_charset = html_charset,
headers = headers,
validation = validation,
exclude = exclude
),
class = "staticPathOptions"
)
normalizeStaticPathOptions(res)
}
#' @export
print.staticPathOptions <- function(x, ...) {
cat(format(x, ...), sep = "\n")
invisible(x)
}
#' @export
format.staticPathOptions <- function(x, ...) {
paste0(
"<staticPathOptions>\n",
format_opts(x, format_empty = "<none>")
)
}
format_opts <- function(x, format_empty = "<inherit>") {
format_option <- function(opt) {
if (is.null(opt) || length(opt) == 0) {
format_empty
} else if (!is.null(names(opt))) {
# Named character vector
lines <- mapply(
function(name, value) paste0(' "', name, '" = "', value, '"'),
names(opt),
opt,
SIMPLIFY = FALSE,
USE.NAMES = FALSE
)
lines <- paste(as.character(lines), collapse = "\n")
lines <- paste0("\n", lines)
lines
} else {
paste(as.character(opt), collapse = " ")
}
}
ret <- paste0(
" Use index.html: ", format_option(x$indexhtml), "\n",
" Fallthrough to R: ", format_option(x$fallthrough), "\n",
" HTML charset: ", format_option(x$html_charset), "\n",
" Extra headers: ", format_option(x$headers), "\n",
" Validation params: ", format_option(x$validation), "\n",
" Exclude path: ", format_option(x$exclude), "\n"
)
}
# This function always returns a named list of staticPath objects. The names
# will all start with "/". The input can be a named character vector or a
# named list containing a mix of strings and staticPath objects. This function
# is idempotent.
normalizeStaticPaths <- function(paths) {
if (is.null(paths) || length(paths) == 0) {
return(list())
}
if (any_unnamed(paths)) {
stop("paths must be a named character vector, a named list containing strings and staticPath objects, or NULL.")
}
if (!is.character(paths) && !is.list(paths)) {
stop("paths must be a named character vector, a named list containing strings and staticPath objects, or NULL.")
}
# Convert to list of staticPath objects. Need this verbose wrapping of
# as.staticPath because of S3 dispatch for non-registered methods.
paths <- lapply(paths, function(path) as.staticPath(path))
# Make sure URL paths have a leading '/' and no trailing '/'.
names(paths) <- vapply(names(paths), function(path) {
path <- enc2utf8(path)
if (path == "") {
stop("All paths must be non-empty strings.")
}
# Ensure there's a leading / for every path
if (substr(path, 1, 1) != "/") {
path <- paste0("/", path)
}
# Strip trailing slashes, except when the path is just "/".
if (path != "/") {
path <- sub("/+$", "", path)
}
path
}, "")
paths
}
# Takes a staticPathOptions object and modifies it so that the resulting
# object is easier to work with on the C++ side. The resulting object is not
# meant to be modified on the R side. This function is idempotent; if the
# object has already been normalized, it will not be modified. For each entry,
# a NULL means to inherit.
normalizeStaticPathOptions <- function(opts) {
if (isTRUE(attr(opts, "normalized", exact = TRUE))) {
return(opts)
}
# html_charset can accept "" or character(0). But on the C++ side, we want
# "".
if (!is.null(opts$html_charset)) {
if (length(opts$html_charset) == 0) {
opts$html_charset <- ""
}
}
if (!is.null(opts$exclude)) {
if (!is.logical(opts$exclude) || length(opts$exclude) != 1) {
stop("`exclude` option must be TRUE or FALSE.")
}
}
# Can be a named list of strings, or a named character vector. On the C++
# side, we want a named character vector.
if (is.list(opts$headers)) {
# Convert list to named character vector
opts$headers <- unlist(opts$headers, recursive = FALSE)
# Special case: if opts$headers was an empty list before unlist(), it is
# now NULL. Replace it with an empty named character vector.
if (length(opts$headers) == 0) {
opts$headers <- c(a="a")[0]
}
if (!is.character(opts$headers) || any_unnamed(opts$headers)) {
stop("`headers` option must be a named list or character vector.")
}
}
if (!is.null(opts$validation)) {
if (!is.character(opts$validation) || length(opts$validation) > 1) {
stop("`validation` option must be a character vector with zero or one element.")
}
# Both "" and character(0) result in character(0). Length-1 strings other
# than "" will be parsed.
if (length(opts$validation) == 1) {
if (opts$validation == "") {
opts$validation <- character(0)
} else {
fail <- FALSE
tryCatch(
p <- parse(text = opts$validation)[[1]],
error = function(e) fail <<- TRUE
)
if (!fail) {
if (length(p) != 3 ||
p[[1]] != as.symbol("==") ||
!is.character(p[[2]]) ||
length(p[[2]]) != 1 ||
!is.character(p[[3]]) ||
length(p[[3]]) != 1)
{
fail <- TRUE
}
}
if (fail) {
stop("`validation` must be a string of the form: '\"xxx\" == \"yyy\"'")
}
# Turn it into a char vector for easier processing in C++
opts$validation <- as.character(p)
}
}
}
attr(opts, "normalized") <- TRUE
opts
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.