Skip to content

Commit e485d7f

Browse files
authored
Control how spaces are handed in query strings (#609)
Fixes #432
1 parent 63b6e14 commit e485d7f

File tree

7 files changed

+56
-9
lines changed

7 files changed

+56
-9
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# httr2 (development version)
22

3+
* `req_url_query()` gains the ability to control how spaces are encoded (#432).
34
* New `resp_request()` aids debugging by returning the request associated with a response (#604).
45
* `print.request()` now correctly escapes `{}` in headers (#586).
56
* New `req_headers_redacted()` provides a user-friendlier way to set redacted headers (#561).

R/req-url.R

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,17 @@ req_url_relative <- function(req, url) {
7474
#' If none of these options work for your needs, you can instead supply a
7575
#' function that takes a character vector of argument values and returns a
7676
#' a single string.
77+
#' @param .space How should spaces in query params be escaped? The default,
78+
#' "percent", uses standard percent encoding (i.e. `%20`), but you can opt-in
79+
#' to "form" encoding, which uses `+` instead.
7780
req_url_query <- function(.req,
7881
...,
79-
.multi = c("error", "comma", "pipe", "explode")) {
82+
.multi = c("error", "comma", "pipe", "explode"),
83+
.space = c("percent", "form")
84+
) {
8085
check_request(.req)
8186

82-
dots <- multi_dots(..., .multi = .multi)
87+
dots <- multi_dots(..., .multi = .multi, .space = .space)
8388

8489
url <- url_parse(.req$url)
8590
url$query <- modify_list(url$query, !!!dots)

R/url.R

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,19 @@ elements_build <- function(x, name, collapse, error_call = caller_env()) {
249249
format_query_param <- function(x,
250250
name,
251251
multi = FALSE,
252+
form = FALSE,
252253
error_call = caller_env()) {
253254
check_query_param(x, name, multi = multi, error_call = error_call)
254255

255256
if (inherits(x, "AsIs")) {
256257
unclass(x)
257258
} else {
258259
x <- format(x, scientific = FALSE, trim = TRUE, justify = "none")
259-
curl::curl_escape(x)
260+
x <- curl::curl_escape(x)
261+
if (form) {
262+
x <- gsub("%20", "+", x, fixed = TRUE)
263+
}
264+
x
260265
}
261266
}
262267
check_query_param <- function(x, name, multi = FALSE, error_call = caller_env()) {

R/utils-multi.R

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
multi_dots <- function(...,
22
.multi = c("error", "comma", "pipe", "explode"),
3+
.space = c("percent", "form"),
34
error_arg = "...",
45
error_call = caller_env()) {
56
if (is.function(.multi)) {
67
check_function2(.multi, call = error_call, arg = ".multi")
78
} else {
89
.multi <- arg_match(.multi, error_arg = ".multi", error_call = error_call)
910
}
11+
.space <- arg_match(.space, call = error_call)
12+
form <- .space == "form"
1013

1114
dots <- list2(...)
1215
if (length(dots) == 0) {
@@ -31,20 +34,20 @@ multi_dots <- function(...,
3134
n <- lengths(dots)
3235
if (any(n > 1)) {
3336
if (is.function(.multi)) {
34-
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE)
37+
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE, form = form)
3538
dots[n > 1] <- lapply(dots[n > 1], .multi)
3639
dots[n > 1] <- lapply(dots[n > 1], I)
3740
} else if (.multi == "comma") {
38-
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE)
41+
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE, form = form)
3942
dots[n > 1] <- lapply(dots[n > 1], paste0, collapse = ",")
4043
dots[n > 1] <- lapply(dots[n > 1], I)
4144
} else if (.multi == "pipe") {
42-
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE)
45+
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE, form = form)
4346
dots[n > 1] <- lapply(dots[n > 1], paste0, collapse = "|")
4447
dots[n > 1] <- lapply(dots[n > 1], I)
4548
} else if (.multi == "explode") {
4649
dots <- explode(dots)
47-
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE)
50+
dots[n > 1] <- imap(dots[n > 1], format_query_param, multi = TRUE, form = form)
4851
dots[n > 1] <- lapply(dots[n > 1], I)
4952
} else if (.multi == "error") {
5053
cli::cli_abort(
@@ -58,7 +61,12 @@ multi_dots <- function(...,
5861
}
5962

6063
# Format other params
61-
dots[n == 1] <- imap(dots[n == 1], format_query_param, error_call = error_call)
64+
dots[n == 1] <- imap(
65+
dots[n == 1],
66+
format_query_param,
67+
form = form,
68+
error_call = error_call
69+
)
6270
dots[n == 1] <- lapply(dots[n == 1], I)
6371

6472
dots

man/req_url.Rd

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/req-url.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# can control space handling
2+
3+
Code
4+
req_url_query(req, a = " ", .space = "bar")
5+
Condition
6+
Error in `multi_dots()`:
7+
! `.space` must be one of "percent" or "form", not "bar".
8+
19
# can handle multi query params
210

311
Code

tests/testthat/test-req-url.R

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ test_that("can set query params", {
5252
expect_equal(req_url_query(req, !!!list(a = 1, a = 2))$url, "http://example.com/?a=1&a=2")
5353
})
5454

55+
test_that("can control space handling", {
56+
req <- request("http://example.com/")
57+
expect_equal(req_url_query(req, a = " ")$url, "http://example.com/?a=%20")
58+
expect_equal(req_url_query(req, a = " ", .space = "form")$url, "http://example.com/?a=+")
59+
60+
expect_snapshot(
61+
req_url_query(req, a = " ", .space = "bar"),
62+
error = TRUE
63+
)
64+
})
65+
5566
test_that("can handle multi query params", {
5667
req <- request("http://example.com/")
5768

0 commit comments

Comments
 (0)