123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- {
- Sample program by Andrew Haines to test echo clients.
- }
- program echoclient;
- // Define this if you want to test the woSendErrClosesConn flag.
- { $DEFINE USE_NEW_CLOSE_FLAG}
- {$mode objfpc}{$H+}
- uses
- {$IFDEF UNIX}
- cthreads,
- {$ENDIF}
- Classes,
- URIParser,
- fpwebsocket,
- fpwebsocketclient,
- opensslsockets,
- sysutils;
- const
- addresses: array[0..4] of String = (
- 'ws://127.0.0.1:8080/',
- 'ws://ws.vi-server.org:80/mirror',
- 'wss://ws.vi-server.org:443/mirror',
- 'ws://vi-server.org:1939/',
- '' // <-- used for paramStr(1) if present
- );
- type
- { TRunner }
- TRunner = class(TComponent)
- private type
- TStdInThread = class(TThread)
- protected
- FRunner: TRunner;
- StrToSend: String;
- procedure Execute; override;
- public
- constructor Create(ARunner: TRunner);
- end;
- private
- FQuit: Boolean;
- FPump: TWSThreadMessagePump;
- FClient: TWebsocketClient;
- FUri: String;
- FStdInRead: TStdInThread;
- procedure DoConnect(Sender: TObject);
- procedure DoControl(Sender: TObject; aType: TFrameType; const aData: TBytes );
- procedure DoDisconnect(Sender: TObject);
- procedure DoHandShakeResponse(Sender: TObject;
- aResponse: TWSHandShakeResponse; var aAllow: Boolean);
- procedure DoMessage(Sender: TObject; const aMessage: TWSMessage);
- procedure DoPumpError(Sender: TObject; E: Exception);
- procedure DoSendHandshake(Sender: TObject; aHeaders: TStrings);
- procedure SyncReadLine;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- end;
- function GetAddress: String;
- var
- i: Integer;
- lRead: String;
- lHappy: Boolean = False;
- lNum: Longint;
- lDefault: Integer = 0;
- begin
- if ParamCount > 0 then
- begin
- lDefault := High(addresses);
- addresses[lDefault] := ParamStr(1);
- end;
- while not lHappy do
- begin
- WriteLn('Type an address or select a server number. "Q" quits');
- for i := 0 to High(addresses) do
- if addresses[i] <> '' then
- WriteLn(Format(' %d) %s',[i, addresses[i]]));
- Write(Format('Leave empty for default [%d] :', [lDefault]));
- lRead := '';
- ReadLn(lRead);
- if lRead = 'Q' then
- begin
- Result := '';
- lHappy := True;
- end
- else if lRead = '' then
- begin
- lHappy:=True;
- Result := addresses[lDefault];
- end
- else if TryStrToInt(lRead, lNum)
- and ((lNum in [0..High(addresses)-1]) or
- (lNum = High(addresses)) and (addresses[High(addresses)] <> '')) then
- begin
- Result := addresses[lNum];
- lHappy := True;
- end
- else if pos('ws', lRead) = 1 then
- begin
- Result := lRead;
- lHappy := True;
- end
- else
- begin
- WriteLn('Invalid address. Must be ws[s]://<site>[:port]/');
- lHappy := False;
- end;
- end;
- end;
- { TRunner.TReadThread }
- procedure TRunner.TStdInThread.Execute;
- var
- lRead: String;
- begin
- while not Terminated do
- begin
- Write('Enter text to send. "Q" quits. : ');
- ReadLn(lRead);
- StrToSend:=Copy(lRead, 1, MaxInt);
- Synchronize(@FRunner.SyncReadLine);
- Sleep(500); // only for prettier text lines if we get a reply
- end;
- end;
- constructor TRunner.TStdInThread.Create(ARunner: TRunner);
- begin
- FRunner := ARunner;
- inherited Create(False);
- end;
- { TRunner }
- procedure TRunner.DoDisconnect(Sender: TObject);
- begin
- WriteLn(' -- Disconnect --');
- end;
- procedure TRunner.DoHandShakeResponse(Sender: TObject;
- aResponse: TWSHandShakeResponse; var aAllow: Boolean);
- begin
- aAllow:=aAllow; // <-- silence compiler error
- WriteLn(' -- Handshake Response -- ');
- WriteLn(aResponse.RawHeaders.Text);
- end;
- procedure TRunner.DoMessage(Sender: TObject; const aMessage: TWSMessage);
- begin
- if aMessage.IsText then
- begin
- WriteLn('<<< ', aMessage.AsString);
- end;
- end;
- procedure TRunner.DoPumpError(Sender: TObject; E: Exception);
- begin
- WriteLn(' -- Pump Error: ', E.Message);
- Fclient.Active := False;
- FQuit:=True;
- end;
- procedure TRunner.DoSendHandshake(Sender: TObject; aHeaders: TStrings);
- begin
- {
- aHeaders.Values['Accept-Encoding'] := '';
- aHeaders.Values['Origin'] := '';
- aHeaders.Values['Cache-Control'] := 'no-cache';
- aHeaders.Values['Pragma'] := 'no-cache';
- aHeaders.Values['Sec-WebSocket-Extensions'] := 'client_max_window_bits';
- aHeaders.Values['User-Agent'] := 'My Special Websocket';
- }
- WriteLn(' -- Sending Handshake ... ---');
- WriteLn(aHeaders.Text);
- end;
- procedure TRunner.DoConnect(Sender: TObject);
- begin
- WriteLn(Format(' -- Connected to %s --', [FUri]));
- end;
- procedure TRunner.DoControl(Sender: TObject; aType: TFrameType;
- const aData: TBytes);
- begin
- WriteLn;
- WriteLn(' -- Control message: ', aType);
- case aType of
- ftPing:
- begin
- if not (woPongExplicit in FClient.Options) then
- WriteLn(' -- Pong was sent implicitly ---')
- else
- begin
- WriteLn(' -- Sending Pong Explicitly --- ');
- FClient.Pong(Copy(PChar(@aData[0]), 0, Length(aData)));
- end;
- end;
- ftClose:
- begin
- WriteLn(' -- Close recieved --- ');
- WriteLn(' -- Reason: ', TEncoding.UTF8.GetAnsiString(aData));
- FClient.Active := False;
- end
- else
- // the compiler is quiet
- end;
- WriteLn;
- end;
- procedure TRunner.SyncReadLine;
- begin
- if (FStdInRead.StrToSend = 'Q') or (not FClient.Active) then
- begin
- if not FClient.Active then
- WriteLn(' --- Client is not active. Quitting ---')
- else
- WriteLn(' --- Quitting ---');
- FStdInRead.Terminate;
- FClient.Active:=False;
- FQuit := True;
- Exit;
- end;
- WriteLn('>>> ', FStdInRead.StrToSend);
- FClient.SendMessage(FStdInRead.StrToSend);
- end;
- constructor TRunner.Create(AOwner: TComponent);
- var
- lURI: TURI;
- begin
- inherited Create(AOwner);
- FPump := TWSThreadMessagePump.Create(Self);
- FPump.OnError:=@DoPumpError;
- FPump.Execute;
- FClient := TWebsocketClient.Create(Self);
- // Clients must have a mask defined
- FClient.OutGoingFrameMask:=Random(MaxInt);
- FClient.OnConnect:=@DoConnect;
- FCLient.OnControl:=@DoControl;
- FClient.OnDisconnect:=@DoDisconnect;
- FClient.OnMessageReceived:=@DoMessage;
- FClient.OnSendHandShake:=@DoSendHandshake;
- Fclient.OnHandshakeResponse:=@DoHandShakeResponse;
- FClient.MessagePump := FPump;
- {$IFDEF USE_NEW_CLOSE_FLAG}
- FClient.Options:=[woSendErrClosesConn];
- {$ENDIF}
- FUri := GetAddress;
- if FUri = '' then
- begin
- FQuit:=True;
- Exit;
- end;
- lUri := ParseURI(FUri);
- if (lURI.Port = 0) then
- begin
- if lURI.Protocol = 'ws' then
- lURI.Port:=80
- else
- lURI.Port:=443;
- end;
- FClient.HostName:=lURI.Host;
- FClient.Port:=lURI.Port;
- FClient.Resource:=lURI.Path;
- FClient.UseSSL:=lURI.Protocol='wss';
- FClient.Active:=True;
- FStdInRead := TStdInThread.Create(Self);
- end;
- destructor TRunner.Destroy;
- begin
- if Assigned(FStdInRead) then
- begin
- FStdInRead.Terminate;
- FStdInRead.WaitFor;
- FreeAndNil(FStdInRead);
- end;
- inherited Destroy;
- end;
- var
- Runner: TRunner;
- begin
- Runner := TRunner.Create(nil);
- try
- while not Runner.FQuit do
- CheckSynchronize(5000);
- finally
- Runner.Free;
- end;
- end.
|