session 的 源码

1.session 和 cookie 的 区别:

答: cookie 它是保存在浏览器的键值对。

  session 它是保存在服务端的键值对。  它 依赖cookie 存在。

  流程:

  一个服务端,     一个客户端第一次来

  服务端  去 客户端 发来的请求里的cookie 里面找一个 随机字符串。

  新用户没有,服务端就会为客户端创建一个随机字符串,发到它的cookie里,并且保存到服务端。

  下次这个客户端又来了,那么这次服务端就可以获取到了随机字符串。

  根据这个随机字符串看看你以前有没有在session里面放数据。(对比)

  如果数据更新,是会先更新到内存里面,请求返回到时候,在会把内存的数据保存的session。

  session

    {

      pc随机字符串: {},

    }

  当请求第一次来,生成随机字符串,发给用户的cookie,保存到session字段中。(其实就是调用stark(中介),将随机字符串和对应的值,放到local(房主)里)

  以后去取的时候,top就行了。

  请求处理完毕,就将session持久化,存到数据库,存到redis,存到加密cookie中。

2.分析session源码:

1.请求刚进来先走Flask类的__call__方法。

2.用到类和方法。

3. 细分:

 0.app.run()   ==>  run_simple(host, port, self, **options) #第一: hold 住 对象过来就执行 __call__方法.

 1.__call__ 跳入:

  

 2.self.wsgi_app 跳入:

  def wsgi_app(self, environ, start_response):

  2.1 如何封装? push 跳入

   

 3. push跳入之后

  

  _request_ctx_stack跳入: request 和 session 都会放入local 中。

  

  3.1 刚开始处理session

    

 4.self.wsgi_app. 处理函数

  

  full_dispatch_request()跳入:

 5.full_dispatch_request

  

 6.full_dispatch_request。对返回值进行封装,请求完了之后 after。

  finalize_request跳入;

 7.finalize_request

  process_response跳入:

 8.process_response

 9. self.wsgi_app 里。

  

4.超级细分:

三部曲:

 1. 上下文的处理:  他做的是: 把request,session 的请求相关信息放到local里。(赋值给RequestContext对象)

  首先.

if __name__ == '__main__':app.run()app.__call__()   #第一

ps:   __call__ 跳入

def __call__(self, environ, start_response):"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)   #第二:请求开始的地方,也是结束的地方

ps:  wsgi_app  跳入

    def wsgi_app(self, environ, start_response):    #第三'''{"唯一标识":{'stack':[ResquestContext对象(request,session)]}}'''#第四: 开始之前做的事情。(两件事:处理了request,session),将这两个数据封装到local中。ctx = self.request_context(environ) #将request信息封装到Request(environ)对象并赋值给了 RequestContext对象
        ctx.push()error = Nonetry:try:response = self.full_dispatch_request()   #第七:执行:before装饰器函数,视图函数:after装饰器函数except Exception as e: #如果请求出现异常error = eresponse = self.handle_exception(e)  # 信号6: 第2/3/4/5/got_request_exceptionexcept:error = sys.exc_info()[1]raisereturn response(environ, start_response)finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)  # 结束了  #ctx是request-Context对象 信号7: 里面做了 第6信号:

ps:  request_context  跳入

    def request_context(self, environ):return RequestContext(self, environ)

ps:  RequestContext 跳入

class RequestContext(object):def __init__(self, app, environ, request=None):self.app = appif request is None:     #如果request 为空request = app.request_class(environ)   # app.request_class就是request对象,app就是flask对象self.request = requestself.url_adapter = app.create_url_adapter(self.request)self.flashes = Noneself.session = None# Request contexts can be pushed multiple times and interleaved with# other request contexts.  Now only if the last level is popped we# get rid of them.  Additionally if an application context is missing# one is created implicitly so for each level we add this informationself._implicit_app_ctx_stack = []# indicator if the context was preserved.  Next time another context# is pushed the preserved context is popped.self.preserved = False# remembers the exception for pop if there is one in case the context# preservation kicks in.self._preserved_exc = None# Functions that should be executed after the request on the response# object.  These will be called before the regular "after_request"# functions.self._after_request_functions = []self.match_request()def _get_g(self):return _app_ctx_stack.top.gdef _set_g(self, value):_app_ctx_stack.top.g = valueg = property(_get_g, _set_g)del _get_g, _set_gdef copy(self):"""Creates a copy of this request context with the same request object.This can be used to move a request context to a different greenlet.Because the actual request object is the same this cannot be used tomove a request context to a different thread unless access to therequest object is locked... versionadded:: 0.10"""return self.__class__(self.app,environ=self.request.environ,request=self.request)def match_request(self):"""Can be overridden by a subclass to hook into the matchingof the request."""try:url_rule, self.request.view_args = \self.url_adapter.match(return_rule=True)self.request.url_rule = url_ruleexcept HTTPException as e:self.request.routing_exception = edef push(self):"""Binds the request context to the current context."""# If an exception occurs in debug mode or if context preservation is# activated under exception situations exactly one context stays# on the stack.  The rationale is that you want to access that# information under debug situations.  However if someone forgets to# pop that context again we want to make sure that on the next push# it's invalidated, otherwise we run at risk that something leaks# memory.  This is usually only a problem in test suite since this# functionality is not active in production environments.top = _request_ctx_stack.top   # 取值if top is not None and top.preserved:top.pop(top._preserved_exc)# Before we push the request context we have to ensure that there# is an application context.app_ctx = _app_ctx_stack.topif app_ctx is None or app_ctx.app != self.app:app_ctx = self.app.app_context()  # 信号1: 把app放到 local里。app_ctx.push()  # 信号2:AppContext(self)的push  (里面做了:触发执行 第1信号:appcontext_pushed)self._implicit_app_ctx_stack.append(app_ctx) # 信号3: 把上面app_ctx对象加进来,以后可以取else:self._implicit_app_ctx_stack.append(None)if hasattr(sys, 'exc_clear'):sys.exc_clear()_request_ctx_stack.push(self)  #第五:将requestContext对象(request,session)的引用,只要一修改,都修改了,添加到local中。# Open the session at the moment that the request context is# available. This allows a custom open_session method to use the# request context (e.g. code that access database information# stored on `g` instead of the appcontext).#self.session_interface.open_session(self, request) = self.app.open_session(self.request)#return self.session_interface.open_session(self, request)#return SecureCookieSessionInterface().open_session(sekf,request)  经过一段处理,session就不是None了self.session = self.app.open_session(self.request)  #第六:刚开始处理session。self.app 表示:flask 对象if self.session is None:  #  self 表示:requestContext对象.请求一开始进来,为空self.session = self.app.make_null_session()  #  走这里,最后还是指null_session_class = NullSession。def pop(self, exc=_sentinel):"""Pops the request context and unbinds it by doing that.  This willalso trigger the execution of functions registered by the:meth:`~flask.Flask.teardown_request` decorator... versionchanged:: 0.9Added the `exc` argument."""app_ctx = self._implicit_app_ctx_stack.pop()try:clear_request = Falseif not self._implicit_app_ctx_stack:self.preserved = Falseself._preserved_exc = Noneif exc is _sentinel:exc = sys.exc_info()[1]self.app.do_teardown_request(exc)  # 信号7。2: 第6信号  request_tearing_down# If this interpreter supports clearing the exception information# we do that now.  This will only go into effect on Python 2.x,# on 3.x it disappears automatically at the end of the exception# stack.if hasattr(sys, 'exc_clear'):sys.exc_clear()request_close = getattr(self.request, 'close', None)if request_close is not None:request_close()clear_request = Truefinally:rv = _request_ctx_stack.pop()# get rid of circular dependencies at the end of the request# so that we don't require the GC to be active.if clear_request:rv.request.environ['werkzeug.request'] = None# Get rid of the app as well if necessary.if app_ctx is not None:app_ctx.pop(exc)  # 信号8:pop属Appcontxt 跳入: 它做了触发了 第7信号 appcontext_tearing_downassert rv is self, 'Popped wrong request context.  ' \'(%r instead of %r)' % (rv, self)def auto_pop(self, exc):if self.request.environ.get('flask._preserve_context') or \(exc is not None and self.app.preserve_context_on_exception):self.preserved = Trueself._preserved_exc = excelse:self.pop(exc)  #信号7.1  跳入def __enter__(self):self.push()return selfdef __exit__(self, exc_type, exc_value, tb):# do not pop the request stack if we are in debug mode and an# exception happened.  This will allow the debugger to still# access the request object in the interactive shell.  Furthermore# the context can be force kept alive for the test client.# See flask.testing for how this works.
        self.auto_pop(exc_value)if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:reraise(exc_type, exc_value, tb)def __repr__(self):return '<%s \'%s\' [%s] of %s>' % (self.__class__.__name__,self.request.url,self.request.method,self.app.name,)

ps:   这里,我们暂时先可以看下: session 和 request 一开进来都None。  接着,我们返回,从push 方法进入:

 def wsgi_app(self, environ, start_response):    #第三'''{"唯一标识":{'stack':[ResquestContext对象(request,session)]}}'''#第四: 开始之前做的事情。(两件事:处理了request,session),将这两个数据封装到local中。ctx = self.request_context(environ) #将request信息封装到Request(environ)对象并赋值给了 RequestContext对象ctx.push()

ps:  push 跳入,看看里面都执行了什么?

_request_ctx_stack = LocalStack()

ps:  LocalStack()  跳入

  def __init__(self):self._local = Local()

ps:  Local () 跳入

执行setattr 方法,

 def __setattr__(self, name, value):ident = self.__ident_func__()storage = self.__storage__try:storage[ident][name] = valueexcept KeyError:storage[ident] = {name: value}

返回:

        #self.session_interface.open_session(self, request) = self.app.open_session(self.request)#return self.session_interface.open_session(self, request)#return SecureCookieSessionInterface().open_session(sekf,request)  经过一段处理,session就不是None了self.session = self.app.open_session(self.request)  #第六:刚开始处理session。self.app 表示:flask 对象if self.session is None:  #  self 表示:requestContext对象.请求一开始进来,为空self.session = self.app.make_null_session()  #  走这里,最后还是指null_session_class = NullSession。

ps:  open_session  跳入

    def open_session(self, request):return self.session_interface.open_session(self, request)

ps: session_interface 跳入

 session_interface = SecureCookieSessionInterface()

ps: SecureCookieSessionInterface()  跳入

 

class SecureCookieSessionInterface(SessionInterface):
#: the salt that should be applied on top of the secret key for the#: signing of cookie based sessions.salt = 'cookie-session'#: the hash function to use for the signature.  The default is sha1digest_method = staticmethod(hashlib.sha1)#: the name of the itsdangerous supported key derivation.  The default#: is hmac.key_derivation = 'hmac'#: A python serializer for the payload.  The default is a compact#: JSON derived serializer with support for some extra Python types#: such as datetime objects or tuples.serializer = session_json_serializersession_class = SecureCookieSession   #其实,最后返回的还是个字典的形式

ps: SecureCookieSession跳入

class SecureCookieSession(CallbackDict, SessionMixin):"""Base class for sessions based on signed cookies."""def __init__(self, initial=None):def on_update(self):self.modified = TrueCallbackDict.__init__(self, initial, on_update)self.modified = False

ps: CallbackDict 跳入

class CallbackDict(UpdateDictMixin, dict):   #dict 字典"""A dict that calls a function passed every time something is changed.The function is passed the dict instance."""

ps:  从这可以看出,它最后继承是  dict  字典。

返回:

return self.session_interface.open_session(self, request)

ps: 从open_session 进入

    def open_session(self, app, request):s = self.get_signing_serializer(app) # 加密if s is None:return Noneval = request.cookies.get(app.session_cookie_name)  #request:请求的对象,去cookie中获取session作为key对应的值。if not val:  #没有return self.session_class()  #返回session_classmax_age = total_seconds(app.permanent_session_lifetime)try:data = s.loads(val, max_age=max_age)  #序列化解密return self.session_class(data)except BadSignature:return self.session_class()

ps: 进行一系列处理,最后的返回的 session_class 是以字典形式存的。

小结: 以上的流程就是上下文处理的过程。

    简单的来说,上下文处理做的就是把请求的数据给封装到了 local 里。

 2. 视图函数处理

 首先:

    def wsgi_app(self, environ, start_response):    #第三'''{"唯一标识":{'stack':[ResquestContext对象(request,session)]}}'''#第四: 开始之前做的事情。(两件事:处理了request,session),将这两个数据封装到local中。ctx = self.request_context(environ) #将request信息封装到Request(environ)对象并赋值给了 RequestContext对象
        ctx.push()error = Nonetry:try:response = self.full_dispatch_request()   #第七:执行:before装饰器函数,视图函数:after装饰器函数except Exception as e: #如果请求出现异常error = eresponse = self.handle_exception(e)  # 信号6: 第2/3/4/5/got_request_exceptionexcept:error = sys.exc_info()[1]raisereturn response(environ, start_response)finally:if self.should_ignore_error(error):error = Nonectx.auto_pop(error)  # 结束了  #ctx是request-Context对象 信号7: 里面做了 第6信号:

ps: full_dispatch_request  跳入

 def full_dispatch_request(self):"""Dispatches the request and on top of that performs requestpre and postprocessing as well as HTTP exception catching anderror handling... versionadded:: 0.7"""self.try_trigger_before_first_request_functions() #第八: 执行@before_first_request 装饰的所有函数try:request_started.send(self) #信号4: 请求刚进来执行,触发了  第2信号:request_started.rv = self.preprocess_request()  #第九: 执行 @before_request 装饰的所有函数if rv is None:rv = self.dispatch_request()except Exception as e:rv = self.handle_user_exception(e)return self.finalize_request(rv)  #第十: 执行@after_request 装饰的所有函数:session保存

ps:   finalize_request  跳入

    def finalize_request(self, rv, from_error_handler=False):"""Given the return value from a view function this finalizesthe request by converting it into a response and invoking thepostprocessing functions.  This is invoked for both normalrequest dispatching as well as error handlers.Because this means that it might be called as a result of afailure a special safe mode is available which can be enabledwith the `from_error_handler` flag.  If enabled, failures inresponse processing will be logged and otherwise ignored.:internal:"""response = self.make_response(rv)try:response = self.process_response(response)  #第11:  进入process_responserequest_finished.send(self, response=response) #信号5:第5信号 request_finished 执行之前,视图函数如果有模板语法,会先执行:第3,4,信号  :   before_render_template   template-renderedexcept Exception:if not from_error_handler:raiseself.logger.exception('Request finalizing failed with an ''error while handling an error')return response

ps: process_session  跳入

    def process_response(self, response):"""Can be overridden in order to modify the response objectbefore it's sent to the WSGI server.  By default this willcall all the :meth:`after_request` decorated functions... versionchanged:: 0.5As of Flask 0.5 the functions registered for after requestexecution are called in reverse order of registration.:param response: a :attr:`response_class` object.:return: a new response object or the same, has to be aninstance of :attr:`response_class`."""ctx = _request_ctx_stack.topbp = ctx.request.blueprintfuncs = ctx._after_request_functionsif bp is not None and bp in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[bp]))if None in self.after_request_funcs:funcs = chain(funcs, reversed(self.after_request_funcs[None]))for handler in funcs:   # 执行 @after_request 装饰的所有函数response = handler(response)if not self.session_interface.is_null_session(ctx.session):  #最后处理session
            self.save_session(ctx.session, response)return response

ps:  save_session 跳入

    def save_session(self, session, response):"""Saves the session if it needs updates.  For the defaultimplementation, check :meth:`open_session`.  Instead of overriding thismethod we recommend replacing the :class:`session_interface`.:param session: the session to be saved (a:class:`~werkzeug.contrib.securecookie.SecureCookie`object):param response: an instance of :attr:`response_class`"""return self.session_interface.save_session(self, session, response)

ps:  save_session  跳入

 3. 保存,返回

    def save_session(self, app, session, response):domain = self.get_cookie_domain(app)path = self.get_cookie_path(app)# Delete case.  If there is no session we bail early.# If the session was modified to be empty we remove the# whole cookie.if not session:if session.modified:response.delete_cookie(app.session_cookie_name,domain=domain, path=path)return# Modification case.  There are upsides and downsides to# emitting a set-cookie header each request.  The behavior# is controlled by the :meth:`should_set_cookie` method# which performs a quick check to figure out if the cookie# should be set or not.  This is controlled by the# SESSION_REFRESH_EACH_REQUEST config flag as well as# the permanent flag on the session itself.if not self.should_set_cookie(app, session):returnhttponly = self.get_cookie_httponly(app)secure = self.get_cookie_secure(app)expires = self.get_expiration_time(app, session)val = self.get_signing_serializer(app).dumps(dict(session)) #内存中的session,字典进行加密,也有序列化
        response.set_cookie(app.session_cookie_name, val,expires=expires, httponly=httponly,domain=domain, path=path, secure=secure)  #存到加密的cookie里。

ps: 最后  加密,存到cookie里  返回: 执行  finally

ctx.auto_pop(error)  # 结束了  #ctx是request-Context对象 信号7: 里面做了 第6信号:

ps: auto_pop  跳入

 def auto_pop(self, exc):if self.request.environ.get('flask._preserve_context') or \(exc is not None and self.app.preserve_context_on_exception):self.preserved = Trueself._preserved_exc = excelse:self.pop(exc)  #信号7.1  跳入

ps:  self.pop(exc)  跳入

    def pop(self, exc=_sentinel):"""Pops the request context and unbinds it by doing that.  This willalso trigger the execution of functions registered by the:meth:`~flask.Flask.teardown_request` decorator... versionchanged:: 0.9Added the `exc` argument."""app_ctx = self._implicit_app_ctx_stack.pop()try:clear_request = Falseif not self._implicit_app_ctx_stack:self.preserved = Falseself._preserved_exc = Noneif exc is _sentinel:exc = sys.exc_info()[1]self.app.do_teardown_request(exc)  # 信号7。2: 第6信号  request_tearing_down# If this interpreter supports clearing the exception information# we do that now.  This will only go into effect on Python 2.x,# on 3.x it disappears automatically at the end of the exception# stack.if hasattr(sys, 'exc_clear'):sys.exc_clear()request_close = getattr(self.request, 'close', None)if request_close is not None:request_close()clear_request = Truefinally:rv = _request_ctx_stack.pop()  #把ContentText对象 POP 出来

ps:  把ContentText对象 POP 出来

结束。

转载于:https://www.cnblogs.com/zhongbokun/p/8245569.html

session 的 源码相关推荐

  1. Apache IoTDB源码解析(0.11.2版本):Session的源码解析

    1. 声明 当前内容主要为解析Apache IoTDB 0.11.2版本的Session的源码解析 通过前面的Apache Thrift的Demo,可以发现iotdb中的server是使用了thrif ...

  2. 【网络安全】登录问题(一)Session/Cookie源码分析

    从身份验证开始说起. 知 我们在上BBS的时候,有些帖子是有限制的,只有允许身份的人才能观看,那么如果去校验你的身份呢?我们想到了一个办法,让用户有一个唯一的身份证明.但只有身份证明是不够的,你的身份 ...

  3. pbp 读取 mysql数据_SqlAlchemy 中操作数据库时session和scoped_session的区别(源码分析)...

    原生session: from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from sqlalch ...

  4. 从Spring-Session源码看Session机制的实现细节

    作者:徐靖峰 原文:https://www.cnkirito.moe/2018/04/17/spring-session-4/ 去年本文作者曾经写过几篇和 Spring Session 相关的文章,从 ...

  5. Spring Session - 源码解读

    文章目录 Spring Session 流程图 源码分析 Spring Session 流程图 Spring Session 主要是利用过滤器,偷梁换柱,实现session储存无感知的切换. 源码分析 ...

  6. Spring Session源码解析

    AbstractHttpSessionApplicationInitializer,很明显它是一个初始化的类,它是一个抽象类,可以理解为一个公用的基类,然后看一下onStartup这个方法,最主要的方 ...

  7. Laravel核心解读--Session源码解析

    Session 模块源码解析 由于HTTP最初是一个匿名.无状态的请求/响应协议,服务器处理来自客户端的请求然后向客户端回送一条响应.现代Web应用程序为了给用户提供个性化的服务往往需要在请求中识别出 ...

  8. Docker源码分析(十):Docker镜像下载

    http://www.infoq.com/cn/articles/docker-source-code-analysis-part10 1.前言 说Docker Image是Docker体系的价值所在 ...

  9. ActiveMQ源码解析 建立连接

    作为一个消息中间件,有客户端和服务端两部分代码,这次的源码解析系列主要从客户端的代码入手,分成建立连接.消息发送.消息消费三个部分.趁着我昨天弄明白了源码编译的兴奋劲头还没过去,今天研究一下建立连接的 ...

最新文章

  1. 4个做管理后才知道的秘密
  2. python pip-python的pip安装以及使用教程
  3. DFTug - Architecture Your Test Design
  4. Ksusha and Array (vector)
  5. 阿里云李飞飞:传统数据库步履蹒跚,未来的机会在哪里?
  6. SpringBoot 对象输出
  7. JAVA-JAVA WEB开发工具下载与安装
  8. python装好了怎么启动车_【填空题】Python安装好后,可以直接在CMD命令行下输入( )命令, 可启动交互式编程,提示窗口如下:...
  9. Python-Excel 模块哪家强 #华为云·寻找黑马程序员#
  10. C++之指针探究(五):数组指针和二维数组
  11. Mysql设置某字段唯一
  12. 重拾《 两周自制脚本语言 》- 支持中文标识符
  13. 下列软件包有未满足的依赖关系,依赖: libxxx(= 2.2.10) 但是 2.3.0正要被安装
  14. 双11 背后的全链路可观测性:阿里巴巴鹰眼在“云原生时代”的全面升级
  15. 万娟 白话大数据和机械学习_《白话大数据与机器学习》.pdf
  16. https网站安全证书提示已过期怎么办?
  17. FPGA笔记之verilog语言(基础语法篇)
  18. java中的IO流(超全)(超详解)结合实例轻松掌握
  19. 礼是随还是不随,年轻人该如何选择?
  20. c语言中组件出现错误,错误消息:无法载入文件或组件 或其相依性的其中之一。 找到的组件资讯清单定义与组件参考不符。 (发生例外状况于 HRESULT: 0x80131040)...

热门文章

  1. 「工具」IndexDB 版备忘录
  2. Exynos4412裸机开发——中断处理
  3. python的urllib2模块
  4. 对话框找不到WM_ERASEBKGND消息的解决方法与对话框背景图片的载入方法
  5. 简单调试 Python 程序
  6. yarn安装依赖包报错 error An unexpected error occurred: “https://registry.npm.taobao.orgnpm/element-ui: get
  7. 前端学习(3121):组件和模块
  8. 前端学习(3166):react-hello-react之鼠标移入效果
  9. 前端学习(2823):sitemap配置
  10. 工作225:当前导致name报错