My code looks like this:
curl = curl_easy_init();
if (curl) {
headers = curl_slist_append(headers, client_id_header);
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, "127.0.0.1/test.php");
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
res = curl_easy_perform(curl);
res = curl_easy_send(curl, json_struct, strlen(json_struct), &io_len);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
Which doesnt work, the program just hangs forever.
In test.php these are the request headers I get:
array(6) {
["Host"]=>
string(9) "127.0.0.1"
["Accept"]=>
string(3) "*/*"
["Transfer-Encoding"]=>
string(7) "chunked"
["X-ClientId"]=>
string(36) "php_..."
["Content-Type"]=>
string(16) "application/json"
开发者_开发问答["Expect"]=>
string(12) "100-continue"
}
But the body is empty, means, no json data is sent with the request.
What I want to do with libcurl is actually nothing else then these command line script:
curl -X PUT -H "Content-Type: application/json" -d '... some json ...' 127.0.0.1/test.php
Got it :)
Dont use
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
Make a custom request and send the data as POSTFIELDS:
curl = curl_easy_init();
if (curl) {
headers = curl_slist_append(headers, client_id_header);
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, request_url);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_struct); /* data goes here */
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
CURLOPT_PUT
is deprecated, and has been for a while. You should use CURLOPT_UPLOAD
.
For unknown amounts of data with HTTP, you should be using chunked transfer encoding. The CURLOPT_UPLOAD
docs say:
If you use PUT to a HTTP 1.1 server, you can upload data without knowing the size before starting the transfer if you use chunked encoding. You enable this by adding a header like "Transfer-Encoding: chunked" with CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must specify the size.
This one did not work for me. I HAD to use UPLOAD and PUT to perform this correctly. The answer that worked for me is here:
How do I send long PUT data in libcurl without using file pointers?
You need the callback function for READFILE and then use that to copy your data to the pointer curl offers in that callback.
What ultimately worked for me was to make sure I set the FILE size using either CURLOPT_INFILESIZE or CURLOPT_INFILESIZE_LARGE depending on your payload. Otherwise you get the problem posted in the backstory.
Backstory: I was expecting a JSON request but using either the PUT CURL option or this custom request approach I get the same result as doing this via console
curl -H "Accept:application/json" -H "Authorization:authxxxx" -v -X PUT "http://server.xxxdomain.com/path0/path1/path2/state?data1=1&data2=1421468910543&data3=-3"
* Adding handle: conn: 0x7fd752003a00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fd752003a00) send_pipe: 1, recv_pipe: 0
* About to connect() to server.xxxdomain.com port 80 (#0)
* Trying ipaddress...
* Connected to api-qos.boingodev.com (ipaddress) port 80 (#0)
> PUT /path0/path1/path2/done?data1=1&data2=1421468910543&data3=-3 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: server.xxxdomain.com
> Accept:application/json
> Authorization:authxxxx
>
< HTTP/1.1 411 Length Required
* Server nginx/1.1.19 is not blacklisted
< Server: nginx/1.1.19
< Date: Sat, 17 Jan 2015 04:32:18 GMT
< Content-Type: text/html
< Content-Length: 181
< Connection: close
<
<html>
<head><title>411 Length Required</title></head>
<body bgcolor="white">
<center><h1>411 Length Required</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
* Closing connection 0
On the other hand making the same request on the console and adding a data field (PUT -d "" URL)gets me what I want:
curl -H "Accept:application/json" -H "authxxxx" -v -X PUT -d "" "http://server.xxxdomain.com/path0/path1/path2/state?data1=1&data2=1421468910543&data3=-3"
* Adding handle: conn: 0x7fe8aa803a00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fe8aa803a00) send_pipe: 1, recv_pipe: 0
* About to connect() to server.xxxdomain.com port 80 (#0)
* Trying ipaddress...
* Connected to server.xxxdomain.com (ipaddress) port 80 (#0)
> PUT /path0/path1/path2/state?data1=1&data2=1421468910543&data3=-3" HTTP/1.1
> User-Agent: curl/7.30.0
> Host: server.xxxdomain.com
> Accept:application/json
> Authorization:authxxxx
> Content-Length: 0
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
* Server nginx/1.1.19 is not blacklisted
< Server: nginx/1.1.19
< Date: Sat, 17 Jan 2015 17:16:59 GMT
< Content-Type: application/json
< Content-Length: 32
< Connection: keep-alive
<
* Connection #0 to host server.xxxdomain.com left intact
{"code":"0","message":"Success"}
In summary it looks like I need to figure out the CURL options that'll do the equivalent of PUT -d "". Also you can see the difference between both response, in one case the return is HTML and the connection is closed. In the other case the Content is JSON and the connection is kept alive.
Based on what I've found on error 411:
http://www.checkupdown.com/status/E411.html
The problem is that the content length needs to be set regardless of whether you use CURLOPT_UPLOAD with CURLOPT_PUT or the CUSTOM option.
So, if you have a stream of data you have it seems you HAVE to use the READDATA and READFUNCTION options to determine the length of your data.
Note to admin:
Keep in mind rep 50 is REQUIRED to post comments so I HAVE NO CHOICE but to make separate posts in order to communicate. So consider this when you are thinking about deleting these posts as it has been done in the past.
精彩评论