Help with hbcurl

Issues and Discussions related to Harbour

Moderator: Rathinagiri

Post Reply
User avatar
Rathinagiri
Posts: 5411
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 252 times
Been thanked: 305 times
Contact:

Help with hbcurl

Post by Rathinagiri »

I want to use hbcurl to POST/GET to/from web especially https.

Can you please tell me what are the libraries required to be statically linked and working in HMG?
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
Rathinagiri
Posts: 5411
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 252 times
Been thanked: 305 times
Contact:

Post by Rathinagiri »

As of now, I am using hbtip successfully to send and receive data from https secured websites.

But in the Harbour distribution there is a warning which states like the one below. So, I want to switch over to others like hbcurl or MSXML2.ServerXMLHTTP

I have tried both. Both of them I am getting so many errors.

WARNING for HBTIP users
=======================

Due to the excessive amount of problem reports and long known (and unfixed)
problems and due to the non-trivial nature of internet protocols, this document
_strongly recommends to avoid_ using this library in anything production or
in fact anything more serious than simple test code for educational purposes.
Please notice that even if something happens to work in some specific
scenario, it's highly likely it's not a stable solution, nor is it a secure
solution.

The only reason hbtip hasn't been deleted altogether is compatibility
with 3.0, xHarbour and existing projects, plus the fact there are still some
low-level functions that do work fine (tip_MimeType()) or have no better
replacement yet (tip_MailAssemble()).

For internet protocol related tasks, the recommended and supported library
is _hbcurl_, which is a thin wrapper over libcurl's 'easy' API. libcurl is
a highly ubiquitious, very stable, actively and professionally developed,
secure communications library
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

edk
Posts: 708
Joined: Thu Oct 16, 2014 11:35 am
Location: Poland
Has thanked: 193 times
Been thanked: 651 times

Post by edk »

I have done communication with RESTAPI on the server with TLS 1.2 and I use both: cURL and "MSXML2.ServerXMLHTTP".

With "MSXML2.ServerXMLHTTP" I use json data exchange between my application and RESTAPI, while cURL for uploading image files ("MSXML2.ServerXMLHTTP" doesn't send form-data boundary correctly, at least I couldn't make him do it ;) )

Necessary are libraries that support TLS1.2 (those that were previously published on the forum did not work with TLS 1.2, I am attaching new ones - I do not know if they will work with TLS 1.3 also :roll: )
lib.zip
(1.19 MiB) Downloaded 35 times
My configuration:
HBP:

Code: Select all

Myapi.prg
hbziparc.hbc
hbcurl.hbc
HBC:

Code: Select all

inc=yes
head=native
libs=hbmxml mxml libeay32 ssleay32 libwinpthread-1 libcurl zlib1 libidn-11
HMG 3.4.4

Code snippets:

Code: Select all

//Init
BEGIN SEQUENCE WITH {|o| break(o)}
	oApi := Win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
	oApi:setTimeouts(nTimeout * 1000 /* nResolve */ , nTimeout * 1000 /* nConnect*/ , nTimeout * 1000 /* nSend */, nTimeout * 1000 /* nReceive */ )

RECOVER
     MsgStop( "Microsoft XML Core Services (MSXML) 6.0 is not installed."+CRLF+;
          "Download and install MSXML 6.0 from http://msdn.microsoft.com/xml"+CRLF+;
          "before continuing.")
     oApi:=""
END SEQUENCE

IF EMPTY(oApi)
	MsgStop('Error while initialization')
	RETURN Nil
ENDIF

cResp := SendApi( cUrl, hb_jsonEncode( hData , .F. ), oApi )

Msgdebug ( cResp )

cResp := GetApi( cUrl, oApi )

Msgdebug ( cResp )

*********************************************************
Function SendApi( cUrl, cBody, oApi, cMethod )
Local cReturn, cAPIKey :="blablabla"

Default cMethod := "POST"

BEGIN SEQUENCE WITH {|o| break(o)}

	oApi:Open( cMethod, cUrl, .F. )
	oApi:setRequestHeader("Content-Type", "application/json;charset=utf-8")
	oApi:setRequestHeader("client-secret", cAPIKey)
	oApi:Send( cBody )

	IF oApi:Status <> 200
		BREAK "HTTPS status: " + hb_NToS(oApi:status) + " "  +oApi:statusText 
	ENDIF

	cReturn := oApi:ResponseBody()

RECOVER USING oErr
	cReturn := "!ERROR!" + CRLF + IF (ValType(oErr) = 'O', oErr:Description, oErr )
 
END SEQUENCE
 	
RETURN cReturn


**************************************
Function GetApi( cUrl, oApi)
Local cReturn, cAPIKey := "blablabla"

BEGIN SEQUENCE WITH {|o| break(o)}

	oApi:Open( "GET", cUrl, .F. )
	oApi:setRequestHeader("client-secret", cAPIKey)
	
	oApi:Send()
	oApi:WaitForResponse()

	IF oApi:Status <> 200
		BREAK "HTTPS status: " + hb_NToS(oApi:status) + " "  +oApi:statusText 
	ENDIF

	cReturn := oApi:ResponseBody()

RECOVER USING oErr

	cReturn := "!ERORR!" + CRLF + IF (ValType(oErr) = 'O', oErr:Description, oErr )
 
END SEQUENCE
 	
RETURN cReturn

Code: Select all

***********************************************************
* cURL
***********************************************************

//Init
curlHandle := curl_easy_init()
IF EMPTY(curlHandle)
	MsgStop('Error while initialization')
	RETURN Nil
ENDIF

//upload pictures
cResp := SendCurlPictApi( curlHandle, cUrl, cImageFile, hb_HGetDef( hPrd [i0], "id", 0) /* ProductId */ , Str(nIdZdjecia) /* id */, hb_HGetDef( hPrd [i0], "name", "") + " [" + Left( hb_HGetDef( hPrd [i0], "code", ""), 5 ) + "]" /* desc */ )

Msgdebug ( cResp )

curl_global_cleanup( curlHandle )

*********************************************************
Function SendCurlPictApi( curlHandle, cUrl, cImage, idProd, idZdjecia, cOpis )
Local cReturn, cAPIKey := "blablabla"
Local cPlikPict:=hb_FNameNameExt ( cImage )
Local boundary:='------' + hb_StrToHex ( hb_TtoC( hb_DateTime(), 'YYYYMMDDhhmmssfff' ) ) 
Local cFormBody:='--' + boundary + CRLF
Local aHeaders, curlErr
Default cOpis:=""

Do Events

cFormBody+='Content-Disposition: form-data; name="id"' + CRLF + CRLF
cFormBody+=Alltrim(idZdjecia) + CRLF

cFormBody+='--' + boundary + CRLF
cFormBody+='Content-Disposition: form-data; name="productId"' + CRLF + CRLF
cFormBody+=Alltrim(Str(idProd)) + CRLF

cFormBody+='--' + boundary + CRLF
cFormBody+='Content-Disposition: form-data; name="version"' + CRLF + CRLF
cFormBody+='1' + CRLF

cFormBody+='--' + boundary + CRLF
cFormBody+='Content-Disposition: form-data; name="description"' + CRLF + CRLF
cFormBody+=Alltrim (cOpis) + CRLF

cFormBody+='--' + boundary + CRLF

cFormBody+='Content-Disposition: form-data; name="file"; filename="' + cPlikPict + '"' + CRLF
SWITCH Upper(hb_FNameExt ( cImage ))
	CASE "JPG"
		cContentType:='image/jpeg'
		EXIT
	CASE "JPEG"
		cContentType:='image/jpeg'
		EXIT
	CASE "BMP"
		cContentType:='image/bmp'
		EXIT
	CASE "PNG"
		cContentType:='image/png'
		EXIT
	OTHERWISE
		cContentType:='image/jpeg'
END SWITCH
	
cFormBody+='Content-Type: ' + cContentType + CRLF + CRLF

cFormBody+=FILESTR( cImage ) + CRLF

cFormBody+='--' + boundary + '--' + CRLF

DO EVENTS

curl_easy_reset( curlHandle )

aHeaders := {"client-secret: " + cAPIKey, ;
		   "Content-Length: " + Str(Len(cFormBody)) , ;
		   "Content-Type: " + "multipart/form-data; boundary=" + Boundary }


/* Specify the Header  data */
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPHEADER, aHeaders)

curl_easy_setopt( curlHandle, HB_CURLOPT_URL, cUrl )
curl_easy_setopt( curlHandle, HB_CURLOPT_FOLLOWLOCATION, .T. )
curl_easy_setopt( curlHandle, HB_CURLOPT_SSL_VERIFYPEER, .F. )

curl_easy_setopt( curlHandle, HB_CURLOPT_DOWNLOAD )
curl_easy_setopt( curlHandle, HB_CURLOPT_DL_BUFF_SETUP )
	
curl_easy_setopt( curlHandle, HB_CURLOPT_POST, .T. )
curl_easy_setopt( curlHandle, HB_CURLOPT_POSTFIELDSIZE, LEN( cFormBody ) )
curl_easy_setopt( curlHandle, HB_CURLOPT_POSTFIELDS, cFormBody )

curlErr := curl_easy_perform( curlHandle )		/* Do everything */

IF !EMPTY( curlErr )	/* Report any errors */
	cReturn := "!ERROR!" + CRLF + curl_easy_strerror(curlErr)
ELSE
	cReturn := curl_easy_dl_buff_get( curlHandle )
ENDIF
 	
RETURN cReturn
************************************

User avatar
Rathinagiri
Posts: 5411
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 252 times
Been thanked: 305 times
Contact:

Post by Rathinagiri »

OMG! You are a true saviour man!

Let me revert back with my experience. Thanks a lot.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
Rathinagiri
Posts: 5411
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 252 times
Been thanked: 305 times
Contact:

Post by Rathinagiri »

I am getting the following errors.

Code: Select all

d:/hmg.3.5/mingw/bin/../lib/gcc/i686-w64-mingw32/9.3.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -llibwinpthread-1
d:/hmg.3.5/mingw/bin/../lib/gcc/i686-w64-mingw32/9.3.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -llibcurl
d:/hmg.3.5/mingw/bin/../lib/gcc/i686-w64-mingw32/9.3.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -lzlib1
d:/hmg.3.5/mingw/bin/../lib/gcc/i686-w64-mingw32/9.3.0/../../../../i686-w64-mingw32/bin/ld.exe: cannot find -llibidn-11
Can you give me the .a library files also?
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

edk
Posts: 708
Joined: Thu Oct 16, 2014 11:35 am
Location: Poland
Has thanked: 193 times
Been thanked: 651 times

Post by edk »

Unfortunately, I don't have these files. They are not needed if you are compiling with build.bat. I don't compile with the IDE.

User avatar
Rathinagiri
Posts: 5411
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Has thanked: 252 times
Been thanked: 305 times
Contact:

Post by Rathinagiri »

Ok. Let me try with build.bat then.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.

User avatar
vagblad
Posts: 133
Joined: Tue Jun 18, 2013 12:18 pm
DBs Used: MySQL,DBF
Location: Thessaloniki, Greece
Has thanked: 22 times
Been thanked: 28 times

Post by vagblad »

i am going to hijack Rathi's thread here (sorry Rathi) .

I am trying to communicate with a Wordpress/Woocommerce website. Now WooCommerce has a fully functional rest api which you can use to communicate.
So my next thought was curl. I used Edward's example and knowledge(thank you once more Edward) and i am starting the communication like this:
i am trying to get the list of all the products in the WooCommerce installation.Woocommerce API documentation says you need to send a GET request at your website's URL + "/wp-json/wc/v3/products" and you will receive a response in json format.

Code: Select all

curlHandle := curl_easy_init()
aHeaders := { "Content-Type: application/json", "Accept: application/json"}
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPHEADER, aHeaders)
curl_easy_setopt( curlHandle, HB_CURLOPT_SSL_VERIFYPEER, .F. ) //because i am currently running a non-SSl test server
curl_easy_setopt( curlHandle, HB_CURLOPT_USERPWD, cAPIKey + ":" + cAPIKey2) //the woocommerce consumer_key and consumer_secret
curl_easy_setopt( curlHandle, HB_CURLOPT_URL, cUrl ) //The url i mentioned above
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPGET, .T. )
curlErr := curl_easy_perform( curlHandle )

IF !EMPTY( curlErr )	/* Report any errors */
  	cReturn := curl_easy_strerror(curlErr)
ELSE
	cReturn := curl_easy_dl_buff_get( curlHandle )
ENDIF
Now, curlErr returns 0 which means everything went ok. cReturn returns "". Anyone has any idea where can i capture the response in json?Ive been trying to figure it out but to no avail.
In php is easy as $curlErr = curl_exec($curlHandle);
Vagelis Prodromidis
Email: vagblad@gmail.com, Skype: vagblad

edk
Posts: 708
Joined: Thu Oct 16, 2014 11:35 am
Location: Poland
Has thanked: 193 times
Been thanked: 651 times

Post by edk »

vagblad wrote:
Wed Jul 28, 2021 10:35 am
i am going to hijack Rathi's thread here (sorry Rathi) .

I am trying to communicate with a Wordpress/Woocommerce website. Now WooCommerce has a fully functional rest api which you can use to communicate.
So my next thought was curl. I used Edward's example and knowledge(thank you once more Edward) and i am starting the communication like this:
i am trying to get the list of all the products in the WooCommerce installation.Woocommerce API documentation says you need to send a GET request at your website's URL + "/wp-json/wc/v3/products" and you will receive a response in json format.

Code: Select all

curlHandle := curl_easy_init()
aHeaders := { "Content-Type: application/json", "Accept: application/json"}
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPHEADER, aHeaders)
curl_easy_setopt( curlHandle, HB_CURLOPT_SSL_VERIFYPEER, .F. ) //because i am currently running a non-SSl test server
curl_easy_setopt( curlHandle, HB_CURLOPT_USERPWD, cAPIKey + ":" + cAPIKey2) //the woocommerce consumer_key and consumer_secret
curl_easy_setopt( curlHandle, HB_CURLOPT_URL, cUrl ) //The url i mentioned above
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPGET, .T. )
curlErr := curl_easy_perform( curlHandle )

IF !EMPTY( curlErr )	/* Report any errors */
  	cReturn := curl_easy_strerror(curlErr)
ELSE
	cReturn := curl_easy_dl_buff_get( curlHandle )
ENDIF
Now, curlErr returns 0 which means everything went ok. cReturn returns "". Anyone has any idea where can i capture the response in json?Ive been trying to figure it out but to no avail.
In php is easy as $curlErr = curl_exec($curlHandle);
Try something like this (not tested):

Code: Select all

Local aHeaders := { "Content-Type: application/json", "Accept: application/json"}
Local cContent := "", cMethod := "GET"
Local nResult, cResultCode, nResultCode, nCodeHTTP := 0

Local cUrl := "https://your_url" +  "/wp-json/wc/v3/products"
Local cAPIKey := "your consumer_key"
Local cAPIKey2 := "your consumer_secret"

curlHandle := curl_easy_init()

curl_easy_reset( curlHandle )

curl_easy_setopt( curlHandle, HB_CURLOPT_URL, cUrl ) //The url i mentioned above
curl_easy_setopt( curlHandle, HB_CURLOPT_HTTPHEADER, aHeaders)
curl_easy_setopt( curlHandle, HB_CURLOPT_SSL_VERIFYPEER, 0 ) //because i am currently running a non-SSl test server
curl_easy_setopt( curlHandle, HB_CURLOPT_USERPWD, cAPIKey + ":" + cAPIKey2) //the woocommerce consumer_key and consumer_secret

/* Setup response data */
curl_easy_setopt( curlHandle, HB_CURLOPT_DOWNLOAD )
curl_easy_setopt( curlHandle, HB_CURLOPT_DL_BUFF_SETUP )

DO CASE
	CASE cMethod = "POST"
		/* Specify the POST method & data */
		curl_easy_setopt(curlHandle, HB_CURLOPT_POST, 1)
		IF !EMPTY( cContent )
			curl_easy_setopt(curlHandle, HB_CURLOPT_POSTFIELDS, cContent)
		ENDIF
	CASE cMethod = "GET"
		/* Specify the GET method */
		curl_easy_setopt(curlHandle, HB_CURLOPT_HTTPGET, 1)

	CASE cMethod = "PUT"
		/* Specify the PUT method & data */
		curl_easy_setopt(curlHandle, HB_CURLOPT_CUSTOMREQUEST, "PUT")
		IF !EMPTY( cContent )
			curl_easy_setopt(curlHandle, HB_CURLOPT_POSTFIELDS, cContent)
		ENDIF
ENDCASE

 
nResult     := curl_easy_perform( curlHandle )
cResultCode := curl_easy_strerror( nResult )
nCodeHTTP   := curl_easy_getinfo( curlHandle, HB_CURLINFO_RESPONSE_CODE, @nResultCode )

IF nResult <> HB_CURLE_OK .OR. nCodeHTTP <> 200

	ShowWarning ( "Getting products", "Error while getting products.", nResult, cResultCode, nCodeHTTP )

ENDIF

cResponse := curl_easy_dl_buff_get( curlHandle )
msgdebug ( cResponse )


***********************************************************************************************
FUNCTION ShowWarning ( cMessage, cTitle, nResult, cResultCode, nCodeHTTP )
Local nTimeOut := 5000		//5 sekund
MessageBoxTimeout ( cMessage + CRLF + CRLF + "Something went wrong: " + ;
					cResultCode + " (" + AllTrim( Str ( nResult ) ) + ;
					")   HTTP code: " + AllTrim( Str ( nCodeHTTP ) ), cTitle, ;
					MB_OK + MB_ICONERROR + MB_SYSTEMMODAL , nTimeOut )
RETURN

User avatar
vagblad
Posts: 133
Joined: Tue Jun 18, 2013 12:18 pm
DBs Used: MySQL,DBF
Location: Thessaloniki, Greece
Has thanked: 22 times
Been thanked: 28 times

Post by vagblad »

Thank you Edward.
The problem was that i was missing those 2 options:

Code: Select all

curl_easy_setopt( curlHandle, HB_CURLOPT_DOWNLOAD )
curl_easy_setopt( curlHandle, HB_CURLOPT_DL_BUFF_SETUP )
Now i am getting the json response just fine. Thanks once more time!
Vagelis Prodromidis
Email: vagblad@gmail.com, Skype: vagblad

Post Reply