#29343: static file serving fails with "Connection reset by peer" on HEAD 
requests
---------------------------------+------------------------------------
     Reporter:  Yehor Smoliakov  |                    Owner:  nobody
         Type:  Bug              |                   Status:  new
    Component:  HTTP handling    |                  Version:  2.0
     Severity:  Normal           |               Resolution:
     Keywords:                   |             Triage Stage:  Accepted
    Has patch:  0                |      Needs documentation:  0
  Needs tests:  0                |  Patch needs improvement:  0
Easy pickings:  0                |                    UI/UX:  0
---------------------------------+------------------------------------

Comment (by Jannik Schürg):

 I could reproduce it now a few times but not consistently,  it is a timing
 issue maybe. Or my version of curl is more hesitant with TCP RST (leading
 to ConnectionResetError).

 Here is what happens, I think:

 1. Curl closes the connection by resetting it instead of closing it (RST
 vs. FIN).
 2. An exception is thrown in wsgiref/socket.
 3. `wsgiref.handlers.BaseHandler.close()` is called, which reset the state
 of the handler (in particular `self.environ` is set to `None`).
 4. The error handler `handle_error()` is called. Here Django has
 overwritten the method and checks if `is_broken_pipe_error()` in which
 case nothing would happen.
 5. But curl did reset instead of close, so we have not "broken pipe," but
 "connection reset". The default handler is called, which tries to use
 `self.environ`, exception.

 A proper fix probably would be to modify
 `core.servers.basehttp.ServerHandler` by adding the method

 {{{#!python
 def finish_response(self):
     try:
         if self.environ['REQUEST_METHOD'] == 'HEAD':
             self.finish_content()
         else:
             if not self.result_is_file() or not self.sendfile():
                 for data in self.result:
                     self.write(data)
                 self.finish_content()
     finally:
         self.close()
 }}}

 With this the server does no longer try to send the body for a HEAD
 request (as it should be).
 I am sure there are cases where this does produce an incorrect HEAD
 response according to the HTTP standard, but at least no body is sent
 anymore.

 Alternatively/additionally one might want to change the check with
 `is_broken_pipe()`. For example one could also check for connection reset,
 or if `self.environ` is not `None`, or if the connection was closed
 (overwrite close() method).

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29343#comment:7>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/066.090ee007e8243b40a8a740775d9aca7f%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to