-
Notifications
You must be signed in to change notification settings - Fork 9
Initial implementation of WiSH in C++ #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| 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 | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. started with
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
| 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(); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| } | ||
| 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; | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?