<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>
<channel>
	<title>Comments on: Asynchronous/parallel HTTP requests using PHP multi_curl</title>
	<atom:link href="http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/</link>
	<description>A blog about killer code</description>
	<pubDate>Thu, 04 Dec 2008 21:04:51 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
		<item>
		<title>By: Scot</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-258</link>
		<dc:creator>Scot</dc:creator>
		<pubDate>Fri, 07 Nov 2008 19:58:04 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-258</guid>
		<description>Would it be possible to use this library to get asynchronous notifications when reading a very large binary file?  We want to be able to read a very large binary file and periodically process fixed length buffers read from this binary file.</description>
		<content:encoded><![CDATA[<p>Would it be possible to use this library to get asynchronous notifications when reading a very large binary file?  We want to be able to read a very large binary file and periodically process fixed length buffers read from this binary file.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Sun Location</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-236</link>
		<dc:creator>Sun Location</dc:creator>
		<pubDate>Thu, 09 Oct 2008 09:07:30 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-236</guid>
		<description>Thanks you for this article about multi curl uses</description>
		<content:encoded><![CDATA[<p>Thanks you for this article about multi curl uses</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: jaisen</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-234</link>
		<dc:creator>jaisen</dc:creator>
		<pubDate>Mon, 06 Oct 2008 23:29:04 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-234</guid>
		<description>3 of my coworkers and I spent a few days trying to figure out why this was happening.  In our case we had something which took 4 seconds in between making the request and getting the results.  We tracked it down to a magic number of ~2 seconds which would break the curl requests.  No guarantee that your problem is the same as ours but it sounds almost identical.

What was happening on our end was that requests were not completely being sent to the server.  It was establishing the connection but never sending the request.  If we looked at tcpdump we noticed that we got an ACK then a FIN.  So then it waits 4 seconds and by the time we come back to get our results curl has timed out.  

One major problem is that there's no way to differentiate between data which needs to be sent from data which needs to be received.  We want to block for the first but not for the second (unless we're asking for said data).  This isn't a problem specifically with PHP's curl.  The underlying curl library doesn't expose this nor do the system calls.

I have been meaning to patch this.  I will do that in the next day or two and then add a comment to this post.

Thanks for the feedback...this bug was a pain in the tail!</description>
		<content:encoded><![CDATA[<p>3 of my coworkers and I spent a few days trying to figure out why this was happening.  In our case we had something which took 4 seconds in between making the request and getting the results.  We tracked it down to a magic number of ~2 seconds which would break the curl requests.  No guarantee that your problem is the same as ours but it sounds almost identical.</p>
<p>What was happening on our end was that requests were not completely being sent to the server.  It was establishing the connection but never sending the request.  If we looked at tcpdump we noticed that we got an ACK then a FIN.  So then it waits 4 seconds and by the time we come back to get our results curl has timed out.  </p>
<p>One major problem is that there&#8217;s no way to differentiate between data which needs to be sent from data which needs to be received.  We want to block for the first but not for the second (unless we&#8217;re asking for said data).  This isn&#8217;t a problem specifically with PHP&#8217;s curl.  The underlying curl library doesn&#8217;t expose this nor do the system calls.</p>
<p>I have been meaning to patch this.  I will do that in the next day or two and then add a comment to this post.</p>
<p>Thanks for the feedback&#8230;this bug was a pain in the tail!</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Alan H</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-233</link>
		<dc:creator>Alan H</dc:creator>
		<pubDate>Mon, 06 Oct 2008 23:13:11 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-233</guid>
		<description>Cool code.

I'm having a weird problem with it though:

I have a server application which I call several times in a loop process the responses later.  The server expects username and password so I have a static wrapper class which sets all the required cURL options and then calls EpiCurl::getInstance followed by addUrl.

This all works fine if I call in a loop, and then immediately process the results.  If, however I simulate some processing by adding a sleep(5) or a big loop, then go process the responses the first response is an authentication error from the server.

So: (inside my static class)

static function wrapper(url)
{
  $ch = curl_init();
  // set lots of cURL options inc...
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
  curl_setopt($ch, CURLOPT_USERPWD, $username . ":" . $password);
  // etc
  $mc = EpiCurl::getInstance();
  return $mc-&#62;addCurl($ch);
}

Then: (in my client code)

$cha = array();
$urls = array(...urls...);
foreach ($urls as $url)
{
  $cha[] = staticclass::wrapper($url);
}

sleep(5);

foreach ($cha as $ch)
{
  print $ch-&#62;data;
}

Without the sleep(5) this works fine on each url.  With the sleep(5) the first response is an authentication error.

If I put $x = $cha[0]-&#62;code after the first loop (before the sleep(5)) then everything works fine.

Any ideas?  This has me stumped!

Thanks,
Alan</description>
		<content:encoded><![CDATA[<p>Cool code.</p>
<p>I&#8217;m having a weird problem with it though:</p>
<p>I have a server application which I call several times in a loop process the responses later.  The server expects username and password so I have a static wrapper class which sets all the required cURL options and then calls EpiCurl::getInstance followed by addUrl.</p>
<p>This all works fine if I call in a loop, and then immediately process the results.  If, however I simulate some processing by adding a sleep(5) or a big loop, then go process the responses the first response is an authentication error from the server.</p>
<p>So: (inside my static class)</p>
<p>static function wrapper(url)<br />
{<br />
  $ch = curl_init();<br />
  // set lots of cURL options inc&#8230;<br />
  curl_setopt($ch, CURLOPT_URL, $url);<br />
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);<br />
  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);<br />
  curl_setopt($ch, CURLOPT_USERPWD, $username . &#8220;:&#8221; . $password);<br />
  // etc<br />
  $mc = EpiCurl::getInstance();<br />
  return $mc-&gt;addCurl($ch);<br />
}</p>
<p>Then: (in my client code)</p>
<p>$cha = array();<br />
$urls = array(&#8230;urls&#8230;);<br />
foreach ($urls as $url)<br />
{<br />
  $cha[] = staticclass::wrapper($url);<br />
}</p>
<p>sleep(5);</p>
<p>foreach ($cha as $ch)<br />
{<br />
  print $ch-&gt;data;<br />
}</p>
<p>Without the sleep(5) this works fine on each url.  With the sleep(5) the first response is an authentication error.</p>
<p>If I put $x = $cha[0]-&gt;code after the first loop (before the sleep(5)) then everything works fine.</p>
<p>Any ideas?  This has me stumped!</p>
<p>Thanks,<br />
Alan</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonny 5</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-230</link>
		<dc:creator>Jonny 5</dc:creator>
		<pubDate>Wed, 03 Sep 2008 03:18:39 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-230</guid>
		<description>Hello again,

first, thanks a lot for your work :)

I had some problems with memory overflow, when running in php-cli. I'm not experienced with classes, so please correct me.

My own function uses class EpiCurl to pull X sources. After that I call destruct, which I added to EpiCurl:

function __destruct()
{
  // close handles
  foreach($this-&#62;requests AS $ch){
    curl_close($ch);
  }
  
  // close multi handle
  if(curl_multi_info_read($this-&#62;mc)){
    curl_multi_close($this-&#62;mc);
  }
  
  // unset misc
  unset( 
    $this-&#62;requests,
    $this-&#62;responses,
    $this-&#62;properties,
    $this-&#62;running,
    $this-&#62;mc
  );
  
  // reset class
  self::__construct();
}

regards, Robert</description>
		<content:encoded><![CDATA[<p>Hello again,</p>
<p>first, thanks a lot for your work :)</p>
<p>I had some problems with memory overflow, when running in php-cli. I&#8217;m not experienced with classes, so please correct me.</p>
<p>My own function uses class EpiCurl to pull X sources. After that I call destruct, which I added to EpiCurl:</p>
<p>function __destruct()<br />
{<br />
  // close handles<br />
  foreach($this-&gt;requests AS $ch){<br />
    curl_close($ch);<br />
  }</p>
<p>  // close multi handle<br />
  if(curl_multi_info_read($this-&gt;mc)){<br />
    curl_multi_close($this-&gt;mc);<br />
  }</p>
<p>  // unset misc<br />
  unset(<br />
    $this-&gt;requests,<br />
    $this-&gt;responses,<br />
    $this-&gt;properties,<br />
    $this-&gt;running,<br />
    $this-&gt;mc<br />
  );</p>
<p>  // reset class<br />
  self::__construct();<br />
}</p>
<p>regards, Robert</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Jonny 5</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-229</link>
		<dc:creator>Jonny 5</dc:creator>
		<pubDate>Tue, 02 Sep 2008 02:55:35 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-229</guid>
		<description>Just wanted to point out:

curl_multi_info_read = PHP 5.2+
http://at.php.net/curl_multi_info_read</description>
		<content:encoded><![CDATA[<p>Just wanted to point out:</p>
<p>curl_multi_info_read = PHP 5.2+<br />
<a href="http://at.php.net/curl_multi_info_read" rel="nofollow">http://at.php.net/curl_multi_info_read</a></p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Alistair</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-228</link>
		<dc:creator>Alistair</dc:creator>
		<pubDate>Wed, 13 Aug 2008 14:38:20 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-228</guid>
		<description>Yes, that's pretty much what I am now doing.

I also think it would be good practice to close the curl handles after they are removed from multi-CURL as:

"When a single transfer is completed, the easy handle is still left added to the multi stack. You need to first remove the easy handle with url_multi_remove_handle(3) and then close it with curl_easy_cleanup(3), or possibly set new options to it and add it again with curl_multi_add_handle(3) to start another transfer."
(source http://curl.haxx.se/libcurl/c/libcurl-multi.html)

Re: credits. Feel free to put in my name, but not email please.</description>
		<content:encoded><![CDATA[<p>Yes, that&#8217;s pretty much what I am now doing.</p>
<p>I also think it would be good practice to close the curl handles after they are removed from multi-CURL as:</p>
<p>&#8220;When a single transfer is completed, the easy handle is still left added to the multi stack. You need to first remove the easy handle with url_multi_remove_handle(3) and then close it with curl_easy_cleanup(3), or possibly set new options to it and add it again with curl_multi_add_handle(3) to start another transfer.&#8221;<br />
(source <a href="http://curl.haxx.se/libcurl/c/libcurl-multi.html" rel="nofollow">http://curl.haxx.se/libcurl/c/libcurl-multi.html</a>)</p>
<p>Re: credits. Feel free to put in my name, but not email please.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: jaisen</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-227</link>
		<dc:creator>jaisen</dc:creator>
		<pubDate>Wed, 13 Aug 2008 07:43:55 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-227</guid>
		<description>@Alistair,

Thanks for pointing that out.  Here are the changes I made as a result.

...
if($res === CURLM_OK &#124;&#124; $res === CURLM_CALL_MULTI_PERFORM)
    {
      do {
          $mrc = curl_multi_exec($this-&gt;mc, $active);
      } while ($mrc === CURLM_CALL_MULTI_PERFORM);
...

Is that similar to your fix?

Can I put your name/email in the notes for credits for the fix in the source?</description>
		<content:encoded><![CDATA[<p>@Alistair,</p>
<p>Thanks for pointing that out.  Here are the changes I made as a result.</p>
<p>&#8230;<br />
if($res === CURLM_OK || $res === CURLM_CALL_MULTI_PERFORM)<br />
    {<br />
      do {<br />
          $mrc = curl_multi_exec($this->mc, $active);<br />
      } while ($mrc === CURLM_CALL_MULTI_PERFORM);<br />
&#8230;</p>
<p>Is that similar to your fix?</p>
<p>Can I put your name/email in the notes for credits for the fix in the source?</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Alistair</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-226</link>
		<dc:creator>Alistair</dc:creator>
		<pubDate>Wed, 13 Aug 2008 07:22:48 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-226</guid>
		<description>I believe there is an error in the script, as it was failing on my server. The problem is at the following if statement:

$res = curl_multi_add_handle($this-&#62;mc, $ch);  
if($res == 0)  
{  
curl_multi_exec($this-&#62;mc, $active);

One of the responses that can be returned by curl_multi_handle is '-1'. This is described in the CURL error list (http://curl.haxx.se/libcurl/c/libcurl-errors.html) as: 

"CURLM_CALL_MULTI_PERFORM (-1)

This is not really an error. It means you should call curl_multi_perform(3) again without doing select() or similar in between. "

I don't think the script should fail on this 'error' as it is not really an error. Changing the if statement to execute if a -1 is discovered fixes the problem. NB this does not appear to be a problem with all versions of PHP.</description>
		<content:encoded><![CDATA[<p>I believe there is an error in the script, as it was failing on my server. The problem is at the following if statement:</p>
<p>$res = curl_multi_add_handle($this-&gt;mc, $ch);<br />
if($res == 0)<br />
{<br />
curl_multi_exec($this-&gt;mc, $active);</p>
<p>One of the responses that can be returned by curl_multi_handle is &#8216;-1&#8242;. This is described in the CURL error list (http://curl.haxx.se/libcurl/c/libcurl-errors.html) as: </p>
<p>&#8220;CURLM_CALL_MULTI_PERFORM (-1)</p>
<p>This is not really an error. It means you should call curl_multi_perform(3) again without doing select() or similar in between. &#8221;</p>
<p>I don&#8217;t think the script should fail on this &#8216;error&#8217; as it is not really an error. Changing the if statement to execute if a -1 is discovered fixes the problem. NB this does not appear to be a problem with all versions of PHP.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: jaisen</title>
		<link>http://www.jaisenmathai.com/blog/2008/05/29/asynchronous-parallel-http-requests-using-php-multi_curl/#comment-220</link>
		<dc:creator>jaisen</dc:creator>
		<pubDate>Fri, 04 Jul 2008 06:17:44 +0000</pubDate>
		<guid isPermaLink="false">http://www.jaisenmathai.com/blog/?p=28#comment-220</guid>
		<description>@Raul,

You could do it in the same manner as you would if you were calling different urls.  You won't be guaranteed the order in which the requests are processed though.

In the last sample code simply replace yahoo.com, google.com and slooow.com urls with identical urls.</description>
		<content:encoded><![CDATA[<p>@Raul,</p>
<p>You could do it in the same manner as you would if you were calling different urls.  You won&#8217;t be guaranteed the order in which the requests are processed though.</p>
<p>In the last sample code simply replace yahoo.com, google.com and slooow.com urls with identical urls.</p>
]]></content:encoded>
	</item>
</channel>
</rss>
