CURLE_SEND_FAIL_REWIND on 303 Http Code

1,292 views
Skip to first unread message

Donald White

unread,
Apr 25, 2012, 1:50:10 PM4/25/12
to guz...@googlegroups.com
After doing a POST, I'm getting a 303 response (see other) then Guzzle fails with a CURLE_SEND_FAIL_REWIND (65) Error.  I don't have any additional curl settings, i'm using Guzzle as is.

The correct implementation for this post should be: GUZZLE POST -> REST SERVER provides 303 & location header -> GUZZLE sends a GET to the location header provided -> REST SERVER provides data & a 200 response.

My setup:
PHP: 5.3.8 on Zend Server: ZS5.5.0
cURL Support Enabled Version: 7.20.1

I'm trying to replace Zend_Http_Client with Guzzle, which has proven difficult since Symfony is required for most of the events and loading.  Plus the phar isn't working so I've included the entire Guzzle library, even though phar support is enabled on my machine.

Michael

unread,
Apr 25, 2012, 2:03:41 PM4/25/12
to guz...@googlegroups.com
Hi Donald,

I'm not sure what's going on with the POST issue you are seeing.  This error is apparently caused when cURL needs to resend data, but fails to rewind the data that needs to be sent.  Are you sending raw POST data or a application/x-www-form-urlencoded (form/array based) request?  If you are sending raw POST data, is your entity body using a remote stream or just using a temp stream with text?  I wonder if cURL is trying to resend the POST data after the 303 redirect.   I'll have to do some digging to figure this out, but a repo script would help speed this up.

As for not being able to use the phar: Are you using http://guzzlephp.org/guzzle.phar?  What is the error you're getting when including the phar and attempting to instantiate a class?

I just did a basic test of the phar file to ensure that it's working.  Does this work for you?

wget http://guzzlephp.org/guzzle.phar
php -r 'require "guzzle.phar"; $c = new Guzzle\Service\Client; echo $c->get("http://www.google.com")->send();'

-Michael

Donald White

unread,
Apr 25, 2012, 2:15:04 PM4/25/12
to guz...@googlegroups.com
Hey Michael,

Thanks for the quick reply! Phar seems to work using that method.  It must be clashing with Zend's Autoloader.  I haven't seen any examples of using Guzzle in Zend other than the logger and cache examples.  If I simply include the phar and not the entire library I get: Fatal error: Class 'Guzzle\Service\Client' not found

As for the POST, I am using Raw post data using setBody on the request.  The content type is being set and it's "application/json". The body is simply "{}", just an empty json object.
If I'm understanding your question about the stream, it's a temp stream, but I thought that was just the cache for the request:

object(Guzzle\Http\Message\EntityEnclosingRequest)[3274]
      protected 'body' => 
        object(Guzzle\Http\EntityBody)[3278]
          protected 'contentEncoding' => boolean false
          protected 'stream' => resource(67, stream)
          protected 'size' => null
          protected 'cache' => 
            array (size=9)
              'wrapper_type' => string 'php' (length=3)
              'stream_type' => string 'temp' (length=4)
              'mode' => string 'w+b' (length=3)
              'unread_bytes' => int 0
              'seekable' => boolean true
              'uri' => string 'php://temp' (length=10)
              'is_local' => boolean true
              'is_readable' => boolean true
              'is_writable' => boolean true

Donald White

unread,
Apr 25, 2012, 2:22:03 PM4/25/12
to guz...@googlegroups.com
I might also mention that using Zend_Http_Client instead of Guzzle with the same exact url does what I'm expecting, without the error.  The headers are the same, it just seems that Guzzle stops at 303 and won't continue.

Dowling, Michael

unread,
Apr 25, 2012, 2:27:24 PM4/25/12
to guz...@googlegroups.com
Have you tried including the Guzzle phar before setting up the Zend autoloader?

Do you think the POST issue is related to https://github.com/guzzle/guzzle/issues/23?

Donald White

unread,
Apr 25, 2012, 3:15:46 PM4/25/12
to guz...@googlegroups.com
That's highly possible, since cURL supports POST -> GET and not POST -> POST by default from what php.net is saying. Is guzzle doing something else to only support one?  I looked through the posts and there seems to be a bit of conflicting data.

Donald White

unread,
Apr 25, 2012, 3:57:24 PM4/25/12
to guz...@googlegroups.com
I was able to track the packets using wireshark. From what I see,

POST {url} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Expect: 100-Continue
Content-Length: 2

HTTP/1.1 100 Continue

{}HTTP/1.1 303 See Other
Server: Apache-Coyote/1.1
X-Conversation-Id: {sessionid}
Location: {new url}
Date: Wed, 25 Apr 2012 19:27:15 GMT
Content-Type: application/json
Content-Length: 0

Then it just dies with the curle_send_fail_rewind (65) error.  It's not attempting to post, or at least no attempts are made after the 303.  Is it possible that the expect 100-continue is causing an issue?

Dowling, Michael

unread,
Apr 25, 2012, 4:02:45 PM4/25/12
to guz...@googlegroups.com
Thanks for providing the packets.  That's really helpful.  Could you try removing the Expect header?

$request->post('http://example.com', null, '{}');
$request->removeHeader('Expect');
echo $request->send();

-Michael

Donald White

unread,
Apr 25, 2012, 4:09:37 PM4/25/12
to guz...@googlegroups.com
same issue as before:

POST {url} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Content-Length: 2

{}HTTP/1.1 303 See Other
Server: Apache-Coyote/1.1
X-Conversation-Id: {sessionid}
Location: {new url}
Date: Wed, 25 Apr 2012 20:06:52 GMT
Content-Type: application/json
Content-Length: 0

Dowling, Michael

unread,
Apr 25, 2012, 4:29:17 PM4/25/12
to guz...@googlegroups.com
I was able to reproduce this problem.  It appears to be a known issue with sending PUT and POST requests using a read callback that receive a redirect: https://bugs.php.net/bug.php?id=47204.  If this bug is affecting you, please vote for it to be fixed.

In the meantime, can you send your POST to the redirected URL?  Someone from that bug report claims that "this problem can be worked around by using CURLOPT_POSTFIELDS with POST requests".  So you could possibly override the createRequest method of your client so that when POST requests are created, you blacklist CURLOPT_READ_FUNCTION and set CURLOPT_POSTFIELDS using the contents of the request body--

Something like this might work, but it's untested:

$request->getCurlOptions()->set('curl.blacklist', array(CURLOPT_READ_FUNCTION))->set('curl.CURLOPT_POSTFIELDS', (string) $request->getBody());

Donald White

unread,
Apr 25, 2012, 4:50:24 PM4/25/12
to guz...@googlegroups.com
This may have gotten me a bit closer:  By setting the following:

$request->getCurlOptions()
    ->set(CURLOPT_READFUNCTION, false)
    ->set(CURLOPT_POSTFIELDS, (string) $body);
$request->removeHeader('Expect');

I will get the following packets:
POST {url} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Content-Length: 2

{}HTTP/1.1 303 See Other
Server: Apache-Coyote/1.1
X-Conversation-Id: {sessionid}
Location: {newurl}
Date: Wed, 25 Apr 2012 20:40:05 GMT
Content-Type: application/json
Content-Length: 0

POST {newurl} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Content-Length: 2

It now gets past the curl error, but returns nothing since it's supposed to be a GET rather than POST

Donald White

unread,
Apr 25, 2012, 6:57:31 PM4/25/12
to guz...@googlegroups.com
Ok, by modifying Guzzle\Http\Curl\CurlHandle: line 102

case 'POST':
    $curlOptions[CURLOPT_POST] = true;
    $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); // should be set automatically when posting data
    unset($curlOptions[CURLOPT_CUSTOMREQUEST]); // shouldn't be set when posting.
    break;

I get the following:

POST {url} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Content-Length: 2

{}
HTTP/1.1 303 See Other
Server: Apache-Coyote/1.1
X-Conversation-Id: {sessionid}
Location: {newurl}
Date: Wed, 25 Apr 2012 22:34:14 GMT
Content-Type: application/json
Content-Length: 0

GET {newurl} HTTP/1.1
Accept-Encoding: deflate, gzip
Host: {host}
X-Conversation-ID: {sessionid}
Accept: application/json
Content-type: application/json
User-Agent: Guzzle/2.0 (Language=PHP/5.3.8-ZS5.5.0; curl=7.20.1; Host=i386-apple-darwin9.8.0)
Content-Length: 2


Here's the problem, I'm getting a GET request to the 303 url, however it's not clearing the headers (Content-type, & content-length)!  Which without them the GET works perfectly, with them, it times out forever.

Donald White

unread,
Apr 26, 2012, 1:26:23 PM4/26/12
to guz...@googlegroups.com
SOLVED!

After adding a removeHeader('Content-Length') after my request is generated, it doesn't send it over GET after the 303!  All is well.  Thanks for your help!

vivek

unread,
Sep 10, 2012, 6:03:00 AM9/10/12
to guz...@googlegroups.com
I am trying to connect experian net connect api using curl and getting following error
HTTP/1.1 303 See Other
Server: Oracle-iPlanet-Web-Server/7.0
Date: Mon, 10 Sep 2012 09:57:30 GMT
Set-cookie: Actrust-session-v002b=L25ldGNvbm5lY3QyXzBEZW1vL3NlcnZsZXRzL05ldENvbm5lY3RTZXJ2bGV0; secure; domain=.experian.com; path=/
Content-length: 0
Location: /securecontrol/sso_logon.html
Connection: close


Please Advise.

Thanks,
Vivek

Matthias Choules

unread,
Oct 23, 2012, 12:56:51 PM10/23/12
to guz...@googlegroups.com
Hi Donald,

I just ran into the same issue but had no luck trying your solution. Is it possible, that you quickly summarize the needed steps?

Thanks in advance,
Matthias

Donald White

unread,
Oct 23, 2012, 4:33:09 PM10/23/12
to guz...@googlegroups.com
Hey Matthias,

You're getting CURL_SEND_FAIL_REWIND due to FOLLOW_LOCATION being set to true.  This is directly due to the problem in libcurl, and will likely never get resolved.  If you set the follow location to false, you should no longer get the error.  

That being said, you'll need to create a plugin for redirecting the request to the right location in guzzle.  Using the subscribe event onRequestComplete you can detect if the response is a redirect, than load the location header in the "303 see other" request.  Creating a new client is how I found to be the easiest way to clear the headers that aren't needed.  You can also do a "getConfig" from the original request's client to pull the default settings that you'd need to create the secondary client for the follow request.  If you're seeing an issue with timeouts when validating a cache request (304) do a clear of the multicurl instance on the new client before sending the request.
Reply all
Reply to author
Forward
0 new messages