Jump to content

Proper use for cURL


NotionCommotion

Recommended Posts

I was recently working on a script where the browser client makes an ajax REST request to a webserver, and that webserver makes a REST request using cURL to another server, and kept intermittently getting the following text in my response.

<!DOCTYPE HTML PUBLIC \"-\/\/IETF\/\/DTD HTML 2.0\/\/EN\">\n<html><head>\n<title>400 Bad Request<\/title>\n<\/head><body>\n<h1>Bad Request<\/h1>\n<p>Your browser sent a request that this server could not understand.<br \/>\n<\/p>\n<\/body><\/html>\n

Both servers are under my control, and I spent way to much time figuring out the problem.  None of the machines logged any errors (should they have?  Maybe my error logging is not correct?), and the correct data was being returned to each machine, but this damn text kept on breaking my JSON.   It turns out that CURLOPT_POSTFIELDS must be set if a POST request (i.e. CURLOPT_POST=1).  At least on my machines, this is not required for either PUT or DELETE requests.  The solution was just to include an empty string in CURLOPT_POSTFIELDS.

 

I started using this function a while back just because it worked, and I think it is about time to reconsider how I am using it.  Specifically, should I really be using the values for CURLOPT_RETURNTRANSFER, CURLOPT_HEADER, CURLOPT_FOLLOWLOCATION, CURLOPT_ENCODING,and CURLOPT_USERAGENT as I show?  Also, should I consider changing anything else?  As I said, I spent way too much time identifying this issue, and don't ever want to do it again.

 

Thanks

function CallAPI($method, $url, array $data, array $options=[])
{
    $options=$options+[    //Don't use array_merge since it reorders!
        CURLOPT_RETURNTRANSFER => true,     // return web page
        CURLOPT_HEADER         => false,    // don't return headers
        CURLOPT_FOLLOWLOCATION => true,     // follow redirects
        CURLOPT_ENCODING       => "",       // handle all encodings
        CURLOPT_USERAGENT      => "unknown",// who am i
        CURLOPT_AUTOREFERER    => true,     // set referrer on redirect
        CURLOPT_CONNECTTIMEOUT => 120,      // timeout on connect
        CURLOPT_TIMEOUT        => 120,      // timeout on response
        CURLOPT_MAXREDIRS      => 10,       // stop after 10 redirects
    ];
    //Optional authentication
    if (isset($options[CURLOPT_USERPWD])) {$options[CURLOPT_HTTPAUTH]=CURLAUTH_BASIC;}
    switch (strtolower($method)) {
        case "get":
            if ($data) {$url = sprintf("%s?%s", $url, http_build_query($data));}
            break;
        case "post":
            $options[CURLOPT_POST]=1;
            //if ($data) {$options[CURLOPT_POSTFIELDS]=http_build_query($data);}
            $options[CURLOPT_POSTFIELDS]=$data?http_build_query($data):'';
            break;
        case "put":
            //$options[CURLOPT_PUT]=1;
            $options[CURLOPT_CUSTOMREQUEST]="PUT";
            if ($data) {$options[CURLOPT_POSTFIELDS]=http_build_query($data);}
            break;
        case "delete":
            //$options[CURLOPT_DELETE]=1;
            $options[CURLOPT_CUSTOMREQUEST]="DELETE";
            if ($data) {$options[CURLOPT_POSTFIELDS]=http_build_query($data);}
            break;
        default:trigger_error("Invalid HTTP method.", E_USER_ERROR);
    }
    $options[CURLOPT_URL]=$url;
    $ch      = curl_init();
    curl_setopt_array( $ch, $options );
    $rsp=['rsp'=>curl_exec( $ch ),'errno'=>curl_errno($ch),'code'=>curl_getinfo($ch, CURLINFO_HTTP_CODE),'error'=>false];
    if($rsp['errno']) {
        $rsp['error']=curl_error($ch);
    }
    curl_close( $ch );
    return $rsp;
}

 

Link to comment
Share on other sites

- RETURNTRANSFER, HEADER, and FOLLOWLOCATION should be obvious as to whether you want to use them or not.

- ENCODING would only be necessary if the remote server is checking the Accept-Encoding. It probably isn't.

- For USERAGENT either leave it out entirely or set it with an appropriate value (eg, "PHP/" + PHP_VERSION).

 

If you're doing a POST request then it should have a body. No body is weird. But it's acceptable, so in that edge case using an empty string as the request body is an acceptable workaround.

 

//Don't use array_merge since it reorders!
Order does not matter. array_merge() is fine. Technically array_replace() would be more appropriate but with those arrays it won't matter.
Link to comment
Share on other sites

Yes, CURLOPT_FOLLOWLOCATION is obvious.  Didn't mean to include.  I saw my comment "return web page" after CURLOPT_RETURNTRANSFER which temporarily confused me and yes this is obvious.  Why am I including CURLOPT_HEADER as FALSE?  And even more puzzling is why am I asking you why I did something :) Shouldn't headers typically be included in the output?  And then there is CURLOPT_USERAGENT which I appear to be including for no reason.  On a related note, it seems to me that I should always use default values unless I have a specific reason to do differently.

 

Agree that the order doesn't matter, but the key values do, and array_merge() will re-order the keys from 0 to N.

Link to comment
Share on other sites

Headers in output, yes. Headers in the output/return from cURL you don't want. Like, do you really want to see all the headers and the whole response, or just the response?

 

And array_merge() will reorder numeric keys. It leaves string keys alone.

Link to comment
Share on other sites

Headers in output, yes. Headers in the output/return from cURL you don't want. Like, do you really want to see all the headers and the whole response, or just the response?

 

And array_merge() will reorder numeric keys. It leaves string keys alone.

Ah, gotcha regarding the headers.

 

Regarding numeric and string keys, are you sure CURLOPT_RETURNTRANSFER, etc are strings? 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.