andelf/go-curl

Perform() hangs when passing in false to WRITEFUNCTION

Opened this issue · 10 comments

Hi,

I was trying to write data returned into a buffer. However if there is a write error in the call back function then it looks like easy.Perform() hangs and never returns.

`b := NewBuffer(make([]byte, 0, 10), capacity)

// Callback function to save data instead of redirecting it into stdout.
writeToBufferFunc := func(buf []byte, userdata interface{}) bool {
	if silent == false {
		_, err := b.Write([]byte(buf))
		if err != nil {
			return false
		}
	}
	return true
}
    // the above callback function returns false in my case. Since there is a write error
    // I am trying to restrict the buffer capacity. 

this.myCurl.Setopt(curl.OPT_WRITEFUNCTION, writeToBufferFunc)

this.myCurl.Setopt(curl.OPT_WRITEDATA, b.mybuf)
logging.Infof("\n\n ---------------- Reaches here \n")

    // Hangs here and never reaches 2. 
if err := this.myCurl.Perform(); err != nil {
            logging.Infof("\n\n ---------------- Reaches here 2 \n")
	if show_error == true {
		return nil, err
	} else {
		return nil, nil
	}
}

`

Is there a way for me to fix this in my code ?

Thanks
Isha

What I was initially trying to do when I came across this error, was to restrict the buffer size that the results of curl are written into. That is possible in c (libcurl). Im wondering if there is an easier way to do this in go-curl.

you can access b.mybuf by convert userdata to []byte.

Yes, but does that allow us to restrict the write function writing into the buffer of a certain size ?

Thanks
Isha

ref: https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html

I've wrapped the original size_t. Since it denotes both written size and error.

Maybe we can change write function's type into func([]byte, interface{}) int, error

That would be perfect. Then one can use a custom write function, that restricts buffer size that returns length and error.

Since it is a callback function, can it return multiple values ? How will it throw the error ? Can we capture the error ?

Thanks
Isha

Hi @andelf
Was wondering if there was any update on this ? Since I wanted to restrict the size of the return buffer for curl.

Thanks
Isha

😿

Haven't look into this project for a long time.
Give me some time to recall how I wrote it.

Found a workaround for this. In order to restrict the return buffer size, we can do the following :

var b bytes.Buffer

// Callback function to save data instead of redirecting it into stdout.
writeToBufferFunc := func(buf []byte, userdata interface{}) bool {
	if silent == false {

		// Check length of buffer b. If it is greater than
		if int64(b.Len()) > responseSize {       == > Check for size here
			// No more writing we are all done
			// If this interrupts the stream of data then we throw not a JSON endpoint error.
			sizeError = true  ==> Set boolean value to handle error post return from  
                                                                OPT_WRITEFUNCTION
			return true  ==> Don't handle error but return true to stop writing into the buffer.
		} else {
			b.Write([]byte(buf))
		}
	}
	return true
}

this.myCurl.Setopt(curl.OPT_WRITEFUNCTION, writeToBufferFunc)
this.myCurl.Setopt(curl.OPT_WRITEDATA, b)

Handle size error here. Since we can't return false in the event of an error, we can do the above and handle error later.

@andelf, what's the status on this? The workaround above isn't really a workaround since the data is still downloaded. go-curl currently pauses the download using CURL_WRITEFUNC_PAUSE when the writeFunc returns false, and will wait until the timeout to actually finish.

EDIT: I've made a workaround, using a Write Func and a Progress Func. Since the progress func does cancel the request when it returns false I use a writeFunc to return false when the download size is exceeded (pausing the download), and cancel the request in the progress func. Then it's just a matter of ignoring the curl.E_ABORTED_BY_CALLBACK that is returned by Perform()