Skip to content

I() protection lost when subsequent req_url_query() calls re-parse the URL #837

@Brar

Description

@Brar

When a value is wrapped in I() to prevent encoding in req_url_query(), the protection is silently lost if any subsequent req_url_query() call is chained on the same request. The second call re-parses and re-encodes the entire URL, defeating I(). This makes I() order-dependent in a way that isn't documented — it only works if no further req_url_query() calls follow.

Minimal repro

library(httr2)

# I() works when it's the only/last req_url_query() call
req1 <- request("https://example.com/api") |>
  req_url_query(param = I("abc;def;ghi"))
req1$url
#> "https://example.com/api?param=abc;def;ghi"
# ✅ Semicolons preserved

# But a subsequent req_url_query() re-parses the URL and re-encodes them
req2 <- req1 |>
  req_url_query(other = "xyz")
req2$url
#> "https://example.com/api?param=abc%3Bdef%3Bghi&other=xyz"
# ❌ Semicolons encoded as %3B despite I()

Verified output

$ Rscript --vanilla -e "library(httr2); req <- req_url_query(request('https://example.com/api'), param = I('abc;def;ghi')); cat(req[['url']], '\n')"
https://example.com/api?param=abc;def;ghi

$ Rscript --vanilla -e "library(httr2); req <- req_url_query(request('https://example.com/api'), param = I('abc;def;ghi')); req <- req_url_query(req, other = 'xyz'); cat(req[['url']], '\n')"
https://example.com/api?param=abc%3Bdef%3Bghi&other=xyz

Expected behavior

I() should prevent encoding of the value regardless of whether additional req_url_query() calls follow.

Actual behavior

The AsIs protection is silently lost when any subsequent req_url_query() call re-parses and rebuilds the URL.

Session info

packageVersion("httr2")
#> [1] '1.2.2'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions