@@ -33,18 +33,24 @@ interface
3333 Delphi.WebMock.Dynamic.RequestStub, Delphi.WebMock.Response,
3434 Delphi.WebMock.ResponseBodySource, Delphi.WebMock.ResponseStatus,
3535 IdContext, IdCustomHTTPServer, IdGlobal, IdHTTPServer,
36- System.Classes, System.Generics.Collections, System.RegularExpressions;
36+ System.Classes, System.Generics.Collections, System.RegularExpressions,
37+ System.SysUtils;
3738
3839type
40+ EWebMockError = class (Exception);
41+ EWebMockExceededBindAttempts = class (EWebMockError);
42+
3943 TWebWockPort = TIdPort;
4044
4145 TWebMock = class (TObject)
46+ class var NextPort: Integer;
4247 private
4348 FServer: TIdHTTPServer;
4449 FBaseURL: string;
4550 FStubRegistry: TList<IWebMockRequestStub>;
4651 FHistory: TList<IWebMockHTTPRequest>;
4752 procedure InitializeServer (const APort: TWebWockPort);
53+ procedure StartServer (const APort: TWebWockPort);
4854 procedure OnServerRequest (AContext: TIdContext;
4955 ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
5056 function GetRequestStub (ARequestInfo: IWebMockHTTPRequest) : IWebMockRequestStub;
@@ -56,9 +62,11 @@ TWebMock = class(TObject)
5662 const AResponseHeaders: TStrings);
5763 procedure SetResponseStatus (AResponseInfo: TIdHTTPResponseInfo;
5864 const AResponseStatus: TWebMockResponseStatus);
65+ function GetNextPort : Integer;
66+ function GetPort : Integer;
5967 property Server: TIdHTTPServer read FServer write FServer;
6068 public
61- constructor Create(const APort: TWebWockPort = 8080 );
69+ constructor Create(const APort: TWebWockPort = 0 );
6270 destructor Destroy; override;
6371 function Assert : TWebMockAssertion;
6472 procedure PrintStubRegistry ;
@@ -75,16 +83,18 @@ TWebMock = class(TObject)
7583 property BaseURL: string read FBaseURL;
7684 property History: TList<IWebMockHTTPRequest> read FHistory;
7785 property StubRegistry: TList<IWebMockRequestStub> read FStubRegistry;
86+ property Port: Integer read GetPort;
7887 end ;
7988
8089implementation
8190
8291uses
8392 Delphi.WebMock.HTTP.Request,
8493 Delphi.WebMock.HTTP.RequestMatcher,
94+ IdException,
8595 IdHTTP,
8696 IdSocketHandle,
87- System.SysUtils ;
97+ IdStack ;
8898
8999{ TWebMock }
90100
@@ -93,7 +103,7 @@ function TWebMock.Assert: TWebMockAssertion;
93103 Result := TWebMockAssertion.Create(History);
94104end ;
95105
96- constructor TWebMock.Create(const APort: TWebWockPort = 8080 );
106+ constructor TWebMock.Create(const APort: TWebWockPort = 0 );
97107begin
98108 inherited Create;
99109 FStubRegistry := TList<IWebMockRequestStub>.Create;
@@ -109,6 +119,22 @@ destructor TWebMock.Destroy;
109119 inherited ;
110120end ;
111121
122+ function TWebMock.GetNextPort : Integer;
123+ var
124+ FIsInitial: Boolean;
125+ begin
126+ AtomicCmpExchange(NextPort, 8080 , 0 , FIsInitial);
127+ if FIsInitial then
128+ Exit(NextPort);
129+
130+ Result := AtomicIncrement(NextPort);
131+ end ;
132+
133+ function TWebMock.GetPort : Integer;
134+ begin
135+ Result := Server.Bindings.Items[0 ].Port;
136+ end ;
137+
112138function TWebMock.GetRequestStub (ARequestInfo: IWebMockHTTPRequest) : IWebMockRequestStub;
113139var
114140 LRequestStub: IWebMockRequestStub;
@@ -131,11 +157,9 @@ procedure TWebMock.InitializeServer(const APort: TWebWockPort);
131157
132158 FServer := TIdHTTPServer.Create;
133159 Server.ServerSoftware := ' Delphi WebMocks' ;
134- Server.DefaultPort := APort;
135160 Server.OnCommandGet := OnServerRequest;
136161 Server.OnCommandOther := OnServerRequest;
137- Server.Active := True;
138- FBaseURL := Format(' http://127.0.0.1:%d/' , [Server.DefaultPort]);
162+ StartServer(APort);
139163end ;
140164
141165procedure TWebMock.OnServerRequest (AContext: TIdContext;
@@ -209,6 +233,44 @@ procedure TWebMock.SetResponseStatus(AResponseInfo: TIdHTTPResponseInfo;
209233 AResponseInfo.ResponseText := AResponseStatus.Text;
210234end ;
211235
236+ procedure TWebMock.StartServer (const APort: TWebWockPort);
237+ var
238+ LAttempt, LMaxAttempts: Integer;
239+ LPort: Integer;
240+ LSocketHandle: TIdSocketHandle;
241+ begin
242+ LAttempt := 0 ;
243+ LMaxAttempts := 3 ;
244+ while not Server.Active do
245+ begin
246+ Inc(LAttempt);
247+ if LAttempt >= LMaxAttempts then
248+ raise EWebMockExceededBindAttempts.Create(' Exceeded attempts to bind port.' );
249+ if APort > 0 then
250+ LPort := APort
251+ else
252+ LPort := GetNextPort;
253+ Server.Bindings.Clear;
254+ LSocketHandle := Server.Bindings.Add;
255+ LSocketHandle.Port := LPort;
256+ try
257+ Server.Active := True;
258+ FBaseURL := Format(' http://127.0.0.1:%d/' , [LSocketHandle.Port]);
259+ except
260+ on E: EIdCouldNotBindSocket do
261+ begin
262+ Server.Active := False;
263+ StartServer(APort);
264+ end ;
265+ on E: EIdSocketError do
266+ begin
267+ Server.Active := False;
268+ StartServer(APort);
269+ end ;
270+ end ;
271+ end ;
272+ end ;
273+
212274function TWebMock.StubRequest (
213275 const AMatcher: TWebMockDynamicRequestMatcher): TWebMockDynamicRequestStub;
214276var
0 commit comments