<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 async def read_data_frame(self, max_size: int) -> Optional[Frame]: """ Read a single data frame from the connection. Process control frames received before the next data frame. Return ``None`` if a close frame is encountered before any data frame. """

        # 6.2. Receiving Data
        while True:
            frame = await self.read_frame(max_size)

            # 5.5. Control Frames
            if frame.opcode == OP_CLOSE:
                # 7.1.5.  The WebSocket Connection Close Code
                # 7.1.6.  The WebSocket Connection Close Reason
                self.close_code, self.close_reason = parse_close(frame.data)
                # Echo the original data instead of re-serializing it with
                # serialize_close() because that fails when the close frame is
                # empty and parse_close() synthetizes a 1005 close code.
                await self.write_close_frame(frame.data)
                return None

            elif frame.opcode == OP_PING:
                # Answer pings.
                ping_hex = frame.data.hex() or "[empty]"
                logger.debug(
                    "%s - received ping, sending pong: %s", self.side, ping_hex
                )
                await self.pong(frame.data)

            elif frame.opcode == OP_PONG:
                # Acknowledge pings on solicited pongs.
                if frame.data in self.pings:
                    # Acknowledge all pings up to the one matching this pong.
                    ping_id = None
                    ping_ids = []
                    while ping_id != frame.data:
                        ping_id, pong_waiter = self.pings.popitem(last=False)
                        ping_ids.append(ping_id)
                        pong_waiter.set_result(None)
                    pong_hex = binascii.hexlify(frame.data).decode() or "[empty]"
                    logger.debug(
                        "%s - received solicited pong: %s", self.side, pong_hex
                    )
                    ping_ids = ping_ids[:-1]
                    if ping_ids:
                        pings_hex = ", ".join(
                            binascii.hexlify(ping_id).decode() or "[empty]"
                            for ping_id in ping_ids
                        )
                        plural = "s" if len(ping_ids) > 1 else ""
                        logger.debug(
                            "%s - acknowledged previous ping%s: %s",
                            self.side,
                            plural,
                            pings_hex,
                        )
                else:
                    pong_hex = binascii.hexlify(frame.data).decode() or "[empty]"
                    logger.debug(
                        "%s - received unsolicited pong: %s", self.side, pong_hex
                    )

            # 5.6. Data Frames
            else:
                return frame