# coding=utf-8 import threading import requests try: from typing import Dict, Union, Tuple except: # pragma: no cover pass class ZmirrorThreadLocal(threading.local): """ 由于Python内置thread-local对代码补全的提示非常不友好, 所以自己继承一个 如果不知道什么是thread-local, 请看 http://tinyurl.com/hqgb2r8 本类在 zmirror 中被实例化为变量 parse 这个变量的重要性不亚于 request, 在 zmirror 各个部分都会用到 其各个变量的含义如下: parse.time 记录请求过程中的各种时间点 .method 请求的方法, 如 GET POST .remote_domain 当前请求对应的远程域名 .is_external_domain 远程域名是否是外部域名, 比如google镜像, www.gstatic.com 就是外部域名 .is_https 是否需要用https 来请求远程域名 .remote_url 远程服务器的url, 比如 https://google.com/search?q=233 .url_no_scheme 没有协议前缀的url,比如 google.com/search?q=233 通常在缓存中用 .remote_path_query 对应的远程path+query, 比如 /search?q=2333 .remote_path 对应的远程path, 比如 /search .client_header 经过转换和重写以后的访问者请求头 .content_type 远程服务器响应头中的 content_type, 比如 "text/plain; encoding=utf-8" .mime 远程服务器响应的MIME, 比如 "text/html" .request_data 浏览器传入的data(已经经过重写) 可能为str或bytes或None .request_data_encoding 浏览器传入的data的编码(如果有) 如果为二进制或编码未知, 则为None .request_data_encoded 编码后的二进制 request_data, 只读 .cache_control 远程服务器响应的cache_control内容 .remote_response 远程服务器的响应, requests.Response .cacheable 是否可以对这一响应应用缓存 (CDN也算是缓存的一种, 依赖于此选项) .extra_resp_headers 发送给浏览器的额外响应头 (比如一些调试信息什么的) .extra_cookies 额外的cookies, 在目前版本只能添加, 不能覆盖已有cookie .streamed_our_response 是否以 stream 模式向浏览器传送这个响应 .temporary_domain_alias 用于纯文本域名替换, 见 `plain_replace_domain_alias` 选项 本类的方法: .dump() dump所有信息到dict .set_extra_resp_header() 设置一个响应头, 会发送给访问者, 会在内部操作 self.extra_resp_headers .set_cookie() 添加一个cookie 会在内部操作 self.extra_cookies, 目前版本只能添加新的cookie, 不能覆盖已有cookie """ def __init__(self): self.init() def init(self): # 初始化成空白值 self.method = None self.remote_domain = None self.is_external_domain = None self.is_https = None self.remote_url = None self.url_no_scheme = None self.remote_path_query = None self.client_header = None self.content_type = None self.remote_path = None self.mime = None self.cache_control = None self.remote_response = None self.streamed_our_response = False self.cacheable = False self.request_data = None self.request_data_encoding = None self.time = {} self.extra_resp_headers = {} self.temporary_domain_alias = [] self.extra_cookies = {} def dump(self): return { "time": self.time, "method": self.method, "remote_domain": self.remote_domain, "is_external_domain": self.is_external_domain, "is_https": self.is_https, "remote_url": self.remote_url, "url_no_scheme": self.url_no_scheme, "remote_path_query": self.remote_path_query, "client_header": self.client_header, "content_type": self.content_type, "remote_path": self.remote_path, "mime": self.mime, "cache_control": self.cache_control, "temporary_domain_alias": self.temporary_domain_alias, "remote_response": self.remote_response, "streamed_our_response": self.streamed_our_response, "cacheable": self.cacheable, "extra_resp_headers": self.extra_resp_headers, "extra_cookies": self.extra_cookies, "request_data": self.request_data, "request_data_encoding": self.request_data_encoding, } def __str__(self): return str(self.dump()) def set_extra_resp_header(self, name, value): """ :type name: str :type value: str """ h = self.extra_resp_headers h[name] = value self.extra_resp_headers = h def set_cookies(self, name, value, ttl=12 * 35 * 24 * 60 * 60, path='/'): """ :param ttl: cookie有效时间, 秒 :type ttl: int :type path: str :type name: str :type value: str """ from http.cookies import SimpleCookie c = SimpleCookie() c[name] = value c[name]["path"] = path c[name]["expires"] = ttl self.extra_cookies[name] = c[name].OutputString() @property def remote_domain(self): """ 当前请求对应的远程域名 :rtype: str """ return self.__getattribute__("_remote_domain") @remote_domain.setter def remote_domain(self, value): """:type value: str""" self.__setattr__("_remote_domain", value) @property def is_external_domain(self): """ 远程域名是否是外部域名, 比如google镜像, www.gstatic.com 就是外部域名 :rtype: bool """ return self.__getattribute__("_is_external_domain") @is_external_domain.setter def is_external_domain(self, value): """:type value: bool""" self.__setattr__("_is_external_domain", value) @property def is_https(self): """ 是否需要用https 来请求远程域名 :rtype: bool """ return self.__getattribute__("_is_https") @is_https.setter def is_https(self, value): """:type value: bool""" self.__setattr__("_is_https", value) @property def method(self): """ 请求的方法 :rtype: str """ return self.__getattribute__("_method") @method.setter def method(self, value): """:type value: str""" self.__setattr__("_method", value) @property def remote_url(self): """ 远程服务器的url, 比如 https://google.com/search?q=233 :rtype: str """ return self.__getattribute__("_remote_url") @remote_url.setter def remote_url(self, value): """:type value: str""" self.__setattr__("_remote_url", value) @property def url_no_scheme(self): """ 没有协议前缀的url,比如 google.com/search?q=233 通常在缓存中用 :rtype: str """ return self.__getattribute__("_url_no_scheme") @url_no_scheme.setter def url_no_scheme(self, value): """:type value: str""" self.__setattr__("_url_no_scheme", value) @property def remote_path_query(self): """ 对应的远程path+query, 比如 /search?q=2333 :rtype: str """ return self.__getattribute__("_remote_path_query") @remote_path_query.setter def remote_path_query(self, value): """:type value: str""" self.__setattr__("_remote_path_query", value) @property def remote_path(self): """ 对应的远程path, 比如 /search :rtype: str """ return self.__getattribute__("_remote_path") @remote_path.setter def remote_path(self, value): """:type value: str""" self.__setattr__("_remote_path", value) @property def client_header(self): """ 经过转换和重写以后的访问者请求头 :rtype: dict[str, str] """ return self.__getattribute__("_client_header") @client_header.setter def client_header(self, value): """:type value: dict[str, str]""" self.__setattr__("_client_header", value) @property def content_type(self): """ 远程服务器响应头中的 content_type, 比如 "text/plain; encoding=utf-8" :rtype: str """ return self.__getattribute__("_content_type") @content_type.setter def content_type(self, value): """:type value: str""" self.__setattr__("_content_type", value) @property def mime(self): """ 远程服务器响应的MIME, 比如 "text/html" :rtype: str """ return self.__getattribute__("_mime") @mime.setter def mime(self, value): """:type value: str""" self.__setattr__("_mime", value) @property def cache_control(self): """ 远程服务器响应的cache_control内容 :rtype: str """ return self.__getattribute__("_cache_control") @cache_control.setter def cache_control(self, value): """:type value: str""" self.__setattr__("_cache_control", value) @property def remote_response(self): """ 远程服务器的响应, 对象, requests.Response :rtype: requests.Response """ return self.__getattribute__("_remote_response") @remote_response.setter def remote_response(self, value): """:type value: requests.Response""" self.__setattr__("_remote_response", value) @property def temporary_domain_alias(self): """ 用于纯文本域名替换, 见 `plain_replace_domain_alias` 选项 :rtype: list """ return self.__getattribute__("_temporary_domain_alias") @temporary_domain_alias.setter def temporary_domain_alias(self, value): """:type value: list""" self.__setattr__("_temporary_domain_alias", value) @property def streamed_our_response(self): """我们的响应是否用 stream 模式传送 :rtype: bool""" return self.__getattribute__("_streamed_our_response") @streamed_our_response.setter def streamed_our_response(self, value): """:type value: bool""" self.__setattr__("_streamed_our_response", value) @property def cacheable(self): """响应能否被缓存 :rtype: bool""" return self.__getattribute__("_cacheable") @cacheable.setter def cacheable(self, value): """:type value: bool""" self.__setattr__("_cacheable", value) @property def extra_resp_headers(self): """额外的响应头 :rtype: Dict[str, str]""" return self.__getattribute__("_extra_resp_headers") @extra_resp_headers.setter def extra_resp_headers(self, value): """:type value: Dict[str, str]""" self.__setattr__("_extra_resp_headers", value) @property def extra_cookies(self): """额外的cookie :rtype: Dict[str, str]""" return self.__getattribute__("_extra_cookies") @extra_cookies.setter def extra_cookies(self, value): """:type value: Dict[str, str]""" self.__setattr__("_extra_cookies", value) @property def time(self): """用于记录时间 :rtype: Dict[str, float]""" return self.__getattribute__("_time") @time.setter def time(self, value): """:type value: Dict[str, float]""" self.__setattr__("_time", value) @property def request_data(self): """浏览器传入的data(已经经过重写) :rtype: Union[str, bytes, None]""" return self.__getattribute__("_request_data") @request_data.setter def request_data(self, value): """:type value: Union[str, bytes, None]""" self.__setattr__("_request_data", value) @property def request_data_encoding(self): """浏览器传入的data的编码 :rtype: Union[str, None]""" return self.__getattribute__("_request_data_encoding") @request_data_encoding.setter def request_data_encoding(self, value): """:type value: Union[str, None]""" self.__setattr__("_request_data_encoding", value) @property def request_data_encoded(self): """:rtype: Union[bytes, None]""" if isinstance(self.request_data, str): return self.request_data.encode(encoding=self.request_data_encoding or 'utf-8') else: return self.request_data