Skip to content

Commit 8350a2f

Browse files
committed
Refactor internalApiRequest() and getEventFeedAction() for streaming via CLI
Fetching the event feed from the API via `internalApiRequest` wasn't working since it returns a StreamedResponse. Fix this by only using an output buffer in `internalApiRequest` and only calling `ob_flush` from `getEventFeedAction` if the top-level output buffer allows it. Also return contents from `internalApiRequest` without JSON decoding it, since the event feed endpoint is NDJSON, *not* JSON. Explicitly decode the JSON at the other places calling internalApiRequest`.
1 parent 6be1166 commit 8350a2f

File tree

5 files changed

+33
-14
lines changed

5 files changed

+33
-14
lines changed

webapp/src/Command/CallApiActionCommand.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
144144
}
145145

146146
try {
147-
$response = null;
147+
$response = '';
148148
$this->dj->withAllRoles(function () use ($input, $data, $files, &$response) {
149149
$response = $this->dj->internalApiRequest('/' . $input->getArgument('endpoint'), $input->getOption('method'), $data, $files, true);
150150
}, $user);
@@ -154,7 +154,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
154154
return Command::FAILURE;
155155
}
156156

157-
$output->writeln(json_encode($response, JSON_PRESERVE_ZERO_FRACTION | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT));
157+
$output->writeln($response);
158158
return Command::SUCCESS;
159159
}
160160
}

webapp/src/Controller/API/ContestController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ public function getEventFeedAction(
871871
}
872872

873873
echo Utils::jsonEncode($result) . "\n";
874-
ob_flush();
874+
Utils::ob_flush_if_possible();
875875
flush();
876876
$lastUpdate = Utils::now();
877877
$lastIdSent = $event->getEventid();
@@ -896,7 +896,7 @@ public function getEventFeedAction(
896896
# Send keep alive every 10s. Guarantee according to spec is 120s.
897897
# However, nginx drops the connection if we don't update for 60s.
898898
echo "\n";
899-
ob_flush();
899+
Utils::ob_flush_if_possible();
900900
flush();
901901
$lastUpdate = $now;
902902
}

webapp/src/Service/DOMJudgeService.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -652,13 +652,15 @@ public function internalApiRequest(string $url, string $method = Request::METHOD
652652
return null;
653653
}
654654

655-
$content = $response->getContent();
656-
657-
if ($content === '') {
658-
return null;
655+
if ($response instanceof StreamedResponse) {
656+
ob_start(flags: PHP_OUTPUT_HANDLER_REMOVABLE);
657+
$response->sendContent();
658+
$content = ob_get_clean();
659+
} else {
660+
$content = $response->getContent();
659661
}
660662

661-
return Utils::jsonDecode($content);
663+
return $content;
662664
}
663665

664666
public function getDomjudgeEtcDir(): string

webapp/src/Service/EventLogService.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,19 +316,22 @@ public function log(
316316
}
317317

318318
$this->dj->withAllRoles(function () use ($query, $url, &$json) {
319-
$json = $this->dj->internalApiRequest($url, Request::METHOD_GET, $query);
319+
$response = $this->dj->internalApiRequest($url, Request::METHOD_GET, $query);
320320
});
321321

322-
if ($json === null) {
322+
if ($response === '') {
323323
$this->logger->warning(
324-
"EventLogService::log got no JSON data from '%s'", [ $url ]
324+
"EventLogService::log got no data from '%s'", [ $url ]
325325
);
326326
// If we didn't get data from the API, then that is probably
327327
// because this particular data is not visible, for example
328328
// because it belongs to an invisible jury team. If we don't
329329
// have data, there's also no point in trying to insert
330330
// anything in the eventlog table.
331331
return;
332+
$json = null;
333+
} else {
334+
$json = Utils::jsonDecode($response);
332335
}
333336
}
334337

@@ -484,7 +487,8 @@ public function addMissingStateEvents(Contest $contest): void
484487
$url = sprintf('/contests/%s/awards', $contest->getExternalid());
485488
$awards = [];
486489
$this->dj->withAllRoles(function () use ($url, &$awards) {
487-
$awards = $this->dj->internalApiRequest($url);
490+
$response = $this->dj->internalApiRequest($url);
491+
if ( !empty($response) ) $awards = Utils::jsonDecode($response);
488492
});
489493
foreach ($awards as $award) {
490494
$this->insertEvent($contest, 'awards', $award['id'], $award);
@@ -625,7 +629,8 @@ public function initStaticEvents(Contest $contest): void
625629
$urlPart = $endpoint === 'contests' ? '' : ('/' . $endpoint);
626630
$url = sprintf('/contests/%s%s', $contestId, $urlPart);
627631
$this->dj->withAllRoles(function () use ($url, &$data) {
628-
$data = $this->dj->internalApiRequest($url);
632+
$response = $this->dj->internalApiRequest($url);
633+
$data = (empty($response) ? null : Utils::jsonDecode($response));
629634
});
630635

631636
// Get a partial reference to the contest,

webapp/src/Utils/Utils.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,4 +1023,16 @@ public static function extendMaxExecutionTime(int $minimumMaxExecutionTime): voi
10231023
ini_set('max_execution_time', $minimumMaxExecutionTime);
10241024
}
10251025
}
1026+
1027+
/**
1028+
* Call ob_flush() unless the top-level output buffer does not allow it.
1029+
*/
1030+
public static function ob_flush_if_possible(): bool
1031+
{
1032+
$status = ob_get_status();
1033+
if ( empty($status) || ($status['flags'] & PHP_OUTPUT_HANDLER_CLEANABLE) ) {
1034+
return ob_flush();
1035+
}
1036+
return false;
1037+
}
10261038
}

0 commit comments

Comments
 (0)