$now) { echo "Info: reached rate limit on «{$napid}»; sleeping until ".date('c',$naps[$napid]).' ...'; sleep($naps[$napid]-$now); echo "\n"; $naps[$napid]=0; } } --- Example usage: --- $postData=['file'=>new CURLStringFile($file['content'],$file['name'],$file['type']), 'description'=>$file['description']; $url="https://{$conf['fedi_hostname']}/api/v2/media"; $res=curl($url,'/api/v2/media',["Authorization: Bearer {$conf['fedi_token']}", 'Accept: application/json'],$postData); if ($res['content']===false) { echo "Warning: could not connect to «{$url}» (error: «{$res['error']}»).\n"; } elseif (is_null($res['content']=@json_decode($res['content'],true))) { echo "Warning: «{$url}» did not return valid JSON.\n"; } elseif ($res['httpcode']!='200' && $res['httpcode']!='202') { (isset($res['content']['error'])) ? $buff=" (error: «{$res['content']['error']}»)" : $buff=''; echo "Warning: «{$url}» returned http code «{$res['httpcode']}»{$buff}.\n"; } elseif (!isset($res['content']['id'])) { echo "Warning: no «id» in JSON from «{$url}».\n"; } else { $id=$res['content']['id']; [...] } --- */ function curl($url,$napid=null,$headers=null,$postdata=null,$conntimeout=null,$functimeout=null,$proxy=null) { if (!is_null($napid)) cknap($napid); if (is_null($conntimeout)) $conntimeout=5; if (is_null($functimeout)) $functimeout=20; $ch=curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); curl_setopt($ch,CURLOPT_FAILONERROR,false); curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,$conntimeout); curl_setopt($ch,CURLOPT_TIMEOUT,$functimeout); curl_setopt($ch,CURLOPT_HEADER,true); // example $headers: ['Content-type: text/plain', 'Content-length: 100'] if (is_array($headers)) curl_setopt($ch,CURLOPT_HTTPHEADER,$headers); curl_setopt($ch,CURLOPT_ENCODING,''); curl_setopt($ch,CURLOPT_USERAGENT,'curl/1.0'); // curl automatically uses a "Content-Type" of "multipart/form-data" if $postdata is an array, of "application/x-www-form-urlencoded" if it's a string if (!is_null($postdata)) curl_setopt($ch,CURLOPT_POSTFIELDS,$postdata); if (!is_null($proxy)) { curl_setopt($ch,CURLOPT_PROXY,$proxy); curl_setopt($ch,CURLOPT_PROXYTYPE,CURLPROXY_SOCKS5); } $gheaders=null; $httpcode=null; $error=false; $res=curl_exec($ch); if ($res!==false) { $now=time(); $gheaders_sz=curl_getinfo($ch,CURLINFO_HEADER_SIZE); $gheaders=substr($res,0,$gheaders_sz-4);// "-4" accounts for the 2 "\r\n" that separates headers from body // echo "{$gheaders}\n"; $gheaders=explode("\r\n",$gheaders); // the line below is needed to discard from the top of the headers possible "HTTP/(version) 100 Continue" and the empty lines that may follow while (trim($gheaders[0])=='' || preg_match('#^HTTP/\d+\.\d+\s+100\s+#',$gheaders[0])===1) array_shift($gheaders); $res=substr($res,$gheaders_sz); $httpcode=preg_replace('#^\S+\s+(\d+).*$#','$1',$gheaders[0]); if (!is_null($napid)) { global $naps; foreach ($gheaders as $header) { if (preg_match('#^date: (.*)$#i',$header,$matches)===1) { $sdate=@strtotime($matches[1]); if ($sdate===false) break; } elseif (preg_match('#^x-ratelimit-remaining: (\d+)$#i',$header,$matches)===1) { $rlrem=$matches[1]+0; // echo "Ratelimit-remaining: {$rlrem}\n"; } elseif (preg_match('#^x-ratelimit-reset: (.*)$#i',$header,$matches)===1) { $rlres=@strtotime($matches[1]); if ($rlres===false) break; } if (isset($sdate) && isset($rlrem) && isset($rlres)) { // echo "date: {$sdate}; rem: {$rlrem}; res: {$rlres}.\n"; if ($rlrem==0) $naps[$napid]=$now+$rlres-$sdate+1; break; } } } } else { $error=curl_errno($ch).': '.trim(curl_error($ch)); } curl_close($ch); return ['content'=>$res,'httpcode'=>$httpcode,'headers'=>$gheaders,'error'=>$error]; } ?>