Géry <gery.o...@gmail.com> added the comment:

> http.server.BaseHTTPRequestHandler end_headers() can reference _header_buffer 
> array before it is assigned.

@grumblor I was about to open the same bug after reading the implementation of 
http.server this morning and noticing that the attribute _headers_buffer of 
BaseHTTPRequestHandler is used in 4 methods:

- send_response_only;
- send_header;
- end_headers;
- flush_headers

but its existence is not checked only in end_headers.

> It seems like sending zero headers is not supported

@andrei.avk It is actually supported by the syntax of HTTP/1.1 messages, cf. 
RFC 7230, § 3:

    HTTP-message   = start-line
                          *( header-field CRLF )
                          CRLF
                          [ message-body ]

For instance the method handle_expect_100 does not send any header:

    def handle_expect_100(self):
        self.send_response_only(HTTPStatus.CONTINUE)
        self.end_headers()
        return True

It only writes a start line (which includes \r\n) followed by an empty line 
(\r\n) as a response:

    HTTP/1.1 100 Continue\r\n\r\n

But self.end_headers() does not raise an AttributeError here like one might 
expect from its implementation:

    def end_headers(self):
        if self.request_version != 'HTTP/0.9':
            self._headers_buffer.append(b"\r\n")
            self.flush_headers()

because, contrary to what its name suggests, self._headers_buffer does not only 
include the response headers but also the response start line, which is 
appended to the buffer before by self.send_response_only(HTTPStatus.CONTINUE):

    def send_response_only(self, code, message=None):
        """Send the response header only."""
        if self.request_version != 'HTTP/0.9':
            if message is None:
                if code in self.responses:
                    message = self.responses[code][0]
                else:
                    message = ''
            if not hasattr(self, '_headers_buffer'):
                self._headers_buffer = []
            self._headers_buffer.append(("%s %d %s\r\n" %
                    (self.protocol_version, code, message)).encode(
                        'latin-1', 'strict'))

So I am not sure it is a bug if we consider that send_response_only (which 
appends a start line to the buffer) is a precondition to end_headers (which 
appends an empty line to the buffer and flushes it). But then flush_headers 
should also have this precondition instead of preventing the AttributeError 
like this:

    def flush_headers(self):
        if hasattr(self, '_headers_buffer'):
            self.wfile.write(b"".join(self._headers_buffer))
            self._headers_buffer = []

Let’s ask Andrew Schaaf (@endian) who introduced flush_headers in Python 3.3 
(cf. https://bugs.python.org/issue3709) why he implemented end_headers by 
contract and flush_headers defensively.

----------
nosy: +endian, maggyero

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue43474>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to