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( <hMulti>[, <aHandles> ]) -> <aResults>
* 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( <hMulti>[, <aHandles> ]) -> <aResults>
with a possibility to return real GC pointer of specific
transfer related to reported event. <aHandles> should be
specified if this is expected.
+ contrib/hbcurl/tests/multi.prg
+ demo of the additions
This commit is contained in:
@@ -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( <hMulti>[, <aHandles> ]) -> <aResults>
|
||||
|
||||
* 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( <hMulti>[, <aHandles> ]) -> <aResults>
|
||||
with a possibility to return real GC pointer of specific
|
||||
transfer related to reported event. <aHandles> 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
|
||||
|
||||
@@ -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 ) )
|
||||
|
||||
@@ -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 <array> passed to curl_multi_info_read(, <array> ) */
|
||||
#define HB_CURLMSG_RESP_LAST HB_CURLMSG_RESP_HPOS
|
||||
|
||||
#endif /* HBCURL_CH_ */
|
||||
|
||||
156
contrib/hbcurl/tests/multi.prg
Normal file
156
contrib/hbcurl/tests/multi.prg
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user