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 by Aymeric Augustin, 12 years ago

Description: modified (diff)

Improved formatting (please use preview).

comment:2 by Aymeric Augustin, 12 years ago

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 by Ole Laursen, 12 years ago

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

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

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 by Ole Laursen, 12 years ago

Django 1.3 and Python 2.5.

comment:6 by Łukasz Rekucki, 12 years ago

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 by Ole Laursen, 12 years ago

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 by Ivan Virabyan, 12 years ago

Cc: Ivan Virabyan added

comment:9 by jroweboy@…, 12 years ago

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 by Ashwin Purohit <purohit@…>, 11 years ago

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 by Claude Paroz, 11 years ago

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 by Preston Holmes, 11 years ago

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 by Aymeric Augustin, 11 years ago

Status: reopenednew

comment:14 by Aymeric Augustin, 11 years ago

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