Files
aaPanel/class/panelHttpProxy.py
T
Jack a24f2e23c6 Update to 7.25.0
1. Added Mail Marketing Automation trigger tasks
2. Added Mail Marketing Groups Import, Export, Merge
3. Added Mail Marketing Subscribers to support paste import
4. Added Mail Marketing Template Import Export Duplicate
5. Added Mail server add timed automatic reply
6. Added HTTPS Protection function for Website (disables automatic HTTP to HTTPS redirection when enabled)
7. Optimize Website Interface Button Integration
8. Optimize the response speed of WP Toolkit interface
2025-03-13 17:52:26 +08:00

277 lines
11 KiB
Python

#coding: utf-8
#-------------------------------------------------------------------
# aaPanel
#-------------------------------------------------------------------
# Copyright (c) 2015-2099 aaPanel(www.aapanel.com) All rights reserved.
#-------------------------------------------------------------------
# Author: hwliang <hwl@aapanel.com>
#-------------------------------------------------------------------
#------------------------------
# HTTP代理模块
#------------------------------
import requests,os,re,time
from BTPanel import request,Response,public,app,get_phpmyadmin_dir,session
from http.cookies import SimpleCookie
import requests.packages.urllib3.util.connection as urllib3_conn
import socket
class HttpProxy:
_pma_path = None
def get_res_headers(self,p_res):
'''
@name 获取响应头
@author hwliang<2022-01-19>
@param p_res<Response> requests响应对像
@return dict
'''
headers = {}
for h in p_res.headers.keys():
if h in ['content-encoding', 'Content-Encoding', 'transfer-encoding', 'Transfer-Encoding']: continue
headers[h] = p_res.headers[h]
if h in ['location', 'Location']:
if headers[h].find('phpmyadmin_') != -1:
if not self._pma_path:
self._pma_path = get_phpmyadmin_dir()
if self._pma_path:
self._pma_path = self._pma_path[0]
else:
self._pma_path = ''
headers[h] = headers[h].replace(self._pma_path,'phpmyadmin')
if headers[h].find('127.0.0.1') != -1:
headers[h] = re.sub(r"https?://127.0.0.1(:\d+)?/",request.url_root,headers[h])
if request.url_root.find('https://') == 0:
headers[h] = headers[h].replace('http://','https://')
return headers
def set_res_headers(self,res,p_res):
'''
@name 设置响应头
@author hwliang<2022-01-19>
@param res<Response> flask响应对像
@param p_res<Response> requests响应对像
@return res<Response>
'''
# from datetime import datetime
# cookie_dict = p_res.cookies.get_dict()
# expires = datetime.utcnow() + app.permanent_session_lifetime
# for k in cookie_dict.keys():
# httponly = True
# if k in ['phpMyAdmin']: httponly = True
# res.set_cookie(k, cookie_dict[k],
# expires=expires, httponly=httponly,
# path='/')
return res
def get_pma_phpversion(self):
'''
@name 获取phpmyadmin的php版本
@author hwliang<2022-01-19>
@return str
'''
from panelPlugin import panelPlugin
pma_status = panelPlugin().getPHPMyAdminStatus()
if 'phpversion' in pma_status:
return pma_status['phpversion']
return None
def get_pma_version(self):
'''
@name 获取phpmyadmin的版本
@author hwliang<2022-01-19>
@return str
'''
pma_vfile = public.get_setup_path() + '/phpmyadmin/version.pl'
if not os.path.exists(pma_vfile): return ''
pma_version = public.readFile(pma_vfile).strip()
if not pma_version: return ''
return pma_version
def set_pma_phpversion(self):
'''
@name 设置phpmyadmin兼容的php版本
@author hwliang<2022-01-19>
@return str
'''
pma_version = self.get_pma_version()
if not pma_version: return False
old_phpversion = self.get_pma_phpversion()
if not old_phpversion: return False
if pma_version == '4.0':
php_versions = ['52','53','54']
elif pma_version == '4.4':
php_versions = ['54','55','56']
elif pma_version == '4.9':
php_versions = ['55','56','70','71','72','73','74']
elif pma_version == '5.0':
php_versions = ['70','71','72','73','74']
elif pma_version == '5.1':
php_versions = ['71','72','73','74','80']
elif pma_version == '5.2':
php_versions = ['72','73','74','80','81']
elif pma_version == '5.3':
php_versions = ['72','73','74','80','81']
else:
return False
if old_phpversion in php_versions: return True
installed_php_versions = []
php_install_path = '/www/server/php'
for version in php_versions:
php_bin = php_install_path + '/' + version + '/bin/php'
if os.path.exists(php_bin):
installed_php_versions.append(version)
if not installed_php_versions: return False
php_version = installed_php_versions[-1]
import ajax
args = public.dict_obj()
args.phpversion = php_version
ajax.ajax().setPHPMyAdmin(args)
public.WriteLog('Database','The PHP version used by phpMyAdmin has been detected to be incompatible and has been automatically changed to the best compatible version: PHP-' + php_version)
time.sleep(0.5)
def get_request_headers(self):
'''
@name 获取请求头
@author hwliang<2022-01-19>
@return dict
'''
headers = {}
rm_cookies = [app.config['SESSION_COOKIE_NAME'],'bt_user_info','file_recycle_status','ltd_end',
'memSize','page_number','pro_end','request_token','serverType','site_model',
'sites_path','soft_remarks','load_page','Path','distribution','order']
for k in request.headers.keys():
headers[k] = request.headers.get(k)
if k == 'Cookie':
cookie_dict = SimpleCookie(headers[k])
for rm_cookie in rm_cookies:
if rm_cookie in cookie_dict:
del(cookie_dict[rm_cookie])
headers[k] = cookie_dict.output(header='',sep=';').strip()
return headers
def form_to_dict(self,form):
'''
@name 将表单转为字典
@author hwliang<2022-02-18>
@param form<request.form> 表单数据
@return dict
'''
data = {}
for k in form.keys():
data[k] = form.getlist(k)
if len(data[k]) == 1: data[k] = data[k][0]
return data
def proxy(self,proxy_url):
'''
@name 代理指定URL地址
@author hwliang<2022-01-19>
@param proxy_url<string> 被代理的URL地址
@return Response
'''
try:
urllib3_conn.allowed_gai_family = lambda: socket.AF_INET
s_key = 'proxy_{}_{}'.format(app.secret_key,self.get_pma_version())
if not s_key in session:
session[s_key] = requests.Session()
session[s_key].keep_alive = False
session[s_key].headers = {
'User-Agent':'BT-Panel',
'Connection':'close'
}
if proxy_url.find('phpmyadmin') != -1:
if proxy_url.find('https://') == 0:
session[s_key].cookies.update({'pma_lang_https':'zh_CN'})
else:
session[s_key].cookies.update({'pma_lang':'zh_CN'})
self.set_pma_phpversion()
if 'Authorization' in request.headers:
session[s_key].headers['Authorization'] = request.headers['Authorization']
try:
session[s_key].headers['Host'] = public.en_punycode(request.url_root).replace('http://','').replace('https://','').split('/')[0]
except:pass
# headers = self.get_request_headers()
headers = None
if request.method == 'GET':
# 转发GET请求
p_res = session[s_key].get(proxy_url,headers=headers,verify=False,allow_redirects=False)
elif request.method == 'POST':
# 转发POST请求
if request.files: # 如果上传文件
tmp_path = '{}/tmp'.format(public.get_panel_path())
if not os.path.exists(tmp_path): os.makedirs(tmp_path,384)
# 处理请求头
if headers:
if 'Content-Type' in headers: del(headers['Content-Type'])
if 'Content-Length' in headers: del(headers['Content-Length'])
# 遍历form表单中的所有文件
files = {}
f_list = {}
for key in request.files:
upload_files = request.files.getlist(key)
filename = upload_files[0].filename
if not filename: filename = public.GetRandomString(12)
tmp_file = '{}/{}'.format(tmp_path,filename)
# 保存上传文件到临时目录
with open(tmp_file,'wb') as f:
for tmp_f in upload_files:
f.write(tmp_f.read())
f.close()
# 构造文件上传对象
f_list[key] = open(tmp_file,'rb')
files[key] = (filename, f_list[key])
# 删除临时文件
if os.path.exists(tmp_file): os.remove(tmp_file)
# 转发上传请求
p_res = session[s_key].post(proxy_url,self.form_to_dict(request.form),headers=headers,files=files,verify=False,allow_redirects=False)
# 释放文件对象
for fkey in f_list.keys():
f_list[fkey].close()
else:
p_res = session[s_key].post(proxy_url,self.form_to_dict(request.form),headers=headers,verify=False,allow_redirects=False)
else:
return Response('不支持的请求类型',500)
# PHP版本自动切换处理
if proxy_url.find('phpmyadmin') != -1 and proxy_url.find('/index.php') != -1:
if len(p_res.content) < 1024:
if p_res.content.find(b'syntax error, unexpected') != -1 or p_res.content.find(b'offset access syntax with') != -1 or p_res.content.find(b'+ is required') != -1:
self.set_pma_phpversion()
return 'Incompatible PHP version, an attempt has been made to automatically switch to a compatible PHP version, please refresh the page and try again!'
elif p_res.content.find(b'<strong>Deprecation Notice</strong>') != -1 and not session.get('set_pma_phpversion'):
self.set_pma_phpversion()
session['set_pma_phpversion'] = True
return 'Incompatible PHP version, an attempt has been made to automatically switch to a compatible PHP version, please refresh the page and try again!'
res = Response(p_res.content,headers=self.get_res_headers(p_res),content_type=p_res.headers.get('content-type',None),status=p_res.status_code)
res = self.set_res_headers(res,p_res)
return res
except Exception as ex:
return Response(str(ex),500)