NotionCommotion Posted December 4, 2017 Share Posted December 4, 2017 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; } Quote Link to comment Share on other sites More sharing options...
requinix Posted December 4, 2017 Share Posted December 4, 2017 - 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted December 4, 2017 Author Share Posted December 4, 2017 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. Quote Link to comment Share on other sites More sharing options...
requinix Posted December 4, 2017 Share Posted December 4, 2017 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted December 4, 2017 Author Share Posted December 4, 2017 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? Quote Link to comment Share on other sites More sharing options...
requinix Posted December 4, 2017 Share Posted December 4, 2017 ... Quote Link to comment Share on other sites More sharing options...
requinix Posted December 4, 2017 Share Posted December 4, 2017 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.