Files
OMS/app/base/lib/http.php
2025-12-28 23:13:25 +08:00

304 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
class base_http {
var $timeout = 10;
var $defaultChunk = 4096;
var $http_ver = '1.1';
var $hostaddr = null;
var $default_headers = array(
'Pragma' => "no-cache",
'Cache-Control' => "no-cache",
'Connection' => "close"
);
function __construct() {
if (defined('HTTP_PROXY')) {
list($this->proxyHost, $this->proxyPort) = explode(':', HTTP_PROXY);
}
}
function set_timeout($timeout) {
$timeout = (intval($timeout) > 0) ? intval($timeout) : 10;
$this->timeout = $timeout;
return $this;
}
function action($action, $url, $headers = null, $callback = null, $data = null, $ping_only = false) {
$this->callback = $callback;
$tmp_data = $data;
if ($url) {
$url_info = parse_url($url);
$request_query = (isset($url_info['path']) ? $url_info['path'] : '/') . (isset($url_info['query']) ? '?' . $url_info['query'] : '');
$request_server = $request_host = $url_info['host'];
$request_port = (isset($url_info['port']) ? $url_info['port'] : (($url_info['scheme'] == 'https') ? 443 : 80));
} else {
$request_server = $_SERVER['SERVER_ADDR'];
$request_query = $_SERVER['PHP_SELF'];
$request_host = $_SERVER['HTTP_HOST'];
$request_port = $_SERVER['SERVER_PORT'];
}
$out = strtoupper($action) . ' ' . $request_query . " HTTP/{$this->http_ver}\r\n";
$out .= 'Host: ' . $request_host . ($request_port != 80 && $request_port != 443 ? (':' . $request_port) : '') . "\r\n";
$this->responseHeader = &$responseHeader;
$this->responseBody = &$responseBody;
if ($data) {
if (is_array($data)) {
$data = utils::http_build_query($data);
}
if ($headers['Content-Encoding'] == 'gzip') {
$gdata = gzencode($data);
if ($gdata) {
$data = $gdata;
} else {
unset($headers['Content-Encoding']);
}
}//todo: 判断是否需要gzip
$headers['Content-Length'] = strlen($data);
if (!isset($headers['Content-Type'])) {
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
}
$headers = array_merge($this->default_headers, (array)$headers);
foreach ((array)$headers as $k => $v) {
$out .= $k . ': ' . $v . "\r\n";
}
$out .= "\r\n" . $data;
$data = null;
kernel::log($out);
$responseHeader = array();
if ($this->proxyHost && $this->proxyPort) {
$request_server = $this->proxyHost;
$request_port = $this->proxyPort;
kernel::log('Using proxy ' . $request_server . ':' . $request_port . '. ');
}
if ($this->hostaddr) {
$request_addr = $this->hostaddr;
} else {
//if (!$this->is_addr($request_server)) {
// kernel::log('Resolving ' . $request_server . '... ', true);
// $request_addr = gethostbyname($request_server);
// kernel::log($request_addr);
//} else {
$request_addr = $request_server;
//}
if ($url_info['scheme'] == 'https') {
$request_addr = "ssl://" . $request_addr;
} else {
$request_addr = "tcp://" . $request_addr;
}
}
if ($this->hostport) {
$request_port = $this->hostport;
}
$request_addr = $request_addr . ":" . $request_port;
$request_addr = (!is_array($request_addr)) ? array($request_addr) : $request_addr;
foreach ($request_addr AS $request_host_addr) {
kernel::log(sprintf('Connecting to %s|%s|:%s... connected.', $request_server, $request_host_addr, $request_port));
$stream_context = stream_context_create(array('ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true)));
$fp = stream_socket_client($request_host_addr, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT,$stream_context);
if ($fp) {
if ($this->timeout && function_exists('stream_set_timeout')) {
$this->read_time_left = $this->read_time_total = $this->timeout;
} else {
$this->read_time_total = null;
}
$sent = fwrite($fp, $out);
if ($ping_only !== false) {
if (is_numeric($ping_only) && $ping_only > 0) {
sleep($ping_only);
}
return $sent;
}
kernel::log('HTTP request sent, awaiting response... ', true);
$this->request_start = $this->microtime();
$out = null;
$responseBody = '';
if (HTTP_TIME_OUT === $this->readsocket($fp, 512, $status, 'fgets')) {
return HTTP_TIME_OUT;
}
if (preg_match('/\d{3}/', $status, $match)) {
$this->responseCode = $match[0];
}
kernel::log($this->responseCode, true);
while (!feof($fp)) {
if (HTTP_TIME_OUT === $this->readsocket($fp, 512, $raw, 'fgets')) {
return HTTP_TIME_OUT;
}
$raw = trim($raw);
if ($raw) {
if ($p = strpos($raw, ':')) {
$responseHeader[strtolower(trim(substr($raw, 0, $p)))] = trim(substr($raw, $p + 1));
}
} else {
break;
}
}
switch ($this->responseCode) {
case 301:
case 302:
kernel::log(" Redirect \n\t--> " . $responseHeader['location']);
if (isset($responseHeader['location'])) {
return $this->action($action, $responseHeader['location'], $headers, $callback, $tmp_data);
} else {
return false;
}
case 200:
kernel::log(' OK');
return $this->process($fp);
case 404:
kernel::log(' file not found');
return false;
default:
return false;
}
}
}
return false;
}
function process($fp) {
$chunkmode = (isset($this->responseHeader['transfer-encoding']) && $this->responseHeader['transfer-encoding'] == 'chunked');
if ($chunkmode) {
if (HTTP_TIME_OUT === $this->readsocket($fp, 512, $chunklen, 'fgets')) {
return HTTP_TIME_OUT;
}
$chunklen = hexdec(trim($chunklen));
} elseif (isset($this->responseHeader['content-length'])) {
// 设置为非阻塞, 因不分块传输时 服务端不会再输入数据
stream_set_blocking($fp, false);
$chunklen = min($this->defaultChunk, $this->responseHeader['content-length']);
} else {
$chunklen = $this->defaultChunk;
}
while (!feof($fp) && $chunklen) {
if (HTTP_TIME_OUT === $this->readsocket($fp, $chunklen, $content)) {
return HTTP_TIME_OUT;
}
$readlen = strlen($content);
while ($chunklen != $readlen) {
if (HTTP_TIME_OUT === $this->readsocket($fp, $chunklen - $readlen, $buffer)) {
return HTTP_TIME_OUT;
}
if (!strlen($buffer))
break;
$readlen += strlen($buffer);
$content .= $buffer;
}
if ($this->callback) {
if (!call_user_func_array($this->callback, array(&$this, &$content))) {
break;
}
} else {
$responseBody .= $content;
}
if ($chunkmode) {
fread($fp, 2);
if (HTTP_TIME_OUT === $this->readsocket($fp, 512, $chunklen, 'fgets')) {
return HTTP_TIME_OUT;
}
$chunklen = hexdec(trim($chunklen));
} else {
$readed += strlen($content);
if ($this->responseHeader['content-length'] <= $readed) {
break;
}
}
}
fclose($fp);
if ($this->callback) {
return true;
} else {
return $responseBody;
}
}
function is_addr($ip) {
return preg_match('/^[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}$/', $ip);
}
private function microtime() {
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
private function readsocket($fp, $length, &$content, $func = 'fread') {
if (!$this->reset_time_out($fp)) {
return HTTP_TIME_OUT;
}
$content = $func($fp, $length);
if ($this->check_time_out($fp)) {
return HTTP_TIME_OUT;
} else {
return true;
}
}
private function reset_time_out(&$fp) {
if ($this->read_time_total === null) {
return true;
} elseif ($this->read_time_left < 0) {
return false;
} else {
$this->read_time_left = $this->read_time_total - $this->microtime() + $this->request_start;
$second = floor($this->read_time_left);
$microsecond = intval(($this->read_time_left - $second) * 1000000);
stream_set_timeout($fp, $second, $microsecond);
return true;
}
}
private function check_time_out(&$fp) {
if (function_exists('stream_get_meta_data')) {
$info = stream_get_meta_data($fp);
return $info['timed_out'];
} else {
return false;
}
}
}