<commit_before><?php

namespace Gini;

class RPC
{
    private $_url;
    private $_path;
    private $_cookie;
    private $_header = [];
    private $_uniqid = 1;

    private static $_RPCs = [];
    public static function of($name, $cookie = null, $header = [])
    {
        if (!self::$_RPCs[$name]) {
            $conf = \Gini\Config::get('app.rpc');
            $rpc = IoC::construct('\Gini\RPC', $conf[$name]['url'], null, $cookie, $header);
            self::$_RPCs[$name] = $rpc;
        }

        return self::$_RPCs[$name];
    }

    public function __construct($url, $path = null, $cookie = null, $header = [])
    {
        $this->_url = $url;
        $this->_path = $path;
        $this->_cookie = $cookie ?: IoC::construct('\Gini\HTTP\Cookie');
        $this->_header = (array) $header;
    }

    public function __get($name)
    {
        return IoC::construct('\Gini\RPC', $this->_url, $this->_path ? $this->_path.'/'.$name : $name, $this->_cookie, $this->_header);
    }

    public function __call($method, $params)
    {
        if ($method === __FUNCTION__) {
            return;
        }

        if ($this->_path) {
            $method = $this->_path.'/'.$method;
        }

        $id = base_convert($this->_uniqid++, 10, 36);

        $rpcTimeout = Config::get('rpc.timeout');
        $timeout = $rpcTimeout[$method] ?: $rpcTimeout['default'];

        $raw_data = $this->post(J([
            'jsonrpc' => '2.0',
            'method' => $method,
            'params' => $params,
            'id' => $id,
        ]), $timeout);

        \Gini\Logger::of('core')->debug('RPC <= {data}', ['data' => $raw_data]);

        $data = @json_decode($raw_data, true);
        if (!isset($data['result'])) {
            if (isset($data['error'])) {
                $message = sprintf('remote error: %s', $data['error']['message']);
                $code = $data['error']['code'];
                throw IoC::construct('\Gini\RPC\Exception', $message, $code);
            } elseif (is_null($data)) {
                $message = sprintf('unknown error with raw data: %s', $raw_data ?: '(null)');
                throw IoC::construct('\Gini\RPC\Exception', $message, -32400);
            } elseif ($id != $data['id']) {
                $message = 'wrong response id!';
                throw IoC::construct('\Gini\RPC\Exception', $message, -32400);
            }
        }

        return $data['result'];
    }

    public function setHeader(array $header)
    {
        // if format is ['xx: xx'], convert it to ['xx' => 'xx']
        $kh = [];
        foreach ($header as $k => $h) {
            if (is_numeric($k)) {
                list($k, $v) = explode(':', $h, 2);
                $kh[trim($k)] = trim($v);
            } else {
                $kh[$k] = $h;
            }
        }
        $this->_header = array_merge($this->_header, $kh);
    }

    public function post($post_data, $timeout = 5)
    {
        $cookie_file = $this->_cookie->file;

        $ch = curl_init();

        $this->_header['Content-Type'] = 'application/json';
        // convert to Key: Value format
        $header = array_map(function ($k, $v) {
            return "$k: $v";
        }, array_keys($this->_header), $this->_header);

        curl_setopt_array($ch, [
            CURLOPT_DNS_USE_GLOBAL_CACHE => false,
            CURLOPT_DNS_CACHE_TIMEOUT => 0,
            CURLOPT_COOKIEJAR => $cookie_file,
            CURLOPT_COOKIEFILE => $cookie_file,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_URL => $this->_url,
            CURLOPT_AUTOREFERER => false,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_RETURNTRANSFER => true,
            // CURLOPT_FRESH_CONNECT => false,
            CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',
            CURLOPT_HTTPHEADER => $header,
        ]);

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

        \Gini\Logger::of('core')->debug('RPC => {url}: {data}', ['url' => $this->_url, 'data' => $post_data]);

        $data = curl_exec($ch);
        $errno = curl_errno($ch);
        if ($errno) {
            $message = curl_error($ch);
            curl_close($ch);

            \Gini\Logger::of('core')->error('RPC cURL error: {url}: {message}', ['url' => $this->_url, 'message' => $message]);
            throw IoC::construct('\Gini\RPC\Exception', "transport error: $message", -32300);
        }

        curl_close($ch);

        return $data;
    }
}<commit_msg>add X-Gini-RPC-Session in HTTP headers to trace HTTP JSON-RPC<commit_after><?php

namespace Gini;

class RPC
{
    private $_url;
    private $_path;
    private $_cookie;
    private $_header = [];
    private $_uniqid = 1;

    private static $_RPCs = [];
    public static function of($name, $cookie = null, $header = [])
    {
        if (!self::$_RPCs[$name]) {
            $conf = \Gini\Config::get('app.rpc');
            $rpc = IoC::construct('\Gini\RPC', $conf[$name]['url'], null, $cookie, $header);
            self::$_RPCs[$name] = $rpc;
        }

        return self::$_RPCs[$name];
    }

    public function __construct($url, $path = null, $cookie = null, $header = [])
    {
        $this->_url = $url;
        $this->_path = $path;
        $this->_cookie = $cookie ?: IoC::construct('\Gini\HTTP\Cookie');
        $this->_header = (array) $header;
    }

    public function __get($name)
    {
        return IoC::construct('\Gini\RPC', $this->_url, $this->_path ? $this->_path.'/'.$name : $name, $this->_cookie, $this->_header);
    }

    public function __call($method, $params)
    {
        if ($method === __FUNCTION__) {
            return;
        }

        if ($this->_path) {
            $method = $this->_path.'/'.$method;
        }

        $id = base_convert($this->_uniqid++, 10, 36);

        $rpcTimeout = Config::get('rpc.timeout');
        $timeout = $rpcTimeout[$method] ?: $rpcTimeout['default'];

        $this->_header['X-Gini-RPC-Session'] = $_SERVER['HTTP_X_GINI_RPC_SESSION'] ?: $this->_url.'/'.$method;

        $raw_data = $this->post(J([
            'jsonrpc' => '2.0',
            'method' => $method,
            'params' => $params,
            'id' => $id,
        ]), $timeout);

        \Gini\Logger::of('http-jsonrpc')->debug('RPC <= {data}', ['data' => $raw_data]);

        $data = @json_decode($raw_data, true);
        if (!isset($data['result'])) {
            if (isset($data['error'])) {
                $message = sprintf('remote error: %s', $data['error']['message']);
                $code = $data['error']['code'];
                throw IoC::construct('\Gini\RPC\Exception', $message, $code);
            } elseif (is_null($data)) {
                $message = sprintf('unknown error with raw data: %s', $raw_data ?: '(null)');
                throw IoC::construct('\Gini\RPC\Exception', $message, -32400);
            } elseif ($id != $data['id']) {
                $message = 'wrong response id!';
                throw IoC::construct('\Gini\RPC\Exception', $message, -32400);
            }
        }

        return $data['result'];
    }

    public function setHeader(array $header)
    {
        // if format is ['xx: xx'], convert it to ['xx' => 'xx']
        $kh = [];
        foreach ($header as $k => $h) {
            if (is_numeric($k)) {
                list($k, $v) = explode(':', $h, 2);
                $kh[trim($k)] = trim($v);
            } else {
                $kh[$k] = $h;
            }
        }
        $this->_header = array_merge($this->_header, $kh);
    }

    public function post($post_data, $timeout = 5)
    {
        $cookie_file = $this->_cookie->file;

        $ch = curl_init();

        $this->_header['Content-Type'] = 'application/json';

        // convert to Key: Value format
        $header = array_map(function ($k, $v) {
            return "$k: $v";
        }, array_keys($this->_header), $this->_header);

        curl_setopt_array($ch, [
            CURLOPT_DNS_USE_GLOBAL_CACHE => false,
            CURLOPT_DNS_CACHE_TIMEOUT => 0,
            CURLOPT_COOKIEJAR => $cookie_file,
            CURLOPT_COOKIEFILE => $cookie_file,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_URL => $this->_url,
            CURLOPT_AUTOREFERER => false,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_RETURNTRANSFER => true,
            // CURLOPT_FRESH_CONNECT => false,
            CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',
            CURLOPT_HTTPHEADER => $header,
        ]);

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

        \Gini\Logger::of('http-jsonrpc')->debug('RPC => {url}: {data}', ['url' => $this->_url, 'data' => $post_data]);

        $data = curl_exec($ch);
        $errno = curl_errno($ch);
        if ($errno) {
            $message = curl_error($ch);
            curl_close($ch);

            \Gini\Logger::of('http-jsonrpc')->error('RPC cURL error: {url}: {message}', ['url' => $this->_url, 'message' => $message]);
            throw IoC::construct('\Gini\RPC\Exception', "transport error: $message", -32300);
        }

        curl_close($ch);

        return $data;
    }
}
{'fraction_non_alphanumeric': 0.18406391254073373, 'fraction_numerical': 0.006727635866708714, 'mean_word_length': 2.0522938723131214, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 109, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2178967', 'n_tokens_mistral': 3115, 'n_tokens_neox': 3006, 'n_words': 587}