Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP stream_context_create and HTTP headers - array or string, \r\n at the end, Content-Length optional?

Tags:

http

php

header

I am troubleshooting some problems with POSTing to a remote site, specifically the remote host never returns any data (empty string).

Before I try to troubleshoot anything else, I want to make sure the calling code is actually correct. The code is:

$context = stream_context_create(array('http' => array(
    'method'        => "POST",
    'header'        => "Content-Type: application/xml",
    'timeout'       => 60.0,
    'ignore_errors' => true, # return body even if HTTP status != 200
    'content'       => $send_xml
)));

$response = trim(file_get_contents($this->bulk_service_url, false, $context));

All my questions belong to the "header" option and it's values, and how to correctly format it and write it. The PHP documentation, discussion below it and even stackoverflow research yield very inconsistent results.

1) do I have to include the Content-Length header, and if not, will PHP calculate it correctly? The documentation does not include it, but I've seen many people include it manually, is it then respected or overwritten by PHP?

2) do I have to pass the header option as a string, or an associative array? Manual says string, majority pass it as a string, but this comment says that if PHP was compiled with --with-curlwrappers option, you have to pass it as an array. This is very inconsistent behavior.

3) when passing as a string, do I have to include terminating \r\n characters? Especially when specifying just one header. Manual does not provide such an example, first comment on manual page does include it, second one does not, again, no clear rule on how to specify this. Does PHP automatically handle both cases?

The server is using PHP 5.3.

like image 619
user2905418 Avatar asked Oct 22 '13 03:10

user2905418


3 Answers

You should really store your headers within code as an array and finalize the preparation just prior to sending the request...

function prepareHeaders($headers) {
  $flattened = array();

  foreach ($headers as $key => $header) {
    if (is_int($key)) {
      $flattened[] = $header;
    } else {
      $flattened[] = $key.': '.$header;
    }
  }

  return implode("\r\n", $flattened);
}

$headers = array(
  'Content-Type' => 'application/xml',
  'ContentLength' => $dl,
);

$context = stream_context_create(array('http' => array(
  'method'        => "POST",
  'header'        => prepareHeaders($headers),
  'timeout'       => 60.0,
  'ignore_errors' => true,
  'content'       => $send_xml
)));

$response = trim(file_get_contents($url, FALSE, $context));
like image 160
doublejosh Avatar answered Nov 16 '22 10:11

doublejosh


Preparing context try to add:

  1. ContentLength: {here_calculated_length} in 'header' key preceded with \r\n
  2. "\r\n" at the end of 'header' key.

So it should look like:

$dl = strlen($send_xml);//YOUR_DATA_LENGTH
$context = stream_context_create(array('http' => array(
            'method'        => "POST",
            'header'        => "Content-Type: application/xml\r\nContentLength: $dl\r\n",
            'timeout'       => 60.0,
            'ignore_errors' => true, # return body even if HTTP status != 200
            'content'       => $send_xml
        )));
like image 2
user2276146 Avatar answered Nov 16 '22 08:11

user2276146


Just a little improvement of the suggestion by @doublejosh, in case it helps someone:
(use of array notation and one-liner lambda function)

    $headers = [
      'Content-Type'   => 'application/xml',
      'Content-Length' => strlen($send_xml)
    ];
    
    $context = stream_context_create(['http' => [
        'method'       => "POST",
        'header'       => array_map(function ($h, $v) {return "$h: $v";}, array_keys($headers), $headers),
        'timeout'      => 60.0,
        'ignore_errors'=> true, 
        'content'      => $send_xml
        ]
    ]);
like image 1
Cédric Françoys Avatar answered Nov 16 '22 10:11

Cédric Françoys