+sub long_response {
+ my ($self, $beg, $end, $cb) = @_;
+ die "BUG: nested long response" if $self->{long_res};
+
+ # make sure we disable reading during a long response,
+ # clients should not be sending us stuff and making us do more
+ # work while we are stream a response to them
+ $self->watch_read(0);
+ $self->{long_res} = sub {
+ # limit our own running time for fairness with other
+ # clients and to avoid buffering too much:
+ my $yield;
+ local $SIG{ALRM} = sub { $yield = 1 };
+ ualarm(100000);
+
+ my $err;
+ do {
+ eval { $cb->(\$beg) };
+ } until (($err = $@) || $self->{closed} || $yield ||
+ $self->{write_buf_size} || ++$beg > $end);
+ ualarm(0);
+
+ if ($err || $self->{closed}) {
+ $self->{long_res} = undef;
+ warning("$err during long response") if $err;
+ $self->watch_read(1) unless $self->{closed};
+ } elsif ($yield || $self->{write_buf_size}) {
+ # no recursion, schedule another call ASAP
+ # but only after all pending writes are done
+ Danga::Socket->AddTimer(0, sub {
+ $self->write($self->{long_res});
+ });
+ } else { # all done!
+ $self->{long_res} = undef;
+ $self->watch_read(1);
+ res($self, '.');
+ }
+ };
+ $self->{long_res}->(); # kick off!
+ undef;
+}
+