diff --git a/ChangeLog.txt b/ChangeLog.txt index 226e43d112..d23e5e410f 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,34 @@ Entries may not always be in chronological/commit order. See license at the end of file. */ +2025-01-07 22:49 UTC+0100 Aleksander Czajczynski (hb fki.pl) + * contrib/hbcurl/hbcurl.ch + * updated HB_CURLOPT_SSL_OPTIONS: HB_CURLSSLOPT_* + + + added HB_CURLMSG_RESP_HANDLE, HB_CURLMSG_RESP_HPOS + mappings of array elements returned by + curl_multi_info_read( [, ]) -> + + * contrib/hbcurl/core.c + * adapted @emazv72 contribution from: + https://github.com/vszakats/hb/pull/344 + Many thanks, it could be useful for managing multiple transfers, + by extending some kind of Inkey() loop or in many other + scenarios. + + + added curl lib version guards + + * minor code and code formattings + + + extended function + curl_multi_info_read( [, ]) -> + with a possibility to return real GC pointer of specific + transfer related to reported event. should be + specified if this is expected. + + + contrib/hbcurl/tests/multi.prg + + demo of the additions + 2025-01-07 21:43 UTC+0100 Emanuele Zavallone (emanuele.zavallone gmail.com) * contrib/hbcurl/easy.c * contrib/hbcurl/hbcurl.ch diff --git a/contrib/hbcurl/core.c b/contrib/hbcurl/core.c index 2982b19f43..a8c97ddf57 100644 --- a/contrib/hbcurl/core.c +++ b/contrib/hbcurl/core.c @@ -127,6 +127,16 @@ typedef struct _HB_CURL } HB_CURL, * PHB_CURL; +/* Multi interface */ +/* --------------- */ + +#if LIBCURL_VERSION_NUM >= 0x070906 +typedef struct _HB_CURLM +{ + CURLM * curlm; + +} HB_CURLM, * PHB_CURLM; +#endif /* functions to keep passed string values accessible even if HVM * destroy them. It's necessary for old CURL versions which do not @@ -2499,19 +2509,10 @@ HB_FUNC( CURL_GETDATE ) hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* Multi interface */ -/* ----------------- */ - -typedef struct _HB_CURLM -{ - CURLM * curlm; - -} HB_CURLM, * PHB_CURLM; - - -/* Constructor/Destructor */ -/* ---------------------- */ +/* Multi interface Constructor/Destructor */ +/* -------------------------------------- */ +#if LIBCURL_VERSION_NUM >= 0x070906 static void PHB_CURLM_free( PHB_CURLM hb_curlm ) { curl_multi_cleanup( hb_curlm->curlm ); @@ -2574,18 +2575,21 @@ static PHB_CURLM PHB_CURLM_par( int iParam ) return ph ? ( PHB_CURLM ) *ph : NULL; } +#endif -/* Harbour interface */ -/* ----------------- */ - +/* Harbour multi interface */ +/* ----------------------- */ HB_FUNC( CURL_MULTI_INIT ) { +#if LIBCURL_VERSION_NUM >= 0x070906 PHB_CURLM_ret( ); +#endif } HB_FUNC( CURL_MULTI_CLEANUP ) { +#if LIBCURL_VERSION_NUM >= 0x070906 if( PHB_CURLM_is( 1 ) ) { void ** ph = ( void ** ) hb_parptrGC( &s_gcCURLMFuncs, 1 ); @@ -2598,11 +2602,13 @@ HB_FUNC( CURL_MULTI_CLEANUP ) } } else +#endif hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } HB_FUNC( CURL_MULTI_ADD_HANDLE ) { +#if LIBCURL_VERSION_NUM >= 0x070906 if( PHB_CURLM_is( 1 ) && PHB_CURL_is( 2 ) ) { PHB_CURLM hb_curlm = PHB_CURLM_par( 1 ); @@ -2611,11 +2617,13 @@ HB_FUNC( CURL_MULTI_ADD_HANDLE ) hb_retnl( hb_curlm && hb_curl ? ( long ) curl_multi_add_handle( hb_curlm->curlm, hb_curl->curl) : HB_CURLM_INTERNAL_ERROR ); } else +#endif hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } HB_FUNC( CURL_MULTI_REMOVE_HANDLE ) { +#if LIBCURL_VERSION_NUM >= 0x070906 if( PHB_CURLM_is( 1 ) && PHB_CURL_is( 2 ) ) { PHB_CURLM hb_curlm = PHB_CURLM_par( 1 ); @@ -2624,18 +2632,20 @@ HB_FUNC( CURL_MULTI_REMOVE_HANDLE ) hb_retnl( hb_curlm && hb_curl ? ( long ) curl_multi_remove_handle( hb_curlm->curlm, hb_curl->curl) : HB_CURLM_INTERNAL_ERROR ); } else +#endif hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } HB_FUNC( CURL_MULTI_PERFORM ) { +#if LIBCURL_VERSION_NUM >= 0x070906 if( PHB_CURLM_is( 1 ) && HB_ISBYREF( 2 ) ) { CURLMcode res = ( CURLMcode ) HB_CURLM_INTERNAL_ERROR; PHB_CURLM hb_curlm = PHB_CURLM_par( 1 ); - if ( hb_curlm ) + if( hb_curlm ) { int running_handles = 0; res = curl_multi_perform( hb_curlm->curlm, &running_handles); @@ -2646,18 +2656,20 @@ HB_FUNC( CURL_MULTI_PERFORM ) } else +#endif hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } HB_FUNC( CURL_MULTI_POLL ) { +#if LIBCURL_VERSION_NUM >= 0x074200 if( PHB_CURLM_is( 1 ) && HB_ISNUM( 2 ) ) { CURLMcode res = ( CURLMcode ) HB_CURLM_INTERNAL_ERROR; PHB_CURLM hb_curlm = PHB_CURLM_par( 1 ); - if ( hb_curlm ) + if( hb_curlm ) { res = curl_multi_poll( hb_curlm->curlm, NULL, @@ -2670,41 +2682,60 @@ HB_FUNC( CURL_MULTI_POLL ) } else +#endif hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } HB_FUNC( CURL_MULTI_INFO_READ ) { +#if LIBCURL_VERSION_NUM >= 0x070906 if( PHB_CURLM_is( 1 ) ) { PHB_ITEM pReturn = NULL; PHB_CURLM hb_curlm = PHB_CURLM_par( 1 ); - if ( hb_curlm ) + if( hb_curlm ) { int msgs_in_queue = 0; + long response_code = 0; + struct CURLMsg * msg = curl_multi_info_read( hb_curlm->curlm, &msgs_in_queue ); - struct CURLMsg *msg = curl_multi_info_read( hb_curlm->curlm, &msgs_in_queue ); - if ( msg ) + if( msg && curl_easy_getinfo( msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code ) == CURLE_OK ) { - - CURLcode res = ( CURLcode ) HB_CURLE_ERROR; - long response_code = 0; - res = curl_easy_getinfo( msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code ); - + PHB_ITEM pHandles = hb_param( 2, HB_IT_ARRAY ); pReturn = hb_itemArrayNew( HB_CURLMSG_RESP_LAST ); hb_arraySetNL( pReturn, HB_CURLMSG_RESP_LEN, ( long ) msgs_in_queue ); hb_arraySetNL( pReturn, HB_CURLMSG_RESP_RESPONSE_CODE, ( long ) response_code ); hb_arraySetNL( pReturn, HB_CURLMSG_RESP_MSG, ( long ) msg->msg ); hb_arraySetNL( pReturn, HB_CURLMSG_RESP_RESULT, ( long ) msg->data.result ); + hb_arraySetNI( pReturn, HB_CURLMSG_RESP_HPOS, 0 ); + + if( pHandles ) + { + HB_SIZE handles_count = hb_arrayLen( pHandles ); + HB_SIZE i; + for( i = 1; i <= handles_count; i++ ) + { + void ** ph = hb_arrayGetPtrGC( pHandles, i, &s_gcCURLFuncs ); + if( ph && *ph ) + { + PHB_CURL hbcurl = ( PHB_CURL ) *ph; + if( hbcurl->curl == msg->easy_handle ) + { + hb_arraySetNL( pReturn, HB_CURLMSG_RESP_HPOS, ( long ) i ); + hb_arraySet( pReturn, HB_CURLMSG_RESP_HANDLE, hb_arrayGetItemPtr( pHandles, i ) ); + } + } + } + } } } - if ( pReturn ) + if( pReturn ) hb_itemReturnRelease( pReturn ); else hb_ret(); @@ -2712,12 +2743,10 @@ HB_FUNC( CURL_MULTI_INFO_READ ) } else hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); - +#endif } -/* Harbour interface (session independent) */ - -/* NOTE: Obsolete, superceded by curl_easy_escape() */ +/* NOTE: Obsolete, superseded by curl_easy_escape() */ HB_FUNC( CURL_ESCAPE ) { if( HB_ISCHAR( 1 ) ) @@ -2730,7 +2759,7 @@ HB_FUNC( CURL_ESCAPE ) hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } -/* NOTE: Obsolete, superceded by curl_easy_unescape() */ +/* NOTE: Obsolete, superseded by curl_easy_unescape() */ HB_FUNC( CURL_UNESCAPE ) { if( HB_ISCHAR( 1 ) ) diff --git a/contrib/hbcurl/hbcurl.ch b/contrib/hbcurl/hbcurl.ch index b1b4177934..2aa362c350 100644 --- a/contrib/hbcurl/hbcurl.ch +++ b/contrib/hbcurl/hbcurl.ch @@ -295,6 +295,11 @@ /* HB_CURLOPT_SSL_OPTIONS values */ #define HB_CURLSSLOPT_ALLOW_BEAST hb_bitShift( 1, 0 ) +#define HB_CURLSSLOPT_NO_REVOKE hb_bitShift( 1, 1 ) +#define HB_CURLSSLOPT_NO_PARTIALCHAIN hb_bitShift( 1, 2 ) +#define HB_CURLSSLOPT_REVOKE_BEST_EFFORT hb_bitShift( 1, 3 ) +#define HB_CURLSSLOPT_NATIVE_CA hb_bitShift( 1, 4 ) +#define HB_CURLSSLOPT_AUTO_CLIENT_CERT hb_bitShift( 1, 5 ) /* HB_CURLOPT_HTTPAUTH option */ #define HB_CURLAUTH_NONE 0 /* nothing */ @@ -604,6 +609,8 @@ #define HB_CURLMSG_RESP_RESPONSE_CODE 2 /* curl_easy_getinfo( msg->easy_handle, CURLINFO_RESPONSE_CODE ) */ #define HB_CURLMSG_RESP_MSG 3 /* CURLMSG */ #define HB_CURLMSG_RESP_RESULT 4 /* CURLcode */ -#define HB_CURLMSG_RESP_LAST HB_CURLMSG_RESP_RESULT +#define HB_CURLMSG_RESP_HANDLE 5 /* handle to original curl_easy_init */ +#define HB_CURLMSG_RESP_HPOS 6 /* position in handle passed to curl_multi_info_read(, ) */ +#define HB_CURLMSG_RESP_LAST HB_CURLMSG_RESP_HPOS #endif /* HBCURL_CH_ */ diff --git a/contrib/hbcurl/tests/multi.prg b/contrib/hbcurl/tests/multi.prg new file mode 100644 index 0000000000..6a660be0a7 --- /dev/null +++ b/contrib/hbcurl/tests/multi.prg @@ -0,0 +1,156 @@ +#define REMOTE_1 "https://github.com/" +#define REMOTE_2 "https://github.com/harbour/core/" +#define REMOTE_3 "https://raw.githubusercontent.com/harbour/core/refs/heads/master/contrib/hbcurl/hbcurl.hbm" + +#require "hbcurl" + +#include "inkey.ch" + +PROCEDURE Main( cRemote4 ) + LOCAL hMulti + LOCAL hEasy, aResult, nKey := 0 + LOCAL aUrls := { REMOTE_1, REMOTE_2, REMOTE_3 } + LOCAL aHandles, i, nErr, nRun := 0, nLastRun := 0 + + /* add a remote from command-line if you want */ + + IF ! Empty( cRemote4 ) + AAdd( aUrls, cRemote4 ) + ENDIF + + /* add the individual transfers */ + + aHandles := Array( Len( aUrls ) ) + + curl_global_init() + + hMulti := curl_multi_init() + + FOR i := 1 TO Len( aUrls ) + hEasy := curl_easy_init() + IF hEasy != NIL + curl_easy_setopt( hEasy, HB_CURLOPT_URL, aUrls[ i ] ) + curl_easy_setopt( hEasy, HB_CURLOPT_DOWNLOAD ) + curl_easy_setopt( hEasy, HB_CURLOPT_DL_BUFF_SETUP ) +// curl_easy_setopt( hEasy, HB_CURLOPT_VERBOSE, .T. ) + curl_easy_setopt( hEasy, HB_CURLOPT_SSL_OPTIONS, HB_CURLSSLOPT_NATIVE_CA ) + curl_multi_add_handle( hMulti, hEasy ) + aHandles[ i ] := hEasy + ENDIF + NEXT + + CLS + + nErr := curl_multi_perform( hMulti, @nLastRun ) + + IF nErr != HB_CURLM_OK + ? "curl_multi_perform() failed, err=", nErr + Cleanup( aHandles, hMulti ) + RETURN + ENDIF + + SetPos( 3, 0 ) + + DO WHILE curl_multi_perform( hMulti, @nRun ) == HB_CURLM_OK + + aResult := CurGet() + @ 1, 1 SAY Time() + @ 2, 1 SAY "downloading... (" + hb_ntos( nRun ) + " transfers left)" + CurSet( aResult ) + + IF nRun < nLastRun + FOR i := 1 TO nLastRun - nRun + + /* + * passing possible aHandles is required to receive curl_easy_* + * compatible GC-pointer in aResult[ HB_CURLMSG_RESP_HANDLE ] + */ + + aResult := curl_multi_info_read( hMulti, aHandles ) + + IF aResult # NIL + /* + * instead of FOR loop you could possibly + * DO WHILE HB_IsArray( aResult := curl_multi_info_read( hMulti ) ) .AND. ; + * aResult[ HB_CURLMSG_RESP_LEN ] > 0 + */ + ? "HB_CURLMSG_RESP_LEN :", aResult[ HB_CURLMSG_RESP_LEN ] + ? "HB_CURLMSG_RESP_RESPONSE_CODE:", aResult[ HB_CURLMSG_RESP_RESPONSE_CODE ] + ? "HB_CURLMSG_RESP_MSG :", aResult[ HB_CURLMSG_RESP_MSG ] + ? "HB_CURLMSG_RESP_RESULT :", aResult[ HB_CURLMSG_RESP_RESULT ] + ? "HB_CURLMSG_RESP_HPOS :", aResult[ HB_CURLMSG_RESP_HPOS ] + IF aResult[ HB_CURLMSG_RESP_HPOS ] > 0 + ? "URL :", aUrls[ aResult[ HB_CURLMSG_RESP_HPOS ] ] + IF ! Empty( curl_easy_getinfo( aResult[ HB_CURLMSG_RESP_HANDLE ], HB_CURLINFO_REDIRECT_URL ) ) + ? "HB_CURLINFO_REDIRECT_URL :", curl_easy_getinfo( aResult[ HB_CURLMSG_RESP_HANDLE ], HB_CURLINFO_REDIRECT_URL ) + ENDIF + ENDIF + ? "--" + ENDIF + NEXT + ENDIF + + IF nRun == 0 .OR. nKey == K_ESC + EXIT + ENDIF + + nLastRun := nRun + + nKey := Inkey( 0.1 ) + + ENDDO + + PrintAllDownloaded( aHandles ) + + Cleanup( aHandles, hMulti ) + + RETURN + +STATIC PROCEDURE PrintAllDownloaded( aHandles ) + LOCAL h + + FOR EACH h IN aHandles + + IF h == NIL + LOOP + ENDIF + + ? "Transfer #" + hb_ntos( h:__enumIndex ) + " (" + hb_ntos( curl_easy_getinfo( h, HB_CURLINFO_RESPONSE_CODE ) ) + "): " + IF curl_easy_getinfo( h, HB_CURLINFO_RESPONSE_CODE ) == 0 + ?? "aborted" + ELSE + ?? Left( curl_easy_dl_buff_get( h ), 40 ) + IIF( Len( curl_easy_dl_buff_get( h ) ) > 40, "...", "" ) + ENDIF + NEXT + + RETURN + + +STATIC PROCEDURE Cleanup( aHandles, hMulti ) + LOCAL h + + FOR EACH h IN aHandles + + IF h == NIL + LOOP + ENDIF + + curl_multi_remove_handle( hMulti, h ) + curl_easy_cleanup( h ) + + NEXT + + curl_multi_cleanup( hMulti ) + + curl_global_cleanup() + + RETURN + +STATIC FUNCTION CurGet() + RETURN { Row(), Col() } + +STATIC PROCEDURE CurSet( a ) + + SetPos( a[ 1 ], a[ 2 ] ) + + RETURN