Repository: httpie/httpie
File: httpie/models.py (Python)

Code before:
        if not isinstance(ct, str):
            ct = ct.decode('utf8')
        return ct


class HTTPResponse(HTTPMessage):
    """A :class:`requests.models.Response` wrapper."""

    def iter_body(self, chunk_size=1):
        return self._orig.iter_content(chunk_size=chunk_size)

    def iter_lines(self, chunk_size):
        return ((line, b'\n') for line in self._orig.iter_lines(chunk_size))

    def _headers_http_client_http_message(self):
        """ Extract header information from http.client.HTTPMessage """
        original = self._orig.raw._original_response

        version = {
            9: '0.9',
            10: '1.0',
            11: '1.1',
            20: '2',
        }[original.version]

        return version, original.status, original.reason, original.msg._headers

    def _headers_requests_response(self):
        """ Extract header information from Requests.models.Response """
        version = "??"
        return version, self._orig.status_code, self._orig.reason, self._orig.headers.items()


    # noinspection PyProtectedMember
    @property
    def headers(self):
        if isinstance(self._orig.raw, urllib3.HTTPResponse):
            header_extract_method = self._headers_http_client_http_message
        else:
            header_extract_method = self._headers_requests_response
        version, status, reason, http_headers = header_extract_method()

        status_line = f'HTTP/{version} {status} {reason}'
        headers = [status_line]
        headers.extend(
            ": ".join(header) for header in http_headers)
        return '\r\n'.join(headers)

    @property
    def encoding(self):
        return self._orig.encoding or 'utf8'

Review comment (bug):
Status, reason and headers ought to be pulled from self._orig as is done in _headers_requests_response instead of accessing the protected member self._orig.raw._original_response. Its not clear why this was not done to begin with and for that reason I'm leaving the old logic in place.

Notable that same approach cannot be used for version. Perhaps it would be better to file a bug against requests and ask for HTTP version to be exposed without having to rely on protected members.

Code after:
        ct = self._orig.headers.get('Content-Type', '')
        if not isinstance(ct, str):
            ct = ct.decode('utf8')
        return ct


class HTTPResponse(HTTPMessage):
    """A :class:`requests.models.Response` wrapper."""

    def iter_body(self, chunk_size=1):
        return self._orig.iter_content(chunk_size=chunk_size)

    def iter_lines(self, chunk_size):
        return ((line, b'\n') for line in self._orig.iter_lines(chunk_size))

    # noinspection PyProtectedMember
    @property
    def headers(self):
        try:
            raw_version = self._orig.raw._original_response.version
        except AttributeError:
            # Assume HTTP/1.1
            raw_version = 11
        version = {
            9: '0.9',
            10: '1.0',
            11: '1.1',
            20: '2',
        }[raw_version]

        original = self._orig
        status_line = f'HTTP/{version} {original.status_code} {original.reason}'
        headers = [status_line]
        headers.extend(
            ': '.join(header)
            for header in original.headers.items()
            if header[0] != 'Set-Cookie'
        )
        headers.extend(
            f'Set-Cookie: {cookie}'
            for cookie in split_cookies(original.headers.get('Set-Cookie'))
        )
        return '\r\n'.join(headers)

    @property
    def encoding(self):
        return self._orig.encoding or 'utf8'

    @property
    def body(self):
        # Only now the response body is fetched.