I want to implement a specific processing on server on client side which is additionally applied to other processings. Server-side I would just create a new middleware and assign there a wrapper for Request.ContentStream.
I am not completely sure how to do this on the client side, as I need this also for ongoing transfers (larger ones). I searched in the forum and saw there that the OnResponseReceived EventHandler shall be used.
From the Online Help I learned that ContentAsStream can be read from. And here I have an understanding issue: If read the data from the stream and do changes, how can I achieve, that the XDataClient gets the changed stream, esp. when the client is still receiving data?
In OnSendingRequest event you can simply set the Content property of the Request object.
In the OnResponseReceived, you should replace the THttpResponse object by your own, and replace it in the event.
If you check the EncryptionMiddleware demo, you will see it does that. Here is the key code in the demo:
procedure TForm1.Button1Click(Sender: TObject);
const
cHeaderAuthorization = 'Authorization';
cTokenPrefix = 'Bearer ';
cUser = 'admin';
cPassword = '67Uyvjvcp1bk25rXCZtC4xeFD5OIne';
var
lClient: TXDataClient;
lToken: string;
lEncrypted: Boolean;
begin
lEncrypted := CheckBox1.Checked;
lClient := TXDataClient.Create;
try
lCLient.Uri := 'http://127.0.0.1:2001/TMS/AppServer';
lClient.HttpClient.OnSendingRequest :=
procedure(ARequest: THttpRequest)
var
pBytes: TBytes;
begin
if lToken <> '' then
ARequest.Headers.SetValue(cHeaderAuthorization, cTokenPrefix + lToken);
if ARequest.ContentLength > 0 then
begin
if lEncrypted then
begin
pBytes := TBytes.FromBuffer(ARequest.ContentBuffer^, ARequest.ContentLength);
pBytes := TAESFunc.Encrypt(pBytes, TAESKeys.GetReceiveKey);
ARequest.SetContent(pBytes);
end
else
ARequest.Headers.AddValue(cCustomXDataHeader, TAESKeys.GetHeaderKey)
end;
end;
lClient.HttpClient.OnResponseReceived :=
procedure(ARequest: THttpRequest; var AResponse: THttpResponse)
begin
if not SameText(AResponse.Headers.Get(cCustomXDataHeader), TAESKeys.GetHeaderKey) then
begin
AResponse := TEncryptedHttpResponse.Create(AResponse,
function(const AValue: TBytes): TBytes
begin
Result := TAESFunc.Decrypt(AValue, TAESKeys.GetSendKey);
end);
end;
end;
lToken := lClient.Service<IAppServer>.Connect(cUser, cPassword);
Edit1.Text := lClient.Service<IAppServer>.Echo(Edit1.Text);
finally
lClient.Free;
end;
end;
And for illustration purposes, here is the implementation of TEncryptedHttpResponse:
unit Sparkle.Encrypted.Response;
interface
uses
System.SysUtils, System.Classes, Sparkle.Http.Headers, Sparkle.Http.Engine;
type
TDecryptFunc = reference to function(const ABytes: TBytes): TBytes;
TEncryptedHttpResponse = class(THttpResponse)
strict private
FBytes: TBytesStream;
FOrig: THttpResponse;
strict protected
function CreateContentStream: TStream; override;
function GetChunked: Boolean; override;
function GetContentAsBytes: TBytes; override;
function GetContentAsStream: TStream; override;
function GetContentAsString: string; override;
function GetContentEncoding: string; override;
function GetContentLength: Int64; override;
function GetContentType: string; override;
function GetStatusCode: Integer; override;
function GetStatusReason: string; override;
procedure UpdateHeaders(AHeaders: THttpHeaders); override;
public
constructor Create(const AOrig: THttpResponse; const ADecrypt: TDecryptFunc);
destructor Destroy; override;
end;
implementation
type
TUnprotectedHttpResponse = class(THttpResponse);
{ TEncryptedHttpResponse }
constructor TEncryptedHttpResponse.Create(const AOrig: THttpResponse; const ADecrypt: TDecryptFunc);
var
lInfo: THttpHeaderInfo;
begin
inherited Create;
FOrig := AOrig;
FBytes := TBytesStream.Create(ADecrypt(TUnprotectedHttpResponse(FOrig).GetContentAsBytes));
for lInfo in AOrig.Headers.AllHeaders do
Headers.AddValue(lInfo.Name, lInfo.Value);
end;
destructor TEncryptedHttpResponse.Destroy;
begin
FOrig.Free;
FBytes.Free;
inherited Destroy;
end;
function TEncryptedHttpResponse.CreateContentStream: TStream;
begin
Result := TBytesStream.Create(GetContentAsBytes);
end;
function TEncryptedHttpResponse.GetChunked: Boolean;
begin
Result := FOrig.Chunked;
end;
function TEncryptedHttpResponse.GetContentAsBytes: TBytes;
begin
Result := Copy(FBytes.Bytes, 0, FBytes.Size);
end;
function TEncryptedHttpResponse.GetContentAsStream: TStream;
begin
Result := FBytes;
end;
function TEncryptedHttpResponse.GetContentAsString: string;
begin
Result := StringOf(GetContentAsBytes);
end;
function TEncryptedHttpResponse.GetContentEncoding: string;
begin
Result := FOrig.ContentEncoding;
end;
function TEncryptedHttpResponse.GetContentLength: Int64;
begin
Result := FBytes.Size;
end;
function TEncryptedHttpResponse.GetContentType: string;
begin
Result := FOrig.ContentType;
end;
function TEncryptedHttpResponse.GetStatusCode: Integer;
begin
Result := FOrig.StatusCode;
end;
function TEncryptedHttpResponse.GetStatusReason: string;
begin
Result := FOrig.StatusReason;
end;
procedure TEncryptedHttpResponse.UpdateHeaders(AHeaders: THttpHeaders);
begin
TUnprotectedHttpResponse(FOrig).UpdateHeaders(AHeaders);
end;
end.