# encoding:utf-8
import socket
import _thread
import base64
import logging
import tkinter
from tkinter import Label, Button, Entry
import time
import json

class Header:
    def __init__(self, conn):
        self._method = None
        header = b''
        try:
            while 1:
                data = conn.recv(4096)
                header = b"%s%s" % (header, data)
                if header.endswith(b'\r\n\r\n') or (not data):
                    break
        except:
            pass
        self._header = header
        self.header_list = header.split(b'\r\n')
        self._host = None
        self._port = None
        self._auth = None
 
    def get_method(self):
        if self._method is None:
            self._method = self._header[:self._header.index(b' ')]
        return self._method
 
    def get_auth(self):
        if self._auth is None:
            for s in self.header_list:
                if s.startswith(b"Proxy-Authorization:"):
                    k = s.split(b"Basic ")
                    if len(k) > 1:
                        self._auth = k[1].decode('utf8')
                    break
            else:
                self._auth = ""
        return self._auth
 
    def get_host_info(self):
        if self._host is None:
            method = self.get_method()
            line = self.header_list[0].decode('utf8')
            if method == b"CONNECT":
                host = line.split(' ')[1]
                if ':' in host:
                    host, port = host.split(':')
                else:
                    port = 443
            else:
                for i in self.header_list:
                    if i.startswith(b"Host:"):
                        host = i.split(b" ")
                        if len(host) < 2:
                            continue
                        host = host[1].decode('utf8')
                        break
                else:
                    host = line.split('/')[2]
                if ':' in host:
                    host, port = host.split(':')
                else:
                    port = 80
            self._host = host
            self._port = int(port)
        return self._host, self._port
 
    @property
    def data(self):
        return self._header
 
    def is_ssl(self):
        if self.get_method() == b'CONNECT':
            return True
        return False
 
    def __repr__(self):
        return str(self._header.decode("utf8"))
 
 
pxy_ip = ""
pxy_port = 0
pxy_auth = ""

frame = tkinter.Tk()
frame.title('QY Http Proxy Server')

screenWidth = frame.winfo_screenwidth() # 获取显示区域的宽度
screenHeight = frame.winfo_screenheight() # 获取显示区域的高度
width = 450
height = 215
left = (screenWidth - width) / 2
top = (screenHeight - height) / 2
frame.geometry("%dx%d+%d+%d" % (width, height, left, top))
#frame.geometry('450x215')

label_port = Label(frame, text="端口:", font=('Arial', 12))
label_user = Label(frame, text="用户名:", font=('Arial', 12))
label_pwd = Label(frame, text="密码:", font=('Arial', 12))
label_info= Label(frame, text="未启动", font=('Arial', 10), width=50, bg="lightyellow", anchor="w", justify="left")

text_port = Entry(frame, width=30, font=('Arial', 12))
text_user = Entry(frame, width=30, font=('Arial', 12))
text_pswd = Entry(frame, width=30, font=('Arial', 12))

def curr_time():
    return time.strftime('%Y%m%d %X')

def communicate(sock1, sock2):
    try:
        while 1:
            data = sock1.recv(1024)
            if not data:
                return
            sock2.sendall(data)
    except:
        pass

def handle(client, ipaddr):
    timeout = 60
    client.settimeout(timeout)
    header = Header(client)
    if not header.data:
        client.close()
        return
    auth = header.get_auth()
    header.get_host_info()
    #print(*header.get_host_info(), header.get_method(), auth)
    sInfo =   ipaddr[0] + " " + header._host + " " + str(header._method,'utf-8') 
    label_info["text"] =curr_time() + " " +sInfo
    #logger.info(sInfo)
    if auth != pxy_auth:
        logger.info(sInfo + "==>Authorization Failed...")
        client.close()
        return
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        server.connect(header.get_host_info())
        server.settimeout(timeout)
        if header.is_ssl():
            data = b"HTTP/1.0 200 Connection Established\r\n\r\n"
            client.sendall(data)
            _thread.start_new_thread(communicate, (client, server))
        else:
            server.sendall(header.data)
        communicate(server, client)
    except:
        server.close()
        client.close()
 
logger = logging.getLogger('mylogger')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('./http_proxy.log')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s.%(msecs)d %(levelname)s: %(message)s', datefmt='%Y-%m-%d %X')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info('http proxy opened...')

fname_cfg = "./http_proxy.json"
tmp_port = "13187"
tmp_user = "Qytrader"
tmp_pswd = "Ty123123"

try:
    with open(fname_cfg) as f:
        data = json.load(f)
        tmp_port = data['port']
        tmp_user = data['user']
        tmp_pswd = data['pswd']
except:
    pass

def serve():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((pxy_ip, pxy_port))
    s.listen(10)
    print('proxy start...', pxy_ip, pxy_port, pxy_auth)
    logger.info('proxy start...' + pxy_ip + " " + str(pxy_port) + " " + pxy_auth)
    label_info["text"] = "等待客户端连接。。。"
    while True:
        conn, addr = s.accept()
        _thread.start_new_thread(handle, (conn,addr))
 
text_port.insert(0, tmp_port)
text_user.insert(0, tmp_user)
text_pswd.insert(0, tmp_pswd)

def start_proxy():
    global pxy_ip
    global pxy_port
    global pxy_auth
    button_ok['text'] = "正在运行"
    button_ok['state'] = tkinter.DISABLED
    button_ok.flash()

    s_port = text_port.get()
    s_user = text_user.get()
    s_pswd = text_pswd.get()

    with open(fname_cfg, 'w') as f:
        #dataw = { "port": "'" + s_port + "'", "user":  "'" +s_user + "'", "pswd":  "'" +s_pswd + "'" }
        dataw = { "port": s_port, "user": s_user, "pswd": s_pswd }
        json.dump(dataw, f)

    pxy_ip = "0.0.0.0"
    pxy_port = int(s_port)
    pxy_auth = base64.b64encode(bytes(s_user+":"+s_pswd, 'utf-8') ).decode('utf8')
    _thread.start_new_thread(serve, ())

button_ok = Button(frame, text="启动", width=10, font=('Arial', 12), command=start_proxy)

label_port.grid(row=0, column=0, pady=10, padx=30)
label_user.grid(row=1, column=0, pady=10)
label_pwd.grid(row=2, column=0, pady=10)

text_port.grid(row=0, column=1)
text_user.grid(row=1, column=1)
text_pswd.grid(row=2, column=1)
button_ok.grid(row=3, column=0, columnspan=2)

label_info.grid(row=4, column=0, padx=10, pady=15, columnspan=3)

frame.mainloop()
