Solved by wrapping curl_easy_perform
in @async
Julia version: 1.0.5
LibCURL: [b27032c2] LibCURL v0.5.2
julia> unsafe_string(LibCURL.curl_version())
"libcurl/7.64.1 mbedTLS/2.6.1 zlib/1.2.11"
When I use a write callback with a Channel, I always get a Channel is closed
error even though I never close the channel.
Code:
using LibCURL, Test
function curl_write_cb(curlbuf::Ptr{Cvoid}, s::Csize_t, n::Csize_t, p_ctxt::Ptr{Cvoid})::Csize_t
sz = s * n
data = Array{UInt8}(undef, sz)
ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64), data, curlbuf, sz)
ch_ref = unsafe_pointer_to_objref(p_ctxt)
GC.@preserve data ch_ref begin
put!(ch_ref[], data)
end
sz::Csize_t
end
c_curl_write_cb = @cfunction(curl_write_cb, Csize_t, (Ptr{Cvoid}, Csize_t, Csize_t, Ptr{Cvoid}))
function curl_add_headers(curl::Ptr, headers::Vector{String})
slist = Ptr{Cvoid}(0)
for header in headers
slist = curl_slist_append(slist, header)
end
return curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist)
end
function test_writeCB()
curl = curl_easy_init()
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1) # Follow HTTP redirects
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1) # Verify the peer's SSL cert
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2) # Verify the server's Common Name
curl_easy_setopt(curl, CURLOPT_SSLVERSION, 7<<16) # Try highest version up to TLS 1.3
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, 4) # Use H2 over SSL or HTTP/1.1 otherwise
curl_easy_setopt(curl, CURLOPT_TCP_FASTOPEN, 1) # Use TCP Fastopen
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1) # Use TCP Keepalive
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "") # Use best supported encoding (compression) method. gzip or deflate
curl_easy_setopt(curl, CURLOPT_POST, 1) # Use HTTP Post
curl_easy_setopt(curl, CURLOPT_URL, "https://postman-echo.com/post")
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1)
# We create a channel to pass data between the curl write handler and this function
databuffer = Array{UInt8}[]
ch = Channel(ctype=Array{UInt8}) do ch
push!(databuffer, take!(ch))
end
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, c_curl_write_cb)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, Ref(ch))
errorbuffer = Array{UInt8}(undef, CURL_ERROR_SIZE)
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer)
requestBody = """{"a":10,"b":[1,2,3]}"""
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, length(requestBody))
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, requestBody)
curl_add_headers(curl, [
"Content-Type: application/json",
"Content-Length: $(length(requestBody))"
])
res = curl_easy_perform(curl)
println(length(databuffer))
println(join(map(String, databuffer), ""))
@test 0 == res
end
test_writeCB()
Output (after all the TCP and SSL stuff):
> POST /post HTTP/1.1
Host: postman-echo.com
Accept: */*
Accept-Encoding: deflate, gzip
Content-Type: application/json
Content-Length: 20
* upload completely sent off: 20 out of 20 bytes
< HTTP/1.1 200 OK
< Date: Thu, 04 Jun 2020 01:39:12 GMT
< Content-Type: application/json; charset=utf-8
< Content-Length: 383
< Connection: keep-alive
< ETag: W/"17f-a0IU3ZFzsjQnxeU9NYDf8IpRn7I"
< Vary: Accept-Encoding
< set-cookie: sails.sid=s%3AKXgZGDIOUCGmkt61Dp26tyyEckbI2cBk.AeM6UJ6UjW7L9HA1jbKrM7W1rSPiyemT5Ez%2FJ%2BCodlg; Path=/; HttpOnly
<
ERROR: LoadError: InvalidStateException("Channel is closed.", :closed)
Stacktrace:
[1] check_channel_state at ./channels.jl:120 [inlined]
[2] put!(::Channel{Array{UInt8,N} where N}, ::Array{UInt8,1}) at ./channels.jl:250
[3] macro expansion at ./gcutils.jl:87 [inlined]
[4] curl_write_cb(::Ptr{Nothing}, ::UInt64, ::UInt64, ::Ptr{Nothing}) at test/runtests.jl:12
[5] curl_easy_perform at /home/ubuntu/.julia/packages/LibCURL/lWJxD/src/lC_curl_h.jl:162 [inlined]
[6] test_writeCB() at test/runtests.jl:68
[7] top-level scope at none:0
[8] include at ./boot.jl:317 [inlined]
[9] include_relative(::Module, ::String) at ./loading.jl:1044
[10] include(::Module, ::String) at ./sysimg.jl:29
[11] exec_options(::Base.JLOptions) at ./client.jl:266
[12] _start() at ./client.jl:425
in expression starting at test/runtests.jl:77