PHP curl changes the Content-Type to application/x-www-form-urlencoded. Shouldn't do that

28,691

Solution 1

According to the documentation section regarding the options' parameter setting the HTTP method to POST defaults to content type being set to application/x-www-form-urlencoded. So my suspicion is that setting all the options at once, results in your Content-Type being overwritten.
My suggestion would be to set the content type in a single statement, after you've set the method, i.e.

$curlConfig = array(
    CURLOPT_URL => 'http://uploads.gdata.youtube.com/feeds/api/users/default/uploads',
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_BINARYTRANSFER => true,
    CURLOPT_POSTFIELDS => $content,
);

curl_setopt_array($ch, $curlConfig);

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Authorization: ' . sprintf('GoogleLogin auth=%s', $this->accessToken),
    'GData-Version: 2',
    'X-GData-Key: ' . sprintf('key=%s', $this->developerKey),
    'Slug: ' . sprintf('%s', $this->video->getFilename()),
    'Content-Type: ' . sprintf('multipart/related; boundary="%s"',
                                  $this->boundaryString),
    'Content-Length: ' . strlen($content),
    'Connection: close'
));

Solution 2

Accepted answer was not helpful for me and I found another problem in your code. According to the current PHP documentation CURLOPT_HTTPHEADER must be set like this:

curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-type: text/plain',
    'Content-length: 100'
));

It must be a list of strings, not a hash like array('Content-Type' => 'text/plain'). Hope it will save someone's debugging time.

Share:
28,691
Johannes Klauß
Author by

Johannes Klauß

Software Engineer at Spaceteams.

Updated on August 19, 2022

Comments

  • Johannes Klauß
    Johannes Klauß almost 2 years

    I want to upload a video direct to Youtube from my server for which I am using PHP curl.

    I need this request format:

    POST /feeds/api/users/default/uploads HTTP/1.1
    Host: uploads.gdata.youtube.com
    Authorization: Bearer ACCESS_TOKEN
    GData-Version: 2
    X-GData-Key: key=adf15ee97731bca89da876c...a8dc
    Slug: video-test.mp4
    Content-Type: multipart/related; boundary="f93dcbA3"
    Content-Length: 1941255
    Connection: close
    
    --f93dcbA3
    Content-Type: application/atom+xml; charset=UTF-8
    
    <?xml version="1.0"?>
    <entry xmlns="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/"
      xmlns:yt="http://gdata.youtube.com/schemas/2007">
      <media:group>
        <media:title type="plain">Bad Wedding Toast</media:title>
        <media:description type="plain">
          I gave a bad toast at my friend's wedding.
        </media:description>
        <media:category
          scheme="http://gdata.youtube.com/schemas/2007/categories.cat">People
        </media:category>
        <media:keywords>toast, wedding</media:keywords>
      </media:group>
    </entry>
    --f93dcbA3
    Content-Type: video/mp4
    Content-Transfer-Encoding: binary
    
    <Binary File Data>
    --f93dcbA3--
    

    This is what I have:

    $content = $this->buildRequestContent();
    
    $ch = curl_init();
    
    $curlConfig = array(
        CURLOPT_URL => 'http://uploads.gdata.youtube.com/feeds/api/users/default/uploads',
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_BINARYTRANSFER => true,
        CURLOPT_POSTFIELDS => $content,
        CURLOPT_HTTPHEADER, array(
            "Authorization" => sprintf("GoogleLogin auth=%s", $this->accessToken),
            "GData-Version" => 2,
            "X-GData-Key" => sprintf("key=%s", $this->developerKey),
            "Slug" => sprintf("%s", $this->video->getFilename()),
            "Content-Type" => sprintf("multipart/related; boundary=\"%s\"", $this->boundaryString),
            "Content-Length" => strlen($content),
            "Connection" => "close"
        ),
    );
    
    curl_setopt_array($ch, $curlConfig);
    
    $result = curl_exec($ch);
    

    Dumping the result shows me that curl changed the Content-Type to application/x-www-form-urlencoded which is of course not supported by youtube.

    I put my binary content (the video) into CURLOPT_POSTFIELDS, maybe this is wrong, but I don't know how to set the request body other than that.

    So how do I preserve my Content-Type that I set?

  • andreszs
    andreszs over 9 years
    In other words, you cannot pass an associative array to CURLOPT_HTTPHEADER, only simple arrays. This issue drove me mad for hours...
  • John Congdon
    John Congdon about 9 years
    Please update your example, it's not a two dimensional array. It should be: array("Authorization: " . sprintf..., "Content-Type: " . sprintf....)
  • Havelock
    Havelock about 9 years
    @JohnCongdon: correct, thanks! It's funny nobody noticed it in just over two years, three upvotes and "accepted answer". (I'm sure you meant "not an associative array" instead of "not a two dimensional array" ;) )
  • NeverEndingQueue
    NeverEndingQueue over 7 years
    Shame for the PHP cURL. Not only CURLOPT_POST overrides the Content-Type, but prevents the other HTTP headers to be set at the same time. Thanks for that solution !
  • dmarra
    dmarra almost 5 years
    I can't get this solution to work. I have separated the setting of the header from the rest of the options and it STILL adds the additional content-type.
  • Havelock
    Havelock almost 5 years
    @dmarra I'd suggest to just use a dedicated client library like GuzzleHttp for instance