Skip to content
Snippets Groups Projects
Commit 722199e1 authored by Nicolas Pope's avatar Nicolas Pope
Browse files

Initial net code

parent bba50d5a
No related branches found
No related tags found
No related merge requests found
# ftl
# Future Tech Lab
Future Tech Lab
\ No newline at end of file
This monorepo contains all elements of the FTL software system.
* p2p-remote-array : Access 2D memory arrays on other machines in P2P group
* fabric : C++ library implementing the neural fabric
* fabric-modules : custom native fabric relations
* fabric-js : Node.js bindings for fabric
The architecture relies on NodeJS with C++ native addons for processing.
cmake_minimum_required (VERSION 2.8.11)
include (CheckIncludeFile)
include (CheckFunctionExists)
project (libftlnet)
#find_package(PkgConfig)
#pkg_check_modules(GTKMM gtkmm-3.0)
# Need to include staged files and libs
include_directories(${PROJECT_SOURCE_DIR}/includes)
include_directories(${PROJECT_BINARY_DIR})
set(ftl_VERSION_MAJOR "1")
set(ftl_VERSION_MINOR "0")
set(ftl_VERSION_PATCH "0")
set(CMAKE_CXX_FLAGS "-pthread -fopenmp -std=c++14 -Wall -Wno-deprecated -Werror -Wno-psabi")
set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG -pg -Wall -Werror")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
SET(CMAKE_USE_RELATIVE_PATHS ON)
set(FTLSOURCE
src/raw.cpp
)
check_include_file("uriparser/Uri.h" HAVE_URI_H)
if(NOT HAVE_URI_H)
message(FATAL_ERROR "Uriparser not found")
endif()
check_function_exists(uriParseSingleUriA HAVE_URIPARSESINGLE)
add_library(libftlnet ${FTLSOURCE})
target_include_directories(libftlnet PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(libftlnet pthread)
ADD_SUBDIRECTORY(test)
#ifndef _FTL_NET_ICE_HPP_
#define _FTL_NET_ICE_HPP_
#include <vector>
#include <stdint.h>
#include <string>
namespace ftl {
namespace net {
namespace raw {
class Socket;
}
namespace ice {
/**
* Obtain a list of ICE candidate URLs.
*/
int candidates(std::vector<std::string> &c, uint16_t lport, bool tcp);
int stun(std::string &c, uint16_t lport, bool tcp);
int stun(std::string &c, const char *stunuri, uint16_t lport);
/**
* Request that two sockets connect together directly. This is the main
* entry point into the ICE mechanism, but should be initiated by the FTL
* server and not individual nodes.
*/
//int link(ftl::net::raw::Socket *a, ftl::net::raw::Socket *b);
/**
* Attempt to connect to each candidate in order using the ICE standard.
*/
ftl::net::raw::Socket *connect(std::vector<std::string> &addrs);
} // ice
} // net
} // ftl
#endif // _FTL_NET_ICE_HPP_
#ifndef _FTL_NET_HPP_
#define _FTL_NET_HPP_
#include <functional>
#include <sstream>
#include <string>
#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
namespace ftl {
namespace net {
namespace raw {
class Socket;
int listen(const char *uri);
void stop();
int run(bool blocking);
/**
* Accepts tcp, ipc and ws URIs. An example would be:
* ws://ftl.utu.fi/api/connect
*/
Socket *connect(const char *uri);
typedef std::function<void(int, std::string&)> sockdatahandler_t;
typedef std::function<void(int)> sockerrorhandler_t;
typedef std::function<void()> sockconnecthandler_t;
typedef std::function<void(int)> sockdisconnecthandler_t;
typedef std::function<void(Socket&, int, std::string&)> datahandler_t;
typedef std::function<void(Socket&, int)> errorhandler_t;
typedef std::function<void(Socket&)> connecthandler_t;
typedef std::function<void(Socket&)> disconnecthandler_t;
class Socket {
public:
int close();
int send(uint32_t service, std::string &data);
int send(uint32_t service, std::ostringstream &data);
int send(uint32_t service, void *data, int length);
friend int ftl::net::raw::listen(const char*);
friend Socket *ftl::net::raw::connect(const char*);
friend int ftl::net::raw::run(bool);
int _socket() { return m_sock; };
bool isConnected() { return m_sock != INVALID_SOCKET; };
void onMessage(sockdatahandler_t handler) { m_handler = handler; }
void onError(sockerrorhandler_t handler) {}
void onConnect(sockconnecthandler_t handler) {}
void onDisconnect(sockdisconnecthandler_t handler) {}
protected:
Socket(int s, const char *uri);
~Socket();
bool data();
void error();
char m_addr[INET6_ADDRSTRLEN];
private:
const char *m_uri;
int m_sock;
size_t m_pos;
char *m_buffer;
sockdatahandler_t m_handler;
static const int MAX_MESSAGE = 10*1024*1024; // 10Mb currently
static const int BUFFER_SIZE = MAX_MESSAGE + 16;
};
/**
* Get the number of current connections.
* @return Connection count
*/
int connections();
void onMessage(datahandler_t handler);
void onConnect(connecthandler_t handler);
void onDisconnect(disconnecthandler_t handler);
void onError(errorhandler_t handler);
const int MAX_CONNECTIONS = 100; // TODO Is this a good number?
} // raw
} // net
} // ftl
#endif // _FTL_NET_HPP_
//#define _GNU_SOURCE
#include <ftl/net/ice.hpp>
#include <ftl/net/stun.hpp>
#include <ftl/uri.hpp>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <linux/if_link.h>
#define MAXLINE 200
using ftl::URI;
struct Candidate {
Candidate() : port(0) { addr[0] = 0; }
char addr[15];
uint16_t port;
};
int stun_internal(std::string &c, bool tcp, uint16_t lport, std::string &host, uint16_t port) {
int sockfd;
sockaddr_in servaddr;
sockaddr_in localaddr;
unsigned char bindingReq[20];
unsigned char buf[MAXLINE] = {0};
sockfd = socket(AF_INET, (tcp) ? SOCK_STREAM : SOCK_DGRAM, 0);
hostent *hoste = gethostbyname(host.c_str());
if (hoste == NULL) {
std::cerr << "Host not found: " << host << std::endl;
close(sockfd);
return -4;
}
// STUN Server
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = ((in_addr *)(hoste->h_addr))->s_addr;
servaddr.sin_port = htons(port);
// local
bzero(&localaddr, sizeof(localaddr));
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(lport);
int n = bind(sockfd,(sockaddr *)&localaddr,sizeof(localaddr));
// Build message packet
* (short *)(&bindingReq[0]) = htons(0x0001); // stun_method
* (short *)(&bindingReq[2]) = htons(0x0000); // msg_length
* (int *)(&bindingReq[4]) = htonl(0x2112A442);// magic cookie
*(int *)(&bindingReq[8]) = htonl(0x63c7117e); // transacation ID
*(int *)(&bindingReq[12])= htonl(0x0714278f);
*(int *)(&bindingReq[16])= htonl(0x5ded3221);
if (!tcp) {
n = sendto(sockfd, bindingReq, sizeof(bindingReq),0,(sockaddr *)&servaddr, sizeof(servaddr));
} else {
// TODO TCP STUN
}
if (n < 0) {
// Error
std::cerr << "STUN send error : " << host << ":" << port << std::endl;
close(sockfd);
return -2;
}
// Sleep!!? Select?
if (!tcp) {
n = recvfrom(sockfd, buf, MAXLINE, 0, NULL,0); // recv UDP
} else {
// TODO TCP
}
if (n < 0) {
// Error
std::cerr << "STUN recv error : " << host << ":" << port << std::endl;
close(sockfd);
return -2;
}
if (*(short *)(&buf[0]) == htons(0x0101)) {
std::cerr << "STUN Success " << n << std::endl;
n = htons(*(short *)(&buf[2]));
size_t i = 20; // Header is 20 bytes, so skip
Candidate cc;
while(i<sizeof(buf)) {
short attr_type = htons(*(short *)(&buf[i]));
short attr_length = htons(*(short *)(&buf[i+2]));
std::cerr << " -- Attr type: " << std::hex << attr_type << std::dec << std::endl;
if (attr_type == 0x0001) {
// parse : port, IP
cc.port = ntohs(*(short *)(&buf[i+6]));
sprintf(cc.addr,"%d.%d.%d.%d",buf[i+8],buf[i+9],buf[i+10],buf[i+11]);
break;
} else if (attr_type == 0x0020) {
// parse : port, IP
cc.port = ntohs(*(short *)(&buf[i+6]));
cc.port ^= 0x2112;
sprintf(cc.addr,"%d.%d.%d.%d",buf[i+8]^0x21,buf[i+9]^0x12,buf[i+10]^0xA4,buf[i+11]^0x42);
break;
}
i += (4 + attr_length);
}
c = ((tcp) ? "tcp://" : "udp://");
c += cc.addr;
c += ":";
c += std::to_string(cc.port);
close(sockfd);
return 0;
}
close(sockfd);
return -3;
}
int ftl::net::ice::stun(std::string &c, const char *stunuri, uint16_t lport) {
URI uri(stunuri);
if (uri.getProtocol() == URI::SCHEME_UDP) return stun_internal(c, false, lport, uri.getHost(), uri.getPort());
else if (uri.getProtocol() == URI::SCHEME_TCP) return stun_internal(c, true, lport, uri.getHost(), uri.getPort());
else return -1;
}
int ftl::net::ice::stun(std::string &c, uint16_t lport, bool tcp) {
// Choose a STUN server
std::string uristr = (tcp) ? "tcp://" : "udp://";
uristr += "stun.l.google.com:19302";
return ftl::net::ice::stun(c, uristr.c_str(), lport);
}
int local_interfaces(std::vector<std::string> &c, bool tcp, uint16_t lport) {
ifaddrs *ifaddr, *ifa;
int s, n;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1) {
return -1;
}
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL) continue;
auto family = ifa->ifa_addr->sa_family;
if (family == AF_INET) { // || AF_INET6
s = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (s != 0) return -1;
c.push_back(std::string((tcp) ? "tcp://" : "udp://") + host + ":" + std::to_string(lport));
}
}
freeifaddrs(ifaddr);
return 0;
}
int ftl::net::ice::candidates(std::vector<std::string> &c, uint16_t lport, bool tcp) {
local_interfaces(c, tcp, lport);
std::string stunstr;
ftl::net::ice::stun(stunstr, lport, tcp);
c.push_back(stunstr);
// TURN or proxy
return 0;
}
#include <ftl/net/raw.hpp>
#include <ftl/uri.hpp>
#include <vector>
#include <iostream>
#ifndef WIN32
#include <errno.h>
#include <fcntl.h>
#endif
#undef ERROR
using ftl::URI;
using ftl::net::raw::Socket;
static std::vector<Socket*> sockets;
static int ssock = INVALID_SOCKET;
static fd_set sfdread;
static fd_set sfderror;
static sockaddr_in slocalAddr;
static int freeSocket() {
int freeclient = -1;
//Find a free client slot and allocated it
for (unsigned int i=0; i<sockets.size(); i++) {
if (sockets[i] == 0) { // CHECK, was 0 which seems wrong
freeclient = i;
break;
}
}
//Max clients reached, so send error
if (freeclient == -1) {
if (sockets.size() < ftl::net::raw::MAX_CONNECTIONS) {
sockets.push_back(0);
freeclient = sockets.size()-1;
} else {
// exceeded max connections
return -1;
}
}
return freeclient;
}
static int setDescriptors() {
//Reset all file descriptors
FD_ZERO(&sfdread);
FD_ZERO(&sfderror);
int n = 0;
//Set file descriptor for the listening socket.
if (ssock) {
FD_SET(ssock, &sfdread);
FD_SET(ssock, &sfderror);
n = ssock;
}
//Set the file descriptors for each client
for (auto s : sockets) {
if (s != NULL && s->isConnected()) {
if (s->_socket() > n) {
n = s->_socket();
}
FD_SET(s->_socket(), &sfdread);
FD_SET(s->_socket(), &sfderror);
}
}
return n;
}
static int tcpListen(URI &uri) {
//std::cerr << "TCP Listen: " << uri.getHost() << " : " << uri.getPort() << std::endl;
#ifdef WIN32
WSAData wsaData;
//If Win32 then load winsock
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
return 1;
}
#endif
ssock = socket(AF_INET, SOCK_STREAM, 0);
if (ssock == INVALID_SOCKET) {
return 1;
}
//Specify listen port and address
slocalAddr.sin_family = AF_INET;
slocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); // TODO, use that given in URI
slocalAddr.sin_port = htons(uri.getPort());
int rc = ::bind(ssock, (struct sockaddr*)&slocalAddr, sizeof(slocalAddr));
if (rc == SOCKET_ERROR) {
#ifndef WIN32
close(ssock);
#else
closesocket(ssock);
#endif
ssock = INVALID_SOCKET;
return 1;
}
//Attempt to start listening for connection requests.
rc = ::listen(ssock, 1);
if (rc == SOCKET_ERROR) {
#ifndef WIN32
close(ssock);
#else
closesocket(ssock);
#endif
ssock = INVALID_SOCKET;
return 1;
}
return 0;
}
static int wsListen(URI &uri) {
return 1;
}
static int tcpConnect(URI &uri) {
int rc;
sockaddr_in destAddr;
//std::cerr << "TCP Connect: " << uri.getHost() << " : " << uri.getPort() << std::endl;
#ifdef WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(1,1), &wsaData) != 0) {
//ERROR
return INVALID_SOCKET;
}
#endif
//We want a TCP socket
int csocket = socket(AF_INET, SOCK_STREAM, 0);
if (csocket == INVALID_SOCKET) {
return INVALID_SOCKET;
}
#ifdef WIN32
HOSTENT *host = gethostbyname(uri.getHost().c_str());
#else
hostent *host = gethostbyname(uri.getHost().c_str());
#endif
if (host == NULL) {
#ifndef WIN32
close(csocket);
#else
closesocket(csocket);
#endif
std::cerr << "Address not found : " << uri.getHost() << std::endl;
return INVALID_SOCKET;
}
destAddr.sin_family = AF_INET;
destAddr.sin_addr.s_addr = ((in_addr *)(host->h_addr))->s_addr;
destAddr.sin_port = htons(uri.getPort());
// Make nonblocking
/*long arg = fcntl(csocket, F_GETFL, NULL));
arg |= O_NONBLOCK;
fcntl(csocket, F_SETFL, arg) < 0)*/
rc = ::connect(csocket, (struct sockaddr*)&destAddr, sizeof(destAddr));
if (rc < 0) {
if (errno == EINPROGRESS) {
} else {
#ifndef WIN32
close(csocket);
#else
closesocket(csocket);
#endif
std::cerr << "Could not connect" << std::endl;
return INVALID_SOCKET;
}
}
// Make blocking again
/*rg = fcntl(csocket, F_GETFL, NULL));
arg &= (~O_NONBLOCK);
fcntl(csocket, F_SETFL, arg) < 0)*/
// Handshake??
return csocket;
}
static int wsConnect(URI &uri) {
return 1;
}
int ftl::net::raw::listen(const char *pUri) {
URI uri(pUri);
if (uri.getProtocol() == URI::SCHEME_TCP) {
return tcpListen(uri);
} else if (uri.getProtocol() == URI::SCHEME_WS) {
return wsListen(uri);
} else {
return 1;
}
}
void ftl::net::raw::stop() {
for (auto s : sockets) {
if (s != NULL) s->close();
}
sockets.clear();
#ifndef WIN32
if (ssock != INVALID_SOCKET) close(ssock);
#else
if (ssock != INVALID_SOCKET) closesocket(ssock);
#endif
ssock = INVALID_SOCKET;
}
Socket *ftl::net::raw::connect(const char *pUri) {
URI uri(pUri);
if (uri.getProtocol() == URI::SCHEME_TCP) {
int csock = tcpConnect(uri);
Socket *s = new Socket(csock, pUri);
int fs = freeSocket();
if (fs >= 0) {
sockets[fs] = s;
return s;
} else {
return NULL;
}
} else if (uri.getProtocol() == URI::SCHEME_WS) {
wsConnect(uri);
return NULL;
} else {
return NULL;
}
}
int ftl::net::raw::run(bool blocking) {
timeval block;
int n;
int selres = 1;
//if (ssock == INVALID_SOCKET) return 1;
bool active = true;
bool repeat = false;
while (active || repeat) {
n = setDescriptors();
//Wait for a network event or timeout in 3 seconds
block.tv_sec = (repeat) ? 0 : 3;
block.tv_usec = 0;
selres = select(n+1, &sfdread, 0, &sfderror, &block);
repeat = false;
active = blocking;
//Some kind of error occured, it is usually possible to recover from this.
if (selres <= 0) {
return 1;
}
//If connection request is waiting
if (FD_ISSET(ssock, &sfdread)) {
int rsize = sizeof(sockaddr_storage);
sockaddr_storage addr;
int freeclient = freeSocket();
if (freeclient >= 0) {
// TODO Limit connection rate or allow a pause in accepting
// TODO Send auto reject message under heavy load
//Finally accept this client connection.
int csock = accept(ssock, (sockaddr*)&addr, (socklen_t*)&rsize);
if (csock != INVALID_SOCKET) {
Socket *sock = new Socket(csock, NULL);
sockets[freeclient] = sock;
//Save the ip address
// deal with both IPv4 and IPv6:
if (addr.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
//port = ntohs(s->sin_port);
inet_ntop(AF_INET, &s->sin_addr, sock->m_addr, INET6_ADDRSTRLEN);
} else { // AF_INET6
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
//port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, sock->m_addr, INET6_ADDRSTRLEN);
}
}
}
}
//Also check each clients socket to see if any messages or errors are waiting
for (auto s : sockets) {
if (s != NULL && s->isConnected()) {
//If message received from this client then deal with it
if (FD_ISSET(s->_socket(), &sfdread)) {
repeat |= s->data();
//An error occured with this client.
} else if (FD_ISSET(s->_socket(), &sfderror)) {
s->error();
}
}
}
}
return 1;
}
int Socket::close() {
if (isConnected()) {
#ifndef WIN32
::close(m_sock);
#else
closesocket(m_sock);
#endif
m_sock = INVALID_SOCKET;
// Attempt auto reconnect?
}
return 0;
}
void Socket::error() {
int err;
uint32_t optlen = sizeof(err);
getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &err, &optlen);
std::cerr << "GOT A SOCKET ERROR : " << err << std::endl;
//close();
}
bool Socket::data() {
//std::cerr << "GOT SOCKET DATA" << std::endl;
//Read data from socket
size_t n = 0;
uint32_t len = 0;
if (m_pos < 4) {
n = 4 - m_pos;
} else {
len = *(int*)m_buffer;
n = len+4-m_pos;
}
while (m_pos < len+4) {
if (len > MAX_MESSAGE) {
close();
return false; // Prevent DoS
}
const int rc = recv(m_sock, m_buffer+m_pos, n, 0);
if (rc > 0) {
m_pos += static_cast<size_t>(rc);
if (m_pos < 4) {
n = 4 - m_pos;
} else {
len = *(int*)m_buffer;
n = len+4-m_pos;
}
} else if (rc == EWOULDBLOCK || rc == 0) {
// Data not yet available
return false;
} else {
// Close socket due to error
close();
return false;
}
}
// All data available
if (m_handler) {
uint32_t service = ((uint32_t*)m_buffer)[1];
auto d = std::string(m_buffer+8, len-4);
//std::cerr << "DATA : " << service << " -> " << d << std::endl;
m_handler(service, d);
}
m_pos = 0;
return true;
}
Socket::Socket(int s, const char *uri) : m_uri(uri), m_sock(s), m_pos(0) {
// Allocate buffer
m_buffer = new char[BUFFER_SIZE];
}
Socket::~Socket() {
// Delete socket buffer
delete [] m_buffer;
}
add_executable(tests EXCLUDE_FROM_ALL
./tests.cpp
./net_raw.cpp
../src/raw.cpp
./ice.cpp
../src/ice.cpp
./uri.cpp
)
target_include_directories(tests PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(tests uriparser)
This diff is collapsed.
#include "catch.hpp"
#include <string.h>
#include <ftl/net/ice.hpp>
#include <iostream>
TEST_CASE( "net::ice::stun()", "[ice][net]" ) {
std::string c;
SECTION( "manual stun server" ) {
REQUIRE( ftl::net::ice::stun(c, "udp://stun.l.google.com:19302", 7079) == 0 );
}
SECTION( "automatic stun server" ) {
REQUIRE( ftl::net::ice::stun(c, 7079, false) == 0 );
}
std::cerr << "STUN Result: " << c << std::endl;
REQUIRE(c.size() > 0);
}
TEST_CASE( "net::ice::candidates()", "[ice][net]" ) {
std::vector<std::string> cc;
SECTION( "udp candidate list" ) {
ftl::net::ice::candidates(cc, 7079, false);
}
REQUIRE( cc.size() >= 2 );
for (auto x : cc) std::cerr << "Candidate: " << x << std::endl;
}
#include "catch.hpp"
#include <string.h>
#include <ftl/net/raw.hpp>
#include <ftl/net/services.hpp>
#include <iostream>
// ---- 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::raw::Socket;
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 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;
//Finally accept this client connection.
csock = accept(ssock, (sockaddr*)&addr, (socklen_t*)&rsize);
} else {
}
}
// ---- END MOCK ---------------------------------------------------------------
TEST_CASE("net::connect()", "[net]") {
init_server();
REQUIRE(ssock != INVALID_SOCKET);
Socket *sock = NULL;
SECTION("valid tcp connection using ipv4") {
sock = ftl::net::raw::connect("tcp://127.0.0.1:7077");
REQUIRE(sock != NULL);
accept_connection();
}
SECTION("valid tcp connection using hostname") {
sock = ftl::net::raw::connect("tcp://localhost:7077");
REQUIRE(sock != NULL);
accept_connection();
}
SECTION("invalid protocol") {
sock = ftl::net::raw::connect("http://127.0.0.1:7077");
REQUIRE(sock == NULL);
}
SECTION("empty uri") {
sock = ftl::net::raw::connect("");
REQUIRE(sock == NULL);
}
SECTION("null uri") {
sock = ftl::net::raw::connect(NULL);
REQUIRE(sock == NULL);
}
// 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::raw::connect("tcp://xryyrrgrtgddgr.com:7077");
REQUIRE(sock != NULL);
REQUIRE(sock->isConnected() == false);
sock = NULL;
}
if (sock) {
REQUIRE(sock->isConnected());
REQUIRE(csock != INVALID_SOCKET);
sock->close();
}
fin_server();
}
TEST_CASE("net::listen()", "[net]") {
SECTION("tcp any interface") {
REQUIRE( ftl::net::raw::listen("tcp://*:7078") == 0);
SECTION("can connect to listening socket") {
Socket *sock = ftl::net::raw::connect("tcp://127.0.0.1:7078");
REQUIRE(sock != NULL);
REQUIRE(sock->isConnected());
ftl::net::raw::run(false);
// TODO Need way of knowing about connection
}
ftl::net::raw::stop();
}
}
TEST_CASE("Socket.onMessage()", "[net]") {
// Need a fake server...
init_server();
Socket *sock = ftl::net::raw::connect("tcp://127.0.0.1:7077");
REQUIRE(sock != NULL);
REQUIRE(sock->isConnected());
accept_connection();
SECTION("small valid message") {
send_json(ftl::net::SERVICE_CONFIGURE, "{message: \"Hello\"}");
bool msg = false;
sock->onMessage([&](int service, std::string &data) {
REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
REQUIRE(data == "{message: \"Hello\"}");
msg = true;
});
ftl::net::raw::run(false);
REQUIRE(msg);
}
SECTION("empty message") {
send_json(ftl::net::SERVICE_CONFIGURE, "");
bool msg = false;
sock->onMessage([&](int service, std::string &data) {
REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
REQUIRE(data == "");
msg = true;
});
ftl::net::raw::run(false);
REQUIRE(msg);
}
SECTION("multiple valid messages") {
send_json(ftl::net::SERVICE_CONFIGURE, "{message: \"Hello\"}");
send_json(ftl::net::SERVICE_CONFIGURE, "{test: \"world\"}");
int msg = 0;
sock->onMessage([&](int service, std::string &data) {
REQUIRE(service == ftl::net::SERVICE_CONFIGURE);
if (msg == 0) REQUIRE(data == "{message: \"Hello\"}");
else REQUIRE(data == "{test: \"world\"}");
msg++;
});
ftl::net::raw::run(false);
REQUIRE(msg == 2);
}
SECTION("disconnected does not get message") {
send_json(ftl::net::SERVICE_CONFIGURE, "world");
bool msg = false;
sock->onMessage([&](int service, std::string &data) {
msg = true;
});
sock->close();
ftl::net::raw::run(false);
REQUIRE(!msg);
}
fin_server();
}
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "catch.hpp"
#include <ftl/uri.hpp>
using ftl::URI;
SCENARIO( "URI() can parse valid URIs", "[utility]" ) {
GIVEN( "a valid scheme, no port or path" ) {
URI uri("http://localhost");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( uri.getPort() == 0 );
REQUIRE (uri.getPath() == "" );
}
GIVEN( "a valid scheme with port and path" ) {
URI uri("http://localhost:8080/test/case.html");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( uri.getPort() == 8080 );
REQUIRE (uri.getPath() == "/test/case.html" );
}
GIVEN( "a valid scheme with path and query" ) {
URI uri("ftl://utu.fi/test/case.html?v=1");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_FTL );
REQUIRE( uri.getHost() == "utu.fi" );
REQUIRE( uri.getPath() == "/test/case.html" );
REQUIRE( uri.getQuery() == "v=1" );
REQUIRE( uri.getBaseURI() == "ftl://utu.fi/test/case.html" );
}
}
SCENARIO( "URI() fails gracefully with invalid URIs", "[utility]" ) {
GIVEN( "an invalid scheme" ) {
URI uri("@://localhost:8080/test/case.html");
REQUIRE( uri.getScheme() == URI::SCHEME_NONE );
REQUIRE( !uri.isValid() );
}
GIVEN( "an invalid port" ) {
URI uri("http://localhost:k/test/case.html");
REQUIRE( !uri.isValid() );
}
GIVEN( "an empty URI" ) {
URI uri("");
REQUIRE( !uri.isValid() );
}
GIVEN( "only a scheme" ) {
URI uri("tcp:");
REQUIRE( !uri.isValid() );
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment