This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author maggyero
Recipients andrei.avk, endian, grumblor, maggyero
Date 2022-01-20.10:36:25
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1642674985.77.0.976942137333.issue43474@roundup.psfhosted.org>
In-reply-to
Content
> 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.
History
Date User Action Args
2022-01-20 10:36:25maggyerosetrecipients: + maggyero, endian, andrei.avk, grumblor
2022-01-20 10:36:25maggyerosetmessageid: <1642674985.77.0.976942137333.issue43474@roundup.psfhosted.org>
2022-01-20 10:36:25maggyerolinkissue43474 messages
2022-01-20 10:36:25maggyerocreate