1342 lines
46 KiB
PHP
1342 lines
46 KiB
PHP
<?php
|
|
/* vim: set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// File: WEB ACCESS 2.1.3
|
|
// Date: 13.10.2011
|
|
// Author: Gilles Masson
|
|
// Updated: Xymph
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// This class and functions can be used to make asynchronous xml or http
|
|
// (POST or GET) queries.
|
|
// This means that you call a function to send the query, and a callback
|
|
// function will automatically be called when the response has arrived,
|
|
// without having your program waiting for the response.
|
|
// You can also use it for synchronous queries (see below).
|
|
// The class handles (for each URL) keepalive and compression (when possible).
|
|
// It supports Cookies, and so can use sessions like php one (anyway the cookie
|
|
// is not stored, so its maximal life is the life of the program).
|
|
//
|
|
//
|
|
// usage: $_webaccess = new Webaccess();
|
|
// $_webaccess->request($url, array('func_name',xxx), $datas, $is_xmlrpc, $keepalive_min_timeout);
|
|
// $url: the web script URL.
|
|
// $datas: string to send in http body (xml, xml_rpc or POST data)
|
|
// $is_xmlrpc: true if it's an xml or xml-rpc request, false if it's a
|
|
// standard html GET or POST
|
|
// $keepalive_min_timeout: minimal value of server keepalive timeout to
|
|
// send a keepalive request,
|
|
// else make a request with close connection.
|
|
// func_name is the callback function name, which will be called this way:
|
|
// func_name(array('Code'=>code,'Reason'=>reason,'Headers'=>headers,'Message'=>message), xxx)
|
|
// where:
|
|
// xxx is the same as given previously in callback description.
|
|
// code is the returned http code
|
|
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6)
|
|
// reason is the returned http reason
|
|
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6)
|
|
// headers are the http headers of the reply
|
|
// message is the returned text body
|
|
//
|
|
// IMPORTANT: to have this work, the main part of your program must include a
|
|
// $_webaccess->select() call periodically, which work exactly like stream_select().
|
|
// This is because the send and receive are asynchronous and will be completed
|
|
// later in the select() when datas are ready to be received and sent.
|
|
|
|
|
|
// This class can be used to make a synchronous query too. For that use null
|
|
// for the callback, so make the request this way:
|
|
// $response = $_webaccess->request($url, null, $datas, $is_xmlrpc, $keepalive_min_timeout);
|
|
// where $response is an array('Code'=>code,'Reason'=>reason,'Headers'=>headers,'Message'=>message)
|
|
// like the one passed to the callback function of the asynchronous request.
|
|
// If you use only synchronous queries then there is no need to call select()
|
|
// as the function will return when the reply will be fully returned.
|
|
// If the connection itself fail, the array response will include a 'Error' string.
|
|
|
|
|
|
// Other functions:
|
|
// list($host, $port, $path) = getHostPortPath($url);
|
|
// gzdecode() workaround
|
|
|
|
|
|
global $_web_access_compress_xmlrpc_request, $_web_access_compress_reply,
|
|
$_web_access_keepalive, $_web_access_keepalive_timeout,
|
|
$_web_access_keepalive_max, $_web_access_retry_timeout,
|
|
$_web_access_retry_timeout_max, $_web_access_post_xmlrpc;
|
|
|
|
|
|
// Will compress xmlrpc request ('never','accept','force','force-gzip','force-deflate')
|
|
// If set to 'accept' the first request will be made without, and the eventual
|
|
// 'Accept-Encoding' in reply will permit to decide if request compression can
|
|
// be used (and if gzip or deflate)
|
|
$_web_access_compress_xmlrpc_request = 'accept';
|
|
|
|
// Will ask server for compressed reply (false, true)
|
|
// If true then will add a 'Accept-Encoding' header to tell the server to
|
|
// compress the reply if it supports it.
|
|
$_web_access_compress_reply = true;
|
|
|
|
|
|
// Keep alive connection ? else close it after the reply.
|
|
// Unless false, first request will be with keepalive, to get server timeout
|
|
// and max values after timeout will be compared with the request
|
|
// $keepalive_min_timeout value to decide if keepalive have to be used or not.
|
|
// Note that Apache2 timeout is short (about 15s).
|
|
// The classes will open, re-open or use existing connection as needed.
|
|
$_web_access_keepalive = true;
|
|
// timeout(s) without request before close, for keepalive
|
|
$_web_access_keepalive_timeout = 600;
|
|
// max requests before close, for keepalive
|
|
$_web_access_keepalive_max = 2000;
|
|
|
|
|
|
// For asynchronous call, in case of error, timeout before retrying.
|
|
// It will be x2 for each error (on request or auto retry) until max,
|
|
// then stop automatic retry, and next request calls will return false.
|
|
// When stopped, a retry() or synchronous request will force a retry.
|
|
$_web_access_retry_timeout = 20;
|
|
$_web_access_retry_timeout_max = 5*60;
|
|
|
|
|
|
// Use text/html with xmlrpc=, instead of of pure text/xml request (false, true)
|
|
// Standard xml-rpc use pure text/xml request, where the xml is simply the body
|
|
// of the http request (and it's how the xml-rpc reply will be made). As a
|
|
// facility Dedimania also supports to get the xml in a html GET or POST,
|
|
// where xmlrpc= will contain a urlsafe base64 of the xml. Default to false,
|
|
// so use pure text/xml.
|
|
$_web_access_post_xmlrpc = false;
|
|
|
|
// Note that in each request the text/xml or xmlrpc= will be used only if
|
|
// $is_xmlrpc is true. If false then the request will be a standard
|
|
// application/x-www-form-urlencoded html GET or POST request; in that case
|
|
// you have to build the URL (GET) and/or body data (POST) yourself.
|
|
// If $is_xmlrpc is a string, then it's used as the Content-type: value.
|
|
|
|
|
|
require_once('includes/urlsafebase64.php');
|
|
|
|
class Webaccess {
|
|
|
|
var $_WebaccessList;
|
|
|
|
function Webaccess() {
|
|
$this->_WebaccessList = array();
|
|
}
|
|
|
|
|
|
function request($url, $callback, $datas, $is_xmlrpc = false, $keepalive_min_timeout = 300, $opentimeout = 3, $waittimeout = 5, $agent = 'XMLaccess') {
|
|
global $aseco, $_web_access_keepalive, $_web_access_keepalive_timeout, $_web_access_keepalive_max;
|
|
|
|
list($host, $port, $path) = getHostPortPath($url);
|
|
|
|
if ($host === false)
|
|
$aseco->console('Webaccess request(): Bad URL: ' . $url);
|
|
|
|
else {
|
|
$server = $host . ':' . $port;
|
|
// create object if needed
|
|
if (!isset($this->_WebaccessList[$server]) || $this->_WebaccessList[$server] === null) {
|
|
$this->_WebaccessList[$server] = new WebaccessUrl($this, $host, $port,
|
|
$_web_access_keepalive,
|
|
$_web_access_keepalive_timeout,
|
|
$_web_access_keepalive_max,
|
|
$agent);
|
|
}
|
|
|
|
// increase the default timeout for sync/wait request
|
|
if ($callback == null && $waittimeout == 5)
|
|
$waittimeout = 12;
|
|
|
|
// call request
|
|
if ($this->_WebaccessList[$server] !== null) {
|
|
$query = array('Path' => $path,
|
|
'Callback' => $callback,
|
|
'QueryData' => $datas,
|
|
'IsXmlrpc' => $is_xmlrpc,
|
|
'KeepaliveMinTimeout' => $keepalive_min_timeout,
|
|
'OpenTimeout' => $opentimeout,
|
|
'WaitTimeout' => $waittimeout
|
|
);
|
|
|
|
return $this->_WebaccessList[$server]->request($query);
|
|
}
|
|
}
|
|
return false;
|
|
} // request
|
|
|
|
|
|
function retry($url) {
|
|
global $aseco;
|
|
|
|
list($host, $port, $path) = getHostPortPath($url);
|
|
if ($host === false) {
|
|
$aseco->console('Webaccess retry(): Bad URL: ' . $url);
|
|
} else {
|
|
$server = $host . ':' . $port;
|
|
if (isset($this->_WebaccessList[$server]))
|
|
$this->_WebaccessList[$server]->retry();
|
|
}
|
|
} // retry
|
|
|
|
|
|
function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) {
|
|
|
|
$timeout = (int)($tv_sec*1000000 + $tv_usec);
|
|
if ($read == null)
|
|
$read = array();
|
|
if ($write == null)
|
|
$write = array();
|
|
if ($except == null)
|
|
$except = array();
|
|
|
|
$read = $this->_getWebaccessReadSockets($read);
|
|
$write = $this->_getWebaccessWriteSockets($write);
|
|
//$except = $this->_getWebaccessReadSockets($except);
|
|
|
|
//print_r($this->_WebaccessList);
|
|
//print_r($read);
|
|
//print_r($write);
|
|
//print_r($except);
|
|
|
|
// if no socket to select then return
|
|
if (count($read) + count($write) + count($except) == 0) {
|
|
// sleep the asked timeout...
|
|
if ($timeout > 1000)
|
|
usleep($timeout);
|
|
return 0;
|
|
}
|
|
|
|
$utime = (int)(microtime(true)*1000000);
|
|
$nb = @stream_select($read, $write, $except, $tv_sec, $tv_usec);
|
|
if ($nb === false) {
|
|
// in case stream_select "forgot" to wait, sleep the remaining asked timeout...
|
|
$dtime = (int)(microtime(true)*1000000) - $utime;
|
|
$timeout -= $dtime;
|
|
if ($timeout > 1000)
|
|
usleep($timeout);
|
|
return false;
|
|
}
|
|
|
|
$this->_manageWebaccessSockets($read, $write, $except);
|
|
// workaround for stream_select bug with amd64, replace $nb with sum of arrays
|
|
return count($read) + count($write) + count($except);
|
|
} // select
|
|
|
|
|
|
private function _manageWebaccessSockets(&$receive, &$send, &$except) {
|
|
|
|
// send pending data on all webaccess sockets
|
|
if (is_array($send) && count($send) > 0) {
|
|
foreach ($send as $key => $socket) {
|
|
$i = $this->_findWebaccessSocket($socket);
|
|
if ($i !== false) {
|
|
if (isset($this->_WebaccessList[$i]->_spool[0]['State']) &&
|
|
$this->_WebaccessList[$i]->_spool[0]['State'] == 'OPEN')
|
|
$this->_WebaccessList[$i]->_open();
|
|
else
|
|
$this->_WebaccessList[$i]->_send();
|
|
unset($send[$key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// read data from all needed webaccess sockets
|
|
if (is_array($receive) && count($receive) > 0) {
|
|
foreach ($receive as $key => $socket) {
|
|
$i = $this->_findWebaccessSocket($socket);
|
|
if ($i !== false) {
|
|
$this->_WebaccessList[$i]->_receive();
|
|
unset($receive[$key]);
|
|
}
|
|
}
|
|
}
|
|
} // _manageWebaccessSockets
|
|
|
|
|
|
private function _findWebaccessSocket($socket) {
|
|
|
|
foreach ($this->_WebaccessList as $key => $wau) {
|
|
if ($wau->_socket == $socket)
|
|
return $key;
|
|
}
|
|
return false;
|
|
} // _findWebaccessSocket
|
|
|
|
|
|
private function _getWebaccessReadSockets($socks) {
|
|
|
|
foreach ($this->_WebaccessList as $key => $wau) {
|
|
if ($wau->_state == 'OPENED' && $wau->_socket)
|
|
$socks[] = $wau->_socket;
|
|
}
|
|
return $socks;
|
|
} // _getWebaccessReadSockets
|
|
|
|
|
|
private function _getWebaccessWriteSockets($socks) {
|
|
|
|
foreach ($this->_WebaccessList as $key => $wau) {
|
|
if (isset($wau->_spool[0]['State']) &&
|
|
($wau->_spool[0]['State'] == 'OPEN' ||
|
|
$wau->_spool[0]['State'] == 'BAD' ||
|
|
$wau->_spool[0]['State'] == 'SEND')) {
|
|
|
|
if (($wau->_state == 'CLOSED' || $wau->_state == 'BAD') && !$wau->_socket)
|
|
$wau->_open();
|
|
|
|
if ($wau->_state == 'OPENED' && $wau->_socket)
|
|
$socks[] = $wau->_socket;
|
|
}
|
|
}
|
|
return $socks;
|
|
} // _getWebaccessWriteSockets
|
|
|
|
|
|
function getAllSpools() {
|
|
|
|
$num = 0;
|
|
$bad = 0;
|
|
foreach ($this->_WebaccessList as $key => $wau) {
|
|
if ($wau->_state == 'OPENED' || $wau->_state == 'CLOSED')
|
|
$num += count($wau->_spool);
|
|
elseif ($wau->_state == 'BAD')
|
|
$bad += count($wau->_spool);
|
|
}
|
|
return array($num, $bad);
|
|
} // getAllSpools
|
|
} // class Webaccess
|
|
|
|
|
|
// useful data to handle received headers
|
|
$_wa_header_separator = array('cookie' => ';', 'set-cookie' => ';');
|
|
$_wa_header_multi = array('set-cookie' => true);
|
|
|
|
|
|
class WebaccessUrl {
|
|
//-----------------------------
|
|
// Fields
|
|
//-----------------------------
|
|
|
|
var $wa;
|
|
var $_host;
|
|
var $_port;
|
|
var $_compress_request;
|
|
var $_socket;
|
|
var $_state;
|
|
var $_keepalive;
|
|
var $_keepalive_timeout;
|
|
var $_keepalive_max;
|
|
var $_serv_keepalive_timeout;
|
|
var $_serv_keepalive_max;
|
|
var $_spool;
|
|
var $_wait;
|
|
var $_response;
|
|
var $_query_num;
|
|
var $_request_time;
|
|
var $_cookies;
|
|
var $_webaccess_str;
|
|
var $_bad_time;
|
|
var $_bad_timeout;
|
|
var $_read_time;
|
|
var $_agent;
|
|
|
|
// $_state values:
|
|
// 'OPENED' : socket is opened
|
|
// 'CLOSED' : socket is closed (asked, completed, or closed by server)
|
|
// 'BAD' : socket is closed, bad/error or beginning state
|
|
|
|
// $query['State'] values: (note: $query is added in $_spool, so $this->_spool[0] is the first $query to handle)
|
|
// 'BAD' : bad/error or beginning state
|
|
// 'OPEN' : should prepare request data then send them
|
|
// 'SEND' : request data are prepared, send them
|
|
// 'RECEIVE': request data are sent, receive reply data
|
|
// 'DONE' : request completed
|
|
|
|
//-----------------------------
|
|
// Methods
|
|
//-----------------------------
|
|
|
|
function WebaccessUrl(&$wa, $host, $port, $keepalive = true, $keepalive_timeout = 600, $keepalive_max = 300, $agent = 'XMLaccess') {
|
|
global $_web_access_compress_xmlrpc_request, $_web_access_retry_timeout;
|
|
|
|
$this->wa = &$wa;
|
|
$this->_host = $host;
|
|
$this->_port = $port;
|
|
$this->_webaccess_str = 'Webaccess (' . $this->_host . ':' . $this->_port . '): ';
|
|
$this->_agent = $agent;
|
|
|
|
// request compression setting
|
|
if ($_web_access_compress_xmlrpc_request == 'accept')
|
|
$this->_compress_request = 'accept';
|
|
elseif ($_web_access_compress_xmlrpc_request == 'force') {
|
|
if (function_exists('gzencode'))
|
|
$this->_compress_request = 'gzip';
|
|
elseif (function_exists('gzdeflate'))
|
|
$this->_compress_request = 'deflate';
|
|
else
|
|
$this->_compress_request = false;
|
|
}
|
|
elseif ($_web_access_compress_xmlrpc_request == 'force-gzip' && function_exists('gzencode'))
|
|
$this->_compress_request = 'gzip';
|
|
elseif ($_web_access_compress_xmlrpc_request == 'force-deflate' && function_exists('gzdeflate'))
|
|
$this->_compress_request = 'deflate';
|
|
else
|
|
$this->_compress_request = false;
|
|
|
|
$this->_socket = null;
|
|
$this->_state = 'CLOSED';
|
|
$this->_keepalive = $keepalive;
|
|
$this->_keepalive_timeout = $keepalive_timeout;
|
|
$this->_keepalive_max = $keepalive_max;
|
|
$this->_serv_keepalive_timeout = $keepalive_timeout;
|
|
$this->_serv_keepalive_max = $keepalive_max;
|
|
$this->_spool = array();
|
|
$this->_wait = false;
|
|
$this->_response = '';
|
|
$this->_query_num = 0;
|
|
$this->_query_time = time();
|
|
$this->_cookies = array();
|
|
$this->_bad_time = time();
|
|
$this->_bad_timeout = 0;
|
|
$this->_read_time = 0;
|
|
} // WebaccessUrl
|
|
|
|
|
|
// put connection in BAD state
|
|
function _bad($errstr, $isbad = true) {
|
|
global $aseco, $_web_access_retry_timeout;
|
|
|
|
$aseco->console($this->_webaccess_str . $errstr);
|
|
$this->infos();
|
|
|
|
if ($this->_socket)
|
|
@fclose($this->_socket);
|
|
$this->_socket = null;
|
|
|
|
if ($isbad) {
|
|
if (isset($this->_spool[0]['State']))
|
|
$this->_spool[0]['State'] = 'BAD';
|
|
$this->_state = 'BAD';
|
|
|
|
$this->_bad_time = time();
|
|
if ($this->_bad_timeout < $_web_access_retry_timeout)
|
|
$this->_bad_timeout = $_web_access_retry_timeout;
|
|
else
|
|
$this->_bad_timeout *= 2;
|
|
|
|
} else {
|
|
if (isset($this->_spool[0]['State']))
|
|
$this->_spool[0]['State'] = 'OPEN';
|
|
$this->_state = 'CLOSED';
|
|
}
|
|
$this->_callCallback($this->_webaccess_str . $errstr);
|
|
} // _bad
|
|
|
|
|
|
function retry() {
|
|
global $_web_access_retry_timeout;
|
|
|
|
if ($this->_state == 'BAD') {
|
|
$this->_bad_time = time();
|
|
$this->_bad_timeout = 0;
|
|
}
|
|
} // retry
|
|
|
|
|
|
//$query = array('Path' => $path,
|
|
// 'Callback' => $callback,
|
|
// 'QueryData' => $datas,
|
|
// 'IsXmlrpc' => $is_xmlrpc,
|
|
// 'KeepaliveMinTimeout' => $keepalive_min_timeout,
|
|
// 'OpenTimeout' => $opentimeout,
|
|
// 'WaitTimeout' => $waittimeout );
|
|
// will add: 'State', 'HDatas', 'Datas', 'DatasSize', 'DatasSent',
|
|
// 'Response', 'ResponseSize', 'Headers', 'Close', 'Times'
|
|
function request(&$query) {
|
|
global $aseco, $_web_access_compress_reply, $_web_access_post_xmlrpc, $_web_access_retry_timeout, $_web_access_retry_timeout_max;
|
|
|
|
$query['State'] = 'BAD';
|
|
$query['HDatas'] = '';
|
|
$query['Datas'] = '';
|
|
$query['DatasSize'] = 0;
|
|
$query['DatasSent'] = 0;
|
|
$query['Response'] = '';
|
|
$query['ResponseSize'] = 0;
|
|
$query['Headers'] = array();
|
|
$query['Close'] = false;
|
|
$query['Times'] = array('open' => array(-1.0,-1.0), 'send' => array(-1.0,-1.0), 'receive' => array(-1.0,-1.0,0));
|
|
|
|
// if asynch, in error, and maximal timeout, then forget the request and return false
|
|
if (($query['Callback'] != null) && ($this->_state == 'BAD')) {
|
|
if ($this->_bad_timeout > $_web_access_retry_timeout_max) {
|
|
$aseco->console($this->_webaccess_str . 'Request refused for consecutive errors (' . $this->_bad_timeout . ' / ' . $_web_access_retry_timeout_max . ')');
|
|
return false;
|
|
|
|
} else {
|
|
// if not max then accept the request and try a request (minimum $_web_access_retry_timeout/2 after previous try)
|
|
$time = time();
|
|
$timeout = ($this->_bad_timeout / 2) - ($time - $this->_bad_time);
|
|
if ($timeout < 0)
|
|
$timeout = 0;
|
|
$this->_bad_time = $time - $this->_bad_timeout + $timeout;
|
|
}
|
|
}
|
|
|
|
// build data to send
|
|
if (($query['Callback'] == null) || (is_array($query['Callback']) &&
|
|
isset($query['Callback'][0]) &&
|
|
is_callable($query['Callback'][0]))) {
|
|
|
|
if (is_string($query['QueryData']) && strlen($query['QueryData']) > 0) {
|
|
$msg = "POST " . $query['Path'] . " HTTP/1.1\r\n";
|
|
$msg .= "Host: " . $this->_host . "\r\n";
|
|
$msg .= "User-Agent: " . $this->_agent . "\r\n";
|
|
$msg .= "Cache-Control: no-cache\r\n";
|
|
|
|
if ($_web_access_compress_reply) {
|
|
// ask compression of response if gzdecode() and/or gzinflate() is available
|
|
if (function_exists('gzdecode') && function_exists('gzinflate'))
|
|
$msg .= "Accept-Encoding: deflate, gzip\r\n";
|
|
elseif (function_exists('gzdecode'))
|
|
$msg .= "Accept-Encoding: gzip\r\n";
|
|
elseif (function_exists('gzinflate'))
|
|
$msg .= "Accept-Encoding: deflate\r\n";
|
|
}
|
|
|
|
//echo "\nData:\n\n" . $query['QueryData'] . "\n";
|
|
|
|
if ($query['IsXmlrpc'] === true) {
|
|
if ($_web_access_post_xmlrpc) {
|
|
$msg .= "Content-type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
|
|
|
|
//echo "\n=========================== Data =================================\n\n" . $datas . "\n";
|
|
//$d2 = urlsafe_base64_encode($datas);
|
|
//$d3 = urlsafe_base64_decode($d2);
|
|
//echo "\n--------------------------- Data ---------------------------------\n\n" . $d3 . "\n";
|
|
|
|
$query['QueryData'] = 'xmlrpc=' . urlsafe_base64_encode($query['QueryData']);
|
|
}
|
|
else {
|
|
$msg .= "Content-type: text/xml; charset=UTF-8\r\n";
|
|
}
|
|
|
|
if ($this->_compress_request == 'gzip' && function_exists('gzencode')) {
|
|
$msg .= "Content-Encoding: gzip\r\n";
|
|
$query['QueryData'] = gzencode($query['QueryData']);
|
|
} elseif ($this->_compress_request == 'deflate' && function_exists('gzdeflate')) {
|
|
$msg .= "Content-Encoding: deflate\r\n";
|
|
$query['QueryData'] = gzdeflate($query['QueryData']);
|
|
}
|
|
|
|
}
|
|
elseif (is_string($query['IsXmlrpc'])) {
|
|
$msg .= "Content-type: " . $query['IsXmlrpc'] . "\r\n";
|
|
$msg .= "Accept: */*\r\n";
|
|
} else {
|
|
$msg .= "Content-type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
|
|
}
|
|
$msg .= "Content-length: " . strlen($query['QueryData']) . "\r\n";
|
|
$query['HDatas'] = $msg;
|
|
$query['State'] = 'OPEN';
|
|
$query['Retries'] = 0;
|
|
|
|
//print_r($msg);
|
|
|
|
// add the query in spool
|
|
$this->_spool[] = &$query;
|
|
|
|
if ($query['Callback'] == null) {
|
|
$this->_wait = true;
|
|
$this->_open($query['OpenTimeout'], $query['WaitTimeout']); // wait more in not callback mode
|
|
$this->_spool = array();
|
|
$this->_wait = false;
|
|
return $query['Response'];
|
|
} else
|
|
$this->_open();
|
|
|
|
} else {
|
|
$aseco->console($this->_webaccess_str . 'Bad data');
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
$aseco->console($this->_webaccess_str . 'Bad callback function: ' . $query['Callback']);
|
|
return false;
|
|
}
|
|
return true;
|
|
} // request
|
|
|
|
|
|
// open the socket (close it before if needed)
|
|
private function _open_socket($opentimeout = 0.0) {
|
|
global $aseco;
|
|
|
|
// if socket not opened, then open it (2 tries)
|
|
if (!$this->_socket || $this->_state != 'OPENED') {
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['open'][0] = $time;
|
|
|
|
$errno = '';
|
|
$errstr = '';
|
|
$this->_socket = @fsockopen($this->_host, $this->_port, $errno, $errstr, 1.8); // first try
|
|
if (!$this->_socket) {
|
|
|
|
if ($opentimeout >= 1.0)
|
|
$this->_socket = @fsockopen($this->_host, $this->_port, $errno, $errstr, $opentimeout);
|
|
if (!$this->_socket) {
|
|
$this->_bad('Error(' . $errno . ') ' . $errstr . ', connection failed!');
|
|
return;
|
|
}
|
|
}
|
|
$this->_state = 'OPENED';
|
|
//$aseco->console($this->_webaccess_str . 'connection opened!');
|
|
|
|
// new socket connection: reset all pending request original values
|
|
for ($i = 0; $i < count($this->_spool); $i++) {
|
|
$this->_spool[$i]['State'] = 'OPEN';
|
|
$this->_spool[$i]['DatasSent'] = 0;
|
|
$this->_spool[$i]['Response'] = '';
|
|
$this->_spool[$i]['Headers'] = array();
|
|
}
|
|
$this->_response = '';
|
|
$this->_query_num = 0;
|
|
$this->_query_time = time();
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['open'][1] = $time - $this->_spool[0]['Times']['open'][0];
|
|
}
|
|
} // _open_socket
|
|
|
|
|
|
// open the connection (if not already opened) and send
|
|
function _open($opentimeout = 0.0, $waittimeout = 5.0) {
|
|
global $aseco, $_web_access_retry_timeout_max;
|
|
|
|
if (!isset($this->_spool[0]['State']))
|
|
return false;
|
|
$time = time();
|
|
|
|
// if asynch, in error, then return false until timeout or if >max)
|
|
if (!$this->_wait && $this->_state == 'BAD' &&
|
|
(($this->_bad_timeout > $_web_access_retry_timeout_max) ||
|
|
(($time - $this->_bad_time) < $this->_bad_timeout))) {
|
|
//$aseco->console($this->_webaccess_str . 'wait to retry (' . ($time - $this->_bad_time) . ' / ' . $this->_bad_timeout . ')');
|
|
return false;
|
|
}
|
|
|
|
// if the socket is probably in timeout, close it
|
|
if ($this->_socket && $this->_state == 'OPENED' &&
|
|
($this->_serv_keepalive_timeout <= ($time - $this->_query_time))) {
|
|
//$aseco->console($this->_webaccess_str . 'timeout, close it!');
|
|
$this->_state = 'CLOSED';
|
|
@fclose($this->_socket);
|
|
$this->_socket = null;
|
|
}
|
|
|
|
// if socket is not opened, open it
|
|
if (!$this->_socket || $this->_state != 'OPENED')
|
|
$this->_open_socket($opentimeout);
|
|
|
|
// if socket is open, send data if possible
|
|
if ($this->_socket) {
|
|
$this->_read_time = microtime(true);
|
|
|
|
// if wait (synchronous query) then go on all pending write/read until the last
|
|
if ($this->_wait) {
|
|
@stream_set_timeout($this->_socket, 0, 10000); // timeout 10 ms
|
|
|
|
while (isset($this->_spool[0]['State']) &&
|
|
($this->_spool[0]['State'] == 'OPEN' ||
|
|
$this->_spool[0]['State'] == 'SEND' ||
|
|
$this->_spool[0]['State'] == 'RECEIVE')) {
|
|
//echo 'State=' . $this->_spool[0]['State'] . " (" . count($this->_spool) . ")\n";
|
|
if (!$this->_socket || $this->_state != 'OPENED')
|
|
$this->_open_socket($opentimeout);
|
|
|
|
if ($this->_spool[0]['State'] == 'OPEN') {
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['send'][0] = $time;
|
|
$this->_send($waittimeout);
|
|
}
|
|
elseif ($this->_spool[0]['State'] == 'SEND')
|
|
$this->_send($waittimeout);
|
|
elseif ($this->_spool[0]['State'] == 'RECEIVE')
|
|
$this->_receive($waittimeout*4);
|
|
|
|
// if timeout then error
|
|
if (($difftime = microtime(true) - $this->_read_time) > $waittimeout) {
|
|
$this->_bad('Request timeout, in _open (' . round($difftime) . ' > ' . $waittimeout . 's) state=' . $this->_spool[0]['State']);
|
|
return;
|
|
}
|
|
}
|
|
if ($this->_socket)
|
|
@stream_set_timeout($this->_socket, 0, 2000); // timeout 2 ms
|
|
}
|
|
|
|
// else just do a send on the current
|
|
elseif (isset($this->_spool[0]['State']) && $this->_spool[0]['State'] == 'OPEN') {
|
|
@stream_set_timeout($this->_socket, 0, 2000); // timeout 2 ms
|
|
$this->_send($waittimeout);
|
|
}
|
|
}
|
|
} // _open
|
|
|
|
|
|
function _send($waittimeout = 20) {
|
|
|
|
if (!isset($this->_spool[0]['State']))
|
|
return;
|
|
|
|
// if OPEN then become SEND
|
|
if ($this->_spool[0]['State'] == 'OPEN') {
|
|
|
|
$this->_spool[0]['State'] = 'SEND';
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['send'][0] = $time;
|
|
$this->_spool[0]['Response'] = '';
|
|
$this->_spool[0]['Headers'] = array();
|
|
|
|
// finish to prepare header and data to send
|
|
$msg = $this->_spool[0]['HDatas'];
|
|
if (!$this->_keepalive || ($this->_spool[0]['KeepaliveMinTimeout'] < 0) ||
|
|
($this->_serv_keepalive_timeout < $this->_spool[0]['KeepaliveMinTimeout']) ||
|
|
($this->_serv_keepalive_max <= ($this->_query_num + 2)) ||
|
|
($this->_serv_keepalive_timeout <= (time() - $this->_query_time + 2))) {
|
|
$msg .= "Connection: close\r\n";
|
|
$this->_spool[0]['Close'] = true;
|
|
}
|
|
else {
|
|
$msg .= 'Keep-Alive: timeout=' . $this->_keepalive_timeout . ', max=' . $this->_keepalive_max
|
|
. "\r\nConnection: Keep-Alive\r\n";
|
|
}
|
|
|
|
// add cookie header
|
|
if (count($this->_cookies) > 0) {
|
|
$cookie_msg = '';
|
|
$sep = '';
|
|
foreach ($this->_cookies as $name => $cookie) {
|
|
if (!isset($cookie['path']) ||
|
|
strncmp($this->_spool[0]['Path'], $cookie['path'], strlen($cookie['path'])) == 0) {
|
|
$cookie_msg .= $sep . $name . '=' . $cookie['Value'];
|
|
$sep = '; ';
|
|
}
|
|
}
|
|
if ($cookie_msg != '')
|
|
$msg .= "Cookie: $cookie_msg\r\n";
|
|
}
|
|
|
|
$msg .= "\r\n";
|
|
$msg .= $this->_spool[0]['QueryData'];
|
|
$this->_spool[0]['Datas'] = $msg;
|
|
$this->_spool[0]['DatasSize'] = strlen($msg);
|
|
$this->_spool[0]['DatasSent'] = 0;
|
|
|
|
//print_r($msg);
|
|
}
|
|
|
|
// if not SEND then stop
|
|
if ($this->_spool[0]['State'] != 'SEND')
|
|
return;
|
|
|
|
do {
|
|
$sent = @stream_socket_sendto($this->_socket,
|
|
substr($this->_spool[0]['Datas'], $this->_spool[0]['DatasSent'],
|
|
($this->_spool[0]['DatasSize'] - $this->_spool[0]['DatasSent'])));
|
|
if ($sent == false) {
|
|
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['send'][1] = $time - $this->_spool[0]['Times']['send'][0];
|
|
//var_dump($this->_spool[0]['Datas']);
|
|
$this->_bad('Error(' . $errno . ') ' . $errstr . ', could not send data! ('
|
|
. $sent . ' / ' . ($this->_spool[0]['DatasSize'] - $this->_spool[0]['DatasSent']) . ', '
|
|
. $this->_spool[0]['DatasSent'] . ' / ' . $this->_spool[0]['DatasSize'] . ')');
|
|
if ($this->_wait)
|
|
return;
|
|
break;
|
|
|
|
} else {
|
|
$this->_spool[0]['DatasSent'] += $sent;
|
|
if ($this->_spool[0]['DatasSent'] >= $this->_spool[0]['DatasSize']) {
|
|
// All is sent, prepare to receive the reply
|
|
$this->_query_num++;
|
|
$this->_query_time = time();
|
|
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['send'][1] = $time - $this->_spool[0]['Times']['send'][0];
|
|
|
|
//@stream_set_blocking($this->_socket, 0);
|
|
$this->_spool[0]['State'] = 'RECEIVE';
|
|
$this->_spool[0]['Times']['receive'][0] = $time;
|
|
}
|
|
|
|
// if timeout then error
|
|
elseif (($difftime = microtime(true) - $this->_read_time) > $waittimeout) {
|
|
$this->_bad('Request timeout, in _send (' . round($difftime) . ' > ' . $waittimeout . 's)');
|
|
}
|
|
}
|
|
|
|
// if not async-callback then continue until all is sent
|
|
} while ($this->_wait && isset($this->_spool[0]['State']) && ($this->_spool[0]['State'] == 'SEND'));
|
|
} // _send
|
|
|
|
|
|
function _receive($waittimeout = 40) {
|
|
global $aseco, $_Webaccess_last_response;
|
|
|
|
if (!$this->_socket || $this->_state != 'OPENED')
|
|
return;
|
|
|
|
$state = false;
|
|
$time0 = microtime(true);
|
|
$timeout = ($this->_wait) ? $waittimeout : 0;
|
|
do {
|
|
$r = array($this->_socket);
|
|
$w = null;
|
|
$e = null;
|
|
$nb = @stream_select($r, $w, $e, $timeout);
|
|
if ($nb === 0)
|
|
$nb = count($r);
|
|
|
|
while (!@feof($this->_socket) && $nb !== false && $nb > 0) {
|
|
$timeout = 0;
|
|
|
|
if (count($r) > 0) {
|
|
$res = @stream_socket_recvfrom($this->_socket, 8192);
|
|
|
|
if ($res == '') { // should not happen habitually, but...
|
|
break;
|
|
} elseif ($res !== false) {
|
|
$this->_response .= $res;
|
|
}
|
|
else {
|
|
if (isset($this->_spool[0])) {
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0];
|
|
}
|
|
$this->_bad('Error(' . $errno . ') ' . $errstr . ', could not read all data!');
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if timeout then error
|
|
if (($difftime = microtime(true) - $this->_read_time) > $waittimeout) {
|
|
$this->_bad('Request timeout, in _receive (' . round($difftime) . ' > ' . $waittimeout . 's)');
|
|
break;
|
|
}
|
|
|
|
$r = array($this->_socket);
|
|
$w = null;
|
|
$e = null;
|
|
$nb = @stream_select($r, $w, $e, $timeout);
|
|
if ($nb === 0)
|
|
$nb = count($r);
|
|
}
|
|
|
|
if (isset($this->_spool[0]['Times']['receive'][2])) {
|
|
$time = microtime(true);
|
|
$this->_spool[0]['Times']['receive'][2] += ($time - $time0);
|
|
}
|
|
|
|
// get headers and full message
|
|
$state = $this->_handleHeaders();
|
|
//echo "receive9\n";
|
|
//var_dump($state);
|
|
|
|
} while ($this->_wait && $state === false && $this->_socket && !@feof($this->_socket));
|
|
|
|
if (!isset($this->_spool[0]['State']) || $this->_spool[0]['State'] != 'RECEIVE') {
|
|
// in case of (probably keepalive) connection closed by server
|
|
if ($this->_socket && @feof($this->_socket)){
|
|
//$aseco->console($this->_webaccess_str . 'Socket closed by server (' . $this->_host . ')');
|
|
$this->_state = 'CLOSED';
|
|
@fclose($this->_socket);
|
|
$this->_socket = null;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// terminated but incomplete! more than probably closed by server...
|
|
if ($state === false && $this->_socket && @feof($this->_socket)) {
|
|
$this->_state = 'CLOSED';
|
|
if (isset($this->_spool[0])) {
|
|
$time = microtime(true);
|
|
$this->_spool[0]['State'] = 'OPEN';
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0];
|
|
}
|
|
if (strlen($this->_response) > 0) // if not 0 sized then show error message
|
|
$this->_bad('Error: closed with incomplete read: re-open socket and re-send! (' . strlen($this->_response) . ')');
|
|
else
|
|
$this->_bad('Closed by server when reading: re-open socket and re-send! (' . strlen($this->_response) . ')', false);
|
|
|
|
$this->_spool[0]['Retries']++;
|
|
if ($this->_spool[0]['Retries'] > 2) {
|
|
// 3 tries failed, remove entry from spool
|
|
$aseco->console($this->_webaccess_str . "failed {$this->_spool[0]['Retries']} times: skip current request");
|
|
array_shift($this->_spool);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// reply is complete :)
|
|
if ($state === true) {
|
|
$this->_bad_timeout = 0; // reset error timeout
|
|
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0];
|
|
$this->_spool[0]['State'] = 'DONE';
|
|
|
|
// store http/xml response in global $_Webaccess_last_response for debugging use
|
|
$_Webaccess_last_response = $this->_spool[0]['Response'];
|
|
//debugPrint('Webaccess->_receive - Response', $_Webaccess_last_response);
|
|
|
|
// call callback func
|
|
$this->_callCallback();
|
|
$this->_query_time = time();
|
|
|
|
if (!$this->_keepalive || $this->_spool[0]['Close']) {
|
|
//if ($this->_spool[0]['Close'])
|
|
// $aseco->console($this->_webaccess_str . 'close connection (asked in headers)');
|
|
$this->_state = 'CLOSED';
|
|
@fclose($this->_socket);
|
|
$this->_socket = null;
|
|
}
|
|
|
|
$this->infos();
|
|
|
|
// request completed, remove it from spool!
|
|
array_shift($this->_spool);
|
|
}
|
|
} // _receive
|
|
|
|
private function _callCallback($error = null) {
|
|
|
|
// store optional error message
|
|
if ($error !== null)
|
|
$this->_spool[0]['Response']['Error'] = $error;
|
|
|
|
// call callback func
|
|
if (isset($this->_spool[0]['Callback'])) {
|
|
$callbackinfo = $this->_spool[0]['Callback'];
|
|
if (isset($callbackinfo[0]) && is_callable($callbackinfo[0])) {
|
|
$callback_func = $callbackinfo[0];
|
|
$callbackinfo[0] = $this->_spool[0]['Response'];
|
|
call_user_func_array($callback_func, $callbackinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function _handleHeaders() {
|
|
global $aseco, $_wa_header_separator, $_wa_header_multi;
|
|
|
|
if (!isset($this->_spool[0]['State']))
|
|
return false;
|
|
|
|
if (strlen($this->_response) < 8) // not enough data, continue read
|
|
return false;
|
|
if (strncmp($this->_response, 'HTTP/', 5) != 0) { // not HTTP!
|
|
$this->_bad("Error, not HTTP response ! **********\n" . substr($this->_response, 0, 300) . "\n***************\n");
|
|
return null;
|
|
}
|
|
|
|
// separate headers and data
|
|
$datas = explode("\r\n\r\n", $this->_response, 2);
|
|
if (count($datas) < 2) {
|
|
$datas = explode("\n\n", $this->_response, 2);
|
|
if (count($datas) < 2) {
|
|
$datas = explode("\r\r", $this->_response, 2);
|
|
if (count($datas) < 2)
|
|
return false; // not complete headers, continue read
|
|
}
|
|
}
|
|
|
|
// get headers if not done on previous read
|
|
if (!isset($this->_spool[0]['Headers']['Command'][0])) {
|
|
// separate headers
|
|
//echo "Get Headers! (" . strlen($datas[0]) . ")\n";
|
|
|
|
$headers = array();
|
|
$heads = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $datas[0])));
|
|
if (count($heads) < 2) {
|
|
$this->_bad("Error, uncomplete headers! **********\n" . $datas[0] . "\n***************\n");
|
|
return null;
|
|
}
|
|
|
|
$headers['Command'] = explode(' ', $heads[0], 3);
|
|
|
|
for ($i = 1; $i < count($heads); $i++) {
|
|
$header = explode(':', $heads[$i], 2);
|
|
if (count($header) > 1) {
|
|
$headername = strtolower(trim($header[0]));
|
|
if (isset($_wa_header_separator[$headername]))
|
|
$sep = $_wa_header_separator[$headername];
|
|
else
|
|
$sep = ',';
|
|
if (isset($_wa_header_multi[$headername]) && $_wa_header_multi[$headername]) {
|
|
if (!isset($headers[$headername]))
|
|
$headers[$headername] = array();
|
|
$headers[$headername][] = explode($sep, trim($header[1]));
|
|
} else
|
|
$headers[$headername] = explode($sep, trim($header[1]));
|
|
}
|
|
}
|
|
|
|
if (isset($headers['content-length'][0]))
|
|
$headers['content-length'][0] += 0; // convert to int
|
|
|
|
$this->_spool[0]['Headers'] = $headers;
|
|
|
|
// add header specific info in case of Dedimania reply
|
|
if (isset($headers['server'][0]))
|
|
$this->_webaccess_str = 'Webaccess (' . $this->_host . ':' . $this->_port .'/'. $headers['server'][0] . '): ';
|
|
}
|
|
else {
|
|
$headers = &$this->_spool[0]['Headers'];
|
|
//echo "Previous Headers! (" . strlen($datas[0]) . ")\n";
|
|
}
|
|
|
|
// get real message
|
|
$datasize = strlen($datas[1]);
|
|
if (isset($headers['content-length'][0]) && $headers['content-length'][0] >= 0) {
|
|
//echo 'mess_size0=' . strlen($datas[1]) . "\n";
|
|
|
|
if ($headers['content-length'][0] > $datasize) // incomplete message
|
|
return false;
|
|
|
|
elseif ($headers['content-length'][0] < $datasize) {
|
|
$message = substr($datas[1], 0, $headers['content-length'][0]);
|
|
// remaining buffer for next reply
|
|
$this->_response = substr($datas[1], $headers['content-length'][0]);
|
|
}
|
|
else {
|
|
$message = $datas[1];
|
|
$this->_response = '';
|
|
}
|
|
$this->_spool[0]['ResponseSize'] = strlen($datas[0]) + 4 + $headers['content-length'][0];
|
|
}
|
|
|
|
// get real message when reply is chunked
|
|
elseif (isset($headers['transfer-encoding'][0]) && $headers['transfer-encoding'][0] == 'chunked') {
|
|
|
|
// get chunk size and make message with chunks data
|
|
$size = -1;
|
|
$chunkpos = 0;
|
|
if (($datapos = strpos($datas[1], "\r\n", $chunkpos)) !== false) {
|
|
$message = '';
|
|
$chunk = explode(';', substr($datas[1], $chunkpos, $datapos - $chunkpos));
|
|
$size = hexdec($chunk[0]);
|
|
//debugPrint("Webaccess->Response - chunk - $chunkpos, $datapos, $size (" . strlen($datas[1]) . ")", $chunk);
|
|
while ($size > 0) {
|
|
if ($datapos + 2 + $size > $datasize) // incomplete message
|
|
return false;
|
|
$message .= substr($datas[1], $datapos + 2, $size);
|
|
$chunkpos = $datapos + 2 + $size + 2;
|
|
if (($datapos = strpos($datas[1], "\r\n", $chunkpos)) !== false) {
|
|
$chunk = explode(';', substr($datas[1], $chunkpos, $datapos - $chunkpos));
|
|
$size = hexdec($chunk[0]);
|
|
} else
|
|
$size = -1;
|
|
//debugPrint("Webaccess->Response - chunk - $chunkpos, $datapos, $size (" . strlen($datas[1]) . ")", $chunk);
|
|
}
|
|
|
|
}
|
|
if ($size < 0) // error bad size or incomplete message
|
|
return false;
|
|
|
|
if (strpos($datas[1], "\r\n\r\n", $chunkpos) === false) // incomplete message: end is missing
|
|
return false;
|
|
|
|
// store complete message size
|
|
$msize = strlen($message);
|
|
$headers['transfer-encoding'][1] = 'total_size=' . $msize; // add message size after 'chunked' for information
|
|
$this->_spool[0]['ResponseSize'] = strlen($datas[0]) + 4 + $msize;
|
|
|
|
// after the message itself...
|
|
$message_end = explode("\r\n\r\n", substr($datas[1], $chunkpos), 2);
|
|
|
|
// add end headers if any
|
|
$heads = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $message_end[0])));
|
|
for ($i = 1; $i < count($heads); $i++) {
|
|
$header = explode(':', $heads[$i], 2);
|
|
if (count($header) > 1) {
|
|
$headername = strtolower(trim($header[0]));
|
|
if (isset($_wa_header_separator[$headername]))
|
|
$sep = $_wa_header_separator[$headername];
|
|
else
|
|
$sep = ',';
|
|
if (isset($_wa_header_multi[$headername]) && $_wa_header_multi[$headername]) {
|
|
if (!isset($headers[$headername]))
|
|
$headers[$headername] = array();
|
|
$headers[$headername][] = explode($sep, trim($header[1]));
|
|
} else
|
|
$headers[$headername] = explode($sep, trim($header[1]));
|
|
}
|
|
}
|
|
$this->_spool[0]['Headers'] = $headers;
|
|
|
|
// remaining buffer for next reply
|
|
if (isset($message_end[1]) && strlen($message_end[1]) > 0) {
|
|
$this->_response = $message_end[1];
|
|
}
|
|
else
|
|
$this->_response = '';
|
|
}
|
|
// no content-length and not chunked!
|
|
else {
|
|
$this->_bad("Error, bad http, no content-length and not chunked! **********\n" . $datas[0] . "\n***************\n");
|
|
return null;
|
|
}
|
|
|
|
//echo 'mess_size1=' . strlen($message) . "\n";
|
|
|
|
// if Content-Encoding: gzip or Content-Encoding: deflate
|
|
if (isset($headers['content-encoding'][0])) {
|
|
if ($headers['content-encoding'][0] == 'gzip')
|
|
$message = @gzdecode($message);
|
|
elseif ($headers['content-encoding'][0] == 'deflate')
|
|
$message = @gzinflate($message);
|
|
}
|
|
|
|
// if Accept-Encoding: gzip or deflate
|
|
if ($this->_compress_request == 'accept' && isset($headers['accept-encoding'][0])) {
|
|
foreach ($headers['accept-encoding'] as $comp) {
|
|
$comp = trim($comp);
|
|
if ($comp == 'gzip' && function_exists('gzencode')) {
|
|
$this->_compress_request = 'gzip';
|
|
break;
|
|
}
|
|
elseif ($comp == 'deflate' && function_exists('gzdeflate')) {
|
|
$this->_compress_request = 'deflate';
|
|
break;
|
|
}
|
|
}
|
|
if ($this->_compress_request == 'accept')
|
|
$this->_compress_request = false;
|
|
|
|
$aseco->console($this->_webaccess_str . 'send: ' . ($this->_compress_request === false ? 'no compression' : $this->_compress_request)
|
|
. ', receive: ' . (isset($headers['content-encoding'][0]) ? $headers['content-encoding'][0] : 'no compression'));
|
|
}
|
|
|
|
// get cookies values
|
|
if (isset($headers['set-cookie'])) {
|
|
foreach ($headers['set-cookie'] as $cookie) {
|
|
$cook = explode('=', $cookie[0], 2);
|
|
if (count($cook) > 1) {
|
|
// set main cookie value
|
|
$cookname = trim($cook[0]);
|
|
if (!isset($this->_cookies[$cookname]))
|
|
$this->_cookies[$cookname] = array();
|
|
$this->_cookies[$cookname]['Value'] = trim($cook[1]);
|
|
|
|
// set cookie options
|
|
for ($i = 1; $i < count($cookie); $i++) {
|
|
$cook = explode('=', $cookie[$i], 2);
|
|
$cookarg = strtolower(trim($cook[0]));
|
|
if (isset($cook[1]))
|
|
$this->_cookies[$cookname][$cookarg] = trim($cook[1]);
|
|
}
|
|
}
|
|
}
|
|
//debugPrint('SET-COOKIES: ', $headers['set-cookie']);
|
|
//debugPrint('STORED COOKIES: ', $this->_cookies);
|
|
}
|
|
|
|
// if the server reply ask to close, then close
|
|
if (!isset($headers['connection'][0]) || $headers['connection'][0] == 'close') {
|
|
//if (!$this->_spool[0]['Close'])
|
|
// $aseco->console($this->_webaccess_str . 'server ask to close connection');
|
|
$this->_spool[0]['Close'] = true;
|
|
}
|
|
|
|
// verify server keepalive value and use them if lower
|
|
if (isset($headers['keep-alive'])) {
|
|
$kasize = count($headers['keep-alive']);
|
|
for ($i = 0; $i < $kasize; $i++) {
|
|
$keep = explode('=', $headers['keep-alive'][$i], 2);
|
|
if (count($keep) > 1)
|
|
$headers['keep-alive'][trim(strtolower($keep[0]))] = intval(trim($keep[1]));
|
|
}
|
|
if (isset($headers['keep-alive']['timeout']))
|
|
$this->_serv_keepalive_timeout = $headers['keep-alive']['timeout'];
|
|
if (isset($headers['keep-alive']['max']))
|
|
$this->_serv_keepalive_max = $headers['keep-alive']['max'];
|
|
//$aseco->console($this->_webaccess_str . 'max=' . $this->_serv_keepalive_max . ', timeout=' . $this->_serv_keepalive_timeout . "\n");
|
|
}
|
|
|
|
// store complete reply message for the request
|
|
$this->_spool[0]['Response'] = array('Code' => intval($headers['Command'][1]),
|
|
'Reason' => $headers['Command'][2],
|
|
'Headers' => $headers,
|
|
'Message' => $message
|
|
);
|
|
//echo 'mess_size2=' . strlen($message) . "\n";
|
|
return true;
|
|
} // _handleHeaders
|
|
|
|
|
|
function infos() {
|
|
global $aseco;
|
|
|
|
$size = (isset($this->_spool[0]['Response']['Message'])) ? strlen($this->_spool[0]['Response']['Message']) : 0;
|
|
$msg = $this->_webaccess_str
|
|
. sprintf('[%s,%s]: %0.3f / %0.3f / %0.3f (%0.3f) / %d [%d,%d,%d]',
|
|
$this->_state, $this->_spool[0]['State'],
|
|
$this->_spool[0]['Times']['open'][1],
|
|
$this->_spool[0]['Times']['send'][1],
|
|
$this->_spool[0]['Times']['receive'][1],
|
|
$this->_spool[0]['Times']['receive'][2],
|
|
$this->_query_num, $this->_spool[0]['DatasSize'],
|
|
$size, $this->_spool[0]['ResponseSize']);
|
|
//$aseco->console($msg);
|
|
} // infos
|
|
} // class WebaccessUrl
|
|
|
|
|
|
// use: list($host, $port, $path) = getHostPortPath($url);
|
|
function getHostPortPath($url) {
|
|
|
|
$http_pos = strpos($url, 'http://');
|
|
if ($http_pos !== false) {
|
|
$script = explode('/', substr($url, $http_pos + 7), 2);
|
|
if (isset($script[1]))
|
|
$path = '/' . $script[1];
|
|
else
|
|
$path = '/';
|
|
$serv = explode(':', $script[0], 2);
|
|
$host = $serv[0];
|
|
if (isset($serv[1]))
|
|
$port = (int)$serv[1];
|
|
else
|
|
$port = 80;
|
|
if (strlen($host) > 2)
|
|
return array($host, $port, $path);
|
|
}
|
|
return array(false, false, false);
|
|
} // getHostPortPath
|
|
|
|
|
|
// gzdecode() workaround
|
|
if (!function_exists('gzdecode') && function_exists('gzinflate')) {
|
|
|
|
function gzdecode($data) {
|
|
|
|
$len = strlen($data);
|
|
if ($len < 18 || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
|
|
return null; // Not GZIP format (See RFC 1952)
|
|
}
|
|
$method = ord(substr($data, 2, 1)); // Compression method
|
|
$flags = ord(substr($data, 3, 1)); // Flags
|
|
if ($flags & 31 != $flags) {
|
|
// Reserved bits are set -- NOT ALLOWED by RFC 1952
|
|
return null;
|
|
}
|
|
// NOTE: $mtime may be negative (PHP integer limitations)
|
|
$mtime = unpack('V', substr($data, 4, 4));
|
|
$mtime = $mtime[1];
|
|
$xfl = substr($data, 8, 1);
|
|
$os = substr($data, 8, 1);
|
|
$headerlen = 10;
|
|
$extralen = 0;
|
|
$extra = '';
|
|
if ($flags & 4) {
|
|
// 2-byte length prefixed EXTRA data in header
|
|
if ($len - $headerlen - 2 < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$extralen = unpack('v', substr($data, 8, 2));
|
|
$extralen = $extralen[1];
|
|
if ($len - $headerlen - 2 - $extralen < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$extra = substr($data, 10, $extralen);
|
|
$headerlen += $extralen + 2;
|
|
}
|
|
|
|
$filenamelen = 0;
|
|
$filename = '';
|
|
if ($flags & 8) {
|
|
// C-style string file NAME data in header
|
|
if ($len - $headerlen - 1 < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$filenamelen = strpos(substr($data, 8 + $extralen), chr(0));
|
|
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$filename = substr($data, $headerlen, $filenamelen);
|
|
$headerlen += $filenamelen + 1;
|
|
}
|
|
|
|
$commentlen = 0;
|
|
$comment = '';
|
|
if ($flags & 16) {
|
|
// C-style string COMMENT data in header
|
|
if ($len - $headerlen - 1 < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$commentlen = strpos(substr($data, 8 + $extralen + $filenamelen), chr(0));
|
|
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
|
|
return false; // Invalid header format
|
|
}
|
|
$comment = substr($data, $headerlen, $commentlen);
|
|
$headerlen += $commentlen + 1;
|
|
}
|
|
|
|
$headercrc = '';
|
|
if ($flags & 1) {
|
|
// 2-bytes (lowest order) of CRC32 on header present
|
|
if ($len - $headerlen - 2 < 8) {
|
|
return false; // Invalid format
|
|
}
|
|
$calccrc = crc32(substr($data, 0, $headerlen)) & 0xffff;
|
|
$headercrc = unpack('v', substr($data, $headerlen, 2));
|
|
$headercrc = $headercrc[1];
|
|
if ($headercrc != $calccrc) {
|
|
return false; // Bad header CRC
|
|
}
|
|
$headerlen += 2;
|
|
}
|
|
|
|
// GZIP FOOTER - These be negative due to PHP's limitations
|
|
$datacrc = unpack('V', substr($data, -8, 4));
|
|
$datacrc = $datacrc[1];
|
|
$isize = unpack('V', substr($data, -4));
|
|
$isize = $isize[1];
|
|
|
|
// Perform the decompression:
|
|
$bodylen = $len - $headerlen - 8;
|
|
if ($bodylen < 1) {
|
|
// This should never happen - IMPLEMENTATION BUG!
|
|
return null;
|
|
}
|
|
$body = substr($data, $headerlen, $bodylen);
|
|
$data = '';
|
|
if ($bodylen > 0) {
|
|
switch ($method) {
|
|
case 8:
|
|
// Currently the only supported compression method:
|
|
$data = gzinflate($body);
|
|
break;
|
|
default:
|
|
// Unknown compression method
|
|
return false;
|
|
}
|
|
} else {
|
|
// I'm not sure if zero-byte body content is allowed.
|
|
// Allow it for now... Do nothing...
|
|
}
|
|
|
|
// Verify decompressed size and CRC32:
|
|
// NOTE: This may fail with large data sizes depending on how
|
|
// PHP's integer limitations affect strlen() since $isize
|
|
// may be negative for large sizes
|
|
if ($isize != strlen($data) || crc32($data) != $datacrc) {
|
|
// Bad format! Length or CRC doesn't match!
|
|
return false;
|
|
}
|
|
return $data;
|
|
} // gzdecode
|
|
}
|
|
?>
|