Skip to content

req_body_file leaks file handles #534

@plietar

Description

@plietar

When using httr2::req_body_file, httr2 opens a connection to the file being uploaded but never closes it. Eventually the handle gets closed by the garbage collector, but a warning gets printed onto the console.

Looking at the source, I see some code that is supposed to close the handle on a short read:

httr2/R/req-body.R

Lines 241 to 246 in 06e9133

out <- readBin(con, "raw", nbytes)
if (length(out) < nbytes) {
close(con)
done <<- TRUE
con <<- NULL
}

However from what I can tell by adding print statements, libcurl never tries to read more than necessary, so short reads never happen, even on a successful file transfer. For small enough files, nbytes is always just equal to size, and to length(out)

One possible solution would be to accumulate the number of bytes read so far (the sum of all the length(out)), and when that equals size then close the file. This would work in the happy case where the connection is not closed early.

I think a more foolproof solution would be to record the connection handle in the request object, and have req_perform close it when the request is complete.

Example reproduction:

> writeLines("Hello", "hello.txt")
> httr2::request("https://example.com") |> httr2::req_body_file("hello.txt") |> httr2::req_perform()
<httr2_response>
POST https://example.com/
Status: 200 OK
Content-Type: text/html
Body: In memory (1256 bytes)

> gc()

           used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells  6221410 332.3    9245604 493.8  9245604 493.8
Vcells 37136790 283.4   64900073 495.2 42431065 323.8
Warning message:
In .Internal(gc(verbose, reset, full)) :
  closing unused connection 3 (hello.txt)

The call to gc() is used to force the GC to run, but even without it the warning message gets printed eventually.

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