77#include " XrootdHttpRemote.h"
88#include " adios2/helper/adiosLog.h"
99
10+ #include < chrono>
11+ #include < cstdlib>
1012#include < cstring>
1113#include < sstream>
1214#include < stdexcept>
15+ #include < thread>
16+
17+ #ifdef _WIN32
18+ #include < direct.h>
19+ #include < stdlib.h>
20+ #define getcwd _getcwd
21+ #define PATH_MAX _MAX_PATH
22+ #else
23+ #include < climits>
24+ #include < unistd.h>
25+ #endif
1326
1427#ifdef ADIOS2_HAVE_CURL
1528#include < curl/curl.h>
@@ -219,9 +232,18 @@ void CurlMultiPool::WorkerLoop()
219232 continue ;
220233 }
221234
222- // Wait for socket activity (up to 100ms)
235+ // Skip waiting if new requests are queued -- submit them immediately
236+ {
237+ std::lock_guard<std::mutex> lock (m_QueueMutex);
238+ if (!m_PendingQueue.empty ())
239+ {
240+ continue ;
241+ }
242+ }
243+
244+ // Wait for socket activity (up to 10ms)
223245 int numfds;
224- CURLMcode mc = curl_multi_wait (m_MultiHandle, nullptr , 0 , 100 , &numfds);
246+ CURLMcode mc = curl_multi_wait (m_MultiHandle, nullptr , 0 , 10 , &numfds);
225247 if (mc != CURLM_OK )
226248 {
227249 helper::Log (" Remote" , " CurlMultiPool" , " WorkerLoop" ,
@@ -269,7 +291,23 @@ void XrootdHttpRemote::Open(const std::string hostname, const int32_t port,
269291 const std::string filename, const Mode mode, bool RowMajorOrdering,
270292 const Params ¶ms)
271293{
272- m_Filename = filename;
294+ // Ensure filename is absolute so the URL path is clean (no double-slash ambiguity)
295+ if (!filename.empty () && filename[0 ] != ' /' )
296+ {
297+ char cwd[PATH_MAX ];
298+ if (getcwd (cwd, sizeof (cwd)))
299+ {
300+ m_Filename = std::string (cwd) + " /" + filename;
301+ }
302+ else
303+ {
304+ m_Filename = filename;
305+ }
306+ }
307+ else
308+ {
309+ m_Filename = filename;
310+ }
273311 m_Mode = mode;
274312 m_RowMajorOrdering = RowMajorOrdering;
275313
@@ -310,7 +348,8 @@ void XrootdHttpRemote::Open(const std::string hostname, const int32_t port,
310348 }
311349
312350 std::ostringstream urlStream;
313- urlStream << (m_UseHttps ? " https" : " http" ) << " ://" << hostname << " :" << port << " /ssi" ;
351+ urlStream << (m_UseHttps ? " https" : " http" ) << " ://" << hostname << " :" << port << " /adios/"
352+ << UrlEncode (m_Filename);
314353 m_BaseUrl = urlStream.str ();
315354
316355 m_OpenSuccess = true ;
@@ -323,26 +362,47 @@ std::string XrootdHttpRemote::BuildRequestString(const char *VarName, size_t Ste
323362 const Dims &Start, const Accuracy &accuracy)
324363{
325364 std::ostringstream reqStream;
326- std::string encodedFilename = UrlEncode (m_Filename);
327365 std::string encodedVarName = UrlEncode (std::string (VarName));
328366
329- reqStream << " get Filename=" << encodedFilename;
330- reqStream << " &RMOrder=" << (m_RowMajorOrdering ? 1 : 0 );
331- reqStream << " &Varname=" << encodedVarName;
332- reqStream << " &StepStart=" << Step;
333- reqStream << " &StepCount=" << StepCount;
334- reqStream << " &Block=" << BlockID;
335- reqStream << " &Dims=" << Count.size ();
336-
337- for (const auto &c : Count)
338- reqStream << " &Count=" << c;
339- for (const auto &s : Start)
340- reqStream << " &Start=" << s;
341-
342- reqStream << " &AccuracyError=" << accuracy.error ;
343- reqStream << " &AccuracyNorm=" << accuracy.norm ;
344- reqStream << " &AccuracyRelative=" << (accuracy.relative ? 1 : 0 );
345-
367+ reqStream << " get&Varname=" << encodedVarName;
368+ if (m_RowMajorOrdering)
369+ {
370+ reqStream << " &RMOrder=1" ;
371+ }
372+ if (Step != 0 )
373+ {
374+ reqStream << " &StepStart=" << Step;
375+ }
376+ if (StepCount != 1 )
377+ {
378+ reqStream << " &StepCount=" << StepCount;
379+ }
380+ if (BlockID != static_cast <size_t >(-1 ))
381+ {
382+ reqStream << " &Block=" << BlockID;
383+ }
384+ if (!Count.empty ())
385+ {
386+ reqStream << " &Count=" << Count[0 ];
387+ for (size_t i = 1 ; i < Count.size (); i++)
388+ {
389+ reqStream << " ," << Count[i];
390+ }
391+ }
392+ if (!Start.empty ())
393+ {
394+ reqStream << " &Start=" << Start[0 ];
395+ for (size_t i = 1 ; i < Start.size (); i++)
396+ {
397+ reqStream << " ," << Start[i];
398+ }
399+ }
400+ if (accuracy.error != 0.0 || accuracy.norm != 0.0 || accuracy.relative )
401+ {
402+ reqStream << " &AccuracyError=" << accuracy.error ;
403+ reqStream << " &AccuracyNorm=" << accuracy.norm ;
404+ reqStream << " &AccuracyRelative=" << (accuracy.relative ? 1 : 0 );
405+ }
346406 if (!m_EngineParams.empty ())
347407 {
348408 reqStream << " &EngineParams=" << UrlEncode (m_EngineParams);
@@ -352,7 +412,7 @@ std::string XrootdHttpRemote::BuildRequestString(const char *VarName, size_t Ste
352412}
353413
354414CURL *XrootdHttpRemote::CreateEasyHandle (AsyncGet *asyncOp, const std::string &url,
355- const std::string &postData )
415+ const std::string &queryData )
356416{
357417#ifdef ADIOS2_HAVE_CURL
358418 CURL *easy = curl_easy_init ();
@@ -362,8 +422,9 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
362422 curl_easy_setopt (easy, CURLOPT_PRIVATE , asyncOp);
363423 asyncOp->easyHandle = easy;
364424
365- curl_easy_setopt (easy, CURLOPT_URL , url.c_str ());
366- curl_easy_setopt (easy, CURLOPT_COPYPOSTFIELDS , postData.c_str ());
425+ std::string fullUrl = url + " ?" + queryData;
426+ curl_easy_setopt (easy, CURLOPT_URL , fullUrl.c_str ());
427+ curl_easy_setopt (easy, CURLOPT_HTTPGET , 1L );
367428 curl_easy_setopt (easy, CURLOPT_WRITEFUNCTION , WriteCallback);
368429 curl_easy_setopt (easy, CURLOPT_WRITEDATA , &asyncOp->responseData );
369430 curl_easy_setopt (easy, CURLOPT_CONNECTTIMEOUT , m_ConnectTimeout);
@@ -385,9 +446,6 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
385446 }
386447 }
387448
388- asyncOp->headers =
389- curl_slist_append (nullptr , " Content-Type: application/x-www-form-urlencoded" );
390- curl_easy_setopt (easy, CURLOPT_HTTPHEADER , asyncOp->headers );
391449 curl_easy_setopt (easy, CURLOPT_HTTP_VERSION , CURL_HTTP_VERSION_2TLS );
392450
393451 return easy;
@@ -399,11 +457,12 @@ CURL *XrootdHttpRemote::CreateEasyHandle(AsyncGet *asyncOp, const std::string &u
399457std::string XrootdHttpRemote::BuildBatchRequestString (const std::vector<BatchGetRequest> &requests)
400458{
401459 std::ostringstream reqStream;
402- std::string encodedFilename = UrlEncode (m_Filename);
403460
404- reqStream << " batchget Filename=" << encodedFilename;
405- reqStream << " &RMOrder=" << (m_RowMajorOrdering ? 1 : 0 );
406- reqStream << " &NVars=" << requests.size ();
461+ reqStream << " batchget&NVars=" << requests.size ();
462+ if (m_RowMajorOrdering)
463+ {
464+ reqStream << " &RMOrder=1" ;
465+ }
407466 if (!m_EngineParams.empty ())
408467 {
409468 reqStream << " &EngineParams=" << UrlEncode (m_EngineParams);
@@ -412,18 +471,41 @@ std::string XrootdHttpRemote::BuildBatchRequestString(const std::vector<BatchGet
412471 for (const auto &req : requests)
413472 {
414473 std::string encodedVarName = UrlEncode (std::string (req.VarName ));
415- reqStream << " |Varname=" << encodedVarName;
416- reqStream << " &StepStart=" << req.Step ;
417- reqStream << " &StepCount=" << req.StepCount ;
418- reqStream << " &Block=" << req.BlockID ;
419- reqStream << " &Dims=" << req.Count .size ();
420- for (const auto &c : req.Count )
421- reqStream << " &Count=" << c;
422- for (const auto &s : req.Start )
423- reqStream << " &Start=" << s;
424- reqStream << " &AccuracyError=" << req.accuracy .error ;
425- reqStream << " &AccuracyNorm=" << req.accuracy .norm ;
426- reqStream << " &AccuracyRelative=" << (req.accuracy .relative ? 1 : 0 );
474+ reqStream << " &Varname=" << encodedVarName;
475+ if (req.Step != 0 )
476+ {
477+ reqStream << " &StepStart=" << req.Step ;
478+ }
479+ if (req.StepCount != 1 )
480+ {
481+ reqStream << " &StepCount=" << req.StepCount ;
482+ }
483+ if (req.BlockID != static_cast <size_t >(-1 ))
484+ {
485+ reqStream << " &Block=" << req.BlockID ;
486+ }
487+ if (!req.Count .empty ())
488+ {
489+ reqStream << " &Count=" << req.Count [0 ];
490+ for (size_t i = 1 ; i < req.Count .size (); i++)
491+ {
492+ reqStream << " ," << req.Count [i];
493+ }
494+ }
495+ if (!req.Start .empty ())
496+ {
497+ reqStream << " &Start=" << req.Start [0 ];
498+ for (size_t i = 1 ; i < req.Start .size (); i++)
499+ {
500+ reqStream << " ," << req.Start [i];
501+ }
502+ }
503+ if (req.accuracy .error != 0.0 || req.accuracy .norm != 0.0 || req.accuracy .relative )
504+ {
505+ reqStream << " &AccuracyError=" << req.accuracy .error ;
506+ reqStream << " &AccuracyNorm=" << req.accuracy .norm ;
507+ reqStream << " &AccuracyRelative=" << (req.accuracy .relative ? 1 : 0 );
508+ }
427509 }
428510
429511 return reqStream.str ();
@@ -489,30 +571,58 @@ bool XrootdHttpRemote::BatchGet(const std::vector<BatchGetRequest> &requests)
489571 subBatches.push_back ({startIdx, count, asyncOp});
490572 }
491573
492- // Wait for all sub-batches and parse responses
574+ // Wait for all sub-batches, retry transient failures
575+ const int maxRetries = 3 ;
493576 bool allOk = true ;
494577 for (auto &sb : subBatches)
495578 {
496579 bool success = sb.asyncOp ->promise .get_future ().get ();
580+ std::string lastErr;
497581
498- if (!success || !allOk )
582+ if (!success)
499583 {
500- if (!success && allOk)
584+ lastErr = sb.asyncOp ->errorMsg ;
585+ delete sb.asyncOp ;
586+ sb.asyncOp = nullptr ;
587+
588+ // Retry this sub-batch
589+ for (int retry = 1 ; retry <= maxRetries; retry++)
501590 {
502- std::string errMsg = sb.asyncOp ->errorMsg ;
503- // Clean up remaining sub-batches before throwing
504- delete sb.asyncOp ;
505- for (size_t j = (&sb - &subBatches[0 ]) + 1 ; j < subBatches.size (); j++)
591+ helper::Log (" Remote" , " XrootdHttpRemote" , " BatchGet" ,
592+ " Sub-batch failed (" + lastErr + " ), retry " + std::to_string (retry) +
593+ " /" + std::to_string (maxRetries),
594+ helper::LogMode::WARNING );
595+
596+ std::this_thread::sleep_for (std::chrono::milliseconds (100 * retry));
597+
598+ std::vector<BatchGetRequest> subRequests (requests.begin () + sb.startIdx ,
599+ requests.begin () + sb.startIdx + sb.count );
600+ std::string postData = BuildBatchRequestString (subRequests);
601+ AsyncGet *retryOp = new AsyncGet ();
602+ CURL *easy = CreateEasyHandle (retryOp, m_BaseUrl, postData);
603+ if (!easy)
506604 {
507- subBatches[j]. asyncOp -> promise . get_future (). get () ;
508- delete subBatches[j]. asyncOp ;
605+ delete retryOp ;
606+ continue ;
509607 }
608+ pool.Submit (easy, retryOp);
609+ success = retryOp->promise .get_future ().get ();
610+ if (success)
611+ {
612+ sb.asyncOp = retryOp;
613+ break ;
614+ }
615+ lastErr = retryOp->errorMsg ;
616+ delete retryOp;
617+ }
618+
619+ if (!success)
620+ {
510621 helper::Throw<std::runtime_error>(" Remote" , " XrootdHttpRemote" , " BatchGet" ,
511- " Sub-batch failed: " + errMsg);
622+ " Sub-batch failed after " +
623+ std::to_string (maxRetries) +
624+ " retries: " + lastErr);
512625 }
513- allOk = false ;
514- delete sb.asyncOp ;
515- continue ;
516626 }
517627
518628 // Parse binary response:
0 commit comments