Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2015-01

Cannot reuse TFPHTTPClient object for 2 different POST requests

12 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

leledumbo

Consider the following program (it's a real program with real parameters, nothing hidden, so you can really just try it out because the target server is public):

{$mode objfpc}{$H+}
uses
  fphttpclient;
var
  HTTP: TFPHTTPClient;
begin
  HTTP := TFPHTTPClient.Create(nil);
  WriteLn('Response 1:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/price',

'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d&from=BOO10000&thru=AMI10000&weight=1' )

  );
  WriteLn('Response 2:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/price',

'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d&from=BDO10000&thru=AMI10000&weight=1' )

  );
  WriteLn('Response 3:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/origin/key/BANDUNG',
      'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d'
    )
  );
  HTTP.Free;
end.

In above program, Response 3 will stuck in infinite loop. Similarly, if we move Response 3 up as the first request and then copy-paste-d:

{$mode objfpc}{$H+}
uses
  fphttpclient;
var
  HTTP: TFPHTTPClient;
begin
  HTTP := TFPHTTPClient.Create(nil);
  WriteLn('Response 1:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/origin/key/BANDUNG',
      'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d'
    )
  );
  WriteLn('Response 2:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/origin/key/BOGOR',
      'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d'
    )
  );
  WriteLn('Response 3:');
  WriteLn(
    HTTP.FormPost(
      'http://api.jne.co.id:8889/tracing/sirclo/price',

'username=SIRCLO&api_key=4fd0e22535c32eb9e5496c311190760d&from=BOO10000&thru=AMI10000&weight=1' )

  );
  HTTP.Free;
end.

the new Response 3 will result in empty string. As can be seen, the request starts failing (either stuck or empty string) when the URL is changed. Change in FormData is OK as demonstrated above.

Reply by email

waldo kitty
to leledumbo
leledumbo wrote
Consider the following program (it's a real program with real parameters, nothing hidden, so you can really just try it out because the target server is public):

you don't have to clear the inbound buffer to prevent constipation? ;)

NOTE: No off-list assistance is given without prior approval. Please *keep mailing list traffic on the list* unless private contact is specifically requested and granted.

Reply by email

waldo kitty
to leledumbo
leledumbo wrote
> you don't have to clear the inbound buffer to prevent constipation? ;) Which inbound buffer?

i was thinking that there's some sort of inbound traffic that may be filling a buffer that needs to be emptied... i don't know or have any other ideas to offer...

NOTE: No off-list assistance is given without prior approval. Please *keep mailing list traffic on the list* unless private contact is specifically requested and granted.

Reply by email

Michael Van Canneyt
to leledumbo: Consider the following program (it's a real program with real parameters, nothing hidden,…
leledumbo wrote
Consider the following program (it's a real program with real parameters, nothing hidden, so you can really just try it out because the target server is public):

The problem is not in TFPHTTPClient. The problem is the data and/or URL for request 3:

if you strace the program, you see what happens:

connect(3, {sa_family=AF_INET, sin_port=htons(8889), sin_addr=inet_addr("222.124.201.117")}, 16) = 0 sendto(3, "POST /tracing/sirclo/origin/key/"..., 245, 0, NULL, 0) = 245 sendto(3, "username=SIRCLO&api_key=4fd0e225"..., 56, 0, NULL, 0) = 56 recvfrom(3,

And there it stops.

I killed the program after 2 minutes. The server is not replying.

Michael.

Reply by email

leledumbo
to Michael Van Canneyt
i was thinking that there's some sort of inbound traffic that may be

filling a
buffer that needs to be emptied... i don't know or have any other ideas to offer...

Mmm...could be, that's what should be searched for.

The problem is not in TFPHTTPClient. The problem is the data and/or URL for request 3:

OK, now try creating a new TFPHTTPClient instance for each request, don't reuse. It should work as it's my current workaround, but I think it's a waste of resource. Can you explain that?

I also use strace to compare the the 3 consecutive FormPost call using one TFPHTTPClient instance with 3 curl command line calls. The only difference I see is that TFPHTTPClient sends cookie and some other headers instead of just a url-encoded fields. Content-Length in the 2nd example that returns empty string is 0, while the 1st example is the same as yours: stuck at recvfrom.

Reply by email

Michael Van Canneyt
to leledumbo
leledumbo wrote 13 lines
> i was thinking that there's some sort of inbound traffic that may be filling a buffer that needs to be emptied... i don't know or have any other ideas to offer... Mmm...could be, that's what should be searched for. > The problem is not in TFPHTTPClient. The problem is the data and/or URL > for request 3: OK, now try creating a new TFPHTTPClient instance for each request, don't reuse. It should work as it's my current workaround, but I think it's a waste of resource. Can you explain that?

Yes, I can. The TFPHTTPClient instance is not stateless.

Before doing a call, some headers are added to the request headers if they are not there yet. For example, the Content-Size header.

But the Content-Size header is not added if is already present. For calls 1 and 2 it does not matter, since the size is the same.

For the third call, however, it does matter, since the content-size header still contains the size of call 2 (93), which does not match the size of call 3: call 3 has less data (57).

So the server is waiting for more data (it expects 93 bytes as indicated in Content-Size instead of 57 actually present).

The solution is simple: Try clearing the headers before doing your calls.

   WriteLn('Response 2:');
   HTTP.RequestHeaders.Clear;

etc. then it will work. (it does here)

Michael.

Reply by email

leledumbo
to Michael Van Canneyt
Show quoted text 22 lines
Yes, I can. The TFPHTTPClient instance is not stateless. Before doing a call, some headers are added to the request headers if they are not there yet. For example, the Content-Size header. But the Content-Size header is not added if is already present. For calls 1 and 2 it does not matter, since the size is the same. For the third call, however, it does matter, since the content-size header still contains the size of call 2 (93), which does not match the size of call 3: call 3 has less data (57). So the server is waiting for more data (it expects 93 bytes as indicated in Content-Size instead of 57 actually present). The solution is simple: Try clearing the headers before doing your calls. WriteLn('Response 2:'); HTTP.RequestHeaders.Clear; etc. then it will work. (it does here)

This is the answer I need, thanks :)

Looks like this needs some kind of documentation or at least comment somewhere in the source.

I still wonder why the header is not updated based on given parameters though...

Reply by email

Michael Van Canneyt
to leledumbo
leledumbo wrote 27 lines
> Yes, I can. The TFPHTTPClient instance is not stateless. > > Before doing a call, some headers are added to the request headers if they > are not there yet. > For example, the Content-Size header. > > But the Content-Size header is not added if is already present. > For calls 1 and 2 it does not matter, since the size is the same. > > For the third call, however, it does matter, since the content-size header > still contains the size of call 2 (93), > which does not match the size of call 3: call 3 has less data (57). > > So the server is waiting for more data (it expects 93 bytes as indicated > in Content-Size instead of 57 actually present). > > The solution is simple: Try clearing the headers before doing your calls. > > WriteLn('Response 2:'); > HTTP.RequestHeaders.Clear; > > etc. then it will work. (it does here) This is the answer I need, thanks :) Looks like this needs some kind of documentation or at least comment somewhere in the source.

Will do.

I still wonder why the header is not updated based on given parameters though...

Because the header may have been set on purpose by the user.

I will look at a mechanism to remove headers that were 'automatically' added.

Michael.

Reply by email

leledumbo
to Michael Van Canneyt
Because the header may have been set on purpose by the user.

Even if the actual data sent is not of the same length?

I will look at a mechanism to remove headers that were 'automatically' added.

How about a property ClearHeadersOnEachRequest (defaults to true for backward compatibility)?

Reply by email

Michael Van Canneyt
to leledumbo
leledumbo wrote
> Because the header may have been set on purpose by the user. Even if the actual data sent is not of the same length?

You cannot always trust the Size property of a stream.

Show quoted text 6 lines
> I will look at a mechanism to remove headers that were 'automatically' > added. How about a property ClearHeadersOnEachRequest (defaults to true for backward compatibility)?

That is not quite correct, because that will also clear any custom headers you may want to set. Cookies, Accept-encoding and so on.

Maybe ForceStandardHeaders or so.

Michael.

Reply by email

leledumbo
to Michael Van Canneyt
You cannot always trust the Size property of a stream.

Err...OK

That is not quite correct, because that will also clear any custom headers you may want to set. Cookies, Accept-encoding and so on.

Exactly what I have in mind, just need to document it somewhere. Anyway, the default should be false, I was thinking the other way around.

Reply by email