diff --git a/httpclient.php b/httpclient.php index 1a76d33..43a51b6 100644 --- a/httpclient.php +++ b/httpclient.php @@ -4,8 +4,6 @@ * @author Dominique Fournier */ -namespace vendor\domframework; - /** This programe allow to get a HTTP page from a site, and examine the content. * It will store the Cookies, allow to do the redirects, follow links and * get form / input and send the values. @@ -53,6 +51,10 @@ class Httpclient */ private $tcpclient = null; + /** The SSL Options parameters (if set) + */ + private $ssloptions = array (); + /** The maximum maxsize allowed */ private $maxsize; @@ -83,6 +85,10 @@ class Httpclient */ private $formData = array (); + /** The raw data to send + */ + private $rawData = ""; + /** The timeout in second before expiring the connection */ private $timeout = 30; @@ -123,6 +129,7 @@ class Httpclient if ($maxsize === 0) $maxsize = 8000000000; $this->maxsize = $maxsize; + $this->headersReset (); } // }}} @@ -169,6 +176,7 @@ class Httpclient return $this->method; if (! is_string ($method)) throw new \Exception ("Invalid method to set : not an string", 406); + $method = strtoupper ($method); if (! in_array ($method, array ("GET", "POST", "PUT", "DELETE", "HEAD"))) throw new \Exception ("Invalid method to set : not in list", 406); $this->method = $method; @@ -194,6 +202,28 @@ class Httpclient } // }}} + /** Set the headers to initial value + */ + public function headersReset () + // {{{ + { + $this->headersSent = array (); + return $this; + } + // }}} + + /** Add a new header to be sent to the server + * @param string $header The header to add/update + * @param string $value The value to save + */ + public function headerAdd ($header, $value) + // {{{ + { + $this->headersSent[$header] = $value; + return $this; + } + // }}} + /** Get the port used for connection */ public function port () @@ -245,7 +275,7 @@ class Httpclient * If value is like "@/tmp/file", use the /tmp/file as content * @param array|null $formData The data to send to the server */ - public function formData ($formData) + public function formData ($formData = null) // {{{ { if ($formData === null) @@ -253,6 +283,25 @@ class Httpclient if (! is_array ($formData)) throw new \Exception ("Invalid form data provided : not an array", 406); $this->formData = $formData; + $this->rawData = ""; + return $this; + } + // }}} + + /** Set / Get the content Data + * The data is in raw format and will not be modified. + * Overwrite the eventually previous form data and rawData + * @param string|null $rawData The data to use + */ + public function rawData ($rawData = null) + // {{{ + { + if ($rawData === null) + return $this->rawData; + if (! is_string ($rawData)) + throw new \Exception ("Invalid raw data provided : not a string", 406); + $this->rawData = $rawData; + $this->formData = array (); return $this; } // }}} @@ -400,39 +449,7 @@ class Httpclient $this->method ("GET"); $this->url ($url); $this->connect ($ssloptions); - $content = ""; - while ($tmp = $this->read (1000000)) - { - $content .= $tmp; - if (strlen ($content) > $this->maxsize) - throw new \Exception ("File to get exceeded maxsize", 500); - } - $this->disconnect (); - if ($this->httpCode === 301 || $this->httpCode === 302 || - key_exists ("Location", $this->headersReceived)) - { - if (! key_exists ("Location", $this->headersReceived)) - throw new \Exception ("Redirect without location provided", 406); - $this->redirectCount++; - if ($this->redirectCount > $this->redirectMaxCount) - throw new \Exception ("Redirect exceed maximum limit", 406); - // echo "REDIRECT TO ".$this->headersReceived["Location"]."\n"; - $location = $this->headersReceived["Location"]; - $parseURLInit = parse_url ($url); - $parseURLLocation = parse_url ($location); - if (! key_exists ("port", $parseURLLocation) && - key_exists ("port", $parseURLInit)) - $location = ":".$parseURLInit["port"].$location; - if (! key_exists ("host", $parseURLLocation)) - $location = $parseURLInit["host"].$location; - if (! key_exists ("scheme", $parseURLLocation)) - $location = $parseURLInit["scheme"]."://".$location; - $this->log (1, "REDIRECT $this->httpCode to $location"); - $content = $this->getPage ($location, $ssloptions); - } - $this->referer = $url; - $this->redirectCount = 0; - return $content; + return $this->getContent (); } // }}} @@ -445,6 +462,7 @@ class Httpclient // {{{ { $this->log (2, "## URL Start $this->method $this->url"); + $this->ssloptions = $ssloptions; $this->headersReceived = array (); $this->bodySize = null; $this->httpCode = null; @@ -507,20 +525,21 @@ class Httpclient // Prepare the headers to be sent // {{{ - $this->headersSent = array (); - $this->headersSent[] = "$this->method $path HTTP/1.1"; - $this->headersSent[] = "Host: ".$parseURL["host"]; + unset ($this->headersSent[0]); + array_unshift ($this->headersSent, + "$this->method $path HTTP/1.1"); + $this->headersSent["Host"] = $parseURL["host"]; if ($this->acceptEncoding !== "") - $this->headersSent[] = "Accept-Encoding: $this->acceptEncoding"; + $this->headersSent["Accept-Encoding"] = $this->acceptEncoding; if ($this->acceptLanguage !== "") - $this->headersSent[] = "Accept-Language: $this->acceptLanguage"; + $this->headersSent["Accept-Language"] = $this->acceptLanguage; if ($this->accept != "") - $this->headersSent[] = "Accept: $this->accept"; + $this->headersSent["Accept"] = $this->accept; if ($this->useragent !== "") - $this->headersSent[] = "User-Agent: $this->useragent"; + $this->headersSent["User-Agent"] = $this->useragent; if ($this->referer !== "") - $this->headersSent[] = "Referer: $this->referer"; - $this->headersSent[] = "Connection: keep-alive"; + $this->headersSent["Referer"] = $this->referer; + $this->headersSent["Connection"] = "keep-alive"; $cookies = $this->cookieToSend ($this->url); if (! empty ($cookies)) { @@ -528,7 +547,7 @@ class Httpclient } if ($this->method !== "GET" && ! empty ($this->formData)) { - $this->headersSent[] = "Content-Type: application/x-www-form-urlencoded"; + $this->headersSent["Content-Type"] = "application/x-www-form-urlencoded"; $len = 0; foreach ($this->formData as $key => $val) { @@ -546,8 +565,16 @@ class Httpclient else $len += strlen (rawurlencode ($val)); } - $this->headersSent[] = "Content-Length: $len"; + $this->headersSent["Content-Length"] = $len; } + if ($this->method !== "GET" && $this->rawData !== "") + { + if (! key_exists ("Content-Type", $this->headersSent)) + $this->headersSent["Content-Type"] = + "application/x-www-form-urlencoded"; + $this->headersSent["Content-Length"] = strlen ($this->rawData); + } + $this->log (2, "Headers Send :"); $this->log (2, $this->headersSent); // }}} @@ -561,12 +588,15 @@ class Httpclient $this->tcpclient->timeout ($this->timeout); $this->tcpclient->connect (); if ($parseURL["scheme"] === "https") - $this->tcpclient->cryptoEnable (true, null, $ssloptions); + $this->tcpclient->cryptoEnable (true, null, $this->ssloptions); } $this->tcpclient->readMode ("text"); - foreach ($this->headersSent as $header) + $this->tcpclient->send ($this->headersSent[0]."\r\n"); + foreach ($this->headersSent as $header => $value) { - $this->tcpclient->send ("$header\r\n"); + if ($header === 0) + continue; + $this->tcpclient->send ("$header: $value\r\n"); } $this->tcpclient->send ("\r\n"); // }}} @@ -595,6 +625,10 @@ class Httpclient $i ++; } } + elseif ($this->method !== "GET" && $this->rawData !== "") + { + $this->tcpclient->send ($this->rawData); + } // }}} // Get the result header from the server @@ -649,8 +683,7 @@ class Httpclient $this->contentMethod = "chunked"; $this->bodySize = 0; } - elseif (key_exists ("Content-Length", $headers) && - $headers["Content-Length"] > 0) + elseif (key_exists ("Content-Length", $headers)) { $this->contentMethod = "Content-Length"; $this->bodySize = $headers["Content-Length"]; @@ -667,12 +700,59 @@ class Httpclient } if ($this->contentMethod === "chunked") $this->log (1, "URL $this->method $this->url $this->httpCode Chunked"); - elseif (is_int ($this->bodySize)) + elseif (is_numeric ($this->bodySize)) $this->log (1, "URL $this->method $this->url $this->httpCode ". $this->bodySize); else $this->log (1, "URL $this->method $this->url $this->httpCode 0"); // }}} + return $this; + } + // }}} + + /** Get the content from the server and put it in memory + */ + public function getContent () + // {{{ + { + $url = $this->url; + $content = ""; + while ($tmp = $this->read (1000000)) + { + $content .= $tmp; + if (strlen ($content) > $this->maxsize) + throw new \Exception ("File to get exceeded maxsize", 500); + } + $this->disconnect (); + if ($this->httpCode === 301 || $this->httpCode === 302 || + key_exists ("Location", $this->headersReceived)) + { + if (! key_exists ("Location", $this->headersReceived)) + throw new \Exception ("Redirect without location provided", 406); + $this->redirectCount++; + if ($this->redirectCount > $this->redirectMaxCount) + throw new \Exception ("Redirect exceed maximum limit", 406); + // echo "REDIRECT TO ".$this->headersReceived["Location"]."\n"; + $location = $this->headersReceived["Location"]; + $parseURLInit = parse_url ($url); + $parseURLLocation = parse_url ($location); + if ($parseURLLocation["path"]{0} !== "/" && + key_exists ("path", $parseURLInit)) + $location = dirname ($parseURLInit["path"]).$location; + if (! key_exists ("port", $parseURLLocation) && + key_exists ("port", $parseURLInit)) + $location = ":".$parseURLInit["port"].$location; + if (! key_exists ("host", $parseURLLocation)) + $location = $parseURLInit["host"].$location; + if (! key_exists ("scheme", $parseURLLocation)) + $location = $parseURLInit["scheme"]."://".$location; + $this->log (1, "REDIRECT $this->httpCode to $location"); + $this->log (2, " content=$content\n"); + $content = $this->getPage ($location, $this->ssloptions); + } + $this->referer = $url; + $this->redirectCount = 0; + return $content; } // }}} @@ -688,12 +768,6 @@ class Httpclient if ($this->tcpclient === null) throw new \Exception ("HTTPClient : can not read non connected URL", 406); $this->tcpclient->timeout ($this->timeout); -/* if ($this->contentMethod === false) - { - // If the server will never send anything, code 204 by example, do not try - // to get data - return ""; - }*/ $content = ""; if ($this->contentMethod === "chunked" && $this->bodySize === 0) { @@ -711,7 +785,8 @@ class Httpclient if ($toBeRead > $maxsize) $toBeRead = $maxsize; $this->tcpclient->readMode ("binary"); - $content = $this->tcpclient->read ($toBeRead); + if ($toBeRead > 0) + $content = $this->tcpclient->read ($toBeRead); // In close mode, the bodySize is not set and should not be updated if ($this->bodySize !== null) $this->bodySize = $this->bodySize - strlen ($content);