Загрузка данных


# Функция принимает 5 параметров:
# 1. response: locust.FastResponse (https://docs.locust.io/en/stable/_modules/locust/contrib/fasthttp.html#FastResponse) для HTTP-зарпосов
# и тело ответа для всех асинхронных операций
# 2. Объект операции (то, вы создавали здесь в UI). Формат операции и ее ключи можно посмотреть в итоговом JSON
# 3. Словарь, "переменная": "значение" с локальными переменными потока
# 4. Список созданных листенеров Kafka, WS, gRPC
# 5. Объект класса UserClass
#
# И должна возвращать 4 параметра:
# 1. Успешность операции (None, если успешность должна быть определена стандартными средствами
# В http по коду ответа, в других протоколах всегда успешно))
# 2. id следующей операции (None, если на этом нужно завершить итерацию)
# 3. Словарь, "переменная": "значение" (может быть пустым - {}) для локальных переменных потока
# 4. Словарь, "переменная": "значение" (может быть пустым - {}) для глобальных переменных теста


from typing import NamedTuple, Optional
import re
# from locust.contrib.fasthttp import ResponseContextManager
# from app.clients.class_builder import UserClass
# from app.data_classes.config import OperationTypes
# from app.data_classes.user_vars import UserVar
# from app.users.async_response_readers import AsyncResponseReader, WebSocketResponseReader, KafkaResponseReader


class ReturnFromPostProcessor(NamedTuple):
    is_operation_successful: bool
    next_operation_id: Optional[str]
    local_vars: dict
    global_vars: dict

# def post_processor(response: Union[ResponseContextManager, str, bytes], operation: OperationTypes, user_vars: dict[str, UserVar], listeners: list[AsyncResponseReader], user_class: UserClass, *args, **kwargs) -> ReturnFromPostProcessor:
def post_processor(
    response, operation, user_vars, listener, user_class, *args, **kwargs
) -> ReturnFromPostProcessor:
    if hasattr(response, "status_code") and response.status_code == 200: # if isinstance(response, ResponseContextManager)
        if hasattr(response, "headers") and ('set-cookie' in response.headers) and (len(response.headers["set-cookie"]) > 1):
            try:
                resp_json = response.json()
                if not isinstance(resp_json, dict):
                    return ReturnFromPostProcessor(False, None, {}, {})

                success_data = resp_json.get("success")
                if not isinstance(success_data, dict):
                    return ReturnFromPostProcessor(False, None, {}, {})
                
                sessionId = success_data.get("sbbolSessionId")

                if "csrftoken" not in response.headers:

                    return ReturnFromPostProcessor(False, None, {}, {})

                csrftoken = response.headers["csrftoken"]

                conf = f"{user_class.environment.config=}"
                position = conf.find("UC51_prelogin_mob_AUTH_pp.py")

                if position == -1:
                    return ReturnFromPostProcessor(False, None, {}, {})

                pacing = conf[position:position+130]
                pacing = pacing.split(",")[3]
                position = pacing.find("’")
                pacing = pacing[position+1:len(pacing)-1]
                position = pacing.find("-")
                numA = pacing[0:position]
                numB = pacing[position+1:len(pacing)]

                iteration = 0
                total_iterations = 60


                index_header_set_cookies_login = conf.split("UserVar(name=‘index_header_set_cookies_login’, next_value_rule=<NextValueRule.NEVER: ‘never’>, select_rule=None, value=‘")[1][:1]
                idx = int(index_header_set_cookies_login)



                # Проверяем, что индексов в set-cookie достаточно

                if len(response.headers["set-cookie"]) > idx + 1:

                    TOKEN = response.headers["set-cookie"][idx]
                    TOKEN_Split = TOKEN.split(';')[0]
                    SESSION = response.headers["set-cookie"][idx+1]
                    SESSION_split = SESSION.split(';')[0]

                    return ReturnFromPostProcessor(True, 'UC51_POST_rest_wholesale_client', {"numA":numA, "numB":numB, "iteration":iteration, "total_iterations":total_iterations, "UFS-TOKEN": TOKEN_Split,"UFS-SESSION":SESSION_split,"csrftoken":csrftoken,"sessionId":sessionId}, {})
            
            except (ValueError, KeyError, IndexError, AttributeError, TypeError):

                # Любые ошибки парсинга или несовпадения типов прерывают операцию
                pass



        return ReturnFromPostProcessor(False, None, {}, {})



    elif hasattr(response, "status_code") and response.status_code >= 400:
        return ReturnFromPostProcessor(False, None, {}, {})

    else:
        return ReturnFromPostProcessor(False, None, {}, {})         
            



{
    "success": {
        "sbbolSessionId": "000000000-0000-0000-0000-000000000000",
        "sbbolRouteSessionId": "0||000000000-0000-0000-0000-000000000000"
    }
}

200


хэдеры ответа

Server: SynGX
Date: Thu, 25 Jun 2026 10:11:23 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
set-cookie: UFS-TOKEN=db0ca47f-6dbd-45f2-9ffc-fe95987dd030; Path=/; Secure; HttpOnly
set-cookie: UFS-SESSION=lh2C4rq_Tm-T5pQWdqeNmDzYBNg78hI2BRqR_R1FanfS0NsXnjg4TPzxlWDzWbnj; Path=/; Secure; HttpOnly
set-cookie: UFS-SESSION="bd20b86465afb022"; Path=/sds-sticky-session/nonexistent-path; HttpOnly
csrftoken: 7201777588841702108
csrftokensbbid: e08c2fdf-4a8b-4365-bd13-6d118844539a
x-envoy-upstream-service-time: 899
Access-Control-Allow-Credentials: true
Allow: GET, POST, PUT, DELETE, HEAD
Allow: GET, POST, PUT, DELETE, HEAD
X-Frame-Options: SAMEORIGIN
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Encoding: gzip
ufs-business-id: e192a0243cf37adba411850f3236c31b