2

I'm wanting to create a CloudFlare client in the Firemonkey framework. For those who don't know, CloudFlare serves as a CDN of sorts for anyone with a website. They have an API available, and as with many web API's, they are using JSON with a token-based system. It requires both the account email address and the account token to access the API. It runs on HTTPS, and as you can imagine, attempting to access the API via HTTP/non-SSL simply produces null results.

The application i wish to create would serve as an all-in-one management tool, intending to eliminate the need for me to use a web browser to manage my CloudFlare settings. I'm having the most basic of issues; SSL POST. See, i can submit an API request via a web browser and get a list of results (e.g. https://www.cloudflare.com/api_json.html?a=stats&z=DOMAIN&u=EMAIL&tkn=TOKEN - Personal details removed for obvious reasons), but i'm unsure how i would go about getting these same results (or any results from the API for that matter) in Firemonkey.

I've got Overbyte ICS with SSL installed, as well as the basic bundled Indy components, but i'm struggling to get started with this. I need to post a list of parameters to https://www.cloudflare.com/api_json.html via HTTPS/SSL, but i've very little idea on where to start. I've seen a few various example around SO, mostly using ICS, but i've been unable to find any specific to posting with multiple parameters, how i should format it, etc.

One example i tried was using ICS TSSLHttpCli, writing my parameters as a single string (i.e. a=stats&z=DOMAIN&u=EMAIL&tkn=TOKEN), writing that to the SendStream of TSSLHttpCli, seeking to 0,0, setting the URL (i.e. https://www.cloudflare.com/api_json.html?), and then calling the Post method. However, this gives me Connection aborted on request. This is the code i've tried (though i've replaced personal details with generic values);

var
  Data : AnsiString;
  RcvStrm, SndStrm : TMemoryStream;
begin
  SndStrm := TMemoryStream.Create;
  RcvStrm := TMemoryStream.Create;
  Data := '?a=stats&z=MYDOMAIN&u=MYEMAIL&tkn=MYTOKEN';
  SslHttpCli.SendStream := SndStrm;
  SslHttpCli.SendStream.Write(Data[1],Length(Data));
  SslHttpCli.SendStream.Seek(0,0);
  Memo1.Lines.LoadFromStream(SndStrm);
  ShowMessage('Waiting!');
  SslHttpCli.RcvdStream := RcvStrm;
  SslHttpCli.URL := 'https://www.cloudflare.com/api_json.html';
  SslHttpCli.Post;
  Memo1.Lines.Clear;
  Memo1.Lines.LoadFromStream(RcvStrm);
  Memo1.Lines.Add('.....');
  RcvStrm.Free;
  SndStrm.Free;
  ShowMessage('Complete!');
end;

The ShowMessage procedures are simply there to provide a visual break so i can see what data is in the stream at each time. When Memo1.Lines.LoadFromStream(SndStrm); is called, i get a single question mark the contents of the Data in the memo as expected.

When i call Memo1.Lines.LoadFromStream(RcvStrm);, i expect it to add the return result from the API, and then the 5 dots underneath it. However, this does not happen, and it's apparent that the message i'm receiving is related to the issue. I'm assuming i've not set up the data correctly, but i'm simply unsure exactly how i should format it prior to attempting to post it. I've even commented out everything below Memo1.Lines.LoadFromStream(RcvStrm); to the end to see whether the Clear procedure is called on the memo, but the contents of the memo remain the same as they were when i called LoadFromStream(SndStrm). The final ShowMessage is also not called.

I initially tried using String instead of AnsiString, but this simply output the first character of Data rather than the whole string.

There could be numerous reasons why it's not working (all details for API access are correct, so it's an issue with the code), but i need someone with more experience and knowledge to point me in the right direction.

My network coding knowledge is limited, and i've only dealt with basic SQL and FTP in Delphi so far. I've still got to work with the parsed JSON once i do get past this step, but for now, can anyone assist me in this endeavor so i can get started?

Marcus Adams
  • 53,009
  • 9
  • 91
  • 143
Scott P
  • 1,462
  • 1
  • 19
  • 31
  • Data should be as Data := 'a=stats&z=MYDOMAIN&u=MYEMAIL&tkn=MYTOKEN'; So without the ? – Runner Jul 16 '12 at 06:41
  • Also check if you are encoding the data correctly and check if they support post in the first place. Why are you not doing a simple GET? – Runner Jul 16 '12 at 06:42
  • @Runner The API uses POST for requests in most of the examples, and as such i simply followed that. There is some account setting control available with the API which is why i assume POST is more desirable than GET. I did remove the question mark at the start, but this left me with the same result of no returned results, and the `connection aborted on request` message. – Scott P Jul 16 '12 at 14:33

2 Answers2

0

I noticed you seemed to solve this with a GET request, but I noticed two immediate problems with your POST request:

  1. as Runner Suggested, drop the '?' in your data. The '?' is only used when appending parameters to the URL in a GET request.

  2. You never set the content type of the HTTP Request (should be application/x-www-form-urlencoded). You can do this with the following code:

    SSLHttpCli.ContentTypePost := 'application/x-www-form-urlencoded';

Just a helpful thought. I checked https://www.cloudflare.com/docs/client-api.html and they mention that POST requests are accepted. It's possible the server rejects requests that have any other content type.

Just some food for thought if you ever need to contact another API via POST requests and want to use the Overbyte Components.

Hope the info is useful!

splrk
  • 186
  • 1
  • 7
0

Try this;

SndStrm := TMemoryStream.Create;
RcvStrm := TMemoryStream.Create;
Data := 'a=stats&z=MYDOMAIN&u=MYEMAIL&tkn=MYTOKEN';
SndStrm.Write(Data[1], Length(Data));
SndStrm.Seek(0, 0);
SslHttpCli.SendStream := SndStrm;
Murat Cem YALIN
  • 317
  • 1
  • 6