python3 手動編寫的簡單的web服務器(HttpServer)

httpserver代碼如下

import socket
import pymysql
import re
from uuid import uuid4
from datetime import datetime, timedelta

# 1. 識別不同的網址 --> 返回不同的頁面
# 2. 能夠加載外部的html文件進來
# 3. 服務器去鏈接數據庫
# 4. 註冊功能 --> 插入一條數據到mysql中
# 5. 登陸功能 --> 在數據庫中查詢 在註冊的時候插入的賬戶密碼是否匹配
# 6. 保持登陸 --> cookie 或者 session

HOME_DIR = '.\html'

class HttpServer():
    def __init__(self):
        self.ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 重啓不改端口號
        self.ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.ss.bind(('127.0.0.1', 8080))
        self.ss.listen(10)
        self.session_id = None  # session默默認爲None
        print('HTTP服務器已啓動...')

    def start(self):
        while True:
            conn, addr = self.ss.accept()
            try:
                msg = conn.recv(1024)
                url = self.get_url_argument(msg)        # url是個列表,裏面存了url、用戶參數和cookie
                func, res, info = self.url_header_list(url)     #參數url數據直接返回到info中
                # 得到session_id,加入到響應報文
                cookie = self.set_cookie(self.session_id)
                # 響應報文
                response_header = 'HTTP/1.1 OK\r\nContent-Type:text/html\r\n' \
                                    'Connection:Close\r\n'
                response_body = func(res, info)
                conn.send((response_header + cookie + response_body).encode())
                conn.close()
            except Exception as e:
                conn.close()
                print(e)

    # 把的url和對應的邏輯函數、資源路徑和請求參數返回一個tuple
    def url_header_list(self, url):
        if url[0] == '/':
            res = HOME_DIR + '\\index.html'
            return (self.index, res, url)
        if url[0] == '/index.html':
            res = HOME_DIR + '\\index.html'
            return (self.index, res, url)
        elif url[0] == '/login.html':
            print(url)
            res = HOME_DIR + '\\login.html'
            return (self.login, res, url)
        elif url[0] == '/register.html':
            res = HOME_DIR + '\\register.html'
            return (self.register, res, url)
        elif url[0] == '/exit':
            res = HOME_DIR + '\\index.html'
            return (self.main_exit, res, url)
        else:
            res = HOME_DIR + '\\error.html'
            return (self.error, res, url[1])

    # 請求報文切割url和請求argument
    def get_url_argument(self, msg):
        msg = msg.decode()
        msg_list = msg.split('\r\n\r\n')
        info_dict = {}      # 參數信息,放入字典
        request_info = []  # 打包所有信息的列表
        request_msg = msg_list[0].split()      # 切割請求報文,找到url
        request_info.append(request_msg[1])   # 把url放入列表
        cookie = self.get_cookie(msg)       # 獲取cookie信息
        if msg_list[1] != '':       # 從請求報文裏切割傳遞的參數,並放入字典
            info_list = msg_list[1].split('&')
            info1 = info_list[0].split('=')
            info2 = info_list[1].split('=')
            info_dict[info1[0]] = info1[1]
            info_dict[info2[0]] = info2[1]
            request_info.append(info_dict)      # 把信息字典放入列表
            request_info.append(cookie)     # 把cookie放入列表
            return request_info
        else:
            request_info.append(info_dict)
            request_info.append(cookie)  # 把cookie放入列表
            return request_info
    # 連接mysql數據庫
    def mysql_conn(self):
        conn = pymysql.connect(
            host='127.0.0.1',
            user='root', password='root',
            database='httpweb',
            charset='utf8')
        return conn
    # 進入首頁,判斷session是否過期,未過期直接登錄。
    def index(self, res, info):
        html = res
        count, username = self.get_session(info[2])
        if info is not None:
            # count, username = self.get_session(info[2])
            if count > 0:
                html = HOME_DIR + '\\main.html'
        with open(html, encoding='utf-8') as f:
            response_body = f.read()
            try:
                response_body = self.render(response_body, username)
            except Exception:
                print("報錯")
            print(response_body)
        return response_body

    def main_exit(self, res, info):
        delete = self.del_session(info[2])
        print(delete)
        with open(res, encoding='utf-8') as f:
            response_body = f.read()
        return response_body

    # 進入錯誤頁面
    def error(self, res, info):
        with open(res, encoding='utf-8') as f:
            response_body = f.read()
        return response_body

    # 進入登錄界面和登錄邏輯
    def login(self, res, info):
        if not info[1]:
            with open(res, encoding='utf-8') as f:
                response_body = f.read()
            return response_body
        else:
            count, user_info = self.login_dao(info[1])
            if count > 0:
                session = self.set_session(user_info[0])
                html = '\\main.html'
            else:
                html = '\\login_fail.html'
            with open(HOME_DIR+html, encoding='utf-8') as f:
                response_body = f.read()
                response_body = self.render(response_body, user_info[1])
                return response_body

    # 進入註冊界面和註冊邏輯
    def register(self, res, info):
        if not info[1]:
            with open(res, encoding='utf-8') as f:
                response_body = f.read()
            return response_body
        else:
            html = ''
            is_exist = self.user_exists(info[1])
            if is_exist > 0:
                html = '\\register_fail.html'
            else:
                count = self.register_dao(info[1])
                if count > 0:
                    html = '\\login.html'
                else:
                    html = '\\register_error.html'
            with open(HOME_DIR+html, encoding='utf-8') as f:
                response_body = f.read()
                return response_body

    # 登錄dao函數
    def login_dao(self, info):
        sql_conn = self.mysql_conn()
        username = info['username']
        password = info['password']
        sql = "select * from users where `name`='{}' and `password`='{}'".format(username, password)
        cursor = sql_conn.cursor()
        count = cursor.execute(sql)
        user_info = cursor.fetchone()
        cursor.close()
        sql_conn.commit()
        sql_conn.close()
        return count, user_info

    # 註冊dao函數
    def register_dao(self, info):
        sql_conn = self.mysql_conn()
        username = info['username']
        password = info['password']
        sql = "insert into users (`name`,`password`) values ('{}','{}')".format(username, password)
        cursor = sql_conn.cursor()
        count = cursor.execute(sql)
        sql_conn.commit()
        sql_conn.close()
        return count

    # 判斷用戶是否存在dao函數
    def user_exists(self, info):
        sql_conn = self.mysql_conn()
        username = info['username']
        sql = "select * from users where `name`='{}'".format(username)
        cursor = sql_conn.cursor()
        count = cursor.execute(sql)
        cursor.close()
        sql_conn.commit()
        sql_conn.close()
        return count

    # 利用正則表達式替換html中的內容,渲染技術
    def render(self, data, replace):
        pattern = re.compile('{{ }}')
        res = re.sub(pattern, replace, data)
        return res

    # 設置cookie
    def set_cookie(self, session_id):
        cookie = 'Set-Cookie: session_id={}\r\n\r\n'.format(session_id)
        return cookie

    # 獲取cookie,把session_id從請求報文切割出來
    def get_cookie(self, msg):
        msg_list = msg.split('\r\n')
        for msg in msg_list:
            if msg.startswith('Cookie:'):
                cookies = msg[len("Cookie:"):]
                cookie_list = cookies.split(';')
                for cookie in cookie_list:
                    cookie = cookie.replace(' ', "")
                    if cookie.startswith('session_id='):
                        serect_key = cookie.split('=')[-1]
                        return serect_key
                break
        return None

    # 獲取5天后時間
    def get_time(self, days=5):
        return datetime.now() + timedelta(days=days)

    # 設置session,把session放入數據庫中
    def set_session(self, users_id):
        # del_session = self.del_all_session(users_id)      # 多瀏覽器,不使用刪除所有session
        # print(del_session)
        sql_conn = self.mysql_conn()
        session_id = self.gen_uuid()
        self.session_id = session_id
        sql = "insert into `session` (serect_key, validate, users_id) values('{}', '{}', '{}')".format(session_id, self.get_time(), users_id)
        cursor = sql_conn.cursor()
        count = cursor.execute(sql)
        cursor.close()
        sql_conn.commit()
        sql_conn.close()
        return count

    # 獲取session,從數據庫中查找用戶是否有未過期的session數據
    def get_session(self, info):
        session_id = info
        sql_conn = self.mysql_conn()
        cursor = sql_conn.cursor()
        # 根據session有效時間查,暫時沒寫
        sql = "select users.name from  `session`  join users on `session`.users_id=users.id where `session`.serect_key='{}'".format(session_id)
        count = cursor.execute(sql)
        res = None
        if count > 0:
            res = cursor.fetchone()
        sql_conn.commit()
        cursor.close()
        sql_conn.close()
        if res is None:
            return count, res
        else:
            return count, res[0]

    # 用uuid生成唯一碼作爲session
    def gen_uuid(self):
        return uuid4().hex

    # 刪除用戶當前session
    def del_session(self, info):
        session_id = info
        sql_conn = self.mysql_conn()
        cursor = sql_conn.cursor()
        sql = "delete from `session` where serect_key='{}'".format(session_id)
        count = cursor.execute(sql)
        sql_conn.commit()
        cursor.close()
        sql_conn.close()
        return count

if __name__ == '__main__':
    hp = HttpServer()
    hp.start()

目錄格式
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章