# coding:utf-8 import json import os import time import re import sys import time import struct from .fcgi_client import FCGIApp FCGI_Header = '!BBHHBx' if sys.version_info[0] == 2: try: from cStringIO import StringIO except: from StringIO import StringIO else: from io import BytesIO as StringIO def get_header_data(sock): ''' @name 获取头部32KB数据 @param sock socketobject(fastcgi套接字对象) @return bytes ''' headers_data = b'' total_len = 0 header_len = 1024 * 128 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) if fast_pack[1] == 3: break tlen = fast_pack[3] while tlen > 0: sd = sock.recv(tlen) if not sd: break headers_data += sd tlen -= len(sd) 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 格式化响应头 @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 if line.find(b':') == -1: continue header, value = line.split(b':', 1) header = header.strip() value = value.strip() if isinstance(header, bytes): header = header.decode() value = value.decode() if header == 'Status': status = value if status.find(' ') < 0: status += ' BTPanel' else: headers[header] = value bdata = headers_data[pos:] status = int(status.split(' ')[0]) return status, headers, bdata def resp_sock(sock, bdata): ''' @name 以流的方式发送剩余数据 @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) if fast_pack[1] == 3: break tlen = fast_pack[3] while tlen > 0: sd = sock.recv(tlen) if not sd: break tlen -= len(sd) if sd: yield sd if fast_pack[4]: sock.recv(fast_pack[4]) sock.close() class fpm(object): def __init__(self, sock=None, document_root='', last_path=''): ''' @name 实例化FPM对象 @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 self.last_path = last_path def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'): ''' @name 转发URL到PHP-FPM 公共 @param url string(URI地址) @param content stream(POST数据io对象) @return fastcgi-socket ''' fcgi = FCGIApp(connect=self.fcgi_sock) try: script_name, query_string = url.split('?') except ValueError: script_name = url query_string = '' content_length = len(content) if content: content = StringIO(content) env = { 'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name), 'QUERY_STRING': query_string, 'REQUEST_METHOD': method, 'SCRIPT_NAME': self.last_path + script_name, 'REQUEST_URI': url, 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'MW-PANEL', 'REDIRECT_STATUS': '200', 'CONTENT_TYPE': content_type, 'CONTENT_LENGTH': str(content_length), 'DOCUMENT_URI': script_name, 'DOCUMENT_ROOT': self.document_root, 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '7200', 'SERVER_ADDR': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_NAME': 'MW-Panel' } fpm_sock = fcgi(env, content) _data = b'' while True: fastcgi_header = fpm_sock.recv(8) if not fastcgi_header: break if len(fastcgi_header) != 8: _data += fastcgi_header break fast_pack = struct.unpack(FCGI_Header, fastcgi_header) if fast_pack[1] == 3: break tlen = fast_pack[3] while tlen > 0: sd = fpm_sock.recv(tlen) if not sd: break tlen -= len(sd) _data += sd if fast_pack[4]: fpm_sock.recv(fast_pack[4]) status, headers, data = format_header_data(_data) return data