From 1a7e9a13ee91d101c7e2b3f4d276e6f6d9a8ca9d Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Thu, 20 Aug 2026 14:47:51 +0200 Subject: [PATCH] Add parallel WebDAV download stress test --- webdav-download-test.php | 296 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 webdav-download-test.php diff --git a/webdav-download-test.php b/webdav-download-test.php new file mode 100644 index 0000000..8e9b222 --- /dev/null +++ b/webdav-download-test.php @@ -0,0 +1,296 @@ +$connection_id, + 'webdav_path'=>$webdav_path, + 'size'=>$size, + ); +} + +function bratonien_tools_webdav_download_test_connection(array $source) +{ + $table = $GLOBALS['prefixeTable'].'bratonien_tools_nc_connections'; + $result = pwg_query('SELECT config_json, secret_blob FROM `'.$table.'` WHERE id='.(int)$source['connection_id'].' LIMIT 1'); + if (!pwg_db_num_rows($result)) return null; + + $row = pwg_db_fetch_assoc($result); + $config = json_decode((string)$row['config_json'], true); + if (!is_array($config)) return null; + + $key_result = pwg_query("SELECT value FROM ".$GLOBALS['prefixeTable']."config WHERE param='bratonien_nc_connector_secret' LIMIT 1"); + if (!pwg_db_num_rows($key_result)) return null; + $key_row = pwg_db_fetch_assoc($key_result); + + $credentials = bratonien_tools_webdav_download_test_decrypt_secret((string)$row['secret_blob'], (string)$key_row['value']); + if (!is_array($credentials)) return null; + + $base_url = rtrim((string)($config['nextcloud_url'] ?? ''), '/'); + $user = trim((string)($credentials['nextcloud_user'] ?? '')); + $password = (string)($credentials['nextcloud_password'] ?? ''); + if ($base_url === '' || $user === '' || $password === '') return null; + + return array('base_url'=>$base_url, 'user'=>$user, 'password'=>$password); +} + +global $user; +if (!isset($user['status']) || !in_array($user['status'], array('admin', 'webmaster'), true)) +{ + bratonien_tools_webdav_download_test_abort(403, 'Dieser Test-Endpunkt ist nur fuer Administratoren verfuegbar.'); +} + +$image_id = (int)($_GET['id'] ?? 0); +$parallel = (int)($_GET['parallel'] ?? 1); +if ($image_id < 1) +{ + bratonien_tools_webdav_download_test_abort(400, 'Bild-ID fehlt.'); +} +if ($parallel < 1 || $parallel > 20) +{ + bratonien_tools_webdav_download_test_abort(400, 'parallel muss zwischen 1 und 20 liegen.'); +} + +$result = pwg_query('SELECT * FROM '.IMAGES_TABLE.' WHERE id='.$image_id.' LIMIT 1'); +if (!pwg_db_num_rows($result)) +{ + bratonien_tools_webdav_download_test_abort(404, 'Bild nicht gefunden.'); +} +$image_row = pwg_db_fetch_assoc($result); + +$source = bratonien_tools_webdav_download_test_source_info($image_id, $image_row); +if (!$source) +{ + bratonien_tools_webdav_download_test_abort(404, 'Keine WebDAV-Quelle fuer dieses Bild gefunden.'); +} + +$connection = bratonien_tools_webdav_download_test_connection($source); +if (!$connection) +{ + bratonien_tools_webdav_download_test_abort(500, 'WebDAV-Verbindung konnte nicht geladen werden.'); +} + +$upload_dir = rtrim((string)($GLOBALS['conf']['upload_dir'] ?? './upload'), '/'); +$test_rel_dir = $upload_dir.'/bratonien-webdav-download-test'; +$test_root = PHPWG_ROOT_PATH.$test_rel_dir; +if (!is_dir($test_root) && !@mkdir($test_root, 0755, true)) +{ + bratonien_tools_webdav_download_test_abort(500, 'Download-Testbereich konnte nicht angelegt werden: '.$test_root); +} +if (!is_writable($test_root)) +{ + bratonien_tools_webdav_download_test_abort(500, 'Download-Testbereich ist fuer PHP nicht beschreibbar: '.$test_root); +} + +$url = $connection['base_url'].'/remote.php/dav/files/'.rawurlencode($connection['user']).'/'.bratonien_tools_webdav_download_test_quote_path($source['webdav_path']); +$token = getmypid().'-'.bin2hex(random_bytes(6)); +$multi = curl_multi_init(); +$items = array(); + +for ($i = 0; $i < $parallel; $i++) +{ + $path = $test_root.'/download-'.$image_id.'-'.$token.'-'.$i.'.tmp'; + $fp = @fopen($path, 'xb'); + if (!$fp) + { + foreach ($items as $item) + { + @fclose($item['fp']); + @unlink($item['path']); + curl_multi_remove_handle($multi, $item['ch']); + curl_close($item['ch']); + } + curl_multi_close($multi); + bratonien_tools_webdav_download_test_abort(500, 'Temporaere Download-Datei konnte nicht angelegt werden.'); + } + + $ch = curl_init($url); + curl_setopt_array($ch, array( + CURLOPT_FOLLOWLOCATION => false, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_TIMEOUT => 180, + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_USERPWD => $connection['user'].':'.$connection['password'], + CURLOPT_RETURNTRANSFER => false, + CURLOPT_FAILONERROR => false, + CURLOPT_FILE => $fp, + CURLOPT_USERAGENT => 'Bratonien-Tools-WebDAV-Download-Stress-Test', + )); + curl_multi_add_handle($multi, $ch); + $items[] = array('ch'=>$ch, 'fp'=>$fp, 'path'=>$path); +} + +$started_at = microtime(true); +do +{ + $status = curl_multi_exec($multi, $running); + if ($status !== CURLM_OK) break; + if ($running > 0) + { + $selected = curl_multi_select($multi, 1.0); + if ($selected === -1) usleep(10000); + } +} +while ($running > 0); +$elapsed_seconds = microtime(true) - $started_at; + +$downloads = array(); +$total_bytes = 0; +$successful = 0; +$failed = 0; + +foreach ($items as $index => $item) +{ + @fflush($item['fp']); + @fclose($item['fp']); + + $http = (int)curl_getinfo($item['ch'], CURLINFO_HTTP_CODE); + $time_ms = round((float)curl_getinfo($item['ch'], CURLINFO_TOTAL_TIME) * 1000, 2); + $errno = curl_errno($item['ch']); + $error = curl_error($item['ch']); + $bytes = is_file($item['path']) ? (int)filesize($item['path']) : 0; + $ok = ($errno === 0 && $http >= 200 && $http < 300 && $bytes > 0); + + if ($ok) $successful++; else $failed++; + $total_bytes += $bytes; + + $downloads[] = array( + 'index'=>$index + 1, + 'ok'=>$ok, + 'http'=>$http, + 'bytes'=>$bytes, + 'elapsed_ms'=>$time_ms, + 'mbps'=>$time_ms > 0 ? round(($bytes * 8) / ($time_ms / 1000) / 1000000, 2) : 0, + 'curl_errno'=>$errno, + 'curl_error'=>$error, + ); + + @unlink($item['path']); + curl_multi_remove_handle($multi, $item['ch']); + curl_close($item['ch']); +} +curl_multi_close($multi); + +$elapsed_ms = round($elapsed_seconds * 1000, 2); +$aggregate_mbps = $elapsed_seconds > 0 ? round(($total_bytes * 8) / $elapsed_seconds / 1000000, 2) : 0; + +header('Content-Type: application/json; charset=utf-8'); +header('Cache-Control: no-store'); +echo json_encode(array( + 'image_id'=>$image_id, + 'parallel'=>$parallel, + 'same_source_repeated'=>true, + 'expected_source_bytes'=>(int)$source['size'], + 'successful'=>$successful, + 'failed'=>$failed, + 'total_bytes'=>$total_bytes, + 'peak_temp_disk_bytes'=>$total_bytes, + 'elapsed_ms'=>$elapsed_ms, + 'aggregate_mbps'=>$aggregate_mbps, + 'downloads'=>$downloads, +), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);