Send post body after process response headers with libcurl

423 Views Asked by At

TL;DR: As topic says - need send multipart post body ONLY after process response headers, received after established connection.

According to libcurl documentation, CURLOPT_HEADERFUNCTION gets called by libcurl as soon as it has received header data. But after simple investigation this not happened.

How to reproduce:

Callbacks:

size_t HeaderCallback(void *data, size_t size, size_t nmemb, void *userdata) {
    cout << __FUNCTION__ << " " << data << '\n';
    return size*nmemb;
}

static int Trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp) {
    cout << __FUNCTION__  << " " <<timestamp<< ' ' << data << endl;
    return 0;
}

static size_t ReadFromStateCallBack(void* dst, size_t size, size_t nmemb, void* state) {    
    cout << __FUNCTION__  << " " <<timestamp<< endl;
    return READ();
}

Short main request body:

CURL *curl = curl_easy_init();
curl_slist* h = NULL;
const char * url = "https://upload.box.com/api/2.0/files/content";

curl_httppost *formpost=NULL;
curl_httppost *lastptr=NULL;

curl_formadd(&formpost,&lastptr,
             CURLFORM_COPYNAME, "filename",
             CURLFORM_CONTENTTYPE, "text/plain",
             CURLFORM_FILENAME, "uploadthis.txt",
             CURLFORM_STREAM, (void*)state,
             CURLFORM_CONTENTSLENGTH, GetFileSize(),
             CURLFORM_END);

curl_formadd(&formpost, &lastptr,
    CURLFORM_COPYNAME, "parent_id",
    CURLFORM_COPYCONTENTS, "0",
    CURLFORM_END);

curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, Trace);

curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_POST, 1L);

curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, ReadFromStateCallBack);
curl_easy_setopt(curl, CURLOPT_READDATA, state);

curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buf);

h = curl_slist_append(h, "Expect: 100-continue");
h = curl_slist_append(h, "Authorization: Bearer WRONGTOKEN");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, h);

curl_easy_perform(curl);

curl_formfree(formpost);

And then we can recive next log(large number time since c++11 chrono epoch):

Type [0] data: [SSL Related stuff]

Type [0] data: [Done waiting for 100-continue]

ReadFromStateCallBack 1455532592078426519

...

ReadFromStateCallBack 1455532592507779449

HeaderCallback 1455532592937695923 [HTTP/1.1 100 Continue]

HeaderCallback 1455532592937713786 [Date: Mon, 15 Feb 2016 10:36:32 GMT]

Type [0] data: [Server ATS is not blacklisted]

HeaderCallback: 1455532593030759917 [HTTP/1.1 401 Unauthorized]

Additional info from Headers

And as you can see libcurl for own needs processed 100-continue header, but callback for processing headers called only after sending data.

Also it seems all necessary headers received. Example of tcpdump:

13:36:30.425361 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [S], seq 164841209, win 29200, options [mss 1460,sackOK,TS val 2164947 ecr 0,nop,wscale 7], length 0

13:36:30.639293 IP 74.112.184.85.https > 192.168.1.70.41895: Flags [S.], seq 1336121630, ack 164841210, win 14480, options [mss 1460,sackOK,TS val 2083933652 ecr 2164947,nop,wscale 7], length 0

13:36:30.639337 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [.], ack 1, win 229, options [nop,nop,TS val 2165000 ecr 2083933652], length 0

13:36:30.640031 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [P.], seq 1:297, ack 1, win 229, options [nop,nop,TS val 2165000 ecr 2083933652], length 296

13:36:30.853750 IP 74.112.184.85.https > 192.168.1.70.41895: Flags [.], ack 297, win 122, options [nop,nop,TS val 2083933866 ecr 2165000], length 0

13:36:30.861507 IP 74.112.184.85.https > 192.168.1.70.41895: Flags [P.], seq 1:2690, ack 297, win 122, options [nop,nop,TS val 2083933874 ecr 2165000], length 2689

^^^^^ - right here.

13:36:30.861524 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [.], ack 2690, win 271, options [nop,nop,TS val 2165056 ecr 2083933874], length 0

13:36:30.862654 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [P.], seq 297:423, ack 2690, win 271, options [nop,nop,TS val 2165056 ecr 2083933874], length 126

13:36:31.076949 IP 74.112.184.85.https > 192.168.1.70.41895: Flags [P.], seq 2690:2741, ack 423, win 122, options [nop,nop,TS val 2083934090 ecr 2165056], length 51

13:36:31.077286 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [P.], seq 423:693, ack 2741, win 271, options [nop,nop,TS val 2165110 ecr 2083934090], length 270

13:36:31.330808 IP 74.112.184.85.https > 192.168.1.70.41895: Flags [.], ack 693, win 130, options [nop,nop,TS val 2083934344 ecr 2165110], length 0

13:36:32.078397 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [P.], seq 693:870, ack 2741, win 271, options [nop,nop,TS val 2165360 ecr 2083934344], length 177

13:36:32.078533 IP 192.168.1.70.41895 > 74.112.184.85.https: Flags [.], seq 870:2218, ack 2741, win 271, options [nop,nop,TS val 2165360 ecr 2083934344], length 1348

repeating send data

Because of SSL connection I can`t view tcpdump with -A, to be sure that headers received in suggested part.

Software:

  • libcurl(7.36)
  • Compiler GCC(4.8.4)

P.S. For me it seems libcurl have wrong behavior for [RFC 2616(8.2.4)] (https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)

P.P.S Same behavior if existed Connection: close header.

0

There are 0 best solutions below