# Channel is closed error when trying to put! objects in LibCURL write callback

**URL:** <https://discourse.julialang.org/t/channel-is-closed-error-when-trying-to-put-objects-in-libcurl-write-callback/40705>\
**Category:** General Usage\
**Tags:** libcurl, channel\
**Created:** [June 4, 2020, 3:20am UTC](https://discourse.julialang.org/t/channel-is-closed-error-when-trying-to-put-objects-in-libcurl-write-callback/40705 "2020-06-04T03:20:14Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![bluesmoon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bluesmoon/32/327_2.png) [@bluesmoon](https://discourse.julialang.org/u/bluesmoon)\
**Post date:** [June 4, 2020, 3:20am UTC](https://discourse.julialang.org/t/channel-is-closed-error-when-trying-to-put-objects-in-libcurl-write-callback/40705/1 "2020-06-04T03:20:14Z")

</div>

**Solved** by wrapping `curl_easy_perform` in `@async`

Julia version: 1.0.5  
LibCURL: [b27032c2] LibCURL v0.5.2

```julia
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:

```julia
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):

```julia
> 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

```

---

<div class="post-metadata">

**Author:** ![bluesmoon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bluesmoon/32/327_2.png) [@bluesmoon](https://discourse.julialang.org/u/bluesmoon)\
**Post date:** [June 4, 2020, 1:20pm UTC](https://discourse.julialang.org/t/channel-is-closed-error-when-trying-to-put-objects-in-libcurl-write-callback/40705/2 "2020-06-04T13:20:23Z")

</div>

Figured out that I could wrap `curl_easy_perform` in `@async` to get this to work
