Response.write + ConflictError = desaster

Bug #740831 reported by Dieter Maurer
6
This bug affects 1 person
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.fix_responsewrite_conflict". The workaround will prevent Zope to retry a request that previously has used "Response.write". The workaround is not complete: it hides the "ConflictError"
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_conflict". Every second call of this view will result in a "ConflictError" together with the use of "Response.write". To reproduce the problem, visit the view, then visit the top page. The top page will get the result of the retried view visit.

Revision history for this message
Dieter Maurer (d.maurer) wrote :
Revision history for this message
Dieter Maurer (d.maurer) wrote :

"dm.zopepatches.fix_responsewrite_conflict" essentially prepends to "ZPublisher.HTTPRequest.HTTPRequest.supports_retry" an

  if getattr(self.response, "_streaming", False):
    # 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.error("prevent request retry for which part of its response has already been written")
    return False

Changed in zope2:
importance: Undecided → Medium
status: New → Confirmed
Revision history for this message
Colin Watson (cjwatson) wrote :

The zope2 project on Launchpad has been archived at the request of the Zope developers (see https://answers.launchpad.net/launchpad/+question/683589 and https://answers.launchpad.net/launchpad/+question/685285). If this bug is still relevant, please refile it at https://github.com/zopefoundation/zope2.

Changed in zope2:
status: Confirmed → Invalid
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.