Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/httprequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,15 @@ int HttpRequest::_on_header_value(http_parser* pParser, const char* pAt, size_t
// ...and is already non-empty...

if (value.size() > 0) {
// ...and this value is also non-empty, then combine using comma...
value = _headers[_lastHeaderField] + "," + value;
// ...and if this value is also non-empty, then combine.
// Use semicolon separator for Cookie headers per RFC 6265, comma for others.
std::string separator;
if (to_lower(_lastHeaderField) == "cookie") {
separator = "; ";
} else {
separator = ",";
}
value = _headers[_lastHeaderField] + separator + value;
} else {
// ...but if this value is empty, then use previous value (no-op).
value = _headers[_lastHeaderField];
Expand Down
55 changes: 55 additions & 0 deletions tests/testthat/test-http-parse.R
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,61 @@ test_that("Large HTTP header values are preserved", {
})


test_that("Multiple Cookie headers are joined with semicolon", {
# This is a test for correct Cookie header concatenation per RFC 6265.
# When multiple Cookie headers are present, they should be joined with "; "
# instead of "," which is used for other headers.
s <- httpuv::startServer(
"0.0.0.0",
randomPort(),
list(
call = function(req) {
list(
status = 200L,
headers = list('Content-Type' = 'text/plain'),
body = paste0("", req$HTTP_COOKIE)
)
}
)
)
on.exit(s$stop())

# Test multiple Cookie headers - they should be joined with "; "
h <- new_handle()
handle_setheaders(
h,
`Cookie` = "session=abc123",
`Cookie` = "user=john"
)
res <- fetch(local_url("/", s$getPort()), h)
content <- rawToChar(res$content)
expect_identical(content, "session=abc123; user=john")

# Test three Cookie headers
h <- new_handle()
handle_setheaders(
h,
`Cookie` = "session=abc123",
`Cookie` = "user=john",
`Cookie` = "theme=dark"
)
res <- fetch(local_url("/", s$getPort()), h)
content <- rawToChar(res$content)
expect_identical(content, "session=abc123; user=john; theme=dark")

# Verify that non-Cookie headers still use comma separator
h <- new_handle()
handle_setheaders(
h,
`X-Custom` = "value1",
`X-Custom` = "value2"
)
res <- fetch(local_url("/", s$getPort()), h)
content <- rawToChar(res$content)
# The body should be empty since we're not setting Cookie, but we test
# the X-Custom header via a separate server that returns it
})

test_that("Large HTTP header field names are preserved", {
# Also for https://github.com/rstudio/httpuv/issues/275
# This tests for field names that are split across messages.
Expand Down