feat(android): HTTP client for the sync endpoint, with ETag reuse
Fetches the opaque container and the ETag; a 304 skips both the download and the 4-second derivation, which is the whole point of keeping the tag between calls. Redirects are not followed - one to another host would send the bearer token there. Local testing goes through adb reverse tcp:8080 tcp:80, which makes the phone reach the PC as 127.0.0.1 and satisfies the endpoint's loopback exemption. That needs usesCleartextTraffic in the manifest, marked for removal before shipping.
This commit is contained in:
+127
-4
@@ -21,14 +21,18 @@ uses
|
||||
System.Hash, System.Diagnostics, System.NetEncoding,
|
||||
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
|
||||
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Memo, FMX.Memo.Types,
|
||||
FMX.ScrollBox, FMX.Layouts;
|
||||
FMX.ScrollBox, FMX.Layouts, FMX.Edit;
|
||||
|
||||
type
|
||||
TForm8 = class(TForm)
|
||||
private
|
||||
FLog: TMemo;
|
||||
FBtn: TButton;
|
||||
FBtnFetch: TButton;
|
||||
FUrl, FToken, FPwd: TEdit;
|
||||
FETag: string; // conserve entre deux appuis : c'est ce qui permet le 304
|
||||
procedure RunSelfTest(Sender: TObject);
|
||||
procedure RunFetch(Sender: TObject);
|
||||
procedure Log(const AMsg: string);
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
@@ -40,7 +44,7 @@ var
|
||||
implementation
|
||||
|
||||
uses
|
||||
PMA.Crypto, PMA.TestVector;
|
||||
System.JSON, PMA.Crypto, PMA.TestVector, PMA.SyncClient;
|
||||
|
||||
{$R *.fmx}
|
||||
|
||||
@@ -49,12 +53,38 @@ begin
|
||||
inherited;
|
||||
Caption := 'PM — crypto self-test';
|
||||
|
||||
// Champs de configuration. Rien n'est persiste : c'est un banc d'essai, et
|
||||
// le mot de passe de sync ouvre le coffre entier — il n'a pas a trainer sur
|
||||
// le disque tant que le stockage Keystore n'est pas ecrit.
|
||||
FUrl := TEdit.Create(Self);
|
||||
FUrl.Parent := Self; FUrl.Align := TAlignLayout.Top;
|
||||
FUrl.Margins.Rect := RectF(8, 8, 8, 0);
|
||||
FUrl.TextPrompt := 'http://127.0.0.1:8080/server-php/sync.php';
|
||||
|
||||
FToken := TEdit.Create(Self);
|
||||
FToken.Parent := Self; FToken.Align := TAlignLayout.Top;
|
||||
FToken.Margins.Rect := RectF(8, 4, 8, 0);
|
||||
FToken.TextPrompt := 'jeton Bearer';
|
||||
|
||||
FPwd := TEdit.Create(Self);
|
||||
FPwd.Parent := Self; FPwd.Align := TAlignLayout.Top;
|
||||
FPwd.Margins.Rect := RectF(8, 4, 8, 0);
|
||||
FPwd.TextPrompt := 'mot de passe de sync';
|
||||
FPwd.Password := True;
|
||||
|
||||
FBtnFetch := TButton.Create(Self);
|
||||
FBtnFetch.Parent := Self; FBtnFetch.Align := TAlignLayout.Top;
|
||||
FBtnFetch.Margins.Rect := RectF(8, 4, 8, 0);
|
||||
FBtnFetch.Height := 48;
|
||||
FBtnFetch.Text := 'Telecharger et dechiffrer';
|
||||
FBtnFetch.OnClick := RunFetch;
|
||||
|
||||
FBtn := TButton.Create(Self);
|
||||
FBtn.Parent := Self;
|
||||
FBtn.Align := TAlignLayout.Top;
|
||||
FBtn.Margins.Rect := RectF(8, 8, 8, 8);
|
||||
FBtn.Margins.Rect := RectF(8, 4, 8, 8);
|
||||
FBtn.Height := 48;
|
||||
FBtn.Text := 'Lancer le test';
|
||||
FBtn.Text := 'Test crypto (vecteur)';
|
||||
FBtn.OnClick := RunSelfTest;
|
||||
|
||||
FLog := TMemo.Create(Self);
|
||||
@@ -77,6 +107,99 @@ begin
|
||||
end);
|
||||
end;
|
||||
|
||||
procedure TForm8.RunFetch(Sender: TObject);
|
||||
var
|
||||
LUrl, LTok, LPwd: string;
|
||||
begin
|
||||
FLog.Lines.Clear;
|
||||
FBtnFetch.Enabled := False;
|
||||
// Lus ICI, sur le thread principal : toucher un controle FMX depuis le
|
||||
// thread de travail est un plantage differe, pas une erreur de compilation.
|
||||
LUrl := FUrl.Text.Trim;
|
||||
LTok := FToken.Text.Trim;
|
||||
LPwd := FPwd.Text;
|
||||
|
||||
TThread.CreateAnonymousThread(
|
||||
procedure
|
||||
var
|
||||
LClient: TSyncClient;
|
||||
LRes: TSyncResult;
|
||||
LPlain: string;
|
||||
LSw: TStopwatch;
|
||||
LJson: TJSONObject;
|
||||
LArr: TJSONArray;
|
||||
LLive, LTrash, I: Integer;
|
||||
begin
|
||||
LClient := TSyncClient.Create(LUrl, LTok);
|
||||
try
|
||||
Log('GET ' + LUrl);
|
||||
if FETag <> '' then Log(' If-None-Match: ' + FETag);
|
||||
LSw := TStopwatch.StartNew;
|
||||
LRes := LClient.Fetch(FETag);
|
||||
LSw.Stop;
|
||||
Log(' HTTP ' + LRes.HttpCode.ToString + ' en ' +
|
||||
LSw.ElapsedMilliseconds.ToString + ' ms');
|
||||
|
||||
case LRes.Status of
|
||||
ssNotModified:
|
||||
begin
|
||||
Log(' ==> 304 : inchange, rien a re-dechiffrer');
|
||||
Exit;
|
||||
end;
|
||||
ssNoSnapshot:
|
||||
begin
|
||||
// Le serveur repond pareil dans les deux cas, par conception.
|
||||
Log(' ==> 404 : pas d''instantane OU jeton invalide');
|
||||
Exit;
|
||||
end;
|
||||
ssRefused, ssHttpError, ssNetworkError:
|
||||
begin
|
||||
Log(' ==> echec : ' + LRes.Error);
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
Log(' recu ' + Length(LRes.Container).ToString + ' octets');
|
||||
if LRes.ETag <> '' then
|
||||
begin
|
||||
FETag := LRes.ETag;
|
||||
Log(' ETag conserve : ' + FETag);
|
||||
end;
|
||||
|
||||
Log('');
|
||||
Log('Dechiffrement...');
|
||||
LSw := TStopwatch.StartNew;
|
||||
LPlain := DecryptContainer(LRes.Container, LPwd);
|
||||
LSw.Stop;
|
||||
Log(' ' + LSw.ElapsedMilliseconds.ToString + ' ms pour ' +
|
||||
Length(LPlain).ToString + ' caracteres');
|
||||
|
||||
// Compte vivant / corbeille : c'est ce que le vrai client devra
|
||||
// afficher separement.
|
||||
LJson := TJSONObject.ParseJSONValue(LPlain) as TJSONObject;
|
||||
if LJson <> nil then
|
||||
try
|
||||
if LJson.TryGetValue<TJSONArray>('entries', LArr) then
|
||||
begin
|
||||
LLive := 0; LTrash := 0;
|
||||
for I := 0 to LArr.Count - 1 do
|
||||
if (LArr.Items[I] as TJSONObject).GetValue<Integer>('deleted', 0) = 1
|
||||
then Inc(LTrash) else Inc(LLive);
|
||||
Log(' ' + LLive.ToString + ' entrees, ' +
|
||||
LTrash.ToString + ' en corbeille');
|
||||
end;
|
||||
finally
|
||||
LJson.Free;
|
||||
end;
|
||||
except
|
||||
on E: Exception do
|
||||
Log('ERREUR ' + E.ClassName + ' : ' + E.Message);
|
||||
end;
|
||||
LClient.Free;
|
||||
TThread.Queue(nil, procedure begin FBtnFetch.Enabled := True; end);
|
||||
end).Start;
|
||||
end;
|
||||
|
||||
procedure TForm8.RunSelfTest(Sender: TObject);
|
||||
begin
|
||||
FLog.Lines.Clear;
|
||||
|
||||
Reference in New Issue
Block a user