diff --git a/DESCRIPTION b/DESCRIPTION index d700902..0afa506 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -50,4 +50,4 @@ Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.2 diff --git a/NEWS.md b/NEWS.md index 41fbf72..0c1fa0d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # webfakes (development version) +* New server option: `decode_url`. If set to `FALSE`, then the web server + will not URL-decode the URL (#106). + # webfakes 1.3.1 No changes. diff --git a/R/server.R b/R/server.R index 967f900..31e6c06 100644 --- a/R/server.R +++ b/R/server.R @@ -27,7 +27,8 @@ server_start <- function(opts = server_opts()) { # These are not configurable currently "request_timeout_ms" = "100000", - "enable_auth_domain_check" = "no" + "enable_auth_domain_check" = "no", + "decode_url" = if (opts$decode_url) "yes" else "no" ) srv <- call_with_cleanup(c_server_start, options) @@ -59,6 +60,10 @@ server_start <- function(opts = server_opts()) { #' @param throttle Limit download speed for clients. If not `Inf`, #' then it is the maximum number of bytes per second, that is sent to #' as connection. +#' @param decode_url Whether the server should automatically decode +#' URL-encodded URLs. If `TRUE` (the default), `/foo%2fbar` will be +#' converted to `/foo/bar` automatically. If `FALSE`, URLs as not +#' URL-decoded. #' @return List of options that can be passed to `webfakes_app$listen()` #' (see [new_app()]), and [new_app_process()]. #' @@ -87,7 +92,8 @@ server_opts <- function(remote = FALSE, port = NULL, num_threads = 1, access_log_file = remote, error_log_file = TRUE, tcp_nodelay = FALSE, - throttle = Inf) { + throttle = Inf, + decode_url = TRUE) { log_dir <- Sys.getenv("WEBFAKES_LOG_DIR", file.path(tempdir(), "webfakes")) if (isTRUE(access_log_file)) { diff --git a/man/server_opts.Rd b/man/server_opts.Rd index 2c085b3..1b1531b 100644 --- a/man/server_opts.Rd +++ b/man/server_opts.Rd @@ -13,7 +13,8 @@ server_opts( access_log_file = remote, error_log_file = TRUE, tcp_nodelay = FALSE, - throttle = Inf + throttle = Inf, + decode_url = TRUE ) } \arguments{ @@ -47,6 +48,11 @@ possible, instead of waiting for a full buffer or timeout to occur.} \item{throttle}{Limit download speed for clients. If not \code{Inf}, then it is the maximum number of bytes per second, that is sent to as connection.} + +\item{decode_url}{Whether the server should automatically decode +URL-encodded URLs. If \code{TRUE} (the default), \verb{/foo\%2fbar} will be +converted to \verb{/foo/bar} automatically. If \code{FALSE}, URLs as not +URL-decoded.} } \value{ List of options that can be passed to \code{webfakes_app$listen()} diff --git a/tests/testthat/test-decode-url.R b/tests/testthat/test-decode-url.R new file mode 100644 index 0000000..bf083d8 --- /dev/null +++ b/tests/testthat/test-decode-url.R @@ -0,0 +1,37 @@ + +test_that("decode_url option", { + app <- webfakes::new_app() + app$get( + webfakes::new_regexp("^/hello/(?.*)$"), + function(req, res) { + res$send(paste0("Return content of ", req$params$path, "!")) + } + ) + web <- webfakes::local_app_process(app) + + resp <- curl::curl_fetch_memory(web$url("/hello/foo%2fbar/suffix")) + expect_equal(resp$status_code, 200L) + expect_equal( + rawToChar(resp$content), + "Return content of foo/bar/suffix!" + ) + + app <- webfakes::new_app() + app$get( + webfakes::new_regexp("^/hello/(?.*)$"), + function(req, res) { + res$send(paste0("Return content of ", req$params$path, "!")) + } + ) + web <- webfakes::local_app_process( + app, + opts = server_opts(remote = TRUE, decode_url = FALSE) + ) + + resp <- curl::curl_fetch_memory(web$url("/hello/foo%2fbar/suffix")) + expect_equal(resp$status_code, 200L) + expect_equal( + rawToChar(resp$content), + "Return content of foo%2fbar/suffix!" + ) +})