Skip to content

Commit e58e86c

Browse files
authored
Add support for relative urls (#610)
Add `base_url` parameter to `url_parse()` and implement new `req_url_relative()`. Fixes #449
1 parent 9db8f7e commit e58e86c

File tree

8 files changed

+54
-3
lines changed

8 files changed

+54
-3
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export(req_url)
9595
export(req_url_path)
9696
export(req_url_path_append)
9797
export(req_url_query)
98+
export(req_url_relative)
9899
export(req_user_agent)
99100
export(req_verbose)
100101
export(request)

NEWS.md

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

3+
* New `req_url_relative()` for constructing relative urls (#449).
4+
* `url_parse()` gains `base_url` argument so you can also use it to parse relative URLs (#449).
35
* `url_parse()` now uses `curl::curl_parse_url()` which is much faster and more correct (#577).
46
* `req_retry()` now defaults to `max_tries = 2` with a message.
57
Set to `max_tries = 1` to disable retries.

R/req-url.R

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
#' req |>
3232
#' req_url("http://google.com")
3333
#'
34+
#' # Use a relative url
35+
#' req <- request("http://example.com/a/b/c")
36+
#' req |> req_url_relative("..")
37+
#' req |> req_url_relative("/d/e/f")
38+
#'
3439
#' # Use .multi to control what happens with vector parameters:
3540
#' req |> req_url_query(id = 100:105, .multi = "comma")
3641
#' req |> req_url_query(id = 100:105, .multi = "explode")
@@ -47,6 +52,15 @@ req_url <- function(req, url) {
4752
req
4853
}
4954

55+
#' @export
56+
#' @rdname req_url
57+
req_url_relative <- function(req, url) {
58+
check_request(req)
59+
60+
new_url <- url_parse(url, base_url = req$url)
61+
req_url(req, url_build(new_url))
62+
}
63+
5064
#' @export
5165
#' @rdname req_url
5266
#' @param .multi Controls what happens when an element of `...` is a vector

R/url.R

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#'
77
#' @param url For `url_parse()` a string to parse into a URL;
88
#' for `url_build()` a URL to turn back into a string.
9+
#' @param base_url Use this as a parent, if `url` is a relative URL.
910
#' @returns
1011
#' * `url_build()` returns a string.
1112
#' * `url_parse()` returns a URL: a S3 list with class `httr2_url`
@@ -18,15 +19,20 @@
1819
#' url_parse("http://google.com:80/?a=1&b=2")
1920
#' url_parse("http://username@google.com:80/path;test?a=1&b=2#40")
2021
#'
22+
#' # You can parse a relative URL if you also provide a base url
23+
#' url_parse("foo", "http://google.com/bar/")
24+
#' url_parse("..", "http://google.com/bar/")
25+
#'
2126
#' url <- url_parse("http://google.com/")
2227
#' url$port <- 80
2328
#' url$hostname <- "example.com"
2429
#' url$query <- list(a = 1, b = 2, c = 3)
2530
#' url_build(url)
26-
url_parse <- function(url) {
31+
url_parse <- function(url, base_url = NULL) {
2732
check_string(url)
33+
check_string(base_url, allow_null = TRUE)
2834

29-
curl <- curl::curl_parse_url(url)
35+
curl <- curl::curl_parse_url(url, baseurl = base_url)
3036

3137
parsed <- list(
3238
scheme = curl$scheme,

man/req_url.Rd

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/url_parse.Rd

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

tests/testthat/test-req-url.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ test_that("can opt-out of query escaping", {
9696
expect_equal(req_url_query(req, a = I(","))$url, "http://example.com/?a=,")
9797
})
9898

99+
test_that("can construct relative urls", {
100+
req <- request("http://example.com/a/b/c.html")
101+
expect_equal(req_url_relative(req, ".")$url, "http://example.com/a/b/")
102+
expect_equal(req_url_relative(req, "..")$url, "http://example.com/a/")
103+
expect_equal(req_url_relative(req, "/d/e/f")$url, "http://example.com/d/e/f")
104+
})
99105
# explode -----------------------------------------------------------------
100106

101107
test_that("explode handles expected inputs", {

tests/testthat/test-url.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ test_that("can round trip urls", {
2121
expect_equal(map(urls, ~ url_build(url_parse(.x))), urls)
2222
})
2323

24+
test_that("can parse relative urls", {
25+
base <- "http://example.com/a/b/c/"
26+
expect_equal(url_parse("d", base)$path, "/a/b/c/d")
27+
expect_equal(url_parse("..", base)$path, "/a/b/")
28+
29+
expect_equal(url_parse("//archive.org", base)$scheme, "http")
30+
})
31+
2432
test_that("can print all url details", {
2533
expect_snapshot(
2634
url_parse("http://user:pass@example.com:80/path?a=1&b=2&c={1{2}3}#frag")

0 commit comments

Comments
 (0)