|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
use eXpansion\Framework\Core\Services\Console; |
|
4
|
|
|
|
|
5
|
|
|
//////////////////////////////////////////////////////////////// |
|
6
|
|
|
// |
|
7
|
|
|
// File: WEB ACCESS 2.1.3 |
|
8
|
|
|
// Date: 13.10.2011 |
|
9
|
|
|
// Author: Gilles Masson |
|
10
|
|
|
// Contributor & fixes: Xymph |
|
11
|
|
|
// |
|
12
|
|
|
// Additional changes for expansion2: Reaby - updated at 27.2.2017 |
|
13
|
|
|
// |
|
14
|
|
|
//////////////////////////////////////////////////////////////// |
|
15
|
|
|
// This class and functions can be used to make asynchronous xml or http (POST or GET) queries. |
|
16
|
|
|
// this means that you call a function to send the query, and a callback function |
|
17
|
|
|
// will automatically be called when the response has arrived, without having your |
|
18
|
|
|
// program waiting for the response. |
|
19
|
|
|
// You can also use it for synchronous queries (see below). |
|
20
|
|
|
// The class handle (for each url) keepalive and compression (when possible). |
|
21
|
|
|
// It support Cookies, and so can use sessions like php one (anyway the cookie is not stored, |
|
22
|
|
|
// so its maximal life is the life of the program). |
|
23
|
|
|
// |
|
24
|
|
|
// |
|
25
|
|
|
// usage: $_webaccess = new Webaccess(); |
|
26
|
|
|
// $_webaccess->request($url, array('func_name',xxx), $data, $is_xmlrpc, $keepalive_min_timeout); |
|
27
|
|
|
// $url: the web script URL. |
|
28
|
|
|
// $data: string to send in http body (xml, xml_rpc or POST data) |
|
29
|
|
|
// $is_xmlrpc: true if it's a xml or xml-rpc request, false if it's a standard html GET or POST |
|
30
|
|
|
// $keepalive_min_timeout: minimal value of server keepalive timeout to send a keepalive request, |
|
31
|
|
|
// else make a request with close connection. |
|
32
|
|
|
// func_name is the callback function name, which will be called this way: |
|
33
|
|
|
|
|
34
|
|
|
// func_name(array('Code'=>code,'Reason'=>reason,'Headers'=>headers,'Message'=>message),xxx), where: |
|
35
|
|
|
// xxx is the same as given previously in callback description. |
|
36
|
|
|
// code is the returned http code |
|
37
|
|
|
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6) |
|
38
|
|
|
// reason is the returned http reason |
|
39
|
|
|
// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6) |
|
40
|
|
|
// headers are the http headers of the reply |
|
41
|
|
|
// message is the returned text body |
|
42
|
|
|
// |
|
43
|
|
|
// IMPORTANT: to have this work, the main part of your program must include a |
|
44
|
|
|
// $webaccess->select() call periodically, which work exactly like stream_select(). |
|
45
|
|
|
// This is because the send and receive are asynchronous and will be completed later |
|
46
|
|
|
// in the select() when data are ready to be received and sent. |
|
47
|
|
|
// |
|
48
|
|
|
// This class can be use to make a synchronous query too. For such use null for the callback, |
|
49
|
|
|
// so make the request this way: |
|
50
|
|
|
// $response = $webaccess->request($url, null, $data, $is_xmlrpc, $keepalive_min_timeout); |
|
51
|
|
|
// where $response is an array('Code'=>code,'Reason'=>reason,'Headers'=>headers,'Message'=>message) |
|
52
|
|
|
// like the one passed to the callback function of the asynchronous request. |
|
53
|
|
|
// |
|
54
|
|
|
// If you use only synchronous queries then there is no need to call select() as the function |
|
55
|
|
|
// will return when the reply will be fully returned. |
|
56
|
|
|
// |
|
57
|
|
|
// If the connection itself fail, the array response will include a 'Error' string. |
|
58
|
|
|
// other functions: |
|
59
|
|
|
// list($host,$port,$path) = getHostPortPath($url); |
|
60
|
|
|
// gzdecode() workaround |
|
61
|
|
|
|
|
62
|
|
|
|
|
63
|
|
|
global $_web_access_compress_xmlrpc_request, $_web_access_compress_reply, |
|
|
|
|
|
|
64
|
|
|
$_web_access_keepalive, $_web_access_keepalive_timeout, |
|
65
|
|
|
$_web_access_keepalive_max, $_web_access_retry_timeout, |
|
66
|
|
|
$_web_access_retry_timeout_max, $_web_access_post_xmlrpc; |
|
67
|
|
|
|
|
68
|
|
|
|
|
69
|
|
|
// Will compress xmlrpc request ('never','accept','force','force-gzip','force-deflate') |
|
70
|
|
|
// If set to 'accept' the first request will be made without, and the eventual |
|
71
|
|
|
// 'Accept-Encoding' in reply will permit to decide if request compression can |
|
72
|
|
|
// be used (and if gzip or deflate) |
|
73
|
|
|
|
|
74
|
|
|
$_web_access_compress_xmlrpc_request = 'accept'; |
|
75
|
|
|
|
|
76
|
|
|
// will ask server for compressed reply (false, true) |
|
77
|
|
|
// if true then will add a 'Accept-Encoding' header to tell the server to compress |
|
78
|
|
|
// the reply if it support it. |
|
79
|
|
|
|
|
80
|
|
|
$_web_access_compress_reply = true; |
|
81
|
|
|
|
|
82
|
|
|
|
|
83
|
|
|
// keep alive connection ? else close it after the reply. |
|
84
|
|
|
// unless false, first request will be with keepalive, to get server timeout and max values |
|
85
|
|
|
// after timeout will be compared with the request $keepalive_min_timeout value to decide |
|
86
|
|
|
// if keepalive have to be used or not. Note that apache2 timeout is short (about 15s). |
|
87
|
|
|
// The classes will open, re-open or use existing connection as needed. |
|
88
|
|
|
|
|
89
|
|
|
$_web_access_keepalive = true; |
|
90
|
|
|
// timeout (s) without request before close, for keep alive |
|
91
|
|
|
|
|
92
|
|
|
$_web_access_keepalive_timeout = 600; |
|
93
|
|
|
// max requests before close, for keep alive |
|
94
|
|
|
|
|
95
|
|
|
$_web_access_keepalive_max = 2000; |
|
96
|
|
|
|
|
97
|
|
|
|
|
98
|
|
|
// for asynchronous call, in case of error, timeout before retrying. |
|
99
|
|
|
// it will be x2 for each error (on request or auto retry) until max, |
|
100
|
|
|
// then stop automatic retry, and next request calls will return false. |
|
101
|
|
|
// When stopped, a retry() or synchronous request will force a retry. |
|
102
|
|
|
$_web_access_retry_timeout = 20; |
|
103
|
|
|
$_web_access_retry_timeout_max = 60; |
|
104
|
|
|
|
|
105
|
|
|
|
|
106
|
|
|
// use text/html with xmlrpc= , instead of of pure text/xml request (false, true) |
|
107
|
|
|
// standard xml-rpc use pure text/xml request, where the xml is simply the body |
|
108
|
|
|
// of the http request (and it's how the xml-rpc reply will be made). As a facility |
|
109
|
|
|
// Dedimania support also to get the xml in a html GET or POST, where xmlrpc= will |
|
110
|
|
|
// contain a urlsafe base64 of the xml. Default to false, so use pure text/xml. |
|
111
|
|
|
$_web_access_post_xmlrpc = false; |
|
112
|
|
|
|
|
113
|
|
|
// Note that in each request the text/xml or xmlrpc= will be used only if $is_xmlrpc |
|
114
|
|
|
// is true. If false then the request will be a standard application/x-www-form-urlencoded |
|
115
|
|
|
// html GET or POST request ; in that case you have to build the url (GET) and/or |
|
116
|
|
|
// body data (POST) yourself. |
|
117
|
|
|
|
|
118
|
|
|
// use: list($host,$port,$path) = getHostPortPath($url); |
|
|
|
|
|
|
119
|
|
|
|
|
120
|
|
|
class Webaccess |
|
|
|
|
|
|
121
|
|
|
{ |
|
122
|
|
|
private $_WebaccessList; |
|
123
|
|
|
/** |
|
124
|
|
|
* @var Console |
|
125
|
|
|
*/ |
|
126
|
|
|
private $console; |
|
127
|
|
|
|
|
128
|
|
|
public function __construct(Console $console) |
|
129
|
|
|
{ |
|
130
|
|
|
$this->_WebaccessList = array(); |
|
131
|
|
|
$this->console = $console; |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
public function request( |
|
135
|
|
|
$url, |
|
136
|
|
|
$callback, |
|
137
|
|
|
$datas, |
|
138
|
|
|
$is_xmlrpc = false, |
|
139
|
|
|
$keepalive_min_timeout = 300, |
|
140
|
|
|
$opentimeout = 3, |
|
141
|
|
|
$waittimeout = 5, |
|
142
|
|
|
$agent = 'XMLaccess', |
|
143
|
|
|
$mimeType = "text/html" |
|
144
|
|
|
) { |
|
145
|
|
|
global $_web_access_keepalive, $_web_access_keepalive_timeout, $_web_access_keepalive_max; |
|
|
|
|
|
|
146
|
|
|
list($host, $port, $path) = getHostPortPath($url); |
|
147
|
|
|
|
|
148
|
|
|
if ($host === false) { |
|
149
|
|
|
$this->console->writeln('Webaccess request(): Bad url: $ff0'.$url."\n"); |
|
150
|
|
|
} else { |
|
151
|
|
|
$server = $host.':'.$port; |
|
152
|
|
|
// create object is needed |
|
153
|
|
|
if (!isset($this->_WebaccessList[$server]) || $this->_WebaccessList[$server] === null) { |
|
154
|
|
|
$this->_WebaccessList[$server] = new \WebaccessUrl( |
|
155
|
|
|
$this, |
|
156
|
|
|
$host, |
|
157
|
|
|
$port, |
|
158
|
|
|
$_web_access_keepalive, |
|
159
|
|
|
$_web_access_keepalive_timeout, |
|
160
|
|
|
$_web_access_keepalive_max, |
|
161
|
|
|
$agent, |
|
162
|
|
|
$mimeType, |
|
163
|
|
|
$this->console |
|
164
|
|
|
); |
|
165
|
|
|
|
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
// increase the default timeout for sync/wait request |
|
169
|
|
|
if ($callback == null && $waittimeout == 5) { |
|
170
|
|
|
$waittimeout = 12; |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
// call request |
|
174
|
|
|
if ($this->_WebaccessList[$server] !== null) { |
|
175
|
|
|
$query = array( |
|
176
|
|
|
'Path' => $path, |
|
177
|
|
|
'Callback' => $callback, |
|
178
|
|
|
'QueryDatas' => $datas, |
|
179
|
|
|
'IsXmlrpc' => $is_xmlrpc, |
|
180
|
|
|
'KeepaliveMinTimeout' => $keepalive_min_timeout, |
|
181
|
|
|
'OpenTimeout' => $opentimeout, |
|
182
|
|
|
'WaitTimeout' => $waittimeout, |
|
183
|
|
|
'MimeType' => $mimeType, |
|
184
|
|
|
); |
|
185
|
|
|
|
|
186
|
|
|
return $this->_WebaccessList[$server]->request($query); |
|
187
|
|
|
} |
|
188
|
|
|
} |
|
189
|
|
|
|
|
190
|
|
|
return false; |
|
191
|
|
|
} // request |
|
192
|
|
|
|
|
193
|
|
|
public function retry($url) |
|
194
|
|
|
{ |
|
195
|
|
|
list($host, $port, $path) = getHostPortPath($url); |
|
|
|
|
|
|
196
|
|
|
|
|
197
|
|
|
if ($host === false) { |
|
198
|
|
|
$this->console->writeln('Webaccess retry(): Bad url: $ff0'.$url."\r"); |
|
199
|
|
|
} else { |
|
200
|
|
|
$server = $host.':'.$port; |
|
201
|
|
|
if (isset($this->_WebaccessList[$server])) { |
|
202
|
|
|
$this->_WebaccessList[$server]->retry(); |
|
203
|
|
|
} |
|
204
|
|
|
} |
|
205
|
|
|
} // retry |
|
206
|
|
|
|
|
207
|
|
|
public function select(&$read, &$write, &$except, $tv_sec, $tv_usec = 0) |
|
208
|
|
|
{ |
|
209
|
|
|
$timeout = (int)($tv_sec * 1000000 + $tv_usec); |
|
210
|
|
|
|
|
211
|
|
|
if ($read == null) { |
|
212
|
|
|
$read = array(); |
|
213
|
|
|
} |
|
214
|
|
|
if ($write == null) { |
|
215
|
|
|
$write = array(); |
|
216
|
|
|
} |
|
217
|
|
|
if ($except == null) { |
|
218
|
|
|
$except = array(); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
$read = $this->_getWebaccessReadSockets($read); |
|
222
|
|
|
$write = $this->_getWebaccessWriteSockets($write); |
|
223
|
|
|
// $except = $this->_getWebaccessReadSockets($except); |
|
|
|
|
|
|
224
|
|
|
//print_r($except); |
|
225
|
|
|
|
|
226
|
|
|
if (count($read) + count($write) + count($except) == 0) { |
|
227
|
|
|
// sleep the asked timeout... |
|
228
|
|
|
if ($timeout > 1000) { |
|
229
|
|
|
usleep($timeout); |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
return 0; |
|
233
|
|
|
} |
|
234
|
|
|
|
|
235
|
|
|
$utime = (int)(microtime(true) * 1000000); |
|
236
|
|
|
$nb = @stream_select($read, $write, $except, $tv_sec, $tv_usec); |
|
237
|
|
|
if ($nb === false) { |
|
238
|
|
|
// in case stream_select "forgot" to wait, sleep the remaining asked timeout... |
|
239
|
|
|
$dtime = (int)(microtime(true) * 1000000) - $utime; |
|
240
|
|
|
$timeout -= $dtime; |
|
241
|
|
|
if ($timeout > 1000) { |
|
242
|
|
|
usleep($timeout); |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
return false; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
$this->_manageWebaccessSockets($read, $write, $except); |
|
249
|
|
|
|
|
250
|
|
|
// workaround for stream_select bug with amd64, replace $nb with sum of arrays |
|
251
|
|
|
return count($read) + count($write) + count($except); |
|
252
|
|
|
} // select |
|
253
|
|
|
|
|
254
|
|
|
private function _manageWebaccessSockets(&$receive, &$send, &$except) |
|
|
|
|
|
|
255
|
|
|
{ |
|
256
|
|
|
// send pending datas on all webaccess sockets |
|
257
|
|
|
if (is_array($send) && count($send) > 0) { |
|
258
|
|
|
foreach ($send as $key => $socket) { |
|
259
|
|
|
$i = $this->_findWebaccessSocket($socket); |
|
260
|
|
|
if ($i !== false) { |
|
261
|
|
|
if (isset($this->_WebaccessList[$i]->_spool[0]['State']) && |
|
262
|
|
|
$this->_WebaccessList[$i]->_spool[0]['State'] == 'OPEN') { |
|
263
|
|
|
$this->_WebaccessList[$i]->_open(); |
|
264
|
|
|
} else { |
|
265
|
|
|
$this->_WebaccessList[$i]->_send(); |
|
266
|
|
|
} |
|
267
|
|
|
unset($send[$key]); |
|
268
|
|
|
} |
|
269
|
|
|
} |
|
270
|
|
|
} |
|
271
|
|
|
|
|
272
|
|
|
// read datas from all needed webaccess |
|
273
|
|
|
if (is_array($receive) && count($receive) > 0) { |
|
274
|
|
|
foreach ($receive as $key => $socket) { |
|
275
|
|
|
$i = $this->_findWebaccessSocket($socket); |
|
276
|
|
|
if ($i !== false) { |
|
277
|
|
|
$this->_WebaccessList[$i]->_receive(); |
|
278
|
|
|
unset($receive[$key]); |
|
279
|
|
|
} |
|
280
|
|
|
} |
|
281
|
|
|
} |
|
282
|
|
|
} // _manageWebaccessSockets |
|
283
|
|
|
|
|
284
|
|
|
private function _findWebaccessSocket($socket) |
|
285
|
|
|
{ |
|
286
|
|
|
foreach ($this->_WebaccessList as $key => $wau) { |
|
287
|
|
|
if ($wau->_socket == $socket) { |
|
288
|
|
|
return $key; |
|
289
|
|
|
} |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
return false; |
|
293
|
|
|
} |
|
294
|
|
|
|
|
295
|
|
|
private function _getWebaccessReadSockets($socks) |
|
296
|
|
|
{ |
|
297
|
|
|
foreach ($this->_WebaccessList as $key => $wau) { |
|
298
|
|
|
if ($wau->_state == 'OPENED' && $wau->_socket) { |
|
299
|
|
|
$socks[] = $wau->_socket; |
|
300
|
|
|
} |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
return $socks; |
|
304
|
|
|
} |
|
305
|
|
|
|
|
306
|
|
|
private function _getWebaccessWriteSockets($socks) |
|
307
|
|
|
{ |
|
308
|
|
|
foreach ($this->_WebaccessList as $key => $wau) { |
|
309
|
|
|
|
|
310
|
|
|
if (isset($wau->_spool[0]['State']) && |
|
311
|
|
|
($wau->_spool[0]['State'] == 'OPEN' || |
|
312
|
|
|
$wau->_spool[0]['State'] == 'BAD' || |
|
313
|
|
|
$wau->_spool[0]['State'] == 'SEND') |
|
314
|
|
|
) { |
|
315
|
|
|
|
|
316
|
|
|
if (($wau->_state == 'CLOSED' || $wau->_state == 'BAD') && !$wau->_socket) { |
|
317
|
|
|
$wau->_open(); |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
if ($wau->_state == 'OPENED' && $wau->_socket) { |
|
321
|
|
|
$socks[] = $wau->_socket; |
|
322
|
|
|
} |
|
323
|
|
|
} |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
return $socks; |
|
327
|
|
|
} |
|
328
|
|
|
|
|
329
|
|
|
function getAllSpools() |
|
|
|
|
|
|
330
|
|
|
{ |
|
331
|
|
|
$num = 0; |
|
332
|
|
|
$bad = 0; |
|
333
|
|
|
foreach ($this->_WebaccessList as $key => $wau) { |
|
334
|
|
|
if ($wau->_state == 'OPENED' || $wau->_state == 'CLOSED') { |
|
335
|
|
|
$num += count($wau->_spool); |
|
336
|
|
|
} elseif ($wau->_state == 'BAD') { |
|
337
|
|
|
$bad += count($wau->_spool); |
|
338
|
|
|
} |
|
339
|
|
|
} |
|
340
|
|
|
|
|
341
|
|
|
return array($num, $bad); |
|
342
|
|
|
} |
|
343
|
|
|
} |
|
344
|
|
|
|
|
345
|
|
|
// useful datas to handle received headers |
|
346
|
|
|
$_wa_header_separator = array('cookie' => ';', 'set-cookie' => ';'); |
|
347
|
|
|
$_wa_header_multi = array('set-cookie' => true); |
|
348
|
|
|
|
|
349
|
|
|
class WebaccessUrl |
|
|
|
|
|
|
350
|
|
|
{ |
|
351
|
|
|
|
|
352
|
|
|
//----------------------------- |
|
353
|
|
|
// Fields |
|
354
|
|
|
//----------------------------- |
|
355
|
|
|
|
|
356
|
|
|
public $wa; |
|
357
|
|
|
|
|
358
|
|
|
public $_host; |
|
359
|
|
|
|
|
360
|
|
|
public $_port; |
|
361
|
|
|
|
|
362
|
|
|
public $_compress_request; |
|
363
|
|
|
|
|
364
|
|
|
public $_socket; |
|
365
|
|
|
|
|
366
|
|
|
public $_state; |
|
367
|
|
|
|
|
368
|
|
|
public $_keepalive; |
|
369
|
|
|
|
|
370
|
|
|
public $_keepalive_timeout; |
|
371
|
|
|
|
|
372
|
|
|
public $_keepalive_max; |
|
373
|
|
|
|
|
374
|
|
|
public $_serv_keepalive_timeout; |
|
375
|
|
|
|
|
376
|
|
|
public $_serv_keepalive_max; |
|
377
|
|
|
|
|
378
|
|
|
public $_spool; |
|
379
|
|
|
|
|
380
|
|
|
public $_wait; |
|
381
|
|
|
|
|
382
|
|
|
public $_response; |
|
383
|
|
|
|
|
384
|
|
|
public $_query_num; |
|
385
|
|
|
|
|
386
|
|
|
public $_request_time; |
|
387
|
|
|
|
|
388
|
|
|
public $_cookies; |
|
389
|
|
|
|
|
390
|
|
|
public $_webaccess_str; |
|
391
|
|
|
|
|
392
|
|
|
public $_bad_time; |
|
393
|
|
|
|
|
394
|
|
|
public $_bad_timeout; |
|
395
|
|
|
|
|
396
|
|
|
public $_read_time; |
|
397
|
|
|
|
|
398
|
|
|
public $_agent; |
|
399
|
|
|
|
|
400
|
|
|
public $_mimeType; |
|
401
|
|
|
|
|
402
|
|
|
public $_query_time; |
|
403
|
|
|
/** |
|
404
|
|
|
* @var Console |
|
405
|
|
|
*/ |
|
406
|
|
|
private $console; |
|
407
|
|
|
|
|
408
|
|
|
// $_state values : |
|
409
|
|
|
// 'OPENED' : socket is opened |
|
410
|
|
|
// 'CLOSED' : socket is closed (asked, completed, or closed by server) |
|
411
|
|
|
// 'BAD' : socket is closed, bad/error or beginning state |
|
412
|
|
|
|
|
413
|
|
|
// $query['State'] values : (note: $query is added in $_spool, so $this->_spool[0] is the first $query to handle) |
|
|
|
|
|
|
414
|
|
|
// 'BAD' : |
|
415
|
|
|
// 'OPEN' : should prepare request datas then send them |
|
416
|
|
|
// 'SEND' : request datas are prepared, send them |
|
417
|
|
|
// 'RECEIVE' : request datas are sent, receive reply datas |
|
418
|
|
|
// 'DONE' : request completed |
|
419
|
|
|
|
|
420
|
|
|
//----------------------------- |
|
421
|
|
|
// Methods |
|
422
|
|
|
//----------------------------- |
|
423
|
|
|
|
|
424
|
|
|
public function __construct( |
|
425
|
|
|
&$wa, |
|
426
|
|
|
$host, |
|
427
|
|
|
$port, |
|
428
|
|
|
$keepalive = true, |
|
429
|
|
|
$keepalive_timeout = 600, |
|
430
|
|
|
$keepalive_max = 300, |
|
431
|
|
|
$agent = 'XMLaccess', |
|
432
|
|
|
$mimeType = "text/html", |
|
433
|
|
|
Console $console |
|
434
|
|
|
) { |
|
435
|
|
|
global $_web_access_compress_xmlrpc_request; |
|
|
|
|
|
|
436
|
|
|
$this->wa = &$wa; |
|
437
|
|
|
$this->_host = $host; |
|
438
|
|
|
$this->_port = $port; |
|
439
|
|
|
$this->_webaccess_str = 'Webaccess('.$this->_host.':'.$this->_port.'): '; |
|
440
|
|
|
$this->_agent = $agent; |
|
441
|
|
|
$this->_mimeType = $mimeType; |
|
442
|
|
|
|
|
443
|
|
|
// request compression setting |
|
444
|
|
|
if ($_web_access_compress_xmlrpc_request == 'accept') { |
|
445
|
|
|
$this->_compress_request = 'accept'; |
|
446
|
|
|
} elseif ($_web_access_compress_xmlrpc_request == 'force') { |
|
447
|
|
|
if (function_exists('gzencode')) { |
|
448
|
|
|
$this->_compress_request = 'gzip'; |
|
449
|
|
|
} elseif (function_exists('gzdeflate')) { |
|
450
|
|
|
$this->_compress_request = 'deflate'; |
|
451
|
|
|
} else { |
|
452
|
|
|
$this->_compress_request = false; |
|
453
|
|
|
} |
|
454
|
|
|
} elseif ($_web_access_compress_xmlrpc_request == 'force-gzip' && function_exists('gzencode')) { |
|
455
|
|
|
$this->_compress_request = 'gzip'; |
|
456
|
|
|
} elseif ($_web_access_compress_xmlrpc_request == 'force-deflate' && function_exists('gzdeflate')) { |
|
457
|
|
|
$this->_compress_request = 'deflate'; |
|
458
|
|
|
} else { |
|
459
|
|
|
$this->_compress_request = false; |
|
460
|
|
|
} |
|
461
|
|
|
|
|
462
|
|
|
$this->_socket = null; |
|
463
|
|
|
$this->_state = 'CLOSED'; |
|
464
|
|
|
$this->_keepalive = $keepalive; |
|
465
|
|
|
$this->_keepalive_timeout = $keepalive_timeout; |
|
466
|
|
|
$this->_keepalive_max = $keepalive_max; |
|
467
|
|
|
$this->_serv_keepalive_timeout = $keepalive_timeout; |
|
468
|
|
|
$this->_serv_keepalive_max = $keepalive_max; |
|
469
|
|
|
$this->_spool = array(); |
|
470
|
|
|
$this->_wait = false; |
|
471
|
|
|
$this->_response = ''; |
|
472
|
|
|
$this->_query_num = 0; |
|
473
|
|
|
$this->_query_time = time(); |
|
474
|
|
|
$this->_cookies = array(); |
|
475
|
|
|
$this->_bad_time = time(); |
|
476
|
|
|
$this->_bad_timeout = 0; |
|
477
|
|
|
$this->_read_time = 0; |
|
478
|
|
|
$this->console = $console; |
|
479
|
|
|
|
|
480
|
|
|
$this->console->writeln("at request"); |
|
481
|
|
|
|
|
482
|
|
|
} |
|
483
|
|
|
|
|
484
|
|
|
// put connection in BAD state |
|
485
|
|
|
public function _bad($errstr, $isbad = true) |
|
486
|
|
|
{ |
|
487
|
|
|
global $_web_access_retry_timeout; |
|
|
|
|
|
|
488
|
|
|
$this->console->writeln($this->_webaccess_str.'$f00'.$errstr); |
|
489
|
|
|
|
|
490
|
|
|
$this->infos(); |
|
491
|
|
|
|
|
492
|
|
|
if ($this->_socket) { |
|
493
|
|
|
@fclose($this->_socket); |
|
|
|
|
|
|
494
|
|
|
} |
|
495
|
|
|
$this->_socket = null; |
|
496
|
|
|
|
|
497
|
|
|
if ($isbad) { |
|
498
|
|
View Code Duplication |
if (isset($this->_spool[0]['State'])) { |
|
|
|
|
|
|
499
|
|
|
$this->_spool[0]['State'] = 'BAD'; |
|
500
|
|
|
} |
|
501
|
|
|
$this->_state = 'BAD'; |
|
502
|
|
|
|
|
503
|
|
|
$this->_bad_time = time(); |
|
504
|
|
|
if ($this->_bad_timeout < $_web_access_retry_timeout) { |
|
505
|
|
|
$this->_bad_timeout = $_web_access_retry_timeout; |
|
506
|
|
|
} else { |
|
507
|
|
|
$this->_bad_timeout *= 2; |
|
508
|
|
|
} |
|
509
|
|
|
} else { |
|
510
|
|
View Code Duplication |
if (isset($this->_spool[0]['State'])) { |
|
|
|
|
|
|
511
|
|
|
$this->_spool[0]['State'] = 'OPEN'; |
|
512
|
|
|
} |
|
513
|
|
|
$this->_state = 'CLOSED'; |
|
514
|
|
|
} |
|
515
|
|
|
$this->_callCallback($this->_webaccess_str.$errstr); |
|
516
|
|
|
} |
|
517
|
|
|
|
|
518
|
|
|
public function retry() |
|
519
|
|
|
{ |
|
520
|
|
|
global $_web_access_retry_timeout; |
|
|
|
|
|
|
521
|
|
|
if ($this->_state == 'BAD') { |
|
522
|
|
|
$this->_bad_time = time(); |
|
523
|
|
|
$this->_bad_timeout = $_web_access_retry_timeout; |
|
524
|
|
|
} |
|
525
|
|
|
} |
|
526
|
|
|
|
|
527
|
|
|
//$query = array('Path'=>$path,'Callback'=>$callback, 'QueryDatas'=>$datas, |
|
|
|
|
|
|
528
|
|
|
// 'IsXmlrpc'=>$is_xmlrpc, 'KeepaliveMinTimeout'=>$keepalive_min_timeout, |
|
|
|
|
|
|
529
|
|
|
// 'OpenTimeout'=>$opentimeout, 'WaitTimeout'=>$waittimeout); |
|
|
|
|
|
|
530
|
|
|
// will add: 'State','HDatas','Datas','DatasSize', |
|
|
|
|
|
|
531
|
|
|
// will add: 'DatasSent','Response','ResponseSize','Headers','Close','Times' |
|
|
|
|
|
|
532
|
|
|
public function request(&$query) |
|
533
|
|
|
{ |
|
534
|
|
|
global $_web_access_compress_reply; |
|
|
|
|
|
|
535
|
|
|
global $_web_access_post_xmlrpc; |
|
|
|
|
|
|
536
|
|
|
global $_web_access_retry_timeout; |
|
|
|
|
|
|
537
|
|
|
global $_web_access_retry_timeout_max; |
|
|
|
|
|
|
538
|
|
|
|
|
539
|
|
|
$query['State'] = 'BAD'; |
|
540
|
|
|
$query['HDatas'] = ''; |
|
541
|
|
|
$query['Datas'] = ''; |
|
542
|
|
|
$query['DatasSize'] = 0; |
|
543
|
|
|
$query['DatasSent'] = 0; |
|
544
|
|
|
$query['Response'] = ''; |
|
545
|
|
|
$query['ResponseSize'] = 0; |
|
546
|
|
|
$query['Headers'] = array(); |
|
547
|
|
|
$query['Close'] = false; |
|
548
|
|
|
$query['Times'] = array( |
|
549
|
|
|
'open' => array(-1.0, -1.0), |
|
550
|
|
|
'send' => array(-1.0, -1.0), |
|
551
|
|
|
'receive' => array(-1.0, -1.0, 0), |
|
552
|
|
|
); |
|
553
|
|
|
|
|
554
|
|
|
|
|
555
|
|
|
// if async, in error, and maximal timeout, then forget the request and return false. |
|
556
|
|
|
if (($query['Callback'] != null) && ($this->_state == 'BAD')) { |
|
557
|
|
|
if ($this->_bad_timeout > $_web_access_retry_timeout_max) { |
|
558
|
|
|
$this->console->writeln($this->_webaccess_str.'$f00Request refused for consecutive errors $555('.$this->_bad_timeout |
|
559
|
|
|
." / ".$_web_access_retry_timeout_max.")"); |
|
560
|
|
|
|
|
561
|
|
|
return false; |
|
562
|
|
|
} else { |
|
563
|
|
|
// if not max then accept the request |
|
564
|
|
|
// and try a request (minimum $_web_access_retry_timeout/2 after previous try) |
|
565
|
|
|
$time = time(); |
|
566
|
|
|
$timeout = ($this->_bad_timeout / 2) - ($time - $this->_bad_time); |
|
567
|
|
|
if ($timeout < 0) { |
|
568
|
|
|
$timeout = 0; |
|
569
|
|
|
} |
|
570
|
|
|
$this->_bad_time = $time - $this->_bad_timeout + $timeout; |
|
571
|
|
|
} |
|
572
|
|
|
} |
|
573
|
|
|
|
|
574
|
|
|
// build datas to send |
|
575
|
|
|
if (($query['Callback'] == null) || (is_array($query['Callback']) && |
|
576
|
|
|
isset($query['Callback'][0]) && |
|
577
|
|
|
is_callable($query['Callback'][0])) |
|
578
|
|
|
) { |
|
579
|
|
|
|
|
580
|
|
|
if (is_string($query['QueryDatas']) && strlen($query['QueryDatas']) > 0) { |
|
581
|
|
|
$msg = "POST ".$query['Path']." HTTP/1.1\r\n"; |
|
582
|
|
|
$msg .= "Host: ".$this->_host."\r\n"; |
|
583
|
|
|
$msg .= "User-Agent: ".$this->_agent."\r\n"; |
|
584
|
|
|
$msg .= "Cache-Control: no-cache\r\n"; |
|
585
|
|
|
|
|
586
|
|
View Code Duplication |
if ($_web_access_compress_reply) { |
|
|
|
|
|
|
587
|
|
|
// ask compression of response if gzdecode() and/or gzinflate() is available |
|
588
|
|
|
if (function_exists('gzdecode') && function_exists('gzinflate')) { |
|
589
|
|
|
$msg .= "Accept-Encoding: deflate, gzip\r\n"; |
|
590
|
|
|
} elseif (function_exists('gzdecode')) { |
|
591
|
|
|
$msg .= "Accept-Encoding: gzip\r\n"; |
|
592
|
|
|
} elseif (function_exists('gzinflate')) { |
|
593
|
|
|
$msg .= "Accept-Encoding: deflate\r\n"; |
|
594
|
|
|
} |
|
595
|
|
|
} |
|
596
|
|
|
|
|
597
|
|
|
// echo "\nData:\n\n".$query['QueryDatas']."\n"; // for debug purposes, don't remove! |
|
|
|
|
|
|
598
|
|
|
|
|
599
|
|
|
if ($query['IsXmlrpc'] === true) { |
|
600
|
|
|
if ($_web_access_post_xmlrpc) { |
|
601
|
|
|
$msg .= "Content-type: application/x-www-form-urlencoded; charset=UTF-8\r\n"; |
|
602
|
|
|
|
|
603
|
|
|
echo "\n=========================== Data =================================\n\n".$query['Datas']."\n"; |
|
604
|
|
|
|
|
605
|
|
|
$query['QueryDatas'] = "xmlrpc=".urlsafe_base64_encode($query['QueryDatas']); |
|
606
|
|
|
} else { |
|
607
|
|
|
$msg .= "Content-type: text/xml; charset=UTF-8\r\n"; |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
if ($this->_compress_request == 'gzip' && function_exists('gzencode')) { |
|
611
|
|
|
$msg .= "Content-Encoding: gzip\r\n"; |
|
612
|
|
|
$query['QueryDatas'] = gzencode($query['QueryDatas']); |
|
613
|
|
|
} elseif ($this->_compress_request == 'deflate' && function_exists('gzdeflate')) { |
|
614
|
|
|
$msg .= "Content-Encoding: deflate\r\n"; |
|
615
|
|
|
$query['QueryDatas'] = gzdeflate($query['QueryDatas']); |
|
616
|
|
|
} |
|
617
|
|
|
} elseif (is_string($query['IsXmlrpc'])) { |
|
618
|
|
|
$msg .= "Content-type: ".$query['IsXmlrpc']."\r\n"; |
|
619
|
|
|
$msg .= "Accept: */*\r\n"; |
|
620
|
|
|
} else { |
|
621
|
|
|
$msg .= "Content-type: ".$query['MimeType']."\r\n"; |
|
622
|
|
|
$msg .= "Accept: */*\r\n"; |
|
623
|
|
|
} |
|
624
|
|
|
|
|
625
|
|
|
$msg .= "Content-length: ".strlen($query['QueryDatas'])."\r\n"; |
|
626
|
|
|
|
|
627
|
|
|
$query['HDatas'] = $msg; |
|
628
|
|
|
|
|
629
|
|
|
$query['State'] = 'OPEN'; |
|
630
|
|
|
$query['Retries'] = 0; |
|
631
|
|
|
|
|
632
|
|
|
// print_r($msg); for debugging purposes - don't remove |
|
633
|
|
|
|
|
634
|
|
|
// add the query in spool |
|
635
|
|
|
$this->_spool[] = &$query; |
|
636
|
|
|
|
|
637
|
|
View Code Duplication |
if ($query['Callback'] == null) { |
|
|
|
|
|
|
638
|
|
|
$this->_wait = true; |
|
639
|
|
|
$this->_open($query['OpenTimeout'], $query['WaitTimeout']); // wait more in not callback mode |
|
640
|
|
|
$this->_spool = array(); |
|
641
|
|
|
$this->_wait = false; |
|
642
|
|
|
|
|
643
|
|
|
return $query['Response']; |
|
644
|
|
|
} else { |
|
645
|
|
|
$this->_open(); |
|
646
|
|
|
} |
|
647
|
|
|
} else { |
|
648
|
|
|
$msg = "GET ".$query['Path']." HTTP/1.1\r\n"; |
|
649
|
|
|
$msg .= "Host: ".$this->_host."\r\n"; |
|
650
|
|
|
$msg .= "User-Agent: ".$this->_agent."\r\n"; |
|
651
|
|
|
$msg .= "Cache-Control: no-cache\r\n"; |
|
652
|
|
View Code Duplication |
if ($_web_access_compress_reply) { |
|
|
|
|
|
|
653
|
|
|
// ask compression of response if gzdecode() and/or gzinflate() is available |
|
654
|
|
|
if (function_exists('gzdecode') && function_exists('gzinflate')) { |
|
655
|
|
|
$msg .= "Accept-Encoding: deflate, gzip\r\n"; |
|
656
|
|
|
} elseif (function_exists('gzdecode')) { |
|
657
|
|
|
$msg .= "Accept-Encoding: gzip\r\n"; |
|
658
|
|
|
} elseif (function_exists('gzinflate')) { |
|
659
|
|
|
$msg .= "Accept-Encoding: deflate\r\n"; |
|
660
|
|
|
} |
|
661
|
|
|
} |
|
662
|
|
|
$msg .= "Content-type: ".$query['MimeType']."; charset=UTF-8\r\n"; |
|
663
|
|
|
$msg .= "Content-length: ".strlen($query['QueryDatas'])."\r\n"; |
|
664
|
|
|
$query['HDatas'] = $msg; |
|
665
|
|
|
|
|
666
|
|
|
$query['State'] = 'OPEN'; |
|
667
|
|
|
$query['Retries'] = 0; |
|
668
|
|
|
|
|
669
|
|
|
// add the query in spool |
|
670
|
|
|
$this->_spool[] = &$query; |
|
671
|
|
|
|
|
672
|
|
View Code Duplication |
if ($query['Callback'] == null) { |
|
|
|
|
|
|
673
|
|
|
$this->_wait = true; |
|
674
|
|
|
$this->_open($query['OpenTimeout'], $query['WaitTimeout']); // wait more in not callback mode |
|
675
|
|
|
$this->_spool = array(); |
|
676
|
|
|
$this->_wait = false; |
|
677
|
|
|
|
|
678
|
|
|
return $query['Response']; |
|
679
|
|
|
} else { |
|
680
|
|
|
$this->_open(); |
|
681
|
|
|
} |
|
682
|
|
|
} |
|
683
|
|
|
|
|
684
|
|
|
} else { |
|
685
|
|
|
|
|
686
|
|
|
$this->console->writeln($this->_webaccess_str.'Bad callback function:$fff '.$query['Callback']); |
|
687
|
|
|
|
|
688
|
|
|
return false; |
|
689
|
|
|
} |
|
690
|
|
|
|
|
691
|
|
|
return true; |
|
692
|
|
|
} |
|
693
|
|
|
|
|
694
|
|
|
// open the socket (close it before if needed) |
|
695
|
|
|
private function _open_socket($opentimeout = 0.0) |
|
696
|
|
|
{ |
|
697
|
|
|
// if socket not opened, then open it (2 tries) |
|
698
|
|
|
if (!$this->_socket || $this->_state != 'OPENED') { |
|
699
|
|
|
$time = microtime(true); |
|
700
|
|
|
$this->_spool[0]['Times']['open'][0] = $time; |
|
701
|
|
|
|
|
702
|
|
|
$errno = ''; |
|
703
|
|
|
$errstr = ''; |
|
704
|
|
|
$this->_socket = @fsockopen($this->_host, $this->_port, $errno, $errstr, 1.8); // first try |
|
705
|
|
|
if (!$this->_socket) { |
|
706
|
|
|
|
|
707
|
|
|
if ($opentimeout >= 1.0) { |
|
708
|
|
|
$this->_socket = @fsockopen($this->_host, $this->_port, $errno, $errstr, $opentimeout); |
|
709
|
|
|
} |
|
710
|
|
|
if (!$this->_socket) { |
|
711
|
|
|
$this->_bad('Error('.$errno.')'.$errstr.', connection failed!'); |
|
712
|
|
|
|
|
713
|
|
|
return; |
|
714
|
|
|
} |
|
715
|
|
|
} |
|
716
|
|
|
$this->_state = 'OPENED'; |
|
717
|
|
|
// new socket connection : reset all pending request original values |
|
718
|
|
|
for ($i = 0; $i < count($this->_spool); $i++) { |
|
|
|
|
|
|
719
|
|
|
$this->_spool[$i]['State'] = 'OPEN'; |
|
720
|
|
|
$this->_spool[$i]['DatasSent'] = 0; |
|
721
|
|
|
$this->_spool[$i]['Response'] = ''; |
|
722
|
|
|
$this->_spool[$i]['Headers'] = array(); |
|
723
|
|
|
} |
|
724
|
|
|
$this->_response = ''; |
|
725
|
|
|
$this->_query_num = 0; |
|
726
|
|
|
$this->_query_time = time(); |
|
727
|
|
|
$time = microtime(true); |
|
728
|
|
|
$this->_spool[0]['Times']['open'][1] = $time - $this->_spool[0]['Times']['open'][0]; |
|
729
|
|
|
} |
|
730
|
|
|
} |
|
731
|
|
|
|
|
732
|
|
|
// open the connection (if not already opened) and send |
|
733
|
|
|
public function _open($opentimeout = 0.0, $waittimeout = 5.0) |
|
734
|
|
|
{ |
|
735
|
|
|
global $_web_access_retry_timeout_max; |
|
|
|
|
|
|
736
|
|
|
|
|
737
|
|
|
if (!isset($this->_spool[0]['State'])) { |
|
738
|
|
|
return false; |
|
739
|
|
|
} |
|
740
|
|
|
$time = time(); |
|
741
|
|
|
|
|
742
|
|
|
// if async, in error, then return false until timeout or if > max) |
|
743
|
|
|
if (!$this->_wait && $this->_state == 'BAD' && |
|
744
|
|
|
(($this->_bad_timeout > $_web_access_retry_timeout_max) || |
|
745
|
|
|
(($time - $this->_bad_time) < $this->_bad_timeout))) { |
|
746
|
|
|
$this->console->writeln($this->_webaccess_str.'wait to retry ('.($time - $this->_bad_time).' / '.$this->_bad_timeout.')'); |
|
747
|
|
|
|
|
748
|
|
|
return false; |
|
749
|
|
|
} |
|
750
|
|
|
|
|
751
|
|
|
// if the socket is probably in timeout, close it |
|
752
|
|
|
if ($this->_socket && $this->_state == 'OPENED' && |
|
753
|
|
|
($this->_serv_keepalive_timeout <= ($time - $this->_query_time))) { |
|
754
|
|
|
|
|
755
|
|
|
$this->console->writeln($this->_webaccess_str.'timeout, closed it!'); |
|
756
|
|
|
$this->_state = 'CLOSED'; |
|
757
|
|
|
@fclose($this->_socket); |
|
|
|
|
|
|
758
|
|
|
$this->_socket = null; |
|
759
|
|
|
} |
|
760
|
|
|
|
|
761
|
|
|
// if socket is not opened, open it |
|
762
|
|
|
if (!$this->_socket || $this->_state != 'OPENED') { |
|
763
|
|
|
$this->_open_socket($opentimeout); |
|
764
|
|
|
} |
|
765
|
|
|
|
|
766
|
|
|
// if socket is open, send data if possible |
|
767
|
|
|
if ($this->_socket) { |
|
768
|
|
|
$this->_read_time = microtime(true); |
|
769
|
|
|
|
|
770
|
|
|
// if wait (synchronous query) then go on all pending write/read until the last |
|
771
|
|
|
if ($this->_wait) { |
|
772
|
|
|
@stream_set_timeout($this->_socket, 0, 10000); |
|
|
|
|
|
|
773
|
|
|
|
|
774
|
|
|
while (isset($this->_spool[0]['State']) && |
|
775
|
|
|
($this->_spool[0]['State'] == 'OPEN' || |
|
776
|
|
|
$this->_spool[0]['State'] == 'SEND' || |
|
777
|
|
|
$this->_spool[0]['State'] == 'RECEIVE')) { |
|
778
|
|
|
echo 'State='.$this->_spool[0]['State']." (".count($this->_spool).")\n"; |
|
779
|
|
|
if (!$this->_socket || $this->_state != 'OPENED') { |
|
780
|
|
|
$this->_open_socket($opentimeout); |
|
781
|
|
|
} |
|
782
|
|
|
|
|
783
|
|
|
if ($this->_spool[0]['State'] == 'OPEN') { |
|
784
|
|
|
$time = microtime(true); |
|
785
|
|
|
$this->_spool[0]['Times']['send'][0] = $time; |
|
786
|
|
|
$this->_send($waittimeout); |
|
787
|
|
|
} elseif ($this->_spool[0]['State'] == 'SEND') { |
|
788
|
|
|
$this->_send($waittimeout); |
|
789
|
|
|
} elseif ($this->_spool[0]['State'] == 'RECEIVE') { |
|
790
|
|
|
$this->_receive($waittimeout * 4); |
|
791
|
|
|
} |
|
792
|
|
|
|
|
793
|
|
|
// if timeout then error |
|
794
|
|
View Code Duplication |
if (($difftime = round(microtime(true) - $this->_read_time)) > $waittimeout) { |
|
|
|
|
|
|
795
|
|
|
$this->_bad("Request timeout, in _open ({$difftime} > {$waittimeout}s) state=".$this->_spool[0]['State'] |
|
796
|
|
|
); |
|
797
|
|
|
|
|
798
|
|
|
return false; |
|
799
|
|
|
} |
|
800
|
|
|
} |
|
801
|
|
|
if ($this->_socket) { |
|
802
|
|
|
@stream_set_timeout($this->_socket, 0, 2000); |
|
|
|
|
|
|
803
|
|
|
} |
|
804
|
|
|
} // else just do a send on the current |
|
805
|
|
|
elseif (isset($this->_spool[0]['State']) && $this->_spool[0]['State'] == 'OPEN') { |
|
806
|
|
|
@stream_set_timeout($this->_socket, 0, 2000); // timeout 2 ms |
|
|
|
|
|
|
807
|
|
|
$this->_send($waittimeout); |
|
808
|
|
|
} |
|
809
|
|
|
} |
|
810
|
|
|
} // _open |
|
811
|
|
|
|
|
812
|
|
|
public function _send($waittimeout = 20) |
|
813
|
|
|
{ |
|
814
|
|
|
if (!isset($this->_spool[0]['State'])) { |
|
815
|
|
|
return; |
|
816
|
|
|
} |
|
817
|
|
|
|
|
818
|
|
|
$errno = ''; |
|
819
|
|
|
$errstr = ''; |
|
820
|
|
|
|
|
821
|
|
|
// if OPEN then become SEND |
|
822
|
|
|
if ($this->_spool[0]['State'] == 'OPEN') { |
|
823
|
|
|
|
|
824
|
|
|
$this->_spool[0]['State'] = 'SEND'; |
|
825
|
|
|
$time = microtime(true); |
|
826
|
|
|
$this->_spool[0]['Times']['send'][0] = $time; |
|
827
|
|
|
$this->_spool[0]['Response'] = ''; |
|
828
|
|
|
$this->_spool[0]['Headers'] = array(); |
|
829
|
|
|
|
|
830
|
|
|
// finish to prepare header and data to send |
|
831
|
|
|
$msg = $this->_spool[0]['HDatas']; |
|
832
|
|
|
if (!$this->_keepalive || ($this->_spool[0]['KeepaliveMinTimeout'] < 0) || |
|
833
|
|
|
($this->_serv_keepalive_timeout < $this->_spool[0]['KeepaliveMinTimeout']) || |
|
834
|
|
|
($this->_serv_keepalive_max <= ($this->_query_num + 2)) || |
|
835
|
|
|
($this->_serv_keepalive_timeout <= (time() - $this->_query_time + 2)) |
|
836
|
|
|
) { |
|
837
|
|
|
$msg .= "Connection: close\r\n"; |
|
838
|
|
|
$this->_spool[0]['Close'] = true; |
|
839
|
|
|
} else { |
|
840
|
|
|
$msg .= "Keep-Alive: timeout=".$this->_keepalive_timeout.', max='.$this->_keepalive_max |
|
841
|
|
|
."\r\nConnection: Keep-Alive\r\n"; |
|
842
|
|
|
} |
|
843
|
|
|
|
|
844
|
|
|
// add cookie header |
|
845
|
|
|
if (count($this->_cookies) > 0) { |
|
846
|
|
|
$cookie_msg = ''; |
|
847
|
|
|
$sep = ''; |
|
848
|
|
|
foreach ($this->_cookies as $name => $cookie) { |
|
849
|
|
|
if (!isset($cookie['path']) |
|
850
|
|
|
|| strncmp($this->_spool[0]['Path'], $cookie['path'], strlen($cookie['path'])) == 0 |
|
851
|
|
|
) { |
|
852
|
|
|
$cookie_msg .= $sep.$name.'='.$cookie['Value']; |
|
853
|
|
|
$sep = '; '; |
|
854
|
|
|
} |
|
855
|
|
|
} |
|
856
|
|
|
if ($cookie_msg != '') { |
|
857
|
|
|
$msg .= "Cookie: $cookie_msg\r\n"; |
|
858
|
|
|
} |
|
859
|
|
|
} |
|
860
|
|
|
|
|
861
|
|
|
$msg .= "\r\n"; |
|
862
|
|
|
$msg .= $this->_spool[0]['QueryDatas']; |
|
863
|
|
|
$this->_spool[0]['Datas'] = $msg; |
|
864
|
|
|
$this->_spool[0]['DatasSize'] = strlen($msg); |
|
865
|
|
|
$this->_spool[0]['DatasSent'] = 0; |
|
866
|
|
|
//print_r($msg); // for debug purposes, don't remove |
|
|
|
|
|
|
867
|
|
|
} |
|
868
|
|
|
|
|
869
|
|
|
// if not SEND then stop |
|
870
|
|
|
if ($this->_spool[0]['State'] != 'SEND') { |
|
871
|
|
|
return; |
|
872
|
|
|
} |
|
873
|
|
|
|
|
874
|
|
|
do { |
|
875
|
|
|
$sent = @stream_socket_sendto( |
|
876
|
|
|
$this->_socket, |
|
877
|
|
|
substr( |
|
878
|
|
|
$this->_spool[0]['Datas'], |
|
879
|
|
|
$this->_spool[0]['DatasSent'], |
|
880
|
|
|
($this->_spool[0]['DatasSize'] - $this->_spool[0]['DatasSent']) |
|
881
|
|
|
) |
|
882
|
|
|
); |
|
883
|
|
|
|
|
884
|
|
|
if ($sent == false) { |
|
885
|
|
|
|
|
886
|
|
|
$time = microtime(true); |
|
887
|
|
|
$this->_spool[0]['Times']['send'][1] = $time - $this->_spool[0]['Times']['send'][0]; |
|
888
|
|
|
//var_dump($this->_spool[0]['Datas']); // for debug purposes, don't remove |
|
|
|
|
|
|
889
|
|
|
$this->_bad( |
|
890
|
|
|
'Error('.$errno.') '.$errstr.', could not send data! (' |
|
891
|
|
|
.$sent.' / '.($this->_spool[0]['DatasSize'] - $this->_spool[0]['DatasSent']).', ' |
|
892
|
|
|
.$this->_spool[0]['DatasSent'].' / '.$this->_spool[0]['DatasSize'].')'); |
|
893
|
|
|
if ($this->_wait) { |
|
894
|
|
|
return; |
|
895
|
|
|
} |
|
896
|
|
|
break; |
|
897
|
|
|
} else { |
|
898
|
|
|
$this->_spool[0]['DatasSent'] += $sent; |
|
899
|
|
|
if ($this->_spool[0]['DatasSent'] >= $this->_spool[0]['DatasSize']) { |
|
900
|
|
|
// All is sent, prepare to receive the reply |
|
901
|
|
|
$this->_query_num++; |
|
902
|
|
|
$this->_query_time = time(); |
|
903
|
|
|
|
|
904
|
|
|
$time = microtime(true); |
|
905
|
|
|
$this->_spool[0]['Times']['send'][1] = $time - $this->_spool[0]['Times']['send'][0]; |
|
906
|
|
|
|
|
907
|
|
|
$this->_spool[0]['State'] = 'RECEIVE'; |
|
908
|
|
|
$this->_spool[0]['Times']['receive'][0] = $time; |
|
909
|
|
View Code Duplication |
} elseif (($difftime = round(microtime(true) - $this->_read_time)) > $waittimeout) { |
|
|
|
|
|
|
910
|
|
|
// if timeout then error |
|
911
|
|
|
$this->_bad( |
|
912
|
|
|
"Request timeout, in _send ({$difftime} > {$waittimeout}s) state=".$this->_spool[0]['State'] |
|
913
|
|
|
); |
|
914
|
|
|
break; |
|
915
|
|
|
} |
|
916
|
|
|
} |
|
917
|
|
|
|
|
918
|
|
|
// if not async-callback then continue until all is sent |
|
919
|
|
|
} while ($this->_wait && isset($this->_spool[0]['State']) && ($this->_spool[0]['State'] == 'SEND')); |
|
920
|
|
|
} // _send |
|
921
|
|
|
|
|
922
|
|
|
public function _receive($waittimeout = 40) |
|
923
|
|
|
{ |
|
924
|
|
|
global $_Webaccess_last_response; |
|
|
|
|
|
|
925
|
|
|
|
|
926
|
|
|
if (!$this->_socket || $this->_state != 'OPENED') { |
|
927
|
|
|
return; |
|
928
|
|
|
} |
|
929
|
|
|
|
|
930
|
|
|
$errno = ''; |
|
931
|
|
|
$errstr = ''; |
|
932
|
|
|
$time0 = microtime(true); |
|
933
|
|
|
$timeout = ($this->_wait) ? $waittimeout : 0; |
|
934
|
|
|
do { |
|
935
|
|
|
$r = array($this->_socket); |
|
936
|
|
|
$w = null; |
|
937
|
|
|
$e = null; |
|
938
|
|
|
$nb = @stream_select($r, $w, $e, $timeout); |
|
939
|
|
|
if ($nb === 0) { |
|
940
|
|
|
$nb = count($r); |
|
941
|
|
|
} |
|
942
|
|
|
|
|
943
|
|
|
while (!@feof($this->_socket) && $nb !== false && $nb > 0) { |
|
944
|
|
|
$timeout = 0; |
|
945
|
|
|
|
|
946
|
|
|
if (count($r) > 0) { |
|
947
|
|
|
$res = @stream_socket_recvfrom($this->_socket, 8192); |
|
948
|
|
|
|
|
949
|
|
|
if ($res == '') { // should not happen habitually, but... |
|
950
|
|
|
break; |
|
951
|
|
|
} elseif ($res !== false) { |
|
952
|
|
|
$this->_response .= $res; |
|
953
|
|
|
} else { |
|
954
|
|
View Code Duplication |
if (isset($this->_spool[0])) { |
|
|
|
|
|
|
955
|
|
|
$time = microtime(true); |
|
956
|
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0]; |
|
957
|
|
|
} |
|
958
|
|
|
$this->_bad('Error('.$errno.') '.$errstr.', could not read all data!'); |
|
959
|
|
|
|
|
960
|
|
|
return; |
|
961
|
|
|
} |
|
962
|
|
|
} |
|
963
|
|
|
|
|
964
|
|
|
// if timeout then error |
|
965
|
|
|
if (($difftime = round(microtime(true) - $this->_read_time)) > $waittimeout) { |
|
966
|
|
|
$this->_bad("Request timeout, in _receive ({$difftime} > {$waittimeout}s)"); |
|
967
|
|
|
break; |
|
968
|
|
|
} |
|
969
|
|
|
|
|
970
|
|
|
$r = array($this->_socket); |
|
971
|
|
|
$w = null; |
|
972
|
|
|
$e = null; |
|
973
|
|
|
$nb = @stream_select($r, $w, $e, $timeout); |
|
974
|
|
|
if ($nb === 0) { |
|
975
|
|
|
$nb = count($r); |
|
976
|
|
|
} |
|
977
|
|
|
} |
|
978
|
|
|
|
|
979
|
|
View Code Duplication |
if (isset($this->_spool[0]['Times']['receive'][2])) { |
|
|
|
|
|
|
980
|
|
|
$time = microtime(true); |
|
981
|
|
|
$this->_spool[0]['Times']['receive'][2] += ($time - $time0); |
|
982
|
|
|
} |
|
983
|
|
|
|
|
984
|
|
|
// get headers and full message |
|
985
|
|
|
$state = $this->_handleHeaders(); |
|
986
|
|
|
} while ($this->_wait && $state === false && $this->_socket && !@feof($this->_socket)); |
|
987
|
|
|
|
|
988
|
|
|
if (!isset($this->_spool[0]['State']) || $this->_spool[0]['State'] != 'RECEIVE') { |
|
989
|
|
|
// in case of (probably keep-alive) connection closed by server |
|
990
|
|
|
if ($this->_socket && @feof($this->_socket)) { |
|
991
|
|
|
$this->_state = 'CLOSED'; |
|
992
|
|
|
@fclose($this->_socket); |
|
|
|
|
|
|
993
|
|
|
$this->_socket = null; |
|
994
|
|
|
} |
|
995
|
|
|
|
|
996
|
|
|
return; |
|
997
|
|
|
} |
|
998
|
|
|
|
|
999
|
|
|
|
|
1000
|
|
|
// terminated but incomplete! more than probably closed by server... |
|
1001
|
|
|
if ($state === false && $this->_socket && @feof($this->_socket)) { |
|
1002
|
|
|
$this->_state = 'CLOSED'; |
|
1003
|
|
|
if (isset($this->_spool[0])) { |
|
1004
|
|
|
$time = microtime(true); |
|
1005
|
|
|
$this->_spool[0]['State'] = 'OPEN'; |
|
1006
|
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0]; |
|
1007
|
|
|
} |
|
1008
|
|
|
// if not 0 sized then show error message |
|
1009
|
|
|
if (strlen($this->_response) > 0) { |
|
1010
|
|
|
$this->_bad( |
|
1011
|
|
|
'Error: closed with incomplete read: re-open socket and re-send! ('.strlen($this->_response).')' |
|
1012
|
|
|
); |
|
1013
|
|
|
} else { |
|
1014
|
|
|
$this->_bad( |
|
1015
|
|
|
'Closed by server when reading: re-open socket and re-send! ('.strlen($this->_response).')', |
|
1016
|
|
|
false |
|
1017
|
|
|
); |
|
1018
|
|
|
} |
|
1019
|
|
|
|
|
1020
|
|
|
$this->_spool[0]['Retries']++; |
|
1021
|
|
|
|
|
1022
|
|
|
if ($this->_spool[0]['Retries'] > 2) { |
|
1023
|
|
|
// 3 tries failed, remove entry from spool |
|
1024
|
|
|
$this->console->writeln($this->_webaccess_str.'$f00 Failed '.$this->_spool[0]['Retries'].' times: skipping current request.'); |
|
1025
|
|
|
array_shift($this->_spool); |
|
1026
|
|
|
} |
|
1027
|
|
|
|
|
1028
|
|
|
return; |
|
1029
|
|
|
} |
|
1030
|
|
|
|
|
1031
|
|
|
|
|
1032
|
|
|
// reply is complete :) |
|
1033
|
|
|
if ($state === true) { |
|
1034
|
|
|
$this->_bad_timeout = 0; // reset error timeout |
|
1035
|
|
|
$time = microtime(true); // @todo see if this is needed ? |
|
1036
|
|
|
$this->_spool[0]['Times']['receive'][1] = $time - $this->_spool[0]['Times']['receive'][0]; |
|
1037
|
|
|
$this->_spool[0]['State'] = 'DONE'; |
|
1038
|
|
|
|
|
1039
|
|
|
// store http/xml response in global $_Webaccess_last_response - for debugging use |
|
1040
|
|
|
$_Webaccess_last_response = $this->_spool[0]['Response']; |
|
1041
|
|
|
// call callback func |
|
1042
|
|
|
$this->_callCallback(); |
|
1043
|
|
|
|
|
1044
|
|
|
|
|
1045
|
|
|
$this->_query_time = time(); |
|
1046
|
|
|
|
|
1047
|
|
|
if (!$this->_keepalive || $this->_spool[0]['Close']) { |
|
1048
|
|
|
$this->console->writeln($this->_webaccess_str.'closed connection (asked in headers).'); |
|
1049
|
|
|
$this->_state = 'CLOSED'; |
|
1050
|
|
|
@fclose($this->_socket); |
|
|
|
|
|
|
1051
|
|
|
$this->_socket = null; |
|
1052
|
|
|
} |
|
1053
|
|
|
|
|
1054
|
|
|
$this->infos(); |
|
1055
|
|
|
|
|
1056
|
|
|
// request completed, remove it from spool! |
|
1057
|
|
|
array_shift($this->_spool); |
|
1058
|
|
|
} |
|
1059
|
|
|
} // _receive |
|
1060
|
|
|
|
|
1061
|
|
|
private function _callCallback($error = null) |
|
1062
|
|
|
{ |
|
1063
|
|
|
if ($error !== null) { |
|
1064
|
|
|
$this->_spool[0]['Response']['Error'] = $error; |
|
1065
|
|
|
} |
|
1066
|
|
|
// call callback func |
|
1067
|
|
|
if (isset($this->_spool[0]['Callback'])) { |
|
1068
|
|
|
$callbackinfo = $this->_spool[0]['Callback']; |
|
1069
|
|
|
if (isset($callbackinfo[0]) && is_callable($callbackinfo[0])) { |
|
1070
|
|
|
$callback_func = $callbackinfo[0]; |
|
1071
|
|
|
$callbackinfo[0] = $this->_spool[0]['Response']; |
|
1072
|
|
|
call_user_func_array($callback_func, $callbackinfo); |
|
1073
|
|
|
} |
|
1074
|
|
|
} |
|
1075
|
|
|
} |
|
1076
|
|
|
|
|
1077
|
|
|
private function _handleHeaders() |
|
1078
|
|
|
{ |
|
1079
|
|
|
global $_wa_header_separator, $_wa_header_multi; |
|
|
|
|
|
|
1080
|
|
|
|
|
1081
|
|
|
if (!isset($this->_spool[0]['State'])) { |
|
1082
|
|
|
return false; |
|
1083
|
|
|
} |
|
1084
|
|
|
|
|
1085
|
|
|
// not enough data, continue read |
|
1086
|
|
|
if (strlen($this->_response) < 8) { |
|
1087
|
|
|
return false; |
|
1088
|
|
|
} |
|
1089
|
|
|
if (strncmp($this->_response, 'HTTP/', 5) != 0) { // not HTTP! |
|
1090
|
|
|
$this->_bad( |
|
1091
|
|
|
"Error, not HTTP response ! **********\n".substr($this->_response, 0, 300)."\n***************\n" |
|
1092
|
|
|
); |
|
1093
|
|
|
|
|
1094
|
|
|
return null; |
|
1095
|
|
|
} |
|
1096
|
|
|
|
|
1097
|
|
|
// separate headers and data |
|
1098
|
|
|
$datas = explode("\r\n\r\n", $this->_response, 2); |
|
1099
|
|
|
if (count($datas) < 2) { |
|
1100
|
|
|
$datas = explode("\n\n", $this->_response, 2); |
|
1101
|
|
|
if (count($datas) < 2) { |
|
1102
|
|
|
$datas = explode("\r\r", $this->_response, 2); |
|
1103
|
|
|
if (count($datas) < 2) { |
|
1104
|
|
|
return false; // not complete headers, continue read |
|
1105
|
|
|
} |
|
1106
|
|
|
} |
|
1107
|
|
|
} |
|
1108
|
|
|
|
|
1109
|
|
|
// get headers if not done on previous read |
|
1110
|
|
|
if (!isset($this->_spool[0]['Headers']['Command'][0])) { |
|
1111
|
|
|
// separate headers |
|
1112
|
|
|
|
|
1113
|
|
|
$headers = array(); |
|
1114
|
|
|
$heads = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $datas[0]))); |
|
1115
|
|
|
if (count($heads) < 2) { |
|
1116
|
|
|
$this->_bad("Error, uncomplete headers ! **********\n".$datas[0]."\n***************\n"); |
|
1117
|
|
|
|
|
1118
|
|
|
return null; |
|
1119
|
|
|
} |
|
1120
|
|
|
|
|
1121
|
|
|
$headers['Command'] = explode(' ', $heads[0], 3); |
|
1122
|
|
|
|
|
1123
|
|
View Code Duplication |
for ($i = 1; $i < count($heads); $i++) { |
|
|
|
|
|
|
1124
|
|
|
$header = explode(':', $heads[$i], 2); |
|
1125
|
|
|
if (count($header) > 1) { |
|
1126
|
|
|
$headername = strtolower(trim($header[0])); |
|
1127
|
|
|
if (isset($_wa_header_separator[$headername])) { |
|
1128
|
|
|
$sep = $_wa_header_separator[$headername]; |
|
1129
|
|
|
} else { |
|
1130
|
|
|
$sep = ','; |
|
1131
|
|
|
} |
|
1132
|
|
|
if (isset($_wa_header_multi[$headername]) && $_wa_header_multi[$headername]) { |
|
1133
|
|
|
if (!isset($headers[$headername])) { |
|
1134
|
|
|
$headers[$headername] = array(); |
|
1135
|
|
|
} |
|
1136
|
|
|
$headers[$headername][] = explode($sep, trim($header[1])); |
|
1137
|
|
|
} else { |
|
1138
|
|
|
$headers[$headername] = explode($sep, trim($header[1])); |
|
1139
|
|
|
} |
|
1140
|
|
|
} |
|
1141
|
|
|
} |
|
1142
|
|
|
|
|
1143
|
|
|
if (isset($headers['content-length'][0])) { |
|
1144
|
|
|
$headers['content-length'][0] += 0; //convert to int |
|
1145
|
|
|
} |
|
1146
|
|
|
|
|
1147
|
|
|
$this->_spool[0]['Headers'] = $headers; |
|
1148
|
|
|
|
|
1149
|
|
|
// add header specific info in case of Dedimania reply |
|
1150
|
|
|
if (isset($headers['server'][0])) { |
|
1151
|
|
|
$this->_webaccess_str = 'Webaccess('.$this->_host.':'.$this->_port.'/' |
|
1152
|
|
|
.$headers['server'][0].'): '; |
|
1153
|
|
|
} |
|
1154
|
|
|
} else { |
|
1155
|
|
|
$headers = &$this->_spool[0]['Headers']; |
|
1156
|
|
|
// echo "Previous Headers! (".strlen($datas[0]).")\n"; // for debugging purposes, don't remove |
|
|
|
|
|
|
1157
|
|
|
} |
|
1158
|
|
|
|
|
1159
|
|
|
|
|
1160
|
|
|
// get real message |
|
1161
|
|
|
$datasize = strlen($datas[1]); |
|
1162
|
|
|
if (isset($headers['content-length'][0]) && $headers['content-length'][0] >= 0) { |
|
1163
|
|
|
//echo 'mess_size0=' . strlen($datas[1]) . "\n"; // for debugging purposes, don't remove |
|
|
|
|
|
|
1164
|
|
|
|
|
1165
|
|
|
// incomplete message |
|
1166
|
|
|
if ($headers['content-length'][0] > $datasize) { |
|
1167
|
|
|
return false; |
|
1168
|
|
|
} elseif ($headers['content-length'][0] < $datasize) { |
|
1169
|
|
|
$message = substr($datas[1], 0, $headers['content-length'][0]); |
|
1170
|
|
|
// remaining buffer for next reply |
|
1171
|
|
|
$this->_response = substr($datas[1], $headers['content-length'][0]); |
|
1172
|
|
|
} else { |
|
1173
|
|
|
$message = $datas[1]; |
|
1174
|
|
|
$this->_response = ''; |
|
1175
|
|
|
} |
|
1176
|
|
|
$this->_spool[0]['ResponseSize'] = strlen($datas[0]) + 4 + $headers['content-length'][0]; |
|
1177
|
|
|
} // get real message when reply is chunked |
|
1178
|
|
|
elseif (isset($headers['transfer-encoding'][0]) |
|
1179
|
|
|
&& $headers['transfer-encoding'][0] == 'chunked') { |
|
1180
|
|
|
|
|
1181
|
|
|
// get chunk size and make message with chunks data |
|
1182
|
|
|
$size = -1; |
|
1183
|
|
|
$chunkPos = 0; |
|
1184
|
|
|
$message = ''; |
|
1185
|
|
|
if (($datapos = strpos($datas[1], "\r\n", $chunkPos)) !== false) { |
|
1186
|
|
|
|
|
1187
|
|
|
$chunk = explode(';', substr($datas[1], $chunkPos, $datapos - $chunkPos)); |
|
1188
|
|
|
$size = hexdec($chunk[0]); |
|
1189
|
|
|
while ($size > 0) { |
|
1190
|
|
|
// incomplete message |
|
1191
|
|
|
if ($datapos + 2 + $size > $datasize) { |
|
1192
|
|
|
return false; |
|
1193
|
|
|
} |
|
1194
|
|
|
$message .= substr($datas[1], $datapos + 2, $size); |
|
1195
|
|
|
$chunkPos = $datapos + 2 + $size + 2; |
|
1196
|
|
|
if (($datapos = strpos($datas[1], "\r\n", $chunkPos)) !== false) { |
|
1197
|
|
|
$chunk = explode(';', substr($datas[1], $chunkPos, $datapos - $chunkPos)); |
|
1198
|
|
|
$size = hexdec($chunk[0]); |
|
1199
|
|
|
} else { |
|
1200
|
|
|
$size = -1; |
|
1201
|
|
|
} |
|
1202
|
|
|
} |
|
1203
|
|
|
} |
|
1204
|
|
|
// error bad size or incomplete message |
|
1205
|
|
|
if ($size < 0) { |
|
1206
|
|
|
return false; |
|
1207
|
|
|
} |
|
1208
|
|
|
|
|
1209
|
|
|
// incomplete message : end is missing |
|
1210
|
|
|
if (strpos($datas[1], "\r\n\r\n", $chunkPos) === false) { |
|
1211
|
|
|
return false; |
|
1212
|
|
|
} |
|
1213
|
|
|
|
|
1214
|
|
|
// store complete message size |
|
1215
|
|
|
$msize = strlen($message); |
|
1216
|
|
|
// add message size after 'chunked' for information |
|
1217
|
|
|
$headers['transfer-encoding'][1] = 'total_size='.$msize; |
|
1218
|
|
|
$this->_spool[0]['ResponseSize'] = strlen($datas[0]) + 4 + $msize; |
|
1219
|
|
|
|
|
1220
|
|
|
// after the message itself... |
|
1221
|
|
|
$message_end = explode("\r\n\r\n", substr($datas[1], $chunkPos), 2); |
|
1222
|
|
|
|
|
1223
|
|
|
// add end headers if any |
|
1224
|
|
|
$heads = explode("\n", str_replace("\r", "\n", str_replace("\r\n", "\n", $message_end[0]))); |
|
1225
|
|
View Code Duplication |
for ($i = 1; $i < count($heads); $i++) { |
|
|
|
|
|
|
1226
|
|
|
$header = explode(':', $heads[$i], 2); |
|
1227
|
|
|
if (count($header) > 1) { |
|
1228
|
|
|
$headername = strtolower(trim($header[0])); |
|
1229
|
|
|
if (isset($_wa_header_separator[$headername])) { |
|
1230
|
|
|
$sep = $_wa_header_separator[$headername]; |
|
1231
|
|
|
} else { |
|
1232
|
|
|
$sep = ','; |
|
1233
|
|
|
} |
|
1234
|
|
|
if (isset($_wa_header_multi[$headername]) && $_wa_header_multi[$headername]) { |
|
1235
|
|
|
if (!isset($headers[$headername])) { |
|
1236
|
|
|
$headers[$headername] = array(); |
|
1237
|
|
|
} |
|
1238
|
|
|
$headers[$headername][] = explode($sep, trim($header[1])); |
|
1239
|
|
|
} else { |
|
1240
|
|
|
$headers[$headername] = explode($sep, trim($header[1])); |
|
1241
|
|
|
} |
|
1242
|
|
|
} |
|
1243
|
|
|
} |
|
1244
|
|
|
$this->_spool[0]['Headers'] = $headers; |
|
1245
|
|
|
|
|
1246
|
|
|
// remaining buffer for next reply |
|
1247
|
|
|
if (isset($message_end[1]) && strlen($message_end[1]) > 0) { |
|
1248
|
|
|
$this->_response = $message_end[1]; |
|
1249
|
|
|
} else { |
|
1250
|
|
|
$this->_response = ''; |
|
1251
|
|
|
} |
|
1252
|
|
|
} else { // no content-length and not chunked ! |
|
1253
|
|
|
$this->_bad( |
|
1254
|
|
|
"Error, bad http, no content-length and not chunked ! **********\n".$datas[0]."\n***************\n" |
|
1255
|
|
|
); |
|
1256
|
|
|
|
|
1257
|
|
|
return null; |
|
1258
|
|
|
} |
|
1259
|
|
|
|
|
1260
|
|
|
echo 'mess_size1='.strlen($message)."\n"; |
|
1261
|
|
|
|
|
1262
|
|
|
// if Content-Encoding: gzip or Content-Encoding: deflate |
|
1263
|
|
|
if (isset($headers['content-encoding'][0])) { |
|
1264
|
|
|
if ($headers['content-encoding'][0] == 'gzip') { |
|
1265
|
|
|
$message = @gzdecode($message); |
|
1266
|
|
|
} elseif ($headers['content-encoding'][0] == 'deflate') { |
|
1267
|
|
|
$message = @gzinflate($message); |
|
1268
|
|
|
} |
|
1269
|
|
|
} |
|
1270
|
|
|
|
|
1271
|
|
|
// if Accept-Encoding: gzip or deflate |
|
1272
|
|
|
if ($this->_compress_request == 'accept' && isset($headers['accept-encoding'][0])) { |
|
1273
|
|
|
foreach ($headers['accept-encoding'] as $comp) { |
|
1274
|
|
|
$comp = trim($comp); |
|
1275
|
|
|
if ($comp == 'gzip' && function_exists('gzencode')) { |
|
1276
|
|
|
$this->_compress_request = 'gzip'; |
|
1277
|
|
|
break; |
|
1278
|
|
|
} elseif ($comp == 'deflate' && function_exists('gzdeflate')) { |
|
1279
|
|
|
$this->_compress_request = 'deflate'; |
|
1280
|
|
|
break; |
|
1281
|
|
|
} |
|
1282
|
|
|
} |
|
1283
|
|
|
if ($this->_compress_request == 'accept') { |
|
1284
|
|
|
$this->_compress_request = false; |
|
1285
|
|
|
} |
|
1286
|
|
|
$this->console->writeln($this->_webaccess_str.'send: '.($this->_compress_request === false ? 'no compression' : $this->_compress_request) |
|
1287
|
|
|
.', receive: '.(isset($headers['content-encoding'][0]) ? $headers['content-encoding'][0] : 'no compression')); |
|
1288
|
|
|
|
|
1289
|
|
|
} |
|
1290
|
|
|
|
|
1291
|
|
|
// get cookies values |
|
1292
|
|
|
if (isset($headers['set-cookie'])) { |
|
1293
|
|
|
foreach ($headers['set-cookie'] as $cookie) { |
|
1294
|
|
|
$cook = explode('=', $cookie[0], 2); |
|
1295
|
|
|
if (count($cook) > 1) { |
|
1296
|
|
|
// set main cookie value |
|
1297
|
|
|
$cookname = trim($cook[0]); |
|
1298
|
|
|
if (!isset($this->_cookies[$cookname])) { |
|
1299
|
|
|
$this->_cookies[$cookname] = array(); |
|
1300
|
|
|
} |
|
1301
|
|
|
$this->_cookies[$cookname]['Value'] = trim($cook[1]); |
|
1302
|
|
|
|
|
1303
|
|
|
// set cookie options |
|
1304
|
|
|
for ($i = 1; $i < count($cookie); $i++) { |
|
|
|
|
|
|
1305
|
|
|
$cook = explode('=', $cookie[$i], 2); |
|
1306
|
|
|
$cookarg = strtolower(trim($cook[0])); |
|
1307
|
|
|
if (isset($cook[1])) { |
|
1308
|
|
|
$this->_cookies[$cookname][$cookarg] = trim($cook[1]); |
|
1309
|
|
|
} |
|
1310
|
|
|
} |
|
1311
|
|
|
} |
|
1312
|
|
|
} |
|
1313
|
|
|
//debugPrint('SET-COOKIES: ', $headers['set-cookie']); |
|
|
|
|
|
|
1314
|
|
|
//debugPrint('STORED COOKIES: ', $this->_cookies); |
|
|
|
|
|
|
1315
|
|
|
} |
|
1316
|
|
|
|
|
1317
|
|
|
// if the server reply ask to close, then close |
|
1318
|
|
|
if (!isset($headers['connection'][0]) || $headers['connection'][0] == 'close') { |
|
1319
|
|
|
$this->console->writeln($this->_webaccess_str.'server ask to close connection'); |
|
1320
|
|
|
$this->_spool[0]['Close'] = true; |
|
1321
|
|
|
} |
|
1322
|
|
|
|
|
1323
|
|
|
// verify server keep-alive value and use them if lower |
|
1324
|
|
|
if (isset($headers['keep-alive'])) { |
|
1325
|
|
|
$kasize = count($headers['keep-alive']); |
|
1326
|
|
|
for ($i = 0; $i < $kasize; $i++) { |
|
1327
|
|
|
$keep = explode('=', $headers['keep-alive'][$i], 2); |
|
1328
|
|
|
if (count($keep) > 1) { |
|
1329
|
|
|
$headers['keep-alive'][trim(strtolower($keep[0]))] = intval(trim($keep[1])); |
|
1330
|
|
|
} |
|
1331
|
|
|
} |
|
1332
|
|
|
if (isset($headers['keep-alive']['timeout'])) { |
|
1333
|
|
|
$this->_serv_keepalive_timeout = $headers['keep-alive']['timeout']; |
|
1334
|
|
|
} |
|
1335
|
|
|
if (isset($headers['keep-alive']['max'])) { |
|
1336
|
|
|
$this->_serv_keepalive_max = $headers['keep-alive']['max']; |
|
1337
|
|
|
} |
|
1338
|
|
|
} |
|
1339
|
|
|
|
|
1340
|
|
|
// store complete reply message for the request |
|
1341
|
|
|
$this->_spool[0]['Response'] = array( |
|
1342
|
|
|
'Code' => intval($headers['Command'][1]), |
|
1343
|
|
|
'Reason' => $headers['Command'][2], |
|
1344
|
|
|
'Headers' => $headers, |
|
1345
|
|
|
'Message' => $message, |
|
1346
|
|
|
); |
|
1347
|
|
|
|
|
1348
|
|
|
return true; |
|
1349
|
|
|
} |
|
1350
|
|
|
|
|
1351
|
|
|
public function infos() |
|
1352
|
|
|
{ |
|
1353
|
|
|
try { |
|
1354
|
|
|
$size = (isset($this->_spool[0]['Response']['Message'])) ? strlen($this->_spool[0]['Response']['Message']) : 0; |
|
1355
|
|
|
$msg = $this->_webaccess_str |
|
1356
|
|
|
.sprintf( |
|
1357
|
|
|
"[%s,%s]: %0.3f / %0.3f / %0.3f (%0.3f) / %d [%d,%d,%d]", |
|
1358
|
|
|
$this->_state, |
|
1359
|
|
|
$this->_spool[0]['State'], |
|
1360
|
|
|
$this->_spool[0]['Times']['open'][1], |
|
1361
|
|
|
$this->_spool[0]['Times']['send'][1], |
|
1362
|
|
|
$this->_spool[0]['Times']['receive'][1], |
|
1363
|
|
|
$this->_spool[0]['Times']['receive'][2], |
|
1364
|
|
|
$this->_query_num, |
|
1365
|
|
|
$this->_spool[0]['DatasSize'], |
|
1366
|
|
|
$size, |
|
1367
|
|
|
$this->_spool[0]['ResponseSize'] |
|
1368
|
|
|
); |
|
1369
|
|
|
$this->console->writeln($msg); |
|
1370
|
|
|
} catch (Exception $e) { |
|
1371
|
|
|
echo $e->getMessage(); |
|
1372
|
|
|
|
|
1373
|
|
|
} |
|
1374
|
|
|
} |
|
1375
|
|
|
} |
|
1376
|
|
|
|
|
1377
|
|
|
|
|
1378
|
|
|
/* Additional functions */ |
|
1379
|
|
|
|
|
1380
|
|
|
/** |
|
1381
|
|
|
* @param $url |
|
1382
|
|
|
* @return array |
|
1383
|
|
|
*/ |
|
1384
|
|
|
function getHostPortPath($url) |
|
1385
|
|
|
{ |
|
1386
|
|
|
$http_pos = strpos($url, 'http://'); |
|
1387
|
|
|
if ($http_pos !== false) { |
|
1388
|
|
|
$script = explode('/', substr($url, $http_pos + 7), 2); |
|
1389
|
|
|
if (isset($script[1])) { |
|
1390
|
|
|
$path = '/'.$script[1]; |
|
1391
|
|
|
} else { |
|
1392
|
|
|
$path = '/'; |
|
1393
|
|
|
} |
|
1394
|
|
|
$serv = explode(':', $script[0], 2); |
|
1395
|
|
|
$host = $serv[0]; |
|
1396
|
|
|
if (isset($serv[1])) { |
|
1397
|
|
|
$port = 0 + $serv[1]; |
|
1398
|
|
|
} else { |
|
1399
|
|
|
$port = 80; |
|
1400
|
|
|
} |
|
1401
|
|
|
if (strlen($host) > 2) { |
|
1402
|
|
|
return array($host, $port, $path); |
|
1403
|
|
|
} |
|
1404
|
|
|
} |
|
1405
|
|
|
|
|
1406
|
|
|
return array(false, false, false); |
|
1407
|
|
|
} |
|
1408
|
|
|
|
|
1409
|
|
|
function urlsafe_base64_encode($input) |
|
1410
|
|
|
{ |
|
1411
|
|
|
return strtr(\base64_encode($input), '+/=', '-_,'); |
|
1412
|
|
|
} |
|
1413
|
|
|
|
|
1414
|
|
|
|
|
1415
|
|
|
// gzdecode() workaround |
|
1416
|
|
|
if (!function_exists('gzdecode') && function_exists('gzinflate')) { |
|
1417
|
|
|
function gzdecode($data) |
|
1418
|
|
|
{ |
|
1419
|
|
|
$len = strlen($data); |
|
1420
|
|
|
if ($len < 18 || strcmp(substr($data, 0, 2), "\x1f\x8b")) { |
|
1421
|
|
|
return null; // Not GZIP format (See RFC 1952) |
|
1422
|
|
|
} |
|
1423
|
|
|
$method = ord(substr($data, 2, 1)); // Compression method |
|
1424
|
|
|
$flags = ord(substr($data, 3, 1)); // Flags |
|
1425
|
|
|
if ($flags & 31 != $flags) { |
|
1426
|
|
|
// Reserved bits are set -- NOT ALLOWED by RFC 1952 |
|
1427
|
|
|
return null; |
|
1428
|
|
|
} |
|
1429
|
|
|
// NOTE: $mtime may be negative (PHP integer limitations) |
|
1430
|
|
|
$mtime = unpack("V", substr($data, 4, 4)); |
|
1431
|
|
|
$mtime = $mtime[1]; |
|
|
|
|
|
|
1432
|
|
|
$xfl = substr($data, 8, 1); |
|
|
|
|
|
|
1433
|
|
|
$os = substr($data, 8, 1); |
|
|
|
|
|
|
1434
|
|
|
$headerlen = 10; |
|
1435
|
|
|
$extralen = 0; |
|
1436
|
|
|
$extra = ""; |
|
|
|
|
|
|
1437
|
|
|
if ($flags & 4) { |
|
1438
|
|
|
// 2-byte length prefixed EXTRA data in header |
|
1439
|
|
|
if (($len - $headerlen - 2) < 8) { |
|
1440
|
|
|
return false; // Invalid format |
|
1441
|
|
|
} |
|
1442
|
|
|
$extralen = unpack("v", substr($data, 8, 2)); |
|
1443
|
|
|
$extralen = $extralen[1]; |
|
1444
|
|
|
if (($len - $headerlen - 2 - $extralen) < 8) { |
|
1445
|
|
|
return false; // Invalid format |
|
1446
|
|
|
} |
|
1447
|
|
|
$extra = substr($data, 10, $extralen); |
|
|
|
|
|
|
1448
|
|
|
$headerlen += 2 + $extralen; |
|
1449
|
|
|
} |
|
1450
|
|
|
|
|
1451
|
|
|
$filenamelen = 0; |
|
1452
|
|
|
$filename = ""; |
|
|
|
|
|
|
1453
|
|
View Code Duplication |
if ($flags & 8) { |
|
|
|
|
|
|
1454
|
|
|
// C-style string file NAME data in header |
|
1455
|
|
|
if (($len - $headerlen - 1) < 8) { |
|
1456
|
|
|
return false; // Invalid format |
|
1457
|
|
|
} |
|
1458
|
|
|
$filenamelen = strpos(substr($data, 8 + $extralen), chr(0)); |
|
1459
|
|
|
if ($filenamelen === false || ($len - $headerlen - $filenamelen - 1) < 8) { |
|
1460
|
|
|
return false; // Invalid format |
|
1461
|
|
|
} |
|
1462
|
|
|
$filename = substr($data, $headerlen, $filenamelen); |
|
|
|
|
|
|
1463
|
|
|
$headerlen += $filenamelen + 1; |
|
1464
|
|
|
} |
|
1465
|
|
|
|
|
1466
|
|
|
$commentlen = 0; |
|
|
|
|
|
|
1467
|
|
|
$comment = ""; |
|
|
|
|
|
|
1468
|
|
View Code Duplication |
if ($flags & 16) { |
|
|
|
|
|
|
1469
|
|
|
// C-style string COMMENT data in header |
|
1470
|
|
|
if (($len - $headerlen - 1) < 8) { |
|
1471
|
|
|
return false; // Invalid format |
|
1472
|
|
|
} |
|
1473
|
|
|
$commentlen = strpos(substr($data, 8 + $extralen + $filenamelen), chr(0)); |
|
1474
|
|
|
if ($commentlen === false || ($len - $headerlen - $commentlen - 1) < 8) { |
|
1475
|
|
|
return false; // Invalid header format |
|
1476
|
|
|
} |
|
1477
|
|
|
$comment = substr($data, $headerlen, $commentlen); |
|
|
|
|
|
|
1478
|
|
|
$headerlen += $commentlen + 1; |
|
1479
|
|
|
} |
|
1480
|
|
|
|
|
1481
|
|
|
$headercrc = ""; |
|
|
|
|
|
|
1482
|
|
|
if ($flags & 1) { |
|
1483
|
|
|
// 2-bytes (lowest order) of CRC32 on header present |
|
1484
|
|
|
if (($len - $headerlen - 2) < 8) { |
|
1485
|
|
|
return false; // Invalid format |
|
1486
|
|
|
} |
|
1487
|
|
|
$calccrc = crc32(substr($data, 0, $headerlen)) & 0xffff; |
|
1488
|
|
|
$headercrc = unpack("v", substr($data, $headerlen, 2)); |
|
1489
|
|
|
$headercrc = $headercrc[1]; |
|
1490
|
|
|
if ($headercrc != $calccrc) { |
|
1491
|
|
|
return false; // Bad header CRC |
|
1492
|
|
|
} |
|
1493
|
|
|
$headerlen += 2; |
|
1494
|
|
|
} |
|
1495
|
|
|
|
|
1496
|
|
|
// GZIP FOOTER - These be negative due to PHP's limitations |
|
1497
|
|
|
$datacrc = unpack("V", substr($data, -8, 4)); |
|
1498
|
|
|
$datacrc = $datacrc[1]; |
|
1499
|
|
|
$isize = unpack("V", substr($data, -4)); |
|
1500
|
|
|
$isize = $isize[1]; |
|
1501
|
|
|
|
|
1502
|
|
|
// Perform the decompression: |
|
1503
|
|
|
$bodylen = $len - $headerlen - 8; |
|
1504
|
|
|
if ($bodylen < 1) { |
|
1505
|
|
|
// This should never happen - IMPLEMENTATION BUG! |
|
1506
|
|
|
return null; |
|
1507
|
|
|
} |
|
1508
|
|
|
$body = substr($data, $headerlen, $bodylen); |
|
1509
|
|
|
$data = ""; |
|
1510
|
|
|
if ($bodylen > 0) { |
|
1511
|
|
|
switch ($method) { |
|
1512
|
|
|
case 8: |
|
1513
|
|
|
// Currently the only supported compression method: |
|
1514
|
|
|
$data = gzinflate($body); |
|
1515
|
|
|
break; |
|
1516
|
|
|
default: |
|
1517
|
|
|
// Unknown compression method |
|
1518
|
|
|
return false; |
|
1519
|
|
|
} |
|
1520
|
|
|
} |
|
1521
|
|
|
// Verify decompressed size and CRC32: |
|
1522
|
|
|
// NOTE: This may fail with large data sizes depending on how |
|
1523
|
|
|
// PHP's integer limitations affect strlen() since $isize |
|
1524
|
|
|
// may be negative for large sizes. |
|
1525
|
|
|
if ($isize != strlen($data) || crc32($data) != $datacrc) { |
|
1526
|
|
|
// Bad format! Length or CRC doesn't match! |
|
1527
|
|
|
return false; |
|
1528
|
|
|
} |
|
1529
|
|
|
|
|
1530
|
|
|
return $data; |
|
1531
|
|
|
} |
|
1532
|
|
|
} |
|
1533
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.