读webpy源码-请求响应1

上一篇简述了webpy响应流程过程,这里着重讲下可能大家关心的细节点。
运行时启动一个socket(默认8080端口),当在浏览器请求url发起时,就是向这个socket发送了http协议的报文。socket收到后会解析包装成request对象,然后调用application的方法返回。
后面主要看看解析做了什么?响应又是怎么做的? 本人的主要思路是:

  1. 在哪里解析出url中的信息(比如path,param)
  2. 在哪里根据解析出来的信息找到对应的处理类(根据web.ctx.path映射到handlerClass)
因为主要流程在上一篇已经阐述过,所以这里直接重点看下解析部分。

        httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))


-->

    #全局变量,后面的server.start()里面就用self.server方式把server放入到WorkThread中。
server = None
def runsimple(func, server_address=("0.0.0.0", 8080)):
    global server
    func = StaticMiddleware(func)
    func = LogMiddleware(func)
    #这里会把wsgi_app(即func)填充到server,这样后面respond响应时就可以调用self.server.wsgi_app了
    server = WSGIServer(server_address, func)
    if server.ssl_adapter:
        print "https://%s:%d/" % server_address
    else:
        print "http://%s:%d/" % server_address
    try:
        server.start()
    except (KeyboardInterrupt, SystemExit):
        server.stop()
        server = None
    
 
-------------------->
 
 #server.start()
def start(self):
        self._interrupt = None

        if self.software is None:
            self.software = "%s Server" % self.version

        if (self.ssl_adapter is None and
            getattr(self, 'ssl_certificate', None) and
            getattr(self, 'ssl_private_key', None)):
            warnings.warn(
                    "SSL attributes are deprecated in CherryPy 3.2, and will "
                    "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
                    "instead.",
                    DeprecationWarning
                )
            try:
                from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
            except ImportError:
                pass
            else:
                self.ssl_adapter = pyOpenSSLAdapter(
                    self.ssl_certificate, self.ssl_private_key,
                    getattr(self, 'ssl_certificate_chain', None))

        if isinstance(self.bind_addr, basestring):
            try: os.unlink(self.bind_addr)
            except: pass
            try: os.chmod(self.bind_addr, 0777)
            except: pass

            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
        else:
            host, port = self.bind_addr
            try:
                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
            except socket.gaierror:
                if ':' in self.bind_addr[0]:
                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
                             0, "", self.bind_addr + (0, 0))]
                else:
                    info = [(socket.AF_INET, socket.SOCK_STREAM,
                             0, "", self.bind_addr)]

        self.socket = None
        msg = "No socket could be created"
        for res in info:
            af, socktype, proto, canonname, sa = res
            try:
                self.bind(af, socktype, proto)
            except socket.error:
                if self.socket:
                    self.socket.close()
                self.socket = None
                continue
            break
        if not self.socket:
            raise socket.error(msg)

        self.socket.settimeout(1)
        self.socket.listen(self.request_queue_size)

        #由这里进入看request的解析
        self.requests.start()

        self.ready = True
        self._start_time = time.time()
        while self.ready:
            self.tick()
            if self.interrupt:
                while self.interrupt is True:
                    time.sleep(0.1)
                if self.interrupt:
                    raise self.interrupt
 
 
------------------>
 
 def start(self):
        for i in range(self.min):
        #可以看到WorkerThread是一个线程,主要用来处理request
            self._threads.append(WorkerThread(self.server))
        for worker in self._threads:
            worker.setName("CP Server " + worker.getName())
            worker.start()
        for worker in self._threads:
            while not worker.ready:
                time.sleep(.1)
 
 
-------------------->
 
 def run(self):
        self.server.stats['Worker Threads'][self.getName()] = self.stats
        try:
            self.ready = True
            while True:
                conn = self.server.requests.get()
                if conn is _SHUTDOWNREQUEST:
                    return

                self.conn = conn
                if self.server.stats['Enabled']:
                    self.start_time = time.time()
                try:
                #这里是解析入口
                    conn.communicate()
                finally:
                    conn.close()
                    if self.server.stats['Enabled']:
                        self.requests_seen += self.conn.requests_seen
                        self.bytes_read += self.conn.rfile.bytes_read
                        self.bytes_written += self.conn.wfile.bytes_written
                        self.work_time += time.time() - self.start_time
                        self.start_time = None
                    self.conn = None
        except (KeyboardInterrupt, SystemExit), exc:
            self.server.interrupt = exc
 
 
------------------>
 
 def communicate(self):
        request_seen = False
        try:
            while True:
                req = None
                req = self.RequestHandlerClass(self.server, self)
                #解析入口
                req.parse_request()
                if self.server.stats['Enabled']:
                    self.requests_seen += 1
                if not req.ready:
                    return
                request_seen = True
                #响应入口
                req.respond()
                if req.close_connection:
                    return
 
 
-------------------->
 
 def parse_request(self):
        self.rfile = SizeCheckWrapper(self.conn.rfile,
                                      self.server.max_request_header_size)
        try:
            #解析入口
            self.read_request_line()
        except MaxSizeExceeded:
            self.simple_response("414 Request-URI Too Long",
                "The Request-URI sent with the request exceeds the maximum "
                "allowed bytes.")
            return

        try:
            success = self.read_request_headers()
        except MaxSizeExceeded:
            self.simple_response("413 Request Entity Too Large",
                "The headers sent with the request exceed the maximum "
                "allowed bytes.")
            return
        else:
            if not success:
                return

        self.ready = True
 
 
-------------------->
 
 def read_request_line(self):
        request_line = self.rfile.readline()
        self.started_request = True
        if not request_line:
            self.ready = False
            return
        if request_line == CRLF:
            request_line = self.rfile.readline()
            if not request_line:
                self.ready = False
                return
        if not request_line.endswith(CRLF):
            self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
            return
        try:
            method, uri, req_protocol = request_line.strip().split(" ", 2)
            rp = int(req_protocol[5]), int(req_protocol[7])
        except (ValueError, IndexError):
            self.simple_response("400 Bad Request", "Malformed Request-Line")
            return
        self.uri = uri
        self.method = method
        scheme, authority, path = self.parse_request_uri(uri)
        if '#' in path:
            self.simple_response("400 Bad Request",
                                 "Illegal #fragment in Request-URI.")
            return
        if scheme:
            self.scheme = scheme

        qs = ''
        if '?' in path:
            path, qs = path.split('?', 1)
        try:
            atoms = [unquote(x) for x in quoted_slash.split(path)]
        except ValueError, ex:
            self.simple_response("400 Bad Request", ex.args[0])
            return
        path = "%2F".join(atoms)
        #重点在这里有个说明,因为后面去做respond是根据path映射到对应的处理类上
        #后面在回调wsgi_app时,会用self.path填充web.ctx.path
        #比如你的项目里有如此路径映射配置'/getJsonData', 'getJsonDataHandler';那么会根据web.ctx.path的请求路径/getJsonData找到对应的getJsonDataHandler处理类
        self.path = path
        self.qs = qs
        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
        if sp[0] != rp[0]:
            self.simple_response("505 HTTP Version Not Supported")
            return
        self.request_protocol = req_protocol
        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
 

猜你喜欢

转载自zhusirong.iteye.com/blog/2377374
今日推荐