CURLE_RECV_ERROR with PayPal API
I've developed an application that connects to the PayPal API with libcurl, which I use through the OCurl bindings for OCaml from a process running on a Debian server. The code always works when inside the Paypal sandbox (endpoint https://api-3t.sandbox.paypal.开发者_StackOverflowcom/nvp
) but never works when connecting to the actual Paypal servers (endpoint https://api-3t.paypal.com/nvp
).
Libcurl always returns CURLE_RECV_ERROR
. The general consensus is that this error happens when there is a network problem, so I have investigated that.
I ran the exact same request with the command-line curl
tool from the exact same server, using the exact same process uid/gid, and it consistently works. Tracking the transfers with tcpdump
does not reveal any significant difference in the structure of transactions made by the working command-line curl
and the non-working application, so it all appears as if the HTTP request is successfully performed in both cases. Then again, it's HTTPS, so I cannot be certain.
Here is the OCaml code that performs the request:
let c = new Curl.handle in
let buffer = Buffer.create 1763 in
c # set_url "https://api-3t.paypal.com/nvp" ;
c # set_post true ;
c # set_postfields "SOMEDATA" ;
c # set_postfieldsize (String.length "SOMEDATA") ;
c # set_writefunction (fun x -> Buffer.add_string buffer x ; String.length x) ;
c # perform ;
c # cleanup ;
Buffer.contents buffer
Here is the equivalent curl
command line:
curl -X POST https://api-3t.paypal.com/nvp -d SOMEDATA
EDIT: By increasing the libcurl verbosity, I determined that the underlying error is this:
GnuTLS recv error (-9): A TLS packet with unexpected length was received.
What could be the cause of this error? How can I investigate to find out?
EDIT 2: It appears that the difference between command-line and library use is that the command-line version is linked to OpenSSL and the library is linked to GnuTLS.
How do I link both to OpenSSL?
First, the key to further debugging those issues is to use curl's debugging facilities, namely the VERBOSE setting (and also, possibly, the DEBUGFUNCTION setting for printing the data your way).
c # set_verbose true ;
This identified the error as being a problem with GnuTLS that is also discussed here, and which is solved by setting SSLVERSION to 3 to force the use of SSLv3.
c # set_sslversion 3 ;
I always call curl#set_postfieldsize
to the length of the data passed to curl#set_postfields
. My code would be, then:
let make_get url =
let curl = new Curl.handle in
curl#set_writefunction String.length; (* ignore result *)
curl#set_tcpnodelay true;
curl#set_verbose false;
curl#set_post false;
curl#set_url url;
curl
let make_post url =
let curl = make_get url in
curl#set_post true;
curl#set_httpheader [
"Content-Type: text/xml; charset=\"UTF-8\"";
"SOAPAction: \"\"";
];
curl#set_postfields xml;
curl#set_postfieldsize (String.length xml);
curl
I hope this helps.
精彩评论