# -*- coding: utf-8 -*-
"""
test_invalid_content_lengths.py
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This module contains tests that use invalid content lengths, and validates that
they fail appropriately.
"""
import pytest

import h2.connection
import h2.errors
import h2.events
import h2.exceptions


class TestInvalidContentLengths(object):
    """
    Hyper-h2 raises Protocol Errors when the content-length sent by a remote
    peer is not valid.
    """
    example_request_headers = [
        (':authority', 'example.com'),
        (':path', '/'),
        (':scheme', 'https'),
        (':method', 'POST'),
        ('content-length', '15'),
    ]
    example_response_headers = [
        (':status', '200'),
        ('server', 'fake-serv/0.1.0')
    ]

    def test_too_much_data(self, frame_factory):
        """
        Remote peers sending data in excess of content-length causes Protocol
        Errors.
        """
        c = h2.connection.H2Connection(client_side=False)
        c.initiate_connection()
        c.receive_data(frame_factory.preamble())

        headers = frame_factory.build_headers_frame(
            headers=self.example_request_headers
        )
        first_data = frame_factory.build_data_frame(data=b'\x01'*15)
        c.receive_data(headers.serialize() + first_data.serialize())
        c.clear_outbound_data_buffer()

        second_data = frame_factory.build_data_frame(data=b'\x01')
        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
            c.receive_data(second_data.serialize())

        assert exp.value.expected_length == 15
        assert exp.value.actual_length == 16
        assert str(exp.value) == (
            "InvalidBodyLengthError: Expected 15 bytes, received 16"
        )

        expected_frame = frame_factory.build_goaway_frame(
            last_stream_id=1,
            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
        )
        assert c.data_to_send() == expected_frame.serialize()

    def test_insufficient_data(self, frame_factory):
        """
        Remote peers sending less data than content-length causes Protocol
        Errors.
        """
        c = h2.connection.H2Connection(client_side=False)
        c.initiate_connection()
        c.receive_data(frame_factory.preamble())

        headers = frame_factory.build_headers_frame(
            headers=self.example_request_headers
        )
        first_data = frame_factory.build_data_frame(data=b'\x01'*13)
        c.receive_data(headers.serialize() + first_data.serialize())
        c.clear_outbound_data_buffer()

        second_data = frame_factory.build_data_frame(
            data=b'\x01',
            flags=['END_STREAM'],
        )
        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
            c.receive_data(second_data.serialize())

        assert exp.value.expected_length == 15
        assert exp.value.actual_length == 14
        assert str(exp.value) == (
            "InvalidBodyLengthError: Expected 15 bytes, received 14"
        )

        expected_frame = frame_factory.build_goaway_frame(
            last_stream_id=1,
            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
        )
        assert c.data_to_send() == expected_frame.serialize()

    def test_insufficient_data_empty_frame(self, frame_factory):
        """
        Remote peers sending less data than content-length where the last data
        frame is empty causes Protocol Errors.
        """
        c = h2.connection.H2Connection(client_side=False)
        c.initiate_connection()
        c.receive_data(frame_factory.preamble())

        headers = frame_factory.build_headers_frame(
            headers=self.example_request_headers
        )
        first_data = frame_factory.build_data_frame(data=b'\x01'*14)
        c.receive_data(headers.serialize() + first_data.serialize())
        c.clear_outbound_data_buffer()

        second_data = frame_factory.build_data_frame(
            data=b'',
            flags=['END_STREAM'],
        )
        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
            c.receive_data(second_data.serialize())

        assert exp.value.expected_length == 15
        assert exp.value.actual_length == 14
        assert str(exp.value) == (
            "InvalidBodyLengthError: Expected 15 bytes, received 14"
        )

        expected_frame = frame_factory.build_goaway_frame(
            last_stream_id=1,
            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
        )
        assert c.data_to_send() == expected_frame.serialize()
