Search code examples
ssltwilioc++builderindyvcl

Getting "EIdHTTPProtocolException with message 'HTTP/1.1 400 BAD REQUEST'" exception


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);.


Solution

  • You are posting to the wrong URL.

    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);
    }