Sign in Join

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

Mailing lists / fpc-pascal / 2016-07

Improvement to TFPHTTPClient Class

5 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

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

African Wild Dog

Hello,

The current version of the TFPHTTPClient (the one shipped with fpc 3.0) is coupled to the fpc's sockets framework (fcl-net) so we can't utilize other tcp connection framework (e.g Synapse) to perform HTTP requests using TFPHTTPClient.

I have made an improvement to the TFPHTTPClient class (unit fphttpclient, package fcl-web) which consists of decoupling the client from the socket framework that is used to perform HTTP requests.

My solution is quite simple: i created a new class called TTCPConnectionAdapter (as the name suggests, i used the Adapter pattern) and changed the http client class to perform requests using a TCPConnectionAdapter instance . The connection adapter is passed as a parameter to the constructor of the TFPHTTPClient class (dependency injection). So, the TFPHTTPClient is not tied to the a particular socket framework anymore. If one wants to perform http requests using e.g Synapse, just implement the appropriate TTCPConnectionAdapter descendant and inject it into TFPHTTPClient.

The code below shows the TInetAdapter

======================================

{ TTCPConnectionAdapter }

TTCPConnectionAdapter = class(TStream)
  strict private
    FHost: String;
    FPort: Word;
    procedure ValidateConnection;
  strict protected
    procedure DoConnect(const AHost: String; const APort: Word); virtual;
abstract;
    function DoRead(var Buffer; Count: Longint): Longint; virtual; abstract;
    function DoWrite(const Buffer; Count: Longint): Longint; virtual;
abstract;
    function GetConnected: Boolean; virtual; abstract;
  public
    procedure Connect(const AHost: String; const APort: Word);
    procedure Disconnect; virtual; abstract;
    function Read(var Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    property Connected: Boolean read GetConnected;
    property Host: String read FHost;
    property Port: Word read FPort;
  end;

{ TInetConnectionAdapter }

  TInetConnectionAdapter = class(TTCPConnectionAdapter)
  strict private
    FSocket: TInetSocket;
    procedure FreeSocket;
  strict protected
    procedure DoConnect(const AHost: String; const APort: Word); override;
    function DoRead(var Buffer; Count: Longint): Longint; override;
    function DoWrite(const Buffer; Count: Longint): Longint; override;
    function GetConnected: Boolean; override;
  public
    destructor Destroy; override;
    procedure Disconnect; override;
  end;
  { TFPCustomHTTPClient }
  TFPCustomHTTPClient = Class(TComponent)
  private
    ......
    FTCPConnection: TTCPConnectionAdapter;
  public
    Constructor Create(const ATCPConnection: TTCPConnectionAdapter);
  end;

==============================================================

Best regards

Michael Van Canneyt
to African Wild Dog

Hello,

This will not be applied, for 2 reasons:

1. The whole point of TFPHTTPClient is exactly to couple it to sockets framework (fcl-net).
If you want to use synapse, use httpsend. lnet has a similar component for the http protocol.

2. I favour the opposite approach. Abstract out the underlying HTTP implementation at a higher level.

For this I have introduced FPWebclient, which has 2 implementations: TFPHTTPClient and Synapse.

The new google-api and OData apis use FPWebclient.

Michael.

African Wild Dog
to Michael Van Canneyt

2016-07-19 4:52 GMT-03:00 Michael Van Canneyt :

Show quoted text 7 lines
1. The whole point of TFPHTTPClient is exactly to couple it to sockets framework (fcl-net). If you want to use synapse, use httpsend. lnet has a similar component for the http protocol.

I dont want to use synapse. I am developing bindings for a c library. The library offers TCP connection functionality but does not provide any support for http. I need to use that library due to some particularities of my project.

I do not understand the need for such coupling between http client and fcl-net. It is like coupling SQLDb with a specific RDBMS.

2. I favour the opposite approach. Abstract out the underlying HTTP implementation at a higher level.

Why should we duplicate the code? Why should we implement the same functionality in several diferent places?

Show quoted text 5 lines
For this I have introduced FPWebclient, which has 2 implementations: TFPHTTPClient and Synapse.

Through TAbstractWebClient you are introducing another level of complexity to do the same task: perform http requests. TAbstractWebClient is actually an adapter to diferent http implementations. TAbstractWebClient is an interesting class. However, It is a poor solution, IMHO.

Best regards

Michael Van Canneyt
to African Wild Dog
African Wild Dog wrote 16 lines
2016-07-19 4:52 GMT-03:00 Michael Van Canneyt : > > 1. The whole point of TFPHTTPClient is exactly to couple it to sockets > framework (fcl-net). > If you want to use synapse, use httpsend. lnet has a similar component > for the http protocol. > > I dont want to use synapse. I am developing bindings for a c library. The library offers TCP connection functionality but does not provide any support for http. I need to use that library due to some particularities of my project. I do not understand the need for such coupling between http client and fcl-net. It is like coupling SQLDb with a specific RDBMS.

The comparison is at fault, but I will not press the point.

Show quoted text 7 lines
> 2. I favour the opposite approach. Abstract out the underlying HTTP > implementation > at a higher level. > Why should we duplicate the code? Why should we implement the same functionality in several diferent places?

Because all Pascal TCP toolkits out there offer a HTTP Client. (lnet, indy, synapse). By doing this, I leave the choice up to the user which one to use for various APIs that are built ON TOP of HTTP.

Your use case is simply one I didn't foresee,
Because I assume that any decent TCP toolkit offers a HTTP client.

However, now that I understand what you actually need, we can consider this use-case.

I will accept the patch, on the condition that you provide a binding for a C TCP/IP library too. If your c library is open source, then we can use that. if not, then I'd ask you to provide an libevent implementation.

It has no point abstracting out something if we have only 1 implementation.

Michael.

African Wild Dog
to Michael Van Canneyt

2016-07-20 3:45 Michael Van Canneyt :

Because all Pascal TCP toolkits out there offer a HTTP Client. (lnet, indy,

Show quoted text 10 lines
synapse). By doing this, I leave the choice up to the user which one to use for various APIs that are built ON TOP of HTTP. Your use case is simply one I didn't foresee, Because I assume that any decent TCP toolkit offers a HTTP client. However, now that I understand what you actually need, we can consider this use-case.

The change I am proposing should be accepted even if i did not have that problem to solve. Decoupling the TFPHTTPClient class from the underlying socket framework brings many benefits. Perhaps, the most important benefity is improving the testability of the class. For example, one can test the core functionality of the TFPHTTPClient through "fake" TCP adapters.

Show quoted text 8 lines
I will accept the patch, on the condition that you provide a binding for a C TCP/IP library too. If your c library is open source, then we can use that. if not, then I'd ask you to provide an libevent implementation. It has no point abstracting out something if we have only 1 implementation.

Great! I will provide the bindings. My bindings are for the Mozilla 's NSPR toolkit <https://developer.mozilla.org/pt-BR/docs/NSPR>.

Best regards