proof of concept server implementation

This commit is contained in:
Benjamin Sergeant
2018-12-29 23:15:27 -08:00
parent ea07afcc0b
commit 0ee71e9a09
9 changed files with 324 additions and 3 deletions

View File

@ -21,13 +21,13 @@ namespace ix
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
static void configure(int sockfd);
private:
static bool connectToAddress(const struct addrinfo *address,
int& sockfd,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
static void configure(int sockfd);
};
}

View File

@ -0,0 +1,133 @@
/*
* IXWebSocketServer.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include "IXWebSocketServer.h"
#include "IXWebSocketTransport.h"
#include "IXWebSocket.h"
#include "IXSocketConnect.h" // for configure, cleanup, move it back to Socket
#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
namespace ix
{
WebSocketServer::WebSocketServer(int port) :
_port(port)
{
}
WebSocketServer::~WebSocketServer()
{
}
std::pair<bool, std::string> WebSocketServer::run()
{
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.hala001/server.htm
struct sockaddr_in client; /* client address information */
struct sockaddr_in server; /* server address information */
int s; /* socket for accepting connections */
int ns; /* socket connected to client */
/*
* Get a socket for accepting connections.
*/
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
std::string errMsg = "Socket()";
return std::make_pair(false, errMsg);
}
/*
* Bind the socket to the server address.
*/
server.sin_family = AF_INET;
server.sin_port = htons(_port);
server.sin_addr.s_addr = INADDR_ANY;
// server.sin_addr.s_addr = INADDR_LOOPBACK;
if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0)
{
std::string errMsg = "Bind()";
return std::make_pair(false, errMsg);
}
/*
* Listen for connections. Specify the backlog as 1.
*/
if (listen(s, 1) != 0)
{
std::string errMsg = "Listen()";
return std::make_pair(false, errMsg);
}
/*
* Accept a connection.
*/
socklen_t address_len = sizeof(socklen_t);
if ((ns = accept(s, (struct sockaddr *)&client, &address_len)) == -1)
{
std::string errMsg = "Accept()";
return std::make_pair(false, errMsg);
}
// We only handle one connection so far, and we just 'print received message from it'
ix::WebSocketTransport webSocketTransport;
SocketConnect::configure(ns); // We could/should do this inside initFromSocket
webSocketTransport.initFromSocket(ns);
for (;;)
{
webSocketTransport.poll();
// 1. Dispatch the incoming messages
webSocketTransport.dispatch(
[this](const std::string& msg,
size_t wireSize,
bool decompressionError,
WebSocketTransport::MessageKind messageKind)
{
WebSocketMessageType webSocketMessageType;
switch (messageKind)
{
case WebSocketTransport::MSG:
{
webSocketMessageType = WebSocket_MessageType_Message;
} break;
case WebSocketTransport::PING:
{
webSocketMessageType = WebSocket_MessageType_Ping;
} break;
case WebSocketTransport::PONG:
{
webSocketMessageType = WebSocket_MessageType_Pong;
} break;
}
WebSocketErrorInfo webSocketErrorInfo;
webSocketErrorInfo.decompressionError = decompressionError;
// _onMessageCallback(webSocketMessageType, msg, wireSize,
// webSocketErrorInfo, WebSocketCloseInfo(),
// WebSocketHttpHeaders());
// WebSocket::invokeTrafficTrackerCallback(msg.size(), true);
std::cout << "received: " << msg << std::endl;
});
std::chrono::duration<double, std::milli> wait(10);
std::this_thread::sleep_for(wait);
}
return std::make_pair(true, "");
}
}

View File

@ -0,0 +1,24 @@
/*
* IXWebSocketServer.h
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#pragma once
#include <utility> // pair
#include <string>
namespace ix
{
class WebSocketServer {
public:
WebSocketServer(int port = 8080);
virtual ~WebSocketServer();
std::pair<bool, std::string> run();
private:
int _port;
};
}

View File

@ -10,6 +10,7 @@
#include "IXWebSocketTransport.h"
#include "IXWebSocketHttpHeaders.h"
#include "IXSocketConnect.h" // for configure, cleanup, move it back to Socket
#include "IXSocket.h"
#ifdef IXWEBSOCKET_USE_TLS
@ -221,6 +222,7 @@ namespace ix
return s;
}
// Client
WebSocketInitResult WebSocketTransport::init()
{
std::string protocol, host, path, query;
@ -380,12 +382,70 @@ namespace ix
return WebSocketInitResult(true, status, "", headers);
}
// Server
WebSocketInitResult WebSocketTransport::initFromSocket(int fd)
{
_requestInitCancellation = false;
_socket.reset();
_socket = std::make_shared<Socket>(fd);
WebSocketHttpHeaders headers;
// Read first line
char line[256];
int i;
for (i = 0; i < 2 || (i < 255 && line[i-2] != '\r' && line[i-1] != '\n'); ++i)
{
if (!readByte(line+i))
{
return WebSocketInitResult(false, 0, std::string("Failed reading HTTP status line from ") + _url);
}
}
line[i] = 0;
if (i == 255)
{
return WebSocketInitResult(false, 0, std::string("Got bad status line connecting to ") + _url);
}
std::cout << "initFromSocket::start" << std::endl;
std::cout << line << std::endl;
auto result = parseHttpHeaders();
auto headersValid = result.first;
auto headers = result.second;
if (!headersValid)
{
return WebSocketInitResult(false, 401, "Error parsing HTTP headers");
}
if (headers.find("sec-websocket-key") == headers.end())
{
std::string errorMsg("Missing Sec-WebSocket-Key value");
return WebSocketInitResult(false, 401, errorMsg);
}
std::cout << "FIXME perMessageDeflateOptions" << std::endl;
char output[29] = {};
WebSocketHandshake::generate(headers["sec-websocket-key"].c_str(), output);
std::stringstream ss;
ss << "HTTP/1.1 101\r\n";
ss << "Sec-WebSocket-Accept: " << std::string(output) << "\r\n";
ss << "\r\n";
std::string dest("remove host"); // FIXME
std::cout << ss.str() << std::endl;
if (!writeBytes(ss.str()))
{
return WebSocketInitResult(false, 0, std::string("Failed sending response to ") + dest);
}
std::cout << "initFromSocket::end" << std::endl;
return WebSocketInitResult(true, 200, "", headers);
}

View File

@ -0,0 +1,20 @@
/*
* IXSetThreadName_apple.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include "../IXSetThreadName.h"
#include <pthread.h>
namespace ix
{
void setThreadName(const std::string& name)
{
//
// Apple reserves 16 bytes for its thread names
// Notice that the Apple version of pthread_setname_np
// does not take a pthread_t argument
//
pthread_setname_np(name.substr(0, 63).c_str());
}
}

View File

@ -0,0 +1,21 @@
/*
* IXSetThreadName_linux.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include "../IXSetThreadName.h"
#include <pthread.h>
namespace ix
{
void setThreadName(const std::string& name)
{
//
// Linux only reserves 16 bytes for its thread names
// See prctl and PR_SET_NAME property in
// http://man7.org/linux/man-pages/man2/prctl.2.html
//
pthread_setname_np(pthread_self(),
name.substr(0, 15).c_str());
}
}