Opened 12 years ago

Closed 11 years ago

#17474 closed Bug (wontfix)

Problem when the request doesn't have Content-Type.

Reported by: Marcelo Salhab Brogliato <msbrogli@…> Owned by: nobody
Component: HTTP handling Version: 1.4
Severity: Normal Keywords: content-type
Cc: olau@…, Ivan Virabyan, purohit@… Triage Stage: Design decision needed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Aymeric Augustin)

I don't know exactly why, but some ajax requests don't have Content-Type header.
When this happens, when I try to get request.POST, I got the following exception: 'NoneType' object has no attribute 'startswith',
raised by method _load_post_and_files at line 270 of django/http/__init__.py.

The problem is that when the request doesn't have Content-Type, self.META.get('CONTENT_TYPE', '') returns None.
To fix the problem I just did it:

content_type = self.META.get('CONTENT_TYPE', None) or ''
if content_type.startswith('multipart'):

Change History (14)

comment:1 Changed 12 years ago by Aymeric Augustin

Description: modified (diff)

Improved formatting (please use preview).

comment:2 Changed 12 years ago by Aymeric Augustin

Resolution: needsinfo
Status: newclosed

The error you're reporting means that self.META['CONTENT_TYPE'] == None — if it were undefined, self.META.get('CONTENT_TYPE', '') would return '', not None.


request.META['CONTENT_TYPE'] is defined in WSGIRequestHandler.get_environ like this:

        if self.headers.typeheader is None:
            env['CONTENT_TYPE'] = self.headers.type
        else:
            env['CONTENT_TYPE'] = self.headers.typeheader

This code is copy-pasted from wsgiref.simple_server (line 90), and self.headers comes from BaseHTTPRequestHandler.headers.

self.headers.type is computed in Message.parse_type, which sets self.type = '/'.join(fields). This means that self.headers.type can never be None.


So, by inspecting the code, I can't figure out how the bug you're reporting happened. I'd prefer to fix its root cause rather than its symptom. For this reason, I'm not eager to blindly commit your suggestion.

Could you check with your browser's inspector which AJAX requests trigger the bug, and which headers they contain exactly ? If we can reproduce the problem, it will be easier to understand its cause.

Per our standard procedures, I'm going to close this ticket as "needing more information", since I don't have enough data to reproduce the problem. Could you add more information about the problematic requests and reopen it? Thank you!


On a related note, MultiPartParser uses this code:

        content_type = META.get('HTTP_CONTENT_TYPE', META.get('CONTENT_TYPE', ''))
        if not content_type.startswith('multipart/'):
            ...

We may have a small inconsistency here.

comment:3 Changed 12 years ago by Ole Laursen

Cc: olau@… added
Resolution: needsinfo
Status: closedreopened

Hi!

We had the same problem with a Django instance running inside mod_python (yes, I know mod_python is bad, it's a client's server). I think it happens with a custom iPhone client. Here's the traceback:

Traceback:
File "/var/sites/bcl/PYTHON_ENV/lib/python2.5/site-packages/django/core/handlers/base.py" in get_response
  111.                         response = callback(request, *callback_args, **callback_kwargs)
File "/var/sites/bcl/main/api.py" in inner
  17.         token = request.GET.get("authtoken", request.POST.get("authtoken"))
File "/var/sites/bcl/PYTHON_ENV/lib/python2.5/site-packages/django/core/handlers/modpython.py" in _get_post
  101.             self._load_post_and_files()
File "/var/sites/bcl/PYTHON_ENV/lib/python2.5/site-packages/django/http/__init__.py" in _load_post_and_files
  270.         if self.META.get('CONTENT_TYPE', '').startswith('multipart'):

Exception Type: AttributeError at /api/events/log/
Exception Value: 'NoneType' object has no attribute 'startswith'

If you want to test it, you could try telnetting a POST response, something like "telnet localhost 8000", then copy-paste

POST / HTTP/1.0
Content-length: 17

query=1234

comment:4 in reply to:  3 Changed 12 years ago by anonymous

Replying to olau:

Hi!

We had the same problem with a Django instance running inside mod_python

What versions of Django and Python are you using?

comment:5 Changed 12 years ago by Ole Laursen

Django 1.3 and Python 2.5.

comment:6 Changed 12 years ago by Łukasz Rekucki

Triage Stage: UnreviewedDesign decision needed

PEP 333 (WSGI spec):

CONTENT_TYPE
    The contents of any Content-Type fields in the HTTP request. May be empty or absent.

So this is definitively a bug in mod_python. We are supposed to officially support it until 1.4, but I don't think this is worth it.

comment:7 Changed 12 years ago by Ole Laursen

Huh? Is mod_python implementing WSGI? I thought it was using its own protocol.

Why don't you think a one-line bug fix is worth it? Some people may be stuck on mod_python for some years to come.

comment:8 Changed 11 years ago by Ivan Virabyan

Cc: Ivan Virabyan added

comment:9 Changed 11 years ago by jroweboy@…

Just had a similar problem myself, and I suspect it might have the same root problem. I hate to suggest a workaround for things, but for anyone else who has a similar problem, you might be able to directly specify the CONTENT-TYPE in your ajax request. I don't know why it wasn't sending it by default as 'application/x-www-form-urlencoded' as the documentation says it does (http://api.jquery.com/jQuery.ajax/) so I ended up explicitly adding it in the ajax function call. Example:

$.ajax({
     type: 'POST',
     url: 'your url',
     success: function(data){
         // function to call on success
     },
     dataType: 'text',
     contentType: 'application/x-www-form-urlencoded',
});

comment:10 Changed 11 years ago by Ashwin Purohit <purohit@…>

Cc: purohit@… added
Version: 1.31.4

This same thing happens in 1.4:

File "/usr/local/lib/Django1.4/django/core/handlers/modpython.py", line 56, in _get_request
self._request = datastructures.MergeDict(self.POST, self.GET)

File "/usr/local/lib/Django1.4/django/core/handlers/modpython.py", line 69, in _get_post
self._load_post_and_files()

File "/usr/local/lib/Django1.4/django/http/_init_.py", line 353, in _load_post_and_files
if self.META.get('CONTENT_TYPE', '').startswith('multipart'):

AttributeError: 'NoneType' object has no attribute 'startswith'

Request repr() unavailable.

You can easily test it by writing a test that sends a POST without the CONTENT_TYPE header:

def test_missing_content_type(self):                                            
    // Will throw an exception:           
    response = self.client.post('/url/', {}, HTTP_CONTENT_TYPE=None, follow=True)                                 


comment:11 Changed 11 years ago by Claude Paroz

For master, the question is solved by the removal of mod_python. On 1.4, the correct fix would be to change ModPythonRequest._get_meta and replace self._req.headers_in.get('content-type') by self._req.headers_in.get('content-type', ''). I think that this is a safe bug fix, but won't decide until other committers approve it.

comment:12 Changed 11 years ago by Preston Holmes

I agree that this is only a 1.4 issue - however if modpython is in fact passing in a defined content-type of None, then what we actually need is: self._req.headers_in.get('content-type', '') or '' since the key exists in this case.

Last edited 11 years ago by Preston Holmes (previous) (diff)

comment:13 Changed 11 years ago by Aymeric Augustin

Status: reopenednew

comment:14 Changed 11 years ago by Aymeric Augustin

Resolution: wontfix
Status: newclosed

Since 1.4 is now is security-fixes-only mode, this is moot.

Note: See TracTickets for help on using tickets.
Back to Top