proof of concept server implementation
This commit is contained in:
parent
24c9e0abc3
commit
a39209a895
@ -10,12 +10,15 @@ set (CMAKE_CXX_STANDARD 11)
|
||||
set (CXX_STANDARD_REQUIRED ON)
|
||||
set (CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
|
||||
|
||||
set( IXWEBSOCKET_SOURCES
|
||||
ixwebsocket/IXEventFd.cpp
|
||||
ixwebsocket/IXSocket.cpp
|
||||
ixwebsocket/IXSocketConnect.cpp
|
||||
ixwebsocket/IXDNSLookup.cpp
|
||||
ixwebsocket/IXWebSocket.cpp
|
||||
ixwebsocket/IXWebSocketServer.cpp
|
||||
ixwebsocket/IXWebSocketTransport.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflate.cpp
|
||||
ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp
|
||||
@ -29,6 +32,7 @@ set( IXWEBSOCKET_HEADERS
|
||||
ixwebsocket/IXDNSLookup.h
|
||||
ixwebsocket/IXCancellationRequest.h
|
||||
ixwebsocket/IXWebSocket.h
|
||||
ixwebsocket/IXWebSocketServer.h
|
||||
ixwebsocket/IXWebSocketTransport.h
|
||||
ixwebsocket/IXWebSocketSendInfo.h
|
||||
ixwebsocket/IXWebSocketErrorInfo.h
|
||||
|
30
examples/echo_server/CMakeLists.txt
Normal file
30
examples/echo_server/CMakeLists.txt
Normal file
@ -0,0 +1,30 @@
|
||||
#
|
||||
# Author: Benjamin Sergeant
|
||||
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
cmake_minimum_required (VERSION 3.4.1)
|
||||
project (echo_server)
|
||||
|
||||
# There's -Weverything too for clang
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
|
||||
|
||||
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
option(USE_TLS "Add TLS support" ON)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ixwebsocket)
|
||||
|
||||
include_directories(echo_server .)
|
||||
|
||||
add_executable(echo_server
|
||||
echo_server.cpp)
|
||||
|
||||
if (APPLE AND USE_TLS)
|
||||
target_link_libraries(echo_server "-framework foundation" "-framework security")
|
||||
endif()
|
||||
|
||||
target_link_libraries(echo_server ixwebsocket)
|
||||
install(TARGETS echo_server DESTINATION bin)
|
29
examples/echo_server/echo_server.cpp
Normal file
29
examples/echo_server/echo_server.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* echo_server.cpp
|
||||
* Author: Benjamin Sergeant
|
||||
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <ixwebsocket/IXWebSocketServer.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int port = 8080;
|
||||
if (argc == 2)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << argv[1];
|
||||
ss >> port;
|
||||
}
|
||||
|
||||
ix::WebSocketServer server(port);
|
||||
auto res = server.run();
|
||||
if (!res.first)
|
||||
{
|
||||
std::cerr << res.second << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
133
ixwebsocket/IXWebSocketServer.cpp
Normal file
133
ixwebsocket/IXWebSocketServer.cpp
Normal 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, "");
|
||||
}
|
||||
}
|
24
ixwebsocket/IXWebSocketServer.h
Normal file
24
ixwebsocket/IXWebSocketServer.h
Normal 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;
|
||||
};
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
20
ixwebsocket/apple/IXSetThreadName_apple.cpp
Normal file
20
ixwebsocket/apple/IXSetThreadName_apple.cpp
Normal 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());
|
||||
}
|
||||
}
|
21
ixwebsocket/linux/IXSetThreadName_linux.cpp
Normal file
21
ixwebsocket/linux/IXSetThreadName_linux.cpp
Normal 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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user