]> git.mxchange.org Git - core.git/blob - framework/main/classes/client/http/class_HttpClient.php
Continued:
[core.git] / framework / main / classes / client / http / class_HttpClient.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Client\Http;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Client\BaseClient;
8 use Org\Mxchange\CoreFramework\Client\Client;
9 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
10
11 // Import SPL stuff
12 use \InvalidArgumentException;
13 use \UnexpectedValueException;
14
15 /**
16  * A HTTP client class
17  *
18  * @author              Roland Haeder <webmaster@ship-simu.org>
19  * @version             0.0.0
20  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
21  * @license             GNU GPL 3.0 or any newer version
22  * @link                http://www.ship-simu.org
23  *
24  * This program is free software: you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation, either version 3 of the License, or
27  * (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program. If not, see <http://www.gnu.org/licenses/>.
36  */
37 class HttpClient extends BaseClient implements Client {
38         // Constants
39         const HTTP_EOL = "\r\n";
40         const HTTP_USER_AGENT = 'HttpClient-Core/1.0';
41
42         /**
43          * Protected constructor
44          *
45          * @return      void
46          */
47         private function __construct () {
48                 // Set default user agent string (to allow other classes to override this)
49                 $this->setUserAgent(self::HTTP_USER_AGENT);
50
51                 // Call parent constructor
52                 parent::__construct(__CLASS__);
53         }
54
55         /**
56          * Creates an instance of this Client class and prepares it for usage
57          *
58          * @param       $socketResource         Resource of a socket (optional)
59          * @return      $clientInstance         An instance of a Client class
60          */
61         public final static function createHttpClient ($socketResouce = FALSE) {
62                 // Get a new instance
63                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: socketResource[%s]=%s - CALLED!', gettype($socketResource), $socketResource));
64                 $clientInstance = new HttpClient();
65
66                 // Set socket resource
67                 $clientInstance->setSocketResource($socketResource);
68
69                 // Return the prepared instance
70                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: clientInstance=%s - EXIT!', $clientInstance->__toString()));
71                 return $clientInstance;
72         }
73
74         /**
75          * Checks wether proxy configuration is used
76          *
77          * @return      $isUsed         Wether proxy is used
78          */
79         protected function isProxyUsed () {
80                 // Do we have cache?
81                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HTTP-CLIENT: CALLED!');
82                 if (!isset($GLOBALS[__METHOD__])) {
83                         // Determine it
84                         $GLOBALS[__METHOD__] = ((FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('proxy_host') != '') && (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('proxy_port') > 0));
85                 }
86
87                 // Return cache
88                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: isProxyUsed=%d - EXIT!', $GLOBALS[__METHOD__]));
89                 return $GLOBALS[__METHOD__];
90         }
91
92         /**
93          * Sets up a proxy tunnel for given hostname and through resource
94          *
95          * @param       $host           Host to connect to
96          * @param       $port           Port number to connect to
97          * @return      $response       Response array
98          * @throws      InvalidArgumentException        If a paramter has an invalid value
99          * @throws      UnexpectedValueException        If an unexpected value was found
100          */
101         protected function setupProxyTunnel (string $host, int $port) {
102                 // Check paramters
103                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: host=%s,port=%d - CALLED!', $host, $port));
104                 if (empty($host)) {
105                         // Throw IAE
106                         throw new InvalidArgumentException('Parameter "host" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
107                 } elseif ($port < 1) {
108                         // Throw IAE
109                         throw new InvalidArgumentException(sprintf('port=%d is not a valid port number', $port), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
110                 }
111
112                 // Initialize array
113                 $response = ['', '', ''];
114
115                 // Do the connect
116                 $responseArray = $this->doConnectRequest($host, $port);
117
118                 // Check array
119                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: responseArray()=%d', count($responseArray)));
120                 if (count($responseArray) < 2) {
121                         // Not expected count
122                         throw new UnexpectedValueException(sprintf('responseArray()=%d must have at least two elements', count($responseArray)), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
123                 }
124
125                 // Analyze first header line
126                 if (((strtolower($responseArray[0]) !== 'http/1.0') && (strtolower($responseArray[0]) !== 'http/1.1')) || ($responseArray[1] != '200')) {
127                         // Response code is not 200
128                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HTTP-CLIENT: Returning empty response array - EXIT!');
129                         return $response;
130                 }
131
132                 // All fine!
133                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: responseArray()=%d - EXIT!', count($responseArray)));
134                 return $responseArray;
135         }
136
137         /**
138          * Sends a raw HTTP request out to given IP/host and port number
139          *
140          * @param       $method                 Request method (GET, POST, HEAD, CONNECT, ...)
141          * @param       $host                   Host to connect to
142          * @param       $port                   Port number to connect to
143          * @return      $responseArray  Array with raw response
144          */
145         private function sendRawHttpRequest (string $method, string $host, int $port, array $header = []) {
146                 // Minimum raw HTTP/1.1 request
147                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: method=%s,host=%s,port=%d,header()=%d - CALLED!', $method, $host, $port, count($header)));
148                 $rawRequest  = $method . ' ' . $host . ':' . $port . ' HTTP/1.1' . self::HTTP_EOL;
149                 $rawRequest .= 'Host: ' . $host . ':' . $port . self::HTTP_EOL;
150
151                 // Use login data to proxy? (username at least)
152                 if (FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('proxy_username') != '') {
153                         // Add it as well
154                         $encodedAuth = base64_encode(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('proxy_username') . ':' . FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('proxy_password'));
155                         $rawRequest .= 'Proxy-Authorization: Basic ' . $encodedAuth . self::HTTP_EOL;
156                 }
157
158                 // Add last new-line
159                 $rawRequest .= self::HTTP_EOL;
160                 //* DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('HTTP-CLIENT: rawRequest=' . $rawRequest);
161
162                 // Write request
163                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: Sending %d bytes to this->socketResource=%s ...', strlen($rawRequest), $this->getSocketResource()));
164                 fwrite($this->getSocketResource(), $rawRequest);
165
166                 // Got response?
167                 $feof = feof($this->getSocketResource());
168                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: this->socketResource=%s,feof=%d', $this->getSocketResource(), intval($feof)));
169                 if ($feof) {
170                         // No response received
171                         /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: this->socketResource=%s has reached EOF - EXIT!', $this->getSocketResource()));
172                         return $response;
173                 }
174
175                 // Read the first line
176                 $rawResponse = trim(fgets($this->getSocketResource(), 10240));
177
178                 // "Explode" the string to an array
179                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: Received %d bytes back from this->socketResource=%s ...', strlen($rawResponse), $this->getSocketResource()));
180                 $responseArray = explode(' ', $rawResponse);
181
182                 // And return it
183                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: responseArray()=%d - EXIT!', count($responseArray)));
184                 return $responseArray;
185         }
186
187         /**
188          * A HTTP/1.1 CONNECT request
189          *
190          * @param       $host   Host to connect to
191          * @param       $port   Port number to connect to
192          * @return      $responseArray  An array with the read response
193          * @throws      InvalidArgumentException        If a paramter has an invalid value
194          */
195         public function doConnectRequest (string $host, int $port) {
196                 // Check paramters
197                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: host=%s,port=%d - CALLED!', $host, $port));
198                 if (empty($host)) {
199                         // Throw IAE
200                         throw new InvalidArgumentException('Parameter "host" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
201                 } elseif ($port < 1) {
202                         // Throw IAE
203                         throw new InvalidArgumentException(sprintf('port=%d is not a valid port number', $port), FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
204                 }
205
206                 // Prepare extra header(s)
207                 $headers = [
208                         'Proxy-Connection' => 'Keep-Alive'
209                 ];
210
211                 // Prepare raw request
212                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: Invoking this->sendRawHttpRequest(CONNECT,%s,%d,headers()=%d) ...', $host, $port, count($headers)));
213                 $responseArray = $this->sendRawHttpRequest('CONNECT', $host, $port, $headers);
214
215                 // Return response array
216                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('HTTP-CLIENT: responseArray()=%d - EXIT!', count($responseArray)));
217                 return $responseArray;
218         }
219
220 }