FRED™  3.0
FRED™: Framework for Rapid and Easy Development
Polling.php
Go to the documentation of this file.
1 <?php
2 
4 
5 class Polling extends \Rsi\Fred\Security\Check{
6 
7  public $filters = [];
8  public $ext = '.pol'; //!< Extension for times file.
9  public $checkTtl = 3600; //!< Re-use polling info for this amount of time (seconds).
10  public $timeDelta = 5; //!< Consider everything between this amount of seconds as a single request.
11  public $countThreshold = 100; //!< Minimum number of requests.
12  public $deviationPerc = 5; //!< Consider a deviation within this percentage of the average a poller.
13  public $logPrio = \Rsi\Fred\Log::WARNING; //!< Log prio for the warning message.
14 
15  protected $_path = null;
16  protected $_pollerFilename = null; //!< File with IP addresses of pollers (extension must differ from times files or other
17  // directory; defaults to poller.csv in times file path).
18 
19  protected function filename($addr = null){
20  return $this->path . str_replace(['.',':'],'-',$addr ?: $this->component('security')->remoteAddr) . $this->ext;
21  }
22 
23  protected function addTime($filter){
24  $exists = is_file($filename = $this->filename());
25  $f = fopen($filename,$exists ? 'a' : 'w');
26  if(!$exists) fwrite($f,$this->component('security')->remoteAddr . "\n");
27  fwrite($f,time() . "\n");
28  fclose($f);
29  if(!$exists) chmod($filename,0666);
30  }
31 
32  protected function isPoller(){
33  return is_file($this->pollerFilename) && in_array($this->component('security')->remoteAddr,file($this->pollerFilename,FILE_IGNORE_NEW_LINES));
34  }
35 
36  public function check($expected = false){
37  if($expected) return true;
38  $result = null;
39  try{
40  if(
41  ($url = $_SERVER['REQUEST_URI'] ?? null) &&
42  ($this->session->lastTime < $this->_fred->startTime - $this->timeDelta)
43  ) foreach($this->filters as $name => $filter) if(preg_match($filter,$url)){
44  $this->addTime($name);
45  $this->session->lastTime = time();
46  break;
47  }
48  if((($result = $this->session->lastResult) === null) || ($this->session->lastCheck < $this->_fred->startTime - $this->checkTtl)){
49  $this->session->lastResult = $result = !$this->isPoller();
50  $this->session->lastCheck = $this->_fred->startTime;
51  }
52  }
53  catch(\Exception $e){
54  if($this->_fred->debug) throw $e;
55  }
56  return $result;
57  }
58 
59  protected function analyseTimes($addr,$times){
60  $result = false;
61  if(count($times) > $this->countThreshold){
62  $total = $count = $prev = 0;
63  $polls = [];
64  foreach($times as $time){
65  if($prev && ($time > $prev + $this->timeDelta)){
66  if(!$polls) $polls[] = $prev;
67  $polls[] = $time;
68  $total += $time - $prev;
69  $count++;
70  }
71  $prev = $time;
72  }
73  if($count > $this->countThreshold){
74  $average = $total / $count;
75  $total = $prev = 0;
76  foreach($polls as $time){
77  if($prev) $total += pow($time - $prev - $average,2);
78  $prev = $time;
79  }
80  if($result = ($deviation = 100 * sqrt($total / $count) / $average) < $this->deviationPerc)
81  $this->component('log')->add($this->logPrio,"IP address $addr is polling: count = $count, average = " . number_format($average) . ", deviation% = " . number_format($deviation,2),__FILE__,__LINE__);
82  }
83  }
84  return $result;
85  }
86  /**
87  * Analyse the times files, and compile a list of pollers.
88  * Run this function periodically.
89  */
90  public function analyse(){
91  $pollers = [];
92  foreach((new \GlobIterator($this->path . '*' . $this->ext)) as $filename => $file){
93  $lines = file($filename,FILE_IGNORE_NEW_LINES);
94  \Rsi\File::unlink($filename);
95  $addr = array_shift($lines);
96  if($this->analyseTimes($addr,$lines)) $pollers[] = $addr;
97  }
98  file_put_contents($this->pollerFilename,implode("\n",$pollers));
99  }
100 
101  public function unBan($addr){
102  return \Rsi\File::unlink($this->filename($addr));
103  }
104 
105  protected function getPath(){
106  if(!$this->_path) $this->_path = $this->component('security')->path;
107  return $this->_path;
108  }
109 
110  protected function getPollerFilename(){
111  return $this->_pollerFilename ?: $this->path . 'pollers.csv';
112  }
113 
114 }
analyseTimes($addr, $times)
Definition: Polling.php:59
$deviationPerc
Consider a deviation within this percentage of the average a poller.
Definition: Polling.php:12
$logPrio
Log prio for the warning message.
Definition: Polling.php:13
$checkTtl
Re-use polling info for this amount of time (seconds).
Definition: Polling.php:9
$timeDelta
Consider everything between this amount of seconds as a single request.
Definition: Polling.php:10
$countThreshold
Minimum number of requests.
Definition: Polling.php:11
$_pollerFilename
File with IP addresses of pollers (extension must differ from times files or other.
Definition: Polling.php:16
check($expected=false)
Definition: Polling.php:36
$ext
Extension for times file.
Definition: Polling.php:8
analyse()
Analyse the times files, and compile a list of pollers.
Definition: Polling.php:90
component($name)
Get a component (local or default).
Definition: Component.php:80