Skip to content

Commit

Permalink
feat: avoid decoding known answers if we have no answers to give (#1308)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Nov 12, 2023
1 parent ce98cb8 commit 605dc9c
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 74 deletions.
6 changes: 4 additions & 2 deletions src/zeroconf/_core.py
Expand Up @@ -577,15 +577,17 @@ def handle_assembled_query(
) -> None:
"""Respond to a (re)assembled query.
If the protocol recieved packets with the TC bit set, it will
If the protocol received packets with the TC bit set, it will
wait a bit for the rest of the packets and only call
handle_assembled_query once it has a complete set of packets
or the timer expires. If the TC bit is not set, a single
packet will be in packets.
"""
now = packets[0].now
ucast_source = port != _MDNS_PORT
question_answers = self.query_handler.async_response(packets, ucast_source)
if not question_answers:
return
now = packets[0].now
if question_answers.ucast:
questions = packets[0].questions
id_ = packets[0].id
Expand Down
8 changes: 8 additions & 0 deletions src/zeroconf/_handlers/answers.py
Expand Up @@ -59,6 +59,14 @@ def __init__(
self.mcast_aggregate = mcast_aggregate
self.mcast_aggregate_last_second = mcast_aggregate_last_second

def __repr__(self) -> str:
"""Return a string representation of this QuestionAnswers."""
return (
f'QuestionAnswers(ucast={self.ucast}, mcast_now={self.mcast_now}, '
f'mcast_aggregate={self.mcast_aggregate}, '
f'mcast_aggregate_last_second={self.mcast_aggregate_last_second})'
)


class AnswerGroup:
"""A group of answers scheduled to be sent at the same time."""
Expand Down
4 changes: 2 additions & 2 deletions src/zeroconf/_handlers/multicast_outgoing_queue.py
Expand Up @@ -77,7 +77,7 @@ def async_add(self, now: _float, answers: _AnswerWithAdditionalsType) -> None:
# If we calculate a random delay for the send after time
# that is less than the last group scheduled to go out,
# we instead add the answers to the last group as this
# allows aggregating additonal responses
# allows aggregating additional responses
last_group = self.queue[-1]
if send_after <= last_group.send_after:
last_group.answers.update(answers)
Expand Down Expand Up @@ -116,7 +116,7 @@ def async_ready(self) -> None:
# be sure we schedule them to go out later
loop.call_at(loop.time() + millis_to_seconds(self.queue[0].send_after - now), self.async_ready)

if answers:
if answers: # pragma: no branch
# If we have the same answer scheduled to go out, remove them
self._remove_answers_from_queue(answers)
zc.async_send(construct_outgoing_multicast_answers(answers))
33 changes: 28 additions & 5 deletions src/zeroconf/_handlers/query_handler.pxd
Expand Up @@ -18,6 +18,23 @@ cdef cython.set _ADDRESS_RECORD_TYPES
cdef object IPVersion, _IPVersion_ALL
cdef object _TYPE_PTR, _CLASS_IN, _DNS_OTHER_TTL

cdef unsigned int _ANSWER_STRATEGY_SERVICE_TYPE_ENUMERATION
cdef unsigned int _ANSWER_STRATEGY_POINTER
cdef unsigned int _ANSWER_STRATEGY_ADDRESS
cdef unsigned int _ANSWER_STRATEGY_SERVICE
cdef unsigned int _ANSWER_STRATEGY_TEXT

cdef list _EMPTY_SERVICES_LIST
cdef list _EMPTY_TYPES_LIST

cdef class _AnswerStrategy:

cdef public DNSQuestion question
cdef public unsigned int strategy_type
cdef public list types
cdef public list services


cdef class _QueryResponse:

cdef bint _is_probe
Expand Down Expand Up @@ -53,24 +70,30 @@ cdef class QueryHandler:
cdef QuestionHistory question_history

@cython.locals(service=ServiceInfo)
cdef _add_service_type_enumeration_query_answers(self, cython.dict answer_set, DNSRRSet known_answers)
cdef _add_service_type_enumeration_query_answers(self, list types, cython.dict answer_set, DNSRRSet known_answers)

@cython.locals(service=ServiceInfo)
cdef _add_pointer_answers(self, str lower_name, cython.dict answer_set, DNSRRSet known_answers)
cdef _add_pointer_answers(self, list services, cython.dict answer_set, DNSRRSet known_answers)

@cython.locals(service=ServiceInfo, dns_address=DNSAddress)
cdef _add_address_answers(self, str lower_name, cython.dict answer_set, DNSRRSet known_answers, cython.uint type_)
cdef _add_address_answers(self, list services, cython.dict answer_set, DNSRRSet known_answers, cython.uint type_)

@cython.locals(question_lower_name=str, type_=cython.uint, service=ServiceInfo)
cdef cython.dict _answer_question(self, DNSQuestion question, DNSRRSet known_answers)
cdef cython.dict _answer_question(self, DNSQuestion question, unsigned int strategy_type, list types, list services, DNSRRSet known_answers)

@cython.locals(
msg=DNSIncoming,
msgs=list,
strategy=_AnswerStrategy,
question=DNSQuestion,
answer_set=cython.dict,
known_answers=DNSRRSet,
known_answers_set=cython.set,
is_unicast=bint,
is_probe=object,
now=object
now=float
)
cpdef async_response(self, cython.list msgs, cython.bint unicast_source)

@cython.locals(name=str, question_lower_name=str)
cdef _get_answer_strategies(self, DNSQuestion question)

0 comments on commit 605dc9c

Please sign in to comment.