1. 程式人生 > >Web動態伺服器-2-傳遞資料給應用

Web動態伺服器-2-傳遞資料給應用

#coding=utf-8
import socket
import sys
from multiprocessing import Process
import re

class WSGIServer(object):
    addressFamily = socket.AF_INET
    socketType = socket.SOCK_STREAM
    requestQueueSize = 5
    
    def __init__(self, serverAddress):
        #建立一個tcp套接字
        self.listenSocket = socket.socket(self.addressFamily,
        self.socketType)
        #允許重複使用上次的套接字繫結的port
        self.listenSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        #繫結
        self.listenSocket.bind(serverAddress)
        #變為被動,並制定佇列的長度
        self.listenSocket.listen(self.requestQueueSize)
    
        self.servrName = "localhost"
        self.serverPort = serverAddress[1]
    
    def serveForever(self):
        '迴圈執行web伺服器,等待客戶端的連結併為客戶端服務'
        while True:
            #等待新客戶端到來
            self.clientSocket, client_address = self.listenSocket.accept()
            #方法2,多程序伺服器,併發伺服器於多個客戶端
            newClientProcess = Process(target = self.handleRequest)
            newClientProcess.start()
            #因為建立的新程序中,會對這個套接字+1,所以需要在主程序中減去依次,即呼叫⼀一次close
            self.clientSocket.close()

    def setApp(self, application):
        '設定此WSGI伺服器呼叫的應用程式入口函式'
        self.application = application

    def handleRequest(self):
        '用一個新的程序,為一個客戶端進行服務'
        self.recvData = self.clientSocket.recv(2014)
        requestHeaderLines = self.recvData.splitlines()
        for line in requestHeaderLines:
            print(line)

        httpRequestMethodLine = requestHeaderLines[0]
        getFileName = re.match("[^/]+(/[^ ]*)", httpRequestMethodLine).group(1)
        print("file name is ===>%s"%getFileName) #for test

        if getFileName[-3:] != ".py":
            if getFileName == '/':
                getFileName = documentRoot + "/index.html"
            else:
                getFileName = documentRoot + getFileName
            print("file name is ===2>%s"%getFileName) #for test
  
            try:
                f = open(getFileName)
            except IOError:
                responseHeaderLines = "HTTP/1.1 404 not found\r\n"
                responseHeaderLines += "\r\n"
                responseBody = "====sorry ,file not found===="

            else:
                responseHeaderLines = "HTTP/1.1 200 OK\r\n"
                responseHeaderLines += "\r\n"
                responseBody = f.read()
                f.close()
            finally:
                response = responseHeaderLines + responseBody
                self.clientSocket.send(response)
                self.clientSocket.close()
        else:
            #處理接收到的請求頭
            self.parseRequest()

            #根據接收到的請求頭構造環境變數字典
            env = self.getEnviron()

            #呼叫應用的相應方法,完成動態資料的獲取
            bodyContent = self.application(env, self.startResponse)

            #組織資料傳送給客戶端
            self.finishResponse(bodyContent)
    def parseRequest(self):
        '提取出客戶端傳送的request'
        requestLine = self.recvData.splitlines()[0]
        requestLine = requestLine.rstrip('\r\n')
        self.requestMethod, self.path, self.requestVersion =requestLine.split(" ")
        
    def getEnviron(self):
        env = {}
        env['wsgi.version'] = (1, 0)
        env['wsgi.input'] = self.recvData
        env['REQUEST_METHOD'] = self.requestMethod # GET
        env['PATH_INFO'] = self.path # /index.html
        return env

    def startResponse(self, status, response_headers, exc_info=None):
        serverHeaders = [
            ('Date', 'Tue, 31 Mar 2016 10:11:12 GMT'),
            ('Server', 'WSGIServer 0.2'),
        ]
        self.headers_set = [status, response_headers + serverHeaders]

    def finishResponse(self, bodyContent):
        try:
            status, response_headers = self.headers_set
            #response的第一行
            response = 'HTTP/1.1 {status}\r\n'.format(status=status)
            #response的其他頭資訊
            for header in response_headers:
                response += '{0}: {1}\r\n'.format(*header)
            #新增一個換行,用來和body進行分開
            response += '\r\n'
            #添加發送的資料
            for data in bodyContent:
                response += data

            self.clientSocket.send(response)
    finally:
        self.clientSocket.close()

#設定伺服器的埠
serverAddr = (HOST, PORT) = '', 8888
#設定伺服器靜態資源的路徑
documentRoot = './html'
#設定伺服器動態資源的路徑
pythonRoot = './wsgiPy'

def makeServer(serverAddr, application):
    server = WSGIServer(serverAddr)
    server.setApp(application)
    return server

def main():
    if len(sys.argv) < 2:
        sys.exit('請按照要求,指定模組名稱:應用名稱,例如 module:callable')
    #獲取module:callable
    appPath = sys.argv[1]
    #根據冒號切割為module和callable
    module, application = appPath.split(':')
    #新增路徑套sys.path
    sys.path.insert(0, pythonRoot)
    #動態匯入module變數中指定的模組
    module = __import__(module)
    #獲取module變數中制定的模組的application變數指定的屬性
    application = getattr(module, application)
    httpd = makeServer(serverAddr, application)
    print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
    httpd.serveForever()

if __name__ == '__main__':
    main()