dongxiong1935 2018-06-12 04:31 采纳率: 0%
浏览 35

如何保持php:// stdout永远打开

I find a bug with php integrated to Ejabberd in authentication mode. This script, open php://stdin and php://stdout to read/write data, but after a while it will be hang so the authentication feature could not work.

My code:

<?php

/**
 * A class extending EjabberdExternalAuth may optionally implement this interface
 * to support additional, non-critical methods for adding and removing users.
 */
interface EjabberdExternalAuth_UserManagement {

    /**
     * Corresponds to `setpass` operation.
     */
    public function setPassword($user, $server, $password);

    /**
     * Corresponds to `tryregister` operation.
     */
    public function register($user, $server, $password);

    /**
     * Corresponds to `removeuser` operation.
     */
    public function remove($user, $server);

    /**
     * Corresponds to `removeuser3` operation.
     */
    public function removeSafely($user, $server, $password);

}


abstract class EjabberdExternalAuth {

    private $db     = null;
    private $log    = null;
    private $stdin  = null;
    private $stdout = null;

    public static $logLevel = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_INFO, LOG_KERN);

    /**
     * Corresponds to `auth` operation.
     */
    abstract protected function authenticate($user, $server, $password);

    /**
     * Corresponds to `isuser` operation.
     */
    abstract protected function exists($user, $server);


    final public function __construct(PDO $db = null, $log = null) {
        set_error_handler(array($this, 'errorHandler'));

        $this->db     = $db;
        $this->stdin  = fopen('php://stdin', 'rb');
        $this->stdout = fopen('php://stdout', 'wb');

        if ($log) {
            $this->log = fopen($log, 'a');
        }

        $this->log('Starting auth service...', LOG_INFO);

        try {
            $this->work();
        } catch (Exception $e) {
            $this->log($e->getMessage());
        }

        $this->log('Exiting...', LOG_INFO);
    }

    final protected function db() {
        return $this->db;
    }

    final private function work() {
        $this->log('Entering event loop...', LOG_INFO);

        while (true) {
            try {
                $message = $this->read();
                $message = $this->parseMessage($message);
            } catch (UnexpectedValueException $e) {
                $this->log($e->getMessage());
                continue;
            }

            $this->log('Received message: ' . json_encode($message), LOG_DEBUG);

            $response = false;

            switch ($message['command']) {
                case 'auth' :
                    $response = $this->authenticate($message['user'], $message['server'], $message['password']);
                    break;
                case 'isuser' :
                    $response = $this->exists($message['user'], $message['server']);
                    break;
            }

            if ($this instanceof EjabberdExternalAuth_UserManagement) {
                switch ($message['command']) {
                    case 'setpass' :
                        $response = $this->setPassword($message['user'], $message['server'], $message['password']);
                        break;
                    case 'tryregister' :
                        $response = $this->register($message['user'], $message['server'], $message['password']);
                        break;
                    case 'removeuser' :
                        $response = $this->remove($message['user'], $message['server']);
                        break;
                    case 'removeuser3' :
                        $response = $this->removeSafely($message['user'], $message['server'], $message['password']);
                        break;
                }
            }

            $this->respond($response);
        }
    }

    final private function read() {
        $length = fgets($this->stdin, 3);

        if (feof($this->stdin)) {
            throw new RuntimeException('Pipe broken');
        }

        $length = current(unpack('n', $length));
        if (!$length) {
            throw new UnexpectedValueException("Invalid length value, won't continue reading");
        }

        $message = fgets($this->stdin, $length + 1);
        return $message;
    }

    final private function parseMessage($message) {
        $message = explode(':', $message, 4);
        if (count($message) < 3) {
            throw new UnexpectedValueException('Message is too short: ' . join(':', $message));
        }

        list($command, $user, $server) = $message;
        $password = isset($message[3]) ? $message[3] : null;
        return compact('command', 'user', 'server', 'password');
    }

    final private function respond($status) {
        $message = pack('nn', 2, (int)$status);
        $this->log('Sending response: ' . bin2hex($message), LOG_DEBUG);
        fwrite($this->stdout, $message);
    }

    final protected function log($message, $severity = LOG_ERR) {
        if ($this->log && in_array($severity, self::$logLevel, true)) {
            static $types = array(
                LOG_EMERG   => 'EMERGENCY',
                LOG_ALERT   => 'ALERT',
                LOG_CRIT    => 'CRITICAL',
                LOG_ERR     => 'ERROR',
                LOG_WARNING => 'WARNING',
                LOG_NOTICE  => 'NOTICE',
                LOG_INFO    => 'INFO',
                LOG_DEBUG   => 'DEBUG',
                LOG_KERN    => 'KERNEL'
            );

            $message = sprintf('%s <%s> %9s: %s', date('Y-m-d H:i:s'), getmypid(), isset($types[$severity]) ? $types[$severity] : $types[LOG_ERR], $message);
            fwrite($this->log, $message . PHP_EOL);
        }
    }

    final protected function errorHandler($errno, $errstr, $errfile, $errline, array $errcontext) {
        $this->log("($errno) $errstr in $errfile on line $errline");
        return false;
    }

}

I think that, something is wrong, example: the STDOUT may be broken, so I need to write data to php://stdout forever, so how could I make sure the stream will never be broken.

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 Mac系统vs code使用phpstudy如何配置debug来调试php
    • ¥15 目前主流的音乐软件,像网易云音乐,QQ音乐他们的前端和后台部分是用的什么技术实现的?求解!
    • ¥60 pb数据库修改与连接
    • ¥15 spss统计中二分类变量和有序变量的相关性分析可以用kendall相关分析吗?
    • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
    • ¥20 神经网络Sequential name=sequential, built=False
    • ¥16 Qphython 用xlrd读取excel报错
    • ¥15 单片机学习顺序问题!!
    • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
    • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)