Delphi + Binance Api + Limit Order Problem Invalid signatur - delphi

{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
i use trbinance.com api. I keep getting the same error
procedure TForm1.Button1Click(Sender: TObject);
var
url, sign, queryString, nonce: string;
ST: SystemTime;
DT: TDateTime;
uTime: int64;
sHour, sMin, sSec, sMili: Word;
stream, s_url: string;
Post: TStringList;
IdHTTP10 : TIDhttp;
api_key , api_secret : String;
begin
api_key := '**';
api_secret := '**';
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) +
EncodeTime(ST.wHour, ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nonce:=inttostr(uTime);
url :='https://trbinance.com/open/v1/orders';
queryString := 'symbol=BTT_TRY&side=0&type=1&quantity=1&price=0.0022&recvWindow=5000&timestamp='+nonce;
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
IdHTTP10 := TidHTTP.Create(nil);
IdHTTP10.HandleRedirects := True;
IdHTTP10.Request.CustomHeaders.Add('X-MBX-APIKEY:'+api_key);
IdHTTP10.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
Post:=TStringList.Create;
Post.Add('quantity=1&');
Post.Add('price=0.0022&');
Post.Add('recvWindow=5000&');
Post.Add('timestamp='+nonce+'&');
Post.Add('signature='+sign);
s_url := url + '?symbol=BTT_TRY&side=0&type=1';
try
stream:=IdHTTP10.POST(s_url, Post);
Memo1.Lines.Add(stream);
except
on E: Exception do
Memo1.Lines.Add(TimeToStr(time)+' <---> [Order] error: '+E.Message);
end;
IdHTTP10.Free;
Post.Free;
end;
Always problem :
{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
Normaly Binance Symbol : BTTTRY
Turkey Api : BTT_TRY
Normaly Binance Order Url : httpps://api.binance.com/api/v3/order
Turkey Api Order Url : https://trbinance.com/open/v1/orders
Where is the Problem ?
Thank you so much.

Thank you for your answers. I made improvements to the code.
Friends in need can use this procedure in delphi. ( for Global Binance Api )
procedure TForm1.Button1Click(Sender: TObject);
const
Publickey= '***';
Secretkey= '***';
var
IdHTTP : TIdHTTP;
RBody : TStringstream;
QueryString : String;
Signature : String;
url : String;
begin
queryString :=
'symbol=BTTTRY&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1000&price=0.024400&recvWindow=5000&timestamp='
+ ABDULLAH_GetTimestamp();
Signature := THashSHA2.GetHMAC(queryString, Secretkey, SHA256);
Try
IdHTTP := TIdHTTP.Create();
RBody := TStringStream.Create(queryString + '&signature=' + Signature);
IdHTTP.Request.CustomHeaders.Add('X-MBX-APIKEY:' + Publickey);
IdHTTP.Request.CustomHeaders.UnfoldLines := True;
IdHTTP.Request.Accept := 'application/json';
IdHTTP.Request.ContentType := 'application/json';
IdHTTP.Request.ContentEncoding := 'utf-8';
IdHTTP.Request.BasicAuthentication := True;
IdHTTP.HandleRedirects := True;
IdHTTP.HTTPOptions := [hoKeepOrigProtocol, hoForceEncodeParams,
hoNoProtocolErrorException, hoWantProtocolErrorContent];
IdHTTP.iohandler := IdSSLIOHandlerSocketOpenSSL1;
url := 'https://api.binance.com/api/v3/order?';
Try
Memo1.Lines.Add(IdHTTP.Post(url, RBody));
Except
ON E: EXCEPTION DO
Memo1.Lines.Add('Error Message:' + E.Message);
End;
Finally
FreeAndNil(IdHTTP);
FreeAndNil(RBody);
End;
end;
This code works for Global Binance Api.
Change Symbol : BTTTRY -> BTT_TRY
And Post Url : https://api.binance.com/api/v3/order -> https://trbinance.com/open/v1/orders
İt's not working and Result :
{"code":3702,"msg":"Invalid signature.","timestamp":1723648673412}
This Procedure works in 'Global Binance Api' but not working 'trbinance Api'
Same procedure gives 'invalid signature' error in 'trbinance api'. :(
trbinance api example :
HMAC SHA256 signature:
[linux]$ echo -n "symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500&timestamp=1581720670624&recvWindow=5000" | openssl dgst -sha256 -hmac "cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV"
(stdin)= 33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa
curl command:
(HMAC SHA256)
[linux]$ curl -H "X-MBX-APIKEY: cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV" -X POST 'https://www.trbinance.com/open/v1/orders' -d 'symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500&timestamp=1581720670624&recvWindow=5000&signature=33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa'

There are a number of issues with this code, but the biggest one I see is that you should NOT be including trailing & characters in your TStringList strings, eg:
Post:=TStringList.Create;
Post.Add('quantity=1');
Post.Add('price=0.0022');
Post.Add('recvWindow=5000');
Post.Add('timestamp='+nonce);
Post.Add('signature='+sign);
TIdHTTP.Post() will insert those & characters into the request body for you, thus the data being transmitted is different than the data you signed.

According to Binance TR API docs:
Use your secretKey as the key and totalParams as the value for the HMAC operation.
totalParams is defined as the query string concatenated with the request body.
Error from API with the text "Invalid signature." could give you a clue that the problem is with your signature algo:
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
Your code here signs only your query string, but according to API docs you must sign query string concatenated with the request body.
So, you need to change your code like this ("Post" variable must be initialized before this line):
sign := THashSHA2.GetHMAC(queryString + Post.text, api_secret, SHA256);

I found that using the TIdHTTP component in Delphi to connect to Binance has problems. I kept getting Http/1.1 400 Bad Request.
I switched to TNetHTTPClient instead to connect with Binance.
The following is the working program that can be used for placing an order on Binance:
uses
System.Net.HttpClient, System.Hash, DateUtils;
procedure TForm1.fapiorder(oSide: String; oCoin: String; oAmt: Integer);
var
sOrderAmt: string;
ClearEntryUrl: string;
Params: string;
Signature: string;
URL: string;
ResponseStream : TStringStream;
postStream: TStringStream;
uTime: int64;
nowDateTime: string;
ST: SystemTime;
DT: TDateTime;
HttpClient: THttpClient;
HttpResponse: IHttpResponse;
BaseURLUSDM, ApiKey, ApiSecret: string;
begin
//https://binance-docs.github.io/apidocs/futures/cn/#trade-3
BaseURLUSDM := 'https://fapi.binance.com/'; //合約永續
ClearEntryUrl := 'fapi/v1/order';
ApiSecret := 'YOUR API SECRET KEY';
//timestamp 開始
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) + EncodeTime(ST.wHour,
ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nowDateTime := IntToStr(uTime);
Writeln('DateTime = ' + nowDateTime);
//timestamp 結束
Params := 'recvWindow=3000&timestamp=' + nowDateTime;
if (TDCoinMap.TryGetValue(oCoin, CoinMap) = True) then
begin
sOrderAmt := '0.07';
if (StrToFloat(sOrderAmt) <> 0) then
begin
Params := Params + '&symbol=' + oCoin;
Params := Params + '&side=' + oSide;
Params := Params + '&type=MARKET';
Params := Params + '&quantity=' + sOrderAmt;
Signature := THashSHA2.GetHMAC(Params, ApiSecret, SHA256);
URL := BaseURLUSDM + ClearEntryUrl + '?' + Params + '&signature=' + Signature;
postStream := TStringStream.Create('');
ResponseStream := TStringStream.Create('');
try
HttpClient := THTTPClient.Create;
HttpClient.HandleRedirects := True;
HttpClient.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0';
HttpClient.ContentType := 'application/x-www-form-urlencoded';
HttpClient.CustomHeaders['X-MBX-APIKEY'] := 'Your Binance APIKEY';
HttpResponse := HttpClient.Post(URL, ResponseStream);
jResult := HttpResponse.ContentAsString();
Writeln('jResult = ' + jResult);
finally
HttpClient.Free;
end;
end;
end;
end;

Related

Delphi: TOAuth2Authenticator with client_credentials

A while ago I wrote a method in Delphi 2010 to get the OAuth2 token using the Indy components (TidHttp). See code below.
I am now doing something new in Delphi 10.4 and would like to use the REST components such as RESTClient, RESTRequest, TOAuth2Authenticator, etc.
Our grant type is Client Credentials but in none of the examples on the net could I find on how to use TOAuth2Authenticator with Client Credentials. Is it even possible?
We have a client id, client secret and token URL. We do not have authorization or redirect endpoints. In Insomnia, the setup will look like this:
Does somebody know how to get the token using TOAuth2Authenticator with grant type = client_credentials?
Here is the Delphi 2010 code:
procedure TfrmToken.btnGetTokenClick(Sender: TObject);
var
IdHTTP: TidHttp;
lsHttpError: string;
loRequest: TStringStream;
loRespJson: TMemoryStream;
liSuper: iSuperObject;
ldtExpiry: TDateTime;
begin
IdHTTP := TIdHTTP.Create();
loRespJson := TMemoryStream.Create();
try
IdHTTP.HandleRedirects := False;
loRequest := TStringStream.Create('grant_type=client_credentials&client_id=' +
edtKey.Text + '&client_secret='+edtSecret.Text);
try
IdHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
IdHttp.Request.ContentType := 'application/x-www-form-urlencoded';
try
IdHTTP.Post(edtURL.Text, loRequest, loRespJson);
except
on E: EIdHTTPProtocolException do
begin
lsHttpError := E.ErrorMessage;
end;
on E: Exception do
begin
lsHttpError := E.Message;
end;
end;
if idHTTP.ResponseCode = 200 then
begin
liSuper := SO(StreamToString(loRespJSon));
edtToken.Text := liSuper.S['access_token'];
ldtExpiry := IncSecond(Now, liSuper.i['expires_in']);
edtExpiry.Text := 'Expires in ' + liSuper.S['expires_in'] +
' seconds. Time: ' +
FormatDateTime('yyyy/dd/mm hh:nn:ss', ldtExpiry);
end
else
begin
liSuper := SO(lsHttpError);
edtToken.Text := IdHTTP.ResponseText;
edtExpiry.Text := '';
end;
finally
FreeAndNil(loRequest);
end;
finally
FreeAndNil(IdHTTP);
FreeAndNil(loRespJson);
end;
end;

Delphi - idHTTP upload file using TIdMultipartFormDataStream

I'm trying to upload a file using TIdHTTP. The problem is the access token gets changed when the request is sent to the server.
The access token that I'm using is fJNhDM6TlcpeVmD8h3jFuPJS71sxwZB8bZBXajTRB5TNAcRa6PNXfv4J7mPxIvMdMhjy7oKdTLbsRYthpBCCqGVkj4vlojJ4BRBkLAVIBJ1DZAnMZD
The API returns
HTTP/1.1 400 Bad Request
OAuth "invalid_token" "Malformed access token fJNhDM6TlcpeVmD8h3jFu=\r\nPJS71sxwZB8bZBXajTRB5TNAcRa6PNXfv4J7mPxIvMdMhjy7oKdTLbsRYthpBCCqGVkj4v=\r\nlojJ4BRBkLAVIBJ1DZAnMZD"
There is =\r\n added to my token twice.
My code is:
function TFoo.Post(const AToken, guID, AMessage, AImageFileName: string): Boolean;
var
lParam : TIdMultipartFormDataStream;
begin
Result := False;
if not FileExists(AImageFileName) then begin
LastError := 'File not found ' + AImageFileName;
Exit;
end;
ProxyCheck;
lParam := TIdMultipartFormDataStream.Create;
try
lParam.AddFormField('message', AMessage);
lParam.AddFormField('access_token', AToken);
lParam.AddFile('source', AImageFileName);
idHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
try
idHTTP.Post( UrlAPI + guID + '/photos', lParam);
Result := True;
except;
LastError := idHTTP.ResponseText + sLineBreak + idHTTP.Response.WWWAuthenticate.Text;
end;
finally
lParam.Free;
end;
end;
What am I missing here ?
By default, AddFormField() sets the TIdFormDataField.ContentTransfer property to MIME's quoted-printable format. That is where the extra =\r\n is coming from. It is a "soft" line break being inserted by quoted-printable every 76 characters. Any server that supports quoted-printable would remove "soft" line breaks during decoding. But maybe your server does not.
If you want to disable the quoted-printable behavior, you can set the ContentTransfer property to either:
a blank string:
lParam.AddFormField('access_token', AToken).ContentTransfer := '';
'7bit' (since it does not contain any non-ASCII characters):
lParam.AddFormField('access_token', AToken).ContentTransfer := '7bit';
'8bit' or binary:
lParam.AddFormField('access_token', AToken).ContentTransfer := '8bit';
lParam.AddFormField('access_token', AToken).ContentTransfer := 'binary';
In this case, I would suggest #1.
On a side note, do not set the HTTP content type when posting a TIdMultipartFormDataStream. Not only are you using the wrong media type to begin with (it should be multipart/form-data instead), but the TIdMultipartFormDataStream version of Post() will simply overwrite it anyway.
function TFoo.Post(const AToken, guID, AMessage, AImageFileName: string): Boolean;
var
lParam : TIdMultipartFormDataStream;
begin
Result := False;
if not FileExists(AImageFileName) then begin
LastError := 'File not found ' + AImageFileName;
Exit;
end;
ProxyCheck;
lParam := TIdMultipartFormDataStream.Create;
try
lParam.AddFormField('message', AMessage);
lParam.AddFormField('access_token', AToken).ContentTransfer := '';
lParam.AddFile('source', AImageFileName);
try
idHTTP.Post(UrlAPI + guID + '/photos', lParam);
Result := True;
except;
LastError := idHTTP.ResponseText + sLineBreak + idHTTP.Response.WWWAuthenticate.Text;
end;
finally
lParam.Free;
end;
end;

Send emoji with indy delphi7

i want to send emoji with indy 9.00.10 on delphi 7. i use tnt VCL Controls .
i found this url http://apps.timwhitlock.info/emoji/tables/unicode for unicode and bytes code.
how to convert this codes to delphi Constants for Send with indy.
i use this delphi code for send message to telegram bot:
procedure TBotThread.SendMessage(ChatID:String; Text : WideString;
parse_mode:string;disable_notification:boolean);
Var
Stream: TStringStream;
Params: TIdMultipartFormDataStream;
//Text : WideString;
msg : WideString;
Src : string;
LHandler: TIdSSLIOHandlerSocket;
begin
try
try
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
msg := '/sendmessage';
Stream := TStringStream.Create('');
Params := TIdMultipartFormDataStream.Create;
Params.AddFormField('chat_id',ChatID);
if parse_mode <> '' then
Params.AddFormField('parse_mode',parse_mode);
if disable_notification then
Params.AddFormField('disable_notification','true')
else
Params.AddFormField('disable_notification','false');
Params.AddFormField('disable_web_page_preview','true');
Params.AddFormField('text',UTF8Encode(Text));
LHandler := TIdSSLIOHandlerSocket.Create(nil);
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler:=LHandler;
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmUnassigned;
FidHttpSend.HandleRedirects := true;
FidHttpSend.Post(BaseUrl + API + msg, Params, Stream);
finally
Params.Free;
Stream.Free;
ENd;
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
end;
bytes sample for emojies:
AERIAL_TRAMWAY = '\xf0\x9f\x9a\xa1';
AIRPLANE = '\xe2\x9c\x88';
ALARM_CLOCK = '\xe2\x8f\xb0';
ALIEN_MONSTER = '\xf0\x9f\x91\xbe';
sorry for bad english!!!
The Telegram Bot API supports several forms of input:
We support GET and POST HTTP methods. We support four ways of passing parameters in Bot API requests:
URL query string
application/x-www-form-urlencoded
application/json (except for uploading files)
multipart/form-data (use to upload files)
You are using the last option.
Indy 9 does not support Delphi 2009+ or Unicode. All uses of string are assumed to be AnsiString, which is the case in Delphi 7. Any AnsiString you add to TIdMultipartFormDataStream or TStrings, even a UTF-8 encoded one, will be transmitted as-is by TIdHTTP. However, there is no option to specify to the server that the string data is actually using UTF-8 as a charset. But, according to the docs:
All queries must be made using UTF-8.
So not specifying an explicit charset might not be problem.
If you still have problems with multipart/form-data, then consider using application/x-www-form-urlencoded (use TIdHTTP.Post(TStrings)) or application/json (use TIdHTTP.Post(TStream)) instead:
procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
var
Params: TStringList;
LHandler: TIdSSLIOHandlerSocket;
begin
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
Params := TStringList.Create;
try
Params.Add('chat_id=' + UTF8Encode(ChatID));
if parse_mode <> '' then
Params.Add('parse_mode=' + UTF8Encode(parse_mode));
if disable_notification then
Params.Add('disable_notification=true')
else
Params.Add('disable_notification=false');
Params.Add('disable_web_page_preview=true');
Params.Add('text=' + UTF8Encode(Text));
LHandler := TIdSSLIOHandlerSocket.Create(nil);
try
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmClient;
FidHttpSend.HandleRedirects := true;
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler := LHandler;
try
try
FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
finally
FidHttpSend.IOHandler := nil;
end;
finally
LHandler.Free;
end;
finally
Params.Free;
end;
end;
procedure TBotThread.SendMessage(ChatID: String; Text: WideString; parse_mode: string; disable_notification: boolean);
var
Params: TStringStream;
LHandler: TIdSSLIOHandlerSocket;
function JsonEncode(const wStr: WideString): string;
var
I: Integer;
Ch: WideChar;
begin
// JSON uses UTF-16 text, so no need to encode to UTF-8...
Result := '';
for I := 1 to Length(wStr) do
begin
Ch := wStr[i];
case Ch of
#8: Result := Result + '\b';
#9: Result := Result + '\t';
#10: Result := Result + '\n';
#12: Result := Result + '\f';
#13: Result := Result + '\r';
'"': Result := Result + '\"';
'\': Result := Result + '\\';
'/': Result := Result + '\/';
else
if (Ord(Ch) >= 32) and (Ord(Ch) <= 126) then
Result := Result + AnsiChar(Ord(wStr[i]))
else
Result := Result + '\u' + IntToHex(Ord(wStr[i]), 4);
end;
end;
end;
begin
if FShowBotLink then
Text := Text + LineBreak + FBotUser;
Params := TStringStream.Create('');
try
Params.WriteString('{');
Params.WriteString('chat_id: "' + JsonEncode(ChatID) + '",');
if parse_mode <> '' then
Params.WriteString('parse_mode: "' + JsonEncode(parse_mode) + '",')
if disable_notification then
Params.WriteString('disable_notification: True,')
else
Params.WriteString('disable_notification: False,');
Params.WriteString('disable_web_page_preview: True,');
Params.WriteString('text: "' + JsonEncode(Text) + '"');
Params.WriteString('}');
Params.Position := 0;
LHandler := TIdSSLIOHandlerSocket.Create(nil);
try
LHandler.SSLOptions.Method := sslvTLSv1;
LHandler.SSLOptions.Mode := sslmClient;
FidHttpSend.HandleRedirects := true;
FidHttpSend.ReadTimeout := 30000;
FidHttpSend.IOHandler := LHandler;
try
try
FidHttpSend.Request.ContentType := 'application/json';
FidHttpSend.Post(BaseUrl + API + '/sendmessage', Params, TStream(nil));
except
on E: EIdHTTPProtocolException do
begin
if E.ReplyErrorCode = 403 then
begin
WriteToLog('Bot was blocked by the user');
end;
end;
end;
finally
FidHttpSend.IOHandler := nil;
end;
finally
LHandler.Free;
end;
finally
Params.Free;
end;
end;
That being said, your function's Text parameter is a WideString, which uses UTF-16, so you should be able to send any Unicode text, including emojis. If you are trying to generate text in your code, just make sure you UTF-16 encode any non-ASCII characters correctly. For example, codepoint U+1F601 GRINNING FACE WITH SMILING EYES is wide chars $D83D $DE01 in UTF-16:
var
Text: WideString;
Text := 'hi ' + #$D83D#$DE01; // 'hi 😁'
SendMessage('#channel', Text, 'Markup', False);
Alternatively, you can use HTML in your text messages, so you can encode non-ASCII characters using numerical HTML entities. According to the docs:
All numerical HTML entities are supported.
Codepoint U+1F601 is numeric entity $#128513; in HTML:
var
Text: WideString;
Text := 'hi $#128513;'; // 'hi 😁'
SendMessage('#channel', Text, 'HTML', False);

Indy multipart/form-data example [duplicate]

I have a simple php script on my web server which I need to upload a file using HTTP POST, which I am doing in Delphi.
Here is my code with Indy but aparantely it won't work and I can't figure out what i am not doing properly. How can I view what I send on the server is there such a tool ?
procedure TForm1.btn1Click(Sender: TObject);
var
fname : string;
MS,dump : TMemoryStream;
http : TIdHTTP;
const
CRLF = #13#10;
begin
if PromptForFileName(fname,'','','','',false) then
begin
MS := TMemoryStream.Create();
MS.LoadFromFile(fname);
dump := TMemoryStream.Create();
http := TIdHTTP.Create();
http.Request.ContentType:='multipart/form-data;boundary =-----------------------------7cf87224d2020a';
fname := CRLF + '-----------------------------7cf87224d2020a' + CRLF + 'Content-Disposition: form-data; name=\"uploadedfile\";filename=\"test.png"' + CRLF;
dump.Write(fname[1],Length(fname));
dump.Write(MS.Memory^,MS.Size);
fname := CRLF + '-----------------------------7cf87224d2020a--' + CRLF;
dump.Write(fname[1],Length(fname));
ShowMessage(IntToStr(dump.Size));
MS.Clear;
try
http.Request.Method := 'POST';
http.Post('http://posttestserver.com/post.php',dump,MS);
ShowMessage(PAnsiChar(MS.Memory));
ShowMessage(IntToStr(http.ResponseCode));
except
ShowMessage('Could not bind socket');
end;
end;
end;
Indy has TIdMultipartFormDataStream for this purpose:
procedure TForm1.SendPostData;
var
Stream: TStringStream;
Params: TIdMultipartFormDataStream;
begin
Stream := TStringStream.Create('');
try
Params := TIdMultipartFormDataStream.Create;
try
Params.AddFile('File1', 'C:\test.txt','application/octet-stream');
try
HTTP.Post('http://posttestserver.com/post.php', Params, Stream);
except
on E: Exception do
ShowMessage('Error encountered during POST: ' + E.Message);
end;
ShowMessage(Stream.DataString);
finally
Params.Free;
end;
finally
Stream.Free;
end;
end;
Calling a PHP from Indy can fail because of the User-Agent, then you get 403 error.
Try this way, it fixed it for me:
var Answer: string;
begin
GetHTML:= TIdHTTP.create(Nil);
try
GetHTML.Request.UserAgent:= 'Mozilla/3.0';
Answer:= GetHTML.Get('http://www.testserver.com/test.php?id=1');
finally
GetHTML.Free;
end;
end;
You lost 2 characters '--'. It is better to do so:
http.Request.ContentType:='multipart/form-data;boundary='+myBoundery;
fname := CRLF + '--' + myBoundery + CRLF + 'Content-Disposition: form-data; name=\"uploadedfile\";filename=\"test.png"' + CRLF;

HTTPS post - what I'm doing wrong?

I'm making requests to the webaddress to get XML files throught the HTTPS connection. But this connection works like 50%. In most cases it fails. Usual error is "socket error #10060". Or "Error connecting with SSL. EOF was observed that violates the protocol". What I'm doing wrong?
function SendRequest(parameters: string): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TStringStream;
xDoc: IXMLDocument;
begin
sPostData := TStringList.Create;
try
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add(parameters);
sHttpSocket := TIdHTTP.Create;
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Request.Method := 'POST';
resStream := TStringStream.Create;
sHttpSocket.Post(Self.sUrl, sPostData, resStream);
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
resStream.Free;
sHttpSocket.Free;
sshSocketHandler.Free;
sPostData.Free;
except on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end
end;
end;
Maybe I can do this in another way, that works like 100%, when internet connection is available?
Regards,
evilone
An "EOF" error suggests you are connnecting to a server that is not actually using SSL to begin with, or the SSL data may be corrupted.
Besides that, why are you including explicit '&' characters between your post data parameters? Don't do that, Indy will just encode them and send its own '&' characters. Also, consider using TMemoryStream instead of TStringStream to ensure IXMLDocumect.LoadFromStream() is loading the server's original raw XML data as-is, and not an altered version that the RTL/VCL produces due to Unicode handling (TStringStream is TEncoding-enabled).
Edit: Given the URL you provided, an example of calling verifyUser() would look like this:
const
ERPLYAccountCode = '...';
function verifyUser(const user, pass: string; const sessionLength: Integer = 3600): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TMemoryStream;
xDoc: IXMLDocument;
begin
Result := nil;
try
resStream := TMemoryStream.Create;
try
sPostData := TStringList.Create;
try
sPostData.Add('clientCode=' + ERPLYAccountCode);
sPostData.Add('request=verifyUser');
sPostData.Add('version=1.0');
sPostData.Add('responseType=XML');
sPostData.Add('responseMode=normal');
sPostData.Add('username=' + user);
sPostData.Add('password=' + pass);
sPostData.Add('sessionLength=' + IntToStr(sessionLength));
sHttpSocket := TIdHTTP.Create;
try
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(sHttpSocket);
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Post('https://www.erply.net/api/', sPostData, resStream);
finally
sHttpSocket.Free;
end;
finally
sPostData.Free;
end;
resStream.Position := 0;
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
finally
resStream.Free;
end;
except
on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end;
end;
end;

Resources