public function touch() { if (!$this->cursor || $this->cursor->destroyed) { $tag = $this; $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatevents'}->find(function ($cursor) use($tag) { $tag->cursor = $cursor; foreach ($cursor->items as $k => &$item) { if ($item['type'] === 'kickUsers') { foreach ($tag->sessions as $id => $v) { $sess = $tag->appInstance->sessions[$id]; if ($sess->username !== null && $tag->appInstance->compareMask($sess->username, $item['users'])) { $sess->removeTags(array($tag->tag), true); $sess->sysMsg('You were kicked from #' . $tag->tag . '.' . ($item['reason'] !== '' ? ' Reason: ' . $item['reason'] : '')); $sess->send(array('type' => 'youWereKicked', 'reason' => $item['reason'])); $tag->appInstance->broadcastEvent(array('type' => 'msg', 'mtype' => 'system', 'text' => ' Kicked: ' . $sess->username . ($item['reason'] !== '' ? ', reason: ' . $item['reason'] : ''), 'color' => 'green', 'tags' => $tag->tag)); } } } elseif ($item['type'] === 'forceChangeNick') { foreach ($tag->sessions as $id => $v) { $sess = $tag->appInstance->sessions[$id]; if ($sess->username !== null && $sess->username === $item['username']) { $sess->setUsername($item['changeto'], true); } } } else { $item['_id'] = (string) $item['_id']; if (isset($item['sid'])) { $item['sid'] = (string) $item['sid']; } $packet = Session::serialize($item); foreach ($tag->sessions as $id => $v) { $s = $tag->appInstance->sessions[$id]; if (is_string($item['tags'])) { $item['tags'] = array($item['tags']); } if (in_array('%private', $item['tags'])) { if (!isset($item['to'])) { continue; } if (!in_array($s->username, $item['to']) && $s->username != $item['from']) { continue; } } if ($s->putMsgId($item['_id'])) { $s->client->sendFrame($packet); } } } unset($cursor->items[$k]); } }, array('tailable' => true, 'sort' => array('$natural' => 1), 'where' => array('ts' => array('$gt' => microtime(true)), 'tags' => array('$in' => binarySubstr($this->tag, 0, 1) == '%' ? array($this->tag) : array($this->tag, '%all'))))); } elseif (!$this->cursor->session->busy) { try { $this->cursor->getMore(); } catch (ConnectionFinished $e) { $this->cursor = false; } } }
public function gets() { $p = strpos($this->buf, "\n"); if ($p === FALSE) { return FALSE; } $r = binarySubstr($this->buf, 0, $p + 1); $this->buf = binarySubstr($this->buf, $p + 1); return $r; }
/** * Read a first line ended with \n from buffer, removes it from buffer and returns the line * @return string Line. Returns false when failed to get a line */ public function gets() { $p = strpos($this->buf, $this->EOL); if ($p === FALSE) { return FALSE; } $sEOL = strlen($this->EOL); $r = binarySubstr($this->buf, 0, $p + $sEOL); $this->buf = binarySubstr($this->buf, $p + $sEOL); return $r; }
public function send($packet) { if (Daemon::$settings['logevents']) { Daemon::log(__METHOD__ . ' invoked (' . $this->clientAddr . '): ' . Daemon::var_dump($packet)); } if ($this->http) { $s = json_encode($packet); $l = strlen($s); for ($o = 0; $o < $l;) { $c = min(Daemon::$parsedSettings['chunksize'], $l - $o); $chunk = dechex($c) . "\r\n" . ($c === $l ? $s : binarySubstr($s, $o, $c)) . "\r\n"; $this->write($chunk); $o += $c; } } else { $this->writeln(json_encode($packet)); } }
public function onFrame($data, $type) { $packet = json_decode($data, true); if (!$packet) { return; } if (!isset($packet['cmd'])) { return; } $cmd = $packet['cmd']; if ($cmd === 'setUsername' && isset($packet['username'])) { if ($this->username !== null) { return; } //$this->setUsername($packet['username']); } elseif ($cmd === 'hello' && isset($packet['authkey'])) { if ($this->username !== null) { return; } $clientId = $this->client->connId; $appInstance = $this->appInstance; $appInstance->ORM->getAuthKey($packet['authkey'], function ($authkey) use($clientId, $appInstance, $packet) { if (!isset($appInstance->sessions[$clientId])) { return; } $session = $appInstance->sessions[$clientId]; if (!$authkey) { $session->send(array('type' => 'youWereKicked', 'reason' => 'Incorrect auth. data.')); return; } if (isset($authkey['su'])) { $session->su = $authkey['su']; } $session->authkey = $authkey; $appInstance->db->{$appInstance->config->dbname->value . '.akicks'}->findOne(function ($akick) use($authkey, $packet, $clientId, $appInstance) { if (!isset($appInstance->sessions[$clientId])) { return; } $session = $appInstance->sessions[$clientId]; if ($akick) { $session->send(array('type' => 'youWereKicked', 'reason' => $akick['reason'])); return; } $session->updateAvailTags(); Daemon::log('Incorrect auth. data.'); $session->setUsername($authkey['username'], false, $packet['tab']); }); }); } elseif ($cmd === 'getAvailTags') { $this->updateAvailTags($packet['_id']); } elseif ($cmd === 'setTags') { if ($this->username === null) { return; } $this->setTags($packet['tags']); } elseif ($cmd === 'setmuchatignore') { if ($this->username === null) { return; } $doc = array('username' => $this->username, 'blocked' => $packet['username']); if ($doc['username'] == $doc['blocked']) { return; } if ($packet['action']) { $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatignore'}->insert($doc); } else { $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatignore'}->remove($doc); } } elseif ($cmd === 'keepalive') { $this->updateSession(array('atime' => microtime(true))); } elseif ($cmd == 'getHistory') { if ($this->username === null) { return; } $session = $this; $condts = array('$lt' => microtime(true)); $lastTS = isset($packet['lastTS']) ? (double) $packet['lastTS'] : 0; if ($lastTS > 0) { $condts['$gt'] = $lastTS; } $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatevents'}->find(function ($cursor) use($session) { $tag->cursor = $cursor; $cursor->items = array_reverse($cursor->items); foreach ($cursor->items as $k => &$item) { $item['_id'] = (string) $item['_id']; if (isset($item['sid'])) { $item['sid'] = (string) $item['sid']; } if (is_string($item['tags'])) { $item['tags'] = array($item['tags']); } if (in_array('%private', $item['tags'])) { if (!isset($item['to'])) { continue; } if (!in_array($session->username, $item['to']) && $session->username != $item['from']) { continue; } } $item['history'] = true; $session->send($item); unset($cursor->items[$k]); } $cursor->destroy(); }, array('sort' => array('ts' => -1), 'where' => array('ts' => $condts, 'tags' => array('$in' => $packet['tags'])), 'limit' => -20)); } elseif ($cmd == 'getUserlist') { if ($this->username === null) { return; } $session = $this; $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatsessions'}->find(function ($cursor) use($session) { $tag->cursor = $cursor; $cursor->items = array_reverse($cursor->items); foreach ($cursor->items as $k => &$item) { unset($item['_id']); $item['id'] = isset($item['id']) ? (string) $item['id'] : ''; } $session->send(array('type' => 'userlist', 'userlist' => $cursor->items)); $cursor->destroy(); }, array('sort' => array('ctime' => -1), 'where' => array('tags' => array('$in' => array_diff($packet['tags'], array('%private'))), 'atime' => array('$gt' => microtime(true) - 20), 'username' => array('$exists' => true)), 'limit' => -2000)); } elseif ($cmd === 'sendMessage') { if (!isset($packet['tags'])) { return false; } if (!$this->username) { return false; } $username = $this->username; if (!isset($packet['text']) || trim($packet['text']) === '') { return false; } $text = $packet['text']; $color = isset($packet['color']) ? (string) $packet['color'] : ''; static $colors = array('black', 'red', 'green', 'blue'); if (!in_array($color, $colors)) { $color = $colors[0]; } $c = substr($text, 0, 1); if ($c === '/') { $e = explode(' ', $text, 2); $m = strtolower(substr($e[0], 1)); $text = isset($e[1]) ? trim($e[1]) : ''; if ($m === 'me') { if ($text === '') { $this->sysMsg('/me <message>: insufficient parameters', $packet['tab']); } else { $this->updateSession(array('statusmsg' => $text)); } } elseif ($m === 'tags') { $tags = trim($text); if ($tags !== '') { $this->setTags(array_map('trim', explode(',', $tags))); } $this->sysMsg('/tags: ' . implode(', ', $this->tags), $packet['tab']); } elseif ($m === 'join') { $tags = $text; if ($tags !== '') { $this->addTags(array_map('trim', explode(',', $tags))); } else { $this->sysMsg('/join <tag1>{,<tagN>}: insufficient parameters', $packet['tab']); } } elseif ($m === 'part') { $tags = $text; if ($tags !== '') { $this->removeTags(array_map('trim', explode(',', $tags))); } else { $this->sysMsg('/part <tag1>{,<tagN>}: insufficient parameters', $packet['tab']); } } elseif ($m === 'nick') { //$this->setUsername($text); } elseif ($m === 'thetime') { $this->sysMsg('Current time: ' . date('r'), $packet['tab']); } elseif ($m === 'su') { $password = $text; if ($this->su || $password !== '' && $password === $this->appInstance->config->adminpassword->value) { $this->su = true; $this->send(array('type' => 'youAreModerator')); $this->sysMsg('You\'ve got the power.', $packet['tab']); } else { $this->sysMsg('Your powers are weak, old man.', $packet['tab']); } } elseif ($m === 'kick') { $e = explode(' ', $text, 3); $users = isset($e[0]) ? trim($e[0]) : ''; $tags = isset($e[1]) ? trim($e[1]) : ''; $reason = isset($e[2]) ? trim($e[2]) : ''; if ($users === '') { $this->sysMsg('/kick <name> [<tags>] [<reason>]: insufficient parameters', $packet['tab']); } else { if (!$this->su) { $this->sysMsg('Your powers are weak, old man.', $packet['tab']); } else { $this->appInstance->kickUsers($users, $tags, $reason); } } } elseif ($m === 'fchname') { $e = explode(' ', $text); $name = isset($e[0]) ? trim($e[0]) : ''; $newname = isset($e[1]) ? trim($e[1]) : ''; if ($name === '' || $newname === '') { $this->sysMsg('/fchname <name> <newname>: insufficient parameters', $packet['tab']); } elseif (!$this->appInstance->validateUsername($newname)) { $this->sysMsg('/fchname: newname>', $packet['tab']); } else { if (!$this->su) { $this->sysMsg('Your powers are weak, old man.', $packet['tab']); } else { $this->appInstance->forceChangeNick($name, $newname); } } } else { $this->sysMsg($m . ' Unknown command', $packet['tab']); } } else { $doc = array('mtype' => 'pub', 'tags' => array_intersect($packet['tags'], $this->tags), 'from' => $username, 'text' => $text, 'color' => $color, 'tab' => isset($packet['tab']) ? $packet['tab'] : null); if (preg_match_all('~(?<=^|\\s)@([A-Za-z\\-_!0-9\\.\\wА-Яа-я]+)~u', $text, $m)) { $doc['to'] = $m[1]; if (sizeof($doc['to']) == 1) { if (binarySubstr($doc['text'], 0, $n = strlen($s = '@' . $doc['to'][0] . ': ')) == $s) { $doc['text'] = binarySubstr($doc['text'], $n); } } } if (in_array('%private', $packet['tags'])) { $clientId = $this->client->connId; $appInstance = $this->appInstance; $this->appInstance->db->{$this->appInstance->config->dbname->value . '.muchatignore'}->findOne(function ($item) use($clientId, $appInstance, $doc) { if (!$item) { if (!isset($appInstance->sessions[$clientId])) { return; } $session = $appInstance->sessions[$clientId]; $session->sendMessage($doc); } }, array('where' => array('username' => $this->username, 'blocked' => $doc['to'][0]))); } else { $this->sendMessage($doc); } } } }
/** * Convert bitmap into bytes * @param string $bitmap Bitmap * @param integer $check_len Check length? * @return string|false */ public static function bitmap2bytes($bitmap, $check_len = 0) { $r = ''; $bitmap = str_pad($bitmap, ceil(strlen($bitmap) / 8) * 8, '0', STR_PAD_LEFT); for ($i = 0, $n = strlen($bitmap) / 8; $i < $n; ++$i) { $r .= chr((int) bindec(binarySubstr($bitmap, $i * 8, 8))); } if ($check_len && strlen($r) !== $check_len) { return false; } return $r; }
/** * Called when new data received * @return void */ public function onRead() { Timer::setTimeout($this->keepaliveTimer); while (($line = $this->readline()) !== null) { if ($line === '') { continue; } if (strlen($line) > 512) { Daemon::$process->log('IRCBouncerConnection error: buffer overflow.'); $this->finish(); return; } $line = binarySubstr($line, 0, -strlen($this->EOL)); $p = strpos($line, ':', 1); $max = $p ? substr_count($line, " ", 0, $p) + 1 : 18; $e = explode(" ", $line, $max); $i = 0; $cmd = $e[$i++]; $args = []; for ($s = min(sizeof($e), 14); $i < $s; ++$i) { if ($e[$i][0] === ':') { $args[] = binarySubstr($e[$i], 1); break; } $args[] = $e[$i]; } if (ctype_digit($cmd)) { $code = (int) $cmd; $cmd = isset(IRC::$codes[$code]) ? IRC::$codes[$code] : 'UNKNOWN-' . $code; } $this->onCommand($cmd, $args); } if (strlen($this->buf) > 512) { Daemon::$process->log('IRCClientConnection error: buffer overflow.'); $this->finish(); } }
public function stdin($buf) { $this->buf .= $buf; if (!$this->handshaked) { 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 (!($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], Request::$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 (Daemon::$parsedSettings['mod' . $this->appInstance->modname . 'maxallowedpacket'] <= $len) { $this->finish(); return; } if ($buflen < $len + 2) { return; } // not enough data yet $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 (Daemon::$parsedSettings['mod' . $this->appInstance->modname . 'maxallowedpacket'] <= $p - 1) { $this->finish(); return; } $data = binarySubstr($this->buf, 1, $p - 1); $this->buf = binarySubstr($this->buf, $p + 1); $this->onFrame($data, $frametype); } else { if (Daemon::$parsedSettings['mod' . $this->appInstance->modname . 'maxallowedpacket'] <= strlen($this->buf)) { $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(); } } }
public function onRead() { while ($this->connection && ($buflen = strlen($this->connection->buf)) >= 1) { $frametype = ord(binarySubstr($this->connection->buf, 0, 1)); if (($frametype & 0x80) === 0x80) { $len = 0; $i = 0; do { $b = ord(binarySubstr($this->connection->buf, ++$i, 1)); $n = $b & 0x7f; $len *= 0x80; $len += $n; } while ($b > 0x80); if ($this->connection->pool->maxAllowedPacket <= $len) { // Too big packet $this->connection->finish(); return FALSE; } if ($buflen < $len + 2) { // not enough data yet return FALSE; } $decodedData = binarySubstr($this->connection->buf, 2, $len); $this->connection->buf = binarySubstr($this->connection->buf, 2 + $len); $this->connection->onFrame($decodedData, 'BINARY'); } else { if (($p = strpos($this->connection->buf, "ÿ")) !== FALSE) { if ($this->connection->pool->maxAllowedPacket <= $p - 1) { // Too big packet $this->connection->finish(); return FALSE; } $decodedData = binarySubstr($this->connection->buf, 1, $p - 1); $this->connection->buf = binarySubstr($this->connection->buf, $p + 1); $this->connection->onFrame($decodedData, 'STRING'); } else { if ($this->connection->pool->maxAllowedPacket <= $buflen - 1) { // Too big packet $this->connection->finish(); return FALSE; } // not enough data yet return; } } } }
/** * Handles the output from downstream requests. * @param object Request. * @param string The output. * @return void */ public function requestOut($req, $output) { $outlen = strlen($output); /* * Iterate over every character in the string, * escaping with a slash or encoding to UTF-8 where necessary */ // string bytes counter $d = 0; for ($c = 0; $c < $outlen; ++$c) { $ord_var_c = ord($output[$d]); switch (true) { case $ord_var_c >= 0x20 && $ord_var_c <= 0x7f: // characters U-00000000 - U-0000007F (same as ASCII) $d++; break; case ($ord_var_c & 0xe0) == 0xc0: // characters U-00000080 - U-000007FF, mask 110XXXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $d += 2; break; case ($ord_var_c & 0xf0) == 0xe0: // characters U-00000800 - U-0000FFFF, mask 1110XXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $d += 3; break; case ($ord_var_c & 0xf8) == 0xf0: // characters U-00010000 - U-001FFFFF, mask 11110XXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $d += 4; break; case ($ord_var_c & 0xfc) == 0xf8: // characters U-00200000 - U-03FFFFFF, mask 111110XX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $d += 5; break; case ($ord_var_c & 0xfe) == 0xfc: // characters U-04000000 - U-7FFFFFFF, mask 1111110X // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $d += 6; break; default: $d++; } } for ($o = 0; $o < $d;) { $c = min($this->pool->config->chunksize->value, $d - $o); $w = $this->write("" . "" . pack('nn', $req->attrs->id, $c) . "" . "" . ($c === $d ? $output : binarySubstr($output, $o, $c))); if ($w === false) { $req->abort(); return false; } $o += $c; } return true; }
/** * Called when new data received * @return void */ public function onRead() { while (($line = $this->readline()) !== null) { if ($line === '') { continue; } if (strlen($line) > 512) { Daemon::$process->log('IRCClientConnection error: buffer overflow.'); $this->finish(); return; } $line = binarySubstr($line, 0, -strlen($this->EOL)); $p = strpos($line, ' :', 1); $max = $p !== false ? substr_count($line, " ", 0, $p + 1) + 1 : 18; $e = explode(" ", $line, $max); $i = 0; $from = IRC::parseUsermask($e[$i][0] === ':' ? binarySubstr($e[$i++], 1) : null); $cmd = $e[$i++]; $args = []; for ($s = min(sizeof($e), 14); $i < $s; ++$i) { if ($e[$i][0] === ':') { $args[] = binarySubstr($e[$i], 1); break; } $args[] = $e[$i]; } if (ctype_digit($cmd)) { $code = (int) $cmd; $cmd = isset(IRC::$codes[$code]) ? IRC::$codes[$code] : $code; } $this->lastLine = $line; $this->onCommand($from, $cmd, $args); } if (strlen($this->buf) > 512) { Daemon::$process->log('IRCClientConnection error: buffer overflow.'); $this->finish(); } }
/** * Called when new data received * @param string New data * @return void */ public function stdin($buf) { if ($this->state === self::STATE_BODY) { goto body; } $this->buf .= $buf; $buf = ''; while (($line = $this->gets()) !== FALSE) { if ($line === $this->EOL) { if (isset($this->headers['HTTP_CONTENT_LENGTH'])) { $this->contentLength = (int) $this->headers['HTTP_CONTENT_LENGTH']; } else { $this->contentLength = -1; } if (isset($this->headers['HTTP_TRANSFER_ENCODING'])) { $e = explode(', ', strtolower($this->headers['HTTP_TRANSFER_ENCODING'])); $this->chunked = in_array('chunked', $e, true); } else { $this->chunked = false; } if (isset($this->headers['HTTP_CONNECTION'])) { $e = explode(', ', strtolower($this->headers['HTTP_CONNECTION'])); $this->keepalive = in_array('keep-alive', $e, true); } if (!$this->chunked) { $this->body .= $this->buf; $this->buf = ''; } $this->state = self::STATE_BODY; break; } if ($this->state === self::STATE_ROOT) { $this->headers['STATUS'] = rtrim($line); $this->state = self::STATE_HEADERS; } elseif ($this->state === self::STATE_HEADERS) { $e = explode(': ', rtrim($line)); if (isset($e[1])) { $this->headers['HTTP_' . strtoupper(strtr($e[0], HTTPRequest::$htr))] = $e[1]; } } } if ($this->state !== self::STATE_BODY) { return; // not enough data yet } body: if ($this->chunked) { $this->buf .= $buf; chunk: if ($this->curChunkSize === null) { // outside of chunk $l = $this->gets(); if ($l === $this->EOL) { // skip empty line goto chunk; } if ($l === false) { return; // not enough data yet } $l = rtrim($l); if (!ctype_xdigit($l)) { $this->protocolError = __LINE__; $this->finish(); // protocol error return; } $this->curChunkSize = hexdec($l); } if ($this->curChunkSize !== null) { if ($this->curChunkSize === 0) { if ($this->gets() === $this->EOL) { $this->requestFinished(); return; } else { // protocol error $this->protocolError = __LINE__; $this->finish(); return; } } $len = strlen($this->buf); $n = $this->curChunkSize - strlen($this->curChunk); if ($n >= $len) { $this->curChunk .= $this->buf; $this->buf = ''; } else { $this->curChunk .= binarySubstr($this->buf, 0, $n); $this->buf = binarySubstr($this->buf, $n); } if ($this->curChunkSize <= strlen($this->curChunk)) { $this->body .= $this->curChunk; $this->curChunkSize = null; $this->curChunk = ''; goto chunk; } } } else { $this->body .= $buf; if ($this->contentLength !== -1 && strlen($this->body) >= $this->contentLength) { $this->requestFinished(); } } }
/** * Handles the output from downstream requests * @param object $req Request * @param string $out The output * @return boolean Success */ public function requestOut($req, $out) { $cs = $this->pool->config->chunksize->value; if (strlen($out) > $cs) { while (($ol = strlen($out)) > 0) { $l = min($cs, $ol); if ($this->sendChunk($req, binarySubstr($out, 0, $l)) === false) { $req->abort(); return false; } $out = binarySubstr($out, $l); } } elseif ($this->sendChunk($req, $out) === false) { $req->abort(); return false; } return true; }
/** * Write to shared memory * @param string $data Data * @param integer $offset Offset * @return boolean Success */ public function write($data, $offset) { $segno = floor($offset / $this->segsize); if (!isset($this->segments[$segno])) { if (!$this->open($segno, true)) { return false; } } $sOffset = $offset % $this->segsize; $d = $this->segsize - ($sOffset + strlen($data)); if ($d < 0) { $this->write(binarySubstr($data, $d), ($segno + 1) * $this->segsize); $data = binarySubstr($data, 0, $d); } //Daemon::log('writing to #'.$offset.' (segno '.$segno.')'); shmop_write($this->segments[$segno], $data, $sOffset); return true; }
/** * Data decoding, according to related IETF draft * * @see http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#page-16 */ public function onRead() { $data = ''; while ($this->connection && ($buflen = strlen($this->connection->buf)) >= 1) { $p = 0; // offset $first = ord(binarySubstr($this->connection->buf, $p++, 1)); // first byte integer (fin, opcode) $firstBits = decbin($first); $rsv1 = (bool) $firstBits[1]; $rsv2 = (bool) $firstBits[2]; $rsv3 = (bool) $firstBits[3]; $opcode = (int) bindec(substr($firstBits, 4, 4)); if ($opcode === 0x8) { // CLOSE $this->connection->finish(); return; } $opcodeName = $this->opcodes[$opcode]; $second = ord(binarySubstr($this->connection->buf, $p++, 1)); // second byte integer (masked, payload length) $fin = (bool) ($first >> 7); $isMasked = (bool) ($second >> 7); $dataLength = $second & 0x7f; if ($dataLength === 0x7e) { // 2 bytes-length if ($buflen < $p + 2) { return; // not enough data yet } $dataLength = $this->bytes2int(binarySubstr($this->connection->buf, $p, 2), false); $p += 2; } elseif ($dataLength === 0x7f) { // 4 bytes-length if ($buflen < $p + 4) { return; // not enough data yet } $dataLength = $this->bytes2int(binarySubstr($this->connection->buf, $p, 4)); $p += 4; } if ($this->connection->pool->maxAllowedPacket <= $dataLength) { // Too big packet $this->connection->finish(); return; } if ($isMasked) { if ($buflen < $p + 4) { return; // not enough data yet } $mask = binarySubstr($this->connection->buf, $p, 4); $p += 4; } if ($buflen < $p + $dataLength) { return; // not enough data yet } $data = binarySubstr($this->connection->buf, $p, $dataLength); $p += $dataLength; if ($isMasked) { $data = $this->mask($data, $mask); } $this->connection->buf = binarySubstr($this->connection->buf, $p); //Daemon::log(Debug::dump(array('ext' => $this->connection->extensions, 'rsv1' => $rsv1, 'data' => Debug::exportBytes($data)))); if ($rsv1 && in_array('deflate-frame', $this->connection->extensions)) { // deflate frame $data = gzuncompress($data, $this->connection->pool->maxAllowedPacket); } if (!$fin) { $this->connection->framebuf .= $data; } else { $this->connection->onFrame($this->connection->framebuf . $data, $opcodeName); if ($this->connection) { $this->connection->framebuf = ''; } } } }
/** * Called when new data received * @param string New data * @return void */ public function stdin($buf) { $this->buf .= $buf; start: $this->buflen = strlen($this->buf); if (($packet = $this->getPacketHeader()) === FALSE) { return; } $this->seq = $packet[1] + 1; if ($this->buflen < 4 + $packet[0]) { // not whole packet yet return; } $p = 4; if ($this->cstate === self::STATE_ROOT) { $this->cstate = self::STATE_GOT_INIT; $p = 4; $this->protover = ord(binarySubstr($this->buf, $p++, 1)); if ($this->protover === 0xff) { // error $fieldCount = $this->protover; $this->protover = 0; $this->onResponse->push($this->onConnected); $this->onConnected = null; goto field; } $this->serverver = ''; while ($p < $this->buflen) { $c = binarySubstr($this->buf, $p++, 1); if ($c === "") { break; } $this->serverver .= $c; } $this->threadId = $this->bytes2int(binarySubstr($this->buf, $p, 4)); $p += 4; $this->scramble = binarySubstr($this->buf, $p, 8); $p += 9; $this->serverCaps = $this->bytes2int(binarySubstr($this->buf, $p, 2)); $p += 2; $this->serverLang = ord(binarySubstr($this->buf, $p++, 1)); $this->serverStatus = $this->bytes2int(binarySubstr($this->buf, $p, 2)); $p += 2; $p += 13; $restScramble = binarySubstr($this->buf, $p, 12); $this->scramble .= $restScramble; $p += 13; $this->auth(); } else { $fieldCount = ord(binarySubstr($this->buf, $p++, 1)); field: if ($fieldCount === 0xff) { // Error packet $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->errno = $u[1]; $state = binarySubstr($this->buf, $p, 6); $p = +6; $this->errmsg = binarySubstr($this->buf, $p, $packet[0] + 4 - $p); $this->onError(); } elseif ($fieldCount === 0x0) { // OK Packet Empty if ($this->cstate === self::STATE_AUTH_SENT) { $this->cstate = self::STATE_HANDSHAKED; if ($this->path !== '') { $this->query('USE `' . $this->path . '`'); } } $this->affectedRows = $this->parseEncodedBinary($this->buf, $p); $this->insertId = $this->parseEncodedBinary($this->buf, $p); $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->serverStatus = $u[1]; $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->warnCount = $u[1]; $this->message = binarySubstr($this->buf, $p, $packet[0] + 4 - $p); $this->onResultDone(); } elseif ($fieldCount === 0xfe) { // EOF Packet if ($this->instate === self::INSTATE_ROW) { $this->onResultDone(); } else { ++$this->instate; } } else { // Data packet --$p; if ($this->instate === self::INSTATE_HEADER) { // Result Set Header Packet $extra = $this->parseEncodedBinary($this->buf, $p); $this->instate = self::INSTATE_FIELD; } elseif ($this->instate === self::INSTATE_FIELD) { // Field Packet $field = array('catalog' => $this->parseEncodedString($this->buf, $p), 'db' => $this->parseEncodedString($this->buf, $p), 'table' => $this->parseEncodedString($this->buf, $p), 'org_table' => $this->parseEncodedString($this->buf, $p), 'name' => $this->parseEncodedString($this->buf, $p), 'org_name' => $this->parseEncodedString($this->buf, $p)); ++$p; // filler $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $field['charset'] = $u[1]; $u = unpack('V', binarySubstr($this->buf, $p, 4)); $p += 4; $field['length'] = $u[1]; $field['type'] = ord(binarySubstr($this->buf, $p, 1)); ++$p; $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $field['flags'] = $u[1]; $field['decimals'] = ord(binarySubstr($this->buf, $p, 1)); ++$p; $this->resultFields[] = $field; } elseif ($this->instate === self::INSTATE_ROW) { // Row Packet $row = array(); for ($i = 0, $nf = sizeof($this->resultFields); $i < $nf; ++$i) { $row[$this->resultFields[$i]['name']] = $this->parseEncodedString($this->buf, $p); } $this->resultRows[] = $row; } } } $this->buf = binarySubstr($this->buf, 4 + $packet[0]); goto start; }
public function stdin($buf) { $this->buf .= $buf; start: if ($this->state === 0) { while (($l = $this->gets()) !== FALSE) { $e = explode(' ', rtrim($l, "\r\n")); if ($e[0] == 'VALUE') { $this->key = $e[1]; $this->valueFlags = $e[2]; $this->valueLength = $e[3]; $this->result = ''; $this->state = 1; break; } elseif ($e[0] == 'STAT') { if ($this->result === NULL) { $this->result = array(); } $this->result[$e[1]] = $e[2]; } elseif ($e[0] === 'END' || $e[0] === 'DELETED' || $e[0] === 'ERROR' || $e[0] === 'CLIENT_ERROR' || $e[0] === 'SERVER_ERROR') { if ($e[0] !== 'END') { $this->result = FALSE; $this->error = isset($e[1]) ? $e[1] : NULL; } $f = array_shift($this->onResponse); if ($f) { call_user_func($f, $this); } $this->valueSize = 0; $this->result = NULL; } } } if ($this->state === 1) { if ($this->valueSize < $this->valueLength) { $n = $this->valueLength - $this->valueSize; $buflen = strlen($this->buf); if ($buflen > $n) { $this->result .= binarySubstr($this->buf, 0, $n); $this->buf = binarySubstr($this->buf, $n); } else { $this->result .= $this->buf; $n = $buflen; $this->buf = ''; } $this->valueSize += $n; if ($this->valueSize >= $this->valueLength) { $this->state = 0; goto start; } } } }
public function readConn($connId) { static $roles = array(1 => 'FCGI_RESPONDER', 2 => 'FCGI_AUTHORIZER', 3 => 'FCGI_FILTER'); $buf = $this->read($connId, $this->readPacketSize); if (sizeof($this->poolState[$connId]) < 3) { return; } if ($this->poolState[$connId]['state'] === 0) { if (strpos($buf, '<policy-file-request/>') !== FALSE) { if (($FP = Daemon::$appResolver->getInstanceByAppName('FlashPolicy')) && $FP->policyData) { Daemon::$worker->writePoolState[$connId] = TRUE; event_buffer_write($this->buf[$connId], $FP->policyData . ""); } $this->finishConnection($connId); return; } ++Daemon::$worker->queryCounter; ++$this->poolState[$connId]['n']; $rid = $connId . '-' . $this->poolState[$connId]['n']; $this->poolState[$connId]['state'] = 1; $req = new stdClass(); $req->attrs = new stdClass(); $req->attrs->request = array(); $req->attrs->get = array(); $req->attrs->post = array(); $req->attrs->cookie = array(); $req->attrs->server = array(); $req->attrs->files = array(); $req->attrs->session = NULL; $req->attrs->connId = $connId; $req->attrs->id = $this->poolState[$connId]['n']; $req->attrs->params_done = FALSE; $req->attrs->stdin_done = FALSE; $req->attrs->stdinbuf = ''; $req->attrs->stdinlen = 0; $req->attrs->inbuf = ''; $req->attrs->chunked = FALSE; if (Daemon::$settings['mod' . $this->modname . 'logqueue']) { Daemon::log('[WORKER ' . Daemon::$worker->pid . '] new request queued.'); } Daemon::$worker->queue[$rid] = $req; $this->poolQueue[$connId][$req->attrs->id] = $req; } else { $rid = $connId . '-' . $this->poolState[$connId]['n']; if (isset(Daemon::$worker->queue[$rid])) { $req = Daemon::$worker->queue[$rid]; } else { Daemon::log('Unexpected input. Request ID: ' . $rid . '.'); return; } } if ($this->poolState[$connId]['state'] === 1) { $req->attrs->inbuf .= $buf; if (strpos($req->attrs->inbuf, '<policy-file-request/>') !== FALSE) { if (($FP = Daemon::$appResolver->getInstanceByAppName('FlashPolicy')) && $FP->policyData) { Daemon::$worker->writePoolState[$req->attrs->connId] = TRUE; event_buffer_write($this->buf[$req->attrs->connId], $FP->policyData . ""); } $this->finishConnection($req->attrs->connId); return; } $buf = ''; if (($p = strpos($req->attrs->inbuf, "\r\n\r\n")) !== FALSE) { $headers = binarySubstr($req->attrs->inbuf, 0, $p); $h = explode("\r\n", $headers); $req->attrs->inbuf = binarySubstr($req->attrs->inbuf, $p + 4); $e = explode(' ', $h[0]); $u = parse_url($e[1]); $req->attrs->server['REQUEST_METHOD'] = $e[0]; $req->attrs->server['REQUEST_URI'] = $u['path'] . (isset($u['query']) ? '?' . $u['query'] : ''); $req->attrs->server['DOCUMENT_URI'] = $u['path']; $req->attrs->server['PHP_SELF'] = $u['path']; $req->attrs->server['QUERY_STRING'] = isset($u['query']) ? $u['query'] : NULL; $req->attrs->server['SCRIPT_NAME'] = $req->attrs->server['DOCUMENT_URI'] = isset($u['path']) ? $u['path'] : '/'; $req->attrs->server['SERVER_PROTOCOL'] = $e[2]; list($req->attrs->server['REMOTE_ADDR'], $req->attrs->server['REMOTE_PORT']) = explode(':', $this->poolState[$connId]['addr']); for ($i = 1, $n = sizeof($h); $i < $n; ++$i) { $e = explode(': ', $h[$i]); if (isset($e[1])) { $req->attrs->server['HTTP_' . strtoupper(strtr($e[0], Request::$htr))] = $e[1]; } } $req->attrs->params_done = TRUE; if (isset($req->attrs->server['HTTP_CONNECTION']) && $req->attrs->server['HTTP_CONNECTION'] === 'Upgrade' && isset($req->attrs->server['HTTP_UPGRADE']) && $req->attrs->server['HTTP_UPGRADE'] === 'WebSocket') { if ($this->WS) { $this->WS->inheritFromRequest($req, $this); return; } } else { $req = Daemon::$appResolver->getRequest($req, $this, isset(Daemon::$settings[$k = 'mod' . $this->modname . 'responder']) ? Daemon::$settings[$k] : NULL); } if ($req instanceof stdClass) { $this->endRequest($req, 0, 0); unset(Daemon::$worker->queue[$rid]); } else { if (Daemon::$settings['mod' . $this->modname . 'sendfile'] && (!Daemon::$settings['mod' . $this->modname . 'sendfileonlybycommand'] || isset($req->attrs->server['USE_SENDFILE'])) && !isset($req->attrs->server['DONT_USE_SENDFILE'])) { $fn = tempnam(Daemon::$settings['mod' . $this->modname . 'sendfiledir'], Daemon::$settings['mod' . $this->modname . 'sendfileprefix']); $req->sendfp = fopen($fn, 'wb'); $req->header('X-Sendfile: ' . $fn); } $req->stdin($req->attrs->inbuf); $req->attrs->inbuf = ''; Daemon::$worker->queue[$rid] = $req; $this->poolQueue[$connId][$req->attrs->id] = $req; $this->poolState[$connId]['state'] = 2; } } } if ($this->poolState[$connId]['state'] === 2) { $req->stdin($buf); if (Daemon::$settings['logevents']) { Daemon::log('stdin_done = ' . ($req->attrs->stdin_done ? '1' : '0')); } if ($req->attrs->stdin_done) { $this->poolState[$req->attrs->connId]['state'] = 0; } } if ($req->attrs->stdin_done && $req->attrs->params_done) { if (($order = ini_get('request_order')) || ($order = ini_get('variables_order'))) { for ($i = 0, $s = strlen($order); $i < $s; ++$i) { $char = $order[$i]; if ($char == 'G') { $req->attrs->request += $req->attrs->get; } elseif ($char == 'P') { $req->attrs->request += $req->attrs->post; } elseif ($char == 'C') { $req->attrs->request += $req->attrs->cookie; } } } else { $req->attrs->request = $req->attrs->get + $req->attrs->post + $req->attrs->cookie; } $this->timeLastReq = time(); } }
public function stdin($buf) { $this->buf .= $buf; if (Daemon::$settings['mod' . $this->appInstance->modname . 'protologging']) { Daemon::log('Server --> Client: ' . Daemon::exportBytes($buf) . "\n\n"); } start: $this->buflen = strlen($this->buf); if (($packet = $this->getPacketHeader()) === FALSE) { return; } $this->seq = $packet[1] + 1; if ($this->cstate === 0) { if ($this->buflen < 4 + $packet[0]) { return; } // no whole packet yet $this->cstate = 1; $p = 4; $this->protover = ord(binarySubstr($this->buf, $p++, 1)); $this->serverver = ''; while ($p < $this->buflen) { $c = binarySubstr($this->buf, $p++, 1); if ($c === "") { break; } $this->serverver .= $c; } $this->threadId = $this->bytes2int(binarySubstr($this->buf, $p, 4)); $p += 4; $this->scramble = binarySubstr($this->buf, $p, 8); $p += 9; $this->serverCaps = $this->bytes2int(binarySubstr($this->buf, $p, 2)); $p += 2; $this->serverLang = ord(binarySubstr($this->buf, $p++, 1)); $this->serverStatus = $this->bytes2int(binarySubstr($this->buf, $p, 2)); $p += 2; $p += 13; $restScramble = binarySubstr($this->buf, $p, 12); $this->scramble .= $restScramble; $p += 13; $this->auth(); } else { if ($this->buflen < 4 + $packet[0]) { return; } // not whole packet yet $p = 4; $fieldCount = ord(binarySubstr($this->buf, $p, 1)); $p += 1; if ($fieldCount === 0xff) { $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->errno = $u[1]; $state = binarySubstr($this->buf, $p, 6); $p = +6; $this->errmsg = binarySubstr($this->buf, $p, $packet[0] + 4 - $p); $this->onError(); } elseif ($fieldCount === 0x0) { if ($this->cstate === 2) { $this->cstate = 4; } $this->affectedRows = $this->parseEncodedBinary($this->buf, $p); $this->insertId = $this->parseEncodedBinary($this->buf, $p); $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->serverStatus = $u[1]; $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $this->warnCount = $u[1]; $this->message = binarySubstr($this->buf, $p, $packet[0] + 4 - $p); $this->onResultDone(); } elseif ($fieldCount === 0xfe) { ++$this->instate; if ($this->instate === 3) { $this->onResultDone(); } } else { --$p; if ($this->instate === 0) { $extra = $this->parseEncodedBinary($this->buf, $p); ++$this->instate; } elseif ($this->instate === 1) { $field = array(); $field['catalog'] = $this->parseEncodedString($this->buf, $p); $field['db'] = $this->parseEncodedString($this->buf, $p); $field['table'] = $this->parseEncodedString($this->buf, $p); $field['org_table'] = $this->parseEncodedString($this->buf, $p); $field['name'] = $this->parseEncodedString($this->buf, $p); $field['org_name'] = $this->parseEncodedString($this->buf, $p); ++$p; // filler $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $field['charset'] = $u[1]; $u = unpack('V', binarySubstr($this->buf, $p, 4)); $p += 4; $field['length'] = $u[1]; $field['type'] = ord(binarySubstr($this->buf, $p, 1)); ++$p; $u = unpack('v', binarySubstr($this->buf, $p, 2)); $p += 2; $field['flags'] = $u[1]; $field['decimals'] = ord(binarySubstr($this->buf, $p, 1)); ++$p; $this->resultFields[] = $field; } elseif ($this->instate === 2) { $row = array(); for ($i = 0, $nf = sizeof($this->resultFields); $i < $nf; ++$i) { $row[$this->resultFields[$i]['name']] = $this->parseEncodedString($this->buf, $p); } $this->resultRows[] = $row; } } } $this->buf = binarySubstr($this->buf, 4 + $packet[0]); goto start; }
public function stdin($buf) { if ($this->state === 4) { if ($this->slave) { $this->slave->write($buf); } return; } $this->buf .= $buf; start: $l = strlen($this->buf); if ($this->state === 0) { if ($l < 2) { return; } // Not enough data yet $n = ord(binarySubstr($this->buf, 1, 1)); if ($l < $n + 2) { return; } // Not enough data yet $this->ver = binarySubstr($this->buf, 0, 1); $methods = binarySubstr($this->buf, 2, $n); $this->buf = binarySubstr($this->buf, $n + 2); if (!Daemon::$settings['mod' . $this->appInstance->modname . 'auth']) { $m = ""; $this->state = 3; } elseif (strpos($methods, "") !== FALSE) { $m = ""; $this->state = 2; } else { $m = "ÿ"; $this->state = 1; } $this->write($this->ver . $m); if ($this->state === 1) { $this->finish(); } else { goto start; } } elseif ($this->state === 2) { if ($l < 3) { return; } // Not enough data yet $ver = binarySubstr($this->buf, 0, 1); if ($ver !== $this->ver) { $this->finish(); return; } $ulen = ord(binarySubstr($this->buf, 1, 1)); if ($l < 3 + $ulen) { return; } // Not enough data yet $username = binarySubstr($this->buf, 2, $ulen); $plen = ord(binarySubstr($this->buf, 1, 1)); if ($l < 3 + $ulen + $plen) { return; } // Not enough data yet $password = binarySubstr($this->buf, 2 + $ulen, $plen); if ($username != Daemon::$settings['mod' . $this->appInstance->modname . 'username'] || $password != Daemon::$settings['mod' . $this->appInstance->modname . 'password']) { $this->state = 1; $m = ""; } else { $this->state = 3; $m = ""; } $this->buf = binarySubstr($this->buf, 3 + $ulen + $plen); $this->write($this->ver . $m); if ($this->state === 1) { $this->finish(); } else { goto start; } } elseif ($this->state === 3) { if ($l < 4) { return; } // Not enough data yet $ver = binarySubstr($this->buf, 0, 1); if ($ver !== $this->ver) { $this->finish(); return; } $cmd = binarySubstr($this->buf, 1, 1); $atype = binarySubstr($this->buf, 3, 1); $pl = 4; if ($atype === "") { $address = inet_ntop(binarySubstr($this->buf, $pl, 4)); $pl += 4; } elseif ($atype === "") { $len = ord(binarySubstr($this->buf, $pl, 1)); ++$pl; $address = binarySubstr($this->buf, $pl, $len); $pl += $len; } elseif ($atype === "") { $address = inet_ntop(binarySubstr($this->buf, $pl, 16)); $pl += 16; } else { $this->finish(); return; } $u = unpack('nport', $bin = binarySubstr($this->buf, $pl, 2)); $port = $u['port']; $pl += 2; $this->buf = binarySubstr($this->buf, $pl); $connId = $this->appInstance->connectTo($this->destAddr = $address, $this->destPort = $port); if (!$connId) { $this->write($this->ver . ""); $this->finish(); } else { $this->slave = $this->appInstance->sessions[$connId] = new SocksServerSlaveSession($connId, $this->appInstance); $this->slave->client = $this; $this->slave->write($this->buf); $this->buf = ''; $this->state = 4; } } }
/** * Parse request body * @return void */ public function parseStdin() { do { if ($this->boundary === false) { break; } $continue = false; if ($this->mpartstate === 0) { // seek to the nearest boundary if (($p = strpos($this->attrs->stdinbuf, $ndl = '--' . $this->boundary . "\r\n", $this->mpartoffset)) !== false) { // we have found the nearest boundary at position $p $this->mpartoffset = $p + strlen($ndl); $this->mpartstate = 1; $continue = true; } } elseif ($this->mpartstate === 1) { // parse the part's headers $this->mpartcondisp = false; if (($p = strpos($this->attrs->stdinbuf, "\r\n\r\n", $this->mpartoffset)) !== false) { // we got all of the headers $h = explode("\r\n", binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset)); $this->mpartoffset = $p + 4; $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset); $this->mpartoffset = 0; for ($i = 0, $s = sizeof($h); $i < $s; ++$i) { $e = explode(':', $h[$i], 2); $e[0] = strtr(strtoupper($e[0]), HTTPRequest::$htr); if (isset($e[1])) { $e[1] = ltrim($e[1]); } if ($e[0] == 'CONTENT_DISPOSITION' && isset($e[1])) { parse_str(strtr($e[1], HTTPRequest::$hvaltr), $this->mpartcondisp); if (!isset($this->mpartcondisp['form-data'])) { break; } if (!isset($this->mpartcondisp['name'])) { break; } $this->mpartcondisp['name'] = trim($this->mpartcondisp['name'], '"'); if (isset($this->mpartcondisp['filename'])) { $this->mpartcondisp['filename'] = trim($this->mpartcondisp['filename'], '"'); if (!ini_get('file_uploads')) { break; } $this->attrs->files[$this->mpartcondisp['name']] = array('name' => $this->mpartcondisp['filename'], 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_OK, 'size' => 0); $tmpdir = ini_get('upload_tmp_dir'); if ($tmpdir === false) { $this->attrs->files[$this->mpartcondisp['name']]['fp'] = false; $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_NO_TMP_DIR; } else { $this->attrs->files[$this->mpartcondisp['name']]['fp'] = @fopen($this->attrs->files[$this->mpartcondisp['name']]['tmp_name'] = tempnam($tmpdir, 'php'), 'w'); if (!$this->attrs->files[$this->mpartcondisp['name']]['fp']) { $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_CANT_WRITE; } } $this->mpartstate = 3; } else { $this->attrs->post[$this->mpartcondisp['name']] = ''; } } elseif ($e[0] == 'CONTENT_TYPE' && isset($e[1])) { if (isset($this->mpartcondisp['name']) && isset($this->mpartcondisp['filename'])) { $this->attrs->files[$this->mpartcondisp['name']]['type'] = $e[1]; } } } if ($this->mpartstate === 1) { $this->mpartstate = 2; } $continue = true; } } elseif ($this->mpartstate === 2 || $this->mpartstate === 3) { // process the body if (($p = strpos($this->attrs->stdinbuf, $ndl = "\r\n--" . $this->boundary . "\r\n", $this->mpartoffset)) !== false || ($p = strpos($this->attrs->stdinbuf, $ndl = "\r\n--" . $this->boundary . "--\r\n", $this->mpartoffset)) !== false) { if ($this->mpartstate === 2 && isset($this->mpartcondisp['name'])) { $this->attrs->post[$this->mpartcondisp['name']] .= binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset); } elseif ($this->mpartstate === 3 && isset($this->mpartcondisp['filename'])) { if ($this->attrs->files[$this->mpartcondisp['name']]['fp']) { fwrite($this->attrs->files[$this->mpartcondisp['name']]['fp'], binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset)); } $this->attrs->files[$this->mpartcondisp['name']]['size'] += $p - $this->mpartoffset; } if ($ndl === "\r\n--" . $this->boundary . "--\r\n") { $this->mpartoffset = $p + strlen($ndl); $this->mpartstate = 0; // we done at all } else { $this->mpartoffset = $p; $this->mpartstate = 1; // let us parse the next part $continue = true; } $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset); $this->mpartoffset = 0; } else { $p = strrpos($this->attrs->stdinbuf, "\r\n", $this->mpartoffset); if ($p !== false) { if ($this->mpartstate === 2 && isset($this->mpartcondisp['name'])) { $this->attrs->post[$this->mpartcondisp['name']] .= binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset); } elseif ($this->mpartstate === 3 && isset($this->mpartcondisp['filename'])) { if ($this->attrs->files[$this->mpartcondisp['name']]['fp']) { fwrite($this->attrs->files[$this->mpartcondisp['name']]['fp'], binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset)); } $this->attrs->files[$this->mpartcondisp['name']]['size'] += $p - $this->mpartoffset; if ($this->parseSize(ini_get('upload_max_filesize')) < $this->attrs->files[$this->mpartcondisp['name']]['size']) { $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_INI_SIZE; } if (isset($this->attrs->post['MAX_FILE_SIZE']) && $this->attrs->post['MAX_FILE_SIZE'] < $this->attrs->files[$this->mpartcondisp['name']]['size']) { $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_FORM_SIZE; } } $this->mpartoffset = $p; $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset); $this->mpartoffset = 0; } } } } while ($continue); }
/** * Called when new data received * @param string New data * @return void */ public function stdin($buf) { $this->buf .= $buf; start: $l = strlen($this->buf); if ($l < 16) { // we have not enough data yet return; } $h = unpack('Vlen/VreqId/VresponseTo/VopCode', binarySubstr($this->buf, 0, 16)); $plen = (int) $h['len']; if ($plen > $l) { // we have not enough data yet return; } if ($h['opCode'] === MongoClient::OP_REPLY) { $r = unpack('Vflag/VcursorID1/VcursorID2/Voffset/Vlength', binarySubstr($this->buf, 16, 20)); //Daemon::log(array($r,$h)); $r['cursorId'] = binarySubstr($this->buf, 20, 8); $id = (int) $h['responseTo']; $flagBits = str_pad(strrev(decbin($r['flag'])), 8, '0', STR_PAD_LEFT); $cur = $r['cursorId'] !== "" ? 'c' . $r['cursorId'] : 'r' . $h['responseTo']; if (isset($this->pool->requests[$id][2]) && $this->pool->requests[$id][2] === false && !isset($this->pool->cursors[$cur])) { $this->pool->cursors[$cur] = new MongoClientCursor($cur, $this->pool->requests[$id][0], $this); $this->pool->cursors[$cur]->failure = $flagBits[1] == '1'; $this->pool->cursors[$cur]->await = $flagBits[3] == '1'; $this->pool->cursors[$cur]->callback = $this->pool->requests[$id][1]; $this->pool->cursors[$cur]->parseOplog = isset($this->pool->requests[$id][3]) && $this->pool->requests[$id][3]; $this->pool->cursors[$cur]->tailable = isset($this->pool->requests[$id][4]) && $this->pool->requests[$id][4]; } //Daemon::log(array(Debug::exportBytes($cur),get_Class($this->pool->cursors[$cur]))); if (isset($this->pool->cursors[$cur]) && ($r['length'] === 0 || binarySubstr($cur, 0, 1) === 'r')) { if ($this->pool->cursors[$cur]->tailable) { if ($this->pool->cursors[$cur]->finished = $flagBits[0] == '1') { $this->pool->cursors[$cur]->destroy(); } } else { $this->pool->cursors[$cur]->finished = true; } } $p = 36; while ($p < $plen) { $dl = unpack('Vlen', binarySubstr($this->buf, $p, 4)); $doc = bson_decode(binarySubstr($this->buf, $p, $dl['len'])); if (isset($this->pool->cursors[$cur]) && @$this->pool->cursors[$cur]->parseOplog && isset($doc['ts'])) { $tsdata = unpack('Vsec/Vinc', binarySubstr($this->buf, $p + 1 + 4 + 3, 8)); $doc['ts'] = $tsdata['sec'] . ' ' . $tsdata['inc']; } $this->pool->cursors[$cur]->items[] = $doc; $p += $dl['len']; } $this->setFree(true); if (isset($this->pool->requests[$id][2]) && $this->pool->requests[$id][2] && $this->pool->requests[$id][1]) { call_user_func($this->pool->requests[$id][1], isset($this->pool->cursors[$cur]->items[0]) ? $this->pool->cursors[$cur]->items[0] : false); if (isset($this->pool->cursors[$cur])) { if ($this->pool->cursors[$cur] instanceof MongoClientCursor) { $this->pool->cursors[$cur]->destroy(); } else { unset($this->pool->cursors[$cur]); } } } elseif (isset($this->pool->cursors[$cur])) { call_user_func($this->pool->cursors[$cur]->callback, $this->pool->cursors[$cur]); } unset($this->pool->requests[$id]); } $this->buf = binarySubstr($this->buf, $plen); goto start; }
/** * Called when new UDP packet received. * @param string $pct * @return void */ public function onUdpPacket($pct) { $orig = $pct; $this->response = []; /*$id = */ Binary::getWord($pct); $bitmap = Binary::getBitmap(Binary::getByte($pct)) . Binary::getBitmap(Binary::getByte($pct)); //$qr = (int) $bitmap[0]; $opcode = bindec(substr($bitmap, 1, 4)); //$aa = (int) $bitmap[5]; //$tc = (int) $bitmap[6]; //$rd = (int) $bitmap[7]; //$ra = (int) $bitmap[8]; //$z = bindec(substr($bitmap, 9, 3)); //$rcode = bindec(substr($bitmap, 12)); $qdcount = Binary::getWord($pct); $ancount = Binary::getWord($pct); $nscount = Binary::getWord($pct); $arcount = Binary::getWord($pct); for ($i = 0; $i < $qdcount; ++$i) { $name = Binary::parseLabels($pct, $orig); $typeInt = Binary::getWord($pct); $type = isset(Pool::$type[$typeInt]) ? Pool::$type[$typeInt] : 'UNK(' . $typeInt . ')'; $classInt = Binary::getWord($pct); $class = isset(Pool::$class[$classInt]) ? Pool::$class[$classInt] : 'UNK(' . $classInt . ')'; if (!isset($this->response[$type])) { $this->response[$type] = []; } $record = ['name' => $name, 'type' => $type, 'class' => $class]; $this->response['query'][] = $record; } $getResRecord = function (&$pct) use($orig) { $name = Binary::parseLabels($pct, $orig); $typeInt = Binary::getWord($pct); $type = isset(Pool::$type[$typeInt]) ? Pool::$type[$typeInt] : 'UNK(' . $typeInt . ')'; $classInt = Binary::getWord($pct); $class = isset(Pool::$class[$classInt]) ? Pool::$class[$classInt] : 'UNK(' . $classInt . ')'; $ttl = Binary::getDWord($pct); $length = Binary::getWord($pct); $data = binarySubstr($pct, 0, $length); $pct = binarySubstr($pct, $length); $record = ['name' => $name, 'type' => $type, 'class' => $class, 'ttl' => $ttl]; if ($type === 'A') { if ($data === "") { $record['ip'] = false; $record['ttl'] = 5; } else { $record['ip'] = inet_ntop($data); } } elseif ($type === 'NS') { $record['ns'] = Binary::parseLabels($data); } elseif ($type === 'CNAME') { $record['cname'] = Binary::parseLabels($data, $orig); } return $record; }; for ($i = 0; $i < $ancount; ++$i) { $record = $getResRecord($pct); if (!isset($this->response[$record['type']])) { $this->response[$record['type']] = []; } $this->response[$record['type']][] = $record; } for ($i = 0; $i < $nscount; ++$i) { $record = $getResRecord($pct); if (!isset($this->response[$record['type']])) { $this->response[$record['type']] = []; } $this->response[$record['type']][] = $record; } for ($i = 0; $i < $arcount; ++$i) { $record = $getResRecord($pct); if (!isset($this->response[$record['type']])) { $this->response[$record['type']] = []; } $this->response[$record['type']][] = $record; } $this->onResponse->executeOne($this->response); if (!$this->keepalive) { $this->finish(); return; } else { $this->checkFree(); } }
public function readConn($connId) { static $roles = array(1 => 'FCGI_RESPONDER', 2 => 'FCGI_AUTHORIZER', 3 => 'FCGI_FILTER'); static $reqtypes = array(1 => 'FCGI_BEGIN_REQUEST', 2 => 'FCGI_ABORT_REQUEST', 3 => 'FCGI_END_REQUEST', 4 => 'FCGI_PARAMS', 5 => 'FCGI_STDIN', 6 => 'FCGI_STDOUT', 7 => 'FCGI_STDERR', 8 => 'FCGI_DATA', 9 => 'FCGI_GET_VALUES', 10 => 'FCGI_GET_VALUES_RESULT', 11 => 'FCGI_UNKNOWN_TYPE', 11 => 'FCGI_MAXTYPE'); $state = sizeof($this->poolState[$connId]); if ($state === 0) { $header = $this->read($connId, 8); if ($header === FALSE) { return; } $r = unpack('Cver/Ctype/nreqid/nconlen/Cpadlen/Creserved', $header); if ($r['conlen'] > 0) { event_buffer_watermark_set($this->buf[$connId], EV_READ, $r['conlen'], 0xffffff); } $this->poolState[$connId][0] = $r; ++$state; } else { $r = $this->poolState[$connId][0]; } if ($state === 1) { $c = $r['conlen'] === 0 ? '' : $this->read($connId, $r['conlen']); if ($c === FALSE) { return; } if ($r['padlen'] > 0) { event_buffer_watermark_set($this->buf[$connId], EV_READ, $r['padlen'], 0xffffff); } $this->poolState[$connId][1] = $c; ++$state; } else { $c = $this->poolState[$connId][1]; } if ($state === 2) { $pad = $r['padlen'] === 0 ? '' : $this->read($connId, $r['padlen']); if ($pad === FALSE) { return; } $this->poolState[$connId][2] = $pad; } else { $pad = $this->poolState[$connId][2]; } $this->poolState[$connId] = array(); $type =& $r['type']; $r['ttype'] = isset($reqtypes[$type]) ? $reqtypes[$type] : $type; $rid = $connId . '-' . $r['reqid']; if (Daemon::$settings['mod' . $this->modname . 'logrecords']) { Daemon::log('[DEBUG] FastCGI-record #' . $r['type'] . ' (' . $r['ttype'] . '). Request ID: ' . $rid . '. Content length: ' . $r['conlen'] . ' (' . strlen($c) . ') Padding length: ' . $r['padlen'] . ' (' . strlen($pad) . ')'); } if ($type == 1) { ++Daemon::$worker->queryCounter; $rr = unpack('nrole/Cflags', $c); $req = new stdClass(); $req->attrs = new stdClass(); $req->attrs->request = array(); $req->attrs->get = array(); $req->attrs->post = array(); $req->attrs->cookie = array(); $req->attrs->server = array(); $req->attrs->files = array(); $req->attrs->session = NULL; $req->attrs->connId = $connId; $req->attrs->trole = $roles[$rr['role']]; $req->attrs->flags = $rr['flags']; $req->attrs->id = $r['reqid']; $req->attrs->params_done = FALSE; $req->attrs->stdin_done = FALSE; $req->attrs->stdinbuf = ''; $req->attrs->stdinlen = 0; $req->attrs->chunked = FALSE; if (Daemon::$settings['mod' . $this->modname . 'logqueue']) { Daemon::log('[WORKER ' . Daemon::$worker->pid . '] new request queued.'); } Daemon::$worker->queue[$rid] = $req; $this->poolQueue[$connId][$req->attrs->id] = $req; } elseif (isset(Daemon::$worker->queue[$rid])) { $req = Daemon::$worker->queue[$rid]; } else { Daemon::log('Unexpected FastCGI-record #' . $r['type'] . ' (' . $r['ttype'] . '). Request ID: ' . $rid . '.'); return; } if ($type === 2) { $req->abort(); } elseif ($type === 4) { if ($c === '') { $req->attrs->params_done = TRUE; $req = Daemon::$appResolver->getRequest($req, $this); if ($req instanceof stdClass) { $this->endRequest($req, 0, 0); unset(Daemon::$worker->queue[$rid]); } else { if (Daemon::$settings['mod' . $this->modname . 'sendfile'] && (!Daemon::$settings['mod' . $this->modname . 'sendfileonlybycommand'] || isset($req->attrs->server['USE_SENDFILE'])) && !isset($req->attrs->server['DONT_USE_SENDFILE'])) { $fn = tempnam(Daemon::$settings['mod' . $this->modname . 'sendfiledir'], Daemon::$settings['mod' . $this->modname . 'sendfileprefix']); $req->sendfp = fopen($fn, 'wb'); $req->header('X-Sendfile: ' . $fn); } Daemon::$worker->queue[$rid] = $req; } } else { $p = 0; while ($p < $r['conlen']) { if (($namelen = ord($c[$p])) < 128) { ++$p; } else { $u = unpack('Nlen', chr(ord($c[$p]) & 0x7f) . binarySubstr($c, $p + 1, 3)); $namelen = $u['len']; $p += 4; } if (($vlen = ord($c[$p])) < 128) { ++$p; } else { $u = unpack('Nlen', chr(ord($c[$p]) & 0x7f) . binarySubstr($c, $p + 1, 3)); $vlen = $u['len']; $p += 4; } $req->attrs->server[binarySubstr($c, $p, $namelen)] = binarySubstr($c, $p + $namelen, $vlen); $p += $namelen + $vlen; } } } elseif ($type === 5) { if ($c === '') { $req->attrs->stdin_done = TRUE; } $req->stdin($c); } if ($req->attrs->stdin_done && $req->attrs->params_done) { if (($order = ini_get('request_order')) || ($order = ini_get('variables_order'))) { for ($i = 0, $s = strlen($order); $i < $s; ++$i) { $char = $order[$i]; if ($char == 'G') { $req->attrs->request += $req->attrs->get; } elseif ($char == 'P') { $req->attrs->request += $req->attrs->post; } elseif ($char == 'C') { $req->attrs->request += $req->attrs->cookie; } } } else { $req->attrs->request = $req->attrs->get + $req->attrs->post + $req->attrs->cookie; } $this->timeLastReq = time(); } }
/** * @TODO * @param string $mask * @param string $str * @return string */ protected static function mask($mask, $str) { $out = ''; $l = strlen($str); $ml = strlen($mask); while (($o = strlen($out)) < $l) { $out .= binarySubstr($str, $o, $ml) ^ $mask; } return $out; }
/** * Called when new data received. * @param string New data. * @return void */ public function stdin($buf) { if ($this->state === self::STATE_DATAFLOW) { // Data exchange if ($this->slave) { $this->slave->write($buf); } return; } $this->buf .= $buf; start: $l = strlen($this->buf); if ($this->state === self::STATE_ROOT) { // Start if ($l < 2) { // Not enough data yet return; } $n = ord(binarySubstr($this->buf, 1, 1)); if ($l < $n + 2) { // Not enough data yet return; } $this->ver = binarySubstr($this->buf, 0, 1); $methods = binarySubstr($this->buf, 2, $n); $this->buf = binarySubstr($this->buf, $n + 2); if (!$this->pool->config->auth->value) { // No auth $m = ""; $this->state = self::STATE_AUTHORIZED; } elseif (strpos($methods, "") !== FALSE) { // Username/Password authentication $m = ""; $this->state = self::STATE_HANDSHAKED; } else { // No allowed methods $m = "ÿ"; $this->state = self::STATE_ABORTED; } $this->write($this->ver . $m); if ($this->state === self::STATE_ABORTED) { $this->finish(); } else { goto start; } } elseif ($this->state === self::STATE_HANDSHAKED) { // Handshaked if ($l < 3) { // Not enough data yet return; } $ver = binarySubstr($this->buf, 0, 1); if ($ver !== $this->ver) { $this->finish(); return; } $ulen = ord(binarySubstr($this->buf, 1, 1)); if ($l < 3 + $ulen) { // Not enough data yet return; } $username = binarySubstr($this->buf, 2, $ulen); $plen = ord(binarySubstr($this->buf, 1, 1)); if ($l < 3 + $ulen + $plen) { // Not enough data yet return; } $password = binarySubstr($this->buf, 2 + $ulen, $plen); if ($username != $this->pool->config->username->value || $password != $this->pool->config->password->value) { $this->state = 1; $m = ""; } else { $this->state = 3; $m = ""; } $this->buf = binarySubstr($this->buf, 3 + $ulen + $plen); $this->write($this->ver . $m); if ($this->state === self::STATE_ABORTED) { $this->finish(); } else { goto start; } } elseif ($this->state === self::STATE_AUTHORIZED) { // Ready for query if ($l < 4) { // Not enough data yet return; } $ver = binarySubstr($this->buf, 0, 1); if ($ver !== $this->ver) { $this->finish(); return; } $cmd = binarySubstr($this->buf, 1, 1); $atype = binarySubstr($this->buf, 3, 1); $pl = 4; if ($atype === "") { $address = inet_ntop(binarySubstr($this->buf, $pl, 4)); $pl += 4; } elseif ($atype === "") { $len = ord(binarySubstr($this->buf, $pl, 1)); ++$pl; $address = binarySubstr($this->buf, $pl, $len); $pl += $len; } elseif ($atype === "") { $address = inet_ntop(binarySubstr($this->buf, $pl, 16)); $pl += 16; } else { $this->finish(); return; } $u = unpack('nport', $bin = binarySubstr($this->buf, $pl, 2)); $port = $u['port']; $pl += 2; $this->buf = binarySubstr($this->buf, $pl); $connId = $this->pool->connectTo($this->destAddr = $address, $this->destPort = $port, 'SocksServerSlaveConnection'); if (!$connId) { // Early connection error $this->write($this->ver . ""); $this->finish(); } else { $this->slave = $this->pool->getConnectionById($connId); $this->slave->client = $this; $this->slave->write($this->buf); $this->buf = ''; $this->state = self::STATE_DATAFLOW; } } }
/** * Cursor's destructor. Sends a signal to the server. * @return void */ public function __destruct() { if (binarySubstr($this->id, 0, 1) === 'c') { $this->conn->pool->killCursors(array(binarySubstr($this->id, 1))); } }
public function __destruct() { if (binarySubstr($this->id, 0, 1) === 'c') { $this->appInstance->killCursors(array(binarySubstr($this->id, 1))); } }
public function stdin($buf) { $this->buf .= $buf; start: $l = strlen($this->buf); if ($l < 6) { return; } // not enough data yet. extract(unpack('Ctype/Chlen/Nblen', binarySubstr($this->buf, 0, 6))); if ($l < 6 + $hlen + $blen) { return; } // not enough data yet. $header = binarySubstr($this->buf, 6, $hlen); $body = binarySubstr($this->buf, 6 + $hlen, $blen); $this->buf = binarySubstr($this->buf, 6 + $hlen + $blen); list($reqId, $authKey) = explode('.', $header); if (isset($this->appInstance->queue[$reqId]->downstream) && $this->appInstance->queue[$reqId]->authKey == $authKey) { if ($type === WebSocketOverCOMET::IPCPacketType_C2S) { $this->appInstance->queue[$reqId]->downstream->onFrame($body, WebSocketServer::STRING); $this->appInstance->queue[$reqId]->atime = time(); } elseif ($type === WebSocketOverCOMET::IPCPacketType_S2C) { if (isset($this->appInstance->polling[$header])) { foreach ($this->appInstance->polling[$header] as $pollReqId) { if (isset($this->appInstance->queue[$pollReqId])) { $req = $this->appInstance->queue[$pollReqId]; if (isset($req->attrs->get['_script'])) { $q = Request::getString($req->attrs->get['q']); $body = 'var Response' . $q . ' = ' . $body . ";\n"; } else { $body .= "\n"; } $req->out($body); $req->finish(); } } } } elseif ($type === WebSocketOverCOMET::IPCPacketType_POLL) { $this->appInstance->queue[$reqId]->polling[] = $this->connId; $this->appInstance->queue[$reqId]->flushBufferedPackets($body); $this->appInstance->queue[$reqId]->atime = time(); } } else { if (Daemon::$settings['logerrors']) { Daemon::log('Undispatched packet (type = ' . $type . ', reqId = ' . $reqId . ', authKey = ' . $authKey . ', exists = ' . (isset($this->appInstance->queue[$reqId]) ? '1 - ' . get_class($this->appInstance->queue[$reqId]) : '0') . ').'); } } goto start; }
/** * Output some data * @param string $s String to out * @param boolean $flush ob_flush? * @return boolean Success */ public function out($s, $flush = true) { if ($flush) { if (!Daemon::$obInStack) { // preventing recursion ob_flush(); } } if ($this->aborted) { return false; } if (!isset($this->upstream)) { return false; } $l = strlen($s); $this->responseLength += $l; $this->ensureSentHeaders(); if ($this->attrs->chunked) { for ($o = 0; $o < $l;) { $c = min($this->upstream->pool->config->chunksize->value, $l - $o); $chunk = dechex($c) . "\r\n" . ($c === $l ? $s : binarySubstr($s, $o, $c)) . "\r\n"; if ($this->sendfp) { $this->sendfp->write($chunk); } else { $this->upstream->requestOut($this, $chunk); } $o += $c; } return true; } else { if ($this->sendfp) { $this->sendfp->write($s); return true; } if (Daemon::$compatMode) { echo $s; return true; } return $this->upstream->requestOut($this, $s); } }