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