Response.write + ConflictError = desaster
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Zope 2 |
Invalid
|
Medium
|
Unassigned |
Bug Description
"OFS.Image.File" uses "Response.write" to deliver the file content (to avoid an additional in memory copy of the file content).
"Response.write" has a vital precondition: no error is allowed after the first call to "Response.write" as error conditions are reflected in the response header, sent by the first "Response.write". This vital precondition is violated for requests which suffer from a "ConflictError" during the transaction commit. In such a case, the response is already delivered to "ZServer" when the "ConflictError" is recognized. "ZPublisher" will retry the request and we get two responses for a single request. The additional response piles up in the communication channel and later requests will get wrong responses (until the channel is closed) -- a desaster...
Many Plone users have been affected by this problem: see "plone-users" "Does Zope/Plone deliver wrong content" in the first quarter of 2011.
I will publish a partial workaround on PyPI: "dm.zopepatches
and the resulting transaction rollback from the user; thus giving him the wrong impression everything had worked correctly. The only complete fix is to get rid of "Response.write" (or "ConflictError") altogether.
The attached tgz archive contains a product to reproduce the problem. It registers a view "responsewrite_
Changed in zope2: | |
importance: | Undecided → Medium |
status: | New → Confirmed |
"dm.zopepatches .fix_responsewr ite_conflict" essentially prepends to "ZPublisher. HTTPRequest. HTTPRequest. supports_ retry" an
if getattr( self.response, "_streaming", False): error(" prevent request retry for which part of its response has already been written")
# we try to retry a request whose response has already (at least partially)
# been written -- this leads to desaster (wrong responses to subsequents
# requests). Prevent it.
logger.
return False