Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect behavior when aborting download in v0.8 #82

Open
sohaib17 opened this issue Nov 1, 2024 · 1 comment
Open

Incorrect behavior when aborting download in v0.8 #82

sohaib17 opened this issue Nov 1, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@sohaib17
Copy link

sohaib17 commented Nov 1, 2024

Problem

libcurl provides a WRITEFUNCTION option to process response chunks. Its behavior is to abort the download if returned integer is not equal to the length of passed chunk.

Your callback should return the number of bytes actually taken care of. If that amount differs from the amount passed to your callback function, it signals an error condition to the library. This causes the transfer to get aborted and the libcurl function used returns CURLE_WRITE_ERROR.

It looks like instead of E_WRITE_ERROR, latest libcurl-impersonate is returning E_RECV_ERROR which breaks the abort functionality and makes it impossible to differentiate between server side receive errors/timeouts and on-purpose aborts.

Test Case

Following is the minimal test case with pycurl that can be used to reproduce this:

import pycurl

print(pycurl.version)

url = 'https://bit.ly/4874LxR'


def abort_write(_):
    '''
    Abort writing response body.
    '''
    return -1


curl = pycurl.Curl()
curl.setopt(pycurl.URL, url.encode('utf-8'))
curl.setopt(pycurl.FOLLOWLOCATION, 1)
curl.setopt(pycurl.WRITEFUNCTION, abort_write)
curl.perform()
curl.close()

Results

Use LD_PRELOAD to load libcurl-impersonate 0.8.0 and run the test. Error 56 (E_RECV_ERROR) will return:

$ LD_PRELOAD=/local/lib/libcurl-0.8.0.so CURL_IMPERSONATE=chrome124 python pycurl_test.py 

PycURL/7.45.3 libcurl/8.7.0-DEV BoringSSL zlib/1.3 brotli/1.1.0 zstd/1.5.6 nghttp2/1.63.0

Traceback (most recent call last):
  File "/home/ubuntu/pycurl_test.py", line 22, in <module>
    curl.perform()

pycurl.error: (56, 'Failure writing output to destination, passed 1369 returned -1')

I tried the same test case with libcurl-impersonate 0.7.0b1 built with older libcurl v8.5 and it passed:

$ LD_PRELOAD=/local/lib/libcurl-0.7.0b1.so CURL_IMPERSONATE=chrome124 python pycurl_test.py 

PycURL/7.45.3 libcurl/8.5.0 BoringSSL zlib/1.3 brotli/1.0.9 zstd/1.5.6 nghttp2/1.56.0

Traceback (most recent call last):
  File "/home/ubuntu/pycurl_test.py", line 22, in <module>
    curl.perform()

pycurl.error: (23, 'Failure writing output to destination')

Error 23 (E_WRITE_ERROR) is the expected code, which allows client to handle on-purpose aborts and process the result. Something got broken in latest impersonation patch or maybe in libcurl v8.7.1 itself. Strangely though, version API returns 8.7.1 as 8.7.0-DEV implying that it is not stable.

@lexiforest
Copy link
Owner

Thanks for the detailed report. I'll verify the behavior later.

As for the version API, I noticed it a while ago. I did use the v8.7.1 tag from curl repository, you can take a look at the Makefiles, however, the compiled binary does not show this version. There might be something wrong with the build arguments or something like that.

@lexiforest lexiforest added the bug Something isn't working label Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants