/** * Check the integrity of the package. * * @param string $recv_buffer * @param TcpConnection $connection * @return int */ public static function input($recv_buffer, TcpConnection $connection) { if (!strpos($recv_buffer, "\r\n\r\n")) { // Judge whether the package length exceeds the limit. if (strlen($recv_buffer) >= TcpConnection::$maxPackageSize) { $connection->close(); return 0; } return 0; } list($header, ) = explode("\r\n\r\n", $recv_buffer, 2); if (0 === strpos($recv_buffer, "POST")) { // find Content-Length $match = array(); if (preg_match("/\r\nContent-Length: ?(\\d+)/i", $header, $match)) { $content_length = $match[1]; return $content_length + strlen($header) + 4; } else { return 0; } } elseif (0 === strpos($recv_buffer, "GET")) { return strlen($header) + 4; } else { $connection->send("HTTP/1.1 400 Bad Request\r\n\r\n", true); return 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; }
/** * 检查包的完整性 * 如果能够得到包长,则返回包的长度,否则返回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; }
/** * 判断包长 * @param string $recv_buffer * @param TcpConnection $connection * @return int */ public static function input($recv_buffer, TcpConnection $connection) { if (!strpos($recv_buffer, "\r\n\r\n")) { // 无法获得包长,避免客户端传递超大头部的数据包 if (strlen($recv_buffer) >= TcpConnection::$maxPackageSize) { $connection->close(); return 0; } return 0; } list($header, $body) = explode("\r\n\r\n", $recv_buffer, 2); if (0 === strpos($recv_buffer, "POST")) { // find Content-Length $match = array(); if (preg_match("/\r\nContent-Length: ?(\\d+)/", $header, $match)) { $content_lenght = $match[1]; return $content_lenght + strlen($header) + 4; } else { return 0; } } else { return strlen($header) + 4; } }
/** * 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; } }
/** * 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; }
/** * 覆盖原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); } }
/** * 判断数据包长度 * @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; }