I am working on a VCL application and I have to integrate Twilio using its REST API:
https://www.twilio.com/docs/usage/your-request-to-twilio
Here is my code:
pair<bool, String> SMSTwilio::SendMessage(TComponent* Owner,
String ToNumber, String FromNumber, String Message)
{
if(Message.Length() > MESSAGE_LIMIT) {
ShowMessage("Message must have " + IntToStr(MESSAGE_LIMIT) +
" or fewer characters. Cannot send message with " +
IntToStr(Message.Length()) + "characters.");
}
AccountSID = "AC2d48*****************0deb52";
AuthToken = "9e28ec***************c0126e";
Message = "Hi";
FromNumber = "+1740****95";
ToNumber = "+9*****791";
String URI = "https://api.twilio.com/2010-04-01/Accounts/"
+ AccountSID +
"/Messages";
TStringList* params = new TStringList();
params->Add("From=" + FromNumber);
params->Add("To=" + ToNumber);
params->Add("Body=" + Message);
TIdHTTP* HTTP = new TIdHTTP(Owner);
HTTP->Request->Connection = "Keep-Alive";
HTTP->Request->ContentType = "application/x-www-form-urlencoded";
HTTP->Request->BasicAuthentication = true;
HTTP->Request->Username = AccountSID;
HTTP->Request->Password = AuthToken;
TIdSSLIOHandlerSocketOpenSSL* Handler = new TIdSSLIOHandlerSocketOpenSSL(Owner);
Handler->SSLOptions->Method = sslvTLSv1;
HTTP->IOHandler = Handler;
bool isSuccess = false;
String Result = "";
__try {
try {
HTTP->ReadTimeout = 5000;
HTTP->ConnectTimeout = 5000;
Result = HTTP->Post(URI, params);
isSuccess = true;
} catch(Exception &e) {
isSuccess = false;
Result = e.Message;
}
}
__finally {
delete HTTP;
delete params;
}
return make_pair(isSuccess, Result);
}
I am getting an EIdHTTPProtocolException
with message "HTTP/1.1 400 BAD REQUEST"
thrown by Result = HTTP->Post(URI, params);
.
You are posting to .../Messages
but you need to post to .../Messages.json
instead (notice the .json
at the end), per Twilio's Message Resource documentation:
Create a Message resource
POST
https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/Messages.json
To send a new outgoing message, make an HTTP POST to this Messages list resource URI.
Also, although not errors per-say, there are some other issues with your code:
your Owner
parameter is unnecessary. Since you are creating and destroying your TIdHTTP
object in the same function, there is no need to assign an Owner
to it at all. And it would be more useful to assign that TIdHTTP
object as the Owner for the TIdSSLIOHandlerSocketOpenSSL
object, instead of some unknown external Owner.
you are not returning an error to the caller if the Message
is too long to send.
you are not adequately protecting your objects from leaks if something bad happens (why not use C++ smart pointers instead of try..finally
?).
your catch()
should be catching the Exception
object by const reference.
you don't need Request->Connection = "Keep-Alive"
since you are closing the connection after the Post()
is finished, you are not actually using a keep-alive.
you should be using the SSLOptions->SSLVersions
property instead of the SSLOptions->Method
property. That will then allow you to enable sslvTLSv1_1
and sslvTLSv1_2
, since many servers are phasing out TLS 1.0 nowadays, so you should prepare for that sooner rather than later.
With that said, try something more like this:
#include <utility>
#include <memory>
std::pair<bool, String> SMSTwilio::SendMessage(
String ToNumber, String FromNumber, String Message)
{
if (Message.Length() > MESSAGE_LIMIT) {
String msg = Format(_D("Message must have %d or fewer characters. Cannot send message with %d characters."), ARRAYOFCONST(( MESSAGE_LIMIT, Message.Length() )) );
//ShowMessage(msg);
return std::make_pair(false, msg);
}
String AccountSID = _D("AC2d48*****************0deb52");
String AuthToken = _D("9e28ec***************c0126e");
//Message = _D("Hi");
//FromNumber = _D("+1740****95");
//ToNumber = _D("+9*****791");
String URI = Format(_D("https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json"), ARRAYOFCONST(( AccountSID )) );
std::unique_ptr<TStringList> params(new TStringList); // or std::auto_ptr prior to C++11
params->Add(_D("From=") + FromNumber);
params->Add(_D("To=") + ToNumber);
params->Add(_D("Body=") + Message);
std::unique_ptr<TIdHTTP> HTTP(new TIdHTTP(nullptr)); // or std::auto_ptr prior to C++11
HTTP->ReadTimeout = 5000;
HTTP->ConnectTimeout = 5000;
//HTTP->Request->Connection = _D("Keep-Alive");
HTTP->Request->ContentType = _D("application/x-www-form-urlencoded");
HTTP->Request->BasicAuthentication = true;
HTTP->Request->Username = AccountSID;
HTTP->Request->Password = AuthToken;
TIdSSLIOHandlerSocketOpenSSL* Handler = new TIdSSLIOHandlerSocketOpenSSL(HTTP.get());
Handler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
HTTP->IOHandler = Handler;
bool isSuccess = false;
String Result;
try {
Result = HTTP->Post(URI, params);
isSuccess = true;
}
catch (const Exception &e) {
isSuccess = false;
Result = e.Message;
}
return std::make_pair(isSuccess, Result);
}