Наследование: extends Workerman\Connection\ConnectionInterface
Пример #1
0
/**
 * @param TcpConnection $connection
 * @param $data
 */
function onMessage($connection, $data)
{
    //记录进入数据
    MyLog::debug("[IN] [LEN] %s [USER_ID] %s [CMD] %x [body] %s ", $data['pack_len'], $data['user_id'], $data['cmd'], json_encode($data['body']));
    //根据cmd进行路由
    \Workerman\route\CrouteFunc::deal($data, $response);
    //记录发出数据
    MyLog::debug("[OUT] [USER_ID] %s [CMD] 0x%08x [RET] %s [body] %s ", $response['user_id'], $response['cmd'], $response['return_code'], json_encode($response['body']));
    //返回数据
    $connection->send($response);
}
Пример #2
0
 /**
  * 判断数据包长度
  * @param  string $buffer
  * @return int
  */
 public static function input($buffer, TcpConnection $connection)
 {
     // 数据包总长度
     $bufferlenght = strlen($buffer);
     // 防止用户传输不符合协议的超大数据包(10M)
     if ($bufferlenght >= TcpConnection::$maxPackageSize) {
         $connection->close();
         return 0;
     }
     // '+'开头,数据包大于4位
     if ($bufferlenght >= 4) {
         // 第一次出现'++HC'位置
         $headsignall = strpos($buffer, '++HC');
         if ($headsignall === 0 && $bufferlenght >= 8) {
             return self::regmessage($buffer, $bufferlenght, 0);
         }
         // 没有找到协议头
         if ($headsignall === false) {
             return self::findhead($buffer, $bufferlenght);
         }
         // 协议头前面数据
         if ($headsignall) {
             return $headsignall;
         }
     }
     return 0;
 }
Пример #3
0
 /**
  * Check the integrity of the package.
  *
  * @param string        $buffer
  * @param TcpConnection $connection
  * @return int
  */
 public static function input($buffer, TcpConnection $connection)
 {
     // Judge whether the package length exceeds the limit.
     if (strlen($buffer) >= TcpConnection::$maxPackageSize) {
         $connection->close();
         return 0;
     }
     //  Find the position of  "\n".
     $pos = strpos($buffer, "\n");
     // No "\n", packet length is unknown, continue to wait for the data so return 0.
     if ($pos === false) {
         return 0;
     }
     // Return the current package length.
     return $pos + 1;
 }
Пример #4
0
 /**
  * 检查包的完整性
  * 如果能够得到包长,则返回包的长度,否则返回0继续等待数据
  * @param string $buffer
  */
 public static function input($buffer, TcpConnection $connection)
 {
     // 由于没有包头,无法预先知道包长,不能无限制的接收数据,
     // 所以需要判断当前接收的数据是否超过限定值
     if (strlen($buffer) >= TcpConnection::$maxPackageSize) {
         $connection->close();
         return 0;
     }
     // 获得换行字符"\n"位置
     $pos = strpos($buffer, "\n");
     // 没有换行符,无法得知包长,返回0继续等待数据
     if ($pos === false) {
         return 0;
     }
     // 有换行符,返回当前包长,包含换行符
     return $pos + 1;
 }
Пример #5
0
 /**
  * Emit when http message coming.
  *
  * @param Connection\TcpConnection $connection
  * @return void
  */
 public function onMessage($connection)
 {
     // REQUEST_URI.
     $workerman_url_info = parse_url($_SERVER['REQUEST_URI']);
     if (!$workerman_url_info) {
         Http::header('HTTP/1.1 400 Bad Request');
         $connection->close('<h1>400 Bad Request</h1>');
         return;
     }
     $workerman_path = isset($workerman_url_info['path']) ? $workerman_url_info['path'] : '/';
     $workerman_path_info = pathinfo($workerman_path);
     $workerman_file_extension = isset($workerman_path_info['extension']) ? $workerman_path_info['extension'] : '';
     if ($workerman_file_extension === '') {
         $workerman_path = ($len = strlen($workerman_path)) && $workerman_path[$len - 1] === '/' ? $workerman_path . 'index.php' : $workerman_path . '/index.php';
         $workerman_file_extension = 'php';
     }
     $workerman_root_dir = isset($this->serverRoot[$_SERVER['SERVER_NAME']]) ? $this->serverRoot[$_SERVER['SERVER_NAME']] : current($this->serverRoot);
     $workerman_file = "{$workerman_root_dir}/{$workerman_path}";
     if ($workerman_file_extension === 'php' && !is_file($workerman_file)) {
         $workerman_file = "{$workerman_root_dir}/index.php";
         if (!is_file($workerman_file)) {
             $workerman_file = "{$workerman_root_dir}/index.html";
             $workerman_file_extension = 'html';
         }
     }
     // File exsits.
     if (is_file($workerman_file)) {
         // Security check.
         if (!($workerman_request_realpath = realpath($workerman_file)) || !($workerman_root_dir_realpath = realpath($workerman_root_dir)) || 0 !== strpos($workerman_request_realpath, $workerman_root_dir_realpath)) {
             Http::header('HTTP/1.1 400 Bad Request');
             $connection->close('<h1>400 Bad Request</h1>');
             return;
         }
         $workerman_file = realpath($workerman_file);
         // Request php file.
         if ($workerman_file_extension === 'php') {
             $workerman_cwd = getcwd();
             chdir($workerman_root_dir);
             ini_set('display_errors', 'off');
             ob_start();
             // Try to include php file.
             try {
                 // $_SERVER.
                 $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
                 $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
                 include $workerman_file;
             } catch (\Exception $e) {
                 // Jump_exit?
                 if ($e->getMessage() != 'jump_exit') {
                     echo $e;
                 }
             }
             $content = ob_get_clean();
             ini_set('display_errors', 'on');
             if (strtolower($_SERVER['HTTP_CONNECTION']) === "keep-alive") {
                 $connection->send($content);
             } else {
                 $connection->close($content);
             }
             chdir($workerman_cwd);
             return;
         }
         // Send file to client.
         return self::sendFile($connection, $workerman_file);
     } else {
         // 404
         Http::header("HTTP/1.1 404 Not Found");
         $connection->close('<html><head><title>404 File not found</title></head><body><center><h3>404 Not Found</h3></center></body></html>');
         return;
     }
 }
Пример #6
0
 /**
  * 从http数据包中解析$_POST、$_GET、$_COOKIE等 
  * @param string $recv_buffer
  * @param TcpConnection $connection
  * @return void
  */
 public static function decode($recv_buffer, TcpConnection $connection)
 {
     // 初始化
     $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array();
     $GLOBALS['HTTP_RAW_POST_DATA'] = '';
     // 清空上次的数据
     HttpCache::$header = array('Connection' => 'Connection: keep-alive');
     HttpCache::$instance = new HttpCache();
     // 需要设置的变量名
     $_SERVER = array('QUERY_STRING' => '', 'REQUEST_METHOD' => '', 'REQUEST_URI' => '', 'SERVER_PROTOCOL' => '', 'SERVER_SOFTWARE' => 'workerman/3.0', 'SERVER_NAME' => '', 'HTTP_HOST' => '', 'HTTP_USER_AGENT' => '', 'HTTP_ACCEPT' => '', 'HTTP_ACCEPT_LANGUAGE' => '', 'HTTP_ACCEPT_ENCODING' => '', 'HTTP_COOKIE' => '', 'HTTP_CONNECTION' => '', 'REMOTE_ADDR' => '', 'REMOTE_PORT' => '0');
     // 将header分割成数组
     list($http_header, $http_body) = explode("\r\n\r\n", $recv_buffer, 2);
     $header_data = explode("\r\n", $http_header);
     list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);
     unset($header_data[0]);
     foreach ($header_data as $content) {
         // \r\n\r\n
         if (empty($content)) {
             continue;
         }
         list($key, $value) = explode(':', $content, 2);
         $key = strtolower($key);
         $value = trim($value);
         switch ($key) {
             // HTTP_HOST
             case 'host':
                 $_SERVER['HTTP_HOST'] = $value;
                 $tmp = explode(':', $value);
                 $_SERVER['SERVER_NAME'] = $tmp[0];
                 if (isset($tmp[1])) {
                     $_SERVER['SERVER_PORT'] = $tmp[1];
                 }
                 break;
                 // cookie
             // cookie
             case 'cookie':
                 $_SERVER['HTTP_COOKIE'] = $value;
                 parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
                 break;
                 // user-agent
             // user-agent
             case 'user-agent':
                 $_SERVER['HTTP_USER_AGENT'] = $value;
                 break;
                 // accept
             // accept
             case 'accept':
                 $_SERVER['HTTP_ACCEPT'] = $value;
                 break;
                 // accept-language
             // accept-language
             case 'accept-language':
                 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $value;
                 break;
                 // accept-encoding
             // accept-encoding
             case 'accept-encoding':
                 $_SERVER['HTTP_ACCEPT_ENCODING'] = $value;
                 break;
                 // connection
             // connection
             case 'connection':
                 $_SERVER['HTTP_CONNECTION'] = $value;
                 break;
             case 'referer':
                 $_SERVER['HTTP_REFERER'] = $value;
                 break;
             case 'if-modified-since':
                 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $value;
                 break;
             case 'if-none-match':
                 $_SERVER['HTTP_IF_NONE_MATCH'] = $value;
                 break;
             case 'content-type':
                 if (!preg_match('/boundary="?(\\S+)"?/', $value, $match)) {
                     $_SERVER['CONTENT_TYPE'] = $value;
                 } else {
                     $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
                     $http_post_boundary = '--' . $match[1];
                 }
                 break;
         }
     }
     // 需要解析$_POST
     if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         if (isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data') {
             self::parseUploadFiles($http_body, $http_post_boundary);
         } else {
             parse_str($http_body, $_POST);
             // $GLOBALS['HTTP_RAW_POST_DATA']
             $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
         }
     }
     // QUERY_STRING
     $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
     if ($_SERVER['QUERY_STRING']) {
         // $GET
         parse_str($_SERVER['QUERY_STRING'], $_GET);
     } else {
         $_SERVER['QUERY_STRING'] = '';
     }
     // REQUEST
     $_REQUEST = array_merge($_GET, $_POST);
     // REMOTE_ADDR REMOTE_PORT
     $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
     $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
     return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES);
 }
Пример #7
0
 /**
  * Websocket handshake.
  *
  * @param string                              $buffer
  * @param \Workerman\Connection\TcpConnection $connection
  * @return int
  */
 protected static function dealHandshake($buffer, $connection)
 {
     // HTTP protocol.
     if (0 === strpos($buffer, 'GET')) {
         // Find \r\n\r\n.
         $heder_end_pos = strpos($buffer, "\r\n\r\n");
         if (!$heder_end_pos) {
             return 0;
         }
         $header_length = $heder_end_pos + 4;
         // Get Sec-WebSocket-Key.
         $Sec_WebSocket_Key = '';
         if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
             $Sec_WebSocket_Key = $match[1];
         } else {
             $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Sec-WebSocket-Key not found.<br>This is a WebSocket service and can not be accessed via HTTP.", true);
             $connection->close();
             return 0;
         }
         // Calculation websocket key.
         $new_key = base64_encode(sha1($Sec_WebSocket_Key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
         // Handshake response data.
         $handshake_message = "HTTP/1.1 101 Switching Protocols\r\n";
         $handshake_message .= "Upgrade: websocket\r\n";
         $handshake_message .= "Sec-WebSocket-Version: 13\r\n";
         $handshake_message .= "Connection: Upgrade\r\n";
         $handshake_message .= "Server: workerman/" . Worker::VERSION . "\r\n";
         $handshake_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
         // Mark handshake complete..
         $connection->websocketHandshake = true;
         // Websocket data buffer.
         $connection->websocketDataBuffer = '';
         // Current websocket frame length.
         $connection->websocketCurrentFrameLength = 0;
         // Current websocket frame data.
         $connection->websocketCurrentFrameBuffer = '';
         // Consume handshake data.
         $connection->consumeRecvBuffer($header_length);
         // Send handshake response.
         $connection->send($handshake_message, true);
         // There are data waiting to be sent.
         if (!empty($connection->tmpWebsocketData)) {
             $connection->send($connection->tmpWebsocketData, true);
             $connection->tmpWebsocketData = '';
         }
         // blob or arraybuffer
         if (empty($connection->websocketType)) {
             $connection->websocketType = self::BINARY_TYPE_BLOB;
         }
         // Try to emit onWebSocketConnect callback.
         if (isset($connection->onWebSocketConnect)) {
             self::parseHttpHeader($buffer);
             try {
                 call_user_func($connection->onWebSocketConnect, $connection, $buffer);
             } catch (\Exception $e) {
                 Worker::log($e);
                 exit(250);
             } catch (\Error $e) {
                 Worker::log($e);
                 exit(250);
             }
             if (!empty($_SESSION) && class_exists('\\GatewayWorker\\Lib\\Context')) {
                 $connection->session = \GatewayWorker\Lib\Context::sessionEncode($_SESSION);
             }
             $_GET = $_SERVER = $_SESSION = $_COOKIE = array();
         }
         if (strlen($buffer) > $header_length) {
             return self::input(substr($buffer, $header_length), $connection);
         }
         return 0;
     } elseif (0 === strpos($buffer, '<polic')) {
         $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>' . "";
         $connection->send($policy_xml, true);
         $connection->consumeRecvBuffer(strlen($buffer));
         return 0;
     }
     // Bad websocket handshake request.
     $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n<b>400 Bad Request</b><br>Invalid handshake data for websocket. ", true);
     $connection->close();
     return 0;
 }
Пример #8
0
 /**
  * 当worker发来数据时
  * @param TcpConnection $connection
  * @param mixed $data
  * @throws \Exception
  */
 public function onWorkerMessage($connection, $data)
 {
     $cmd = $data['cmd'];
     switch ($cmd) {
         // 向某客户端发送数据,Gateway::sendToClient($client_id, $message);
         case GatewayProtocol::CMD_SEND_TO_ONE:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->send($data['body']);
             }
             break;
             // 关闭客户端连接,Gateway::closeClient($client_id);
         // 关闭客户端连接,Gateway::closeClient($client_id);
         case GatewayProtocol::CMD_KICK:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->destroy();
             }
             break;
             // 广播, Gateway::sendToAll($message, $client_id_array)
         // 广播, Gateway::sendToAll($message, $client_id_array)
         case GatewayProtocol::CMD_SEND_TO_ALL:
             // $client_id_array不为空时,只广播给$client_id_array指定的客户端
             if ($data['ext_data']) {
                 $client_id_array = unpack('N*', $data['ext_data']);
                 foreach ($client_id_array as $client_id) {
                     if (isset($this->_clientConnections[$client_id])) {
                         $this->_clientConnections[$client_id]->send($data['body']);
                     }
                 }
             } else {
                 foreach ($this->_clientConnections as $client_connection) {
                     $client_connection->send($data['body']);
                 }
             }
             break;
             // 更新客户端session
         // 更新客户端session
         case GatewayProtocol::CMD_UPDATE_SESSION:
             if (isset($this->_clientConnections[$data['client_id']])) {
                 $this->_clientConnections[$data['client_id']]->session = $data['ext_data'];
             }
             break;
             // 获得客户端在线状态 Gateway::getOnlineStatus()
         // 获得客户端在线状态 Gateway::getOnlineStatus()
         case GatewayProtocol::CMD_GET_ONLINE_STATUS:
             $online_status = json_encode(array_keys($this->_clientConnections));
             $connection->send($online_status);
             break;
             // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         case GatewayProtocol::CMD_IS_ONLINE:
             $connection->send((int) isset($this->_clientConnections[$data['client_id']]));
             break;
             // 将client_id与uid绑定
         // 将client_id与uid绑定
         case GatewayProtocol::CMD_BIND_UID:
             $uid = $data['ext_data'];
             if (empty($uid)) {
                 echo "uid empty" . var_export($uid, true);
                 return;
             }
             $client_id = $data['client_id'];
             if (!isset($this->_clientConnections[$client_id])) {
                 return;
             }
             $client_connection = $this->_clientConnections[$client_id];
             if (!isset($client_connection->uids)) {
                 $client_connection->uids = array();
             }
             $client_connection->uids[$uid] = $uid;
             $this->_uidConnections[$uid][$client_id] = $client_connection;
             break;
             // 发送数据给uid
         // 发送数据给uid
         case GatewayProtocol::CMD_SEND_TO_UID:
             $uid_array = json_decode($data['ext_data'], true);
             foreach ($uid_array as $uid) {
                 if (!empty($this->_uidConnections[$uid])) {
                     foreach ($this->_uidConnections[$uid] as $connection) {
                         $connection->send($data['body']);
                     }
                 }
             }
             break;
         default:
             $err_msg = "gateway inner pack err cmd={$cmd}";
             throw new \Exception($err_msg);
     }
 }
Пример #9
0
 /**
  * 覆盖原workerman流程,实现更多功能
  * 当接收到完整的http请求后的处理逻辑
  *
  * 1、如果请求的是以php为后缀的文件,则尝试加载
  * 2、如果请求的url没有后缀,则尝试加载对应目录的index.php
  * 3、如果请求的是非php为后缀的文件,尝试读取原始数据并发送
  * 4、如果请求的文件不存在,则返回404
  *
  * @param TcpConnection $connection
  * @param mixed         $data
  * @return mixed
  */
 public function onMessage($connection, $data)
 {
     Base::getLog()->debug(__METHOD__ . ' receive http request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort(), 'data' => $data]);
     // 请求的文件
     $urlInfo = parse_url($_SERVER['REQUEST_URI']);
     if (!$urlInfo) {
         Base::getHttp()->header('HTTP/1.1 400 Bad Request');
         Base::getLog()->warning(__METHOD__ . ' receive bad request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort()]);
         return $connection->close($this->error400);
     }
     $path = $urlInfo['path'];
     $pathInfo = pathinfo($path);
     $extension = isset($pathInfo['extension']) ? $pathInfo['extension'] : '';
     if ($extension === '') {
         $path = ($len = strlen($path)) && $path[$len - 1] === '/' ? $path . $this->indexFile : $path . '/' . $this->indexFile;
         $extension = 'php';
     }
     $serverName = Arr::get($_SERVER, 'SERVER_NAME');
     $rootDir = isset($this->serverRoot[$serverName]) ? $this->serverRoot[$serverName] : current($this->serverRoot);
     $file = "{$rootDir}/{$path}";
     // 对应的php文件不存在,而且支持rewrite
     if (!is_file($file) && $this->rewrite) {
         $file = is_string($this->rewrite) ? $rootDir . '/' . $this->rewrite : $rootDir . '/' . $this->indexFile;
         $extension = 'php';
         $_SERVER['PATH_INFO'] = $_SERVER['REQUEST_URI'];
     }
     // 请求的文件存在
     if (is_file($file)) {
         Base::getLog()->debug(__METHOD__ . ' request file existed', ['file' => $file, 'extension' => $extension]);
         // 判断是否是站点目录里的文件
         if (!($requestRealPath = realpath($file)) || !($rootDirRealPath = realpath($rootDir)) || 0 !== strpos($requestRealPath, $rootDirRealPath)) {
             Base::getHttp()->header('HTTP/1.1 400 Bad Request');
             Base::getLog()->warning(__METHOD__ . ' receive bad request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort()]);
             return $connection->close('<h1>400 Bad Request</h1>');
         }
         $file = realpath($file);
         // 如果请求的是php文件
         // PHP文件需要include
         if ($extension === 'php') {
             Base::getLog()->debug(__METHOD__ . ' handle request', ['uri' => $_SERVER['REQUEST_URI'], 'ip' => $connection->getRemoteIp(), 'port' => $connection->getRemotePort(), 'file' => $file]);
             Base::getLog()->debug(__METHOD__ . ' clean components - start');
             Base::cleanComponents();
             Base::getLog()->debug(__METHOD__ . ' clean components - end');
             $cwd = getcwd();
             chdir($rootDir);
             ini_set('display_errors', 'off');
             // 缓冲输出
             ob_start();
             // 载入php文件
             try {
                 // $_SERVER变量
                 $_SERVER['HOME'] = $_SERVER['DOCUMENT_ROOT'] = dirname($file);
                 $_SERVER['SCRIPT_FILENAME'] = $file;
                 Base::getLog()->debug(__METHOD__ . ' dispatch client info', ['ip' => $_SERVER['REMOTE_ADDR'], 'port' => $_SERVER['REMOTE_PORT']]);
                 include $file;
             } catch (Exception $e) {
                 Base::getLog()->error($e->getMessage(), ['code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine()]);
                 // 如果不是exit
                 if ($e->getMessage() != 'jump_exit') {
                     echo $e;
                 }
             }
             Patch::applyShutdownFunction();
             $content = ob_get_clean();
             ini_set('display_errors', 'on');
             $result = $connection->close($content);
             chdir($cwd);
             return $result;
         } else {
             $contentType = Mime::getMimeFromExtension($extension, self::$defaultMimeType);
             Base::getLog()->debug(__METHOD__ . ' get static file content type', ['extension' => $extension, 'contentType' => $contentType]);
             Base::getHttp()->header('Content-Type: ' . $contentType);
             // 获取文件信息
             $info = stat($file);
             $modifiedTime = $info ? date('D, d M Y H:i:s', Arr::get($info, 'mtime')) . ' GMT' : '';
             // 如果有$_SERVER['HTTP_IF_MODIFIED_SINCE']
             if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $info) {
                 // 文件没有更改则直接304
                 if ($modifiedTime === $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
                     Base::getLog()->debug(__METHOD__ . ' no modified, return 304');
                     // 304
                     Base::getHttp()->header('HTTP/1.1 304 Not Modified');
                     // 发送给客户端
                     return $connection->close('');
                 }
             }
             if ($modifiedTime) {
                 Base::getLog()->debug(__METHOD__ . ' set last modified time', ['time' => $modifiedTime]);
                 Base::getHttp()->header("Last-Modified: {$modifiedTime}");
             }
             // 发送给客户端
             return $connection->close(file_get_contents($file));
         }
     } else {
         Base::getLog()->warning(__METHOD__ . ' requested file not found', ['file' => $file]);
         // 404
         Base::getHttp()->header("HTTP/1.1 404 Not Found");
         return $connection->close($this->error404);
     }
 }
Пример #10
0
 /**
  * This method pulls all the data out of a readable stream, and writes it to the supplied destination.
  *
  * @param TcpConnection $dest
  * @return void
  */
 public function pipe($dest)
 {
     $source = $this;
     $this->onMessage = function ($source, $data) use($dest) {
         $dest->send($data);
     };
     $this->onClose = function ($source) use($dest) {
         $dest->destroy();
     };
     $dest->onBufferFull = function ($dest) use($source) {
         $source->pauseRecv();
     };
     $dest->onBufferDrain = function ($dest) use($source) {
         $source->resumeRecv();
     };
 }
Пример #11
0
 /**
  * 当worker发来数据时
  * @param TcpConnection $connection
  * @param mixed $data
  * @throws \Exception
  */
 public function onWorkerMessage($connection, $data)
 {
     $cmd = $data['cmd'];
     switch ($cmd) {
         case GatewayProtocol::CMD_WORKER_CONNECT:
             $connection->remoteAddress = $connection->getRemoteIp() . ':' . $connection->getRemotePort();
             $this->_workerConnections[$connection->remoteAddress] = $connection;
             return;
             // 向某客户端发送数据,Gateway::sendToClient($client_id, $message);
         // 向某客户端发送数据,Gateway::sendToClient($client_id, $message);
         case GatewayProtocol::CMD_SEND_TO_ONE:
             if (isset($this->_clientConnections[$data['connection_id']])) {
                 $this->_clientConnections[$data['connection_id']]->send($data['body']);
             }
             return;
             // 关闭客户端连接,Gateway::closeClient($client_id);
         // 关闭客户端连接,Gateway::closeClient($client_id);
         case GatewayProtocol::CMD_KICK:
             if (isset($this->_clientConnections[$data['connection_id']])) {
                 $this->_clientConnections[$data['connection_id']]->destroy();
             }
             return;
             // 广播, Gateway::sendToAll($message, $client_id_array)
         // 广播, Gateway::sendToAll($message, $client_id_array)
         case GatewayProtocol::CMD_SEND_TO_ALL:
             // $client_id_array不为空时,只广播给$client_id_array指定的客户端
             if ($data['ext_data']) {
                 $connection_id_array = unpack('N*', $data['ext_data']);
                 foreach ($connection_id_array as $connection_id) {
                     if (isset($this->_clientConnections[$connection_id])) {
                         $this->_clientConnections[$connection_id]->send($data['body']);
                     }
                 }
             } else {
                 foreach ($this->_clientConnections as $client_connection) {
                     $client_connection->send($data['body']);
                 }
             }
             return;
             // 更新客户端session
         // 更新客户端session
         case GatewayProtocol::CMD_UPDATE_SESSION:
             if (isset($this->_clientConnections[$data['connection_id']])) {
                 $this->_clientConnections[$data['connection_id']]->session = $data['ext_data'];
             }
             return;
             // 获得客户端在线状态 Gateway::getALLClientInfo()
         // 获得客户端在线状态 Gateway::getALLClientInfo()
         case GatewayProtocol::CMD_GET_ALL_CLIENT_INFO:
             $client_info_array = array();
             foreach ($this->_clientConnections as $connection_id => $client_connection) {
                 $client_info_array[$connection_id] = $client_connection->session;
             }
             $connection->send(json_encode($client_info_array) . "\n", true);
             return;
             // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         // 判断某个client_id是否在线 Gateway::isOnline($client_id)
         case GatewayProtocol::CMD_IS_ONLINE:
             $connection->send((int) isset($this->_clientConnections[$data['connection_id']]) . "\n", true);
             return;
             // 将client_id与uid绑定
         // 将client_id与uid绑定
         case GatewayProtocol::CMD_BIND_UID:
             $uid = $data['ext_data'];
             if (empty($uid)) {
                 echo "uid empty" . var_export($uid, true);
                 return;
             }
             $connection_id = $data['connection_id'];
             if (!isset($this->_clientConnections[$connection_id])) {
                 return;
             }
             $client_connection = $this->_clientConnections[$connection_id];
             if (isset($client_connection->uid)) {
                 $current_uid = $client_connection->uid;
                 unset($this->_uidConnections[$current_uid][$connection_id]);
                 if (empty($this->_uidConnections[$current_uid])) {
                     unset($this->_uidConnections[$current_uid]);
                 }
             }
             $client_connection->uid = $uid;
             $this->_uidConnections[$uid][$connection_id] = $client_connection;
             return;
             // client_id与uid解绑 Gateway::unbindUid($client_id, $uid);
         // client_id与uid解绑 Gateway::unbindUid($client_id, $uid);
         case GatewayProtocol::CMD_UNBIND_UID:
             $connection_id = $data['connection_id'];
             if (!isset($this->_clientConnections[$connection_id])) {
                 return;
             }
             $client_connection = $this->_clientConnections[$connection_id];
             if (isset($client_connection->uid)) {
                 $current_uid = $client_connection->uid;
                 unset($this->_uidConnections[$current_uid][$connection_id]);
                 if (empty($this->_uidConnections[$current_uid])) {
                     unset($this->_uidConnections[$current_uid]);
                 }
                 $client_connection->uid_info = '';
                 $client_connection->uid = null;
             }
             return;
             // 发送数据给uid Gateway::sendToUid($uid, $msg);
         // 发送数据给uid Gateway::sendToUid($uid, $msg);
         case GatewayProtocol::CMD_SEND_TO_UID:
             $uid_array = json_decode($data['ext_data'], true);
             foreach ($uid_array as $uid) {
                 if (!empty($this->_uidConnections[$uid])) {
                     foreach ($this->_uidConnections[$uid] as $connection) {
                         $connection->send($data['body']);
                     }
                 }
             }
             return;
             // 将$client_id加入用户组 Gateway::joinGroup($client_id, $group);
         // 将$client_id加入用户组 Gateway::joinGroup($client_id, $group);
         case GatewayProtocol::CMD_JOIN_GROUP:
             $group = $data['ext_data'];
             if (empty($group)) {
                 echo "group empty" . var_export($group, true);
                 return;
             }
             $connection_id = $data['connection_id'];
             if (!isset($this->_clientConnections[$connection_id])) {
                 return;
             }
             $client_connection = $this->_clientConnections[$connection_id];
             if (!isset($client_connection->groups)) {
                 $client_connection->groups = array();
             }
             $client_connection->groups[$group] = $group;
             $this->_groupConnections[$group][$connection_id] = $client_connection;
             return;
             // 将$client_id从某个用户组中移除 Gateway::leaveGroup($client_id, $group);
         // 将$client_id从某个用户组中移除 Gateway::leaveGroup($client_id, $group);
         case GatewayProtocol::CMD_LEAVE_GROUP:
             $group = $data['ext_data'];
             if (empty($group)) {
                 echo "leave group empty" . var_export($group, true);
                 return;
             }
             $connection_id = $data['connection_id'];
             if (!isset($this->_clientConnections[$connection_id])) {
                 return;
             }
             $client_connection = $this->_clientConnections[$connection_id];
             if (!isset($client_connection->groups[$group])) {
                 return;
             }
             unset($client_connection->groups[$group], $this->_groupConnections[$group][$connection_id]);
             return;
             // 向某个用户组发送消息 Gateway::sendToGroup($group, $msg);
         // 向某个用户组发送消息 Gateway::sendToGroup($group, $msg);
         case GatewayProtocol::CMD_SEND_TO_GROUP:
             $group_array = json_decode($data['ext_data'], true);
             foreach ($group_array as $group) {
                 if (!empty($this->_groupConnections[$group])) {
                     foreach ($this->_groupConnections[$group] as $connection) {
                         $connection->send($data['body']);
                     }
                 }
             }
             return;
             // 获取某用户组成员信息 Gateway::getClientInfoByGroup($group);
         // 获取某用户组成员信息 Gateway::getClientInfoByGroup($group);
         case GatewayProtocol::CMD_GET_CLINET_INFO_BY_GROUP:
             $group = $data['ext_data'];
             if (!isset($this->_groupConnections[$group])) {
                 $connection->send("[]\n", true);
                 return;
             }
             $client_info_array = array();
             foreach ($this->_groupConnections[$group] as $connection_id => $client_connection) {
                 $client_info_array[$connection_id] = $client_connection->session;
             }
             $connection->send(json_encode($client_info_array) . "\n", true);
             return;
             // 获取用户组成员数 Gateway::getClientCountByGroup($group);
         // 获取用户组成员数 Gateway::getClientCountByGroup($group);
         case GatewayProtocol::CMD_GET_CLIENT_COUNT_BY_GROUP:
             $group = $data['ext_data'];
             if (!isset($this->_groupConnections[$group])) {
                 $connection->send("0\n", true);
                 return;
             }
             $connection->send(count($this->_groupConnections[$group]) . "\n", true);
             return;
             // 获取与某个uid绑定的所有client_id Gateway::getClientIdByUid($uid);
         // 获取与某个uid绑定的所有client_id Gateway::getClientIdByUid($uid);
         case GatewayProtocol::CMD_GET_CLIENT_ID_BY_UID:
             $uid = $data['ext_data'];
             if (empty($this->_uidConnections[$uid])) {
                 $connection->send("[]\n", true);
                 return;
             }
             $connection->send(json_encode(array_keys($this->_uidConnections[$uid])) . "\n", true);
             return;
         default:
             $err_msg = "gateway inner pack err cmd={$cmd}";
             throw new \Exception($err_msg);
     }
 }
Пример #12
0
 /**
  * Parse $_POST、$_GET、$_COOKIE.
  *
  * @param string        $recv_buffer
  * @param TcpConnection $connection
  * @return array
  */
 public static function decode($recv_buffer, TcpConnection $connection)
 {
     // Init.
     $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array();
     $GLOBALS['HTTP_RAW_POST_DATA'] = '';
     // Clear cache.
     HttpCache::$header = array('Connection' => 'Connection: keep-alive');
     HttpCache::$instance = new HttpCache();
     // $_SERVER
     $_SERVER = array('QUERY_STRING' => '', 'REQUEST_METHOD' => '', 'REQUEST_URI' => '', 'SERVER_PROTOCOL' => '', 'SERVER_SOFTWARE' => 'workerman/' . Worker::VERSION, 'SERVER_NAME' => '', 'HTTP_HOST' => '', 'HTTP_USER_AGENT' => '', 'HTTP_ACCEPT' => '', 'HTTP_ACCEPT_LANGUAGE' => '', 'HTTP_ACCEPT_ENCODING' => '', 'HTTP_COOKIE' => '', 'HTTP_CONNECTION' => '', 'REMOTE_ADDR' => '', 'REMOTE_PORT' => '0');
     // Parse headers.
     list($http_header, $http_body) = explode("\r\n\r\n", $recv_buffer, 2);
     $header_data = explode("\r\n", $http_header);
     list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);
     $http_post_boundary = '';
     unset($header_data[0]);
     foreach ($header_data as $content) {
         // \r\n\r\n
         if (empty($content)) {
             continue;
         }
         list($key, $value) = explode(':', $content, 2);
         $key = str_replace('-', '_', strtoupper($key));
         $value = trim($value);
         $_SERVER['HTTP_' . $key] = $value;
         switch ($key) {
             // HTTP_HOST
             case 'HOST':
                 $tmp = explode(':', $value);
                 $_SERVER['SERVER_NAME'] = $tmp[0];
                 if (isset($tmp[1])) {
                     $_SERVER['SERVER_PORT'] = $tmp[1];
                 }
                 break;
                 // cookie
             // cookie
             case 'COOKIE':
                 parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
                 break;
                 // content-type
             // content-type
             case 'CONTENT_TYPE':
                 if (!preg_match('/boundary="?(\\S+)"?/', $value, $match)) {
                     $_SERVER['CONTENT_TYPE'] = $value;
                 } else {
                     $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
                     $http_post_boundary = '--' . $match[1];
                 }
                 break;
             case 'CONTENT_LENGTH':
                 $_SERVER['CONTENT_LENGTH'] = $value;
                 break;
         }
     }
     // Parse $_POST.
     if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         if (isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data') {
             self::parseUploadFiles($http_body, $http_post_boundary);
         } else {
             parse_str($http_body, $_POST);
             // $GLOBALS['HTTP_RAW_POST_DATA']
             $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
         }
     }
     // QUERY_STRING
     $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
     if ($_SERVER['QUERY_STRING']) {
         // $GET
         parse_str($_SERVER['QUERY_STRING'], $_GET);
     } else {
         $_SERVER['QUERY_STRING'] = '';
     }
     // REQUEST
     $_REQUEST = array_merge($_GET, $_POST);
     // REMOTE_ADDR REMOTE_PORT
     $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
     $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
     return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES);
 }
Пример #13
0
 /**
  * Send websocket handshake.
  *
  * @param \Workerman\Connection\TcpConnection $connection
  * @return void 
  */
 public static function sendHandshake($connection)
 {
     if (!empty($connection->handshakeStep)) {
         return;
     }
     // Get Host.
     $port = $connection->getRemotePort();
     $host = $port === 80 ? $connection->getRemoteHost() : $connection->getRemoteHost() . ':' . $port;
     // Handshake header.
     $header = 'GET ' . $connection->getRemoteURI() . " HTTP/1.1\r\n" . "Host: {$host}\r\n" . "Connection: Upgrade\r\n" . "Upgrade: websocket\r\n" . "Origin: " . (isset($connection->websocketOrigin) ? $connection->websocketOrigin : '*') . "\r\n" . "Sec-WebSocket-Version: 13\r\n" . "Sec-WebSocket-Key: " . base64_encode(sha1(uniqid(mt_rand(), true), true)) . "\r\n\r\n";
     $connection->send($header, true);
     $connection->handshakeStep = 1;
     $connection->websocketCurrentFrameLength = 0;
     $connection->websocketDataBuffer = '';
     $connection->tmpWebsocketData = '';
 }
Пример #14
0
 /**
  * 从http数据包中解析$_POST、$_GET、$_COOKIE等
  *
  * @param string        $recv_buffer
  * @param TcpConnection $connection
  * @return array
  */
 public static function decode($recv_buffer, TcpConnection $connection)
 {
     // 初始化
     Base::getLog()->debug(__METHOD__ . ' clean global variables');
     $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = [];
     $GLOBALS['HTTP_RAW_POST_DATA'] = '';
     $userInfo = posix_getpwuid(posix_getuid());
     $_SERVER = ['USER' => Arr::get($userInfo, 'name', ''), 'HOME' => '', 'QUERY_STRING' => '', 'REQUEST_METHOD' => '', 'REQUEST_URI' => '', 'SERVER_PROTOCOL' => '', 'SERVER_SOFTWARE' => 'tourze/' . Base::VERSION, 'SERVER_NAME' => '', 'SCRIPT_FILENAME' => '', 'HTTP_HOST' => '', 'HTTP_USER_AGENT' => '', 'HTTP_ACCEPT' => '', 'HTTP_ACCEPT_LANGUAGE' => '', 'HTTP_ACCEPT_ENCODING' => '', 'HTTP_COOKIE' => '', 'HTTP_CONNECTION' => '', 'REMOTE_ADDR' => '', 'REMOTE_PORT' => '0', 'REQUEST_TIME' => time()];
     $_SERVER['REQUEST_TIME_FLOAT'] = $_SERVER['REQUEST_TIME'] . substr((string) microtime(), 1, 5);
     Base::getLog()->debug(__METHOD__ . ' clean previous headers');
     // 清空上次的数据
     HttpCache::$header = ['Connection: keep-alive'];
     HttpCache::$instance = new HttpCache();
     // 将header分割成数组
     list($httpHeader, $httpBody) = explode("\r\n\r\n", $recv_buffer, 2);
     $headerData = explode("\r\n", $httpHeader);
     // 第一行为比较重要的一行
     $firstLine = array_shift($headerData);
     list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $firstLine);
     $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['DOCUMENT_URI'] = $_SERVER['REQUEST_URI'];
     Base::getLog()->debug(__METHOD__ . ' receive http request', ['method' => $_SERVER['REQUEST_METHOD'], 'uri' => $_SERVER['REQUEST_URI'], 'protocol' => $_SERVER['SERVER_PROTOCOL']]);
     $httpPostBoundary = '';
     foreach ($headerData as $content) {
         // \r\n\r\n
         if (empty($content)) {
             continue;
         }
         list($key, $value) = explode(':', $content, 2);
         $key = strtolower($key);
         $value = trim($value);
         switch ($key) {
             // HTTP_HOST
             case 'host':
                 $_SERVER['HTTP_HOST'] = $value;
                 $tmp = explode(':', $value);
                 $_SERVER['SERVER_NAME'] = $tmp[0];
                 if (isset($tmp[1])) {
                     $_SERVER['SERVER_PORT'] = $tmp[1];
                 }
                 break;
                 // cookie
             // cookie
             case 'cookie':
                 $_SERVER['HTTP_COOKIE'] = $value;
                 parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
                 break;
                 // user-agent
             // user-agent
             case 'user-agent':
                 $_SERVER['HTTP_USER_AGENT'] = $value;
                 break;
                 // accept
             // accept
             case 'accept':
                 $_SERVER['HTTP_ACCEPT'] = $value;
                 break;
                 // accept-language
             // accept-language
             case 'accept-language':
                 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $value;
                 break;
                 // accept-encoding
             // accept-encoding
             case 'accept-encoding':
                 $_SERVER['HTTP_ACCEPT_ENCODING'] = $value;
                 break;
                 // connection
             // connection
             case 'connection':
                 $_SERVER['HTTP_CONNECTION'] = $value;
                 break;
             case 'referer':
                 $_SERVER['HTTP_REFERER'] = $value;
                 break;
             case 'if-modified-since':
                 $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $value;
                 break;
             case 'if-none-match':
                 $_SERVER['HTTP_IF_NONE_MATCH'] = $value;
                 break;
             case 'content-type':
                 if (!preg_match('/boundary="?(\\S+)"?/', $value, $match)) {
                     $_SERVER['CONTENT_TYPE'] = $value;
                 } else {
                     $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
                     $httpPostBoundary = '--' . $match[1];
                 }
                 break;
         }
     }
     // 需要解析$_POST
     if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         if (isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data') {
             self::parseUploadFiles($httpBody, $httpPostBoundary);
         } else {
             parse_str($httpBody, $_POST);
             $GLOBALS['HTTP_RAW_POST_DATA'] = $httpBody;
         }
     }
     // QUERY_STRING
     $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
     if ($_SERVER['QUERY_STRING']) {
         // $GET
         parse_str($_SERVER['QUERY_STRING'], $_GET);
     } else {
         $_SERVER['QUERY_STRING'] = '';
     }
     // REQUEST
     $_REQUEST = array_merge($_GET, $_POST);
     // REMOTE_ADDR REMOTE_PORT
     $_SERVER['REMOTE_ADDR'] = $connection->getRemoteIp();
     $_SERVER['REMOTE_PORT'] = $connection->getRemotePort();
     // 这两处要怎么读取呢?
     $_SERVER['SERVER_ADDR'] = null;
     $_SERVER['SERVER_PORT'] = null;
     $result = ['get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES];
     Base::getLog()->debug(__METHOD__ . ' get request data', $result);
     return $result;
 }