Updated 'core'.
[hub.git] / application / hub / main / helper / connection / class_BaseConnectionHelper.php
1 <?php
2 /**
3  * A general ConnectionHelper class
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Hub Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseConnectionHelper extends BaseHubSystemHelper implements Registerable {
25         // Exception codes
26         const EXCEPTION_UNSUPPORTED_ERROR_HANDLER = 0x9100;
27
28         /**
29          * Connection type 'incoming'
30          */
31         const CONNECTION_TYPE_INCOMING = 'incoming';
32
33         /**
34          * Connection type 'outgoing'
35          */
36         const CONNECTION_TYPE_OUTGOING = 'outgoing';
37
38         /**
39          * Connection type 'server'
40          */
41         const CONNECTION_TYPE_SERVER   = 'server';
42
43         /**
44          * Name of used protocol for this connection
45          */
46         private $connectionType = 'invalid';
47
48         /**
49          * (IP) Adress used
50          */
51         private $address = 0;
52
53         /**
54          * Sent data in bytes
55          */
56         private $sentData = 0;
57
58         /**
59          * Whether this connection is initialized
60          */
61         private $isInitialized = FALSE;
62
63         /**
64          * Whether this connection is shutted down
65          */
66         private $shuttedDown = FALSE;
67
68         /**
69          * Currently queued chunks
70          */
71         private $queuedChunks = array();
72
73         /**
74          * Current final hash
75          */
76         private $currentFinalHash = '';
77
78         /**
79          * Protected constructor
80          *
81          * @param       $className      Name of the class
82          * @return      void
83          */
84         protected function __construct ($className) {
85                 // Call parent constructor
86                 parent::__construct($className);
87
88                 // Init state which sets the state to 'init'
89                 $this->initState();
90
91                 // Initialize output stream
92                 $streamInstance = ObjectFactory::createObjectByConfiguredName('node_raw_data_output_stream_class');
93
94                 // And add it to this connection helper
95                 $this->setOutputStreamInstance($streamInstance);
96
97                 // Get package instance from factory
98                 $packageInstance = NetworkPackageFactory::createNetworkPackageInstance();
99
100                 // ... and set it here
101                 $this->setPackageInstance($packageInstance);
102
103                 // Register this connection helper
104                 Registry::getRegistry()->addInstance('connection', $this);
105
106                 // Get the fragmenter instance
107                 $fragmenterInstance = FragmenterFactory::createFragmenterInstance('package');
108
109                 // Set it here
110                 $this->setFragmenterInstance($fragmenterInstance);
111         }
112
113         /**
114          * Getter for real class name, overwrites generic method and is final
115          *
116          * @return      $class  Name of this class
117          */
118         public final function __toString () {
119                 // Class name representation
120                 $class = self::getConnectionClassName($this->getAddress(), $this->getConnectionPort(), parent::__toString());
121
122                 // Return it
123                 return $class;
124         }
125
126         /**
127          * Getter for connection type
128          *
129          * @return      $connectionType         Name of used protocol in this connection
130          */
131         public final function getConnectionType () {
132                 return $this->connectionType;
133         }
134
135         /**
136          * Setter for connection type
137          *
138          * @param       $connectionType         Name of used protocol in this connection
139          * @return      void
140          */
141         protected final function setConnectionType ($connectionType) {
142                 $this->connectionType = $connectionType;
143         }
144
145         /**
146          * Getter for IP address
147          *
148          * @return      $address        The IP address
149          */
150         public final function getAddress () {
151                 return $this->address;
152         }
153
154         /**
155          * Setter for IP address
156          *
157          * @param       $address        The IP address
158          * @return      void
159          */
160         protected final function setAddress ($address) {
161                 $this->address = $address;
162         }
163
164         /**
165          * Setter for isInitialized
166          *
167          * @param       $isInitialized          Name of used protocol in this connection
168          * @return      void
169          */
170         protected final function setIsInitialized ($isInitialized) {
171                 $this->isInitialized = $isInitialized;
172         }
173
174         /**
175          * Getter for isInitialized (NOTE: no 'get' prefix for boolean attributes!)
176          *
177          * @return      $isInitialized          Name of used protocol in this connection
178          */
179         protected final function isInitialized () {
180                 return $this->isInitialized;
181         }
182
183         /**
184          * Static "getter" for this connection class' name
185          *
186          * @param       $address        IP address
187          * @param       $port           Port number
188          * @param       $className      Original class name
189          * @return      $class          Expanded class name
190          */
191         public static function getConnectionClassName ($address, $port, $className) {
192                 // Construct it
193                 $class = $address . ':' . $port . ':' . $className;
194
195                 // ... and return it
196                 return $class;
197         }
198
199         /**
200          * Initializes the peer's state which sets it to 'init'
201          *
202          * @return      void
203          */
204         private function initState() {
205                 // Get the state factory and create the initial state.
206                 PeerStateFactory::createPeerStateInstanceByName('init', $this);
207         }
208
209         /**
210          * "Getter" for raw data from a package array. A fragmenter is used which
211          * will returns us only so many raw data which fits into the back buffer.
212          * The rest is being held in a back-buffer and waits there for the next
213          * cycle and while be then sent.
214          *
215          * This method does 2 simple steps:
216          * 1) Request a chunk from set fragmenter instance
217          * 2) Finally return the chunk (array) to the caller
218          *
219          * @param       $packageData    Raw package data array
220          * @return      $chunkData              Raw data chunk
221          */
222         private function getRawDataFromPackageArray (array $packageData) {
223                 // Debug message
224                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: currentFinalHash=' . $this->currentFinalHash);
225
226                 // Make sure the final hash is set
227                 assert((is_string($this->currentFinalHash)) && (!empty($this->currentFinalHash)));
228
229                 // Get the next raw data chunk from the fragmenter
230                 $rawDataChunk = $this->getFragmenterInstance()->getNextRawDataChunk($this->currentFinalHash);
231
232                 // Debug message
233                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: rawDataChunk=' . print_r($rawDataChunk, TRUE));
234
235                 // Get chunk hashes and chunk data
236                 $chunkHashes = array_keys($rawDataChunk);
237                 $chunkData   = array_values($rawDataChunk);
238
239                 // Is the required data there?
240                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: chunkHashes[]=' . count($chunkHashes) . ',chunkData[]=' . count($chunkData));
241                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('chunkData='.print_r($chunkData, TRUE));
242                 if ((isset($chunkHashes[0])) && (isset($chunkData[0]))) {
243                         // Remember this chunk as queued
244                         $this->queuedChunks[$chunkHashes[0]] = $chunkData[0];
245
246                         // Return the raw data
247                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Returning ' . strlen($chunkData[0]) . ' bytes from ' . __METHOD__ . ' ...');
248                         return $chunkData[0];
249                 } else {
250                         // Return zero string
251                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Returning zero bytes from ' . __METHOD__ . '!');
252                         return '';
253                 }
254         }
255
256         /**
257          * "Accept" a visitor by simply calling it back
258          *
259          * @param       $visitorInstance        A Visitor instance
260          * @return      void
261          */
262         protected final function accept (Visitor $visitorInstance) {
263                 // Just call the visitor
264                 $visitorInstance->visitConnectionHelper($this);
265         }
266
267         /**
268          * Sends raw package data to the recipient
269          *
270          * @param       $packageData            Raw package data
271          * @return      void
272          * @throws      InvalidSocketException  If we got a problem with this socket
273          */
274         public function sendRawPackageData (array $packageData) {
275                 // The helper's state must be 'connected'
276                 $this->getStateInstance()->validatePeerStateConnected();
277
278                 // Implode the package data array and fragement the resulting string, returns the final hash
279                 $finalHash = $this->getFragmenterInstance()->fragmentPackageArray($packageData, $this);
280
281                 // Is the final hash set?
282                 if ($finalHash !== TRUE) {
283                         // Debug message
284                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Setting finalHash=' . $finalHash . ',currentFinalHash[' . gettype($this->currentFinalHash) . ']=' . $this->currentFinalHash);
285
286                         // Set final hash
287                         $this->currentFinalHash = $finalHash;
288                 } // END - if
289
290                 // Reset serial number
291                 $this->getFragmenterInstance()->resetSerialNumber($this->currentFinalHash);
292
293                 // Init variables
294                 $rawData        = '';
295                 $dataStream     = ' ';
296
297                 // Fill sending buffer with data
298                 while (strlen($dataStream) > 0) {
299                         // Debug message
300                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: packageData=' . print_r($packageData, TRUE));
301
302                         // Convert the package data array to a raw data stream
303                         $dataStream = $this->getRawDataFromPackageArray($packageData);
304                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: Adding ' . strlen($dataStream) . ' bytes to the sending buffer ...');
305                         $rawData .= $dataStream;
306                 } // END - while
307
308                 // Nothing to sent is bad news, so assert on it
309                 assert(strlen($rawData) > 0);
310
311                 // Calculate buffer size
312                 $bufferSize = $this->getConfigInstance()->getConfigEntry($this->getProtocolName() . '_buffer_length');
313
314                 // Encode the raw data with our output-stream
315                 $encodedData = $this->getOutputStreamInstance()->streamData($rawData);
316
317                 // Debug message
318                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('HELPER[' . __METHOD__ . ':' . __LINE__ . ']: socketResource[]=' . gettype($this->getSocketResource()) . PHP_EOL);
319
320                 // Init array
321                 $encodedDataArray = array(
322                         NetworkPackage::RAW_FINAL_HASH_INDEX   => $this->currentFinalHash,
323                         NetworkPackage::RAW_ENCODED_DATA_INDEX => $encodedData,
324                         NetworkPackage::RAW_SENT_BYTES_INDEX   => 0,
325                         NetworkPackage::RAW_SOCKET_INDEX       => $this->getSocketResource(),
326                         NetworkPackage::RAW_BUFFER_SIZE_INDEX  => $bufferSize,
327                         NetworkPackage::RAW_DIFF_INDEX         => 0
328                 );
329
330                 // Calculate difference
331                 $diff = $encodedDataArray[NetworkPackage::RAW_BUFFER_SIZE_INDEX] - strlen($encodedDataArray[NetworkPackage::RAW_ENCODED_DATA_INDEX]);
332
333                 // Push raw data to the package's outgoing stack
334                 $this->getPackageInstance()->getStackInstance()->pushNamed(NetworkPackage::STACKER_NAME_OUTGOING_STREAM, $encodedDataArray);
335         }
336
337         /**
338          * Getter for shuttedDown
339          *
340          * @return      $shuttedDown    Whether this connection is shutted down
341          */
342         public final function isShuttedDown () {
343                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('CONNECTION-HELPER[' . __METHOD__ . ':' . __LINE__ . ']: ' . $this->__toString() . ',shuttedDown=' . intval($this->shuttedDown));
344                 return $this->shuttedDown;
345         }
346 }
347
348 // [EOF]
349 ?>