重命名HTTPCookieManager,并简化HTTPCookieManager实现(不再通过Cookie中http_cookie_manager_id实现)

This commit is contained in:
azhengzz 2021-03-28 13:38:45 +08:00
parent 3db0ae1b24
commit 3e84aecc60
6 changed files with 45 additions and 93 deletions

View File

@ -9,7 +9,7 @@ from logging.handlers import QueueHandler, QueueListener
import click
from app.config import Config, proj_dir
from app.extensions import (db, migrate, login_manager, mail, csrf, bootstrap, http_cookie_manager, session_id_manager,
from app.extensions import (db, migrate, login_manager, mail, csrf, bootstrap, session_id_manager,
socketio, dispatcher_scheduler)
from app import models
from app.template_global import (render_to_json, sort_by_order_in_module, sort_by_order_in_logic_controller,
@ -68,7 +68,6 @@ def register_extensions(app: Flask):
csrf.init_app(app=app)
bootstrap.init_app(app=app)
socketio.init_app(app=app, async_mode='threading')
http_cookie_manager.init_app(app=app)
session_id_manager.init_app(app=app)
dispatcher_scheduler.init_app(app=app)

View File

@ -1,11 +1,11 @@
# coding=utf-8
from app.models import Case, Dispatcher
from app.extensions import http_cookie_manager
from app.cores.logger import DispatcherLogger
from app.cores.dictionaries import DISPATCHER_TYPE, REPORT_RESULT
from app.cores.dispatcher import AbstractCaseDispatcher
from app.cores.case.http.request_util import handle_url
from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager
from app.cores.case.http.http_request_header_pool_manager import HTTPRequestHeaderPoolManager
from app.cores.case.base.script import exec_postprocessor_script, exec_preprocessor_script
from app.cores.case.base.last_result import LastResult
@ -61,7 +61,7 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher):
def set_up(self):
super().set_up()
# 获取cookie
self.rcj = http_cookie_manager.get_request_cookie_jar(type=self.dispatcher_type)
self.rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=self.dispatcher_type)
# 获取HTTP请求头
self.headers = HTTPRequestHeaderPoolManager.get_http_request_header(project_id=self.case.scene.module.project.id)
# 预处理脚本执行
@ -128,7 +128,7 @@ class HTTPCaseDispatcher(AbstractCaseDispatcher):
# 更新结果数据2
LastResult.update_result_to_last_result(result=self.expectations_result)
# cookie处理
http_cookie_manager.update_cookie_pool(
HTTPCookiePoolManager.update_cookie_pool(
rcj=self.request_.response.cookies,
type=self.dispatcher_type,
)

View File

@ -1,27 +1,26 @@
# coding=utf-8
from flask import request, session
from flask import session
from requests.cookies import RequestsCookieJar
from datetime import timedelta, datetime
import uuid
from typing import Optional, List, Mapping
from app.extensions import session_id_manager
from app.cores.dictionaries import DISPATCHER_TYPE, DISPATCHER_TRIGGER_TYPE
# requests库会在发起请求时根据请求服务器的domain选择合适的cookie作为请求头中的cookie数据发出
# 可参考代码 requests.models.prepare_cookies()
class HTTPCookieManager:
class HTTPCookiePoolManager:
"""
管理自动化测试中请求头Cookie数据
CookiePool = {
http_cookie_manager_id1: {
session_id1: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
},
http_cookie_manager_id1: {
session_id2: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
},
@ -41,15 +40,8 @@ class HTTPCookieManager:
# 对于定时构建的项目使用另一个Cookie池保存key为项目id
ScheduleCookiePool = {}
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
app.http_cookie_manager = self
app.after_request(self._update_cookie)
def update_cookie_pool(self, rcj: RequestsCookieJar = None, type: str = None):
@classmethod
def update_cookie_pool(cls, rcj: RequestsCookieJar = None, type: str = None):
"""
:param rcj: RequestsCookieJar对象一般由应答头获得 response.cookies
:param type: 需要更新到的cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
@ -60,11 +52,11 @@ class HTTPCookieManager:
project_id = session.get('project_id')
if project_id:
if project_id not in __class__.ScheduleCookiePool:
self._add_empty_to_cookie_pool()
cls._add_empty_to_cookie_pool()
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
if http_cookie_manager_id not in __class__.CookiePool:
self._add_empty_to_cookie_pool()
session_id = session_id_manager.get_session_id()
if session_id not in __class__.CookiePool:
cls._add_empty_to_cookie_pool()
# 如果入参rcj为空的RequestsCookieJar则不更新直接返回
if len(rcj) == 0:
return
@ -77,22 +69,24 @@ class HTTPCookieManager:
if old_rcj is None:
__class__.ScheduleCookiePool[project_id] = rcj
else:
self._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
cls._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
old_rcj = __class__.CookiePool[http_cookie_manager_id][type]
session_id = session_id_manager.get_session_id()
old_rcj = __class__.CookiePool[session_id][type]
if old_rcj is None:
__class__.CookiePool[http_cookie_manager_id][type] = rcj
__class__.CookiePool[session_id][type] = rcj
else:
self._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
cls._update_request_cookie_jar(old_rcj=old_rcj, new_rcj=rcj)
else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type)
def _update_request_cookie_jar(self, old_rcj: RequestsCookieJar, new_rcj: RequestsCookieJar):
@classmethod
def _update_request_cookie_jar(cls, old_rcj: RequestsCookieJar, new_rcj: RequestsCookieJar):
"""将新的rcj更新到老的rcj上"""
old_rcj.update(other=new_rcj)
def _add_empty_to_cookie_pool(self):
@classmethod
def _add_empty_to_cookie_pool(cls):
"""
为当前会话在cookie pool中添加一个新的默认值为RequestsCookieJar()
:return: None
@ -104,15 +98,16 @@ class HTTPCookieManager:
project_id: RequestsCookieJar(),
})
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
session_id = session_id_manager.get_session_id()
__class__.CookiePool.update({
http_cookie_manager_id: {
session_id: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
}
})
def get_request_cookie_jar(self, type: str) -> Optional[RequestsCookieJar]:
@classmethod
def get_request_cookie_jar(cls, type: str) -> Optional[RequestsCookieJar]:
"""
获取当前会话中指定类型的cookie数据
:param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
@ -125,12 +120,13 @@ class HTTPCookieManager:
return
return __class__.ScheduleCookiePool[project_id]
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
if http_cookie_manager_id not in __class__.CookiePool:
session_id = session_id_manager.get_session_id()
if session_id not in __class__.CookiePool:
return
return __class__.CookiePool[http_cookie_manager_id][type]
return __class__.CookiePool[session_id][type]
def clear_and_reset(self, rcj: RequestsCookieJar, cookies: List[Mapping]):
@classmethod
def clear_and_reset(cls, rcj: RequestsCookieJar, cookies: List[Mapping]):
"""
清除并重置RequestsCookieJar
:param rcj: 待重置的RequestsCookieJar来自cookie pool中该会话cookie
@ -143,63 +139,21 @@ class HTTPCookieManager:
for cookie in cookies:
rcj.set(**cookie)
def _update_cookie(self, response):
"""注册到app.after_request, 当浏览器没有该cookie时将会在应答头中带上该cookie"""
# 判断当前浏览器是否保存有HTTPCookieManager ID的Cookie
# 如果没有的话则会在该浏览器中新增一个HTTPCookieManager ID的Cookie并将ID值保存在CookiePool中
if HTTPCookieManagerConfig.COOKIE_NAME not in request.cookies:
duration = HTTPCookieManagerConfig.COOKIE_DURATION
if isinstance(duration, int):
duration = timedelta(seconds=duration)
try:
expires = datetime.utcnow() + duration
except TypeError:
raise Exception('HTTPCookieManagerConfig.COOKIE_DURATION must be a ' +
'datetime.timedelta, instead got: {0}'.format(duration))
uuid_ = str(uuid.uuid1())
response.set_cookie(HTTPCookieManagerConfig.COOKIE_NAME,
value=uuid_, # 唯一标识
expires=expires,
domain=None,
path='/',
secure=HTTPCookieManagerConfig.COOKIE_SECURE,
httponly=HTTPCookieManagerConfig.COOKIE_HTTPONLY)
else:
pass
return response
def clear_cookie_pool(self, type: str = None):
@classmethod
def clear_cookie_pool(cls, type: str = None):
"""
清空当前会话中的cookie数据
:param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
:return:
"""
if self.get_request_cookie_jar(type=type) is not None:
if cls.get_request_cookie_jar(type=type) is not None:
if type in [DISPATCHER_TYPE.DEBUG, DISPATCHER_TYPE.BUILD]:
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id')
if project_id:
__class__.ScheduleCookiePool[project_id] = RequestsCookieJar()
else:
http_cookie_manager_id = self._get_http_cookie_manager_id()
__class__.CookiePool[http_cookie_manager_id][type] = RequestsCookieJar()
session_id = session_id_manager.get_session_id()
__class__.CookiePool[session_id][type] = RequestsCookieJar()
else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type)
def _get_http_cookie_manager_id(self):
"""获取请求上下文中的http_cookie_manager_id"""
try:
return request.cookies.get(HTTPCookieManagerConfig.COOKIE_NAME)
except KeyError:
raise KeyError('在请求上下文cookies中未找到名为%s的cookie' % HTTPCookieManagerConfig.COOKIE_NAME)
class HTTPCookieManagerConfig:
# 标识HTTPCookieManager在当前浏览器客户端对应id的cookie
COOKIE_NAME = 'http_cookie_manager_id'
COOKIE_DURATION = timedelta(days=365)
COOKIE_SECURE = None
COOKIE_HTTPONLY = False

View File

@ -11,7 +11,6 @@ from functools import partial
import contextlib
from app.template_global import sort_by_order_in_module, sort_by_order_in_project, sort_by_order_in_logic_controller
from app.extensions import http_cookie_manager
from app.cores.dictionaries import (ELEMENT_TYPE, STATUS, CASE_TYPE, DISPATCHER_STATUS, DISPATCHER_TYPE,
DISPATCHER_END_TYPE, REPORT_RESULT, TOOL_TYPE, LOGIC_CONTROLLER_TYPE,
DISPATCHER_TRIGGER_TYPE)
@ -23,6 +22,7 @@ from app.cores.ws import emit_dispatcher_result, emit_dispatcher_end, emit_dispa
from app.cores.exceptions import *
from app.cores import dingtalk
from app.cores.email import send_email_report
from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager
class AbstractDispatcher(ABC):
@ -418,7 +418,7 @@ class AbstractDispatcher(ABC):
if project_advanced_configuration:
if project_advanced_configuration.clear_http_cookie:
try:
http_cookie_manager.clear_cookie_pool(type=DISPATCHER_TYPE.BUILD)
HTTPCookiePoolManager.clear_cookie_pool(type=DISPATCHER_TYPE.BUILD)
except Exception as e:
self.dispatcher_logger.logger.warning(f'清理HTTPCookie数据失败错误信息:\n{traceback.format_exc()}')
else:

View File

@ -8,7 +8,6 @@ from flask_wtf import CSRFProtect
from flask_bootstrap import Bootstrap
from flask_socketio import SocketIO
from app.cores.case.http.http_cookie_manager import HTTPCookieManager
from app.cores.session_id_manager import SessionIDManager
from app.cores.dispatcher_scheduler import DispatcherScheduler
@ -24,7 +23,6 @@ mail = Mail()
csrf = CSRFProtect()
bootstrap = Bootstrap()
socketio = SocketIO()
http_cookie_manager = HTTPCookieManager()
session_id_manager = SessionIDManager()
dispatcher_scheduler = DispatcherScheduler()

View File

@ -9,6 +9,7 @@ from datetime import datetime, timedelta
from app.cores.dictionaries import (STATUS, DISPATCHER_STATUS, ELEMENT_TYPE, DISPATCHER_TYPE, CASE_TYPE, REPORT_RESULT,
EXPECTATION_LOGIC, LOGIC_CONTROLLER_TYPE, TOOL_TYPE, CONTENT_TYPE)
from app.cores.case.http.dispatcher import HTTPCaseDispatcher
from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager
from app.cores.case.ssh.dispatcher import SSHCaseDispatcher
from app.cores.case.sql.dispatcher import SQLCaseDispatcher
from app.cores.case.debug.dispatcher import DebugCaseDispatcher
@ -21,7 +22,7 @@ from app.models import Case, Project, Module, Scene, HTTPCase, SSHCase, SQLCase,
Scheduler, DingTalkRobotSetting, DingTalkRobotSettingAssociationProject, DebugCase, Tool, TimerTool, ScriptTool, \
VariableDefinitionTool, EmailReceiverSetting, EmailReceiverSettingAssociationProject, HTTPRequestHeaderManagerTool
from app.template_global import render_to_json
from app.extensions import http_cookie_manager, dispatcher_scheduler
from app.extensions import dispatcher_scheduler
from app.template_global import sort_by_order_in_project, sort_by_order_in_module, sort_by_order_in_logic_controller
@ -155,7 +156,7 @@ def execute_http_case():
@bp.route('/case/http/cookie/get', methods=['POST'])
def get_cookie():
try:
rcj = http_cookie_manager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
cookies = []
if rcj is not None:
for cookie in rcj:
@ -199,7 +200,7 @@ def save_cookie():
if not exist: return form_cookies
try:
cookies = json.loads(form_cookies)
rcj = http_cookie_manager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
for cookie in cookies:
if cookie.get('domain') is None:
return jsonify({
@ -208,7 +209,7 @@ def save_cookie():
})
if 'Delete' in cookie: cookie.pop('Delete')
if cookie['expires'] == '': cookie['expires'] = None
http_cookie_manager.clear_and_reset(rcj=rcj, cookies=cookies)
HTTPCookiePoolManager.clear_and_reset(rcj=rcj, cookies=cookies)
except Exception as e:
exception_handle(current_app, e)
return jsonify({