重命名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 import click
from app.config import Config, proj_dir 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) socketio, dispatcher_scheduler)
from app import models from app import models
from app.template_global import (render_to_json, sort_by_order_in_module, sort_by_order_in_logic_controller, 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) csrf.init_app(app=app)
bootstrap.init_app(app=app) bootstrap.init_app(app=app)
socketio.init_app(app=app, async_mode='threading') socketio.init_app(app=app, async_mode='threading')
http_cookie_manager.init_app(app=app)
session_id_manager.init_app(app=app) session_id_manager.init_app(app=app)
dispatcher_scheduler.init_app(app=app) dispatcher_scheduler.init_app(app=app)

View File

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

View File

@ -1,27 +1,26 @@
# coding=utf-8 # coding=utf-8
from flask import request, session from flask import session
from requests.cookies import RequestsCookieJar from requests.cookies import RequestsCookieJar
from datetime import timedelta, datetime
import uuid
from typing import Optional, List, Mapping from typing import Optional, List, Mapping
from app.extensions import session_id_manager
from app.cores.dictionaries import DISPATCHER_TYPE, DISPATCHER_TRIGGER_TYPE from app.cores.dictionaries import DISPATCHER_TYPE, DISPATCHER_TRIGGER_TYPE
# requests库会在发起请求时根据请求服务器的domain选择合适的cookie作为请求头中的cookie数据发出 # requests库会在发起请求时根据请求服务器的domain选择合适的cookie作为请求头中的cookie数据发出
# 可参考代码 requests.models.prepare_cookies() # 可参考代码 requests.models.prepare_cookies()
class HTTPCookieManager: class HTTPCookiePoolManager:
""" """
管理自动化测试中请求头Cookie数据 管理自动化测试中请求头Cookie数据
CookiePool = { CookiePool = {
http_cookie_manager_id1: { session_id1: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(), DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(), DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
}, },
http_cookie_manager_id1: { session_id2: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(), DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: RequestsCookieJar(), DISPATCHER_TYPE.BUILD: RequestsCookieJar(),
}, },
@ -41,15 +40,8 @@ class HTTPCookieManager:
# 对于定时构建的项目使用另一个Cookie池保存key为项目id # 对于定时构建的项目使用另一个Cookie池保存key为项目id
ScheduleCookiePool = {} ScheduleCookiePool = {}
def __init__(self, app=None): @classmethod
if app is not None: def update_cookie_pool(cls, rcj: RequestsCookieJar = None, type: str = 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):
""" """
:param rcj: RequestsCookieJar对象一般由应答头获得 response.cookies :param rcj: RequestsCookieJar对象一般由应答头获得 response.cookies
:param type: 需要更新到的cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG :param type: 需要更新到的cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
@ -60,11 +52,11 @@ class HTTPCookieManager:
project_id = session.get('project_id') project_id = session.get('project_id')
if project_id: if project_id:
if project_id not in __class__.ScheduleCookiePool: if project_id not in __class__.ScheduleCookiePool:
self._add_empty_to_cookie_pool() cls._add_empty_to_cookie_pool()
else: else:
http_cookie_manager_id = self._get_http_cookie_manager_id() session_id = session_id_manager.get_session_id()
if http_cookie_manager_id not in __class__.CookiePool: if session_id not in __class__.CookiePool:
self._add_empty_to_cookie_pool() cls._add_empty_to_cookie_pool()
# 如果入参rcj为空的RequestsCookieJar则不更新直接返回 # 如果入参rcj为空的RequestsCookieJar则不更新直接返回
if len(rcj) == 0: if len(rcj) == 0:
return return
@ -77,22 +69,24 @@ class HTTPCookieManager:
if old_rcj is None: if old_rcj is None:
__class__.ScheduleCookiePool[project_id] = rcj __class__.ScheduleCookiePool[project_id] = rcj
else: 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: else:
http_cookie_manager_id = self._get_http_cookie_manager_id() session_id = session_id_manager.get_session_id()
old_rcj = __class__.CookiePool[http_cookie_manager_id][type] old_rcj = __class__.CookiePool[session_id][type]
if old_rcj is None: if old_rcj is None:
__class__.CookiePool[http_cookie_manager_id][type] = rcj __class__.CookiePool[session_id][type] = rcj
else: 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: else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type) 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上""" """将新的rcj更新到老的rcj上"""
old_rcj.update(other=new_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() 为当前会话在cookie pool中添加一个新的默认值为RequestsCookieJar()
:return: None :return: None
@ -104,15 +98,16 @@ class HTTPCookieManager:
project_id: RequestsCookieJar(), project_id: RequestsCookieJar(),
}) })
else: else:
http_cookie_manager_id = self._get_http_cookie_manager_id() session_id = session_id_manager.get_session_id()
__class__.CookiePool.update({ __class__.CookiePool.update({
http_cookie_manager_id: { session_id: {
DISPATCHER_TYPE.DEBUG: RequestsCookieJar(), DISPATCHER_TYPE.DEBUG: RequestsCookieJar(),
DISPATCHER_TYPE.BUILD: 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数据 获取当前会话中指定类型的cookie数据
:param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG :param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
@ -125,12 +120,13 @@ class HTTPCookieManager:
return return
return __class__.ScheduleCookiePool[project_id] return __class__.ScheduleCookiePool[project_id]
else: else:
http_cookie_manager_id = self._get_http_cookie_manager_id() session_id = session_id_manager.get_session_id()
if http_cookie_manager_id not in __class__.CookiePool: if session_id not in __class__.CookiePool:
return 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 清除并重置RequestsCookieJar
:param rcj: 待重置的RequestsCookieJar来自cookie pool中该会话cookie :param rcj: 待重置的RequestsCookieJar来自cookie pool中该会话cookie
@ -143,63 +139,21 @@ class HTTPCookieManager:
for cookie in cookies: for cookie in cookies:
rcj.set(**cookie) rcj.set(**cookie)
def _update_cookie(self, response): @classmethod
"""注册到app.after_request, 当浏览器没有该cookie时将会在应答头中带上该cookie""" def clear_cookie_pool(cls, type: str = None):
# 判断当前浏览器是否保存有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):
""" """
清空当前会话中的cookie数据 清空当前会话中的cookie数据
:param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG :param type: cookie类型 DISPATCHER_TYPE.BUILD或DISPATCHER_TYPE.DEBUG
:return: :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 type in [DISPATCHER_TYPE.DEBUG, DISPATCHER_TYPE.BUILD]:
if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE: if session.get('dispatcher_trigger_type') == DISPATCHER_TRIGGER_TYPE.BY_SCHEDULE:
project_id = session.get('project_id') project_id = session.get('project_id')
if project_id: if project_id:
__class__.ScheduleCookiePool[project_id] = RequestsCookieJar() __class__.ScheduleCookiePool[project_id] = RequestsCookieJar()
else: else:
http_cookie_manager_id = self._get_http_cookie_manager_id() session_id = session_id_manager.get_session_id()
__class__.CookiePool[http_cookie_manager_id][type] = RequestsCookieJar() __class__.CookiePool[session_id][type] = RequestsCookieJar()
else: else:
raise ValueError('不支持传入type值为%s只支持type=single_case 或 type=build_case' % type) 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 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.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, 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_END_TYPE, REPORT_RESULT, TOOL_TYPE, LOGIC_CONTROLLER_TYPE,
DISPATCHER_TRIGGER_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.exceptions import *
from app.cores import dingtalk from app.cores import dingtalk
from app.cores.email import send_email_report from app.cores.email import send_email_report
from app.cores.case.http.http_cookie_pool_manager import HTTPCookiePoolManager
class AbstractDispatcher(ABC): class AbstractDispatcher(ABC):
@ -418,7 +418,7 @@ class AbstractDispatcher(ABC):
if project_advanced_configuration: if project_advanced_configuration:
if project_advanced_configuration.clear_http_cookie: if project_advanced_configuration.clear_http_cookie:
try: try:
http_cookie_manager.clear_cookie_pool(type=DISPATCHER_TYPE.BUILD) HTTPCookiePoolManager.clear_cookie_pool(type=DISPATCHER_TYPE.BUILD)
except Exception as e: except Exception as e:
self.dispatcher_logger.logger.warning(f'清理HTTPCookie数据失败错误信息:\n{traceback.format_exc()}') self.dispatcher_logger.logger.warning(f'清理HTTPCookie数据失败错误信息:\n{traceback.format_exc()}')
else: else:

View File

@ -8,7 +8,6 @@ from flask_wtf import CSRFProtect
from flask_bootstrap import Bootstrap from flask_bootstrap import Bootstrap
from flask_socketio import SocketIO 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.session_id_manager import SessionIDManager
from app.cores.dispatcher_scheduler import DispatcherScheduler from app.cores.dispatcher_scheduler import DispatcherScheduler
@ -24,7 +23,6 @@ mail = Mail()
csrf = CSRFProtect() csrf = CSRFProtect()
bootstrap = Bootstrap() bootstrap = Bootstrap()
socketio = SocketIO() socketio = SocketIO()
http_cookie_manager = HTTPCookieManager()
session_id_manager = SessionIDManager() session_id_manager = SessionIDManager()
dispatcher_scheduler = DispatcherScheduler() 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, 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) EXPECTATION_LOGIC, LOGIC_CONTROLLER_TYPE, TOOL_TYPE, CONTENT_TYPE)
from app.cores.case.http.dispatcher import HTTPCaseDispatcher 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.ssh.dispatcher import SSHCaseDispatcher
from app.cores.case.sql.dispatcher import SQLCaseDispatcher from app.cores.case.sql.dispatcher import SQLCaseDispatcher
from app.cores.case.debug.dispatcher import DebugCaseDispatcher 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, \ Scheduler, DingTalkRobotSetting, DingTalkRobotSettingAssociationProject, DebugCase, Tool, TimerTool, ScriptTool, \
VariableDefinitionTool, EmailReceiverSetting, EmailReceiverSettingAssociationProject, HTTPRequestHeaderManagerTool VariableDefinitionTool, EmailReceiverSetting, EmailReceiverSettingAssociationProject, HTTPRequestHeaderManagerTool
from app.template_global import render_to_json 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 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']) @bp.route('/case/http/cookie/get', methods=['POST'])
def get_cookie(): def get_cookie():
try: try:
rcj = http_cookie_manager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG) rcj = HTTPCookiePoolManager.get_request_cookie_jar(type=DISPATCHER_TYPE.DEBUG)
cookies = [] cookies = []
if rcj is not None: if rcj is not None:
for cookie in rcj: for cookie in rcj:
@ -199,7 +200,7 @@ def save_cookie():
if not exist: return form_cookies if not exist: return form_cookies
try: try:
cookies = json.loads(form_cookies) 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: for cookie in cookies:
if cookie.get('domain') is None: if cookie.get('domain') is None:
return jsonify({ return jsonify({
@ -208,7 +209,7 @@ def save_cookie():
}) })
if 'Delete' in cookie: cookie.pop('Delete') if 'Delete' in cookie: cookie.pop('Delete')
if cookie['expires'] == '': cookie['expires'] = None 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: except Exception as e:
exception_handle(current_app, e) exception_handle(current_app, e)
return jsonify({ return jsonify({