Skip to content
Snippets Groups Projects
net_raw.cpp 6.54 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
#include "catch.hpp"
#include <string.h>
#include <ftl/net.hpp>
#include <ftl/net/socket.hpp>
#include <ftl/net/listener.hpp>
Nicolas Pope's avatar
Nicolas Pope committed
#include <iostream>
#include <memory>

#ifndef WIN32
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#endif

#ifdef WIN32
#include <windows.h>
#include <winsock.h>
typedef int socklen_t;
#define MSG_WAITALL 0
#endif
Nicolas Pope's avatar
Nicolas Pope committed


// ---- MOCK Server Code -------------------------------------------------------

static bool server = false;
static bool running = false;
static int csock = INVALID_SOCKET;
static int ssock = INVALID_SOCKET;
static fd_set sfdread;
static fd_set sfderror;
static sockaddr_in slocalAddr;

using ftl::net::Socket;
using std::shared_ptr;
Nicolas Pope's avatar
Nicolas Pope committed

void fin_server() {
	if (!server) return;

	//int t = 1;
	//setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR,&t,sizeof(int));

	#ifndef WIN32
	if (csock != INVALID_SOCKET) close(csock);
	if (ssock != INVALID_SOCKET) close(ssock);
	#else
	if (csock != INVALID_SOCKET) closesocket(csock);
	if (ssock != INVALID_SOCKET) closesocket(ssock);
	#endif

	csock = INVALID_SOCKET;
	ssock = INVALID_SOCKET;
	server = false;
}

void init_server() {
	fin_server();
	int port = 7077;
	
	#ifdef WIN32
	WSAData wsaData;
	//If Win32 then load winsock
	if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
		std::cerr << "Socket error\n";
		return;
	}
	#endif

	ssock = socket(AF_INET, SOCK_STREAM, 0);
	if (ssock == INVALID_SOCKET) {
		std::cerr << "Socket error 1\n";
		return;
	}

	int enable = 1;
	if (setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
    	std::cerr << "setsockopt(SO_REUSEADDR) failed" << std::endl;

	//Specify listen port and address
	//memset(&s_localAddr, 0, sizeof(s_localAddr));
	slocalAddr.sin_family = AF_INET;
	slocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	slocalAddr.sin_port = htons(port);
	
	int rc = bind(ssock, (struct sockaddr*)&slocalAddr, sizeof(slocalAddr));
	
	if (rc == SOCKET_ERROR) {
		std::cerr << "Socket error 2\n";
		
		#ifndef WIN32
		close(ssock);
		#else
		closesocket(ssock);
		#endif
		csock = INVALID_SOCKET;
		ssock = INVALID_SOCKET;
		return;
	}

	//Attempt to start listening for connection requests.
	rc = ::listen(ssock, 1);

	if (rc == SOCKET_ERROR) {
		std::cerr << "Socket error 3\n";

		#ifndef WIN32
		close(ssock);
		#else
		closesocket(ssock);
		#endif
		csock = INVALID_SOCKET;
		ssock = INVALID_SOCKET;
		return;
	}
	
	running = true;
	server = true;
}

void send_json(int service, const char *json) {
	int flen = strlen(json) + 2 * sizeof(int);
	char *buf = new char[flen];
	int *idat = (int*)buf;
	idat[0] = strlen(json)+sizeof(int);
	idat[1] = service;
	strcpy((char*)&idat[2], json);
	std::cerr << "SENDING " << flen << std::endl;
	::send(csock, buf, flen, MSG_DONTWAIT);
	//delete buf;
}

int setDescriptors() {
	//Reset all file descriptors
	FD_ZERO(&sfdread);
	FD_ZERO(&sfderror);

	int n = 0;

	//Set file descriptor for the listening socket.
	if (ssock != INVALID_SOCKET) {
		FD_SET(ssock, &sfdread);
		FD_SET(ssock, &sfderror);
		n = ssock;
	}

	//Set the file descriptors for each client
	if (csock != INVALID_SOCKET) {
		FD_SET(csock, &sfdread);
		FD_SET(csock, &sfderror);
		n = csock;
	}

	return n;
}

void mocksend(int sd, uint32_t  service, const std::string &data) {
	//std::cout << "HEX SEND: " << hexStr(data) << std::endl;
	char buf[8+data.size()];
	ftl::net::Header *h = (ftl::net::Header*)&buf;
	h->size = data.size()+4;
	h->service = service;
	std::memcpy(&buf[8],data.data(),data.size());
	
	//std::cout << "HEX SEND2: " << hexStr(fakedata[sd]) << std::endl;
	::send(sd, buf, 8+data.size(), 0);
}

Nicolas Pope's avatar
Nicolas Pope committed
void accept_connection() {
	int n = setDescriptors();

	//Wait for a network event or timeout in 3 seconds
	timeval block;
	block.tv_sec = 1;
	block.tv_usec = 0;
	int selres = select(n+1, &sfdread, 0, &sfderror, &block);

	if (selres > 0 && FD_ISSET(ssock, &sfdread)) {
		int rsize = sizeof(sockaddr_storage);
		sockaddr_storage addr;
		
		std::cout << "Accepted connection!" << std::endl;
Nicolas Pope's avatar
Nicolas Pope committed

		//Finally accept this client connection.
		csock = accept(ssock, (sockaddr*)&addr, (socklen_t*)&rsize);
		
		ftl::net::Handshake hs1;
		hs1.magic = ftl::net::MAGIC;
		hs1.version = ftl::net::version();
		mocksend(csock, FTL_PROTOCOL_HS1, std::string((char*)&hs1, sizeof(hs1)));
Nicolas Pope's avatar
Nicolas Pope committed
	} else {

	}
}

// ---- END MOCK ---------------------------------------------------------------

TEST_CASE("net::connect()", "[net]") {
	init_server();
	REQUIRE(ssock != INVALID_SOCKET);

	shared_ptr<Socket> sock = nullptr;
Nicolas Pope's avatar
Nicolas Pope committed

	SECTION("valid tcp connection using ipv4") {
		sock = ftl::net::connect("tcp://127.0.0.1:7077");
		REQUIRE(sock != nullptr);
		REQUIRE(sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
		accept_connection();
	}

	SECTION("valid tcp connection using hostname") {
		sock = ftl::net::connect("tcp://localhost:7077");
		REQUIRE(sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
		accept_connection();
	}

	SECTION("invalid protocol") {
		sock = ftl::net::connect("http://127.0.0.1:7077");
		REQUIRE(!sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
	}

	SECTION("empty uri") {
		sock = ftl::net::connect("");
		REQUIRE(!sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
	}

	SECTION("null uri") {
		sock = ftl::net::connect(NULL);
		REQUIRE(!sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
	}

	// Disabled due to long timeout
	/*SECTION("incorrect ipv4 address") {
		sock = ftl::net::raw::connect("tcp://192.0.1.1:7077");
		REQUIRE(sock != NULL);
		REQUIRE(sock->isConnected() == false);
		sock = NULL;
	}*/

	SECTION("incorrect dns address") {
		sock = ftl::net::connect("tcp://xryyrrgrtgddgr.com:7077");
		REQUIRE(!sock->isValid());
Nicolas Pope's avatar
Nicolas Pope committed
		REQUIRE(sock->isConnected() == false);
	if (sock && sock->isValid()) {
		ftl::net::wait();
		//sock->data();
Nicolas Pope's avatar
Nicolas Pope committed
		REQUIRE(sock->isConnected());
		REQUIRE(csock != INVALID_SOCKET);
		sock->close();
	}
	fin_server();
}

TEST_CASE("net::listen()", "[net]") {

	SECTION("tcp any interface") {
		REQUIRE( ftl::net::listen("tcp://*:7078")->isListening() );
Nicolas Pope's avatar
Nicolas Pope committed

		SECTION("can connect to listening socket") {
			auto sock = ftl::net::connect("tcp://127.0.0.1:7078");
			REQUIRE(sock->isValid());
			ftl::net::wait(); // Handshake 1
			ftl::net::wait(); // Handshake 2
Nicolas Pope's avatar
Nicolas Pope committed
			REQUIRE(sock->isConnected());

			// TODO Need way of knowing about connection
		}

Nicolas Pope's avatar
Nicolas Pope committed
	}
	
	SECTION("on connection event") {
		auto l = ftl::net::listen("tcp://*:7078");
		REQUIRE( l->isListening() );
		
		bool connected = false;
		
		l->onConnection([&](shared_ptr<Socket> s) {
			ftl::net::wait(); // Wait for handshake
			REQUIRE( s->isConnected() );
			connected = true;
		});
		
		auto sock = ftl::net::connect("tcp://127.0.0.1:7078");
		ftl::net::wait();
		REQUIRE( connected );
		ftl::net::stop();
	}