I saw Http Listener code a little bit more and it’s a real nightmare, it’s seems that checking if the router is valid or not is useless. I don’t want to dive into some details, but what you can do for your particular purpose is something like this:
#include "IHttpRouter.h"
#include "HttpServerModule.h"
#include "Sockets.h"
#include "SocketSubsystem.h"
#include "IPAddress.h"
void Foo()
{
class FHttpServerConnector : public TSharedFromThis<FHttpServerConnector>
{
public:
FHttpServerConnector() : FHttpServerConnector(TEXT("127.0.0.1"), 8888) { }
FHttpServerConnector(FString InAddress, uint32 InPort): IpAddress(InAddress), CurrentPort(InPort)
{
check(IsInGameThread())
SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
if ( !SocketSubsystem )
{
UE_LOG(LogTemp, Error, TEXT("HttpListener - SocketSubsystem Initialization Failed"));
check(false) // will stop execution
}
BindAddress = SocketSubsystem->CreateInternetAddr();
SetIp(IpAddress);
SetPort(CurrentPort);
}
void SetIp(FString InIp)
{
IpAddress = InIp;
bool bIsValidAddress = false;
BindAddress->SetIp(*(IpAddress), bIsValidAddress);
if (!bIsValidAddress)
{
UE_LOG(LogTemp, Error, TEXT("Detected invalid bind address (%s:%u)"),
*IpAddress, CurrentPort);
check(false) // will stop execution
}
}
void SetPort(uint32 InPort)
{
CurrentPort = InPort;
BindAddress->SetPort(CurrentPort);
}
TSharedPtr<IHttpRouter> Connect()
{
if(const TSharedPtr<IHttpRouter> HttpRouter = GetRouterInternal(CurrentPort))
{
return HttpRouter;
}
FUniqueSocket NewSocket = SocketSubsystem->CreateUniqueSocket(NAME_Stream,TEXT("HttpListenerSocket"));
if (!NewSocket)
{
UE_LOG(LogTemp, Error, TEXT("Unable to allocate stream socket"));
check(false) // will stop execution
}
NewSocket->SetNonBlocking(true);
if (!NewSocket->Bind(*BindAddress))
{
UE_LOG(LogTemp, Error, TEXT("Unable to bind to %s"), *BindAddress->ToString(true));
NewSocket.Reset();
return NULL;
}
UE_LOG(LogTemp, Log, TEXT("Created new Socket Connection on %s"), *BindAddress->ToString(true));
// Tear down our Socket
if (NewSocket)
{
NewSocket->Close();
NewSocket.Reset();
UE_LOG(LogTemp, Log, TEXT("Socket Connection exhausted on Port %u"), CurrentPort);
// Now we can safely add our router to the Map
return RouterMap.Add(CurrentPort, FHttpServerModule::Get().GetHttpRouter(CurrentPort));
}
return NULL;
}
FString ToString() const
{
TArray<FStringFormatArg> Args;
Args.Add(FStringFormatArg( IpAddress ));
Args.Add(FStringFormatArg( CurrentPort ));
return FString::Format( TEXT( "{0}:{1}" ), Args );
}
private:
TSharedPtr<IHttpRouter> GetRouterInternal(uint32 InPort)
{
if(TSharedPtr<IHttpRouter, ESPMode::Fast>* TmpPtr = RouterMap.Find(InPort))
{
return *TmpPtr;
}
return NULL;
}
ISocketSubsystem* SocketSubsystem = NULL;
TSharedPtr<FInternetAddr> BindAddress = NULL;
uint32 CurrentPort;
FString IpAddress;
TMap<uint32, TSharedPtr<IHttpRouter>> RouterMap;
};
// Test case - 1
uint32 const PortNumber = 27017;
TSharedPtr<FHttpServerConnector> Connector = MakeShareable(new FHttpServerConnector());
Connector->SetPort(PortNumber);
const TSharedPtr<IHttpRouter> HttpRouter = Connector->Connect();
if (!HttpRouter.IsValid())
{
UE_LOG(LogTemp, Error, TEXT("Unable to bind to the specified Port [%s]"), *Connector->ToString());
// ...
}
// Test case - 2
TSharedPtr<IHttpRouter> HttpRouter2 = Connector->Connect();
while(!HttpRouter2.IsValid())
{
uint32 const PortNum = FMath::RandRange(27017, 29017);
Connector->SetPort(PortNum);
HttpRouter2 = Connector->Connect();
if (HttpRouter.IsValid())
{
break;
}
UE_LOG(LogTemp, Error, TEXT("Unable to bind to the specified Port [%s]"), *Connector->ToString());
// ...
}
// Now you can do whatever you want with Router2
// ...
}
// call foo somewhere else
Foo();
And take a look at this screenshot:
As you can see, in my case, i have a local MongoDB instance running on port 27017 so the call to Bind function of the newly created socket will reveal that. Tell me if this is what you want to do; Take in mind that in real life scenario, most of the times, applications tends to crash if the port is used by another application, so you can specify it manually in a config file for example. It’s also a part of programmer’s job, to reserve a port on a server, don’t let the computer take control on that, also because if you want to expose that port to remote clients it won’t be easy and even more, insecure!