Skip to content

Commit 96fb1aa

Browse files
authored
Merge pull request #296 from ikalchev/v3.1.0
V3.1.0
2 parents d3c576c + cd31df8 commit 96fb1aa

File tree

4 files changed

+40
-15
lines changed

4 files changed

+40
-15
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ Sections
1616
### Developers
1717
-->
1818

19+
## [3.1.0] - 2020-12-13
20+
21+
### Fixed
22+
- Ensure an error response is generated on exception. [#292](https://github.com/ikalchev/HAP-python/pull/292)
23+
- Improve error reporting during pairing. [#289](https://github.com/ikalchev/HAP-python/pull/289)
24+
- Handle request for an empty read instead of throwing an exception. [#288](https://github.com/ikalchev/HAP-python/pull/288)
25+
- Fix thread safety in get characteristics. [#287](https://github.com/ikalchev/HAP-python/pull/287)
26+
1927
## [3.0.0] - 2020-07-25
2028

2129
### Added

pyhap/accessory_driver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ def get_characteristics(self, char_ids):
670670
available = True
671671
else:
672672
acc = self.accessory.accessories.get(aid)
673+
if acc is None:
674+
continue
673675
available = acc.available
674676
char = acc.iid_manager.get_obj(iid)
675677

pyhap/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""This module contains constants used by other modules."""
22
MAJOR_VERSION = 3
3-
MINOR_VERSION = 0
3+
MINOR_VERSION = 1
44
PATCH_VERSION = 0
55
__short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION)
66
__version__ = '{}.{}'.format(__short_version__, PATCH_VERSION)

pyhap/hap_server.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class HAP_SERVER_STATUS:
5959
OPERATION_TIMED_OUT = -70408
6060
RESOURCE_DOES_NOT_EXIST = -70409
6161
INVALID_VALUE_IN_REQUEST = -70410
62+
INSUFFICIENT_AUTHORIZATION = -70411
6263

6364

6465
# Error codes and the like, guessed by packet inspection
@@ -91,6 +92,10 @@ def hap_hkdf(key, salt, info):
9192
return hkdf.derive(key)
9293

9394

95+
class TimeoutException(Exception):
96+
pass
97+
98+
9499
class UnprivilegedRequestException(Exception):
95100
pass
96101

@@ -269,14 +274,20 @@ def dispatch(self):
269274
try:
270275
getattr(self, self.HANDLERS[self.command][path])()
271276
except NotAllowedInStateException:
272-
self.send_response(403)
273-
self.end_response(b'')
277+
self.send_response_with_status(403, HAP_SERVER_STATUS.INSUFFICIENT_AUTHORIZATION)
274278
except UnprivilegedRequestException:
275-
response = {"status": HAP_SERVER_STATUS.INSUFFICIENT_PRIVILEGES}
276-
data = json.dumps(response).encode("utf-8")
277-
self.send_response(401)
278-
self.send_header("Content-Type", self.JSON_RESPONSE_TYPE)
279-
self.end_response(data)
279+
self.send_response_with_status(401, HAP_SERVER_STATUS.INSUFFICIENT_PRIVILEGES)
280+
except TimeoutException:
281+
self.send_response_with_status(500, HAP_SERVER_STATUS.OPERATION_TIMED_OUT)
282+
except Exception: # pylint: disable=broad-except
283+
logger.exception("Failed to process request for: %s", path)
284+
self.send_response_with_status(500, HAP_SERVER_STATUS.SERVICE_COMMUNICATION_FAILURE)
285+
286+
def send_response_with_status(self, http_code, hap_server_status):
287+
"""Send a generic HAP status response."""
288+
self.send_response(http_code)
289+
self.send_header("Content-Type", self.JSON_RESPONSE_TYPE)
290+
self.end_response(json.dumps({"status": hap_server_status}).encode("utf-8"))
280291

281292
def handle_pairing(self):
282293
"""Handles arbitrary step of the pairing process."""
@@ -430,8 +441,7 @@ def _pairing_five(self, client_username, client_ltpk, encryption_key):
430441
should_confirm = self.accessory_handler.pair(client_uuid, client_ltpk)
431442

432443
if not should_confirm:
433-
self.send_response(500)
434-
self.end_response(b'')
444+
self.send_response_with_status(500, HAP_SERVER_STATUS.INVALID_VALUE_IN_REQUEST)
435445
return
436446

437447
tlv_data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x06',
@@ -456,7 +466,7 @@ def handle_pair_verify(self):
456466
elif sequence == b'\x03':
457467
self._pair_verify_two(tlv_objects)
458468
else:
459-
raise ValueError
469+
raise ValueError("Unknown pairing sequence of %s during pair verify" % (sequence))
460470

461471
def _pair_verify_one(self, tlv_objects):
462472
"""Generate new session key pair and send a proof to the client.
@@ -610,7 +620,7 @@ def handle_pairings(self):
610620
elif request_type == 4:
611621
self._handle_remove_pairing(tlv_objects)
612622
else:
613-
raise ValueError
623+
raise ValueError("Unknown pairing request type of %s during pair verify" % (request_type))
614624

615625
def _handle_add_pairing(self, tlv_objects):
616626
"""Update client information."""
@@ -621,8 +631,7 @@ def _handle_add_pairing(self, tlv_objects):
621631
should_confirm = self.accessory_handler.pair(
622632
client_uuid, client_public)
623633
if not should_confirm:
624-
self.send_response(500)
625-
self.end_response(b'')
634+
self.send_response_with_status(500, HAP_SERVER_STATUS.INVALID_VALUE_IN_REQUEST)
626635
return
627636

628637
data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02")
@@ -765,7 +774,13 @@ def recv(self, buflen=1042, flags=0):
765774
The received full cipher blocks are decrypted and returned and partial cipher
766775
blocks are buffered locally.
767776
"""
768-
assert not flags and buflen
777+
assert not flags
778+
779+
if buflen == 0:
780+
# If the reads get aligned just right, it possible that we
781+
# could be asked to read zero bytes. Since we do not want to block
782+
# we return an empty bytes string.
783+
return b""
769784

770785
result = self.curr_decrypted
771786

0 commit comments

Comments
 (0)