Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/cherrypy/_cpnative_server.py
blob: 57f715a9307e164272df071bbb799091e5c545d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"""Native adapter for serving CherryPy via its builtin server."""

import logging
import sys

import cherrypy
from cherrypy._cpcompat import BytesIO
from cherrypy._cperror import format_exc, bare_error
from cherrypy.lib import httputil
from cherrypy import wsgiserver


class NativeGateway(wsgiserver.Gateway):
    
    recursive = False
    
    def respond(self):
        req = self.req
        try:
            # Obtain a Request object from CherryPy
            local = req.server.bind_addr
            local = httputil.Host(local[0], local[1], "")
            remote = req.conn.remote_addr, req.conn.remote_port
            remote = httputil.Host(remote[0], remote[1], "")
            
            scheme = req.scheme
            sn = cherrypy.tree.script_name(req.uri or "/")
            if sn is None:
                self.send_response('404 Not Found', [], [''])
            else:
                app = cherrypy.tree.apps[sn]
                method = req.method
                path = req.path
                qs = req.qs or ""
                headers = req.inheaders.items()
                rfile = req.rfile
                prev = None
                
                try:
                    redirections = []
                    while True:
                        request, response = app.get_serving(
                            local, remote, scheme, "HTTP/1.1")
                        request.multithread = True
                        request.multiprocess = False
                        request.app = app
                        request.prev = prev
                        
                        # Run the CherryPy Request object and obtain the response
                        try:
                            request.run(method, path, qs, req.request_protocol, headers, rfile)
                            break
                        except cherrypy.InternalRedirect:
                            ir = sys.exc_info()[1]
                            app.release_serving()
                            prev = request
                            
                            if not self.recursive:
                                if ir.path in redirections:
                                    raise RuntimeError("InternalRedirector visited the "
                                                       "same URL twice: %r" % ir.path)
                                else:
                                    # Add the *previous* path_info + qs to redirections.
                                    if qs:
                                        qs = "?" + qs
                                    redirections.append(sn + path + qs)
                            
                            # Munge environment and try again.
                            method = "GET"
                            path = ir.path
                            qs = ir.query_string
                            rfile = BytesIO()
                    
                    self.send_response(
                        response.output_status, response.header_list,
                        response.body)
                finally:
                    app.release_serving()
        except:
            tb = format_exc()
            #print tb
            cherrypy.log(tb, 'NATIVE_ADAPTER', severity=logging.ERROR)
            s, h, b = bare_error()
            self.send_response(s, h, b)
    
    def send_response(self, status, headers, body):
        req = self.req
        
        # Set response status
        req.status = str(status or "500 Server Error")
        
        # Set response headers
        for header, value in headers:
            req.outheaders.append((header, value))
        if (req.ready and not req.sent_headers):
            req.sent_headers = True
            req.send_headers()
        
        # Set response body
        for seg in body:
            req.write(seg)


class CPHTTPServer(wsgiserver.HTTPServer):
    """Wrapper for wsgiserver.HTTPServer.
    
    wsgiserver has been designed to not reference CherryPy in any way,
    so that it can be used in other frameworks and applications.
    Therefore, we wrap it here, so we can apply some attributes
    from config -> cherrypy.server -> HTTPServer.
    """
    
    def __init__(self, server_adapter=cherrypy.server):
        self.server_adapter = server_adapter
        
        server_name = (self.server_adapter.socket_host or
                       self.server_adapter.socket_file or
                       None)
        
        wsgiserver.HTTPServer.__init__(
            self, server_adapter.bind_addr, NativeGateway,
            minthreads=server_adapter.thread_pool,
            maxthreads=server_adapter.thread_pool_max,
            server_name=server_name)
        
        self.max_request_header_size = self.server_adapter.max_request_header_size or 0
        self.max_request_body_size = self.server_adapter.max_request_body_size or 0
        self.request_queue_size = self.server_adapter.socket_queue_size
        self.timeout = self.server_adapter.socket_timeout
        self.shutdown_timeout = self.server_adapter.shutdown_timeout
        self.protocol = self.server_adapter.protocol_version
        self.nodelay = self.server_adapter.nodelay
        
        ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
        if self.server_adapter.ssl_context:
            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
            self.ssl_adapter = adapter_class(
                self.server_adapter.ssl_certificate,
                self.server_adapter.ssl_private_key,
                self.server_adapter.ssl_certificate_chain)
            self.ssl_adapter.context = self.server_adapter.ssl_context
        elif self.server_adapter.ssl_certificate:
            adapter_class = wsgiserver.get_ssl_adapter_class(ssl_module)
            self.ssl_adapter = adapter_class(
                self.server_adapter.ssl_certificate,
                self.server_adapter.ssl_private_key,
                self.server_adapter.ssl_certificate_chain)