Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Lu Xu
erp5
Commits
f33ea934
Commit
f33ea934
authored
Apr 17, 2019
by
Bryton Lacquement
🚪
Committed by
Julien Muchembled
Jun 24, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
patches: make the WSGIPublisher backport work with Zope 2.13
Parts of ZPublisher.utils are also backported.
parent
81c8663f
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
158 additions
and
42 deletions
+158
-42
product/ERP5Type/ZopePatch.py
product/ERP5Type/ZopePatch.py
+1
-0
product/ERP5Type/patches/WSGIPublisher.py
product/ERP5Type/patches/WSGIPublisher.py
+157
-42
No files found.
product/ERP5Type/ZopePatch.py
View file @
f33ea934
...
...
@@ -21,6 +21,7 @@
##############################################################################
# Load all monkey patches
from
Products.ERP5Type.patches
import
WSGIPublisher
from
Products.ERP5Type.patches
import
HTTPRequest
from
Products.ERP5Type.patches
import
AccessControl_patch
from
Products.ERP5Type.patches
import
Restricted
...
...
product/ERP5Type/patches/WSGIPublisher.py
View file @
f33ea934
# Backport from Zope4
# Backport
(with modified code)
from Zope4
##############################################################################
#
...
...
@@ -19,16 +19,22 @@ from contextlib import closing
from
contextlib
import
contextmanager
from
io
import
BytesIO
from
io
import
IOBase
import
logging
from
six
import
binary_type
from
six
import
PY3
from
six
import
reraise
from
six
import
text_type
from
six.moves._thread
import
allocate_lock
import
transaction
from
AccessControl.SecurityManagement
import
newSecurityManager
from
AccessControl.SecurityManagement
import
noSecurityManager
from
Acquisition
import
aq_acquire
from
Acquisition
import
aq_inner
from
Acquisition
import
aq_parent
from
transaction.interfaces
import
TransientError
from
zExceptions
import
Redirect
from
zExceptions
import
Unauthorized
from
zExceptions
import
upgradeException
from
zope.component
import
queryMultiAdapter
...
...
@@ -38,12 +44,12 @@ from zope.globalrequest import setRequest
from
zope.publisher.skinnable
import
setDefaultSkin
from
zope.security.management
import
endInteraction
from
zope.security.management
import
newInteraction
from
Z
Publisher
import
pubevents
from
ZPublisher
.HTTPRequest
import
WSGIRequest
from
ZPublisher.HTTPRe
sponse
import
WSGIResponse
from
Z
ope2.App.startup
import
validated_hook
from
ZPublisher
import
pubevents
,
Retry
from
ZPublisher.HTTPRe
quest
import
HTTPRequest
from
ZPublisher.Iterators
import
IUnboundStreamIterator
from
ZPublisher.mapply
import
mapply
from
ZPublisher.
utils
import
recordMetaData
from
ZPublisher.
WSGIPublisher
import
call_object
,
missing_name
,
WSGIResponse
if
sys
.
version_info
>=
(
3
,
):
...
...
@@ -57,32 +63,113 @@ _MODULE_LOCK = allocate_lock()
_MODULES
=
{}
def
call_object
(
obj
,
args
,
request
):
return
obj
(
*
args
)
AC_LOGGER
=
logging
.
getLogger
(
'event.AccessControl'
)
if
1
:
# upstream moved WSGIResponse to HTTPResponse.py
def
dont_publish_class
(
klass
,
request
):
request
.
response
.
forbiddenError
(
"class %s"
%
klass
.
__name__
)
# According to PEP 333, WSGI applications and middleware are forbidden from
# using HTTP/1.1 "hop-by-hop" features or headers. This patch prevents Zope
# from sending 'Connection' and 'Transfer-Encoding' headers.
def
finalize
(
self
):
headers
=
self
.
headers
body
=
self
.
body
# <patch>
# There's a bug in 'App.ImageFile.index_html': when it returns a 304 status
# code, 'Content-Length' is equal to a nonzero value.
if
self
.
status
==
304
:
headers
.
pop
(
'content-length'
,
None
)
# Force the removal of "hop-by-hop" headers
headers
.
pop
(
'Connection'
,
None
)
# </patch>
# set 204 (no content) status if 200 and response is empty
# and not streaming
if
(
'content-type'
not
in
headers
and
'content-length'
not
in
headers
and
not
self
.
_streaming
and
self
.
status
==
200
):
self
.
setStatus
(
'nocontent'
)
# add content length if not streaming
content_length
=
headers
.
get
(
'content-length'
)
if
content_length
is
None
and
not
self
.
_streaming
:
self
.
setHeader
(
'content-length'
,
len
(
body
))
# <patch>
# backport from Zope 4.0b1
# (see commit be5b14bd858da787c41a39e2533b0aabcd246fd5)
# </patch>
return
'%s %s'
%
(
self
.
status
,
self
.
errmsg
),
self
.
listHeaders
()
WSGIResponse
.
finalize
=
finalize
def
missing_name
(
name
,
request
):
if
name
==
'self'
:
return
request
[
'PARENTS'
][
0
]
request
.
response
.
badRequestError
(
name
)
# From ZPublisher.utils
def
recordMetaData
(
object
,
request
):
if
hasattr
(
object
,
'getPhysicalPath'
):
path
=
'/'
.
join
(
object
.
getPhysicalPath
())
else
:
# Try hard to get the physical path of the object,
# but there are many circumstances where that's not possible.
to_append
=
()
def
validate_user
(
request
,
user
):
newSecurityManager
(
request
,
user
)
if
hasattr
(
object
,
'__self__'
)
and
hasattr
(
object
,
'__name__'
):
# object is a Python method.
to_append
=
(
object
.
__name__
,)
object
=
object
.
__self__
while
object
is
not
None
and
not
hasattr
(
object
,
'getPhysicalPath'
):
if
getattr
(
object
,
'__name__'
,
None
)
is
None
:
object
=
None
break
to_append
=
(
object
.
__name__
,)
+
to_append
object
=
aq_parent
(
aq_inner
(
object
))
if
object
is
not
None
:
path
=
'/'
.
join
(
object
.
getPhysicalPath
()
+
to_append
)
else
:
# As Jim would say, "Waaaaaaaa!"
# This may cause problems with virtual hosts
# since the physical path is different from the path
# used to retrieve the object.
path
=
request
.
get
(
'PATH_INFO'
)
T
=
transaction
.
get
()
T
.
note
(
safe_unicode
(
path
))
auth_user
=
request
.
get
(
'AUTHENTICATED_USER'
,
None
)
if
auth_user
:
auth_folder
=
aq_parent
(
auth_user
)
if
auth_folder
is
None
:
AC_LOGGER
.
warning
(
'A user object of type %s has no aq_parent.'
,
type
(
auth_user
))
auth_path
=
request
.
get
(
'AUTHENTICATION_PATH'
)
else
:
auth_path
=
'/'
.
join
(
auth_folder
.
getPhysicalPath
()[
1
:
-
1
])
user_id
=
auth_user
.
getId
()
user_id
=
safe_unicode
(
user_id
)
if
user_id
else
u'None'
T
.
setUser
(
user_id
,
safe_unicode
(
auth_path
))
def
set_default_debug_mode
(
debug_mode
):
global
_DEFAULT_DEBUG_MODE
_DEFAULT_DEBUG_MODE
=
debug_mode
def
safe_unicode
(
value
):
if
isinstance
(
value
,
text_type
):
return
value
elif
isinstance
(
value
,
binary_type
):
try
:
value
=
text_type
(
value
,
'utf-8'
)
except
UnicodeDecodeError
:
value
=
value
.
decode
(
'utf-8'
,
'replace'
)
return
value
def
set_default_authentication_realm
(
realm
):
global
_DEFAULT_REALM
_DEFAULT_REALM
=
realm
def
dont_publish_class
(
klass
,
request
):
request
.
response
.
forbiddenError
(
"class %s"
%
klass
.
__name__
)
def
get_module_info
(
module_name
=
'Zope2'
):
...
...
@@ -95,7 +182,8 @@ def get_module_info(module_name='Zope2'):
module
=
__import__
(
module_name
)
app
=
getattr
(
module
,
'bobo_application'
,
module
)
realm
=
_DEFAULT_REALM
if
_DEFAULT_REALM
is
not
None
else
module_name
_MODULES
[
module_name
]
=
info
=
(
app
,
realm
,
_DEFAULT_DEBUG_MODE
)
error_hook
=
getattr
(
module
,
'zpublisher_exception_hook'
,
None
)
_MODULES
[
module_name
]
=
info
=
(
app
,
realm
,
_DEFAULT_DEBUG_MODE
,
error_hook
)
return
info
...
...
@@ -135,7 +223,7 @@ def _exc_view_created_response(exc, request, response):
@
contextmanager
def
transaction_pubevents
(
request
,
response
,
tm
=
transaction
.
manager
):
def
transaction_pubevents
(
request
,
response
,
err_hook
,
tm
=
transaction
.
manager
):
try
:
setDefaultSkin
(
request
)
newInteraction
()
...
...
@@ -166,21 +254,44 @@ def transaction_pubevents(request, response, tm=transaction.manager):
if
request
.
environ
.
get
(
'x-wsgiorg.throw_errors'
,
False
):
reraise
(
*
exc_info
)
# Handle exception view
exc_view_created
=
_exc_view_created_response
(
exc
,
request
,
response
)
if
isinstance
(
exc
,
Unauthorized
):
# _unauthorized modifies the response in-place. If this hook
# is used, an exception view for Unauthorized has to merge
# the state of the response and the exception instance.
exc
.
setRealm
(
response
.
realm
)
response
.
_unauthorized
()
response
.
setStatus
(
exc
.
getStatus
())
retry
=
False
if
isinstance
(
exc
,
TransientError
)
and
request
.
supports_retry
():
retry
=
True
if
err_hook
:
parents
=
request
[
'PARENTS'
]
if
parents
:
parents
=
parents
[
0
]
retry
=
False
try
:
try
:
r
=
err_hook
(
parents
,
request
,
*
exc_info
)
assert
r
is
response
exc_view_created
=
True
except
Retry
:
if
request
.
supports_retry
():
retry
=
True
else
:
r
=
err_hook
(
parents
,
request
,
*
sys
.
exc_info
())
assert
r
is
response
exc_view_created
=
True
except
(
Redirect
,
Unauthorized
):
response
.
exception
()
exc_view_created
=
True
except
BaseException
as
e
:
if
e
is
not
exc
:
raise
exc_view_created
=
False
else
:
# Handle exception view
exc_view_created
=
_exc_view_created_response
(
exc
,
request
,
response
)
if
isinstance
(
exc
,
Unauthorized
):
# _unauthorized modifies the response in-place. If this hook
# is used, an exception view for Unauthorized has to merge
# the state of the response and the exception instance.
exc
.
setRealm
(
response
.
realm
)
response
.
_unauthorized
()
response
.
setStatus
(
exc
.
getStatus
())
retry
=
isinstance
(
exc
,
TransientError
)
and
request
.
supports_retry
()
notify
(
pubevents
.
PubBeforeAbort
(
request
,
exc_info
,
retry
))
tm
.
abort
()
...
...
@@ -217,7 +328,7 @@ def publish(request, module_info):
path
=
request
.
get
(
'PATH_INFO'
)
request
[
'PARENTS'
]
=
[
obj
]
obj
=
request
.
traverse
(
path
,
validated_hook
=
validate
_user
)
obj
=
request
.
traverse
(
path
,
validated_hook
=
validate
d_hook
)
notify
(
pubevents
.
PubAfterTraversal
(
request
))
recordMetaData
(
obj
,
request
)
...
...
@@ -245,7 +356,7 @@ def load_app(module_info):
try
:
yield
(
app
,
realm
,
debug_mode
)
finally
:
if
transaction
.
manager
.
manager
.
_txn
is
not
None
:
if
transaction
.
manager
.
_txn
is
not
None
:
# Only abort a transaction, if one exists. Otherwise the
# abort creates a new transaction just to abort it.
transaction
.
abort
()
...
...
@@ -257,9 +368,10 @@ def publish_module(environ, start_response,
_response
=
None
,
_response_factory
=
WSGIResponse
,
_request
=
None
,
_request_factory
=
WSGI
Request
,
_request_factory
=
HTTP
Request
,
_module_name
=
'Zope2'
):
module_info
=
get_module_info
(
_module_name
)
module_info
,
err_hook
=
module_info
[:
3
],
module_info
[
3
]
result
=
()
path_info
=
environ
.
get
(
'PATH_INFO'
)
...
...
@@ -294,7 +406,7 @@ def publish_module(environ, start_response,
setRequest
(
request
)
try
:
with
load_app
(
module_info
)
as
new_mod_info
:
with
transaction_pubevents
(
request
,
response
):
with
transaction_pubevents
(
request
,
response
,
err_hook
):
response
=
_publish
(
request
,
new_mod_info
)
break
except
TransientError
:
...
...
@@ -324,3 +436,6 @@ def publish_module(environ, start_response,
# Return the result body iterable.
return
result
sys
.
modules
[
'ZPublisher.WSGIPublisher'
]
=
sys
.
modules
[
__name__
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment