0

I am calling a REST service using php curl. If an error occurs (for example because I posted invalid data) the REST server returns error code 400 and provides informative application error details in the response header custom field.

However, when error 400 occurs the header is not provided in the result from curl_exec() at it returns FALSE even though setopt as been provided. Headers are seen if code returned is 2xx.

curl_setopt($curl,CURLOPT_HEADER, 1);

Is there any way to get the response headers on errors >= 400?

steved
  • 31
  • 5
  • Duplicate of https://stackoverflow.com/questions/41978957/get-header-from-php-curl-response ? – Lee Salminen Nov 13 '19 at 22:41
  • not a duplicate. Headers work fine under normal circumstances. But do not get exposed by php curl if error code >= 400 is present. Hence my question. – steved Nov 13 '19 at 23:29
  • Check out my answer below. In my example code, we're using the response from that other SO post to call a URL that returns HTTP 400 status code. I'm able to get an array with the response headers. – Lee Salminen Nov 13 '19 at 23:35
  • Please provide the code that you've attempted to solve this problem with as a [mcve]. Because this works just fine: `$ch = curl_init("https://httpstat.us/400"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_HEADER=>true]); print_r(curl_exec($ch));` – miken32 Nov 14 '19 at 00:01
  • Per my comment below. The actual problem I was seeing was caused by CURLOPT_FAILONERROR set to 1. Removing that solved my issue. – steved Nov 14 '19 at 03:40

2 Answers2

1

In the example below, I'm using https://httpstat.us/400 to simulate a HTTP 400 response code.

<?php

// create curl resource
$ch = curl_init();

// set url that responds with HTTP 400 status
curl_setopt($ch, CURLOPT_URL, "https://httpstat.us/400");

//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//enable headers
curl_setopt($ch, CURLOPT_HEADER, 1);
//get only headers
curl_setopt($ch, CURLOPT_NOBODY, 1);
// $output contains the output string
$output = curl_exec($ch);

// close curl resource to free up system resources
curl_close($ch);

$headers = [];
$output = rtrim($output);
$data = explode("\n",$output);
$headers['status'] = $data[0];
array_shift($data);

foreach($data as $part){

    //some headers will contain ":" character (Location for example), and the part after ":" will be lost, Thanks to @Emanuele
    $middle = explode(":",$part,2);

    //Supress warning message if $middle[1] does not exist, Thanks to @crayons
    if ( !isset($middle[1]) ) { $middle[1] = null; }

    $headers[trim($middle[0])] = trim($middle[1]);
}

// Print all headers as array
print_r($headers);

This returns

Array
(
    [status] => HTTP/1.1 400 Bad Request
    [Cache-Control] => private
    [Content-Length] => 15
    [Content-Type] => text/plain; charset=utf-8
    [Server] => Microsoft-IIS/10.0
    [X-AspNetMvc-Version] => 5.1
    [Access-Control-Allow-Origin] => *
    [X-AspNet-Version] => 4.0.30319
    [X-Powered-By] => ASP.NET
    [Set-Cookie] => ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
    [Date] => Wed, 13 Nov 2019 23:31:51 GMT
)

That array with all response headers matches up with what I get when I use curl from my terminal:

curl -v https://httpstat.us/400

returns

< HTTP/1.1 400 Bad Request
< Cache-Control: private
< Content-Length: 15
< Content-Type: text/plain; charset=utf-8
< Server: Microsoft-IIS/10.0
< X-AspNetMvc-Version: 5.1
< Access-Control-Allow-Origin: *
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< Set-Cookie: ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
< Date: Wed, 13 Nov 2019 23:33:19 GMT
Lee Salminen
  • 900
  • 8
  • 18
  • 1
    Thanks for the sample. Turns out buried in my code I had CURLOPT_FAILONERROR set to 1, this was the culprit. – steved Nov 14 '19 at 00:11
0

Here's another option using the CURLOPT_HEADERFUNCTION option with a callback function:

<?php

// this holds the response headers from the curl call
$responseHeaders = array();

// this function processes the response headers from the curl call
function curlResponseHeaderCallback($ch, $headerLine) {
    global $responseHeaders;

    // trim all the whitespace on this line
    $trimmed = trim($headerLine);

    // only proceed if the string is not empty
    if(!empty($trimmed)) {
        // headers follow Key: Value format
        $split   = explode(':', $trimmed);

        // only proceed if the value of the header is not empty
        if(!empty($split[1])) {
            // $split[0] is the Key of the response header
            // $split[1] is the Value of the response header, which can also have whitespace
            $responseHeaders[$split[0]] = trim($split[1]);
        }
    }

    // who knows why, but you have to return this.
    return strlen($headerLine);
}

// get cURL resource
$ch = curl_init();

// set url
curl_setopt($ch, CURLOPT_URL, "https://httpstat.us/400");

curl_setopt($ch, CURLOPT_HEADERFUNCTION, "curlResponseHeaderCallback");

// send the request
curl_exec($ch);

// close the handle
curl_close($ch);

print_r($responseHeaders);

returns

Array
(
    [Cache-Control] => private
    [Content-Length] => 15
    [Content-Type] => text/plain; charset=utf-8
    [Server] => Microsoft-IIS/10.0
    [X-AspNetMvc-Version] => 5.1
    [Access-Control-Allow-Origin] => *
    [X-AspNet-Version] => 4.0.30319
    [X-Powered-By] => ASP.NET
    [Set-Cookie] => ARRAffinity=93fdbab9d364704de8ef77182b4d13811344b7dd1ec45d3a9682bbd6fa154ead;Path=/;HttpOnly;Domain=httpstat.us
    [Date] => Wed, 13 Nov 2019 23
)
Lee Salminen
  • 900
  • 8
  • 18