docs: async def _receive_chunk(self, chunk):
          """
          Handle an incoming chunk.
          """
          self.__log_debug('< %s', chunk)
  
          # common
          if isinstance(chunk, DataChunk):
              await self._receive_data_chunk(chunk)
          elif isinstance(chunk, SackChunk):
              await self._receive_sack_chunk(chunk)
          elif isinstance(chunk, ForwardTsnChunk):
              await self._receive_forward_tsn_chunk(chunk)
          elif isinstance(chunk, HeartbeatChunk):
              ack = HeartbeatAckChunk()
              ack.params = chunk.params
              await self._send_chunk(ack)
          elif isinstance(chunk, AbortChunk):
              self.__log_debug('x Association was aborted by remote party')
              self._set_state(self.State.CLOSED)
          elif isinstance(chunk, ShutdownChunk):
              self._t2_cancel()
              self._set_state(self.State.SHUTDOWN_RECEIVED)
              ack = ShutdownAckChunk()
              await self._send_chunk(ack)
              self._t2_start(ack)
              self._set_state(self.State.SHUTDOWN_ACK_SENT)
          elif (isinstance(chunk, ShutdownCompleteChunk) and
                self._association_state == self.State.SHUTDOWN_ACK_SENT):
              self._t2_cancel()
              self._set_state(self.State.CLOSED)
          elif (isinstance(chunk, ReconfigChunk) and
                self._association_state == self.State.ESTABLISHED):
              for param in chunk.params:
                  cls = RECONFIG_PARAM_TYPES.get(param[0])
                  if cls:
                      await self._receive_reconfig_param(cls.parse(param[1]))
  
          # server
          elif isinstance(chunk, InitChunk) and self.is_server:
              self._last_received_tsn = tsn_minus_one(chunk.initial_tsn)
              self._reconfig_response_seq = tsn_minus_one(chunk.initial_tsn)
              self._remote_verification_tag = chunk.initiate_tag
              self._ssthresh = chunk.advertised_rwnd
              self._get_extensions(chunk.params)
  
              self.__log_debug('- Peer supports %d outbound streams, %d max inbound streams',
                               chunk.outbound_streams, chunk.inbound_streams)
              self._inbound_streams_count = min(chunk.outbound_streams, self._inbound_streams_max)
              self._outbound_streams_count = min(self._outbound_streams_count, chunk.inbound_streams)
  
              ack = InitAckChunk()
              ack.initiate_tag = self._local_verification_tag
              ack.advertised_rwnd = self._advertised_rwnd
              ack.outbound_streams = self._outbound_streams_count
              ack.inbound_streams = self._inbound_streams_max
              ack.initial_tsn = self._local_tsn
              self._set_extensions(ack.params)
  
              # generate state cookie
              cookie = pack('!L', self._get_timestamp())
              cookie += hmac.new(self._hmac_key, cookie, 'sha1').digest()
              ack.params.append((SCTP_STATE_COOKIE, cookie))
              await self._send_chunk(ack)
          elif isinstance(chunk, CookieEchoChunk) and self.is_server:
              # check state cookie MAC
              cookie = chunk.body
              if (len(cookie) != COOKIE_LENGTH or
                 hmac.new(self._hmac_key, cookie[0:4], 'sha1').digest() != cookie[4:]):
                  self.__log_debug('x State cookie is invalid')
                  return
  
              # check state cookie lifetime
              now = self._get_timestamp()
              stamp = unpack_from('!L', cookie)[0]
              if stamp < now - COOKIE_LIFETIME or stamp > now:
                  self.__log_debug('x State cookie has expired')
                  error = ErrorChunk()
                  error.params.append((SCTP_CAUSE_STALE_COOKIE, b'\x00' * 8))
                  await self._send_chunk(error)
                  return
  
              ack = CookieAckChunk()
              await self._send_chunk(ack)
              self._set_state(self.State.ESTABLISHED)
  
          # client
          elif isinstance(chunk, InitAckChunk) and self._association_state == self.State.COOKIE_WAIT:
              # cancel T1 timer and process chunk
              self._t1_cancel()
              self._last_received_tsn = tsn_minus_one(chunk.initial_tsn)
              self._reconfig_response_seq = tsn_minus_one(chunk.initial_tsn)
              self._remote_verification_tag = chunk.initiate_tag
              self._ssthresh = chunk.advertised_rwnd
              self._get_extensions(chunk.params)
  
              self.__log_debug('- Peer supports %d outbound streams, %d max inbound streams',
                               chunk.outbound_streams, chunk.inbound_streams)
              self._inbound_streams_count = min(chunk.outbound_streams, self._inbound_streams_max)
              self._outbound_streams_count = min(self._outbound_streams_count, chunk.inbound_streams)
  
              echo = CookieEchoChunk()
              for k, v in chunk.params:
                  if k == SCTP_STATE_COOKIE:
                      echo.body = v
                      break
              await self._send_chunk(echo)
  
              # start T1 timer and enter COOKIE-ECHOED state
              self._t1_start(echo)
              self._set_state(self.State.COOKIE_ECHOED)
          elif (isinstance(chunk, CookieAckChunk) and
                self._association_state == self.State.COOKIE_ECHOED):
              # cancel T1 timer and enter ESTABLISHED state
              self._t1_cancel()
              self._set_state(self.State.ESTABLISHED)
          elif (isinstance(chunk, ErrorChunk) and
                self._association_state in [self.State.COOKIE_WAIT, self.State.COOKIE_ECHOED]):
              self._t1_cancel()
              self._set_state(self.State.CLOSED)
              self.__log_debug('x Could not establish association')
              return