日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

session 的 源码

發布時間:2023/12/10 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 session 的 源码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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 sha1
digest_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_serializer
session_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 的 源码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。