Post problems with Indy TIdHTTP - delphi

I am having issues posting to Amazon's SES Service using Indy's TIdHTTP.
Here is an example of the code i am using:
procedure TMainFrm.btnAmazonSESClick(Sender: TObject);
var
SSLHandler: TIdSSLIOHandlerSocket;
HttpClient: TIdHTTP;
Params: TStringStream;
begin
SSLHandler := TIdSSLIOHandlerSocket.Create(Self);
HttpClient := TIdHTTP.Create(Self);
Params := TStringStream.create('');
try
with SSLHandler do
SSLOptions.Method := sslvSSLv3
with HttpClient do
begin
IOHandler := SSLHandler;
AllowCookies := True;
HandleRedirects := True;
HTTPOptions := [hoForceEncodeParams];
Request.ContentType := 'application/x-www-form-urlencoded';
end;
PageMemo.Text := HttpClient.Post('https://email.us-east-1.amazonaws.com?Action=VerifyEmailAddress&AWSAccessKeyId=012Some123Key46&EmailAddress=test#test%2Ecom', Params);
finally
SSLHandler.Free;
HttpClient.Free;
Params.Free;
end;
end;
Result
Under Indy 10.5.7 I get the error: HTTP/1.1 404 Not Found
Under Indy 9.0.14 I get the error: Socket Error # 11004
Debugging Trials
This same demo can successfully GET the HTML from an HTTPS web page.
If i paste the URL above into a browser it displays the expected XML result.
I would appreciate any advice on the cause.

This post is just an incomplete wild guess.
Maybe Remy might help you to correct it. With the following code I'm getting HTTP/1.1 400 Bad Request but I'm not wondering because the API reference talks about Common Query Parameters where is at least required the digital signature you'll create for the request what I don't know how to do.
I can't test this at all because I have no account there. But I think the
procedure TForm1.Button1Click(Sender: TObject);
var
HTTPClient: TIdHTTP;
Parameters: TStrings;
SSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
HTTPClient := TIdHTTP.Create(nil);
Parameters := TStringList.Create;
try
SSLHandler.SSLOptions.Method := sslvSSLv3;
SSLHandler.SSLOptions.Mode := sslmUnassigned;
HTTPClient.IOHandler := SSLHandler;
HTTPClient.HTTPOptions := [hoForceEncodeParams];
HTTPClient.Request.ContentType := 'application/x-www-form-urlencoded';
Parameters.Add('Action=VerifyEmailAddress');
Parameters.Add('EmailAddress=test#test.com');
Parameters.Add('AWSAccessKeyId=012Some123Key46');
Parameters.Add('SignatureVersion=2');
Parameters.Add('Expires='); // ???
Parameters.Add('Signature='); // ???
PageMemo.Text := HTTPClient.Post('https://email.us-east-1.amazonaws.com', Parameters);
finally
SSLHandler.Free;
HTTPClient.Free;
Parameters.Free;
end;
end;

Basically, you need to use the right library, i.e.:
For Indy 10.5.7 use openssl-1.0.1e-i386-win32 or openssl-1.0.1e-x64_86-win64 from http://indy.fulgan.com/SSL/
You may want to download an ssl demo from: http://indy.fulgan.com/ZIP/
Regards
Jose

Related

Delphi EIdOSSLUnderlyingCryptoError Exception - SSL3_GET_RECORD Wrong Version Number

I tried using TRESTClient to connect to an HTTPS web service using TLS 1.2. But NO LUCK with sending multipart/form-data.
So now I am trying with Indy. I got this "Wrong Version Number" error.
I think there is nothing wrong with the code since it worked with HTTP.
Probably my Delphi is missing something. What should I install and how?
procedure TForm10.Button2Click(Sender: TObject);
var
HTTP: TIdHTTP;
RequestBody: TStream;
ResponseBody: string;
myssl: TIdSSLIOHandlerSocketOpenSSL;
Input: TIdMultipartFormDataStream;
begin
ResponseBody := '';
try
try
HTTP := TIdHTTP.Create;
try
Input := TIdMultipartFormDataStream.Create;
try
Input.Clear;
Input.AddFormField('Email', 'xx#xx.com.tr');
Input.AddFormField('Password', 'xx');
myssl := TIdSSLIOHandlerSocketOpenSSL.Create(HTTP);
HTTP.IOHandler := myssl;
myssl.SSLOptions.Mode := sslmUnassigned;
myssl.SSLOptions.Method := sslvTLSv1_2;
myssl.SSLOptions.SSLVersions := [sslvTLSv1_2];
HTTP.HTTPOptions := [hoForceEncodeParams];
HTTP.Request.CustomHeaders.FoldLines := False;
ResponseBody := HTTP.Post('https://xxx.com.tr/api/Mobile/MobileLoginControl', Input);
finally
Input.Free;
end;
finally
HTTP.Free;
end;
finally
end;
except
ResponseBody := '"-20"';
end;
end;
The code is fine, though you are enabling only TLS 1.2 on the SSLIOHandler. Maybe the website in question doesn't support TLS 1.2? Try enabling TLS 1.0 and 1.1 as well:
myssl.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
And don't set the SSLOptions.Method at all. Setting the SSLVersions updates the Method and vice versa. So set one or the other, not both.

Can't open Microsoft Speech Recognition API via Post with Delphi

I want to send a Post request to Microsoft Speech Recognition API via HTTPS using Indy's TIdHTTP in Delphi.
On Microsofts Speech Recognition API Page: Microsoft Speech Recognition API Get started with speech recognition by using the REST API
they write you should send a HTTP POST Request like this:
POST https://speech.platform.bing.com/speech/recognition/interactive/cognitiveservices/v1?language=en-US&format=detailed HTTP/1.1
Accept: application/json;text/xml
Content-Type: audio/wav; codec=audio/pcm; samplerate=16000
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY
Host: speech.platform.bing.com
Transfer-Encoding: chunked
Expect: 100-continue
I try this with Delphi XE 10 Indy.
But I ever got Error 400 - Bad Request as Answer!
What do I false in the following code?
procedure TForm1.Button1Click(Sender: TObject);
var
Response, csrf, url: String;
PostStream: TIdMultipartFormDataStream;
HTTPClient: TIdHTTP;
SSL: TIdSSLIOHandlerSocketOpenSSL;
begin
url := 'https://speech.platform.bing.com/speech/recognition/interactive/cognitiveservices/v1?language=en-US&format=detailed HTTP/1.1';
HTTPClient := TIdHTTP.Create;
try
HTTPClient.Disconnect;
HTTPClient.AllowCookies := True;
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTPClient);
SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
HTTPClient.IOHandler := SSL;
HTTPClient.HandleRedirects := true;
HTTPClient.Request.Accept := 'application/json;text/xml';
HTTPClient.Request.Method := 'POST';
HTTPClient.Request.ContentType := 'audio/wav; codec=audio/pcm; samplerate=16000';
//-----------------------------------------------------------------------
PostStream := TIdMultiPartFormDataStream.Create;
try
PostStream.AddFormField('Ocp-Apim-Subscription-Key','YOUR_SUBSCRIPTION_KEY');
PostStream.AddFile('file', 'test.wav');
Response := HTTPClient.Post(url, PostStream);
PostStream.Clear;
finally
PostStream.Free;
end;
finally
HTTPClient.Free;
end;
end;
Your POST request is not setup the way Microsoft's documentation says. Most importantly, you should not be using TIdMultipartFormDataStream at all, since the REST server is not expecting a request in multipart/form-data format. The body of the request is expected to be just the actual WAV file and nothing else. TIdHTTP even has an overload of Post() specifically for uploading just a file.
Try this instead:
procedure TForm1.Button1Click(Sender: TObject);
var
Response, url: String;
HTTPClient: TIdHTTP;
SSL: TIdSSLIOHandlerSocketOpenSSL;
begin
url := 'https://speech.platform.bing.com/speech/recognition/interactive/cognitiveservices/v1?language=en-US&format=detailed';
HTTPClient := TIdHTTP.Create;
try
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(HTTPClient);
SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
HTTPClient.IOHandler := SSL;
HTTPClient.AllowCookies := True;
HTTPClient.HandleRedirects := true;
HTTPClient.Request.Accept := 'application/json;text/xml';
HTTPClient.Request.ContentType := 'audio/wav; codec=audio/pcm; samplerate=16000';
HTTPClient.Request.CustomHeaders.Values['Ocp-Apim-Subscription-Key'] := 'YOUR_SUBSCRIPTION_KEY';
Response := HTTPClient.Post(url, 'test.wav');
finally
HTTPClient.Free;
end;
end;

Using Indy to retrieve Revcontent OAuth Token

I am trying to use Indy to retrieve a RevContent oAuth token according to this page. Here is my code:
procedure TForm2.Button1Click(Sender: TObject);
var
IdSSLIOHandlerSocket1: TIdSSLIOHandlerSocketOpenSSL;
idhttp:tidhttp;
params: TStringList;
begin
idhttp:=tidhttp.create(self);
IdSSLIOHandlerSocket1 := TIdSSLIOHandlerSocketOpenSSL.create(nil);
with IdSSLIOHandlerSocket1 do begin
SSLOptions.Method := sslvSSLv23;
SSLOptions.SSLVersions := [sslvSSLv23];
end;
params := TStringList.create;
params.add('clientid=xxyyzz');
params.add('client_secret=xxyyzzxxyyzzxxyyzzxxyyzzxxyyzzxxyyzz');
params.add('grant_type=client_credentials');
with IdHTTP do begin
IOHandler := IdSSLIOHandlerSocket1;
Request.ContentType := 'application/x-www-form-urlencoded';
end;
showmessage(idhttp.Post('https://api.revcontent.io/oauth/token',params));
end;
But whenever I run it I get an 400 Bad Request error. What am I doing wrong?
Use client_id, not clientid.
Do not use SSL3, that site supports TLS1.
Do not set ContentType.
Use try/except around Post to catch exception and get full error message (E as EIdHTTPProtocolException).ErrorMessage

Login to craigslist to retrieve account page using Delphi and Indy and SSL

I am trying to log into craigslist using Delphi, and retrieve my account page (in order to gather a listing of all my posts)
However, I can't seem to get the login to work, what Am I doing wrong?
function TfrmMain.Login: string;
var
IdHTTP: TIdHTTP;
Request: TStringList;
Response: TMemoryStream;
begin
Result := '';
try
Response := TMemoryStream.Create;
try
Request := TStringList.Create;
try
Request.Add('op=login');
Request.Add('redirect=http://newyork.craigslist.org/');
Request.Add('login=' + myEmail);
Request.Add('password=' + myPassword);
IdHTTP := TIdHTTP.Create;
try
IdHTTP.AllowCookies := True;
IdHTTP.HandleRedirects := True;
IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
IdHTTP.Post('https://accounts.craigslist.org/login', Request, Response);
Result := IdHTTP.Get('https://accounts.craigslist.org/');
finally
IdHTTP.Free;
end;
finally
Request.Free;
end;
finally
Response.Free;
end;
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
I get a exception class EIdIOHandlerPropInvalid with message 'IOHandler value is not valid' on the line:
IdHTTP.Post('https://accounts.craigslist.org/login', Request, Response);
thanks
See (Indy) TIdHTTP EIdSocketError Socket Error # 0 exceptions when downloading files and the suggestions in the comments. It looks like you should upgrade to a more recent version of Indy.

Seeing Indy Traffic in Fiddler

I think this is an easy question for someone familiar with Indy. I'm using Delphi 2010 and Indy 10. I am trying to get off the ground accessing an SSL web service. I think it will be a lot easier if I can get Fiddler to see my HTTP traffic. I have seen posts on StackOverflow that indicate it's no big thing to get Fiddler to see your Indy traffic, that you just have to configure the port to make it work. My question is how do you do that?
Here is my code so far:
procedure TForm1.Button1Click(Sender: TObject);
var slRequest: TStringList;
sResponse,
sFileName: String;
lHTTP: TIdHTTP;
lIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
sFileName := 'Ping.xml';
slRequest := TStringList.Create;
try
slRequest.LoadFromFile(sFileName);
lHTTP := TIdHTTP.Create(nil);
lHTTP.Intercept := IdLogDebug1;
lIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
try
lHTTP.IOHandler := lIOHandler;
sResponse := lHTTP.Post('https://FSETTESTPROD.EDD.CA.GOV/fsetservice', slRequest);
Memo1.Lines.Text := sResponse;
finally
lIOHandler.Free;
end;
finally
slRequest.Free;
end;
end;
Edit: If I don't use the proxy for Fiddler and click the button while Wireshark is running, I get this traffic in Wireshark.
You can set Indy to use the proxy fiddler provides easily by setting the ProxyParams:
try
lHTTP.IOHandler := lIOHandler;
lHTTP.ProxyParams.ProxyServer := '127.0.0.1';
lHTTP.ProxyParams.ProxyPort := 8888;
sResponse := lHTTP.Post('<URL>', slRequest);
Memo1.Lines.Text := sResponse;
finally
lIOHandler.Free;
end;
You should be able to see all traffic in Fiddler then.
Edit: If that does not work you can add a TIdLogDebug component and add it as interceptor (like you did in your question).
The OnReceive and OnSend events contain the complete headers sent and received aswell as the reply data:
procedure TForm10.captureTraffic(ASender: TIdConnectionIntercept;
var ABuffer: TArray<Byte>);
var
i: Integer;
s: String;
begin
s := '';
for i := Low(ABuffer) to High(ABuffer) do
s := s + chr(ABuffer[i]);
Memo1.Lines.Add(s);
end;

Resources