Every request comes with a session, a dictionary that is persisted on the filesystem and that saves various information such as the user cart on the ecommerce. When a user simply visits the website, a default session is created and saved on disk, this bloats the filestore with many sessions. Creating the session on-the-fly is cheaper than loading it from the filesystem. With this work the default session is not saved on disk anymore unless explicitly asked via `session.touch()`. An exception to the statement "creating the session on-the-fly is cheaper" is geoip, the ip geolocalization is not cheap. In this work, geoip have been moved from http_routing/request.session.geoip to a lazy property core/request.geoip. When requested the info is persisted on the session. Like other keys from the default session, geoip will not be persisted unless there is non-default stuff in the session. Because the CSRF-TOKEN is based on the session-id, it is important the session-id stays the same across multiples requests even when the session is not persisted on disk. Even when a session is not persisted on disk, the session-id cookie is still set so that the next session created on-the-fly uses the same session-id. Technical note regarding the session, it has been decided to drop the session-snapshot protocol and to reintroduce a "modified" flag. It has been decided not to use werkzeug's session (which natively comes with a "modified" flag) and to keep our own session object. We decided to extend MutableMapping instead of dict; using MutableMapping we only have to override __setitem__ and __detitem__; using dict we would had to override update()/pop()/... too. Task: 2789035 Part-of: odoo/odoo#86015
75 lines
1.9 KiB
Python
75 lines
1.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from html.parser import HTMLParser
|
|
from odoo.http import FilesystemSessionStore
|
|
from odoo.tools._vendor.sessions import SessionStore
|
|
|
|
|
|
class MemoryGeoipResolver:
|
|
def resolve(self, ip):
|
|
return {}
|
|
|
|
|
|
class MemorySessionStore(SessionStore):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.store = {}
|
|
|
|
def get(self, sid):
|
|
session = self.store.get(sid)
|
|
if not session:
|
|
session = self.new()
|
|
return session
|
|
|
|
def save(self, session):
|
|
self.store[session.sid] = session
|
|
|
|
def delete(self, session):
|
|
self.store.pop(session.sid, None)
|
|
|
|
def rotate(self, session, env):
|
|
FilesystemSessionStore.rotate(self, session, env)
|
|
|
|
def vacuum(self):
|
|
return
|
|
|
|
|
|
# pylint: disable=W0223(abstract-method)
|
|
class HtmlTokenizer(HTMLParser):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.tokens = []
|
|
|
|
@classmethod
|
|
def _attrs_to_str(cls, attrs):
|
|
out = []
|
|
for key, value in attrs:
|
|
out.append(f"{key}={value!r}" if value else key)
|
|
return " ".join(out)
|
|
|
|
def handle_starttag(self, tag, attrs):
|
|
self.tokens.append(f"<{tag} {self._attrs_to_str(attrs)}>")
|
|
|
|
def handle_endtag(self, tag):
|
|
self.tokens.append(f"</{tag}>")
|
|
|
|
def handle_startendtag(self, tag, attrs):
|
|
# HTML5 <img> instead of XHTML <img/>
|
|
self.handle_starttag(tag, attrs)
|
|
|
|
def handle_data(self, data):
|
|
data = data.strip()
|
|
if data:
|
|
self.tokens.append(data)
|
|
|
|
@classmethod
|
|
def tokenize(cls, source_str):
|
|
"""
|
|
Parse the source html into a list of tokens. Only tags and
|
|
tags data are conserved, other elements such as comments are
|
|
discarded.
|
|
"""
|
|
tokenizer = cls()
|
|
tokenizer.feed(source_str)
|
|
return tokenizer.tokens
|