Skip to content

Commit

Permalink
Merge commit 'e485d7f45813162f8a66f35f14fea0eb5af0cbb3'
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed Jan 6, 2025
2 parents 55229aa + e485d7f commit fd2ceda
Show file tree
Hide file tree
Showing 32 changed files with 577 additions and 101 deletions.
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export(req_cookies_set)
export(req_dry_run)
export(req_error)
export(req_headers)
export(req_headers_redacted)
export(req_method)
export(req_oauth)
export(req_oauth_auth_code)
Expand Down Expand Up @@ -95,6 +96,7 @@ export(req_url)
export(req_url_path)
export(req_url_path_append)
export(req_url_query)
export(req_url_relative)
export(req_user_agent)
export(req_verbose)
export(request)
Expand Down Expand Up @@ -144,6 +146,7 @@ export(secret_write_rds)
export(signal_total_pages)
export(throttle_status)
export(url_build)
export(url_modify)
export(url_parse)
export(with_mock)
export(with_mocked_responses)
Expand Down
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# httr2 (development version)

* `resp_stream_is_complete()` tells you if there is still data remaining to be streamed (#559).
* `req_url_query()` gains the ability to control how spaces are encoded (#432).
* New `resp_request()` aids debugging by returning the request associated with a response (#604).
* `print.request()` now correctly escapes `{}` in headers (#586).
* New `req_headers_redacted()` provides a user-friendlier way to set redacted headers (#561).
* `resp_link_url()` now works if there are multiple `Link` headers (#587).
* New `url_modify()` makes it easier to modify an existing url (#464).
* New `req_url_relative()` for constructing relative urls (#449).
* `url_parse()` gains `base_url` argument so you can also use it to parse relative URLs (#449).
* `url_parse()` now uses `curl::curl_parse_url()` which is much faster and more correct (#577).
* `req_retry()` now defaults to `max_tries = 2` with a message.
Set to `max_tries = 1` to disable retries.
Expand Down
4 changes: 3 additions & 1 deletion R/curl.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ curl_translate <- function(cmd, simplify_headers = TRUE) {

# Content type set with data
type <- data$headers$`Content-Type`
data$headers$`Content-Type` <- NULL
if (!identical(data$data, "")) {
data$headers$`Content-Type` <- NULL
}

headers <- curl_simplify_headers(data$headers, simplify_headers)
steps <- add_curl_step(steps, "req_headers", dots = headers)
Expand Down
14 changes: 5 additions & 9 deletions R/headers.R
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
as_headers <- function(x, error_call = caller_env()) {
if (is.character(x) || is.raw(x)) {
headers <- curl::parse_headers(x)
headers <- headers[grepl(":", headers, fixed = TRUE)]
parsed <- curl::parse_headers(x)
valid <- parsed[grepl(":", parsed, fixed = TRUE)]
halves <- parse_in_half(valid, ":")

equals <- regexpr(":", headers, fixed = TRUE)
pieces <- regmatches(headers, equals, invert = TRUE)

names <- map_chr(pieces, "[[", 1)
values <- as.list(trimws(map_chr(pieces, "[[", 2)))

new_headers(set_names(values, names), error_call = error_call)
headers <- set_names(trimws(halves$right), halves$left)
new_headers(as.list(headers), error_call = error_call)
} else if (is.list(x)) {
new_headers(x, error_call = error_call)
} else {
Expand Down
29 changes: 23 additions & 6 deletions R/req-headers.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#' Modify request headers
#'
#' @description
#' `req_headers()` allows you to set the value of any header.
#'
#' `req_headers_redacted()` is a variation that adds "redacted" headers, which
#' httr2 avoids printing on the console. This is good practice for
#' authentication headers to avoid accidentally leaking them in log files.
#'
#' @param .req A [request].
#' @param ... <[`dynamic-dots`][rlang::dyn-dots]> Name-value pairs of headers
#' and their values.
Expand Down Expand Up @@ -46,13 +51,16 @@
#' # If you have headers in a list, use !!!
#' headers <- list(HeaderOne = "one", HeaderTwo = "two")
#' req |>
#' req_headers(!!!headers, HeaderThree = "three") |>
#' req_dry_run()
#'
#' # Use `.redact` to hide a header in the output
#' req |>
#' req_headers(Secret = "this-is-private", Public = "but-this-is-not", .redact = "Secret") |>
#' req_headers(!!!headers, HeaderThree = "three") |>
#' req_dry_run()
#'
#' # Use `req_headers_redacted()`` to hide a header in the output
#' req_secret <- req |>
#' req_headers_redacted(Secret = "this-is-private") |>
#' req_headers(Public = "but-this-is-not")
#'
#' req_secret
#' req_secret |> req_dry_run()
req_headers <- function(.req, ..., .redact = NULL) {
check_request(.req)

Expand All @@ -68,3 +76,12 @@ req_headers <- function(.req, ..., .redact = NULL) {

.req
}

#' @export
#' @rdname req_headers
req_headers_redacted <- function(.req, ...) {
check_request(.req)

dots <- list(...)
req_headers(.req, !!!dots, .redact = names(dots))
}
2 changes: 1 addition & 1 deletion R/req-options.R
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ verbose_header <- function(prefix, x, redact = TRUE, to_redact = NULL) {
lines <- unlist(strsplit(x, "\r?\n", useBytes = TRUE))

for (line in lines) {
if (grepl(":", line, fixed = TRUE)) {
if (grepl("^[-a-zA-z0-9]+:", line)) {
header <- headers_redact(as_headers(line), redact, to_redact = to_redact)
cli::cat_line(prefix, cli::style_bold(names(header)), ": ", header)
} else {
Expand Down
23 changes: 21 additions & 2 deletions R/req-url.R
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
#' req |>
#' req_url("http://google.com")
#'
#' # Use a relative url
#' req <- request("http://example.com/a/b/c")
#' req |> req_url_relative("..")
#' req |> req_url_relative("/d/e/f")
#'
#' # Use .multi to control what happens with vector parameters:
#' req |> req_url_query(id = 100:105, .multi = "comma")
#' req |> req_url_query(id = 100:105, .multi = "explode")
Expand All @@ -47,6 +52,15 @@ req_url <- function(req, url) {
req
}

#' @export
#' @rdname req_url
req_url_relative <- function(req, url) {
check_request(req)

new_url <- url_parse(url, base_url = req$url)
req_url(req, url_build(new_url))
}

#' @export
#' @rdname req_url
#' @param .multi Controls what happens when an element of `...` is a vector
Expand All @@ -60,12 +74,17 @@ req_url <- function(req, url) {
#' If none of these options work for your needs, you can instead supply a
#' function that takes a character vector of argument values and returns a
#' a single string.
#' @param .space How should spaces in query params be escaped? The default,
#' "percent", uses standard percent encoding (i.e. `%20`), but you can opt-in
#' to "form" encoding, which uses `+` instead.
req_url_query <- function(.req,
...,
.multi = c("error", "comma", "pipe", "explode")) {
.multi = c("error", "comma", "pipe", "explode"),
.space = c("percent", "form")
) {
check_request(.req)

dots <- multi_dots(..., .multi = .multi)
dots <- multi_dots(..., .multi = .multi, .space = .space)

url <- url_parse(.req$url)
url$query <- modify_list(url$query, !!!dots)
Expand Down
4 changes: 3 additions & 1 deletion R/resp-headers.R
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ resp_link_url <- function(resp, rel) {
return()
}

links <- parse_link(resp_header(resp, "Link"))
headers <- resp_headers(resp)
link_headers <- headers[names(headers) == "Link"]
links <- unlist(lapply(link_headers, parse_link), recursive = FALSE)
sel <- map_lgl(links, ~ .$rel == rel)
if (sum(sel) != 1L) {
return()
Expand Down
16 changes: 16 additions & 0 deletions R/resp-request.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#' Find the request responsible for a response
#'
#' To make debugging easier, httr2 includes the request that was used to
#' generate every response. You can use this function to access it.
#'
#' @inheritParams resp_header
#' @export
#' @examples
#' req <- request_test()
#' resp <- req_perform(req)
#' resp_request(resp)
resp_request <- function(resp) {
check_response(resp)

resp$request
}
2 changes: 1 addition & 1 deletion R/test.R
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ example_url <- function() {
env_cache(the, "test_app",
webfakes::new_app_process(
app,
opts = webfakes::server_opts(num_threads = 2)
opts = webfakes::server_opts(num_threads = 6, enable_keep_alive = TRUE)
)
)
the$test_app$url()
Expand Down
Loading

0 comments on commit fd2ceda

Please sign in to comment.