mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-09-23 11:05:32 -04:00
fix: bound the IPSpeaker http timeout instead of leaving it at LWP's default
LWP::UserAgent defaults to 180 seconds. Actions for a monitor are serialised through one control daemon, so a speaker that has dropped off the network holds that daemon for three minutes per attempt and every command queued behind it waits. AMLink already sets a timeout; this did not. Ten seconds, matching AMLink. The speaker answers in milliseconds when it is reachable at all, so this only ever bounds the case where it is not - which is the case right now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UvTCzCbvGt8xKQRNCSA7o8
This commit is contained in:
1 parent
70836e7c83
commit
8b14fba2db
2 files changed
+21
-2
No files matched your search
@@ -57,6 +57,10 @@ use constant VOLUME_MIN => 0;
|
||||
use constant VOLUME_MAX => 100;
|
||||
use constant VOLUME_STEP => 5;
|
||||
|
||||
# Seconds. The speaker answers in milliseconds when it is there at all, so this
|
||||
# only ever bounds the case where it is not.
|
||||
use constant HTTP_TIMEOUT => 10;
|
||||
|
||||
# config=audio.set replaces the whole audio section: any field left out of the
|
||||
# POST reverts to a firmware default, silently taking the microphone, codec
|
||||
# list and echo-cancellation settings with it. Every write therefore reads the
|
||||
@@ -121,7 +125,11 @@ sub open {
|
||||
my $self = shift;
|
||||
$self->loadMonitor();
|
||||
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
# Actions are fire-and-forget from the monitor's point of view, but they are
|
||||
# serialised through this one control daemon, so a speaker that has dropped
|
||||
# off the network must not hold it. LWP's default is 180s, long enough for a
|
||||
# single unreachable speaker to stall every later command behind it.
|
||||
$self->{ua} = LWP::UserAgent->new(timeout => HTTP_TIMEOUT);
|
||||
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION());
|
||||
|
||||
if (!$self->guess_credentials()) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 32;
|
||||
use Test::More tests => 35;
|
||||
|
||||
require_ok('ZoneMinder::Control::IPSpeaker');
|
||||
|
||||
@@ -77,3 +77,14 @@ my $sparse_form = $P->can('audio_set_form')->(\%sparse, outvolume => 0);
|
||||
is_deeply([sort keys %$sparse_form], [qw(micvolume outvolume)],
|
||||
'fields absent from the device response are not invented');
|
||||
is($sparse_form->{outvolume}, 0, 'a zero override is applied, not treated as absent');
|
||||
|
||||
# --- the http timeout -------------------------------------------------------
|
||||
# A speaker that drops off the network must not stall the control daemon. LWP
|
||||
# defaults to 180s, and every action for this monitor queues behind the one in
|
||||
# flight, so an absent speaker would hold up everything that followed it.
|
||||
|
||||
is(ZoneMinder::Control::IPSpeaker::HTTP_TIMEOUT(), 10, 'the http timeout is set, not left to LWP');
|
||||
cmp_ok(ZoneMinder::Control::IPSpeaker::HTTP_TIMEOUT(), '<', 180,
|
||||
'and is well under the LWP default that caused the stall');
|
||||
cmp_ok(ZoneMinder::Control::IPSpeaker::HTTP_TIMEOUT(), '>', 0,
|
||||
'and is a real timeout rather than "no wait"');
|
||||
Reference in new issue
Block a user