3

Whenever a lengthy HTTP requests is aborted by the client (e.g. Browser is closed) Django views seem to raise a IOError exception.

What's the proper way to detect such an aborted request if I just want to ignore them? Just catching IOError seems too wide.. might accidentally ignore other IO problems.

Assaf Lavie
  • 73,079
  • 34
  • 148
  • 203

4 Answers4

2

In django 1.3 and up, you can use a logging filter class to suppress the exceptions which you aren't interested in. Here's the logging filter class I'm using to narrowly suppress IOError exceptions raised from _get_raw_post_data():

import sys, traceback
class _SuppressUnreadablePost(object):
    def filter(self, record):
        _, exception, tb = sys.exc_info()
        if isinstance(exception, IOError):
            for _, _, function, _ in traceback.extract_tb(tb):
                if function == '_get_raw_post_data':
                    return False
        return True

In Django 1.4, you will be able to do away with most of the complexity and suppress the new exception class UnreadablePostError. (See this patch).

dlowe
  • 726
  • 4
  • 9
  • Do I need to call this function inside my main function, if yes what parameter are required to be passed in this function? – Harshit verma Jul 24 '20 at 05:57
1

Raven now connects itself to the got_request_exception() signal to catch unhandled exceptions, bypassing the logging system entirely, so the solution proposed by dlowe does not work anymore.

However raven looks for a skip_sentry attribute on the exception instance, so you can use a middleware to set it on the errors you want to ignore:

import sys
import traceback


class FilterPostErrorsMiddleware(object):
    """
    A middleware that prevents unreadable POST errors to reach Sentry.
    """

    def process_exception(self, request, exception):
        if isinstance(exception, IOError):
            tb = sys.exc_info()[2]
            for _, _, function, _ in traceback.extract_tb(tb):
                if function == '_get_raw_post_data':
                    exception.skip_sentry = True
                    break

Note: you have to use a recent version of raven (e.g. 1.8.4), as previous versions mistakenly checked for the skip_sentry attribute on the exception type rather than instance.

Luper Rouch
  • 9,304
  • 7
  • 42
  • 56
1

The best way to do it would be to use a custom middleware class that implements process_exception() to return a custom HTTP response, say a rendered errors/request_aborted.html template, if an IOException is caught.

Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
  • But what if some other IOError is thrown, not as a result of aborting but for some other reason? I would then suppress that error... – Assaf Lavie Aug 14 '11 at 14:33
  • This works for uncaught exceptions. You'll generally try to catch all the possible exceptions when your doing IO to make sure that only aborted requests are uncaught. – Filip Dupanović Aug 14 '11 at 19:00
  • But it's not that easy to tell where an IOError can come from. E.g. I see it often being raised just by accessing request.POST. I need a way of telling apart client disconnection from every other type of IO error, while assuming that an IOError can be raised by almost any framework function. – Assaf Lavie Aug 15 '11 at 04:27
  • If you really want to be precise about it, your middleware can catch the exception and evaluate the exception message. – Filip Dupanović Aug 15 '11 at 13:01
  • The exception message doesn't seem like it contains anything that would identify exception as a connection-terminated exception. I also think it's brittle, because the message itself may change slightly between machines (e.g. depending on the directories where django/python files are located). – Assaf Lavie Aug 25 '11 at 05:53
  • Note that middleware process_exception() is also limited to processing exceptions that originated in view code; if the exception originates in another middleware, process_exception() is not called. – dlowe Feb 11 '12 at 17:11
0

If you want to ignore the IOError, then just let it be. You don't need to catch it. If you absolutely must catch it, you can do what @Filip Dupanović suggested, and maybe return a django.http.HttpResponseServerError to set the response code to 500.

Roshan Mathews
  • 5,788
  • 2
  • 26
  • 36