]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/cherrypy_backports.py
1 # -*- coding: utf-8 -*-
3 Copyright © 2004-2019, CherryPy Team (team@cherrypy.org)
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 * Neither the name of CherryPy nor the names of its
20 contributors may be used to endorse or promote products derived from
21 this software without specific prior written permission.
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 from pkg_resources
import parse_version
37 # The SSL code in CherryPy 3.5.0 is buggy. It was fixed long ago,
38 # but 3.5.0 is still shipping in major linux distributions
39 # (Fedora 27, Ubuntu Xenial), so we must monkey patch it to get SSL working.
42 def patch_http_connection_init(v
):
43 # It was fixed in 3.7.0. Exact lower bound version is probably earlier,
44 # but 3.5.0 is what this monkey patch is tested on.
45 if parse_version("3.5.0") <= v
< parse_version("3.7.0"):
46 from cherrypy
.wsgiserver
.wsgiserver2
import CP_fileobject
, HTTPConnection
48 def fixed_init(hc_self
, server
, sock
, makefile
=CP_fileobject
):
49 hc_self
.server
= server
51 hc_self
.rfile
= makefile(sock
, "rb", hc_self
.rbufsize
)
52 hc_self
.wfile
= makefile(sock
, "wb", hc_self
.wbufsize
)
53 hc_self
.requests_seen
= 0
55 HTTPConnection
.__init
__ = fixed_init
58 # When the CherryPy server in 3.2.2 (and later) starts it attempts to verify
59 # that the ports its listening on are in fact bound. When using the any address
60 # "::" it tries both ipv4 and ipv6, and in some environments (e.g. kubernetes)
61 # ipv6 isn't yet configured / supported and CherryPy throws an uncaught
63 def skip_wait_for_occupied_port(v
):
64 # the issue was fixed in 3.2.3. it's present in 3.2.2 (current version on
65 # centos:7) and back to at least 3.0.0.
66 if parse_version("3.1.2") <= v
< parse_version("3.2.3"):
67 # https://github.com/cherrypy/cherrypy/issues/1100
68 from cherrypy
.process
import servers
69 servers
.wait_for_occupied_port
= lambda host
, port
: None
72 # cherrypy.wsgiserver was extracted wsgiserver into cheroot in cherrypy v9.0.0
73 def patch_builtin_ssl_wrap(v
, new_wrap
):
74 if v
< parse_version("9.0.0"):
75 from cherrypy
.wsgiserver
.ssl_builtin
import BuiltinSSLAdapter
as builtin_ssl
77 from cheroot
.ssl
.builtin
import BuiltinSSLAdapter
as builtin_ssl
# type: ignore
78 builtin_ssl
.wrap
= new_wrap(builtin_ssl
.wrap
)
81 def accept_exceptions_from_builtin_ssl(v
):
82 # the fix was included by cheroot v5.2.0, which was included by cherrypy
84 if v
< parse_version("10.2.0"):
85 # see https://github.com/cherrypy/cheroot/pull/4
88 def accept_ssl_errors(func
):
89 def wrapper(self
, sock
):
91 return func(self
, sock
)
92 except ssl
.SSLError
as e
:
93 if e
.errno
== ssl
.SSL_ERROR_SSL
:
94 # Check if it's one of the known errors
95 # Errors that are caught by PyOpenSSL, but thrown by
97 _block_errors
= ('unknown protocol', 'unknown ca', 'unknown_ca',
99 'https proxy request', 'inappropriate fallback',
100 'wrong version number',
101 'no shared cipher', 'certificate unknown',
102 'ccs received early',
103 'certificate verify failed', # client cert w/o trusted CA
104 'version too low', # caused by SSL3 connections
105 'unsupported protocol', # caused by TLS1 connections
106 'sslv3 alert bad certificate')
107 for error_text
in _block_errors
:
108 if error_text
in e
.args
[1].lower():
109 # Accepted error, let's pass
113 patch_builtin_ssl_wrap(v
, accept_ssl_errors
)
116 def accept_socket_error_0(v
):
117 # see https://github.com/cherrypy/cherrypy/issues/1618
120 cheroot_version
= parse_version(cheroot
.__version
__)
124 if v
< parse_version("9.0.0") or cheroot_version
< parse_version("6.5.5"):
125 generic_socket_error
= OSError
127 def accept_socket_error_0(func
):
128 def wrapper(self
, sock
):
130 return func(self
, sock
)
131 except generic_socket_error
as e
:
132 """It is unclear why exactly this happens.
134 It's reproducible only with openssl>1.0 and stdlib ``ssl`` wrapper.
135 In CherryPy it's triggered by Checker plugin, which connects
136 to the app listening to the socket port in TLS mode via plain
137 HTTP during startup (from the same process).
139 Ref: https://github.com/cherrypy/cherrypy/issues/1618
142 is_error0
= e
.args
== (0, 'Error')
143 IS_ABOVE_OPENSSL10
= ssl
.OPENSSL_VERSION_INFO
>= (1, 1)
145 if is_error0
and IS_ABOVE_OPENSSL10
:
149 patch_builtin_ssl_wrap(v
, accept_socket_error_0
)
152 def patch_request_unique_id(v
):
154 Older versions of cherrypy don't include request.unique_id field (a lazily
157 Monkey-patching is preferred over alternatives as inheritance, as it'd break
158 type checks (cherrypy/lib/cgtools.py: `isinstance(obj, _cprequest.Request)`)
160 if v
< parse_version('11.1.0'):
162 from functools
import update_wrapper
164 from cherrypy
._cprequest
import Request
166 class LazyUUID4(object):
168 """Return UUID4 and keep it for future calls."""
169 return str(self
.uuid4
)
173 """Provide unique id on per-request basis using UUID4.
174 It's evaluated lazily on render.
177 self
._uuid
4 # type: ignore
178 except AttributeError:
179 # evaluate on first access
180 self
._uuid
4 = uuid
.uuid4()
184 old_init
= Request
.__init
__
186 def init_with_unique_id(self
, *args
, **kwargs
):
187 old_init(self
, *args
, **kwargs
)
188 self
.unique_id
= LazyUUID4()
190 Request
.__init
__ = update_wrapper(init_with_unique_id
, old_init
)
193 def patch_cherrypy(v
):
194 ver
= parse_version(v
)
195 patch_http_connection_init(ver
)
196 skip_wait_for_occupied_port(ver
)
197 accept_exceptions_from_builtin_ssl(ver
)
198 accept_socket_error_0(ver
)
199 patch_request_unique_id(ver
)