Files
yakpanel-core/class/panelPmd.py

189 lines
6.1 KiB
Python
Raw Normal View History

2026-04-07 02:04:22 +05:30
#coding: utf-8
#-------------------------------------------------------------------
# YakPanel
#-------------------------------------------------------------------
# Copyright (c) 2015-2019 YakPanel(www.yakpanel.com) All rights reserved.
#-------------------------------------------------------------------
# Author: hwliang <hwl@yakpanel.com>
#-------------------------------------------------------------------
import public,os
from YakPanel import request,abort,send_file
_PMD_PATH = '/www/server/phpmyadmin/phpmyadmin_b313c665284f1718/'
import time
import fastcgiClient as fcgi_client
import struct
FCGI_Header = '!BBHHBx'
class FPM(object):
def __init__(self,sock=None, document_root='/www/server/phpmyadmin/phpmyadmin_b313c665284f1718/'):
'''
@name 实例化FPM对象
@author hwliang<2020-07-11>
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
def load_url(self, url, content=b''):
'''
@name 转发URL到PHP-FPM
@author hwliang<2020-07-11>
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': request.method,
'SCRIPT_NAME': '/phpmyadmin/'+script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'Yak-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': request.headers.get('Content-Type','application/x-www-form-urlencoded'),
'CONTENT_LENGTH': str(request.headers.get('Content-Length','0')),
'DOCUMENT_URI': request.path,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL' : 'HTTP/1.1',
'REMOTE_ADDR': request.remote_addr.replace('::ffff:',''),
'REMOTE_PORT': str(request.environ.get('REMOTE_PORT')),
'SERVER_ADDR': request.headers.get('host'),
'SERVER_PORT': '80',
'SERVER_NAME': 'Yak-Panel',
}
for k in request.headers.keys():
key = 'HTTP_' + k.replace('-','_').upper()
env[key] = request.headers[k]
fpm_sock = fcgi(env, content)
return fpm_sock
def request_php(uri):
'''
@name 发起fastcgi请求到PHP-FPM
@author hwliang<2020-07-11>
param puri string(URI地址)
@return socket
'''
f = FPM(sock='/tmp/php-cgi-72.sock')
if request.full_path.find('?') != -1:
uri = request.full_path[request.full_path.find(uri):]
sock = f.load_url(uri,content=request.stream)
return sock
def start(puri):
'''
@name 开始处理PHP请求
@author hwliang<2020-07-11>
param puri string(URI地址)
@return socket or Response
'''
if puri in ['/','',None]: puri = '/index.php'
filename = _PMD_PATH + puri
if not os.path.exists(filename):
return abort(404)
#如果是PHP文件
if puri[-4:] == '.php':
return request_php(puri)
#如果是静态文件
return send_file(filename)
#获取头部32KB数据
def get_header_data(sock):
'''
@name 获取头部32KB数据
@author hwliang<2020-07-11>
param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 32
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
headers_data += sock.recv(fast_pack[3])
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len: break
return headers_data
#格式化响应头
def format_header_data(headers_data):
'''
@name 格式化响应头
@author hwliang<2020-07-11>
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0: break
line = headers_data[pos:eolpos-1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2: break
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' YakPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
headers['Transfer-Encoding'] = 'chunked'
status = int(status.split(' ')[0])
return status,headers,bdata
#以流的方式发送剩余数据
def resp_sock(sock,bdata):
'''
@name 以流的方式发送剩余数据
@author hwliang<2020-07-11>
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
#发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header: break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
data = sock.recv(fast_pack[3])
if fast_pack[4]:
sock.recv(fast_pack[4])
if not data: break
yield data
sock.close()