public static function readfileChunked($path, $cb, $chunkcb, $pri = EIO_PRI_DEFAULT) { if (!FS::$supported) { call_user_func($chunkcb, $path, $r = readfile($path)); call_user_func($cb, $r !== false); return; } FS::open($path, 'r', function ($file) use($cb, $chunkcb, $pri) { if (!$file) { call_user_func($cb, $path, false); } $file->readAllChunked($cb, $chunkcb, $pri); }, null, $pri); }
/** * Parse request body * @return void */ public function parseStdin() { do { if ($this->boundary === false) { break; } $continue = false; if ($this->mpartstate === self::MPSTATE_SEEKBOUNDARY) { // 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 = self::MPSTATE_HEADERS; $continue = true; } } elseif ($this->mpartstate === self::MPSTATE_HEADERS) { // 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'], '"'); $name = $this->mpartcondisp['name']; if (isset($this->mpartcondisp['filename'])) { $this->mpartcondisp['filename'] = trim($this->mpartcondisp['filename'], '"'); if (!ini_get('file_uploads')) { break; } $this->attrs->files[$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[$name]['fp'] = false; $this->attrs->files[$name]['error'] = UPLOAD_ERR_NO_TMP_DIR; } else { $this->attrs->files[$name]['tmp_name'] = FS::tempnam($tmpdir, 'php'); if ($this->oldFashionUploadFP) { $this->attrs->files[$name]['fp'] = fopen($this->attrs->files[$name]['tmp_name'], 'c+'); } else { $req = $this; if (FS::$supported) { $this->conn->lockRead(); } FS::open($this->attrs->files[$name]['tmp_name'], 'c+', function ($fp) use($req, $name) { if (!$fp) { $req->attrs->files[$name]['error'] = UPLOAD_ERR_CANT_WRITE; } $req->attrs->files[$name]['fp'] = $fp; if (FS::$supported) { $req->conn->unlockRead(); } }); } } $this->mpartstate = self::MPSTATE_UPLOAD; } 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 === self::MPSTATE_HEADERS) { $this->mpartstate = self::MPSTATE_BODY; } $continue = true; } } elseif ($this->mpartstate === self::MPSTATE_BODY || $this->mpartstate === self::MPSTATE_UPLOAD) { // 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) { // we have the whole Part in buffer $chunk = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset); if ($this->mpartstate === self::MPSTATE_BODY && isset($this->mpartcondisp['name'])) { $this->attrs->post[$this->mpartcondisp['name']] .= $chunk; } elseif ($this->mpartstate === self::MPSTATE_UPLOAD && isset($this->mpartcondisp['filename'])) { if (!isset($this->attrs->files[$this->mpartcondisp['name']]['fp'])) { return; // fd is not ready yet, interrupt } if ($fp = $this->attrs->files[$this->mpartcondisp['name']]['fp']) { if ($this->oldFashionUploadFP) { fwrite($fp, $chunk); } else { $fp->write($chunk); } } $this->attrs->files[$this->mpartcondisp['name']]['size'] += $p - $this->mpartoffset; } if ($ndl === "\r\n--" . $this->boundary . "--\r\n") { // end of whole message $this->mpartoffset = $p + strlen($ndl); $this->mpartstate = self::MPSTATE_SEEKBOUNDARY; $this->stdin_done = true; } else { $this->mpartoffset = $p; $this->mpartstate = self::MPSTATE_HEADERS; // let's read the next part $continue = true; } $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset); $this->mpartoffset = 0; } else { // we have only piece of Part in buffer $p = strlen($this->attrs->stdinbuf) - strlen($ndl); if ($p !== false) { $chunk = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset); if ($this->mpartstate === self::MPSTATE_BODY && isset($this->mpartcondisp['name'])) { $this->attrs->post[$this->mpartcondisp['name']] .= $chunk; } elseif ($this->mpartstate === self::MPSTATE_UPLOAD && isset($this->mpartcondisp['filename'])) { if (!isset($this->attrs->files[$this->mpartcondisp['name']]['fp'])) { return; // fd is not ready yet, interrupt } if ($fp = $this->attrs->files[$this->mpartcondisp['name']]['fp']) { if ($this->oldFashionUploadFP) { fwrite($fp, $chunk); } else { $fp->write($chunk); } } $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. * @return void */ public function onRead() { start: $buf = $this->read($this->readPacketSize); if ($this->state === self::STATE_ROOT) { if (strlen($buf) === 0) { return; } if (strpos($buf, "<policy-file-request/>") !== false) { if (($FP = FlashPolicyServer::getInstance()) && $FP->policyData) { $this->write($FP->policyData . ""); } $this->finish(); return; } $rid = ++Daemon::$process->reqCounter; $this->state = self::STATE_HEADERS; $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->id = $rid; $req->attrs->params_done = false; $req->attrs->stdin_done = false; $req->attrs->stdinbuf = ''; $req->attrs->stdinlen = 0; $req->attrs->inbuf = ''; $req->attrs->chunked = false; $req->queueId = $rid; $req->conn = $this; $this->req = $req; } else { if (!$this->req) { Daemon::log('Unexpected input (HTTP request).'); return; } $req = $this->req; } if ($this->state === self::STATE_HEADERS) { $req->attrs->inbuf .= $buf; $buf = ''; if (($p = strpos($req->attrs->inbuf, "\r\n\r\n")) !== false) { $headers = binarySubstr($req->attrs->inbuf, 0, $p); $headersArray = explode("\r\n", $headers); $req->attrs->inbuf = binarySubstr($req->attrs->inbuf, $p + 4); $command = explode(' ', $headersArray[0]); $u = isset($command[1]) ? parse_url($command[1]) : false; if ($u === false) { $this->badRequest($req); return; } $req->attrs->server['REQUEST_METHOD'] = $command[0]; $req->attrs->server['REQUEST_TIME'] = time(); $req->attrs->server['REQUEST_TIME_FLOAT'] = microtime(true); $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'] = isset($command[2]) ? $command[2] : 'HTTP/1.1'; list($req->attrs->server['REMOTE_ADDR'], $req->attrs->server['REMOTE_PORT']) = explode(':', $this->addr); for ($i = 1, $n = sizeof($headersArray); $i < $n; ++$i) { $e = explode(': ', $headersArray[$i]); if (isset($e[1])) { $req->attrs->server['HTTP_' . strtoupper(strtr($e[0], HTTPRequest::$htr))] = $e[1]; } } if (isset($u['host'])) { $req->attrs->server['HTTP_HOST'] = $u['host']; } $req->attrs->params_done = true; if (isset($req->attrs->server['HTTP_CONNECTION']) && preg_match('~(?:^|\\W)Upgrade(?:\\W|$)~i', $req->attrs->server['HTTP_CONNECTION']) && isset($req->attrs->server['HTTP_UPGRADE']) && strtolower($req->attrs->server['HTTP_UPGRADE']) === 'websocket') { if ($this->pool->WS) { $this->pool->WS->inheritFromRequest($req, $this->pool); return; } else { $this->finish(); return; } } else { $req = Daemon::$appResolver->getRequest($req, $this->pool, isset($this->pool->config->responder->value) ? $this->pool->config->responder->value : null); $req->conn = $this; $this->req = $req; } if ($req instanceof stdClass) { $this->endRequest($req, 0, 0); } else { if ($this->pool->config->sendfile->value && (!$this->pool->config->sendfileonlybycommand->value || isset($req->attrs->server['USE_SENDFILE'])) && !isset($req->attrs->server['DONT_USE_SENDFILE'])) { $fn = FS::tempnam($this->pool->config->sendfiledir->value, $this->pool->config->sendfileprefix->value); $req->sendfp = FS::open($fn, 'wb'); $req->header('X-Sendfile: ' . $fn); } $buf = $req->attrs->inbuf; $req->attrs->inbuf = ''; $this->state = self::STATE_CONTENT; } } else { return; // not enough data } } if ($this->state === self::STATE_CONTENT) { $req->stdin($buf); $buf = ''; if ($req->attrs->stdin_done) { $this->state = self::STATE_ROOT; } else { return; } } if ($req->attrs->stdin_done && $req->attrs->params_done) { if ($this->pool->variablesOrder === null) { $req->attrs->request = $req->attrs->get + $req->attrs->post + $req->attrs->cookie; } else { for ($i = 0, $s = strlen($this->pool->variablesOrder); $i < $s; ++$i) { $char = $this->pool->variablesOrder[$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; } } } Daemon::$process->timeLastReq = time(); } else { goto start; } }
/** * Open logs. * @return void */ public static function openLogs() { if (Daemon::$config->logging->value) { Daemon::$logpointer = fopen(Daemon::$config->logstorage->value, 'a'); if (isset(Daemon::$config->group->value)) { chgrp(Daemon::$config->logstorage->value, Daemon::$config->group->value); // @TODO: rewrite to async I/O } if (isset(Daemon::$config->user->value)) { chown(Daemon::$config->logstorage->value, Daemon::$config->user->value); // @TODO: rewrite to async I/O } if (Daemon::$process instanceof Daemon_WorkerThread && FS::$supported) { FS::open(Daemon::$config->logstorage->value, 'a!', function ($file) { Daemon::$logpointerAsync = $file; if (!$file) { return; } }); } } else { Daemon::$logpointer = null; Daemon::$logpointerAsync = null; } }