189 lines
6.1 KiB
Python
189 lines
6.1 KiB
Python
#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() |