Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions wish/cpp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
_deps
build
*.log
*.a

# CMake artifacts (in-source builds)
CMakeFiles
CMakeCache.txt
cmake_install.cmake
Makefile
CTestTestfile.cmake

# Compiled Object files
*.o
*.obj

# Dynamic Libraries
*.so
*.dylib
*.dll

# Executables
wish-server
wish-client

# Editor/IDE files
.vscode/
.idea/
*.swp
.DS_Store
44 changes: 44 additions & 0 deletions wish/cpp/CMakeLists.txt
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

started with only CMake. good to have Bazel, too?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start with CMake. Ultimately we want this to work with the Python toolchain?

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.14)
project(wish_cpp)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
wslay
GIT_REPOSITORY https://github.com/tatsuhiro-t/wslay.git
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a local patch on this not included in this PR that skips masking validation. I'll think about how to include it later.

GIT_TAG master
)

FetchContent_Declare(
libevent
GIT_REPOSITORY https://github.com/libevent/libevent.git
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

started with libevent. We may want to revisit this after studying what to do with Python binding, etc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wish codec should be based on wslay ... and then it should work with any http client/server?

net_http's httpserver uses libevents for both I/O and http/1.1.

GIT_TAG master
)
# Disable OpenSSL and other heavy features for libevent to keep build fast/simple if possible
set(EVENT__DISABLE_OPENSSL ON CACHE BOOL "" FORCE)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wenbozhu I guess this is what you mentioned in the meeting about disabling TLS.

It's generated code which I didn't take a close look. I'll review these.

set(EVENT__DISABLE_MBEDTLS ON CACHE BOOL "" FORCE)
set(EVENT__DISABLE_TESTS ON CACHE BOOL "" FORCE)
set(EVENT__DISABLE_REGRESS ON CACHE BOOL "" FORCE)
set(EVENT__DISABLE_SAMPLES ON CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(wslay libevent)

include_directories(${wslay_SOURCE_DIR}/lib/includes)
include_directories(${libevent_SOURCE_DIR}/include)
include_directories(${libevent_BINARY_DIR}/include)
include_directories(src)

add_library(wish_handler
src/wish_handler.cc
src/wish_handler.h
)
target_link_libraries(wish_handler wslay event)

add_executable(wish-server examples/server.cc)
target_link_libraries(wish-server wish_handler)

add_executable(wish-client examples/client.cc)
target_link_libraries(wish-client wish_handler)
59 changes: 59 additions & 0 deletions wish/cpp/examples/client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <iostream>
#include <cstring>
#include <string>

#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/dns.h>

#include "../src/wish_handler.h"

int main() {
struct event_base *base = event_base_new();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me change the indentation to comply with the Google C++ Style Guide by a separate PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG

if (!base) {
std::cerr << "Could not initialize libevent!" << std::endl;
return 1;
}

struct evdns_base *dns_base = evdns_base_new(base, 1);
if (!dns_base) {
std::cerr << "Could not initialize dns!" << std::endl;
return 1;
}

struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
std::cerr << "Could not create bufferevent!" << std::endl;
return 1;
}

if (bufferevent_socket_connect_hostname(bev, dns_base, AF_INET, "127.0.0.1", 8080) < 0) {
std::cerr << "Could not connect!" << std::endl;
return 1;
}

// Handler manages its own lifecycle (deletes itself on close)
WishHandler* handler = new WishHandler(bev, false); // is_server = false

handler->SetOnOpen([handler]() {
std::cout << "Connected and Handshake Complete!" << std::endl;

handler->SendText("Hello WiSH Text!");
handler->SendBinary("Hello WiSH Binary!");
handler->SendMetadata(true, "Hello WiSH Metadata!");
});

handler->SetOnMessage([](uint8_t opcode, const std::string& msg) {
std::cout << "Server says [opcode " << (int)opcode << "]: " << msg << std::endl;
});

handler->Start();

std::cout << "Client running..." << std::endl;
event_base_dispatch(base);

evdns_base_free(dns_base, 0);
event_base_free(base);

return 0;
}
88 changes: 88 additions & 0 deletions wish/cpp/examples/server.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include <iostream>
#include <cstring>
#include <string> // Added for std::to_string

#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <arpa/inet.h>

#include "../src/wish_handler.h"

void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *address, int socklen, void *ctx) {

struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

std::cout << "Client connected." << std::endl;

// Handler manages its own lifecycle (deletes itself on close)
WishHandler* handler = new WishHandler(bev, true);

handler->SetOnMessage([handler](uint8_t opcode, const std::string& msg) {
std::string type;
switch(opcode) {
case 1: type = "TEXT"; break;
case 2: type = "BINARY"; break;
case 3: type = "TEXT_METADATA"; break;
case 4: type = "BINARY_METADATA"; break;
default: type = "UNKNOWN(" + std::to_string(opcode) + ")"; break;
}
std::cout << "Received [" << type << "]: " << msg << std::endl;

// Echo back
int res = 0;
if (opcode == 1) res = handler->SendText("Echo: " + msg);
else if (opcode == 2) res = handler->SendBinary("Echo: " + msg);
else if (opcode == 3 || opcode == 4) res = handler->SendMetadata(opcode == 3, "Echo: " + msg);

if (res != 0) {
std::cerr << "Failed to send echo." << std::endl;
}
});

handler->Start();
}

void accept_error_cb(struct evconnlistener *listener, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
std::cerr << "Got an error " << err << " (" << evutil_socket_error_to_string(err) << ") on the listener. Shutting down." << std::endl;
event_base_loopexit(base, NULL);
}

int main(int argc, char **argv) {
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
int port = 8080;

base = event_base_new();
if (!base) {
std::cerr << "Could not initialize libevent!" << std::endl;
return 1;
}

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(0);
sin.sin_port = htons(port);

listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
(struct sockaddr*)&sin, sizeof(sin));

if (!listener) {
std::cerr << "Could not create a listener!" << std::endl;
return 1;
}

std::cout << "Server listening on port " << port << "..." << std::endl;
event_base_dispatch(base);

evconnlistener_free(listener);
event_base_free(base);

return 0;
}
Loading