I'm attempting to work with the Google Drive API in Delphi XE2, and thus far, I have just about everything working. One thing I'm struggling with is the multipart upload. When I attempt to upload a file, I get a 503 error. Normally, that should indicate a problem with the server. However, when I send an upload request with the same body and headers to the same URL using fiddler rather than my API, the file is uploaded successfully. This tells me there has to be a problem with my code. This particular function is a mess, but here it is.
function TGoogleDriveApi.MultipartUpload(aStream : TStream; aTitle : string = 'Untitled';
aDescription : string = ''; mimeType : string = 'application/octet-stream';
indexableText : IGoogleDriveIndexableText = nil;
lastViewedByMeDate : string = ''; modifiedDate : string = '';
parents : IGoogleDriveParentList = nil) : IGoogleDriveFile;
var
json, url, body, boundry, contentType : string;
ss : TStringStream;
ms : TMemoryStream;
lHttpHelper : IHttpHelper;
streamBytes : TBytes;
begin
boundry := 'a_boundry';
body := '--'+boundry+sLineBreak;
body := body + 'Content-Type: application/json; charset=UTF-8';
lHttpHelper := THttpHelper.Create;
if(Token.IsExpired) then
Token.Refresh(TokenUrl, OAuth.ClientId, OAuth.ClientSecret);
url := 'https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart&access_token='+lHttpHelper.UrlEncode(Token.access_token);
json := '{';
json := json + '"title": "'+aTitle+'"';
if(aDescription <> '') then
json := json + ', "description": "'+aDescription+'"';
if(lastViewedByMeDate <> '') then
json := json + ', "lastViewedByMeDate": "'+lastViewedByMeDate+'"';
json := json + ', "mimeType": "'+mimeType+'"';
if(modifiedDate <> '') then
json := json + ', "modifiedDate": "'+modifiedDate+'"';
json := json + '}';
body := body + sLineBreak + sLineBreak + json + sLineBreak + sLineBreak + '--'+boundry;
body := body + sLineBreak + 'Content-Type: '+mimeType + sLineBreak + sLineBreak;
body := body + 'some test text from the api' + sLineBreak + sLineBreak + '--'+boundry+'--';
ss := TStringStream.Create;
ss.WriteString(body);
ss.Position := 0;
ms := TMemoryStream.Create;
ms.Write(ss,ss.Size);
SetLength(streamBytes,aStream.Size);
aStream.Read(streamBytes,aStream.Size);
ms.Write(streamBytes[0],aStream.Size);
ss.Clear;
ss.Position := 0;
ss.WriteString(sLineBreak + sLineBreak + '--'+boundry+'--');
ss.Position := 0;
ms.Write(ss,ss.Size);
contentType := 'multipart/related; boundary="'+boundry+'"';
json := lHttpHelper.PostResponse(url,contentType,ms);
FreeAndNil(ss);
FreeAndNil(ms);
Result := nil;
end;
The line that causes problems is the lHttpHelper.PostResponse call. The code for that is shown here:
function THttpHelper.PostResponse(url, contentType : string; aStream : TStream) : string;
var
lHTTP: TIdHTTP;
lStream : TStringStream;
handler : TIdSSLIOHandlerSocketOpenSSL;
begin
lStream := TStringStream.Create;
lHTTP := TIdHTTP.Create(nil);
handler := TidSSLIOHandlerSocketOpenSSL.Create(nil);
handler.SSLOptions.Method := sslvSSLv3;
lHTTP.IOHandler := handler;
try
lHTTP.Request.ContentType := contentType;
lHTTP.Post(url,aStream,lStream);
lStream.Position := 0;
Result := lStream.ReadString(lStream.Size);
//except
finally
lStream.Free;
lHTTP.Free;
handler.Free;
lStream := nil;
lHTTP := nil;
handler := nil;
end;
end;
I'm currently calling the MultipartUpload function from my test, shown here
procedure TestIGoogleDriveApi.TestMultipartUpload;
var
ReturnValue : IGoogleDriveFile;
fs : TFileStream;
begin
fs := TFileStream.Create('testupload.jpg',fmOpenRead);
ReturnValue := FIGoogleDriveApi.MultipartUpload(fs,'Test Multipart Image Upload from API.txt','test file','image/jpeg');
FreeAndNil(fs);
if(ReturnValue = nil) then
fail('ReturnValue cannot be nil');
end;
Any ideas what might be causing the problems? I'm not even sure what to suspect at this point.
You'll need to somehow follow #RobKennedy's advice. You need to see what's happening on the wire to debug this. Try replacing the https drive url with http and then trace with Wireshark.
#Hendra is also correct, that generally your app needs to implement exponential backoff and retry for 500 errors, although I suspect that isn't your specific problem (yet :-))
After changing two things in my code, and doing some substantial cleanup, I was able to get the upload working. The main changes I made were writing everything directly to the TMemoryStream rather than to a TStringStream then to a TMemoryStream and using AnsiStrings rather than strings. Here is my cleaned up code.
function TGoogleDriveApi.MultipartUpload(aStream: TStream; aFile: TGoogleDriveFileUp) : IGoogleDriveFile;
var
json, url, boundary, body : AnsiString;
lHttpHelper : IHttpHelper;
ms : TMemoryStream;
ss : TStringStream;
writer : IRtSerializeWriter;
reader : IRtSerializeReader;
begin
if(Token.IsExpired) then
Token.Refresh(TokenUrl, OAuth.ClientId, OAuth.ClientSecret);
lHttpHelper := THttpHelper.Create;
ss := TStringStream.Create;
writer := TRtJsonSerializeWriter.Create;
writer.SaveToStream(ss,aFile);
ss.Position := 0;
json := ss.ReadString(ss.Size);
FreeAndNil(ss);
url := 'https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart&access_token='+lHttpHelper.UrlEncode(Token.access_token);
boundary := 'a_bondary';
body := '--'+boundary+sLineBreak;
body := body + 'Content-Type: application/json; charset=UTF-8' + sLineBreak + sLineBreak;
body := body + json + sLineBreak + sLineBreak +'--'+boundary;
body := body + sLineBreak + sLineBreak;
ms := TMemoryStream.Create;
ms.Write(body[1],Length(body));
ms.CopyFrom(aStream, aStream.Size);
body := sLineBreak + sLineBreak + '--' + boundary + '--';
ms.Write(body[1],Length(body));
ms.Position := 0;
json := lHttpHelper.PostResponse(url,'multipart/related; boundary="'+boundary+'"',ms);
ss := TStringStream.Create;
ss.WriteString(json);
ss.Position := 0;
reader := TRtJsonSerializeReader.Create;
Result := TGoogleDriveFileDown.Create;
reader.LoadFromStream(ss,Result as TGoogleDriveFileDown);
FreeAndNil(ms);
FreeAndNil(ss);
end;
Related
I have tested POST function in PostMan to do POST function with body parameters as below:
Here is eBay's document for this function:
HTTP method: POST
URL (Sandbox): https://api.sandbox.ebay.com/identity/v1/oauth2/token
HTTP headers:
Content-Type = application/x-www-form-urlencoded
Authorization = Basic <B64-encoded-oauth-credentials>
Request body:
grant_type=authorization_code
code=<authorization-code-value>
redirect_uri=<RuName-value>
My first attempt was as follow:
function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
xRequestBody: TStringStream;
begin
with objHTTP.Request do begin
CustomHeaders.Clear;
ContentType := 'application/x-www-form-urlencoded';
CustomHeaders.Values['Authorization'] := 'Basic ' + 'V2VpbmluZ0MtV2VpbmluZ0M';
end;
xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + ','
+ 'code=' + 'v%5E1.1%23i%5E1%23f%5E0%23r%5E1%23I%5E3%23p%5E3' + ','
+ 'redirect_uri=' + 'myredirecturlnameinsandbox',
TEncoding.UTF8);
try
try
Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
except
on E: Exception do
ShowMessage('Error on request: ' + #13#10 + e.Message);
end;
finally
xRequestBody.Free;
end;
end;
Second attempt tried with below code for Body
xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + '&'
+ 'code=' + AAuthCode + '&'
+ 'redirect_uri=' + gsRuName,
TEncoding.UTF8);
Both attempts give HTTP/1.1 400 Bad Request.
I have done some searching in Stack Overflow, and this is the closest question. The only different part is body of POST.
IdHTTP how to send raw body
Can anyone please advise me what is correct way to assign POST body part?
Thanks.
The preferred way to send an application/x-www-form-urlencoded request with TIdHTTP is to use the overloaded TIdHTTP.Post() method that takes a TStrings as input. You are not sending your TStringStream data in the proper application/x-www-form-urlencoded format.
You don't need to use the TIdHTTP.Request.CustomHeaders property to setup Basic authorization. TIdHTTP has built-in support for Basic, simply use the TIdHTTP.Request.UserName and TIdHTTP.Request.Password properties as needed, and set the TIdHTTP.Request.BasicAuthentication property to true.
Try this instead:
function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
xRequestBody: TStringList;
begin
with objHTTP.Request do
begin
Clear;
ContentType := 'application/x-www-form-urlencoded';
BasicAuthentication := True;
UserName := ...;
Password := ...;
end;
xRequestBody := TStringList.Create;
try
xRequestBody.Add('grant_type=' + 'authorization_code');
xRequestBody.Add('code=' + AAuthCode);
xRequestBody.Add('redirect_uri=' + 'myredirecturlnameinsandbox');
try
Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
except
on E: Exception do
ShowMessage('Error on request: ' + #13#10 + e.Message);
end;
finally
xRequestBody.Free;
end;
end;
If you want to send your own TStringStream, try this instead:
function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
xRequestBody: TStringStream;
begin
with objHTTP.Request do
begin
Clear;
ContentType := 'application/x-www-form-urlencoded';
BasicAuthentication := True;
UserName := ...;
Password := ...;
end;
xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + '&'
+ 'code=' + TIdURI.ParamsEncode(AAuthCode){'v%5E1.1%23i%5E1%23f%5E0%23r%5E1%23I%5E3%23p%5E3'} + '&'
+ 'redirect_uri=' + 'myredirecturlnameinsandbox',
TEncoding.UTF8);
try
try
xRequestBody.Position := 0;
Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
except
on E: Exception do
ShowMessage('Error on request: ' + #13#10 + e.Message);
end;
finally
xRequestBody.Free;
end;
end;
I was troubled by this problem, but the answer didn't work very much. Later, I used packet capturing to see the actual content sent. For friends with the same problems.
function TFormMain.init_web_account(): Boolean;
var
strTmp: Ansistring;
PostStringData: TstringStream;
idhttp1: Tidhttp;
json, json_array1: TQjson;
itype_version: integer;
ifunction_version: integer;
s: string;
url: ansistring;
idMultiPartFormDataStream:TIdMultiPartFormDataStream;
begin
try
result := False;
idhttp1 := TIdHTTP.Create();
PostStringData := TStringStream.Create;
//init_string http://x.x.x.x:8000/user/key?ke1=v1&k2=v2
strTmp := //
'strdogsoftid=' + edtDogNumber.Text + //
'&chipid=' + '' + //
'&status=' + '0' + //
'®_user_name=' + edtRegName.Text + //
'®_init_date=' + formatdatetime('yyyy-mm-dd', dtpRegCodeGenDate.Date) + //
'&lease_expiration=' + formatdatetime('yyyy-mm-dd', dtp_lease_expiration.Date) + //
'&renew_last_date=' + '' + //
'&mobile=' + '' + //
'&weixin=' + '' + //
'&memo=' + '' + //
'&function_version=' + IntToStr(rgVersion.ItemIndex) + //
'&type_version=' + IntToStr(itype_version); //
url := 'http://x.x.x.x:8000/user/' + edtDogNumber.Text;
PostStringData.Clear;
PostStringData.WriteString(strTmp);
try
idhttp1.Request.ContentType := 'application/x-www-form-urlencoded';
s := idhttp1.Post(url, PostStringData);
result := True;
except
on E: Exception do
begin
showmessage(s);
result := False;
exit;
end;
end;
finally
PostStringData.Free;
idhttp1.Free;
end;
end;
If you use TNetHTTPClient you can use the "System.Net.Mime" unit to manage the multipart form data by HTTP Post and manage request too.
I use Delphi 10.4.2
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);
I am using HttpCli component form ICS to upload files using Delphi 7 ..
i copied code from example in the ICS folder .. tried to edit to let me upload files but got stuck on how to add the file to the file field name (filedata=c:\1.rar)
var
lData: TStringStream;
lDataOut : TMemoryStream;
lResultPage: string;
HttpCli1: THttpCli;
srv : string;
Data : AnsiString;
begin
HttpCli1 := THttpCli.Create(nil);
lDataOut := TMemoryStream.Create;
lData := TStringStream.Create('');
//A post request would use lDataout to post data to server
lDataOut.Seek(0, soFromBeginning);
httpcli1.SendStream := lDataOut;
httpcli1.RcvdStream := lData;
httpcli1.Cookie := memo2.Text;
httpcli1.URL := 'http://www.datafile.com/index.html';
try
httpcli1.get;
//lResultPage will contain the answer from the webserver as a string
lResultPage := lData.DataString;
// srv will contain upload URL ...
srv := ExtractBetween(lResultPage,'upload_url: "','"');
Data := 'Filename=' + UrlEncodeToA('Chrysanthemum.rar') + '&' +
'upload_type=' + UrlEncodeToA('file') + '&' +
'folder_id=0' + '&'+
'Filedata=' + '&' +
'Upload=' + UrlEncodeToA('Submit Query');
HttpCli1.SendStream := TMemoryStream.Create;
HttpCli1.SendStream.Write(Data[1], Length(Data));
HttpCli1.SendStream.Seek(0, 0);
HttpCli1.RcvdStream := lData;
HttpCli1.URL := srv;
HttpCli1.ContentTypePost := 'application/octet-stream';
HttpCli1.PostAsync;
except
lDataOut.Free;
Exit;
end;
lDataOut.Free;
lData.Free;
httpcli1.Free ;
end;
this how it should be ..
So I don't know how i add the file to the "fileData" in the postdata ...
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;
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;