1 |
<?php |
2 |
/* |
3 |
|
4 |
Net_HTTP_Client class |
5 |
|
6 |
@DESCRIPTION |
7 |
|
8 |
HTTP Client component |
9 |
suppots methods HEAD, GET, POST |
10 |
1.0 and 1.1 compliant |
11 |
WebDAV methods tested against Apache/mod_dav |
12 |
Documentation @ http://lwest.free.fr/doc/php/lib/net_http_client-en.html |
13 |
|
14 |
@SYNOPSIS |
15 |
|
16 |
include "Net/HTTP/Client.php"; |
17 |
|
18 |
$http = new Net_HTTP_Client(); |
19 |
$http->connect( "localhost", 80 ) or die( "connect problem" ); |
20 |
$status = $http->get( "/index.html" ); |
21 |
if( $status != 200 ) |
22 |
die( "Problem : " . $http->getStatusMessage() . "\n" ); |
23 |
$http->disconnect(); |
24 |
|
25 |
|
26 |
|
27 |
@CHANGES |
28 |
0.1 initial version |
29 |
0.2 documentation completed |
30 |
+ getHeaders(), getBody() |
31 |
o Post(), Connect() |
32 |
0.3 DAV enhancements: |
33 |
+ Put() method |
34 |
0.4 continued DAV support |
35 |
+ Delete(), Move(), MkCol(), Propfind() methods |
36 |
o added url property, remove host and port properties |
37 |
o Connect, Net_HTTP_Client (use of this.url) |
38 |
o processBody() : use non-blocking to fix a socket pblm |
39 |
0.5 debug support |
40 |
+ setDebug() |
41 |
+ debug levels definitions (DBG*) |
42 |
0.6 + Lock() method |
43 |
+ setCredentials() method and fix - thanks Thomas Olsen |
44 |
+ support for Get( full_url ) |
45 |
o fix POST call (duplicate content-length) - thanks to Javier Sixto |
46 |
0.7 + OPTIONS method support |
47 |
+ addCookie and removeCookies methods |
48 |
o fix the "0" problem |
49 |
o undeifned variable warning fixed |
50 |
|
51 |
|
52 |
@VERSION |
53 |
0.7 |
54 |
|
55 |
@INFORMATIONS |
56 |
|
57 |
Compatibility : PHP 4 >= 4.0b4 |
58 |
created : May 2001 |
59 |
LastModified : Sep 2002 |
60 |
|
61 |
|
62 |
@AUTHOR |
63 |
Leo West <west_leo@yahoo-REMOVE-.com> |
64 |
|
65 |
@TODO |
66 |
remaining WebDAV methods: UNLOCK PROPPATCH |
67 |
|
68 |
|
69 |
*/ |
70 |
|
71 |
|
72 |
/// debug levels , use it as Client::setDebug( DBGSOCK & DBGTRACE ) |
73 |
define( "DBGTRACE", 1 ); // to debug methods calls |
74 |
define( "DBGINDATA", 2 ); // to debug data received |
75 |
define( "DBGOUTDATA", 4 ); // to debug data sent |
76 |
define( "DBGLOW", 8 ); // to debug low-level (usually internal) methods |
77 |
define( "DBGSOCK", 16 ); // to debug socket-level code |
78 |
|
79 |
/// internal errors |
80 |
define( "ECONNECTION", -1 ); // connection failed |
81 |
define( "EBADRESPONSE", -2 ); // response status line is not http compliant |
82 |
|
83 |
define( "CRLF", "\r\n" ); |
84 |
|
85 |
|
86 |
class Net_HTTP_Client |
87 |
{ |
88 |
|
89 |
// @private |
90 |
/// array containg server URL, similar to array returned by parseurl() |
91 |
var $url; |
92 |
/// server response code eg. "304" |
93 |
var $reply; |
94 |
/// server response line eg. "200 OK" |
95 |
var $replyString; |
96 |
/// HTPP protocol version used |
97 |
var $protocolVersion = "1.0"; |
98 |
/// internal buffers |
99 |
var $requestHeaders, $requestBody; |
100 |
/// TCP socket identifier |
101 |
var $socket = false; |
102 |
/// proxy informations |
103 |
var $useProxy = false; |
104 |
var $proxyHost, $proxyPort; |
105 |
/// debugging flag |
106 |
var $debug = 0; |
107 |
|
108 |
/** |
109 |
* Net_HTTP_Client |
110 |
* constructor |
111 |
* Note : when host and port are defined, the connection is immediate |
112 |
* @seeAlso connect |
113 |
**/ |
114 |
function Net_HTTP_Client( $host= NULL, $port= NULL ) |
115 |
{ |
116 |
if( $this->debug & DBGTRACE ) echo "Net_HTTP_Client( $host, $port )\n"; |
117 |
|
118 |
if( $host != NULL ) { |
119 |
$this->connect( $host, $port ); |
120 |
} |
121 |
} |
122 |
|
123 |
/** |
124 |
* turn on debug messages |
125 |
* @param level a combinaison of debug flags |
126 |
* @see debug flags ( DBG..) defined at top of file |
127 |
**/ |
128 |
function setDebug( $level ) |
129 |
{ |
130 |
if( $this->debug & DBGTRACE ) echo "setDebug( $level )\n"; |
131 |
$this->debug = $level; |
132 |
} |
133 |
|
134 |
|
135 |
/** |
136 |
* turn on proxy support |
137 |
* @param proxyHost proxy host address eg "proxy.mycorp.com" |
138 |
* @param proxyPort proxy port usually 80 or 8080 |
139 |
**/ |
140 |
function setProxy( $proxyHost, $proxyPort ) |
141 |
{ |
142 |
if( $this->debug & DBGTRACE ) echo "setProxy( $proxyHost, $proxyPort )\n"; |
143 |
$this->useProxy = true; |
144 |
$this->proxyHost = $proxyHost; |
145 |
$this->proxyPort = $proxyPort; |
146 |
} |
147 |
|
148 |
|
149 |
/** |
150 |
* setProtocolVersion |
151 |
* define the HTTP protocol version to use |
152 |
* @param version string the version number with one decimal: "0.9", "1.0", "1.1" |
153 |
* when using 1.1, you MUST set the mandatory headers "Host" |
154 |
* @return boolean false if the version number is bad, true if ok |
155 |
**/ |
156 |
function setProtocolVersion( $version ) |
157 |
{ |
158 |
if( $this->debug & DBGTRACE ) echo "setProtocolVersion( $version )\n"; |
159 |
|
160 |
if( $version > 0 and $version <= 1.1 ) { |
161 |
$this->protocolVersion = $version; |
162 |
return true; |
163 |
} else { |
164 |
return false; |
165 |
} |
166 |
} |
167 |
|
168 |
/** |
169 |
* set a username and password to access a protected resource |
170 |
* Only "Basic" authentication scheme is supported yet |
171 |
* @param username string - identifier |
172 |
* @param password string - clear password |
173 |
**/ |
174 |
function setCredentials( $username, $password ) |
175 |
{ |
176 |
$hdrvalue = base64_encode( "$username:$password" ); |
177 |
$this->addHeader( "Authorization", "Basic $hdrvalue" ); |
178 |
} |
179 |
|
180 |
/** |
181 |
* define a set of HTTP headers to be sent to the server |
182 |
* header names are lowercased to avoid duplicated headers |
183 |
* @param headers hash array containing the headers as headerName => headerValue pairs |
184 |
**/ |
185 |
function setHeaders( $headers ) |
186 |
{ |
187 |
if( $this->debug & DBGTRACE ) echo "setHeaders( $headers ) \n"; |
188 |
if( is_array( $headers )) { |
189 |
foreach( $headers as $name => $value ) { |
190 |
$this->requestHeaders[$name] = $value; |
191 |
} |
192 |
} |
193 |
} |
194 |
|
195 |
/** |
196 |
* addHeader |
197 |
* set a unique request header |
198 |
* @param headerName the header name |
199 |
* @param headerValue the header value, ( unencoded) |
200 |
**/ |
201 |
function addHeader( $headerName, $headerValue ) |
202 |
{ |
203 |
if( $this->debug & DBGTRACE ) echo "addHeader( $headerName, $headerValue )\n"; |
204 |
$this->requestHeaders[$headerName] = $headerValue; |
205 |
} |
206 |
|
207 |
/** |
208 |
* removeHeader |
209 |
* unset a request header |
210 |
* @param headerName the header name |
211 |
**/ |
212 |
function removeHeader( $headerName ) |
213 |
{ |
214 |
if( $this->debug & DBGTRACE ) echo "removeHeader( $headerName) \n"; |
215 |
unset( $this->requestHeaders[$headerName] ); |
216 |
} |
217 |
|
218 |
/** |
219 |
* addCookie |
220 |
* set a session cookie, that will be used in the next requests. |
221 |
* this is a hack as cookie are usually set by the server, but you may need it |
222 |
* it is your responsabilty to unset the cookie if you request another host |
223 |
* to keep a session on the server |
224 |
* @param string the name of the cookie |
225 |
* @param string the value for the cookie |
226 |
**/ |
227 |
function addCookie( $cookiename, $cookievalue ) |
228 |
{ |
229 |
if( $this->debug & DBGTRACE ) echo "addCookie( $cookiename, $cookievalue ) \n"; |
230 |
$cookie = $cookiename . "=" . $cookievalue; |
231 |
$this->requestHeaders["Cookie"] = $cookie; |
232 |
} |
233 |
|
234 |
/** |
235 |
* removeCookie |
236 |
* unset cookies currently in use |
237 |
**/ |
238 |
function removeCookies() |
239 |
{ |
240 |
if( $this->debug & DBGTRACE ) echo "removeCookies() \n"; |
241 |
unset( $this->requestHeaders["Cookie"] ); |
242 |
} |
243 |
|
244 |
/** |
245 |
* Connect |
246 |
* open the connection to the server |
247 |
* @param host string server address (or IP) |
248 |
* @param port string server listening port - defaults to 80 |
249 |
* @return boolean false is connection failed, true otherwise |
250 |
**/ |
251 |
function Connect( $host, $port = NULL ) |
252 |
{ |
253 |
if( $this->debug & DBGTRACE ) echo "Connect( $host, $port ) \n"; |
254 |
|
255 |
$this->url['scheme'] = "http"; |
256 |
$this->url['host'] = $host; |
257 |
if( $port != NULL ) |
258 |
$this->url['port'] = $port; |
259 |
return true; |
260 |
} |
261 |
|
262 |
/** |
263 |
* Disconnect |
264 |
* close the connection to the server |
265 |
**/ |
266 |
function Disconnect() |
267 |
{ |
268 |
if( $this->debug & DBGTRACE ) echo "Disconnect()\n"; |
269 |
if( $this->socket ) |
270 |
fclose( $this->socket ); |
271 |
} |
272 |
|
273 |
/** |
274 |
* head |
275 |
* issue a HEAD request |
276 |
* @param uri string URI of the document |
277 |
* @return string response status code (200 if ok) |
278 |
* @seeAlso getHeaders() |
279 |
**/ |
280 |
function Head( $uri ) |
281 |
{ |
282 |
if( $this->debug & DBGTRACE ) echo "Head( $uri )\n"; |
283 |
$this->responseHeaders = $this->responseBody = ""; |
284 |
$uri = $this->makeUri( $uri ); |
285 |
if( $this->sendCommand( "HEAD $uri HTTP/$this->protocolVersion" ) ) |
286 |
$this->processReply(); |
287 |
return $this->reply; |
288 |
} |
289 |
|
290 |
|
291 |
/** |
292 |
* get |
293 |
* issue a GET http request |
294 |
* @param uri URI (path on server) or full URL of the document |
295 |
* @return string response status code (200 if ok) |
296 |
* @seeAlso getHeaders(), getBody() |
297 |
**/ |
298 |
function Get( $url ) |
299 |
{ |
300 |
if( $this->debug & DBGTRACE ) echo "Get( $url )\n"; |
301 |
$this->responseHeaders = $this->responseBody = ""; |
302 |
$uri = $this->makeUri( $url ); |
303 |
|
304 |
if( $this->sendCommand( "GET $uri HTTP/$this->protocolVersion" ) ) |
305 |
$this->processReply(); |
306 |
return $this->reply; |
307 |
} |
308 |
|
309 |
/** |
310 |
* Options |
311 |
* issue a OPTIONS http request |
312 |
* @param uri URI (path on server) or full URL of the document |
313 |
* @return array list of options supported by the server or NULL in case of error |
314 |
**/ |
315 |
function Options( $url ) |
316 |
{ |
317 |
if( $this->debug & DBGTRACE ) echo "Options( $url )\n"; |
318 |
$this->responseHeaders = $this->responseBody = ""; |
319 |
$uri = $this->makeUri( $url ); |
320 |
|
321 |
if( $this->sendCommand( "OPTIONS $uri HTTP/$this->protocolVersion" ) ) |
322 |
$this->processReply(); |
323 |
if( @$this->responseHeaders["Allow"] == NULL ) |
324 |
return NULL; |
325 |
else |
326 |
return explode( ",", $this->responseHeaders["Allow"] ); |
327 |
} |
328 |
|
329 |
/** |
330 |
* Post |
331 |
* issue a POST http request |
332 |
* @param uri string URI of the document |
333 |
* @param query_params array parameters to send in the form "parameter name" => value |
334 |
* @return string response status code (200 if ok) |
335 |
* @example |
336 |
* $params = array( "login" => "tiger", "password" => "secret" ); |
337 |
* $http->post( "/login.php", $params ); |
338 |
**/ |
339 |
function Post( $uri, $query_params="" ) |
340 |
{ |
341 |
if( $this->debug & DBGTRACE ) echo "Post( $uri, $query_params )\n"; |
342 |
$uri = $this->makeUri( $uri ); |
343 |
if( is_array($query_params) ) { |
344 |
$postArray = array(); |
345 |
foreach( $query_params as $k=>$v ) { |
346 |
$postArray[] = urlencode($k) . "=" . urlencode($v); |
347 |
} |
348 |
$this->requestBody = implode( "&", $postArray); |
349 |
} |
350 |
// set the content type for post parameters |
351 |
$this->addHeader( 'Content-Type', "application/x-www-form-urlencoded" ); |
352 |
// done in sendCommand() $this->addHeader( 'Content-Length', strlen($this->requestBody) ); |
353 |
|
354 |
if( $this->sendCommand( "POST $uri HTTP/$this->protocolVersion" ) ) |
355 |
$this->processReply(); |
356 |
$this->removeHeader('Content-Type'); |
357 |
$this->removeHeader('Content-Length'); |
358 |
$this->requestBody = ""; |
359 |
return $this->reply; |
360 |
} |
361 |
|
362 |
/** |
363 |
* Put |
364 |
* Send a PUT request |
365 |
* PUT is the method to sending a file on the server. it is *not* widely supported |
366 |
* @param uri the location of the file on the server. dont forget the heading "/" |
367 |
* @param filecontent the content of the file. binary content accepted |
368 |
* @return string response status code 201 (Created) if ok |
369 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
370 |
**/ |
371 |
function Put( $uri, $filecontent ) |
372 |
{ |
373 |
if( $this->debug & DBGTRACE ) echo "Put( $uri, [filecontent not displayed )\n"; |
374 |
$uri = $this->makeUri( $uri ); |
375 |
$this->requestBody = $filecontent; |
376 |
if( $this->sendCommand( "PUT $uri HTTP/$this->protocolVersion" ) ) |
377 |
$this->processReply(); |
378 |
return $this->reply; |
379 |
} |
380 |
|
381 |
/** |
382 |
* Send a MOVE HTTP-DAV request |
383 |
* Move (rename) a file on the server |
384 |
* @param srcUri the current file location on the server. dont forget the heading "/" |
385 |
* @param destUri the destination location on the server. this is *not* a full URL |
386 |
* @param overwrite boolean - true to overwrite an existing destinationn default if yes |
387 |
* @return string response status code 204 (Unchanged) if ok |
388 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
389 |
**/ |
390 |
function Move( $srcUri, $destUri, $overwrite=true ) |
391 |
{ |
392 |
if( $this->debug & DBGTRACE ) echo "Move( $srcUri, $destUri, $overwrite )\n"; |
393 |
if( $overwrite ) |
394 |
$this->requestHeaders['Overwrite'] = "T"; |
395 |
else |
396 |
$this->requestHeaders['Overwrite'] = "F"; |
397 |
|
398 |
$destUrl = $this->url['scheme'] . "://" . $this->url['host']; |
399 |
if( $this->url['port'] != "" ) |
400 |
$destUrl .= ":" . $this->url['port']; |
401 |
$destUrl .= $destUri; |
402 |
$this->requestHeaders['Destination'] = $destUrl; |
403 |
|
404 |
if( $this->sendCommand( "MOVE $srcUri HTTP/$this->protocolVersion" ) ) |
405 |
$this->processReply(); |
406 |
return $this->reply; |
407 |
} |
408 |
|
409 |
/** |
410 |
* Send a COPY HTTP-DAV request |
411 |
* Copy a file -allready on the server- into a new location |
412 |
* @param srcUri the current file location on the server. dont forget the heading "/" |
413 |
* @param destUri the destination location on the server. this is *not* a full URL |
414 |
* @param overwrite boolean - true to overwrite an existing destination - overwrite by default |
415 |
* @return string response status code 204 (Unchanged) if ok |
416 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
417 |
**/ |
418 |
function Copy( $srcUri, $destUri, $overwrite=true ) |
419 |
{ |
420 |
if( $this->debug & DBGTRACE ) echo "Copy( $srcUri, $destUri, $overwrite )\n"; |
421 |
if( $overwrite ) |
422 |
$this->requestHeaders['Overwrite'] = "T"; |
423 |
else |
424 |
$this->requestHeaders['Overwrite'] = "F"; |
425 |
|
426 |
$destUrl = $this->url['scheme'] . "://" . $this->url['host']; |
427 |
if( $this->url['port'] != "" ) |
428 |
$destUrl .= ":" . $this->url['port']; |
429 |
$destUrl .= $destUri; |
430 |
$this->requestHeaders['Destination'] = $destUrl; |
431 |
|
432 |
if( $this->sendCommand( "COPY $srcUri HTTP/$this->protocolVersion" ) ) |
433 |
$this->processReply(); |
434 |
return $this->reply; |
435 |
} |
436 |
|
437 |
|
438 |
/** |
439 |
* Send a MKCOL HTTP-DAV request |
440 |
* Create a collection (directory) on the server |
441 |
* @param uri the directory location on the server. dont forget the heading "/" |
442 |
* @return string response status code 201 (Created) if ok |
443 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
444 |
**/ |
445 |
function MkCol( $uri ) |
446 |
{ |
447 |
if( $this->debug & DBGTRACE ) echo "Mkcol( $uri )\n"; |
448 |
// $this->requestHeaders['Overwrite'] = "F"; |
449 |
if( $this->sendCommand( "MKCOL $uri HTTP/$this->protocolVersion" ) ) |
450 |
$this->processReply(); |
451 |
return $this->reply; |
452 |
} |
453 |
|
454 |
/** |
455 |
* Delete a file on the server using the "DELETE" HTTP-DAV request |
456 |
* This HTTP method is *not* widely supported |
457 |
* Only partially supports "collection" deletion, as the XML response is not parsed |
458 |
* @param uri the location of the file on the server. dont forget the heading "/" |
459 |
* @return string response status code 204 (Unchanged) if ok |
460 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
461 |
**/ |
462 |
function Delete( $uri ) |
463 |
{ |
464 |
if( $this->debug & DBGTRACE ) echo "Delete( $uri )\n"; |
465 |
if( $this->sendCommand( "DELETE $uri HTTP/$this->protocolVersion" ) ) |
466 |
$this->processReply(); |
467 |
return $this->reply; |
468 |
} |
469 |
|
470 |
/** |
471 |
* PropFind |
472 |
* implements the PROPFIND method |
473 |
* PROPFIND retrieves meta informations about a resource on the server |
474 |
* XML reply is not parsed, you'll need to do it |
475 |
* @param uri the location of the file on the server. dont forget the heading "/" |
476 |
* @param scope set the scope of the request. |
477 |
* O : infos about the node only |
478 |
* 1 : infos for the node and its direct children ( one level) |
479 |
* Infinity : infos for the node and all its children nodes (recursive) |
480 |
* @return string response status code - 207 (Multi-Status) if OK |
481 |
* @see RFC2518 "HTTP Extensions for Distributed Authoring WEBDAV" |
482 |
**/ |
483 |
function PropFind( $uri, $scope=0 ) |
484 |
{ |
485 |
if( $this->debug & DBGTRACE ) echo "Propfind( $uri, $scope )\n"; |
486 |
$this->requestHeaders['Depth'] = $scope; |
487 |
if( $this->sendCommand( "PROPFIND $uri HTTP/$this->protocolVersion" ) ) |
488 |
$this->processReply(); |
489 |
return $this->reply; |
490 |
} |
491 |
|
492 |
|
493 |
/** |
494 |
* Lock - WARNING: EXPERIMENTAL |
495 |
* Lock a ressource on the server. XML reply is not parsed, you'll need to do it |
496 |
* @param $uri URL (relative) of the resource to lock |
497 |
* @param $lockScope - use "exclusive" for an eclusive lock, "inclusive" for a shared lock |
498 |
* @param $lockType - acces type of the lock : "write" |
499 |
* @param $lockScope - use "exclusive" for an eclusive lock, "inclusive" for a shared lock |
500 |
* @param $lockOwner - an url representing the owner for this lock |
501 |
* @return server reply code, 200 if ok |
502 |
**/ |
503 |
function Lock( $uri, $lockScope, $lockType, $lockOwner ) |
504 |
{ |
505 |
$body = "<?xml version=\"1.0\" encoding=\"utf-8\" ?> |
506 |
<D:lockinfo xmlns:D='DAV:'> |
507 |
<D:lockscope><D:$lockScope/></D:lockscope>\n<D:locktype><D:$lockType/></D:locktype> |
508 |
<D:owner><D:href>$lockOwner</D:href></D:owner> |
509 |
</D:lockinfo>\n"; |
510 |
|
511 |
$this->requestBody = utf8_encode( $body ); |
512 |
if( $this->sendCommand( "LOCK $uri HTTP/$this->protocolVersion" ) ) |
513 |
$this->processReply(); |
514 |
return $this->reply; |
515 |
} |
516 |
|
517 |
|
518 |
/** |
519 |
* Unlock - WARNING: EXPERIMENTAL |
520 |
* unlock a ressource on the server |
521 |
* @param $uri URL (relative) of the resource to unlock |
522 |
* @param $lockToken the lock token given at lock time, eg: opaquelocktoken:e71d4fae-5dec-22d6-fea5-00a0c91e6be4 |
523 |
* @return server reply code, 204 if ok |
524 |
**/ |
525 |
function Unlock( $uri, $lockToken ) |
526 |
{ |
527 |
$this->addHeader( "Lock-Token", "<$lockToken>" ); |
528 |
if( $this->sendCommand( "UNLOCK $uri HTTP/$this->protocolVersion" ) ) |
529 |
$this->processReply(); |
530 |
return $this->reply; |
531 |
} |
532 |
|
533 |
/** |
534 |
* getHeaders |
535 |
* return the response headers |
536 |
* to be called after a Get() or Head() call |
537 |
* @return array headers received from server in the form headername => value |
538 |
* @seeAlso get, head |
539 |
**/ |
540 |
function getHeaders() |
541 |
{ |
542 |
if( $this->debug & DBGTRACE ) echo "getHeaders()\n"; |
543 |
if( $this->debug & DBGINDATA ) { |
544 |
echo "DBG.INDATA responseHeaders="; print_r( $this->responseHeaders ); |
545 |
} |
546 |
return $this->responseHeaders; |
547 |
} |
548 |
|
549 |
/** |
550 |
* getHeader |
551 |
* return the response header "headername" |
552 |
* @param headername the name of the header |
553 |
* @return header value or NULL if no such header is defined |
554 |
**/ |
555 |
function getHeader( $headername ) |
556 |
{ |
557 |
if( $this->debug & DBGTRACE ) echo "getHeaderName( $headername )\n"; |
558 |
return $this->responseHeaders[$headername]; |
559 |
} |
560 |
|
561 |
/** |
562 |
* getBody |
563 |
* return the response body |
564 |
* invoke it after a Get() call for instance, to retrieve the response |
565 |
* @return string body content |
566 |
* @seeAlso get, head |
567 |
**/ |
568 |
function getBody() |
569 |
{ |
570 |
if( $this->debug & DBGTRACE ) echo "getBody()\n"; |
571 |
return $this->responseBody; |
572 |
} |
573 |
|
574 |
/** |
575 |
* getStatus return the server response's status code |
576 |
* @return string a status code |
577 |
* code are divided in classes (where x is a digit) |
578 |
* - 20x : request processed OK |
579 |
* - 30x : document moved |
580 |
* - 40x : client error ( bad url, document not found, etc...) |
581 |
* - 50x : server error |
582 |
* @see RFC2616 "Hypertext Transfer Protocol -- HTTP/1.1" |
583 |
**/ |
584 |
function getStatus() |
585 |
{ |
586 |
return $this->reply; |
587 |
} |
588 |
|
589 |
|
590 |
/** |
591 |
* getStatusMessage return the full response status, of the form "CODE Message" |
592 |
* eg. "404 Document not found" |
593 |
* @return string the message |
594 |
**/ |
595 |
function getStatusMessage() |
596 |
{ |
597 |
return $this->replyString; |
598 |
} |
599 |
|
600 |
|
601 |
|
602 |
|
603 |
/********************************************* |
604 |
* @scope only protected or private methods below |
605 |
**/ |
606 |
|
607 |
/** |
608 |
* send a request |
609 |
* data sent are in order |
610 |
* a) the command |
611 |
* b) the request headers if they are defined |
612 |
* c) the request body if defined |
613 |
* @return string the server repsonse status code |
614 |
**/ |
615 |
function sendCommand( $command ) |
616 |
{ |
617 |
if( $this->debug & DBGLOW ) echo "sendCommand( $command )\n"; |
618 |
$this->responseHeaders = array(); |
619 |
$this->responseBody = ""; |
620 |
|
621 |
// connect if necessary |
622 |
if( $this->socket == false or feof( $this->socket) ) { |
623 |
|
624 |
if( $this->useProxy ) { |
625 |
$host = $this->proxyHost; |
626 |
$port = $this->proxyPort; |
627 |
} else { |
628 |
$host = $this->url['host']; |
629 |
$port = $this->url['port']; |
630 |
} |
631 |
if( $port == "" ) $port = 80; |
632 |
$this->socket = fsockopen( $host, $port, &$this->reply, &$this->replyString ); |
633 |
if( $this->debug & DBGSOCK ) echo "connexion( $host, $port) => $this->socket\n"; |
634 |
if( ! $this->socket ) { |
635 |
if( $this->debug & DBGSOCK ) echo "FAILED : $this->replyString ($this->reply)\n"; |
636 |
return false; |
637 |
} |
638 |
} |
639 |
|
640 |
if( $this->requestBody != "" ) { |
641 |
$this->addHeader( "Content-Length", strlen( $this->requestBody ) ); |
642 |
} |
643 |
|
644 |
$this->request = $command; |
645 |
$cmd = $command . CRLF; |
646 |
if( is_array( $this->requestHeaders) ) { |
647 |
foreach( $this->requestHeaders as $k => $v ) { |
648 |
$cmd .= "$k: $v" . CRLF; |
649 |
} |
650 |
} |
651 |
|
652 |
if( $this->requestBody != "" ) { |
653 |
$cmd .= CRLF . $this->requestBody; |
654 |
} |
655 |
|
656 |
// unset body (in case of successive requests) |
657 |
$this->requestBody = ""; |
658 |
if( $this->debug & DBGOUTDATA ) echo "DBG.OUTDATA Sending\n$cmd\n"; |
659 |
|
660 |
fputs( $this->socket, $cmd . CRLF ); |
661 |
return true; |
662 |
} |
663 |
|
664 |
function processReply() |
665 |
{ |
666 |
if( $this->debug & DBGLOW ) echo "processReply()\n"; |
667 |
|
668 |
$this->replyString = trim(fgets( $this->socket,1024) ); |
669 |
|
670 |
if( preg_match( "|^HTTP/\S+ (\d+) |i", $this->replyString, $a )) { |
671 |
$this->reply = $a[1]; |
672 |
} else { |
673 |
$this->reply = EBADRESPONSE; |
674 |
} |
675 |
if( $this->debug & DBGINDATA ) echo "replyLine: $this->replyString\n"; |
676 |
|
677 |
// get response headers and body |
678 |
$this->responseHeaders = $this->processHeader(); |
679 |
$this->responseBody = $this->processBody(); |
680 |
// if( $this->responseHeaders['set-cookie'] ) |
681 |
// $this->addHeader( "cookie", $this->responseHeaders['set-cookie'] ); |
682 |
return $this->reply; |
683 |
} |
684 |
|
685 |
/** |
686 |
* processHeader() reads header lines from socket until the line equals $lastLine |
687 |
* @scope protected |
688 |
* @return array of headers with header names as keys and header content as values |
689 |
**/ |
690 |
function processHeader( $lastLine = CRLF ) |
691 |
{ |
692 |
if( $this->debug & DBGLOW ) echo "processHeader( [lastLine] )\n"; |
693 |
$headers = array(); |
694 |
$finished = false; |
695 |
|
696 |
while ( ( ! $finished ) && ( ! feof($this->socket)) ) { |
697 |
$str = fgets( $this->socket, 1024 ); |
698 |
if( $this->debug & DBGINDATA ) echo "HEADER : $str"; |
699 |
$finished = ( $str == $lastLine ); |
700 |
if ( !$finished ) { |
701 |
list( $hdr, $value ) = split( ": ", $str, 2 ); |
702 |
// nasty workaround broken multiple same headers (eg. Set-Cookie headers) @FIXME |
703 |
if( isset( $headers[$hdr]) ) |
704 |
$headers[$hdr] .= "; " . trim($value); |
705 |
else |
706 |
$headers[$hdr] = trim($value); |
707 |
} |
708 |
} |
709 |
return $headers; |
710 |
} |
711 |
|
712 |
/** |
713 |
* processBody() reads the body from the socket |
714 |
* the body is the "real" content of the reply |
715 |
* @return string body content |
716 |
* @scope private |
717 |
**/ |
718 |
function processBody() |
719 |
{ |
720 |
$failureCount = 0; |
721 |
if( $this->debug & DBGLOW ) echo "processBody()\n"; |
722 |
/* if( $this->responseHeaders['Content-Length'] ) |
723 |
{ |
724 |
$length = $this->responseHeaders['Content-Length']; |
725 |
$data = fread( $this->socket, $length ); |
726 |
if( $this->debug & DBGSOCK ) echo "DBG.SOCK socket_read using Content-Length ($length)\n"; |
727 |
|
728 |
} else {*/ |
729 |
|
730 |
$data = ""; |
731 |
$counter = 0; |
732 |
// socket_set_blocking( $this->socket, false ); |
733 |
do{ |
734 |
$status = socket_get_status( $this->socket ); |
735 |
if( $status['eof'] == 1 ) { |
736 |
if( $this->debug & DBGSOCK ) echo "DBG.SOCK status eof met, finished socket_read\n"; |
737 |
break; |
738 |
} |
739 |
if( $status['unread_bytes'] > 0 ) { |
740 |
$buffer = fread( $this->socket, $status['unread_bytes'] ); |
741 |
$counter = 0; |
742 |
} else { |
743 |
$buffer = fread( $this->socket, 1024 ); |
744 |
$failureCount++; |
745 |
usleep(2); |
746 |
} |
747 |
$data .= $buffer; |
748 |
if( $this->debug & DBGSOCK ) |
749 |
echo implode( " | ", $status ), "\n"; |
750 |
|
751 |
} while( $status['unread_bytes'] > 0 || $counter++ < 10 ); |
752 |
|
753 |
if( $this->debug & DBGSOCK ) { |
754 |
echo "DBG.SOCK Counter:$counter\nRead failure #: $failureCount\n"; |
755 |
echo " Socket status: "; print_r($status); |
756 |
} |
757 |
// socket_set_blocking( $this->socket, true ); |
758 |
// } |
759 |
return $data; |
760 |
} |
761 |
|
762 |
|
763 |
/** |
764 |
* Calculate and return the URI to be sent ( proxy purpose ) |
765 |
* @param the local URI |
766 |
* @return URI to be used in the HTTP request |
767 |
* @scope private |
768 |
**/ |
769 |
|
770 |
function makeUri( $uri ) |
771 |
{ |
772 |
$a = parse_url( $uri ); |
773 |
|
774 |
if( isset($a['scheme']) && isset($a['host']) ) { |
775 |
$this->url = $a; |
776 |
} else { |
777 |
unset( $this->url['query']); |
778 |
unset( $this->url['fragment']); |
779 |
$this->url = array_merge( $this->url, $a ); |
780 |
} |
781 |
if( $this->useProxy ) { |
782 |
$requesturi= "http://" . $this->url['host'] . ( empty($this->url['port']) ? "" : ":" . $this->url['port'] ) . $this->url['path'] . ( empty($this->url['query']) ? "" : "?" . $this->url['query'] ); |
783 |
} else { |
784 |
$requesturi = $this->url['path'] . (empty( $this->url['query'] ) ? "" : "?" . $this->url['query']); |
785 |
} |
786 |
return $requesturi; |
787 |
} |
788 |
|
789 |
} // end class Net_HTTP_Client |
790 |
|
791 |
|
792 |
?> |