美文网首页
06-mini-web框架01

06-mini-web框架01

作者: 努力爬行中的蜗牛 | 来源:发表于2018-11-26 14:33 被阅读10次
web服务器静态资源、动态资源解析
import socket
import re
import multiprocessing
# import threading
import mimi_frame

class WSGIServer(object):
    def __init__(self):
        # 1. 创建套接字
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了下次运行的时候可以立即使用这个地址
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        # 2. 绑定
        self.tcp_server_socket.bind(("127.0.0.1", 7890))
        # 3. 变为监听套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """为这个客户端返回数据"""
        # 1. 接收浏览器发送过来的请求,即http请求
        # GET / HTTP 1.1
        request = new_socket.recv(1024).decode("utf-8 ")
        # print(">" * 50)
        # print(request)

        requet_lines = request.splitlines()
        print("")
        print(">" * 20)
        print(requet_lines)

        # GET /index.html HTTP/1.1
        file_name = ""
        # [ ]的^ 表示取反 即不是/ 不是空格
        ret = re.match(r"[^/]+(/[^ ]*)", requet_lines[0])
        print("-" * 100)
        print(ret)
        print("-" * 100)
        if ret:
            file_name = ret.group(1)
            # print("*" * 50, file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2. 返回http格式的数据,给浏览器
        # 2.1 如果请求的资源不是以.py结尾的,那么就认为是静态资源(html/css/js/png,jpg)
        if not file_name.endswith(".py"):
            try:
                f = open("./html" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # 换行
                response += "\r\n"
                response += "---file not found---"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 2. 返回http格式的数据,给浏览器
                # 2.1 准备发送给浏览器的数据---header
                response = "HTTP/1.1 200 OK\r\n"  # 换行
                response += "\r\n"
                # 2.2 准备发送给浏览器的数据---body
                # response += "<h1>hello world</h1>"
                # new_socket.send(response.encode("utf-8"))
                # 将response header发送给浏览器
                new_socket.send(response.encode("utf-8"))
                # 将response body发送给浏览器
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            header = "HTTP/1.1 200 OK\r\n"
            header += "\r\n"

            body = mimi_frame.application(file_name)

            response = header + body
            # 发送response给浏览器
            new_socket.send(response.encode("utf-8"))


        # 3. 关闭套接字
        new_socket.close()

    def run_forerver(self):
        while True:
            # 4. 等待新客户的链接
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 开启一条子进程或者线程来服务客户端
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            # p = threading.Thread(target=service_client, args=(new_socket, ))
            p.start()

            # 多进程需要子进程和主进程同时关闭 才会关闭 fd 文件描述符
            new_socket.close()
        # 关闭监听套接字
        self.tcp_server_socket.close()

def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forerver()


if __name__ == '__main__':
    main()
import time


def login():
    return "Welcome to come here %s" % time.ctime()


def register():
    return "register %s" % time.ctime()


def profile():
    return "profile %s" % time.ctime()


def application(file_name):
    if file_name == "/login.py":
        return login()
    elif file_name == "/register.py":
        return register()
    else:
        return "not found your page..."

WSGI
  • WSGI允许开发者将选择web框架和web服务器分开,可以混合匹配web服务器和web框架,选择一个适合的匹配。比如,可以再Gunicorn或者Nginx/uWSGI或者Waitress上运行Django,Flask或者Pyramid。真正的混合匹配,得益于WSGI同时支持服务器和架构。
  • web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口,它让你不对代码修改就能使服务器和特定的web框架协同工作。
    WSGI由web服务器支持,而web框架允许你选择适合自己的配对,但它同样对于服务器和框架开发者提供便利使他们可以专注于自己偏爱的领域和专长而不至于相互牵制。其他语言也有类似接口:java有Servlet API,Ruby有Rack。
WSGI接口定义

WSGI接口定义非常简单,它只要求web开发者实现一个函数,就可以响应HTTP请求。

def application(environ, start_response):
    start_response('200 OK',[('Content-Type', 'text/html')])
    return 'Hello World!'

上面的application() 函数就是符合WSGI标砖的一个HTTP处理函数,它接收两个参数

  • environ:一个包含所有HTTP请求信息的dict对象;
  • start_response:一个发送HTTP响应的函数
    整个application() 函数本身没有涉及到任何解析HTTP的部分,也就是说,把底层web服务器和应用程序逻辑部分进行了分离,这样开发者就可以专心做一个领域了。


    WSGI协议流程.png
import socket
import re
import multiprocessing
# import threading
import mimi_frame

class WSGIServer(object):
    def __init__(self):
        # 1. 创建套接字
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了下次运行的时候可以立即使用这个地址
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        # 2. 绑定
        self.tcp_server_socket.bind(("127.0.0.1", 7890))
        # 3. 变为监听套接字
        self.tcp_server_socket.listen(128)
        
        # 保存返回数据
        self.status = ""
        self.headers = tuple()

    def service_client(self, new_socket):
        """为这个客户端返回数据"""
        # 1. 接收浏览器发送过来的请求,即http请求
        # GET / HTTP 1.1
        request = new_socket.recv(1024).decode("utf-8 ")
        # print(">" * 50)
        # print(request)

        requet_lines = request.splitlines()
        print("")
        print(">" * 20)
        print(requet_lines)

        # GET /index.html HTTP/1.1
        file_name = ""
        # [ ]的^ 表示取反 即不是/ 不是空格
        ret = re.match(r"[^/]+(/[^ ]*)", requet_lines[0])
        print("-" * 100)
        print(ret)
        print("-" * 100)
        if ret:
            file_name = ret.group(1)
            # print("*" * 50, file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2. 返回http格式的数据,给浏览器
        # 2.1 如果请求的资源不是以.py结尾的,那么就认为是静态资源(html/css/js/png,jpg)
        if not file_name.endswith(".py"):
            try:
                f = open("./html" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # 换行
                response += "\r\n"
                response += "---file not found---"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 2. 返回http格式的数据,给浏览器
                # 2.1 准备发送给浏览器的数据---header
                response = "HTTP/1.1 200 OK\r\n"  # 换行
                response += "\r\n"
                # 2.2 准备发送给浏览器的数据---body
                # response += "<h1>hello world</h1>"
                # new_socket.send(response.encode("utf-8"))
                # 将response header发送给浏览器
                new_socket.send(response.encode("utf-8"))
                # 将response body发送给浏览器
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            env = dict()

            body = mimi_frame.application(env, self.set_response_header)

            header = "HTTP/1.1 %s\r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送response给浏览器
            new_socket.send(response.encode("utf-8"))

        # 3. 关闭套接字
        new_socket.close()

    def set_response_header(self, status, headers):
        self.status = status
        self.headers = [("server", "mini_web v8.8")]
        self.headers += headers

    def run_forerver(self):
        while True:
            # 4. 等待新客户的链接
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 开启一条子进程或者线程来服务客户端
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            # p = threading.Thread(target=service_client, args=(new_socket, ))
            p.start()

            # 多进程需要子进程和主进程同时关闭 才会关闭 fd 文件描述符
            new_socket.close()
        # 关闭监听套接字
        self.tcp_server_socket.close()

def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forerver()


if __name__ == '__main__':
    main()
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'')])
    return 'Hello World!'
获取页面模板数据
import socket
import re
import multiprocessing
# import threading
import mimi_frame

class WSGIServer(object):
    def __init__(self):
        # 1. 创建套接字
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了下次运行的时候可以立即使用这个地址
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        # 2. 绑定
        self.tcp_server_socket.bind(("127.0.0.1", 7890))
        # 3. 变为监听套接字
        self.tcp_server_socket.listen(128)

        # 保存返回数据
        self.status = ""
        self.headers = tuple()

    def service_client(self, new_socket):
        """为这个客户端返回数据"""
        # 1. 接收浏览器发送过来的请求,即http请求
        # GET / HTTP 1.1
        request = new_socket.recv(1024).decode("utf-8 ")
        # print(">" * 50)
        # print(request)

        requet_lines = request.splitlines()
        print("")
        print(">" * 20)
        print(requet_lines)

        # GET /index.html HTTP/1.1
        file_name = ""
        # [ ]的^ 表示取反 即不是/ 不是空格
        ret = re.match(r"[^/]+(/[^ ]*)", requet_lines[0])
        print("-" * 100)
        print(ret)
        print("-" * 100)
        if ret:
            file_name = ret.group(1)
            # print("*" * 50, file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2. 返回http格式的数据,给浏览器
        # 2.1 如果请求的资源不是以.py结尾的,那么就认为是静态资源(html/css/js/png,jpg)
        if not file_name.endswith(".py"):
            try:
                f = open("./html" + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # 换行
                response += "\r\n"
                response += "---file not found---"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 2. 返回http格式的数据,给浏览器
                # 2.1 准备发送给浏览器的数据---header
                response = "HTTP/1.1 200 OK\r\n"  # 换行
                response += "\r\n"
                # 2.2 准备发送给浏览器的数据---body
                # response += "<h1>hello world</h1>"
                # new_socket.send(response.encode("utf-8"))
                # 将response header发送给浏览器
                new_socket.send(response.encode("utf-8"))
                # 将response body发送给浏览器
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            env = dict()
            env['PATH_INFO'] = file_name

            body = mimi_frame.application(env, self.set_response_header)

            header = "HTTP/1.1 %s\r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送response给浏览器
            new_socket.send(response.encode("utf-8"))

        # 3. 关闭套接字
        new_socket.close()

    def set_response_header(self, status, headers):
        self.status = status
        self.headers = [("server", "mini_web v8.8")]
        self.headers += headers

    def run_forerver(self):
        while True:
            # 4. 等待新客户的链接
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 开启一条子进程或者线程来服务客户端
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            # p = threading.Thread(target=service_client, args=(new_socket, ))
            p.start()

            # 多进程需要子进程和主进程同时关闭 才会关闭 fd 文件描述符
            new_socket.close()
        # 关闭监听套接字
        self.tcp_server_socket.close()


def main():
    wsgi_server = WSGIServer()
    wsgi_server.run_forerver()


if __name__ == '__main__':
    main()
def index():
    return "主页"


def login():
    return "登录页"


def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])
    file_name = env['PATH_INFO']
    if file_name == "/index.py":
        return index()
    elif file_name == "/login.py":
        return login()
    else:
        return 'Hello World! 伟大的锅'

给程序传参数、添加web服务器的配置文件、添加shell功能
import socket
import re
import multiprocessing
# import threading
# import mimi_frame
import sys

class WSGIServer(object):
    def __init__(self, port, app, static_path):
        # 1. 创建套接字
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了下次运行的时候可以立即使用这个地址
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        # 2. 绑定
        self.tcp_server_socket.bind(("127.0.0.1", port))
        # 3. 变为监听套接字
        self.tcp_server_socket.listen(128)

        # 保存返回数据
        self.status = ""
        self.headers = tuple()

        self.application = app
        self.static_path = static_path

    def service_client(self, new_socket):
        """为这个客户端返回数据"""
        # 1. 接收浏览器发送过来的请求,即http请求
        # GET / HTTP 1.1
        request = new_socket.recv(1024).decode("utf-8 ")
        # print(">" * 50)
        # print(request)

        requet_lines = request.splitlines()
        print("")
        print(">" * 20)
        print(requet_lines)

        # GET /index.html HTTP/1.1
        file_name = ""
        # [ ]的^ 表示取反 即不是/ 不是空格
        ret = re.match(r"[^/]+(/[^ ]*)", requet_lines[0])
        print("-" * 100)
        print(ret)
        print("-" * 100)
        if ret:
            file_name = ret.group(1)
            # print("*" * 50, file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2. 返回http格式的数据,给浏览器
        # 2.1 如果请求的资源不是以.py结尾的,那么就认为是静态资源(html/css/js/png,jpg)
        if not file_name.endswith(".py"):
            try:
                f = open(self.static_path + file_name, "rb")
            except:
                response = "HTTP/1.1 404 NOT FOUND\r\n"  # 换行
                response += "\r\n"
                response += "---file not found---"
                new_socket.send(response.encode("utf-8"))
            else:
                html_content = f.read()
                f.close()
                # 2. 返回http格式的数据,给浏览器
                # 2.1 准备发送给浏览器的数据---header
                response = "HTTP/1.1 200 OK\r\n"  # 换行
                response += "\r\n"
                # 2.2 准备发送给浏览器的数据---body
                # response += "<h1>hello world</h1>"
                # new_socket.send(response.encode("utf-8"))
                # 将response header发送给浏览器
                new_socket.send(response.encode("utf-8"))
                # 将response body发送给浏览器
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py结尾,那么就认为是动态资源的请求
            env = dict()
            env['PATH_INFO'] = file_name

            body = self.application.application(env, self.set_response_header)

            header = "HTTP/1.1 %s\r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 发送response给浏览器
            new_socket.send(response.encode("utf-8"))

        # 3. 关闭套接字
        new_socket.close()

    def set_response_header(self, status, headers):
        self.status = status
        self.headers = [("server", "mini_web v8.8")]
        self.headers += headers

    def run_forerver(self):
        while True:
            # 4. 等待新客户的链接
            new_socket, client_addr = self.tcp_server_socket.accept()
            # 开启一条子进程或者线程来服务客户端
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            # p = threading.Thread(target=service_client, args=(new_socket, ))
            p.start()

            # 多进程需要子进程和主进程同时关闭 才会关闭 fd 文件描述符
            new_socket.close()
        # 关闭监听套接字
        self.tcp_server_socket.close()


def main():
    if len(sys.argv) == 2:
        try:
            port = int(sys.argv[1])  # 7890
            frame_app_name = sys.argv[2] # mini_frame:application
        except Exception as ret:
            print("端口输入错误。。。")
            return

    ret = re.match(r"([^:]+):(.*)", frame_app_name)
    if ret:
        frame_name = ret.group(1)  # mini_frame
        app_name = ret.group(2)  # application
    else:
        print("请按照以下方式运行:")
        print("python3 xxx.py 7890 mini_frame:application")
        return

    with open("./web_server.conf") as f:
        conf_info = eval(f.read())
        # 此时conf_info 里是一个字典里面的数据为
        # {
        #     "static_path": "./static",
        #     "dynamic_path": "./dynamic"
        # }

    sys.path.append(conf_info["static_path"])

    # import frame_name------> 找frame_name.py
    frame = __import__(frame_name)  # 返回值标记这导入的这个模块
    app = getattr(frame, app_name)  # 此时app就只想了dynamic/mini_frame.py这个模块

    wsgi_server = WSGIServer(port, app, conf_info["static_path"])
    wsgi_server.run_forerver()


if __name__ == '__main__':
    main()
def index():
    with open("./templates/index.html") as f:
        content = f.read()

    return content


def center():
    with open("./templates/center.html") as f:
        content = f.read()
    return content


def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])
    file_name = env['PATH_INFO']
    if file_name == "/index.py":
        return index()
    elif file_name == "/center.py":
        return center()
    else:
        return 'Hello World! 伟大的锅'

配置文件 web_server.conf

{
    "static_path":"./static",
    "dynamic_path":"./dynamic"
}

shell run.sh

python3 web_server.conf 7890 mini_frame:application

相关文章

网友评论

      本文标题:06-mini-web框架01

      本文链接:https://www.haomeiwen.com/subject/yjnvqqtx.html