/** * Send the header * @param string Header. Example: 'Location: http://php.net/' * @param boolean Optional. Replace? * @param int Optional. HTTP response code. * @return void * @throws RequestHeadersAlreadySent */ public function header($s, $replace = true, $code = false) { if ($code !== null) { $this->status($code); } if ($this->headers_sent) { throw new RequestHeadersAlreadySent(); return false; } $e = explode(':', $s, 2); if (!isset($e[1])) { $e[0] = 'STATUS'; if (strncmp($s, 'HTTP/', 5) === 0) { $s = substr($s, 9); } } $k = strtr(strtoupper($e[0]), HTTPRequest::$htr); if ($k === 'CONTENT_TYPE') { HTTPRequest::parse_str(strtr(strtolower($e[1]), HTTPRequest::$hvaltr), $ctype); if (!isset($ctype['charset'])) { $ctype['charset'] = $this->upstream->config->defaultcharset->value; $s = $e[0] . ': '; $i = 0; foreach ($ctype as $k => $v) { $s .= ($i > 0 ? '; ' : '') . $k . ($v !== '' ? '=' . $v : ''); ++$i; } } } if ($k === 'SET_COOKIE') { $k .= '_' . ++$this->cookieNum; } elseif (!$replace && isset($this->headers[$k])) { return false; } $this->headers[$k] = $s; if ($k === 'CONTENT_LENGTH') { $this->contentLength = (int) $e[1]; } elseif ($k === 'LOCATION') { $this->status(301); } if (Daemon::$compatMode) { is_callable('header_native') ? header_native($s) : header($s); } return true; }
/** * Called when new data received. * @param string New received data. * @return void */ public function stdin($buf) { $this->buf .= $buf; if ($this->state === self::STATE_ROOT) { if (strpos($this->buf, "<policy-file-request/>") !== FALSE) { $FP = FlashPolicyServer::getInstance(); if ($FP && $FP->policyData) { $this->write($FP->policyData . ""); } $this->finish(); return; } $i = 0; while (($l = $this->gets()) !== FALSE) { if ($i++ > 100) { break; } if ($l === "\r\n") { $this->state = self::STATE_HANDSHAKING; if (isset($this->server['HTTP_SEC_WEBSOCKET_EXTENSIONS'])) { $str = strtolower($this->server['HTTP_SEC_WEBSOCKET_EXTENSIONS']); $str = preg_replace($this->extensionsCleanRegex, '', $str); $this->extensions = explode(', ', $str); } if (!isset($this->server['HTTP_CONNECTION']) || !preg_match('~(?:^|\\W)Upgrade(?:\\W|$)~i', $this->server['HTTP_CONNECTION']) || !isset($this->server['HTTP_UPGRADE']) || strtolower($this->server['HTTP_UPGRADE']) !== 'websocket') { $this->finish(); return; } if (isset($this->server['HTTP_COOKIE'])) { HTTPRequest::parse_str(strtr($this->server['HTTP_COOKIE'], HTTPRequest::$hvaltr), $this->cookie); } // ---------------------------------------------------------- // Protocol discovery, based on HTTP headers... // ---------------------------------------------------------- if (isset($this->server['HTTP_SEC_WEBSOCKET_VERSION'])) { // HYBI if ($this->server['HTTP_SEC_WEBSOCKET_VERSION'] == '8') { // Version 8 (FF7, Chrome14) $this->protocol = new WebSocketProtocolV13($this); } elseif ($this->server['HTTP_SEC_WEBSOCKET_VERSION'] == '13') { // newest protocol $this->protocol = new WebSocketProtocolV13($this); } else { Daemon::$process->log(get_class($this) . '::' . __METHOD__ . " : Websocket protocol version " . $this->server['HTTP_SEC_WEBSOCKET_VERSION'] . ' is not yet supported for client "' . $this->addr . '"'); $this->finish(); return; } } elseif (!isset($this->server['HTTP_SEC_WEBSOCKET_KEY1']) || !isset($this->server['HTTP_SEC_WEBSOCKET_KEY2'])) { $this->protocol = new WebSocketProtocolVE($this); } else { // Defaulting to HIXIE (Safari5 and many non-browser clients...) $this->protocol = new WebSocketProtocolV0($this); } // ---------------------------------------------------------- // End of protocol discovery // ---------------------------------------------------------- } elseif (!$this->firstline) { $this->firstline = true; $e = explode(' ', $l); $u = parse_url(isset($e[1]) ? $e[1] : ''); $this->server['REQUEST_METHOD'] = $e[0]; $this->server['REQUEST_URI'] = $u['path'] . (isset($u['query']) ? '?' . $u['query'] : ''); $this->server['DOCUMENT_URI'] = $u['path']; $this->server['PHP_SELF'] = $u['path']; $this->server['QUERY_STRING'] = isset($u['query']) ? $u['query'] : NULL; $this->server['SCRIPT_NAME'] = $this->server['DOCUMENT_URI'] = isset($u['path']) ? $u['path'] : '/'; $this->server['SERVER_PROTOCOL'] = isset($e[2]) ? trim($e[2]) : ''; list($this->server['REMOTE_ADDR'], $this->server['REMOTE_PORT']) = explode(':', $this->addr); } else { $e = explode(': ', $l); if (isset($e[1])) { $this->server['HTTP_' . strtoupper(strtr($e[0], HTTPRequest::$htr))] = rtrim($e[1], "\r\n"); } } } } if ($this->state === self::STATE_HANDSHAKING) { if (!$this->handshake($this->buf)) { return; } $this->buf = ''; $this->state = self::STATE_HANDSHAKED; } if ($this->state === self::STATE_HANDSHAKED) { if (!isset($this->protocol)) { Daemon::$process->log(get_class($this) . '::' . __METHOD__ . ' : Cannot find session-related websocket protocol for client "' . $this->addr . '"'); $this->finish(); return; } $this->protocol->onRead(); } }
/** * Event of SocketSession (AsyncServer). Called when new data received. * @param string New received data. * @return void */ public function stdin($buf) { $this->buf .= $buf; if (!$this->handshaked) { if (Daemon::$appResolver->checkAppEnabled('FlashPolicy')) { if (strpos($this->buf, '<policy-file-request/>') !== FALSE) { if (($FP = Daemon::$appResolver->getInstanceByAppName('FlashPolicy')) && $FP->policyData) { $this->write($FP->policyData . ""); } $this->finish(); return; } } $i = 0; while (($l = $this->gets()) !== FALSE) { if ($i++ > 100) { break; } if ($l === "\r\n") { if (!isset($this->server['HTTP_CONNECTION']) || $this->server['HTTP_CONNECTION'] !== 'Upgrade' || !isset($this->server['HTTP_UPGRADE']) || $this->server['HTTP_UPGRADE'] !== 'WebSocket') { $this->finish(); return; } if (isset($this->server['HTTP_COOKIE'])) { HTTPRequest::parse_str(strtr($this->server['HTTP_COOKIE'], HTTPRequest::$hvaltr), $this->cookie); } if (!($this->secprotocol = isset($this->server['HTTP_SEC_WEBSOCKET_KEY1']) && isset($this->server['HTTP_SEC_WEBSOCKET_KEY2']))) { $this->handshake(); } break; } if (!$this->firstline) { $this->firstline = TRUE; $e = explode(' ', $l); $u = parse_url(isset($e[1]) ? $e[1] : ''); $this->server['REQUEST_METHOD'] = $e[0]; $this->server['REQUEST_URI'] = $u['path'] . (isset($u['query']) ? '?' . $u['query'] : ''); $this->server['DOCUMENT_URI'] = $u['path']; $this->server['PHP_SELF'] = $u['path']; $this->server['QUERY_STRING'] = isset($u['query']) ? $u['query'] : NULL; $this->server['SCRIPT_NAME'] = $this->server['DOCUMENT_URI'] = isset($u['path']) ? $u['path'] : '/'; $this->server['SERVER_PROTOCOL'] = isset($e[2]) ? $e[2] : ''; list($this->server['REMOTE_ADDR'], $this->server['REMOTE_PORT']) = explode(':', $this->clientAddr); } else { $e = explode(': ', $l); if (isset($e[1])) { $this->server['HTTP_' . strtoupper(strtr($e[0], HTTPRequest::$htr))] = rtrim($e[1], "\r\n"); } } } } if ($this->handshaked) { while (($buflen = strlen($this->buf)) >= 2) { $frametype = ord(binarySubstr($this->buf, 0, 1)); if (($frametype & 0x80) === 0x80) { $len = 0; $i = 0; do { $b = ord(binarySubstr($this->buf, ++$i, 1)); $n = $b & 0x7f; $len *= 0x80; $len += $n; } while ($b > 0x80); if ($this->appInstance->config->maxallowedpacket->value <= $len) { // Too big packet $this->finish(); return; } if ($buflen < $len + 2) { // not enough data yet return; } $data = binarySubstr($this->buf, 2, $len); $this->buf = binarySubstr($this->buf, 2 + $len); $this->onFrame($data, $frametype); } else { if (($p = strpos($this->buf, "ÿ")) !== FALSE) { if ($this->appInstance->config->maxallowedpacket->value <= $p - 1) { // Too big packet $this->finish(); return; } $data = binarySubstr($this->buf, 1, $p - 1); $this->buf = binarySubstr($this->buf, $p + 1); $this->onFrame($data, $frametype); } else { // not enough data yet if ($this->appInstance->config->maxallowedpacket->value <= strlen($this->buf)) { // Too big packet $this->finish(); } return; } } } } elseif ($this->secprotocol) { if (strlen($this->buf) >= 8) { $bodyData = binarySubstr($this->buf, 0, 8); $this->resultKey = md5($this->computeKey($this->server['HTTP_SEC_WEBSOCKET_KEY1']) . $this->computeKey($this->server['HTTP_SEC_WEBSOCKET_KEY2']) . $bodyData, TRUE); $this->buf = binarySubstr($this->buf, 8); $this->handshake(); } } }