Description

Viewing and downloading attachments (e.g. images) produces an error page in the browser and an error message in the Apache error log:

FastCGI: comm with server "/var/wikis/fcg/moin.fcg" aborted: error parsing headers: duplicate header 'Content-Type', referer: https://www.example.com/SomeWikiPage?action=AttachFile&do=view&target=test.png

(/var/wikis/fcg/moin.fcg is a copy of the standard moin.cgi with correct sys.path.insert(...) statements.)

Steps to reproduce

Attach a file (e.g. an image) to some page and try to view or download it afterwards.

Example

When I try to download an attachment, the server delivers the following error message to the browser:

OK

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator, none and inform them of the time the error occurred, and anything you might have done that may have caused the error.

More information about this error may be available in the server error log.

Component selection

Details

MoinMoin Version

1.9.1

OS and Version

FreeBSD 8.0-RELEASE-p2

Python Version

2.6.4

Server Setup

Apache + mod_fastcgi over HTTPS

Server Details

apache-worker-2.2.14_5, mod_fastcgi-2.4.6

Language you are using the wiki in (set in the browser/UserPreferences)

de

Workaround

After changing my setup to use WSGI instead of FastCGI, everything works as expected.

Discussion

I've done some researching and I think the problem concerns the class Headers (defined in MoinMoin/support/werkzeug/datastructures.py), the constructor of the class Request (defined in MoinMoin/web/request.py) and the methods _do_get and _do_box (defined in MoinMoin/action/AttachFile.py). (I have almost no Python experience though, so I could be wrong...)

Headers stores response headers in a list (which seems quite strange to me, why no dict?). Request has the following line in its constructor: self.headers = Headers([('Content-Type', 'text/html')]), i.e. every response has a default Content-Type header. _do_get and _do_box both add their own Content-Type headers (request.headers.add('Content-Type', content_type)), and as headers are stored in a list there are two Content-Type headers in the end. (This would not happen if Headers used a dict...)

Finally (unlike others?) mod_fastcgi seems to be very strikt about duplicate Content-Type (and other) headers. http://www.fastcgi.com/mod_fastcgi/mod_fastcgi.c:

            ...
            if (strcasecmp(name, "Content-type") == 0) {
                if (hasContentType) {
                    goto DuplicateNotAllowed;
                }
                hasContentType = TRUE;
                ...
            }
            ...

(!) Use headers[key] = value (== werkzeug's Headers.set('Content-Type', '...')) - this will make sure that there is just the last key/value set present in the Headers. We could use this everywhere instead of headers.add for the cases where we only want one header of that type (for headers that may be repeated, we need to use .add of course).

(./) reviewed all headers stuff in moin and fix it.

Plan


CategoryMoinMoinBugFixed

MoinMoin: MoinMoinBugs/191AttachmentsProduceDuplicateHeaderErrorWithFastCGI (last edited 2010-02-27 12:23:57 by EikoBuettner)