Read webpy source code - request response 1

The last article briefly described the webpy response process. Here we focus on the details that you may care about.
A socket (default 8080 port) is started at runtime. When the browser requests the url to initiate, it sends the http protocol message to this socket. After the socket is received, it will be parsed and packaged into a request object, and then the method of the application will be called to return.
Let's see what the analysis does later? How is the response done? My main idea is:

  1. Where to parse out the information in the url (such as path, param)
  2. Where to find the corresponding handler class based on the parsed information (mapped to handlerClass according to web.ctx.path)

 

Because the main process has been explained in the previous article, so here we directly focus on the analysis part.

        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)
 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326846642&siteId=291194637