Download

How to get unused Http Router?

uint32 PortNum = FMath::RandRange(49152, 65535);;
FHttpServerModule* HttpServerModule = &FHttpServerModule::Get();
TSharedPtr<IHttpRouter> HttpRouter = HttpServerModule->GetHttpRouter(PortNum);

Currently, I am getting the http router in this way. However, I am concerned that there may be cases where the corresponding port number is being used.

Is there a way to check if the http router of the port number is in use?

Your concernes are true, make sure that your Router is always valid before binding some path to an invalid Router by calling BindRoute() function. In order to check your Router just do the following:

	const int32 Port = 8888;
	TSharedPtr<IHttpRouter> HttpRouter = FHttpServerModule::Get().GetHttpRouter(Port);
	if (!HttpRouter) // or !HttpRouter.IsValid()
	{
		UE_LOG(LogTemp, Error,  TEXT("Unable to bind to the specified Port [%d]"), Port);
                // ...
	}

If you want to choose a port from that RandRange, it’ll be better to store always the previous value inside a TMap, so you can first check if you have some port number cached in it and if there’s nothing, check the port validity. if the validation fails store the current port tested inside the map and repeate the proccess again and again until you find an unused port number. Good luck.

1 Like

Thank you for answer. But I have a question. If the router is not valid, can it be regarded as a router that is already in use?

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!

1 Like

Thank you so much for the reply based on sincere research! This really gave me a lot of strength.
Thank you!