function {$IFNDEF SBRIDGE}TCRHttpWebRequest{$ELSE}TScHttpWebRequest{$ENDIF}.GetResponse(CancellationToken: TScCancellationToken = nil): {$IFNDEF SBRIDGE}TCRHttpWebResponse{$ELSE}TScHttpWebResponse{$ENDIF};
function StatusIsTimeout(StatusCode: integer): boolean;
begin
Result := (StatusCode = 502) or (StatusCode = 503) or (StatusCode = 504);
end;
function StatusIsRedirection(StatusCode: integer): boolean;
begin
Result := (StatusCode >= 300) and (StatusCode <= 308);
end;
var
ConnectionParameters: TScSecureConnectionParameters;
Hostname: string;
Port: integer;
UserName, Password: string;
ServerMessage: string;
RetryCount, Redirections: integer;
IsInternalTimeout: boolean;
IsSecure: boolean;
UsePooling: boolean;
begin
CheckInactive;
CheckRequest;
FStatusCode := 0;
FStatusDescription := '';
ServerMessage := '';
RetryCount := 0;
Redirections := 0;
IsInternalTimeout := False;
Result := {$IFNDEF SBRIDGE}TCRHttpWebResponse{$ELSE}TScHttpWebResponse{$ENDIF}.Create;
try
try
repeat
DoBeforeSendRequest;
TScHttpParser.ParseURL(FAddress, FScheme, UserName, Password,
FNetworkLocation, FPort, FPath, FResource, FParameters, FQuery, FFragment);
if FScheme = '' then
FScheme := HttpScheme;
if (UserName <> '') or (Password <> '') then begin
FCredentials.UserName := UserName;
FCredentials.Password := Password;
end;
if FPort = '' then
FPortNo := StrToInt(DefaultPort(FScheme))
else
FPortNo := StrToInt(Copy(FPort, 2, Length(FPort)));
if (FProxy.Address <> '') and (FProxy.SocksVersion = svNoSocks) then begin
Hostname := FProxy.Address;
Port := FProxy.Port;
end
else begin
Hostname := FNetworkLocation;
Port := FPortNo;
end;
IsInternalTimeout := False;
IsSecure := AnsiSameText(FScheme, 'https:') or AnsiSameText(FScheme, 'wss:');
FSSLOptions.IdentityDNSName := FNetworkLocation;
Assert(FSecureConnection = nil);
UsePooling := (FConnectionGroupName <> '') and FKeepAlive;
ConnectionParameters := TScSecureConnectionParameters.Create;
try
ConnectionParameters.ConnectionGroupName := FConnectionGroupName;
ConnectionParameters.Hostname := Hostname;
ConnectionParameters.Port := Port;
ConnectionParameters.IPVersion := FIPVersion;
ConnectionParameters.Timeout := FReadWriteTimeout;
ConnectionParameters.IsSecure := IsSecure;
ConnectionParameters.SSLOptions.Assign(FSSLOptions);
if FProxy.SocksVersion <> svNoSocks then begin
ConnectionParameters.ProxyOptions.Hostname := FProxy.Address;
ConnectionParameters.ProxyOptions.Port := FProxy.Port;
ConnectionParameters.ProxyOptions.Username := FProxy.Credentials.UserName;
ConnectionParameters.ProxyOptions.Password := FProxy.Credentials.Password;
ConnectionParameters.ProxyOptions.SocksVersion := FProxy.SocksVersion;
end;
{$IFNDEF SBRIDGE}
ConnectionParameters.IOHandler := FIOHandler;
{$ENDIF}
if UsePooling then begin
FSecureConnection := TScConnectionPoolManager.GetConnection(ConnectionParameters);
FSecureConnection.AddRef;
FSecureConnection.SetReadWriteTimeout(FReadWriteTimeout);
end
else begin
FSecureConnection := TScSecureConnection.Create;
FSecureConnection.CreateVio(ConnectionParameters);
end;
finally
ConnectionParameters.Free;
end;
Result.FSecureConnection := FSecureConnection;
Result.FSecureConnection.AddRef;
Result.FStatusCode := 0;
if CancellationToken <> nil then
CancellationToken.ThrowIfCancellationRequested;
if not FSecureConnection.IsConnected then begin
FSecureConnection.Connect(CancellationToken);
if (FProxy.Address <> '') and (FProxy.SocksVersion = svNoSocks) and (FMethod <> rmCONNECT) then begin
FOldMethod := FMethod;
try
FMethod := rmCONNECT;
SendRequest(CancellationToken);
Result.FMethod := FMethod;
Result.RetrieveHeaders(CancellationToken);
finally
FMethod := FOldMethod;
end;
end;
end;
try
if (Result.FStatusCode = 0) or (Result.FStatusCode = 200) then begin
Result.FStatusCode := 0;
if IsSecure then begin
{$IFDEF SBRIDGE}
if FAssignedSessionInfo <> nil then
FSecureConnection.AssignSession(FAssignedSessionInfo);
{$ENDIF}
FSecureConnection.IsSecure := True;
end;
FOldMethod := FMethod;
SendRequest(CancellationToken);
DoAfterSendRequest;
if Assigned(FOnConnected) then
FOnConnected(Self);
Result.FMethod := FMethod;
Result.RetrieveHeaders(CancellationToken);
DoAfterRetrieveHeaders;
end;
except
on SocketException do
if UsePooling then begin
IsInternalTimeout := True;
Result.FStatusCode := 504;
end
else
raise;
end;
if StatusIsTimeout(Result.FStatusCode) then begin
Inc(RetryCount);
if RetryCount > FMaximumAutomaticReconnections then
if IsInternalTimeout then
FSecureConnection.RaiseLastError
else
raise HttpException.Create(Result.FStatusCode, Result.FStatusDescription);
if Result.FStatusCode = 503 then begin
if (Result.FRetryAfter > 0) and (Result.FRetryAfter <= ReadWriteTimeout) then
Sleep(Result.FRetryAfter * 1000)
else
raise HttpException.Create(Result.FStatusCode, Result.FStatusDescription);
end
else
Sleep(RetryCount * 1000);
end
else
if StatusIsRedirection(Result.FStatusCode) then begin
Inc(Redirections);
if (Redirections > FMaximumAutomaticRedirections) or (Result.FLocation = '') or (FAddress = Result.FLocation) then
raise HttpException.Create(Result.FStatusCode, Result.FStatusDescription)
else
if Result.FStatusCode = 305 then begin
FProxy.Address := Result.FLocation;
FProxy.SocksVersion := svNoSocks;
end
else
FAddress := Result.FLocation;
if Result.FCookies.Count > 0 then begin
FCookies.Clear;
FCookies.AddStrings(Result.FCookies);
end;
if FMethod = rmCONNECT then
FMethod := rmGET;
// FURL := 'http://' + Host + '/' + FURL;
if (FAddress <> '') and (Pos('//', FAddress) = 0) and (FAddress[1] = '/') then
FAddress := FScheme + '//' + FNetworkLocation + FPort + FAddress;
end
else
if (Result.FStatusCode = 401) and Assigned(FOnAuthenticationNeeded) then
FOnAuthenticationNeeded(Self)
else
if Result.FStatusCode >= 400 then begin
if (Result.ContentLength > 0) or Result.FChunked then
ServerMessage := Result.ReadAsString;
if not (ConvertToStatusCode(Result.FStatusCode) in FAllowedStatuses) then
raise HttpException.Create(Result.FStatusCode, Result.FStatusDescription, ServerMessage);
end;
if StatusIsRedirection(Result.FStatusCode) or StatusIsTimeout(Result.FStatusCode) then begin
if FSecureConnection <> nil then begin
FSecureConnection.Disconnect;
FSecureConnection.Release;
FSecureConnection := nil;
end;
Result.FSecureConnection.Release;
Result.FSecureConnection := nil;
end;
until not StatusIsRedirection(Result.FStatusCode) and not StatusIsTimeout(Result.FStatusCode);
finally
if Assigned(FOnConnected) then
FOnConnected(Self);
end;
Result.FResponseUri := FAddress;
except
on E: Exception do begin
FStatusCode := Result.FStatusCode;
FStatusDescription := Result.FStatusDescription;
if FStatusDescription = '' then
FStatusDescription := E.Message;
if FSecureConnection <> nil then begin
FSecureConnection.Disconnect;
FSecureConnection.Release;
FSecureConnection := nil;
end;
Result.Free; // FreeAndNil(Result) ;
if not IsInternalTimeout and (FStatusCode <> 0) and (FStatusCode <> 200) then
raise HttpException.Create(FStatusCode, FStatusDescription, ServerMessage)
else
raise;
end;
end;
end;